From d18d5bab6e5c20124158b07370737e1e5fa9cbaf Mon Sep 17 00:00:00 2001 From: Josh Pigford Date: Wed, 10 Jan 2024 09:41:07 -0600 Subject: [PATCH] Initial commit of old codebase --- .dockerignore | 7 + .editorconfig | 13 + .env.example | 15 + .eslintrc.json | 95 + .github/workflows/deploy-auth0.yml | 23 + .github/workflows/deploy-services-manual.yml | 58 + .github/workflows/deploy-services.yml | 61 + .github/workflows/template.auth0-deploy.yml | 37 + .../workflows/template.deploy-services.yml | 108 + .github/workflows/validate-pull-request.yml | 102 + .gitignore | 58 + .husky/pre-commit | 4 + .prettierignore | 4 + .prettierrc | 15 + .storybook/main.js | 11 + .storybook/tsconfig.json | 5 + .vscode/extensions.json | 9 + .vscode/launch.json | 47 + .vscode/settings.json | 12 + README.md | 80 + apps/.gitkeep | 0 apps/advisor/.eslintrc.json | 34 + .../components/AdvisorMessageInput.tsx | 198 + apps/advisor/components/Conversation.tsx | 81 + .../components/ConversationUserDetails.tsx | 446 + apps/advisor/components/Conversations.tsx | 229 + apps/advisor/components/EditUser.tsx | 107 + apps/advisor/components/Layout.tsx | 213 + apps/advisor/components/Meta.tsx | 34 + apps/advisor/components/RiskSlider.tsx | 108 + apps/advisor/components/Toaster.tsx | 26 + apps/advisor/components/TrendBadge.tsx | 34 + apps/advisor/components/Unauthorized.tsx | 29 + apps/advisor/components/UserList.tsx | 89 + apps/advisor/components/UserSearch.tsx | 25 + apps/advisor/index.d.ts | 6 + apps/advisor/jest.config.ts | 11 + apps/advisor/lib/trpc.ts | 25 + apps/advisor/lib/util.ts | 29 + apps/advisor/next-env.d.ts | 5 + apps/advisor/next.config.js | 27 + apps/advisor/pages/_app.tsx | 54 + apps/advisor/pages/api/auth/[...auth0].ts | 10 + apps/advisor/pages/api/trpc/[...trpc].ts | 25 + apps/advisor/pages/conversations/[id].tsx | 368 + apps/advisor/pages/conversations/index.tsx | 22 + apps/advisor/pages/settings.tsx | 10 + apps/advisor/pages/styles.css | 147 + apps/advisor/pages/users/[userId].tsx | 529 + apps/advisor/pages/users/index.tsx | 76 + apps/advisor/postcss.config.js | 6 + apps/advisor/public/.gitkeep | 0 .../public/assets/android-chrome-192x192.png | Bin 0 -> 5306 bytes .../public/assets/android-chrome-512x512.png | Bin 0 -> 14471 bytes .../public/assets/apple-touch-icon.png | Bin 0 -> 5085 bytes apps/advisor/public/assets/favicon-16x16.png | Bin 0 -> 1151 bytes apps/advisor/public/assets/favicon-32x32.png | Bin 0 -> 1349 bytes apps/advisor/public/assets/favicon.ico | Bin 0 -> 15086 bytes .../generalsans/GeneralSans-Variable.woff2 | Bin 0 -> 39032 bytes .../assets/fonts/inter/Inter-Variable.woff2 | Bin 0 -> 324864 bytes .../monument/MonumentExtended-Black.woff2 | Bin 0 -> 23752 bytes .../monument/MonumentExtended-Bold.woff2 | Bin 0 -> 24448 bytes .../monument/MonumentExtended-Book.woff2 | Bin 0 -> 23868 bytes .../monument/MonumentExtended-Heavy.woff2 | Bin 0 -> 25988 bytes .../monument/MonumentExtended-Light.woff2 | Bin 0 -> 23996 bytes .../monument/MonumentExtended-Medium.woff2 | Bin 0 -> 23968 bytes .../monument/MonumentExtended-Regular.woff2 | Bin 0 -> 24128 bytes .../monument/MonumentExtended-Thin.woff2 | Bin 0 -> 22896 bytes .../public/assets/images/advisor-avatar.png | Bin 0 -> 11027 bytes apps/advisor/public/assets/maybe.svg | 16 + apps/advisor/public/assets/site.webmanifest | 20 + apps/advisor/tailwind.config.js | 69 + apps/advisor/tsconfig.json | 33 + apps/advisor/tsconfig.spec.json | 21 + apps/client/.eslintrc.json | 34 + apps/client/components/APM.tsx | 53 + apps/client/components/Maintenance.tsx | 80 + apps/client/components/Meta.tsx | 62 + apps/client/components/ModalManager.tsx | 65 + .../components/account-views/DefaultView.tsx | 104 + .../account-views/InvestmentView.tsx | 670 + .../components/account-views/LoanView.tsx | 143 + apps/client/components/account-views/index.ts | 2 + apps/client/env.ts | 16 + apps/client/jest.config.ts | 11 + apps/client/next-env.d.ts | 5 + apps/client/next.config.js | 22 + apps/client/pages/404.tsx | 7 + apps/client/pages/_app.tsx | 106 + apps/client/pages/accounts/[accountId].tsx | 168 + apps/client/pages/accounts/index.tsx | 127 + apps/client/pages/api/card.tsx | 95 + .../ask-the-advisor/[conversationId].tsx | 150 + apps/client/pages/ask-the-advisor/index.tsx | 352 + .../pages/ask-the-advisor/questionnaire.tsx | 690 + apps/client/pages/card/[id].tsx | 74 + apps/client/pages/data-editor.tsx | 65 + apps/client/pages/index.tsx | 242 + apps/client/pages/login.tsx | 20 + apps/client/pages/oauth.tsx | 86 + apps/client/pages/onboarding.tsx | 143 + apps/client/pages/plans/[planId].tsx | 455 + apps/client/pages/plans/create.tsx | 33 + apps/client/pages/plans/index.tsx | 56 + apps/client/pages/register.tsx | 20 + apps/client/pages/settings.tsx | 108 + apps/client/pages/upgrade.tsx | 20 + apps/client/postcss.config.js | 6 + apps/client/public/.gitkeep | 0 .../public/assets/android-chrome-192x192.png | Bin 0 -> 5306 bytes .../public/assets/android-chrome-512x512.png | Bin 0 -> 14471 bytes .../client/public/assets/apple-touch-icon.png | Bin 0 -> 5085 bytes apps/client/public/assets/browserconfig.xml | 10 + apps/client/public/assets/favicon-16x16.png | Bin 0 -> 1151 bytes apps/client/public/assets/favicon-32x32.png | Bin 0 -> 1349 bytes apps/client/public/assets/favicon.ico | Bin 0 -> 15086 bytes .../generalsans/GeneralSans-Variable.woff2 | Bin 0 -> 39032 bytes .../assets/fonts/inter/Inter-Regular.ttf | Bin 0 -> 309828 bytes .../assets/fonts/inter/Inter-Variable.woff2 | Bin 0 -> 324864 bytes .../monument/MonumentExtended-Black.woff2 | Bin 0 -> 23752 bytes .../monument/MonumentExtended-Bold.woff2 | Bin 0 -> 24448 bytes .../monument/MonumentExtended-Book.woff2 | Bin 0 -> 23868 bytes .../monument/MonumentExtended-Heavy.woff2 | Bin 0 -> 25988 bytes .../monument/MonumentExtended-Light.woff2 | Bin 0 -> 23996 bytes .../monument/MonumentExtended-Medium.woff2 | Bin 0 -> 23968 bytes .../monument/MonumentExtended-Regular.woff2 | Bin 0 -> 24128 bytes .../monument/MonumentExtended-Thin.woff2 | Bin 0 -> 22896 bytes .../public/assets/images/advisor-avatar.png | Bin 0 -> 11027 bytes .../assets/images/coming-soon-events.png | Bin 0 -> 14969 bytes .../client/public/assets/images/team/josh.jpg | Bin 0 -> 12933 bytes .../public/assets/images/team/justin.jpg | Bin 0 -> 12880 bytes .../client/public/assets/images/team/nick.jpg | Bin 0 -> 14431 bytes apps/client/public/assets/images/team/tim.jpg | Bin 0 -> 11126 bytes .../public/assets/images/team/travis.jpg | Bin 0 -> 13433 bytes .../client/public/assets/images/team/zach.jpg | Bin 0 -> 12983 bytes apps/client/public/assets/maybe-black.svg | 31 + apps/client/public/assets/maybe-box.svg | 107 + apps/client/public/assets/maybe-full.svg | 27 + apps/client/public/assets/maybe-gray.svg | 12 + apps/client/public/assets/maybe-text.svg | 7 + apps/client/public/assets/maybe.svg | 16 + apps/client/public/assets/mstile-150x150.png | Bin 0 -> 2826 bytes .../assets/onboarding/intro-thumbnail.jpg | Bin 0 -> 100991 bytes .../public/assets/onboarding/intro-video.mp4 | Bin 0 -> 2848208 bytes apps/client/public/assets/plan-milestones.svg | 47 + .../public/assets/safari-pinned-tab.svg | 98 + .../public/assets/short-sample-audio-file.mp3 | Bin 0 -> 52079 bytes .../public/assets/short-sample-video-file.mp4 | Bin 0 -> 2848208 bytes apps/client/public/assets/site.webmanifest | 20 + apps/client/styles.css | 147 + apps/client/tailwind.config.js | 83 + apps/client/tsconfig.json | 31 + apps/client/tsconfig.spec.json | 20 + apps/e2e/.eslintrc.json | 17 + apps/e2e/cypress.config.ts | 37 + apps/e2e/src/e2e/accounts.cy.ts | 370 + apps/e2e/src/e2e/subscription.cy.ts | 74 + .../stripe/checkoutSessionCompleted.ts | 88 + .../stripe/customerSubscriptionCreated.ts | 159 + .../stripe/customerSubscriptionDeleted.ts | 161 + apps/e2e/src/fixtures/stripe/index.ts | 2 + apps/e2e/src/support/commands.ts | 108 + apps/e2e/src/support/e2e.ts | 19 + apps/e2e/tsconfig.json | 10 + apps/server/.eslintrc.json | 18 + apps/server/Dockerfile | 21 + apps/server/jest.config.ts | 16 + .../app/__tests__/account.integration.spec.ts | 274 + .../balance-sync.integration.spec.ts | 283 + .../__tests__/connection.integration.spec.ts | 112 + .../__tests__/insights.integration.spec.ts | 440 + .../__tests__/net-worth.integration.spec.ts | 332 + .../app/__tests__/prisma.integration.spec.ts | 160 + .../app/__tests__/stripe.integration.spec.ts | 100 + .../test-data/portfolio-1/holdings.csv | 3 + .../test-data/portfolio-1/securities.csv | 280 + .../test-data/portfolio-1/transactions.csv | 12 + .../test-data/portfolio-2/holdings.csv | 2 + .../test-data/portfolio-2/securities.csv | 25 + .../test-data/portfolio-2/transactions.csv | 6 + .../server/src/app/__tests__/utils/account.ts | 139 + apps/server/src/app/__tests__/utils/axios.ts | 65 + apps/server/src/app/__tests__/utils/csv.ts | 10 + apps/server/src/app/__tests__/utils/server.ts | 20 + apps/server/src/app/__tests__/utils/user.ts | 15 + .../src/app/admin/views/pages/dashboard.ejs | 15 + .../src/app/admin/views/pages/index.ejs | 20 + .../src/app/admin/views/partials/head.ejs | 9 + apps/server/src/app/app.ts | 184 + apps/server/src/app/lib/ability.ts | 165 + apps/server/src/app/lib/auth0.ts | 18 + apps/server/src/app/lib/convertKit.ts | 66 + apps/server/src/app/lib/endpoint.ts | 374 + apps/server/src/app/lib/finicity.ts | 21 + apps/server/src/app/lib/ldClient.ts | 6 + apps/server/src/app/lib/logger.ts | 23 + apps/server/src/app/lib/multerS3Storage.ts | 77 + apps/server/src/app/lib/plaid.ts | 23 + apps/server/src/app/lib/postmark.ts | 6 + apps/server/src/app/lib/prisma.ts | 50 + apps/server/src/app/lib/s3.ts | 15 + apps/server/src/app/lib/secretsClient.ts | 7 + apps/server/src/app/lib/stripe.ts | 6 + apps/server/src/app/lib/types.ts | 27 + apps/server/src/app/lib/webhook.ts | 21 + .../src/app/middleware/auth-error-handler.ts | 18 + apps/server/src/app/middleware/dev-only.ts | 9 + .../src/app/middleware/error-handler.ts | 36 + .../src/app/middleware/identify-user.ts | 10 + apps/server/src/app/middleware/index.ts | 9 + apps/server/src/app/middleware/maintenance.ts | 46 + apps/server/src/app/middleware/superjson.ts | 19 + .../src/app/middleware/validate-auth0-jwt.ts | 22 + .../middleware/validate-finicity-signature.ts | 21 + .../src/app/middleware/validate-plaid-jwt.ts | 25 + .../src/app/routes/account-rollup.router.ts | 23 + apps/server/src/app/routes/accounts.router.ts | 402 + apps/server/src/app/routes/admin.router.ts | 82 + .../src/app/routes/connections.router.ts | 217 + .../src/app/routes/conversations.router.ts | 165 + apps/server/src/app/routes/e2e.router.ts | 79 + apps/server/src/app/routes/finicity.router.ts | 31 + apps/server/src/app/routes/holdings.router.ts | 49 + apps/server/src/app/routes/index.ts | 20 + .../src/app/routes/institutions.router.ts | 54 + apps/server/src/app/routes/messages.router.ts | 31 + .../src/app/routes/notifications.router.ts | 66 + apps/server/src/app/routes/plaid.router.ts | 91 + apps/server/src/app/routes/plans.router.ts | 164 + apps/server/src/app/routes/public.router.ts | 21 + .../src/app/routes/securities.router.ts | 48 + apps/server/src/app/routes/tools.router.ts | 155 + .../src/app/routes/transactions.router.ts | 49 + apps/server/src/app/routes/users.router.ts | 665 + .../src/app/routes/valuations.router.ts | 64 + apps/server/src/app/routes/webhooks.router.ts | 134 + apps/server/src/app/trpc.ts | 429 + apps/server/src/assets/maybe.svg | 16 + apps/server/src/assets/script.js | 9 + apps/server/src/assets/styles.css | 51 + apps/server/src/env.ts | 104 + .../src/environments/environment.prod.ts | 3 + apps/server/src/environments/environment.ts | 3 + apps/server/src/main.ts | 26 + apps/server/tsconfig.app.json | 10 + apps/server/tsconfig.json | 19 + apps/server/tsconfig.spec.json | 9 + apps/workers/.eslintrc.json | 24 + apps/workers/Dockerfile | 21 + apps/workers/jest.config.ts | 16 + .../__tests__/finicity.integration.spec.ts | 317 + .../app/__tests__/helpers/user.test-helper.ts | 25 + .../app/__tests__/plaid.integration.spec.ts | 285 + .../app/__tests__/queue.integration.spec.ts | 223 + .../security-sync.integration.spec.ts | 73 + apps/workers/src/app/lib/auth0.ts | 18 + apps/workers/src/app/lib/di.ts | 290 + apps/workers/src/app/lib/finicity.ts | 10 + apps/workers/src/app/lib/ldClient.ts | 6 + apps/workers/src/app/lib/logger.ts | 23 + apps/workers/src/app/lib/plaid.ts | 17 + apps/workers/src/app/lib/postmark.ts | 6 + apps/workers/src/app/lib/prisma.ts | 50 + apps/workers/src/app/lib/s3.ts | 15 + apps/workers/src/app/lib/stripe.ts | 6 + .../app/services/bull-queue-event-handler.ts | 117 + apps/workers/src/app/services/index.ts | 2 + .../src/app/services/worker-error.service.ts | 28 + apps/workers/src/assets/.gitkeep | 0 apps/workers/src/env.ts | 50 + .../src/environments/environment.prod.ts | 3 + apps/workers/src/environments/environment.ts | 3 + apps/workers/src/main.ts | 210 + apps/workers/tsconfig.app.json | 10 + apps/workers/tsconfig.json | 19 + apps/workers/tsconfig.spec.json | 15 + auth0/README.md | 101 + auth0/config.js | 96 + auth0/deploy.js | 15 + auth0/emailTemplates/reset_email.html | 432 + auth0/emailTemplates/verify_email.html | 427 + auth0/env.js | 12 + auth0/pages/guardian_multifactor.html | 95 + auth0/pages/login.html | 685 + auth0/pages/password_reset.html | 427 + auth0/rules/assignRolesOnLogin.js | 45 + auth0/rules/enhanceIdToken.js | 53 + auth0/rules/mfaAuth.js | 23 + auth0/rules/updateUserMetadata.js | 27 + auth0/rules/verifyUserOnPasswordReset.js | 34 + auth0/sync.js | 26 + auth0/tenant.yaml | 405 + aws/maybe-app/.gitignore | 8 + aws/maybe-app/.npmignore | 6 + aws/maybe-app/README.md | 19 + aws/maybe-app/bin/maybe-app.ts | 93 + aws/maybe-app/cdk.context.json | 20 + aws/maybe-app/cdk.json | 214 + aws/maybe-app/jest.config.js | 8 + .../configure-github-runner.sh | 73 + .../github-actions-runner.ts | 104 + aws/maybe-app/lib/constructs/jump-box.ts | 60 + aws/maybe-app/lib/constructs/network-acl.ts | 130 + aws/maybe-app/lib/constructs/redis.ts | 73 + aws/maybe-app/lib/stacks/server-stack.ts | 331 + aws/maybe-app/lib/stacks/shared-stack.ts | 229 + aws/maybe-app/lib/stacks/tools-stack.ts | 30 + aws/maybe-app/lib/stacks/workers-stack.ts | 188 + aws/maybe-app/lib/utils/get-context.ts | 232 + aws/maybe-app/package.json | 30 + aws/maybe-app/test/maybe-app.test.ts | 202 + aws/maybe-app/tsconfig.json | 25 + aws/maybe-app/yarn.lock | 3657 +++ babel.config.json | 3 + custom-express.d.ts | 31 + docker-compose.yml | 51 + jest.config.ts | 5 + jest.preset.js | 3 + libs/.gitkeep | 0 libs/client/features/.babelrc | 12 + libs/client/features/.eslintrc.json | 18 + libs/client/features/jest.config.ts | 10 + .../features/src/account/AccountMenu.tsx | 52 + .../features/src/account/AccountsSidebar.tsx | 338 + .../client/features/src/account/PageTitle.tsx | 57 + libs/client/features/src/account/index.ts | 3 + .../features/src/accounts-list/Account.tsx | 163 + .../src/accounts-list/AccountDevTools.tsx | 68 + .../src/accounts-list/AccountGroup.tsx | 47 + .../accounts-list/AccountGroupContainer.tsx | 22 + .../accounts-list/ConnectedAccountGroup.tsx | 279 + .../accounts-list/DeleteConnectionDialog.tsx | 48 + .../DisconnectedAccountGroup.tsx | 67 + .../FinicityFixConnectButton.tsx | 36 + .../src/accounts-list/ManualAccountGroup.tsx | 41 + .../accounts-list/PlaidLinkUpdateButton.tsx | 50 + .../features/src/accounts-list/index.ts | 6 + .../src/accounts-manager/AccountTypeGrid.tsx | 103 + .../accounts-manager/AccountTypeSelector.tsx | 238 + .../AccountValuationFormFields.tsx | 71 + .../src/accounts-manager/AccountsManager.tsx | 117 + .../src/accounts-manager/DeleteAccount.tsx | 45 + .../src/accounts-manager/EditAccount.tsx | 47 + .../src/accounts-manager/InstitutionGrid.tsx | 173 + .../src/accounts-manager/InstitutionList.tsx | 120 + .../src/accounts-manager/asset/AddAsset.tsx | 41 + .../src/accounts-manager/asset/AssetForm.tsx | 108 + .../src/accounts-manager/asset/EditAsset.tsx | 32 + .../src/accounts-manager/asset/index.ts | 2 + .../connected/ConnectedAccountForm.tsx | 88 + .../connected/EditConnectedAccount.tsx | 35 + .../src/accounts-manager/connected/index.ts | 1 + .../features/src/accounts-manager/index.ts | 1 + .../liability/AddLiability.tsx | 80 + .../liability/EditLiability.tsx | 96 + .../liability/LiabilityForm.tsx | 264 + .../src/accounts-manager/liability/index.ts | 2 + .../accounts-manager/property/AddProperty.tsx | 61 + .../property/EditProperty.tsx | 45 + .../property/PropertyForm.tsx | 99 + .../src/accounts-manager/property/index.ts | 2 + .../accounts-manager/vehicle/AddVehicle.tsx | 56 + .../accounts-manager/vehicle/EditVehicle.tsx | 42 + .../accounts-manager/vehicle/VehicleForm.tsx | 93 + .../src/accounts-manager/vehicle/index.ts | 2 + .../src/ask-the-advisor/AdvisorCard.tsx | 108 + .../src/ask-the-advisor/Conversation.tsx | 273 + .../src/ask-the-advisor/ConversationCard.tsx | 76 + .../src/ask-the-advisor/ConversationMenu.tsx | 42 + .../src/ask-the-advisor/DevOnlyMenu.tsx | 33 + .../InlineQuestionCardGroup.tsx | 93 + .../ask-the-advisor/NewConversationDialog.tsx | 141 + .../src/ask-the-advisor/OnboardingOverlay.tsx | 33 + .../src/ask-the-advisor/QuestionCard.tsx | 147 + .../src/ask-the-advisor/QuestionTag.tsx | 26 + .../src/ask-the-advisor/RiskSlider.tsx | 115 + .../features/src/ask-the-advisor/index.ts | 9 + .../src/data-editor/account/AccountEditor.tsx | 146 + .../features/src/data-editor/account/index.ts | 1 + libs/client/features/src/data-editor/index.ts | 2 + .../transaction/TransactionEditor.tsx | 144 + .../src/data-editor/transaction/index.ts | 1 + .../src/holdings-list/CostBasisForm.tsx | 92 + .../src/holdings-list/HoldingList.tsx | 70 + .../src/holdings-list/HoldingPopout.tsx | 413 + .../src/holdings-list/HoldingsTable.tsx | 216 + .../src/holdings-list/SecurityPriceChart.tsx | 103 + .../features/src/holdings-list/index.ts | 1 + libs/client/features/src/index.ts | 20 + .../features/src/insights/explainers/index.ts | 2 + .../explainers/investments/AverageReturn.tsx | 77 + .../explainers/investments/Contributions.tsx | 68 + .../investments/PotentialGainLoss.tsx | 68 + .../investments/SectorAllocation.tsx | 79 + .../explainers/investments/TotalFees.tsx | 69 + .../insights/explainers/investments/index.ts | 5 + .../insights/explainers/net-worth/BadDebt.tsx | 86 + .../explainers/net-worth/GoodDebt.tsx | 87 + .../explainers/net-worth/IlliquidAssets.tsx | 72 + .../explainers/net-worth/IncomePayingDebt.tsx | 188 + .../explainers/net-worth/LiquidAssets.tsx | 72 + .../explainers/net-worth/NetWorthTrend.tsx | 168 + .../explainers/net-worth/SafetyNet.tsx | 196 + .../explainers/net-worth/TotalDebtRatio.tsx | 81 + .../explainers/net-worth/YieldingAssets.tsx | 74 + .../insights/explainers/net-worth/index.ts | 11 + libs/client/features/src/insights/index.ts | 2 + .../features/src/insights/insight-states.ts | 37 + .../InvestmentTransactionList.tsx | 97 + .../InvestmentTransactionListItem.tsx | 158 + .../src/investment-transactions-list/index.ts | 1 + .../features/src/layout/DesktopLayout.tsx | 356 + .../features/src/layout/FullPageLayout.tsx | 22 + .../features/src/layout/MenuPopover.tsx | 44 + .../features/src/layout/MobileLayout.tsx | 236 + libs/client/features/src/layout/NotFound.tsx | 141 + .../client/features/src/layout/SidebarNav.tsx | 56 + .../src/layout/WithOnboardingLayout.tsx | 73 + .../features/src/layout/WithSidebarLayout.tsx | 39 + libs/client/features/src/layout/index.ts | 4 + .../features/src/loan-details/LoanCard.tsx | 65 + .../features/src/loan-details/LoanDetail.tsx | 151 + .../client/features/src/loan-details/index.ts | 2 + .../NetWorthInsightBadge.tsx | 14 + .../NetWorthInsightCard.tsx | 81 + .../NetWorthInsightDetail.tsx | 292 + .../NetWorthInsightStateAxis.tsx | 40 + .../NetWorthPrimaryCardGroup.tsx | 218 + .../NetWorthBreakdownSlider.tsx | 109 + .../breakdown-slider/index.ts | 1 + .../breakdown-table/BreakdownTableIcon.tsx | 18 + .../NetWorthBreakdownTable.tsx | 283 + .../breakdown-table/index.ts | 1 + .../income-debt/IncomeDebtBlock.tsx | 143 + .../income-debt/IncomeDebtDialog.tsx | 68 + .../net-worth-insights/income-debt/index.ts | 1 + .../features/src/net-worth-insights/index.ts | 3 + .../safety-net/SafetyNetDialog.tsx | 98 + .../safety-net/SafetyNetOpportunityCost.tsx | 38 + .../safety-net/SliderBlock.tsx | 54 + .../net-worth-insights/safety-net/index.ts | 1 + .../features/src/onboarding/ExampleApp.tsx | 326 + .../src/onboarding/OnboardingBackground.tsx | 461 + .../src/onboarding/OnboardingGuard.tsx | 49 + .../src/onboarding/OnboardingNavbar.tsx | 113 + libs/client/features/src/onboarding/index.ts | 5 + .../onboarding/sidebar/SidebarOnboarding.tsx | 412 + .../features/src/onboarding/sidebar/index.ts | 1 + .../src/onboarding/steps/CountryWaitlist.tsx | 63 + .../features/src/onboarding/steps/Intro.tsx | 143 + .../features/src/onboarding/steps/Profile.tsx | 489 + .../src/onboarding/steps/StepProps.ts | 5 + .../features/src/onboarding/steps/Welcome.tsx | 158 + .../src/onboarding/steps/YourMaybe.tsx | 225 + .../features/src/onboarding/steps/index.ts | 6 + .../steps/setup/AddFirstAccount.tsx | 165 + .../steps/setup/EmailVerification.tsx | 140 + .../onboarding/steps/setup/OtherAccounts.tsx | 186 + .../src/onboarding/steps/setup/Terms.tsx | 181 + .../src/onboarding/steps/setup/index.ts | 4 + .../features/src/plans/AddPlanScenario.tsx | 300 + .../client/features/src/plans/NewPlanForm.tsx | 66 + libs/client/features/src/plans/PlanContext.ts | 19 + .../features/src/plans/PlanEventCard.tsx | 75 + .../features/src/plans/PlanEventForm.tsx | 231 + .../features/src/plans/PlanEventList.tsx | 241 + .../features/src/plans/PlanEventPopout.tsx | 19 + .../features/src/plans/PlanExplainer.tsx | 281 + libs/client/features/src/plans/PlanMenu.tsx | 104 + .../features/src/plans/PlanMilestones.tsx | 197 + .../features/src/plans/PlanParameterCard.tsx | 49 + .../features/src/plans/PlanRangeInput.tsx | 242 + .../features/src/plans/PlanRangeSelector.tsx | 144 + .../src/plans/RetirementMilestoneForm.tsx | 171 + .../src/plans/RetirementPlanChart.tsx | 433 + libs/client/features/src/plans/icon-utils.ts | 55 + libs/client/features/src/plans/index.ts | 13 + .../ExcludeTransactionDialog.tsx | 64 + .../src/transactions-list/TransactionList.tsx | 77 + .../transactions-list/TransactionListItem.tsx | 345 + .../features/src/transactions-list/index.ts | 1 + .../src/user-billing/BillingPreferences.tsx | 101 + .../src/user-billing/PlanSelector.tsx | 83 + .../features/src/user-billing/PremiumIcon.tsx | 53 + .../src/user-billing/SubscriberGuard.tsx | 51 + .../src/user-billing/UpgradePrompt.tsx | 84 + .../src/user-billing/UpgradeTakeover.tsx | 227 + .../user-billing/graphics/FeaturesGlow.tsx | 54 + .../src/user-billing/graphics/SideGrid.tsx | 30 + .../src/user-billing/graphics/index.ts | 2 + .../client/features/src/user-billing/index.ts | 6 + .../src/user-details/DeleteUserButton.tsx | 46 + .../src/user-details/DeleteUserModal.tsx | 59 + .../src/user-details/LinkAccountFlow.tsx | 270 + .../features/src/user-details/UserDetails.tsx | 317 + .../src/user-details/UserIdentityCard.tsx | 93 + .../src/user-details/UserIdentityList.tsx | 110 + .../client/features/src/user-details/index.ts | 2 + .../NotificationPreferences.tsx | 106 + .../features/src/user-notifications/index.ts | 1 + .../MultiFactorAuthentication.tsx | 73 + .../src/user-security/PasswordReset.tsx | 61 + .../src/user-security/SecurityPreferences.tsx | 47 + .../features/src/user-security/index.ts | 1 + libs/client/features/src/user/AuthLoader.tsx | 45 + libs/client/features/src/user/index.ts | 1 + .../src/valuations-list/PerformanceMetric.tsx | 30 + .../src/valuations-list/ValuationList.tsx | 138 + .../valuations-list/ValuationsDateCell.tsx | 117 + .../src/valuations-list/ValuationsTable.tsx | 111 + .../valuations-list/ValuationsTableForm.tsx | 113 + .../features/src/valuations-list/index.ts | 1 + .../features/src/valuations-list/types.ts | 12 + libs/client/features/tsconfig.json | 23 + libs/client/features/tsconfig.lib.json | 13 + libs/client/features/tsconfig.spec.json | 20 + libs/client/shared/.babelrc | 12 + libs/client/shared/.eslintrc.json | 18 + libs/client/shared/README.md | 7 + libs/client/shared/jest.config.ts | 10 + libs/client/shared/src/api/index.ts | 13 + libs/client/shared/src/api/useAccountApi.ts | 433 + .../shared/src/api/useAccountConnectionApi.ts | 197 + .../shared/src/api/useConversationApi.ts | 214 + libs/client/shared/src/api/useFinicityApi.ts | 22 + libs/client/shared/src/api/useHoldingApi.ts | 85 + .../shared/src/api/useInstitutionApi.ts | 103 + .../shared/src/api/useNotificationsApi.ts | 121 + libs/client/shared/src/api/usePlaidApi.ts | 109 + libs/client/shared/src/api/usePlanApi.ts | 207 + libs/client/shared/src/api/useSecurityApi.ts | 65 + .../shared/src/api/useTransactionApi.ts | 96 + libs/client/shared/src/api/useUserApi.ts | 494 + libs/client/shared/src/api/useValuationApi.ts | 159 + .../shared/src/components/cards/MaybeCard.tsx | 170 + .../components/cards/MaybeCardShareModal.tsx | 85 + .../shared/src/components/cards/index.ts | 2 + .../shared/src/components/charts/index.ts | 1 + .../charts/time-series/AxisBottom.tsx | 59 + .../charts/time-series/AxisLeft.tsx | 31 + .../charts/time-series/BaseChart.tsx | 243 + .../components/charts/time-series/Chart.tsx | 78 + .../charts/time-series/DefaultTooltip.tsx | 142 + .../charts/time-series/FloatingIcon.tsx | 26 + .../components/charts/time-series/Line.tsx | 134 + .../charts/time-series/LineRange.tsx | 118 + .../charts/time-series/LoadingChart.tsx | 178 + .../charts/time-series/MultiColorGradient.tsx | 50 + .../charts/time-series/PlusCircleGlyph.tsx | 23 + .../charts/time-series/ZeroPointGradient.tsx | 20 + .../charts/time-series/colorScales.ts | 39 + .../components/charts/time-series/index.ts | 9 + .../components/charts/time-series/types.ts | 151 + .../charts/time-series/useSeries.ts | 71 + .../charts/time-series/useTooltip.ts | 147 + .../shared/src/components/chat/Attachment.tsx | 36 + .../src/components/chat/AudioPlayer.tsx | 103 + .../src/components/chat/AudioVisualizer.tsx | 109 + .../src/components/chat/MessageInput.tsx | 275 + .../shared/src/components/chat/RichText.tsx | 122 + .../shared/src/components/chat/UploadFile.tsx | 19 + .../src/components/chat/VideoPlayer.tsx | 7 + .../shared/src/components/chat/index.ts | 13 + .../src/components/dialogs/ConfirmDialog.tsx | 34 + .../src/components/dialogs/FeedbackDialog.tsx | 70 + .../src/components/dialogs/NonUSDDialog.tsx | 24 + .../shared/src/components/dialogs/index.ts | 3 + .../explainers/ExplainerExternalLink.tsx | 27 + .../explainers/ExplainerInfoBlock.tsx | 15 + .../explainers/ExplainerPerformanceBlock.tsx | 38 + .../explainers/ExplainerSection.tsx | 23 + .../shared/src/components/explainers/index.ts | 4 + .../shared/src/components/generic/BoxIcon.tsx | 44 + .../src/components/generic/Confetti.tsx | 188 + .../src/components/generic/FeedbackButton.tsx | 14 + .../src/components/generic/InfiniteScroll.tsx | 67 + .../src/components/generic/InsightGroup.tsx | 181 + .../src/components/generic/InsightPopout.tsx | 23 + .../src/components/generic/ProfileCircle.tsx | 37 + .../src/components/generic/RelativeTime.tsx | 21 + .../components/generic/TakeoverBackground.tsx | 285 + .../shared/src/components/generic/Toaster.tsx | 41 + .../src/components/generic/TrendBadge.tsx | 66 + .../shared/src/components/generic/index.ts | 14 + .../small-decimals/SmallDecimals.test.tsx | 27 + .../generic/small-decimals/SmallDecimals.tsx | 33 + .../generic/small-decimals/index.ts | 1 + libs/client/shared/src/components/index.ts | 9 + .../components/loaders/MainContentLoader.tsx | 17 + .../shared/src/components/loaders/index.ts | 1 + .../overlays/BlurredContentOverlay.tsx | 44 + .../overlays/ErrorFallbackOverlay.tsx | 20 + .../overlays/MainContentOverlay.tsx | 26 + .../src/components/overlays/Overlay.tsx | 14 + .../shared/src/components/overlays/index.ts | 4 + .../tables/data-table/DataTable.tsx | 124 + .../tables/data-table/DefaultCell.tsx | 9 + .../tables/data-table/EditableBooleanCell.tsx | 26 + .../tables/data-table/EditableCell.tsx | 54 + .../tables/data-table/EditableDateCell.tsx | 85 + .../data-table/EditableDropdownCell.tsx | 96 + .../tables/data-table/EditableStringCell.tsx | 49 + .../src/components/tables/data-table/index.ts | 7 + .../src/components/tables/data-table/types.ts | 24 + .../shared/src/components/tables/index.ts | 1 + libs/client/shared/src/hooks/index.ts | 14 + .../src/hooks/useAccountNotifications.ts | 33 + .../shared/src/hooks/useAxiosWithAuth.ts | 12 + libs/client/shared/src/hooks/useDebounce.ts | 18 + libs/client/shared/src/hooks/useFinicity.ts | 85 + libs/client/shared/src/hooks/useFrame.ts | 23 + libs/client/shared/src/hooks/useIntercom.ts | 50 + libs/client/shared/src/hooks/useInterval.ts | 18 + .../client/shared/src/hooks/useLastUpdated.ts | 32 + .../shared/src/hooks/useLocalStorage.ts | 32 + libs/client/shared/src/hooks/useLogger.ts | 12 + .../shared/src/hooks/useModalManager.ts | 18 + libs/client/shared/src/hooks/usePlaid.ts | 134 + .../shared/src/hooks/useProviderStatus.ts | 27 + libs/client/shared/src/hooks/useQueryParam.ts | 38 + libs/client/shared/src/hooks/useScreenSize.ts | 14 + libs/client/shared/src/index.ts | 6 + .../src/providers/AccountContextProvider.tsx | 115 + .../shared/src/providers/AuthProvider.tsx | 64 + .../shared/src/providers/AxiosProvider.tsx | 139 + .../src/providers/LayoutContextProvider.tsx | 19 + .../shared/src/providers/LogProvider.tsx | 10 + .../shared/src/providers/PopoutProvider.tsx | 34 + .../shared/src/providers/QueryProvider.tsx | 45 + .../providers/UserAccountContextProvider.tsx | 168 + libs/client/shared/src/providers/index.ts | 8 + .../src/types/client-side-feature-flags.ts | 5 + libs/client/shared/src/types/index.ts | 2 + libs/client/shared/src/types/react-types.ts | 5 + libs/client/shared/src/utils/account-utils.ts | 78 + .../shared/src/utils/agreement-utils.ts | 18 + libs/client/shared/src/utils/auth-utils.ts | 7 + libs/client/shared/src/utils/browser-utils.ts | 55 + libs/client/shared/src/utils/form-utils.ts | 39 + libs/client/shared/src/utils/image-loaders.ts | 17 + libs/client/shared/src/utils/index.ts | 7 + libs/client/shared/src/utils/intercom.ts | 36 + libs/client/shared/tsconfig.json | 23 + libs/client/shared/tsconfig.lib.json | 13 + libs/client/shared/tsconfig.spec.json | 20 + libs/design-system/.babelrc | 12 + libs/design-system/.eslintrc.json | 18 + libs/design-system/.storybook/main.js | 24 + libs/design-system/.storybook/manager.js | 6 + libs/design-system/.storybook/preview.js | 31 + .../.storybook/public/favicon.ico | Bin 0 -> 15086 bytes libs/design-system/.storybook/theme.js | 18 + libs/design-system/.storybook/tsconfig.json | 24 + libs/design-system/README.md | 5 + .../assets/fonts/inter/Inter-Variable.woff2 | Bin 0 -> 324864 bytes .../fonts/monument/MonumentExtended-Black.eot | Bin 0 -> 26579 bytes .../fonts/monument/MonumentExtended-Black.ttf | Bin 0 -> 63368 bytes .../monument/MonumentExtended-Black.woff | Bin 0 -> 25648 bytes .../monument/MonumentExtended-Black.woff2 | Bin 0 -> 23752 bytes .../fonts/monument/MonumentExtended-Bold.eot | Bin 0 -> 27538 bytes .../fonts/monument/MonumentExtended-Bold.ttf | Bin 0 -> 64188 bytes .../fonts/monument/MonumentExtended-Bold.woff | Bin 0 -> 26540 bytes .../monument/MonumentExtended-Bold.woff2 | Bin 0 -> 24448 bytes .../fonts/monument/MonumentExtended-Book.eot | Bin 0 -> 27085 bytes .../fonts/monument/MonumentExtended-Book.ttf | Bin 0 -> 62564 bytes .../fonts/monument/MonumentExtended-Book.woff | Bin 0 -> 25880 bytes .../monument/MonumentExtended-Book.woff2 | Bin 0 -> 23868 bytes .../fonts/monument/MonumentExtended-Heavy.eot | Bin 0 -> 29333 bytes .../fonts/monument/MonumentExtended-Heavy.ttf | Bin 0 -> 66684 bytes .../monument/MonumentExtended-Heavy.woff | Bin 0 -> 28112 bytes .../monument/MonumentExtended-Heavy.woff2 | Bin 0 -> 25988 bytes .../fonts/monument/MonumentExtended-Light.eot | Bin 0 -> 26987 bytes .../fonts/monument/MonumentExtended-Light.ttf | Bin 0 -> 62724 bytes .../monument/MonumentExtended-Light.woff | Bin 0 -> 25860 bytes .../monument/MonumentExtended-Light.woff2 | Bin 0 -> 23996 bytes .../monument/MonumentExtended-Medium.eot | Bin 0 -> 27255 bytes .../monument/MonumentExtended-Medium.ttf | Bin 0 -> 63236 bytes .../monument/MonumentExtended-Medium.woff | Bin 0 -> 26172 bytes .../monument/MonumentExtended-Medium.woff2 | Bin 0 -> 23968 bytes .../monument/MonumentExtended-Regular.eot | Bin 0 -> 27265 bytes .../monument/MonumentExtended-Regular.ttf | Bin 0 -> 63368 bytes .../monument/MonumentExtended-Regular.woff | Bin 0 -> 26244 bytes .../monument/MonumentExtended-Regular.woff2 | Bin 0 -> 24128 bytes .../fonts/monument/MonumentExtended-Thin.eot | Bin 0 -> 25895 bytes .../fonts/monument/MonumentExtended-Thin.ttf | Bin 0 -> 62632 bytes .../fonts/monument/MonumentExtended-Thin.woff | Bin 0 -> 24788 bytes .../monument/MonumentExtended-Thin.woff2 | Bin 0 -> 22896 bytes libs/design-system/assets/logo.svg | 24 + libs/design-system/assets/styles.css | 57 + .../docs/Getting Started/About.stories.mdx | 7 + .../docs/Getting Started/Colors.stories.mdx | 110 + .../Getting Started/Typography.stories.mdx | 19 + libs/design-system/docs/util/Swatch.tsx | 18 + libs/design-system/docs/util/SwatchGroup.tsx | 21 + libs/design-system/jest.config.ts | 11 + libs/design-system/jest.setup.js | 1 + libs/design-system/package.json | 104 + libs/design-system/postcss.config.js | 8 + libs/design-system/src/index.ts | 75 + .../lib/AccordionRow/AccordionRow.spec.tsx | 82 + .../lib/AccordionRow/AccordionRow.stories.tsx | 30 + .../src/lib/AccordionRow/AccordionRow.tsx | 112 + .../__snapshots__/AccordionRow.spec.tsx.snap | 704 + .../src/lib/AccordionRow/index.ts | 2 + .../src/lib/Alert/Alert.stories.tsx | 53 + libs/design-system/src/lib/Alert/Alert.tsx | 74 + libs/design-system/src/lib/Alert/index.ts | 2 + .../src/lib/Badge/Badge.spec.tsx | 37 + .../src/lib/Badge/Badge.stories.tsx | 27 + libs/design-system/src/lib/Badge/Badge.tsx | 84 + .../Badge/__snapshots__/Badge.spec.tsx.snap | 220 + libs/design-system/src/lib/Badge/index.ts | 2 + .../src/lib/Breadcrumb/Breadcrumb.spec.tsx | 19 + .../src/lib/Breadcrumb/Breadcrumb.stories.tsx | 21 + .../src/lib/Breadcrumb/Breadcrumb.tsx | 49 + .../__snapshots__/Breadcrumb.spec.tsx.snap | 152 + .../design-system/src/lib/Breadcrumb/index.ts | 2 + .../src/lib/Button/Button.spec.tsx | 39 + .../src/lib/Button/Button.stories.tsx | 24 + libs/design-system/src/lib/Button/Button.tsx | 88 + .../Button/__snapshots__/Button.spec.tsx.snap | 230 + libs/design-system/src/lib/Button/index.ts | 2 + .../src/lib/Checkbox/Checkbox.spec.tsx | 32 + .../src/lib/Checkbox/Checkbox.stories.tsx | 22 + .../src/lib/Checkbox/Checkbox.tsx | 63 + .../__snapshots__/Checkbox.spec.tsx.snap | 213 + libs/design-system/src/lib/Checkbox/index.ts | 2 + .../src/lib/DatePicker/DatePicker.spec.tsx | 107 + .../src/lib/DatePicker/DatePicker.stories.tsx | 27 + .../src/lib/DatePicker/DatePicker.tsx | 172 + .../src/lib/DatePicker/DatePickerCalendar.tsx | 186 + .../src/lib/DatePicker/DatePickerInput.tsx | 82 + .../src/lib/DatePicker/DatePickerMonth.tsx | 84 + .../lib/DatePicker/DatePickerQuickSelect.tsx | 91 + .../DatePickerRange/DatePickerRange.spec.tsx | 214 + .../DatePickerRange.stories.tsx | 108 + .../DatePickerRange/DatePickerRange.tsx | 214 + .../DatePickerRange/DatePickerRangeButton.tsx | 47 + .../DatePickerRangeCalendar.tsx | 282 + .../DatePickerRange/DatePickerRangeTabs.tsx | 141 + .../DatePickerRange.spec.tsx.snap | 324 + .../lib/DatePicker/DatePickerRange/index.ts | 1 + .../src/lib/DatePicker/DatePickerYear.tsx | 100 + .../__snapshots__/DatePicker.spec.tsx.snap | 181 + .../design-system/src/lib/DatePicker/index.ts | 7 + .../src/lib/DatePicker/selectableRanges.ts | 154 + .../src/lib/DatePicker/utils.spec.tsx | 56 + .../src/lib/DatePicker/utils.tsx | 45 + .../src/lib/Dialog/Dialog.spec.tsx | 24 + .../src/lib/Dialog/Dialog.stories.tsx | 149 + libs/design-system/src/lib/Dialog/Dialog.tsx | 198 + .../design-system/src/lib/Dialog/DialogV2.tsx | 111 + .../Dialog/__snapshots__/Dialog.spec.tsx.snap | 176 + libs/design-system/src/lib/Dialog/index.ts | 3 + .../src/lib/FormGroup/FormGroup.spec.tsx | 19 + .../src/lib/FormGroup/FormGroup.tsx | 19 + .../__snapshots__/FormGroup.spec.tsx.snap | 90 + libs/design-system/src/lib/FormGroup/index.ts | 2 + .../FractionalCircle.stories.tsx | 26 + .../lib/FractionalCircle/FractionalCircle.tsx | 75 + .../src/lib/FractionalCircle/index.ts | 2 + .../src/lib/IndexTabs/IndexTabs.spec.tsx | 50 + .../src/lib/IndexTabs/IndexTabs.stories.tsx | 68 + .../src/lib/IndexTabs/IndexTabs.tsx | 142 + .../__snapshots__/IndexTabs.spec.tsx.snap | 120 + libs/design-system/src/lib/IndexTabs/index.ts | 1 + .../src/lib/Listbox/Listbox.spec.tsx | 63 + .../src/lib/Listbox/Listbox.stories.tsx | 75 + .../design-system/src/lib/Listbox/Listbox.tsx | 284 + .../__snapshots__/Listbox.spec.tsx.snap | 589 + libs/design-system/src/lib/Listbox/index.ts | 1 + .../LoadingPlaceholder.spec.tsx | 10 + .../LoadingPlaceholder.stories.tsx | 11 + .../LoadingPlaceholder/LoadingPlaceholder.tsx | 36 + .../LoadingPlaceholder.spec.tsx.snap | 84 + .../src/lib/LoadingPlaceholder/index.ts | 1 + .../LoadingSpinner/LoadingSpinner.spec.tsx | 11 + .../LoadingSpinner/LoadingSpinner.stories.tsx | 10 + .../src/lib/LoadingSpinner/LoadingSpinner.tsx | 142 + .../LoadingSpinner.spec.tsx.snap | 284 + .../src/lib/LoadingSpinner/index.ts | 1 + libs/design-system/src/lib/Menu/Menu.spec.tsx | 60 + .../src/lib/Menu/Menu.stories.tsx | 46 + libs/design-system/src/lib/Menu/Menu.tsx | 157 + .../lib/Menu/__snapshots__/Menu.spec.tsx.snap | 311 + libs/design-system/src/lib/Menu/index.ts | 1 + .../src/lib/Popover/Popover.spec.tsx | 36 + .../src/lib/Popover/Popover.stories.tsx | 20 + .../design-system/src/lib/Popover/Popover.tsx | 114 + .../__snapshots__/Popover.spec.tsx.snap | 345 + libs/design-system/src/lib/Popover/index.ts | 1 + .../src/lib/RTEditor/RTEditor.tsx | 98 + libs/design-system/src/lib/RTEditor/index.ts | 1 + .../src/lib/RadioGroup/RadioGroup.stories.tsx | 35 + .../src/lib/RadioGroup/RadioGroup.tsx | 40 + .../design-system/src/lib/RadioGroup/index.ts | 1 + .../src/lib/Slider/Slider.spec.tsx | 19 + .../src/lib/Slider/Slider.stories.tsx | 17 + libs/design-system/src/lib/Slider/Slider.tsx | 100 + .../Slider/__snapshots__/Slider.spec.tsx.snap | 106 + libs/design-system/src/lib/Slider/index.ts | 2 + libs/design-system/src/lib/Step/Step.spec.tsx | 102 + .../src/lib/Step/Step.stories.tsx | 82 + libs/design-system/src/lib/Step/Step.tsx | 160 + .../lib/Step/__snapshots__/Step.spec.tsx.snap | 379 + libs/design-system/src/lib/Step/index.ts | 1 + libs/design-system/src/lib/Tab/Tab.spec.tsx | 51 + .../design-system/src/lib/Tab/Tab.stories.tsx | 45 + libs/design-system/src/lib/Tab/Tab.tsx | 57 + .../lib/Tab/__snapshots__/Tab.spec.tsx.snap | 166 + libs/design-system/src/lib/Tab/index.ts | 1 + .../src/lib/Takeover/Takeover.spec.tsx | 18 + .../src/lib/Takeover/Takeover.stories.tsx | 27 + .../src/lib/Takeover/Takeover.tsx | 49 + .../__snapshots__/Takeover.spec.tsx.snap | 108 + libs/design-system/src/lib/Takeover/index.ts | 1 + .../src/lib/Toast/Toast.spec.tsx | 20 + .../src/lib/Toast/Toast.stories.tsx | 36 + libs/design-system/src/lib/Toast/Toast.tsx | 58 + .../Toast/__snapshots__/Toast.spec.tsx.snap | 370 + libs/design-system/src/lib/Toast/index.ts | 2 + .../src/lib/Toggle/Toggle.spec.tsx | 50 + .../src/lib/Toggle/Toggle.stories.tsx | 22 + libs/design-system/src/lib/Toggle/Toggle.tsx | 62 + .../Toggle/__snapshots__/Toggle.spec.tsx.snap | 199 + libs/design-system/src/lib/Toggle/index.ts | 2 + .../src/lib/Tooltip/Tooltip.spec.tsx | 47 + .../src/lib/Tooltip/Tooltip.stories.tsx | 68 + .../design-system/src/lib/Tooltip/Tooltip.tsx | 28 + .../__snapshots__/Tooltip.spec.tsx.snap | 173 + libs/design-system/src/lib/Tooltip/index.ts | 2 + .../src/lib/TrendLine/TrendLine.stories.tsx | 25 + .../src/lib/TrendLine/TrendLine.tsx | 92 + .../src/lib/TrendLine/Trendline.spec.tsx | 52 + .../__snapshots__/Trendline.spec.tsx.snap | 364 + libs/design-system/src/lib/TrendLine/index.ts | 2 + .../src/lib/inputs/Input/Input.spec.tsx | 57 + .../src/lib/inputs/Input/Input.stories.tsx | 82 + .../src/lib/inputs/Input/Input.tsx | 144 + .../Input/__snapshots__/Input.spec.tsx.snap | 379 + .../src/lib/inputs/Input/index.ts | 2 + .../inputs/InputColorHint/InputColorHint.tsx | 37 + .../src/lib/inputs/InputColorHint/index.ts | 2 + .../InputCurrency/InputCurrency.spec.tsx | 40 + .../InputCurrency/InputCurrency.stories.tsx | 42 + .../inputs/InputCurrency/InputCurrency.tsx | 40 + .../__snapshots__/InputCurrency.spec.tsx.snap | 203 + .../src/lib/inputs/InputCurrency/index.ts | 2 + .../src/lib/inputs/InputHint/InputHint.tsx | 25 + .../src/lib/inputs/InputHint/index.ts | 2 + .../InputPassword/InputPassword.spec.tsx | 82 + .../InputPassword/InputPassword.stories.tsx | 45 + .../inputs/InputPassword/InputPassword.tsx | 214 + .../__snapshots__/InputPassword.spec.tsx.snap | 1092 + .../src/lib/inputs/InputPassword/index.ts | 2 + libs/design-system/src/lib/inputs/index.ts | 14 + libs/design-system/tailwind.config.js | 156 + libs/design-system/tsconfig.json | 26 + libs/design-system/tsconfig.lib.json | 24 + libs/design-system/tsconfig.spec.json | 21 + libs/finicity-api/.babelrc | 3 + libs/finicity-api/.eslintrc.json | 18 + libs/finicity-api/README.md | 7 + libs/finicity-api/jest.config.ts | 16 + libs/finicity-api/src/finicity-api.ts | 294 + libs/finicity-api/src/index.ts | 2 + libs/finicity-api/src/types/accounts.ts | 163 + libs/finicity-api/src/types/authentication.ts | 4 + libs/finicity-api/src/types/connect.ts | 32 + libs/finicity-api/src/types/customers.ts | 14 + libs/finicity-api/src/types/index.ts | 8 + libs/finicity-api/src/types/institutions.ts | 65 + libs/finicity-api/src/types/transactions.ts | 98 + libs/finicity-api/src/types/txpush.ts | 41 + libs/finicity-api/src/types/webhooks.ts | 37 + libs/finicity-api/tsconfig.json | 13 + libs/finicity-api/tsconfig.lib.json | 11 + libs/finicity-api/tsconfig.spec.json | 20 + libs/server/features/.babelrc | 3 + libs/server/features/.eslintrc.json | 18 + libs/server/features/README.md | 7 + libs/server/features/jest.config.ts | 16 + .../account-balance/balance-sync.strategy.ts | 40 + .../features/src/account-balance/index.ts | 5 + ...tment-transaction-balance-sync.strategy.ts | 144 + .../loan-balance-sync.strategy.ts | 132 + .../transaction-balance-sync.strategy.ts | 53 + .../valuation-balance-sync.strategy.ts | 40 + .../account-connection.processor.ts | 106 + .../account-connection.provider.ts | 26 + .../account-connection.service.ts | 227 + .../features/src/account-connection/index.ts | 3 + .../src/account/account-query.service.ts | 529 + .../features/src/account/account.processor.ts | 33 + .../features/src/account/account.provider.ts | 31 + .../features/src/account/account.schema.ts | 211 + .../features/src/account/account.service.ts | 444 + libs/server/features/src/account/index.ts | 6 + .../features/src/account/insight.service.ts | 912 + .../src/conversation/conversation.schema.ts | 12 + .../src/conversation/conversation.service.ts | 166 + .../server/features/src/conversation/index.ts | 5 + .../src/conversation/message.schema.ts | 8 + .../src/conversation/message.service.ts | 79 + .../features/src/conversation/sandbox.ts | 217 + .../features/src/email/email.processor.ts | 268 + .../server/features/src/email/email.schema.ts | 49 + .../features/src/email/email.service.ts | 131 + libs/server/features/src/email/index.ts | 2 + .../features/src/holding/holding.schema.ts | 8 + .../features/src/holding/holding.service.ts | 73 + libs/server/features/src/holding/index.ts | 2 + libs/server/features/src/index.ts | 15 + libs/server/features/src/institution/index.ts | 2 + .../src/institution/institution.provider.ts | 19 + .../src/institution/institution.service.ts | 283 + .../src/investment-transaction/index.ts | 1 + .../investment-transaction.schema.ts | 12 + libs/server/features/src/plan/index.ts | 3 + libs/server/features/src/plan/plan.schema.ts | 91 + libs/server/features/src/plan/plan.service.ts | 467 + .../features/src/plan/projection/index.ts | 3 + .../src/plan/projection/monte-carlo.spec.ts | 14 + .../src/plan/projection/monte-carlo.ts | 12 + .../projection/projection-calculator.spec.ts | 198 + .../plan/projection/projection-calculator.ts | 197 + .../plan/projection/projection-value.spec.ts | 15 + .../src/plan/projection/projection-value.ts | 41 + .../src/providers/finicity/finicity.etl.ts | 662 + .../providers/finicity/finicity.service.ts | 281 + .../providers/finicity/finicity.webhook.ts | 144 + .../features/src/providers/finicity/index.ts | 3 + libs/server/features/src/providers/index.ts | 4 + .../features/src/providers/plaid/index.ts | 3 + .../src/providers/plaid/plaid.etl.spec.ts | 275 + .../features/src/providers/plaid/plaid.etl.ts | 954 + .../src/providers/plaid/plaid.service.ts | 330 + .../src/providers/plaid/plaid.webhook.ts | 174 + .../features/src/providers/property/index.ts | 1 + .../providers/property/property.service.ts | 37 + .../features/src/providers/vehicle/index.ts | 1 + .../src/providers/vehicle/vehicle.service.ts | 39 + .../features/src/security-pricing/index.ts | 2 + .../security-pricing.processor.ts | 18 + .../security-pricing.service.ts | 157 + libs/server/features/src/stripe/index.ts | 1 + .../features/src/stripe/stripe.webhook.ts | 107 + libs/server/features/src/transaction/index.ts | 2 + .../src/transaction/transaction.schema.ts | 27 + .../src/transaction/transaction.service.ts | 127 + libs/server/features/src/user/index.ts | 5 + .../features/src/user/onboarding.schema.ts | 43 + .../features/src/user/onboarding.service.ts | 130 + .../features/src/user/user.processor.ts | 65 + libs/server/features/src/user/user.service.ts | 713 + libs/server/features/src/valuation/index.ts | 1 + .../src/valuation/valuation.service.ts | 84 + libs/server/features/tsconfig.json | 19 + libs/server/features/tsconfig.lib.json | 11 + libs/server/features/tsconfig.spec.json | 20 + libs/server/shared/.babelrc | 3 + libs/server/shared/.eslintrc.json | 18 + libs/server/shared/README.md | 7 + libs/server/shared/jest.config.ts | 16 + libs/server/shared/src/endpoint.ts | 72 + libs/server/shared/src/etl.ts | 14 + libs/server/shared/src/index.ts | 6 + libs/server/shared/src/logger.ts | 25 + .../shared/src/services/cache.service.ts | 70 + .../shared/src/services/crypto.service.ts | 18 + .../src/services/feature-flag.service.ts | 25 + libs/server/shared/src/services/index.ts | 8 + .../src/services/market-data.service.spec.ts | 21 + .../src/services/market-data.service.ts | 491 + libs/server/shared/src/services/pg.service.ts | 34 + .../shared/src/services/queue.service.ts | 138 + .../shared/src/services/queue/bull-queue.ts | 165 + .../src/services/queue/in-memory-queue.ts | 76 + .../server/shared/src/services/queue/index.ts | 2 + libs/server/shared/src/services/s3.service.ts | 62 + libs/server/shared/src/sql-template-tag.ts | 125 + libs/server/shared/src/utils/auth-utils.ts | 60 + libs/server/shared/src/utils/db-utils.ts | 89 + libs/server/shared/src/utils/error-utils.ts | 118 + .../server/shared/src/utils/finicity-utils.ts | 121 + libs/server/shared/src/utils/index.ts | 8 + .../shared/src/utils/plaid-utils.spec.ts | 67 + libs/server/shared/src/utils/plaid-utils.ts | 180 + libs/server/shared/src/utils/server-utils.ts | 99 + libs/server/shared/tsconfig.json | 19 + libs/server/shared/tsconfig.lib.json | 11 + libs/server/shared/tsconfig.spec.json | 20 + libs/shared/.babelrc | 3 + libs/shared/.eslintrc.json | 24 + libs/shared/README.md | 7 + libs/shared/jest.config.ts | 16 + libs/shared/src/index.ts | 6 + libs/shared/src/superjson.spec.ts | 22 + libs/shared/src/superjson.ts | 24 + libs/shared/src/types/account-types.ts | 234 + libs/shared/src/types/api-types.ts | 22 + libs/shared/src/types/conversation-types.ts | 28 + libs/shared/src/types/email-types.ts | 12 + libs/shared/src/types/general-types.ts | 93 + libs/shared/src/types/holding-types.ts | 18 + libs/shared/src/types/index.ts | 12 + libs/shared/src/types/institution-types.ts | 22 + .../src/types/investment-transaction-types.ts | 3 + libs/shared/src/types/plan-types.ts | 73 + libs/shared/src/types/security-types.ts | 32 + libs/shared/src/types/transaction-types.ts | 38 + libs/shared/src/types/user-types.ts | 324 + libs/shared/src/utils/account-utils.ts | 194 + libs/shared/src/utils/ata-utils.ts | 291 + libs/shared/src/utils/date-utils.spec.ts | 72 + libs/shared/src/utils/date-utils.ts | 140 + libs/shared/src/utils/geo-utils.ts | 305 + libs/shared/src/utils/index.ts | 12 + libs/shared/src/utils/market-utils.ts | 9 + libs/shared/src/utils/number-utils.spec.ts | 111 + libs/shared/src/utils/number-utils.ts | 118 + libs/shared/src/utils/plan-utils.ts | 20 + libs/shared/src/utils/shared-utils.spec.ts | 151 + libs/shared/src/utils/shared-utils.ts | 151 + libs/shared/src/utils/stats-utils.spec.ts | 37 + libs/shared/src/utils/stats-utils.ts | 102 + libs/shared/src/utils/test-utils.ts | 51 + libs/shared/src/utils/transaction-utils.ts | 15 + libs/shared/src/utils/user-utils.ts | 38 + libs/shared/tsconfig.json | 17 + libs/shared/tsconfig.lib.json | 11 + libs/shared/tsconfig.spec.json | 20 + nx.json | 70 + package.json | 270 + .../20211005200319_init/migration.sql | 108 + .../migration.sql | 8 + .../migration.sql | 10 + .../migration.sql | 62 + .../migration.sql | 24 + .../migration.sql | 5 + .../migration.sql | 24 + .../migration.sql | 60 + .../migration.sql | 8 + .../migration.sql | 2 + .../migration.sql | 23 + .../migration.sql | 64 + .../migration.sql | 101 + .../migration.sql | 5 + .../migration.sql | 32 + .../migration.sql | 8 + .../migration.sql | 17 + .../migration.sql | 23 + .../migration.sql | 3 + .../migration.sql | 58 + .../migration.sql | 4 + .../migration.sql | 10 + .../migration.sql | 2 + .../migration.sql | 91 + .../migration.sql | 22 + .../migration.sql | 17 + .../migration.sql | 54 + .../migration.sql | 14 + .../migration.sql | 3 + .../20211209050532_update_fns/migration.sql | 18 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 1 + .../migration.sql | 25 + .../migration.sql | 2 + .../migration.sql | 28 + .../20220112171128_update_fn/migration.sql | 16 + .../migration.sql | 2 + .../migration.sql | 3 + .../migration.sql | 19 + .../migration.sql | 15 + .../migration.sql | 97 + .../20220203234737_update_fn/migration.sql | 96 + .../migration.sql | 12 + .../migration.sql | 9 + .../migration.sql | 5 + .../migration.sql | 2 + .../migration.sql | 136 + .../migration.sql | 2 + .../migration.sql | 10 + .../migration.sql | 19 + .../migration.sql | 8 + .../20220315172110_institution/migration.sql | 20 + .../migration.sql | 8 + .../migration.sql | 8 + .../migration.sql | 130 + .../20220323212807_fix_function/migration.sql | 28 + .../migration.sql | 14 + .../migration.sql | 3 + .../migration.sql | 23 + .../migration.sql | 8 + .../migration.sql | 57 + .../migration.sql | 13 + .../migration.sql | 14 + .../migration.sql | 2 + .../migration.sql | 3 + .../migration.sql | 8 + .../migration.sql | 2 + .../migration.sql | 10 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 29 + .../migration.sql | 8 + .../migration.sql | 4 + .../migration.sql | 2 + .../migration.sql | 11 + .../migration.sql | 25 + .../migration.sql | 33 + .../migration.sql | 67 + .../migration.sql | 5 + .../migration.sql | 3 + .../migration.sql | 15 + .../migration.sql | 56 + .../migration.sql | 23 + .../migration.sql | 14 + .../migration.sql | 5 + .../migration.sql | 5 + .../20220729202323_txn_updates/migration.sql | 15 + .../migration.sql | 63 + .../migration.sql | 2 + .../migration.sql | 2 + .../migration.sql | 57 + .../migration.sql | 44 + .../20220817180833_dietz/migration.sql | 63 + .../migration.sql | 25 + .../20220915200544_add_plans/migration.sql | 17 + .../migration.sql | 1 + .../migration.sql | 8 + .../migration.sql | 1 + .../migration.sql | 64 + .../migration.sql | 102 + .../migration.sql | 9 + .../migration.sql | 5 + .../migration.sql | 11 + .../migration.sql | 13 + .../20221111192223_ata/migration.sql | 75 + .../migration.sql | 5 + .../migration.sql | 17 + .../migration.sql | 2 + .../migration.sql | 8 + .../migration.sql | 8 + .../migration.sql | 8 + .../migration.sql | 14 + .../migration.sql | 2 + .../migration.sql | 4 + .../migration.sql | 2 + .../migration.sql | 43 + .../migration.sql | 10 + .../migration.sql | 3 + .../migration.sql | 21 + .../20230105221446_user_audit/migration.sql | 25 + .../migration.sql | 2 + .../20230106221847_user_profile/migration.sql | 10 + .../migration.sql | 5 + .../migration.sql | 39 + .../migration.sql | 3 + .../migration.sql | 17 + .../20230117150048_user_name/migration.sql | 8 + .../migration.sql | 27 + .../migration.sql | 2 + .../migration.sql | 22 + .../migration.sql | 35 + .../migration.sql | 11 + .../migration.sql | 2 + .../migration.sql | 17 + .../migration.sql | 3 + .../migration.sql | 9 + .../migration.sql | 54 + .../migration.sql | 4 + .../20230211134603_advisor_crm/migration.sql | 29 + .../migration.sql | 5 + .../20230223020847_txn_view/migration.sql | 58 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 735 + prisma/seed.ts | 70 + redis.Dockerfile | 5 + redis.conf | 1883 ++ render.yaml | 19 + tools/generators/.gitkeep | 0 tools/pages/projections.html | 178 + tools/scripts/gen-cloudfront-signing-keys.sh | 12 + tools/scripts/gen-secret.sh | 3 + tools/scripts/getAffectedApps.sh | 13 + .../scripts/run-production-postgres-tunnel.sh | 12 + tools/scripts/run-staging-postgres-tunnel.sh | 12 + tools/scripts/runStagingE2ETests.sh | 11 + tools/scripts/vercelBuildIgnore.js | 75 + tools/scripts/wait-for-it.sh | 182 + tools/test-data/finicity/finicityTestData.ts | 20053 ++++++++++++++ tools/test-data/finicity/index.ts | 1 + tools/test-data/index.ts | 3 + .../plaid/connection-samples/index.ts | 1 + .../plaid/connection-samples/wealthfront1.ts | 1174 + tools/test-data/plaid/index.ts | 5 + tools/test-data/plaid/plaidApiResponses.ts | 163 + tools/test-data/plaid/plaidTestData.ts | 409 + tools/test-data/plaid/stockPrices.ts | 725 + tools/test-data/plaid/testDates.ts | 32 + tools/test-data/polygon/index.ts | 1 + tools/test-data/polygon/snapshots.ts | 99 + tools/tsconfig.tools.json | 12 + tsconfig.base.json | 30 + vercel.json | 5 + workspace.json | 522 + yarn.lock | 21901 ++++++++++++++++ 1210 files changed, 137730 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .env.example create mode 100644 .eslintrc.json create mode 100644 .github/workflows/deploy-auth0.yml create mode 100644 .github/workflows/deploy-services-manual.yml create mode 100644 .github/workflows/deploy-services.yml create mode 100644 .github/workflows/template.auth0-deploy.yml create mode 100644 .github/workflows/template.deploy-services.yml create mode 100644 .github/workflows/validate-pull-request.yml create mode 100644 .gitignore create mode 100755 .husky/pre-commit create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .storybook/main.js create mode 100644 .storybook/tsconfig.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 README.md create mode 100644 apps/.gitkeep create mode 100644 apps/advisor/.eslintrc.json create mode 100644 apps/advisor/components/AdvisorMessageInput.tsx create mode 100644 apps/advisor/components/Conversation.tsx create mode 100644 apps/advisor/components/ConversationUserDetails.tsx create mode 100644 apps/advisor/components/Conversations.tsx create mode 100644 apps/advisor/components/EditUser.tsx create mode 100644 apps/advisor/components/Layout.tsx create mode 100644 apps/advisor/components/Meta.tsx create mode 100644 apps/advisor/components/RiskSlider.tsx create mode 100644 apps/advisor/components/Toaster.tsx create mode 100644 apps/advisor/components/TrendBadge.tsx create mode 100644 apps/advisor/components/Unauthorized.tsx create mode 100644 apps/advisor/components/UserList.tsx create mode 100644 apps/advisor/components/UserSearch.tsx create mode 100644 apps/advisor/index.d.ts create mode 100644 apps/advisor/jest.config.ts create mode 100644 apps/advisor/lib/trpc.ts create mode 100644 apps/advisor/lib/util.ts create mode 100644 apps/advisor/next-env.d.ts create mode 100644 apps/advisor/next.config.js create mode 100644 apps/advisor/pages/_app.tsx create mode 100644 apps/advisor/pages/api/auth/[...auth0].ts create mode 100644 apps/advisor/pages/api/trpc/[...trpc].ts create mode 100644 apps/advisor/pages/conversations/[id].tsx create mode 100644 apps/advisor/pages/conversations/index.tsx create mode 100644 apps/advisor/pages/settings.tsx create mode 100644 apps/advisor/pages/styles.css create mode 100644 apps/advisor/pages/users/[userId].tsx create mode 100644 apps/advisor/pages/users/index.tsx create mode 100644 apps/advisor/postcss.config.js create mode 100644 apps/advisor/public/.gitkeep create mode 100644 apps/advisor/public/assets/android-chrome-192x192.png create mode 100644 apps/advisor/public/assets/android-chrome-512x512.png create mode 100644 apps/advisor/public/assets/apple-touch-icon.png create mode 100644 apps/advisor/public/assets/favicon-16x16.png create mode 100644 apps/advisor/public/assets/favicon-32x32.png create mode 100644 apps/advisor/public/assets/favicon.ico create mode 100644 apps/advisor/public/assets/fonts/generalsans/GeneralSans-Variable.woff2 create mode 100644 apps/advisor/public/assets/fonts/inter/Inter-Variable.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Black.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Bold.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Book.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Heavy.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Light.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Medium.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Regular.woff2 create mode 100644 apps/advisor/public/assets/fonts/monument/MonumentExtended-Thin.woff2 create mode 100644 apps/advisor/public/assets/images/advisor-avatar.png create mode 100644 apps/advisor/public/assets/maybe.svg create mode 100644 apps/advisor/public/assets/site.webmanifest create mode 100644 apps/advisor/tailwind.config.js create mode 100644 apps/advisor/tsconfig.json create mode 100644 apps/advisor/tsconfig.spec.json create mode 100644 apps/client/.eslintrc.json create mode 100644 apps/client/components/APM.tsx create mode 100644 apps/client/components/Maintenance.tsx create mode 100644 apps/client/components/Meta.tsx create mode 100644 apps/client/components/ModalManager.tsx create mode 100644 apps/client/components/account-views/DefaultView.tsx create mode 100644 apps/client/components/account-views/InvestmentView.tsx create mode 100644 apps/client/components/account-views/LoanView.tsx create mode 100644 apps/client/components/account-views/index.ts create mode 100644 apps/client/env.ts create mode 100644 apps/client/jest.config.ts create mode 100644 apps/client/next-env.d.ts create mode 100644 apps/client/next.config.js create mode 100644 apps/client/pages/404.tsx create mode 100644 apps/client/pages/_app.tsx create mode 100644 apps/client/pages/accounts/[accountId].tsx create mode 100644 apps/client/pages/accounts/index.tsx create mode 100644 apps/client/pages/api/card.tsx create mode 100644 apps/client/pages/ask-the-advisor/[conversationId].tsx create mode 100644 apps/client/pages/ask-the-advisor/index.tsx create mode 100644 apps/client/pages/ask-the-advisor/questionnaire.tsx create mode 100644 apps/client/pages/card/[id].tsx create mode 100644 apps/client/pages/data-editor.tsx create mode 100644 apps/client/pages/index.tsx create mode 100644 apps/client/pages/login.tsx create mode 100644 apps/client/pages/oauth.tsx create mode 100644 apps/client/pages/onboarding.tsx create mode 100644 apps/client/pages/plans/[planId].tsx create mode 100644 apps/client/pages/plans/create.tsx create mode 100644 apps/client/pages/plans/index.tsx create mode 100644 apps/client/pages/register.tsx create mode 100644 apps/client/pages/settings.tsx create mode 100644 apps/client/pages/upgrade.tsx create mode 100644 apps/client/postcss.config.js create mode 100644 apps/client/public/.gitkeep create mode 100644 apps/client/public/assets/android-chrome-192x192.png create mode 100644 apps/client/public/assets/android-chrome-512x512.png create mode 100644 apps/client/public/assets/apple-touch-icon.png create mode 100644 apps/client/public/assets/browserconfig.xml create mode 100644 apps/client/public/assets/favicon-16x16.png create mode 100644 apps/client/public/assets/favicon-32x32.png create mode 100644 apps/client/public/assets/favicon.ico create mode 100644 apps/client/public/assets/fonts/generalsans/GeneralSans-Variable.woff2 create mode 100644 apps/client/public/assets/fonts/inter/Inter-Regular.ttf create mode 100644 apps/client/public/assets/fonts/inter/Inter-Variable.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Black.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Bold.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Book.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Heavy.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Light.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Medium.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Regular.woff2 create mode 100644 apps/client/public/assets/fonts/monument/MonumentExtended-Thin.woff2 create mode 100644 apps/client/public/assets/images/advisor-avatar.png create mode 100644 apps/client/public/assets/images/coming-soon-events.png create mode 100644 apps/client/public/assets/images/team/josh.jpg create mode 100644 apps/client/public/assets/images/team/justin.jpg create mode 100644 apps/client/public/assets/images/team/nick.jpg create mode 100644 apps/client/public/assets/images/team/tim.jpg create mode 100644 apps/client/public/assets/images/team/travis.jpg create mode 100644 apps/client/public/assets/images/team/zach.jpg create mode 100644 apps/client/public/assets/maybe-black.svg create mode 100644 apps/client/public/assets/maybe-box.svg create mode 100644 apps/client/public/assets/maybe-full.svg create mode 100644 apps/client/public/assets/maybe-gray.svg create mode 100644 apps/client/public/assets/maybe-text.svg create mode 100644 apps/client/public/assets/maybe.svg create mode 100644 apps/client/public/assets/mstile-150x150.png create mode 100644 apps/client/public/assets/onboarding/intro-thumbnail.jpg create mode 100644 apps/client/public/assets/onboarding/intro-video.mp4 create mode 100644 apps/client/public/assets/plan-milestones.svg create mode 100644 apps/client/public/assets/safari-pinned-tab.svg create mode 100644 apps/client/public/assets/short-sample-audio-file.mp3 create mode 100644 apps/client/public/assets/short-sample-video-file.mp4 create mode 100644 apps/client/public/assets/site.webmanifest create mode 100644 apps/client/styles.css create mode 100644 apps/client/tailwind.config.js create mode 100644 apps/client/tsconfig.json create mode 100644 apps/client/tsconfig.spec.json create mode 100644 apps/e2e/.eslintrc.json create mode 100644 apps/e2e/cypress.config.ts create mode 100644 apps/e2e/src/e2e/accounts.cy.ts create mode 100644 apps/e2e/src/e2e/subscription.cy.ts create mode 100644 apps/e2e/src/fixtures/stripe/checkoutSessionCompleted.ts create mode 100644 apps/e2e/src/fixtures/stripe/customerSubscriptionCreated.ts create mode 100644 apps/e2e/src/fixtures/stripe/customerSubscriptionDeleted.ts create mode 100644 apps/e2e/src/fixtures/stripe/index.ts create mode 100644 apps/e2e/src/support/commands.ts create mode 100644 apps/e2e/src/support/e2e.ts create mode 100644 apps/e2e/tsconfig.json create mode 100644 apps/server/.eslintrc.json create mode 100644 apps/server/Dockerfile create mode 100644 apps/server/jest.config.ts create mode 100644 apps/server/src/app/__tests__/account.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/balance-sync.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/connection.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/insights.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/net-worth.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/prisma.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/stripe.integration.spec.ts create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-1/holdings.csv create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-1/securities.csv create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-1/transactions.csv create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-2/holdings.csv create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-2/securities.csv create mode 100644 apps/server/src/app/__tests__/test-data/portfolio-2/transactions.csv create mode 100644 apps/server/src/app/__tests__/utils/account.ts create mode 100644 apps/server/src/app/__tests__/utils/axios.ts create mode 100644 apps/server/src/app/__tests__/utils/csv.ts create mode 100644 apps/server/src/app/__tests__/utils/server.ts create mode 100644 apps/server/src/app/__tests__/utils/user.ts create mode 100644 apps/server/src/app/admin/views/pages/dashboard.ejs create mode 100644 apps/server/src/app/admin/views/pages/index.ejs create mode 100644 apps/server/src/app/admin/views/partials/head.ejs create mode 100644 apps/server/src/app/app.ts create mode 100644 apps/server/src/app/lib/ability.ts create mode 100644 apps/server/src/app/lib/auth0.ts create mode 100644 apps/server/src/app/lib/convertKit.ts create mode 100644 apps/server/src/app/lib/endpoint.ts create mode 100644 apps/server/src/app/lib/finicity.ts create mode 100644 apps/server/src/app/lib/ldClient.ts create mode 100644 apps/server/src/app/lib/logger.ts create mode 100644 apps/server/src/app/lib/multerS3Storage.ts create mode 100644 apps/server/src/app/lib/plaid.ts create mode 100644 apps/server/src/app/lib/postmark.ts create mode 100644 apps/server/src/app/lib/prisma.ts create mode 100644 apps/server/src/app/lib/s3.ts create mode 100644 apps/server/src/app/lib/secretsClient.ts create mode 100644 apps/server/src/app/lib/stripe.ts create mode 100644 apps/server/src/app/lib/types.ts create mode 100644 apps/server/src/app/lib/webhook.ts create mode 100644 apps/server/src/app/middleware/auth-error-handler.ts create mode 100644 apps/server/src/app/middleware/dev-only.ts create mode 100644 apps/server/src/app/middleware/error-handler.ts create mode 100644 apps/server/src/app/middleware/identify-user.ts create mode 100644 apps/server/src/app/middleware/index.ts create mode 100644 apps/server/src/app/middleware/maintenance.ts create mode 100644 apps/server/src/app/middleware/superjson.ts create mode 100644 apps/server/src/app/middleware/validate-auth0-jwt.ts create mode 100644 apps/server/src/app/middleware/validate-finicity-signature.ts create mode 100644 apps/server/src/app/middleware/validate-plaid-jwt.ts create mode 100644 apps/server/src/app/routes/account-rollup.router.ts create mode 100644 apps/server/src/app/routes/accounts.router.ts create mode 100644 apps/server/src/app/routes/admin.router.ts create mode 100644 apps/server/src/app/routes/connections.router.ts create mode 100644 apps/server/src/app/routes/conversations.router.ts create mode 100644 apps/server/src/app/routes/e2e.router.ts create mode 100644 apps/server/src/app/routes/finicity.router.ts create mode 100644 apps/server/src/app/routes/holdings.router.ts create mode 100644 apps/server/src/app/routes/index.ts create mode 100644 apps/server/src/app/routes/institutions.router.ts create mode 100644 apps/server/src/app/routes/messages.router.ts create mode 100644 apps/server/src/app/routes/notifications.router.ts create mode 100644 apps/server/src/app/routes/plaid.router.ts create mode 100644 apps/server/src/app/routes/plans.router.ts create mode 100644 apps/server/src/app/routes/public.router.ts create mode 100644 apps/server/src/app/routes/securities.router.ts create mode 100644 apps/server/src/app/routes/tools.router.ts create mode 100644 apps/server/src/app/routes/transactions.router.ts create mode 100644 apps/server/src/app/routes/users.router.ts create mode 100644 apps/server/src/app/routes/valuations.router.ts create mode 100644 apps/server/src/app/routes/webhooks.router.ts create mode 100644 apps/server/src/app/trpc.ts create mode 100644 apps/server/src/assets/maybe.svg create mode 100644 apps/server/src/assets/script.js create mode 100644 apps/server/src/assets/styles.css create mode 100644 apps/server/src/env.ts create mode 100644 apps/server/src/environments/environment.prod.ts create mode 100644 apps/server/src/environments/environment.ts create mode 100644 apps/server/src/main.ts create mode 100644 apps/server/tsconfig.app.json create mode 100644 apps/server/tsconfig.json create mode 100644 apps/server/tsconfig.spec.json create mode 100644 apps/workers/.eslintrc.json create mode 100644 apps/workers/Dockerfile create mode 100644 apps/workers/jest.config.ts create mode 100644 apps/workers/src/app/__tests__/finicity.integration.spec.ts create mode 100644 apps/workers/src/app/__tests__/helpers/user.test-helper.ts create mode 100644 apps/workers/src/app/__tests__/plaid.integration.spec.ts create mode 100644 apps/workers/src/app/__tests__/queue.integration.spec.ts create mode 100644 apps/workers/src/app/__tests__/security-sync.integration.spec.ts create mode 100644 apps/workers/src/app/lib/auth0.ts create mode 100644 apps/workers/src/app/lib/di.ts create mode 100644 apps/workers/src/app/lib/finicity.ts create mode 100644 apps/workers/src/app/lib/ldClient.ts create mode 100644 apps/workers/src/app/lib/logger.ts create mode 100644 apps/workers/src/app/lib/plaid.ts create mode 100644 apps/workers/src/app/lib/postmark.ts create mode 100644 apps/workers/src/app/lib/prisma.ts create mode 100644 apps/workers/src/app/lib/s3.ts create mode 100644 apps/workers/src/app/lib/stripe.ts create mode 100644 apps/workers/src/app/services/bull-queue-event-handler.ts create mode 100644 apps/workers/src/app/services/index.ts create mode 100644 apps/workers/src/app/services/worker-error.service.ts create mode 100644 apps/workers/src/assets/.gitkeep create mode 100644 apps/workers/src/env.ts create mode 100644 apps/workers/src/environments/environment.prod.ts create mode 100644 apps/workers/src/environments/environment.ts create mode 100644 apps/workers/src/main.ts create mode 100644 apps/workers/tsconfig.app.json create mode 100644 apps/workers/tsconfig.json create mode 100644 apps/workers/tsconfig.spec.json create mode 100644 auth0/README.md create mode 100644 auth0/config.js create mode 100644 auth0/deploy.js create mode 100644 auth0/emailTemplates/reset_email.html create mode 100644 auth0/emailTemplates/verify_email.html create mode 100644 auth0/env.js create mode 100644 auth0/pages/guardian_multifactor.html create mode 100644 auth0/pages/login.html create mode 100644 auth0/pages/password_reset.html create mode 100644 auth0/rules/assignRolesOnLogin.js create mode 100644 auth0/rules/enhanceIdToken.js create mode 100644 auth0/rules/mfaAuth.js create mode 100644 auth0/rules/updateUserMetadata.js create mode 100644 auth0/rules/verifyUserOnPasswordReset.js create mode 100644 auth0/sync.js create mode 100644 auth0/tenant.yaml create mode 100644 aws/maybe-app/.gitignore create mode 100644 aws/maybe-app/.npmignore create mode 100644 aws/maybe-app/README.md create mode 100644 aws/maybe-app/bin/maybe-app.ts create mode 100644 aws/maybe-app/cdk.context.json create mode 100644 aws/maybe-app/cdk.json create mode 100644 aws/maybe-app/jest.config.js create mode 100755 aws/maybe-app/lib/constructs/github-actions-runner/configure-github-runner.sh create mode 100644 aws/maybe-app/lib/constructs/github-actions-runner/github-actions-runner.ts create mode 100644 aws/maybe-app/lib/constructs/jump-box.ts create mode 100644 aws/maybe-app/lib/constructs/network-acl.ts create mode 100644 aws/maybe-app/lib/constructs/redis.ts create mode 100644 aws/maybe-app/lib/stacks/server-stack.ts create mode 100644 aws/maybe-app/lib/stacks/shared-stack.ts create mode 100644 aws/maybe-app/lib/stacks/tools-stack.ts create mode 100644 aws/maybe-app/lib/stacks/workers-stack.ts create mode 100644 aws/maybe-app/lib/utils/get-context.ts create mode 100644 aws/maybe-app/package.json create mode 100644 aws/maybe-app/test/maybe-app.test.ts create mode 100644 aws/maybe-app/tsconfig.json create mode 100644 aws/maybe-app/yarn.lock create mode 100644 babel.config.json create mode 100644 custom-express.d.ts create mode 100644 docker-compose.yml create mode 100644 jest.config.ts create mode 100644 jest.preset.js create mode 100644 libs/.gitkeep create mode 100644 libs/client/features/.babelrc create mode 100644 libs/client/features/.eslintrc.json create mode 100644 libs/client/features/jest.config.ts create mode 100644 libs/client/features/src/account/AccountMenu.tsx create mode 100644 libs/client/features/src/account/AccountsSidebar.tsx create mode 100644 libs/client/features/src/account/PageTitle.tsx create mode 100644 libs/client/features/src/account/index.ts create mode 100644 libs/client/features/src/accounts-list/Account.tsx create mode 100644 libs/client/features/src/accounts-list/AccountDevTools.tsx create mode 100644 libs/client/features/src/accounts-list/AccountGroup.tsx create mode 100644 libs/client/features/src/accounts-list/AccountGroupContainer.tsx create mode 100644 libs/client/features/src/accounts-list/ConnectedAccountGroup.tsx create mode 100644 libs/client/features/src/accounts-list/DeleteConnectionDialog.tsx create mode 100644 libs/client/features/src/accounts-list/DisconnectedAccountGroup.tsx create mode 100644 libs/client/features/src/accounts-list/FinicityFixConnectButton.tsx create mode 100644 libs/client/features/src/accounts-list/ManualAccountGroup.tsx create mode 100644 libs/client/features/src/accounts-list/PlaidLinkUpdateButton.tsx create mode 100644 libs/client/features/src/accounts-list/index.ts create mode 100644 libs/client/features/src/accounts-manager/AccountTypeGrid.tsx create mode 100644 libs/client/features/src/accounts-manager/AccountTypeSelector.tsx create mode 100644 libs/client/features/src/accounts-manager/AccountValuationFormFields.tsx create mode 100644 libs/client/features/src/accounts-manager/AccountsManager.tsx create mode 100644 libs/client/features/src/accounts-manager/DeleteAccount.tsx create mode 100644 libs/client/features/src/accounts-manager/EditAccount.tsx create mode 100644 libs/client/features/src/accounts-manager/InstitutionGrid.tsx create mode 100644 libs/client/features/src/accounts-manager/InstitutionList.tsx create mode 100644 libs/client/features/src/accounts-manager/asset/AddAsset.tsx create mode 100644 libs/client/features/src/accounts-manager/asset/AssetForm.tsx create mode 100644 libs/client/features/src/accounts-manager/asset/EditAsset.tsx create mode 100644 libs/client/features/src/accounts-manager/asset/index.ts create mode 100644 libs/client/features/src/accounts-manager/connected/ConnectedAccountForm.tsx create mode 100644 libs/client/features/src/accounts-manager/connected/EditConnectedAccount.tsx create mode 100644 libs/client/features/src/accounts-manager/connected/index.ts create mode 100644 libs/client/features/src/accounts-manager/index.ts create mode 100644 libs/client/features/src/accounts-manager/liability/AddLiability.tsx create mode 100644 libs/client/features/src/accounts-manager/liability/EditLiability.tsx create mode 100644 libs/client/features/src/accounts-manager/liability/LiabilityForm.tsx create mode 100644 libs/client/features/src/accounts-manager/liability/index.ts create mode 100644 libs/client/features/src/accounts-manager/property/AddProperty.tsx create mode 100644 libs/client/features/src/accounts-manager/property/EditProperty.tsx create mode 100644 libs/client/features/src/accounts-manager/property/PropertyForm.tsx create mode 100644 libs/client/features/src/accounts-manager/property/index.ts create mode 100644 libs/client/features/src/accounts-manager/vehicle/AddVehicle.tsx create mode 100644 libs/client/features/src/accounts-manager/vehicle/EditVehicle.tsx create mode 100644 libs/client/features/src/accounts-manager/vehicle/VehicleForm.tsx create mode 100644 libs/client/features/src/accounts-manager/vehicle/index.ts create mode 100644 libs/client/features/src/ask-the-advisor/AdvisorCard.tsx create mode 100644 libs/client/features/src/ask-the-advisor/Conversation.tsx create mode 100644 libs/client/features/src/ask-the-advisor/ConversationCard.tsx create mode 100644 libs/client/features/src/ask-the-advisor/ConversationMenu.tsx create mode 100644 libs/client/features/src/ask-the-advisor/DevOnlyMenu.tsx create mode 100644 libs/client/features/src/ask-the-advisor/InlineQuestionCardGroup.tsx create mode 100644 libs/client/features/src/ask-the-advisor/NewConversationDialog.tsx create mode 100644 libs/client/features/src/ask-the-advisor/OnboardingOverlay.tsx create mode 100644 libs/client/features/src/ask-the-advisor/QuestionCard.tsx create mode 100644 libs/client/features/src/ask-the-advisor/QuestionTag.tsx create mode 100644 libs/client/features/src/ask-the-advisor/RiskSlider.tsx create mode 100644 libs/client/features/src/ask-the-advisor/index.ts create mode 100644 libs/client/features/src/data-editor/account/AccountEditor.tsx create mode 100644 libs/client/features/src/data-editor/account/index.ts create mode 100644 libs/client/features/src/data-editor/index.ts create mode 100644 libs/client/features/src/data-editor/transaction/TransactionEditor.tsx create mode 100644 libs/client/features/src/data-editor/transaction/index.ts create mode 100644 libs/client/features/src/holdings-list/CostBasisForm.tsx create mode 100644 libs/client/features/src/holdings-list/HoldingList.tsx create mode 100644 libs/client/features/src/holdings-list/HoldingPopout.tsx create mode 100644 libs/client/features/src/holdings-list/HoldingsTable.tsx create mode 100644 libs/client/features/src/holdings-list/SecurityPriceChart.tsx create mode 100644 libs/client/features/src/holdings-list/index.ts create mode 100644 libs/client/features/src/index.ts create mode 100644 libs/client/features/src/insights/explainers/index.ts create mode 100644 libs/client/features/src/insights/explainers/investments/AverageReturn.tsx create mode 100644 libs/client/features/src/insights/explainers/investments/Contributions.tsx create mode 100644 libs/client/features/src/insights/explainers/investments/PotentialGainLoss.tsx create mode 100644 libs/client/features/src/insights/explainers/investments/SectorAllocation.tsx create mode 100644 libs/client/features/src/insights/explainers/investments/TotalFees.tsx create mode 100644 libs/client/features/src/insights/explainers/investments/index.ts create mode 100644 libs/client/features/src/insights/explainers/net-worth/BadDebt.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/GoodDebt.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/IlliquidAssets.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/IncomePayingDebt.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/LiquidAssets.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/NetWorthTrend.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/SafetyNet.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/TotalDebtRatio.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/YieldingAssets.tsx create mode 100644 libs/client/features/src/insights/explainers/net-worth/index.ts create mode 100644 libs/client/features/src/insights/index.ts create mode 100644 libs/client/features/src/insights/insight-states.ts create mode 100644 libs/client/features/src/investment-transactions-list/InvestmentTransactionList.tsx create mode 100644 libs/client/features/src/investment-transactions-list/InvestmentTransactionListItem.tsx create mode 100644 libs/client/features/src/investment-transactions-list/index.ts create mode 100644 libs/client/features/src/layout/DesktopLayout.tsx create mode 100644 libs/client/features/src/layout/FullPageLayout.tsx create mode 100644 libs/client/features/src/layout/MenuPopover.tsx create mode 100644 libs/client/features/src/layout/MobileLayout.tsx create mode 100644 libs/client/features/src/layout/NotFound.tsx create mode 100644 libs/client/features/src/layout/SidebarNav.tsx create mode 100644 libs/client/features/src/layout/WithOnboardingLayout.tsx create mode 100644 libs/client/features/src/layout/WithSidebarLayout.tsx create mode 100644 libs/client/features/src/layout/index.ts create mode 100644 libs/client/features/src/loan-details/LoanCard.tsx create mode 100644 libs/client/features/src/loan-details/LoanDetail.tsx create mode 100644 libs/client/features/src/loan-details/index.ts create mode 100644 libs/client/features/src/net-worth-insights/NetWorthInsightBadge.tsx create mode 100644 libs/client/features/src/net-worth-insights/NetWorthInsightCard.tsx create mode 100644 libs/client/features/src/net-worth-insights/NetWorthInsightDetail.tsx create mode 100644 libs/client/features/src/net-worth-insights/NetWorthInsightStateAxis.tsx create mode 100644 libs/client/features/src/net-worth-insights/NetWorthPrimaryCardGroup.tsx create mode 100644 libs/client/features/src/net-worth-insights/breakdown-slider/NetWorthBreakdownSlider.tsx create mode 100644 libs/client/features/src/net-worth-insights/breakdown-slider/index.ts create mode 100644 libs/client/features/src/net-worth-insights/breakdown-table/BreakdownTableIcon.tsx create mode 100644 libs/client/features/src/net-worth-insights/breakdown-table/NetWorthBreakdownTable.tsx create mode 100644 libs/client/features/src/net-worth-insights/breakdown-table/index.ts create mode 100644 libs/client/features/src/net-worth-insights/income-debt/IncomeDebtBlock.tsx create mode 100644 libs/client/features/src/net-worth-insights/income-debt/IncomeDebtDialog.tsx create mode 100644 libs/client/features/src/net-worth-insights/income-debt/index.ts create mode 100644 libs/client/features/src/net-worth-insights/index.ts create mode 100644 libs/client/features/src/net-worth-insights/safety-net/SafetyNetDialog.tsx create mode 100644 libs/client/features/src/net-worth-insights/safety-net/SafetyNetOpportunityCost.tsx create mode 100644 libs/client/features/src/net-worth-insights/safety-net/SliderBlock.tsx create mode 100644 libs/client/features/src/net-worth-insights/safety-net/index.ts create mode 100644 libs/client/features/src/onboarding/ExampleApp.tsx create mode 100644 libs/client/features/src/onboarding/OnboardingBackground.tsx create mode 100644 libs/client/features/src/onboarding/OnboardingGuard.tsx create mode 100644 libs/client/features/src/onboarding/OnboardingNavbar.tsx create mode 100644 libs/client/features/src/onboarding/index.ts create mode 100644 libs/client/features/src/onboarding/sidebar/SidebarOnboarding.tsx create mode 100644 libs/client/features/src/onboarding/sidebar/index.ts create mode 100644 libs/client/features/src/onboarding/steps/CountryWaitlist.tsx create mode 100644 libs/client/features/src/onboarding/steps/Intro.tsx create mode 100644 libs/client/features/src/onboarding/steps/Profile.tsx create mode 100644 libs/client/features/src/onboarding/steps/StepProps.ts create mode 100644 libs/client/features/src/onboarding/steps/Welcome.tsx create mode 100644 libs/client/features/src/onboarding/steps/YourMaybe.tsx create mode 100644 libs/client/features/src/onboarding/steps/index.ts create mode 100644 libs/client/features/src/onboarding/steps/setup/AddFirstAccount.tsx create mode 100644 libs/client/features/src/onboarding/steps/setup/EmailVerification.tsx create mode 100644 libs/client/features/src/onboarding/steps/setup/OtherAccounts.tsx create mode 100644 libs/client/features/src/onboarding/steps/setup/Terms.tsx create mode 100644 libs/client/features/src/onboarding/steps/setup/index.ts create mode 100644 libs/client/features/src/plans/AddPlanScenario.tsx create mode 100644 libs/client/features/src/plans/NewPlanForm.tsx create mode 100644 libs/client/features/src/plans/PlanContext.ts create mode 100644 libs/client/features/src/plans/PlanEventCard.tsx create mode 100644 libs/client/features/src/plans/PlanEventForm.tsx create mode 100644 libs/client/features/src/plans/PlanEventList.tsx create mode 100644 libs/client/features/src/plans/PlanEventPopout.tsx create mode 100644 libs/client/features/src/plans/PlanExplainer.tsx create mode 100644 libs/client/features/src/plans/PlanMenu.tsx create mode 100644 libs/client/features/src/plans/PlanMilestones.tsx create mode 100644 libs/client/features/src/plans/PlanParameterCard.tsx create mode 100644 libs/client/features/src/plans/PlanRangeInput.tsx create mode 100644 libs/client/features/src/plans/PlanRangeSelector.tsx create mode 100644 libs/client/features/src/plans/RetirementMilestoneForm.tsx create mode 100644 libs/client/features/src/plans/RetirementPlanChart.tsx create mode 100644 libs/client/features/src/plans/icon-utils.ts create mode 100644 libs/client/features/src/plans/index.ts create mode 100644 libs/client/features/src/transactions-list/ExcludeTransactionDialog.tsx create mode 100644 libs/client/features/src/transactions-list/TransactionList.tsx create mode 100644 libs/client/features/src/transactions-list/TransactionListItem.tsx create mode 100644 libs/client/features/src/transactions-list/index.ts create mode 100644 libs/client/features/src/user-billing/BillingPreferences.tsx create mode 100644 libs/client/features/src/user-billing/PlanSelector.tsx create mode 100644 libs/client/features/src/user-billing/PremiumIcon.tsx create mode 100644 libs/client/features/src/user-billing/SubscriberGuard.tsx create mode 100644 libs/client/features/src/user-billing/UpgradePrompt.tsx create mode 100644 libs/client/features/src/user-billing/UpgradeTakeover.tsx create mode 100644 libs/client/features/src/user-billing/graphics/FeaturesGlow.tsx create mode 100644 libs/client/features/src/user-billing/graphics/SideGrid.tsx create mode 100644 libs/client/features/src/user-billing/graphics/index.ts create mode 100644 libs/client/features/src/user-billing/index.ts create mode 100644 libs/client/features/src/user-details/DeleteUserButton.tsx create mode 100644 libs/client/features/src/user-details/DeleteUserModal.tsx create mode 100644 libs/client/features/src/user-details/LinkAccountFlow.tsx create mode 100644 libs/client/features/src/user-details/UserDetails.tsx create mode 100644 libs/client/features/src/user-details/UserIdentityCard.tsx create mode 100644 libs/client/features/src/user-details/UserIdentityList.tsx create mode 100644 libs/client/features/src/user-details/index.ts create mode 100644 libs/client/features/src/user-notifications/NotificationPreferences.tsx create mode 100644 libs/client/features/src/user-notifications/index.ts create mode 100644 libs/client/features/src/user-security/MultiFactorAuthentication.tsx create mode 100644 libs/client/features/src/user-security/PasswordReset.tsx create mode 100644 libs/client/features/src/user-security/SecurityPreferences.tsx create mode 100644 libs/client/features/src/user-security/index.ts create mode 100644 libs/client/features/src/user/AuthLoader.tsx create mode 100644 libs/client/features/src/user/index.ts create mode 100644 libs/client/features/src/valuations-list/PerformanceMetric.tsx create mode 100644 libs/client/features/src/valuations-list/ValuationList.tsx create mode 100644 libs/client/features/src/valuations-list/ValuationsDateCell.tsx create mode 100644 libs/client/features/src/valuations-list/ValuationsTable.tsx create mode 100644 libs/client/features/src/valuations-list/ValuationsTableForm.tsx create mode 100644 libs/client/features/src/valuations-list/index.ts create mode 100644 libs/client/features/src/valuations-list/types.ts create mode 100644 libs/client/features/tsconfig.json create mode 100644 libs/client/features/tsconfig.lib.json create mode 100644 libs/client/features/tsconfig.spec.json create mode 100644 libs/client/shared/.babelrc create mode 100644 libs/client/shared/.eslintrc.json create mode 100644 libs/client/shared/README.md create mode 100644 libs/client/shared/jest.config.ts create mode 100644 libs/client/shared/src/api/index.ts create mode 100644 libs/client/shared/src/api/useAccountApi.ts create mode 100644 libs/client/shared/src/api/useAccountConnectionApi.ts create mode 100644 libs/client/shared/src/api/useConversationApi.ts create mode 100644 libs/client/shared/src/api/useFinicityApi.ts create mode 100644 libs/client/shared/src/api/useHoldingApi.ts create mode 100644 libs/client/shared/src/api/useInstitutionApi.ts create mode 100644 libs/client/shared/src/api/useNotificationsApi.ts create mode 100644 libs/client/shared/src/api/usePlaidApi.ts create mode 100644 libs/client/shared/src/api/usePlanApi.ts create mode 100644 libs/client/shared/src/api/useSecurityApi.ts create mode 100644 libs/client/shared/src/api/useTransactionApi.ts create mode 100644 libs/client/shared/src/api/useUserApi.ts create mode 100644 libs/client/shared/src/api/useValuationApi.ts create mode 100644 libs/client/shared/src/components/cards/MaybeCard.tsx create mode 100644 libs/client/shared/src/components/cards/MaybeCardShareModal.tsx create mode 100644 libs/client/shared/src/components/cards/index.ts create mode 100644 libs/client/shared/src/components/charts/index.ts create mode 100644 libs/client/shared/src/components/charts/time-series/AxisBottom.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/AxisLeft.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/BaseChart.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/Chart.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/DefaultTooltip.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/FloatingIcon.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/Line.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/LineRange.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/LoadingChart.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/MultiColorGradient.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/PlusCircleGlyph.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/ZeroPointGradient.tsx create mode 100644 libs/client/shared/src/components/charts/time-series/colorScales.ts create mode 100644 libs/client/shared/src/components/charts/time-series/index.ts create mode 100644 libs/client/shared/src/components/charts/time-series/types.ts create mode 100644 libs/client/shared/src/components/charts/time-series/useSeries.ts create mode 100644 libs/client/shared/src/components/charts/time-series/useTooltip.ts create mode 100644 libs/client/shared/src/components/chat/Attachment.tsx create mode 100644 libs/client/shared/src/components/chat/AudioPlayer.tsx create mode 100644 libs/client/shared/src/components/chat/AudioVisualizer.tsx create mode 100644 libs/client/shared/src/components/chat/MessageInput.tsx create mode 100644 libs/client/shared/src/components/chat/RichText.tsx create mode 100644 libs/client/shared/src/components/chat/UploadFile.tsx create mode 100644 libs/client/shared/src/components/chat/VideoPlayer.tsx create mode 100644 libs/client/shared/src/components/chat/index.ts create mode 100644 libs/client/shared/src/components/dialogs/ConfirmDialog.tsx create mode 100644 libs/client/shared/src/components/dialogs/FeedbackDialog.tsx create mode 100644 libs/client/shared/src/components/dialogs/NonUSDDialog.tsx create mode 100644 libs/client/shared/src/components/dialogs/index.ts create mode 100644 libs/client/shared/src/components/explainers/ExplainerExternalLink.tsx create mode 100644 libs/client/shared/src/components/explainers/ExplainerInfoBlock.tsx create mode 100644 libs/client/shared/src/components/explainers/ExplainerPerformanceBlock.tsx create mode 100644 libs/client/shared/src/components/explainers/ExplainerSection.tsx create mode 100644 libs/client/shared/src/components/explainers/index.ts create mode 100644 libs/client/shared/src/components/generic/BoxIcon.tsx create mode 100644 libs/client/shared/src/components/generic/Confetti.tsx create mode 100644 libs/client/shared/src/components/generic/FeedbackButton.tsx create mode 100644 libs/client/shared/src/components/generic/InfiniteScroll.tsx create mode 100644 libs/client/shared/src/components/generic/InsightGroup.tsx create mode 100644 libs/client/shared/src/components/generic/InsightPopout.tsx create mode 100644 libs/client/shared/src/components/generic/ProfileCircle.tsx create mode 100644 libs/client/shared/src/components/generic/RelativeTime.tsx create mode 100644 libs/client/shared/src/components/generic/TakeoverBackground.tsx create mode 100644 libs/client/shared/src/components/generic/Toaster.tsx create mode 100644 libs/client/shared/src/components/generic/TrendBadge.tsx create mode 100644 libs/client/shared/src/components/generic/index.ts create mode 100644 libs/client/shared/src/components/generic/small-decimals/SmallDecimals.test.tsx create mode 100644 libs/client/shared/src/components/generic/small-decimals/SmallDecimals.tsx create mode 100644 libs/client/shared/src/components/generic/small-decimals/index.ts create mode 100644 libs/client/shared/src/components/index.ts create mode 100644 libs/client/shared/src/components/loaders/MainContentLoader.tsx create mode 100644 libs/client/shared/src/components/loaders/index.ts create mode 100644 libs/client/shared/src/components/overlays/BlurredContentOverlay.tsx create mode 100644 libs/client/shared/src/components/overlays/ErrorFallbackOverlay.tsx create mode 100644 libs/client/shared/src/components/overlays/MainContentOverlay.tsx create mode 100644 libs/client/shared/src/components/overlays/Overlay.tsx create mode 100644 libs/client/shared/src/components/overlays/index.ts create mode 100644 libs/client/shared/src/components/tables/data-table/DataTable.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/DefaultCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/EditableBooleanCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/EditableCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/EditableDateCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/EditableDropdownCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/EditableStringCell.tsx create mode 100644 libs/client/shared/src/components/tables/data-table/index.ts create mode 100644 libs/client/shared/src/components/tables/data-table/types.ts create mode 100644 libs/client/shared/src/components/tables/index.ts create mode 100644 libs/client/shared/src/hooks/index.ts create mode 100644 libs/client/shared/src/hooks/useAccountNotifications.ts create mode 100644 libs/client/shared/src/hooks/useAxiosWithAuth.ts create mode 100644 libs/client/shared/src/hooks/useDebounce.ts create mode 100644 libs/client/shared/src/hooks/useFinicity.ts create mode 100644 libs/client/shared/src/hooks/useFrame.ts create mode 100644 libs/client/shared/src/hooks/useIntercom.ts create mode 100644 libs/client/shared/src/hooks/useInterval.ts create mode 100644 libs/client/shared/src/hooks/useLastUpdated.ts create mode 100644 libs/client/shared/src/hooks/useLocalStorage.ts create mode 100644 libs/client/shared/src/hooks/useLogger.ts create mode 100644 libs/client/shared/src/hooks/useModalManager.ts create mode 100644 libs/client/shared/src/hooks/usePlaid.ts create mode 100644 libs/client/shared/src/hooks/useProviderStatus.ts create mode 100644 libs/client/shared/src/hooks/useQueryParam.ts create mode 100644 libs/client/shared/src/hooks/useScreenSize.ts create mode 100644 libs/client/shared/src/index.ts create mode 100644 libs/client/shared/src/providers/AccountContextProvider.tsx create mode 100644 libs/client/shared/src/providers/AuthProvider.tsx create mode 100644 libs/client/shared/src/providers/AxiosProvider.tsx create mode 100644 libs/client/shared/src/providers/LayoutContextProvider.tsx create mode 100644 libs/client/shared/src/providers/LogProvider.tsx create mode 100644 libs/client/shared/src/providers/PopoutProvider.tsx create mode 100644 libs/client/shared/src/providers/QueryProvider.tsx create mode 100644 libs/client/shared/src/providers/UserAccountContextProvider.tsx create mode 100644 libs/client/shared/src/providers/index.ts create mode 100644 libs/client/shared/src/types/client-side-feature-flags.ts create mode 100644 libs/client/shared/src/types/index.ts create mode 100644 libs/client/shared/src/types/react-types.ts create mode 100644 libs/client/shared/src/utils/account-utils.ts create mode 100644 libs/client/shared/src/utils/agreement-utils.ts create mode 100644 libs/client/shared/src/utils/auth-utils.ts create mode 100644 libs/client/shared/src/utils/browser-utils.ts create mode 100644 libs/client/shared/src/utils/form-utils.ts create mode 100644 libs/client/shared/src/utils/image-loaders.ts create mode 100644 libs/client/shared/src/utils/index.ts create mode 100644 libs/client/shared/src/utils/intercom.ts create mode 100644 libs/client/shared/tsconfig.json create mode 100644 libs/client/shared/tsconfig.lib.json create mode 100644 libs/client/shared/tsconfig.spec.json create mode 100644 libs/design-system/.babelrc create mode 100644 libs/design-system/.eslintrc.json create mode 100644 libs/design-system/.storybook/main.js create mode 100644 libs/design-system/.storybook/manager.js create mode 100644 libs/design-system/.storybook/preview.js create mode 100644 libs/design-system/.storybook/public/favicon.ico create mode 100644 libs/design-system/.storybook/theme.js create mode 100644 libs/design-system/.storybook/tsconfig.json create mode 100644 libs/design-system/README.md create mode 100644 libs/design-system/assets/fonts/inter/Inter-Variable.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Black.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Black.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Black.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Black.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Bold.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Bold.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Bold.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Bold.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Book.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Book.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Light.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Light.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Medium.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Medium.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Medium.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Medium.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Regular.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Regular.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Regular.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Regular.woff2 create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Thin.eot create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Thin.ttf create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff create mode 100644 libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff2 create mode 100644 libs/design-system/assets/logo.svg create mode 100644 libs/design-system/assets/styles.css create mode 100644 libs/design-system/docs/Getting Started/About.stories.mdx create mode 100644 libs/design-system/docs/Getting Started/Colors.stories.mdx create mode 100644 libs/design-system/docs/Getting Started/Typography.stories.mdx create mode 100644 libs/design-system/docs/util/Swatch.tsx create mode 100644 libs/design-system/docs/util/SwatchGroup.tsx create mode 100644 libs/design-system/jest.config.ts create mode 100644 libs/design-system/jest.setup.js create mode 100644 libs/design-system/package.json create mode 100644 libs/design-system/postcss.config.js create mode 100644 libs/design-system/src/index.ts create mode 100644 libs/design-system/src/lib/AccordionRow/AccordionRow.spec.tsx create mode 100644 libs/design-system/src/lib/AccordionRow/AccordionRow.stories.tsx create mode 100644 libs/design-system/src/lib/AccordionRow/AccordionRow.tsx create mode 100644 libs/design-system/src/lib/AccordionRow/__snapshots__/AccordionRow.spec.tsx.snap create mode 100644 libs/design-system/src/lib/AccordionRow/index.ts create mode 100644 libs/design-system/src/lib/Alert/Alert.stories.tsx create mode 100644 libs/design-system/src/lib/Alert/Alert.tsx create mode 100644 libs/design-system/src/lib/Alert/index.ts create mode 100644 libs/design-system/src/lib/Badge/Badge.spec.tsx create mode 100644 libs/design-system/src/lib/Badge/Badge.stories.tsx create mode 100644 libs/design-system/src/lib/Badge/Badge.tsx create mode 100644 libs/design-system/src/lib/Badge/__snapshots__/Badge.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Badge/index.ts create mode 100644 libs/design-system/src/lib/Breadcrumb/Breadcrumb.spec.tsx create mode 100644 libs/design-system/src/lib/Breadcrumb/Breadcrumb.stories.tsx create mode 100644 libs/design-system/src/lib/Breadcrumb/Breadcrumb.tsx create mode 100644 libs/design-system/src/lib/Breadcrumb/__snapshots__/Breadcrumb.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Breadcrumb/index.ts create mode 100644 libs/design-system/src/lib/Button/Button.spec.tsx create mode 100644 libs/design-system/src/lib/Button/Button.stories.tsx create mode 100644 libs/design-system/src/lib/Button/Button.tsx create mode 100644 libs/design-system/src/lib/Button/__snapshots__/Button.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Button/index.ts create mode 100644 libs/design-system/src/lib/Checkbox/Checkbox.spec.tsx create mode 100644 libs/design-system/src/lib/Checkbox/Checkbox.stories.tsx create mode 100644 libs/design-system/src/lib/Checkbox/Checkbox.tsx create mode 100644 libs/design-system/src/lib/Checkbox/__snapshots__/Checkbox.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Checkbox/index.ts create mode 100644 libs/design-system/src/lib/DatePicker/DatePicker.spec.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePicker.stories.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePicker.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerCalendar.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerInput.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerMonth.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerQuickSelect.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.spec.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.stories.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeButton.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeCalendar.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeTabs.tsx create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/__snapshots__/DatePickerRange.spec.tsx.snap create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerRange/index.ts create mode 100644 libs/design-system/src/lib/DatePicker/DatePickerYear.tsx create mode 100644 libs/design-system/src/lib/DatePicker/__snapshots__/DatePicker.spec.tsx.snap create mode 100644 libs/design-system/src/lib/DatePicker/index.ts create mode 100644 libs/design-system/src/lib/DatePicker/selectableRanges.ts create mode 100644 libs/design-system/src/lib/DatePicker/utils.spec.tsx create mode 100644 libs/design-system/src/lib/DatePicker/utils.tsx create mode 100644 libs/design-system/src/lib/Dialog/Dialog.spec.tsx create mode 100644 libs/design-system/src/lib/Dialog/Dialog.stories.tsx create mode 100644 libs/design-system/src/lib/Dialog/Dialog.tsx create mode 100644 libs/design-system/src/lib/Dialog/DialogV2.tsx create mode 100644 libs/design-system/src/lib/Dialog/__snapshots__/Dialog.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Dialog/index.ts create mode 100644 libs/design-system/src/lib/FormGroup/FormGroup.spec.tsx create mode 100644 libs/design-system/src/lib/FormGroup/FormGroup.tsx create mode 100644 libs/design-system/src/lib/FormGroup/__snapshots__/FormGroup.spec.tsx.snap create mode 100644 libs/design-system/src/lib/FormGroup/index.ts create mode 100644 libs/design-system/src/lib/FractionalCircle/FractionalCircle.stories.tsx create mode 100644 libs/design-system/src/lib/FractionalCircle/FractionalCircle.tsx create mode 100644 libs/design-system/src/lib/FractionalCircle/index.ts create mode 100644 libs/design-system/src/lib/IndexTabs/IndexTabs.spec.tsx create mode 100644 libs/design-system/src/lib/IndexTabs/IndexTabs.stories.tsx create mode 100644 libs/design-system/src/lib/IndexTabs/IndexTabs.tsx create mode 100644 libs/design-system/src/lib/IndexTabs/__snapshots__/IndexTabs.spec.tsx.snap create mode 100644 libs/design-system/src/lib/IndexTabs/index.ts create mode 100644 libs/design-system/src/lib/Listbox/Listbox.spec.tsx create mode 100644 libs/design-system/src/lib/Listbox/Listbox.stories.tsx create mode 100644 libs/design-system/src/lib/Listbox/Listbox.tsx create mode 100644 libs/design-system/src/lib/Listbox/__snapshots__/Listbox.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Listbox/index.ts create mode 100644 libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.spec.tsx create mode 100644 libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.stories.tsx create mode 100644 libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.tsx create mode 100644 libs/design-system/src/lib/LoadingPlaceholder/__snapshots__/LoadingPlaceholder.spec.tsx.snap create mode 100644 libs/design-system/src/lib/LoadingPlaceholder/index.ts create mode 100644 libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.spec.tsx create mode 100644 libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.stories.tsx create mode 100644 libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.tsx create mode 100644 libs/design-system/src/lib/LoadingSpinner/__snapshots__/LoadingSpinner.spec.tsx.snap create mode 100644 libs/design-system/src/lib/LoadingSpinner/index.ts create mode 100644 libs/design-system/src/lib/Menu/Menu.spec.tsx create mode 100644 libs/design-system/src/lib/Menu/Menu.stories.tsx create mode 100644 libs/design-system/src/lib/Menu/Menu.tsx create mode 100644 libs/design-system/src/lib/Menu/__snapshots__/Menu.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Menu/index.ts create mode 100644 libs/design-system/src/lib/Popover/Popover.spec.tsx create mode 100644 libs/design-system/src/lib/Popover/Popover.stories.tsx create mode 100644 libs/design-system/src/lib/Popover/Popover.tsx create mode 100644 libs/design-system/src/lib/Popover/__snapshots__/Popover.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Popover/index.ts create mode 100644 libs/design-system/src/lib/RTEditor/RTEditor.tsx create mode 100644 libs/design-system/src/lib/RTEditor/index.ts create mode 100644 libs/design-system/src/lib/RadioGroup/RadioGroup.stories.tsx create mode 100644 libs/design-system/src/lib/RadioGroup/RadioGroup.tsx create mode 100644 libs/design-system/src/lib/RadioGroup/index.ts create mode 100644 libs/design-system/src/lib/Slider/Slider.spec.tsx create mode 100644 libs/design-system/src/lib/Slider/Slider.stories.tsx create mode 100644 libs/design-system/src/lib/Slider/Slider.tsx create mode 100644 libs/design-system/src/lib/Slider/__snapshots__/Slider.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Slider/index.ts create mode 100644 libs/design-system/src/lib/Step/Step.spec.tsx create mode 100644 libs/design-system/src/lib/Step/Step.stories.tsx create mode 100644 libs/design-system/src/lib/Step/Step.tsx create mode 100644 libs/design-system/src/lib/Step/__snapshots__/Step.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Step/index.ts create mode 100644 libs/design-system/src/lib/Tab/Tab.spec.tsx create mode 100644 libs/design-system/src/lib/Tab/Tab.stories.tsx create mode 100644 libs/design-system/src/lib/Tab/Tab.tsx create mode 100644 libs/design-system/src/lib/Tab/__snapshots__/Tab.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Tab/index.ts create mode 100644 libs/design-system/src/lib/Takeover/Takeover.spec.tsx create mode 100644 libs/design-system/src/lib/Takeover/Takeover.stories.tsx create mode 100644 libs/design-system/src/lib/Takeover/Takeover.tsx create mode 100644 libs/design-system/src/lib/Takeover/__snapshots__/Takeover.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Takeover/index.ts create mode 100644 libs/design-system/src/lib/Toast/Toast.spec.tsx create mode 100644 libs/design-system/src/lib/Toast/Toast.stories.tsx create mode 100644 libs/design-system/src/lib/Toast/Toast.tsx create mode 100644 libs/design-system/src/lib/Toast/__snapshots__/Toast.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Toast/index.ts create mode 100644 libs/design-system/src/lib/Toggle/Toggle.spec.tsx create mode 100644 libs/design-system/src/lib/Toggle/Toggle.stories.tsx create mode 100644 libs/design-system/src/lib/Toggle/Toggle.tsx create mode 100644 libs/design-system/src/lib/Toggle/__snapshots__/Toggle.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Toggle/index.ts create mode 100644 libs/design-system/src/lib/Tooltip/Tooltip.spec.tsx create mode 100644 libs/design-system/src/lib/Tooltip/Tooltip.stories.tsx create mode 100644 libs/design-system/src/lib/Tooltip/Tooltip.tsx create mode 100644 libs/design-system/src/lib/Tooltip/__snapshots__/Tooltip.spec.tsx.snap create mode 100644 libs/design-system/src/lib/Tooltip/index.ts create mode 100644 libs/design-system/src/lib/TrendLine/TrendLine.stories.tsx create mode 100644 libs/design-system/src/lib/TrendLine/TrendLine.tsx create mode 100644 libs/design-system/src/lib/TrendLine/Trendline.spec.tsx create mode 100644 libs/design-system/src/lib/TrendLine/__snapshots__/Trendline.spec.tsx.snap create mode 100644 libs/design-system/src/lib/TrendLine/index.ts create mode 100644 libs/design-system/src/lib/inputs/Input/Input.spec.tsx create mode 100644 libs/design-system/src/lib/inputs/Input/Input.stories.tsx create mode 100644 libs/design-system/src/lib/inputs/Input/Input.tsx create mode 100644 libs/design-system/src/lib/inputs/Input/__snapshots__/Input.spec.tsx.snap create mode 100644 libs/design-system/src/lib/inputs/Input/index.ts create mode 100644 libs/design-system/src/lib/inputs/InputColorHint/InputColorHint.tsx create mode 100644 libs/design-system/src/lib/inputs/InputColorHint/index.ts create mode 100644 libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.spec.tsx create mode 100644 libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.stories.tsx create mode 100644 libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.tsx create mode 100644 libs/design-system/src/lib/inputs/InputCurrency/__snapshots__/InputCurrency.spec.tsx.snap create mode 100644 libs/design-system/src/lib/inputs/InputCurrency/index.ts create mode 100644 libs/design-system/src/lib/inputs/InputHint/InputHint.tsx create mode 100644 libs/design-system/src/lib/inputs/InputHint/index.ts create mode 100644 libs/design-system/src/lib/inputs/InputPassword/InputPassword.spec.tsx create mode 100644 libs/design-system/src/lib/inputs/InputPassword/InputPassword.stories.tsx create mode 100644 libs/design-system/src/lib/inputs/InputPassword/InputPassword.tsx create mode 100644 libs/design-system/src/lib/inputs/InputPassword/__snapshots__/InputPassword.spec.tsx.snap create mode 100644 libs/design-system/src/lib/inputs/InputPassword/index.ts create mode 100644 libs/design-system/src/lib/inputs/index.ts create mode 100644 libs/design-system/tailwind.config.js create mode 100644 libs/design-system/tsconfig.json create mode 100644 libs/design-system/tsconfig.lib.json create mode 100644 libs/design-system/tsconfig.spec.json create mode 100644 libs/finicity-api/.babelrc create mode 100644 libs/finicity-api/.eslintrc.json create mode 100644 libs/finicity-api/README.md create mode 100644 libs/finicity-api/jest.config.ts create mode 100644 libs/finicity-api/src/finicity-api.ts create mode 100644 libs/finicity-api/src/index.ts create mode 100644 libs/finicity-api/src/types/accounts.ts create mode 100644 libs/finicity-api/src/types/authentication.ts create mode 100644 libs/finicity-api/src/types/connect.ts create mode 100644 libs/finicity-api/src/types/customers.ts create mode 100644 libs/finicity-api/src/types/index.ts create mode 100644 libs/finicity-api/src/types/institutions.ts create mode 100644 libs/finicity-api/src/types/transactions.ts create mode 100644 libs/finicity-api/src/types/txpush.ts create mode 100644 libs/finicity-api/src/types/webhooks.ts create mode 100644 libs/finicity-api/tsconfig.json create mode 100644 libs/finicity-api/tsconfig.lib.json create mode 100644 libs/finicity-api/tsconfig.spec.json create mode 100644 libs/server/features/.babelrc create mode 100644 libs/server/features/.eslintrc.json create mode 100644 libs/server/features/README.md create mode 100644 libs/server/features/jest.config.ts create mode 100644 libs/server/features/src/account-balance/balance-sync.strategy.ts create mode 100644 libs/server/features/src/account-balance/index.ts create mode 100644 libs/server/features/src/account-balance/investment-transaction-balance-sync.strategy.ts create mode 100644 libs/server/features/src/account-balance/loan-balance-sync.strategy.ts create mode 100644 libs/server/features/src/account-balance/transaction-balance-sync.strategy.ts create mode 100644 libs/server/features/src/account-balance/valuation-balance-sync.strategy.ts create mode 100644 libs/server/features/src/account-connection/account-connection.processor.ts create mode 100644 libs/server/features/src/account-connection/account-connection.provider.ts create mode 100644 libs/server/features/src/account-connection/account-connection.service.ts create mode 100644 libs/server/features/src/account-connection/index.ts create mode 100644 libs/server/features/src/account/account-query.service.ts create mode 100644 libs/server/features/src/account/account.processor.ts create mode 100644 libs/server/features/src/account/account.provider.ts create mode 100644 libs/server/features/src/account/account.schema.ts create mode 100644 libs/server/features/src/account/account.service.ts create mode 100644 libs/server/features/src/account/index.ts create mode 100644 libs/server/features/src/account/insight.service.ts create mode 100644 libs/server/features/src/conversation/conversation.schema.ts create mode 100644 libs/server/features/src/conversation/conversation.service.ts create mode 100644 libs/server/features/src/conversation/index.ts create mode 100644 libs/server/features/src/conversation/message.schema.ts create mode 100644 libs/server/features/src/conversation/message.service.ts create mode 100644 libs/server/features/src/conversation/sandbox.ts create mode 100644 libs/server/features/src/email/email.processor.ts create mode 100644 libs/server/features/src/email/email.schema.ts create mode 100644 libs/server/features/src/email/email.service.ts create mode 100644 libs/server/features/src/email/index.ts create mode 100644 libs/server/features/src/holding/holding.schema.ts create mode 100644 libs/server/features/src/holding/holding.service.ts create mode 100644 libs/server/features/src/holding/index.ts create mode 100644 libs/server/features/src/index.ts create mode 100644 libs/server/features/src/institution/index.ts create mode 100644 libs/server/features/src/institution/institution.provider.ts create mode 100644 libs/server/features/src/institution/institution.service.ts create mode 100644 libs/server/features/src/investment-transaction/index.ts create mode 100644 libs/server/features/src/investment-transaction/investment-transaction.schema.ts create mode 100644 libs/server/features/src/plan/index.ts create mode 100644 libs/server/features/src/plan/plan.schema.ts create mode 100644 libs/server/features/src/plan/plan.service.ts create mode 100644 libs/server/features/src/plan/projection/index.ts create mode 100644 libs/server/features/src/plan/projection/monte-carlo.spec.ts create mode 100644 libs/server/features/src/plan/projection/monte-carlo.ts create mode 100644 libs/server/features/src/plan/projection/projection-calculator.spec.ts create mode 100644 libs/server/features/src/plan/projection/projection-calculator.ts create mode 100644 libs/server/features/src/plan/projection/projection-value.spec.ts create mode 100644 libs/server/features/src/plan/projection/projection-value.ts create mode 100644 libs/server/features/src/providers/finicity/finicity.etl.ts create mode 100644 libs/server/features/src/providers/finicity/finicity.service.ts create mode 100644 libs/server/features/src/providers/finicity/finicity.webhook.ts create mode 100644 libs/server/features/src/providers/finicity/index.ts create mode 100644 libs/server/features/src/providers/index.ts create mode 100644 libs/server/features/src/providers/plaid/index.ts create mode 100644 libs/server/features/src/providers/plaid/plaid.etl.spec.ts create mode 100644 libs/server/features/src/providers/plaid/plaid.etl.ts create mode 100644 libs/server/features/src/providers/plaid/plaid.service.ts create mode 100644 libs/server/features/src/providers/plaid/plaid.webhook.ts create mode 100644 libs/server/features/src/providers/property/index.ts create mode 100644 libs/server/features/src/providers/property/property.service.ts create mode 100644 libs/server/features/src/providers/vehicle/index.ts create mode 100644 libs/server/features/src/providers/vehicle/vehicle.service.ts create mode 100644 libs/server/features/src/security-pricing/index.ts create mode 100644 libs/server/features/src/security-pricing/security-pricing.processor.ts create mode 100644 libs/server/features/src/security-pricing/security-pricing.service.ts create mode 100644 libs/server/features/src/stripe/index.ts create mode 100644 libs/server/features/src/stripe/stripe.webhook.ts create mode 100644 libs/server/features/src/transaction/index.ts create mode 100644 libs/server/features/src/transaction/transaction.schema.ts create mode 100644 libs/server/features/src/transaction/transaction.service.ts create mode 100644 libs/server/features/src/user/index.ts create mode 100644 libs/server/features/src/user/onboarding.schema.ts create mode 100644 libs/server/features/src/user/onboarding.service.ts create mode 100644 libs/server/features/src/user/user.processor.ts create mode 100644 libs/server/features/src/user/user.service.ts create mode 100644 libs/server/features/src/valuation/index.ts create mode 100644 libs/server/features/src/valuation/valuation.service.ts create mode 100644 libs/server/features/tsconfig.json create mode 100644 libs/server/features/tsconfig.lib.json create mode 100644 libs/server/features/tsconfig.spec.json create mode 100644 libs/server/shared/.babelrc create mode 100644 libs/server/shared/.eslintrc.json create mode 100644 libs/server/shared/README.md create mode 100644 libs/server/shared/jest.config.ts create mode 100644 libs/server/shared/src/endpoint.ts create mode 100644 libs/server/shared/src/etl.ts create mode 100644 libs/server/shared/src/index.ts create mode 100644 libs/server/shared/src/logger.ts create mode 100644 libs/server/shared/src/services/cache.service.ts create mode 100644 libs/server/shared/src/services/crypto.service.ts create mode 100644 libs/server/shared/src/services/feature-flag.service.ts create mode 100644 libs/server/shared/src/services/index.ts create mode 100644 libs/server/shared/src/services/market-data.service.spec.ts create mode 100644 libs/server/shared/src/services/market-data.service.ts create mode 100644 libs/server/shared/src/services/pg.service.ts create mode 100644 libs/server/shared/src/services/queue.service.ts create mode 100644 libs/server/shared/src/services/queue/bull-queue.ts create mode 100644 libs/server/shared/src/services/queue/in-memory-queue.ts create mode 100644 libs/server/shared/src/services/queue/index.ts create mode 100644 libs/server/shared/src/services/s3.service.ts create mode 100644 libs/server/shared/src/sql-template-tag.ts create mode 100644 libs/server/shared/src/utils/auth-utils.ts create mode 100644 libs/server/shared/src/utils/db-utils.ts create mode 100644 libs/server/shared/src/utils/error-utils.ts create mode 100644 libs/server/shared/src/utils/finicity-utils.ts create mode 100644 libs/server/shared/src/utils/index.ts create mode 100644 libs/server/shared/src/utils/plaid-utils.spec.ts create mode 100644 libs/server/shared/src/utils/plaid-utils.ts create mode 100644 libs/server/shared/src/utils/server-utils.ts create mode 100644 libs/server/shared/tsconfig.json create mode 100644 libs/server/shared/tsconfig.lib.json create mode 100644 libs/server/shared/tsconfig.spec.json create mode 100644 libs/shared/.babelrc create mode 100644 libs/shared/.eslintrc.json create mode 100644 libs/shared/README.md create mode 100644 libs/shared/jest.config.ts create mode 100644 libs/shared/src/index.ts create mode 100644 libs/shared/src/superjson.spec.ts create mode 100644 libs/shared/src/superjson.ts create mode 100644 libs/shared/src/types/account-types.ts create mode 100644 libs/shared/src/types/api-types.ts create mode 100644 libs/shared/src/types/conversation-types.ts create mode 100644 libs/shared/src/types/email-types.ts create mode 100644 libs/shared/src/types/general-types.ts create mode 100644 libs/shared/src/types/holding-types.ts create mode 100644 libs/shared/src/types/index.ts create mode 100644 libs/shared/src/types/institution-types.ts create mode 100644 libs/shared/src/types/investment-transaction-types.ts create mode 100644 libs/shared/src/types/plan-types.ts create mode 100644 libs/shared/src/types/security-types.ts create mode 100644 libs/shared/src/types/transaction-types.ts create mode 100644 libs/shared/src/types/user-types.ts create mode 100644 libs/shared/src/utils/account-utils.ts create mode 100644 libs/shared/src/utils/ata-utils.ts create mode 100644 libs/shared/src/utils/date-utils.spec.ts create mode 100644 libs/shared/src/utils/date-utils.ts create mode 100644 libs/shared/src/utils/geo-utils.ts create mode 100644 libs/shared/src/utils/index.ts create mode 100644 libs/shared/src/utils/market-utils.ts create mode 100644 libs/shared/src/utils/number-utils.spec.ts create mode 100644 libs/shared/src/utils/number-utils.ts create mode 100644 libs/shared/src/utils/plan-utils.ts create mode 100644 libs/shared/src/utils/shared-utils.spec.ts create mode 100644 libs/shared/src/utils/shared-utils.ts create mode 100644 libs/shared/src/utils/stats-utils.spec.ts create mode 100644 libs/shared/src/utils/stats-utils.ts create mode 100644 libs/shared/src/utils/test-utils.ts create mode 100644 libs/shared/src/utils/transaction-utils.ts create mode 100644 libs/shared/src/utils/user-utils.ts create mode 100644 libs/shared/tsconfig.json create mode 100644 libs/shared/tsconfig.lib.json create mode 100644 libs/shared/tsconfig.spec.json create mode 100644 nx.json create mode 100644 package.json create mode 100644 prisma/migrations/20211005200319_init/migration.sql create mode 100644 prisma/migrations/20211019194924_unique_constraint_on_account_balances/migration.sql create mode 100644 prisma/migrations/20211019214200_default_values/migration.sql create mode 100644 prisma/migrations/20211025200206_account_balance_schema_update/migration.sql create mode 100644 prisma/migrations/20211026174357_default_text_type/migration.sql create mode 100644 prisma/migrations/20211026175641_default_values/migration.sql create mode 100644 prisma/migrations/20211102165759_account_status/migration.sql create mode 100644 prisma/migrations/20211102183151_add_account_types_and_subtypes/migration.sql create mode 100644 prisma/migrations/20211104155259_account_uniqueness/migration.sql create mode 100644 prisma/migrations/20211105234550_posted_date_type/migration.sql create mode 100644 prisma/migrations/20211109151750_account_type_seed/migration.sql create mode 100644 prisma/migrations/20211110044559_manual_accounts_rename_fk/migration.sql create mode 100644 prisma/migrations/20211116235652_investment_data/migration.sql create mode 100644 prisma/migrations/20211117190140_add_manual_account_types/migration.sql create mode 100644 prisma/migrations/20211117190719_updated_at_default/migration.sql create mode 100644 prisma/migrations/20211117210112_valuation_date/migration.sql create mode 100644 prisma/migrations/20211117233026_add_date_indices/migration.sql create mode 100644 prisma/migrations/20211118160716_account_balance_update/migration.sql create mode 100644 prisma/migrations/20211118191000_account_balance_timestamps/migration.sql create mode 100644 prisma/migrations/20211118194940_account_functions/migration.sql create mode 100644 prisma/migrations/20211118214727_txn_date_naming/migration.sql create mode 100644 prisma/migrations/20211129155121_connection_status_codes/migration.sql create mode 100644 prisma/migrations/20211130184227_new_accounts_available_flag/migration.sql create mode 100644 prisma/migrations/20211201023540_account_single_table_inheritance/migration.sql create mode 100644 prisma/migrations/20211203180216_security_pricing/migration.sql create mode 100644 prisma/migrations/20211204053810_account_balance_hypertable/migration.sql create mode 100644 prisma/migrations/20211207192726_add_valuation_generated_cols/migration.sql create mode 100644 prisma/migrations/20211208162929_transaction_date/migration.sql create mode 100644 prisma/migrations/20211209041710_remove_initial_txn/migration.sql create mode 100644 prisma/migrations/20211209050532_update_fns/migration.sql create mode 100644 prisma/migrations/20211211140103_add_institution_id_to_connection/migration.sql create mode 100644 prisma/migrations/20211213211517_account_user_index/migration.sql create mode 100644 prisma/migrations/20211214162659_security_pricing_source/migration.sql create mode 100644 prisma/migrations/20211215195518_add_account_start_date/migration.sql create mode 100644 prisma/migrations/20211230035441_account_sync_status/migration.sql create mode 100644 prisma/migrations/20220106215040_add_mask_to_account/migration.sql create mode 100644 prisma/migrations/20220107170334_hypertable_chunk_size_tuning/migration.sql create mode 100644 prisma/migrations/20220112171128_update_fn/migration.sql create mode 100644 prisma/migrations/20220121175453_account_liability_json/migration.sql create mode 100644 prisma/migrations/20220124193549_add_plaid_valuation_valuation_type/migration.sql create mode 100644 prisma/migrations/20220124211317_update_valuation_types_and_sources/migration.sql create mode 100644 prisma/migrations/20220125211038_add_unique_constraint_to_valuations/migration.sql create mode 100644 prisma/migrations/20220202184342_account_balances_gapfilled_fn/migration.sql create mode 100644 prisma/migrations/20220203234737_update_fn/migration.sql create mode 100644 prisma/migrations/20220214175713_narrow_transaction_category/migration.sql create mode 100644 prisma/migrations/20220215201534_transaction_remove_subcategory_add_plaid_category/migration.sql create mode 100644 prisma/migrations/20220215212216_add_transaction_indexes/migration.sql create mode 100644 prisma/migrations/20220217040807_add_merchant_name_to_transactions/migration.sql create mode 100644 prisma/migrations/20220228233043_change_money_type/migration.sql create mode 100644 prisma/migrations/20220302181536_add_price_as_of_to_security_pricing/migration.sql create mode 100644 prisma/migrations/20220307200633_remove_price_from_holding/migration.sql create mode 100644 prisma/migrations/20220307211701_valuation_trigger/migration.sql create mode 100644 prisma/migrations/20220311165323_add_shares_per_contract_to_security/migration.sql create mode 100644 prisma/migrations/20220315172110_institution/migration.sql create mode 100644 prisma/migrations/20220316200652_reset_plaid_derivative_prices/migration.sql create mode 100644 prisma/migrations/20220317191949_reset_plaid_derivative_prices_again/migration.sql create mode 100644 prisma/migrations/20220323203441_multi_provider_updates/migration.sql create mode 100644 prisma/migrations/20220323212807_fix_function/migration.sql create mode 100644 prisma/migrations/20220411193518_stop_generating_and_enumize_account_category/migration.sql create mode 100644 prisma/migrations/20220426190758_add_url_and_logo_url_to_institution/migration.sql create mode 100644 prisma/migrations/20220504231954_finicity_updates/migration.sql create mode 100644 prisma/migrations/20220518005502_finicity_customer_id_uniqueness/migration.sql create mode 100644 prisma/migrations/20220519192445_institution_refactor/migration.sql create mode 100644 prisma/migrations/20220520161223_institution_search_algo/migration.sql create mode 100644 prisma/migrations/20220606160203_add_finicity_username_to_user/migration.sql create mode 100644 prisma/migrations/20220607162542_add_crisp_session_token_to_user/migration.sql create mode 100644 prisma/migrations/20220608171009_add_success_rate_and_oauth_to_provider_institutions/migration.sql create mode 100644 prisma/migrations/20220608190342_add_unique_constraint_to_institution/migration.sql create mode 100644 prisma/migrations/20220608202739_add_success_rate_updated_to_provider_institution/migration.sql create mode 100644 prisma/migrations/20220609195136_remove_success_rate_from_provider_institution/migration.sql create mode 100644 prisma/migrations/20220622160129_add_finicity_error/migration.sql create mode 100644 prisma/migrations/20220623171212_remove_holding_unique_constraint/migration.sql create mode 100644 prisma/migrations/20220630005107_category_overrides/migration.sql create mode 100644 prisma/migrations/20220701013813_merge_updates/migration.sql create mode 100644 prisma/migrations/20220707195013_user_overrides/migration.sql create mode 100644 prisma/migrations/20220708191740_txn_excluded_flag/migration.sql create mode 100644 prisma/migrations/20220713134742_add_provider_field/migration.sql create mode 100644 prisma/migrations/20220714180514_update_account_start_date_fn/migration.sql create mode 100644 prisma/migrations/20220714180819_account_category_consolidation/migration.sql create mode 100644 prisma/migrations/20220714181018_update_account_type_model/migration.sql create mode 100644 prisma/migrations/20220715191415_add_liability_fields/migration.sql create mode 100644 prisma/migrations/20220719200317_plaid_txn_category/migration.sql create mode 100644 prisma/migrations/20220720191551_generated_loan_credit_fields/migration.sql create mode 100644 prisma/migrations/20220725143246_map_credit_loan_data/migration.sql create mode 100644 prisma/migrations/20220726003918_reset_loan_account_balances/migration.sql create mode 100644 prisma/migrations/20220727145316_loan_credit_json_nullable/migration.sql create mode 100644 prisma/migrations/20220727202956_loan_account_start_date/migration.sql create mode 100644 prisma/migrations/20220729012630_security_fields/migration.sql create mode 100644 prisma/migrations/20220729202323_txn_updates/migration.sql create mode 100644 prisma/migrations/20220804180126_holdings_view/migration.sql create mode 100644 prisma/migrations/20220804191558_add_excluded_to_holding/migration.sql create mode 100644 prisma/migrations/20220808171116_investment_txn_fees/migration.sql create mode 100644 prisma/migrations/20220808174032_update_holdings_view/migration.sql create mode 100644 prisma/migrations/20220810190306_transaction_category_update/migration.sql create mode 100644 prisma/migrations/20220817180833_dietz/migration.sql create mode 100644 prisma/migrations/20220819151658_add_investment_transaction_category/migration.sql create mode 100644 prisma/migrations/20220915200544_add_plans/migration.sql create mode 100644 prisma/migrations/20220919203059_make_dob_optional/migration.sql create mode 100644 prisma/migrations/20220929161359_remove_crisp_session_token/migration.sql create mode 100644 prisma/migrations/20221004193621_security_brokerage_cash_flag/migration.sql create mode 100644 prisma/migrations/20221007143103_dietz_div0_fix/migration.sql create mode 100644 prisma/migrations/20221017145454_plan_events_milestones/migration.sql create mode 100644 prisma/migrations/20221021162836_remove_dob_from_plan/migration.sql create mode 100644 prisma/migrations/20221024203133_plan_event_milestone_category/migration.sql create mode 100644 prisma/migrations/20221027180912_cascade_plan_milestone_deletion/migration.sql create mode 100644 prisma/migrations/20221109192536_add_stripe_fields/migration.sql create mode 100644 prisma/migrations/20221111192223_ata/migration.sql create mode 100644 prisma/migrations/20221115201138_advisor_approval_status/migration.sql create mode 100644 prisma/migrations/20221117150434_update_advisor_profile/migration.sql create mode 100644 prisma/migrations/20221117213140_add_stripe_trial_reminder_sent/migration.sql create mode 100644 prisma/migrations/20221121214349_add_user_goals/migration.sql create mode 100644 prisma/migrations/20221129201601_conversation_advisor_unique_key/migration.sql create mode 100644 prisma/migrations/20221202213727_notification_preferences/migration.sql create mode 100644 prisma/migrations/20221206153642_conversation_user_required/migration.sql create mode 100644 prisma/migrations/20221207235557_expiry_email_sent/migration.sql create mode 100644 prisma/migrations/20221209041210_user_advisor_notes/migration.sql create mode 100644 prisma/migrations/20221212164355_update_risk_data_type/migration.sql create mode 100644 prisma/migrations/20221214145140_add_audit_table_and_trigger/migration.sql create mode 100644 prisma/migrations/20221222200240_add_onboarding_profile_fields/migration.sql create mode 100644 prisma/migrations/20230105203751_add_maybe_and_title/migration.sql create mode 100644 prisma/migrations/20230105210810_add_member_number/migration.sql create mode 100644 prisma/migrations/20230105221446_user_audit/migration.sql create mode 100644 prisma/migrations/20230106172727_add_user_residence/migration.sql create mode 100644 prisma/migrations/20230106221847_user_profile/migration.sql create mode 100644 prisma/migrations/20230110173017_add_user_member_id/migration.sql create mode 100644 prisma/migrations/20230112163100_add_agreements_table/migration.sql create mode 100644 prisma/migrations/20230113230312_user_email_required/migration.sql create mode 100644 prisma/migrations/20230117131125_update_ama_onboarding/migration.sql create mode 100644 prisma/migrations/20230117150048_user_name/migration.sql create mode 100644 prisma/migrations/20230117192734_update_agreement_types/migration.sql create mode 100644 prisma/migrations/20230119114411_add_onboarding_steps/migration.sql create mode 100644 prisma/migrations/20230123121401_separate_onboarding_flows/migration.sql create mode 100644 prisma/migrations/20230123192138_user_country_state/migration.sql create mode 100644 prisma/migrations/20230126230520_user_deletion/migration.sql create mode 100644 prisma/migrations/20230127003359_store_link_tokens/migration.sql create mode 100644 prisma/migrations/20230130161915_account_value_start_date/migration.sql create mode 100644 prisma/migrations/20230207111117_user_account_linking/migration.sql create mode 100644 prisma/migrations/20230207181233_add_conversation_relations/migration.sql create mode 100644 prisma/migrations/20230207230108_account_balance_strategy/migration.sql create mode 100644 prisma/migrations/20230210163006_add_user_trial_end/migration.sql create mode 100644 prisma/migrations/20230211134603_advisor_crm/migration.sql create mode 100644 prisma/migrations/20230220194746_remove_stripe_trials/migration.sql create mode 100644 prisma/migrations/20230223020847_txn_view/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.ts create mode 100644 redis.Dockerfile create mode 100644 redis.conf create mode 100644 render.yaml create mode 100644 tools/generators/.gitkeep create mode 100644 tools/pages/projections.html create mode 100755 tools/scripts/gen-cloudfront-signing-keys.sh create mode 100755 tools/scripts/gen-secret.sh create mode 100755 tools/scripts/getAffectedApps.sh create mode 100755 tools/scripts/run-production-postgres-tunnel.sh create mode 100755 tools/scripts/run-staging-postgres-tunnel.sh create mode 100755 tools/scripts/runStagingE2ETests.sh create mode 100644 tools/scripts/vercelBuildIgnore.js create mode 100755 tools/scripts/wait-for-it.sh create mode 100644 tools/test-data/finicity/finicityTestData.ts create mode 100644 tools/test-data/finicity/index.ts create mode 100644 tools/test-data/index.ts create mode 100644 tools/test-data/plaid/connection-samples/index.ts create mode 100644 tools/test-data/plaid/connection-samples/wealthfront1.ts create mode 100644 tools/test-data/plaid/index.ts create mode 100644 tools/test-data/plaid/plaidApiResponses.ts create mode 100644 tools/test-data/plaid/plaidTestData.ts create mode 100644 tools/test-data/plaid/stockPrices.ts create mode 100644 tools/test-data/plaid/testDates.ts create mode 100644 tools/test-data/polygon/index.ts create mode 100644 tools/test-data/polygon/snapshots.ts create mode 100644 tools/tsconfig.tools.json create mode 100644 tsconfig.base.json create mode 100644 vercel.json create mode 100644 workspace.json create mode 100644 yarn.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000000..ed413027e44 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +# Ignores everything except dist/ prisma/ apps/ and yarn.lock +# apps/ is needed so that CDK (in /aws/maybe-app) has access to the per-app Dockerfile +* +!apps/ +!dist/ +!prisma/ +!yarn.lock \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..6e87a003da8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# Editor configuration, see http://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.env.example b/.env.example new file mode 100644 index 00000000000..35f6636dc6a --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Used by `prisma` commands +NX_DATABASE_URL=postgresql://maybe:maybe@localhost:5432/maybe_local + +# If using free ngrok account for webhooks +NGROK_AUTH_TOKEN= + +# Required for Auth0 deploy client (see `yarn auth0:deploy` command) +AUTH0_ENV=development +AUTH0_DEPLOY_CLIENT_SECRET= +POSTMARK_SMTP_PASS= + +# If you want to test any code that utilizes the AWS SDK locally, add temporary STAGING credentials (can be retrieved from SSO dashboard) +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_SESSION_TOKEN= \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000000..23cd970ff59 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,95 @@ +{ + "root": true, + "ignorePatterns": ["**/*", "**/*.png"], + "plugins": ["@nrwl/nx", "eslint-plugin-json"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@nrwl/nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allow": [], + "depConstraints": [ + { + "sourceTag": "scope:shared", + "onlyDependOnLibsWithTags": ["scope:shared"] + }, + { + "sourceTag": "scope:app", + "onlyDependOnLibsWithTags": ["*"] + }, + { + "sourceTag": "scope:client-shared", + "onlyDependOnLibsWithTags": ["scope:client-shared", "scope:shared"] + }, + { + "sourceTag": "scope:server-shared", + "onlyDependOnLibsWithTags": ["scope:server-shared", "scope:shared"] + }, + { + "sourceTag": "scope:server", + "onlyDependOnLibsWithTags": [ + "scope:server", + "scope:server-shared", + "scope:shared" + ] + }, + { + "sourceTag": "scope:client", + "onlyDependOnLibsWithTags": [ + "scope:client", + "scope:client-shared", + "scope:shared" + ] + }, + { + "sourceTag": "scope:advisor", + "onlyDependOnLibsWithTags": [ + "scope:advisor", + "scope:client-shared", + "scope:shared" + ] + } + ] + } + ] + } + }, + { + "files": ["*.ts", "*.tsx"], + "extends": ["plugin:@nrwl/nx/typescript"], + "rules": { + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/ban-ts-comment": "off", + "@typescript-eslint/consistent-type-assertions": "error", + "@typescript-eslint/consistent-type-imports": [ + "error", + { "fixStyle": "inline-type-imports" } + ] + } + }, + { + "files": ["*.js", "*.jsx"], + "extends": ["plugin:@nrwl/nx/javascript"], + "rules": {} + }, + { + "files": ["*.tsx", "*.jsx"], + "rules": { + "jsx-a11y/anchor-is-valid": [ + "error", + { + "components": ["Link"], + "specialLink": ["hrefLeft", "hrefRight"], + "aspects": ["invalidHref", "preferButton"] + } + ] + } + } + ] +} diff --git a/.github/workflows/deploy-auth0.yml b/.github/workflows/deploy-auth0.yml new file mode 100644 index 00000000000..339f5e4f91b --- /dev/null +++ b/.github/workflows/deploy-auth0.yml @@ -0,0 +1,23 @@ +# Deploys the configuration stored in /auth0/tenant.yaml +name: Deploy Auth0 +on: + workflow_dispatch: + inputs: + deploy_env: + required: true + description: The environment to deploy to + type: choice + options: + - staging + - production + default: staging + +jobs: + deploy: + uses: ./.github/workflows/template.auth0-deploy.yml + with: + auth0_env: ${{ github.event.inputs.deploy_env }} + secrets: + client_secret: ${{ (github.event.inputs.deploy_env == 'production' && secrets.PROD_AUTH0_CLIENT_SECRET) || (github.event.inputs.deploy_env == 'staging' && secrets.STAGING_AUTH0_CLIENT_SECRET) }} + postmark_secret: ${{ secrets.POSTMARK_SMTP_PASS }} + apple_secret: ${{ secrets.APPLE_SIGN_IN_SECRET_KEY }} diff --git a/.github/workflows/deploy-services-manual.yml b/.github/workflows/deploy-services-manual.yml new file mode 100644 index 00000000000..4780580c95c --- /dev/null +++ b/.github/workflows/deploy-services-manual.yml @@ -0,0 +1,58 @@ +# This workflow serves two purposes: +# 1. Allows us to deploy a specific PR to staging for testing +# 2. Allows us to re-deploy production off the `main` branch +name: MANUAL | Deploy services +on: + workflow_dispatch: + inputs: + deploy_env: + description: The environment to deploy to + type: choice + options: + - staging + - production + default: staging + required: true + deploy_shared_stack: + description: Deploy shared AWS resources? + type: boolean + required: false + default: true + deploy_server_stack: + description: Deploy server to ECS Fargate? + type: boolean + required: false + default: false + deploy_workers_stack: + description: Deploy Bull workers to ECS Fargate? + type: boolean + required: false + default: false + deploy_tools_stack: + description: Deploy tools stack? (CI/CD resources) + type: boolean + required: false + default: false + deploy_vercel_client: + description: Deploy Vercel client? + type: boolean + required: false + default: false + +concurrency: + group: deployments + cancel-in-progress: false + +jobs: + deploy: + uses: ./.github/workflows/template.deploy-services.yml + # Production can only be deployed from `main` manually, while staging can be deployed from any branch + if: ${{ (github.event.inputs.deploy_env == 'production' && github.ref == 'refs/heads/main') || github.event.inputs.deploy_env == 'staging' }} + with: + deploy_env: ${{ github.event.inputs.deploy_env }} + deploy_shared_stack: ${{ github.event.inputs.deploy_shared_stack == 'true' }} + deploy_server_stack: ${{ github.event.inputs.deploy_server_stack == 'true' }} + deploy_workers_stack: ${{ github.event.inputs.deploy_workers_stack == 'true' }} + deploy_tools_stack: ${{ github.event.inputs.deploy_tools_stack == 'true' }} + deploy_vercel_client: ${{ github.event.inputs.deploy_vercel_client == 'true' }} + secrets: inherit diff --git a/.github/workflows/deploy-services.yml b/.github/workflows/deploy-services.yml new file mode 100644 index 00000000000..794d7ccaa3f --- /dev/null +++ b/.github/workflows/deploy-services.yml @@ -0,0 +1,61 @@ +name: Deploy services + +on: + push: + branches: + - 'main' + +concurrency: + group: deployments + cancel-in-progress: false + +jobs: + get_affected_apps: + name: Get affected apps + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 # NX needs the entire repo history to run affected commands + + - name: Set NX commit SHAs for affected commands + uses: nrwl/nx-set-shas@v2 # derive appropriate SHAs for base and head for `nx affected` commands + + # Only install nx affected command requirements, no need for a full dependency install + - name: Install requirements for nx + run: | + NX_REPO_VERSION=$(node -e "console.log(require('./package.json').devDependencies['@nrwl/workspace'])") + TS_REPO_VERSION=$(node -e "console.log(require('./package.json').devDependencies['typescript'])") + yarn add -D @nrwl/workspace@$NX_REPO_VERSION --prefer-offline + yarn add -D typescript@$TS_VERSION --prefer-offline + ./node_modules/.bin/nx affected:apps --plain + + - name: Set affected outputs + id: set_affected_outputs + shell: bash + run: ./tools/scripts/getAffectedApps.sh + + - name: Verify outputs + run: | + echo "Will deploy client? ${{ steps.set_affected_outputs.outputs.client_affected == 'true' && 'yes' || 'no' }}" + echo "Will deploy server? ${{ steps.set_affected_outputs.outputs.server_affected == 'true' && 'yes' || 'no' }}" + echo "Will deploy workers? ${{ steps.set_affected_outputs.outputs.workers_affected == 'true' && 'yes' || 'no' }}" + + outputs: + should_deploy_client: ${{ steps.set_affected_outputs.outputs.client_affected }} + should_deploy_server: ${{ steps.set_affected_outputs.outputs.server_affected }} + should_deploy_workers: ${{ steps.set_affected_outputs.outputs.workers_affected }} + + deploy_production: + name: Deploy production services + needs: [get_affected_apps] + uses: ./.github/workflows/template.deploy-services.yml + with: + deploy_env: production + deploy_shared_stack: true + deploy_vercel_client: ${{ needs.get_affected_apps.outputs.should_deploy_client == 'true' }} + deploy_server_stack: ${{ needs.get_affected_apps.outputs.should_deploy_server == 'true' }} + deploy_workers_stack: ${{ needs.get_affected_apps.outputs.should_deploy_workers == 'true' }} + secrets: inherit diff --git a/.github/workflows/template.auth0-deploy.yml b/.github/workflows/template.auth0-deploy.yml new file mode 100644 index 00000000000..9b8e188caac --- /dev/null +++ b/.github/workflows/template.auth0-deploy.yml @@ -0,0 +1,37 @@ +# Deploys the configuration stored in /auth0/tenant.yaml +name: TEMPLATE | Deploy Auth0 +on: + workflow_call: + inputs: + auth0_env: + description: Tenant to deploy to. Valid values are `staging | production` + type: string + required: true + secrets: + client_secret: + required: true + postmark_secret: + required: true + apple_secret: + required: true + +concurrency: + group: auth0_deployments + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: '18' + - run: yarn install --frozen-lockfile + - name: Deploy Auth0 resources + env: + AUTH0_DEPLOY_CLIENT_SECRET: ${{ secrets.client_secret }} + POSTMARK_SMTP_PASS: ${{ secrets.postmark_secret }} + APPLE_SIGN_IN_SECRET_KEY: ${{ secrets.apple_secret }} + AUTH0_ENV: ${{ inputs.auth0_env }} + run: yarn auth0:deploy diff --git a/.github/workflows/template.deploy-services.yml b/.github/workflows/template.deploy-services.yml new file mode 100644 index 00000000000..efd58624ed7 --- /dev/null +++ b/.github/workflows/template.deploy-services.yml @@ -0,0 +1,108 @@ +name: TEMPLATE | Deploy services +on: + workflow_call: + inputs: + deploy_env: + description: Environment to deploy to. Valid values are `staging | production` + type: string + required: true + deploy_shared_stack: + type: boolean + required: false + default: true + deploy_server_stack: + type: boolean + required: false + default: false + deploy_workers_stack: + type: boolean + required: false + default: false + deploy_tools_stack: + type: boolean + required: false + default: false + deploy_vercel_client: + type: boolean + required: false + default: false + +jobs: + deploy_services: + name: Deploy services + runs-on: [self-hosted, aws] + + env: + CDK_ENV: ${{ inputs.deploy_env }} # Determines which AWS account resources are deployed to + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: us-west-2 + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Print deploy targets + run: | + echo "Env: ${{ inputs.deploy_env }}" + echo "Shared: ${{ inputs.deploy_shared_stack }}" + echo "Server: ${{ inputs.deploy_server_stack }}" + echo "Workers: ${{ inputs.deploy_workers_stack }}" + echo "Client: ${{ inputs.deploy_vercel_client }}" + echo "Tools: ${{ inputs.deploy_tools_stack }}" + + # If either server/workers need to be built, install node_modules + - name: Install node_modules + if: ${{ inputs.deploy_server_stack || inputs.deploy_workers_stack }} + run: yarn install --frozen-lockfile + + # If server affected, build to /dist + - name: Build server + if: ${{ inputs.deploy_server_stack }} + run: yarn nx run server:build:production + + # If workers affected, build to /dist + - name: Build workers + if: ${{ inputs.deploy_workers_stack }} + run: yarn nx run workers:build:production + + - name: Initialize and test CDK + working-directory: aws/maybe-app + run: yarn install && ./node_modules/.bin/cdk ls && yarn test + + - name: Deploy shared infrastructure + if: ${{ inputs.deploy_shared_stack }} + working-directory: aws/maybe-app + run: ./node_modules/.bin/cdk deploy SharedStack --require-approval never + + - name: Deploy server and workers in parallel + if: ${{ inputs.deploy_server_stack && inputs.deploy_workers_stack }} + working-directory: aws/maybe-app + run: ./node_modules/.bin/cdk deploy ServerStack WorkersStack --concurrency 2 --require-approval never + + - name: Deploy server only + if: ${{ inputs.deploy_server_stack && !inputs.deploy_workers_stack }} + working-directory: aws/maybe-app + run: ./node_modules/.bin/cdk deploy ServerStack --require-approval never + + - name: Deploy workers only + if: ${{ inputs.deploy_workers_stack && !inputs.deploy_server_stack }} + working-directory: aws/maybe-app + run: ./node_modules/.bin/cdk deploy WorkersStack --require-approval never + + - name: Deploy client + env: + VERCEL_DEPLOY_HOOK_URL: ${{ inputs.deploy_env == 'production' && secrets.VERCEL_DEPLOY_HOOK_URL || secrets.STAGING_VERCEL_DEPLOY_HOOK_URL }} + if: ${{ inputs.deploy_vercel_client }} + run: curl -X POST $VERCEL_DEPLOY_HOOK_URL + + # This stack contains the github runner and other CI/CD tools on AWS + - name: Deploy tools + if: ${{ inputs.deploy_tools_stack }} + working-directory: aws/maybe-app + run: CDK_ENV=tools ./node_modules/.bin/cdk deploy ToolsStack --require-approval never + + # Removes all Docker assets older than 1 day (leave recent images on machine to utilize Docker cache) + - name: Cleanup Docker + if: ${{ always() }} + run: docker system prune --all --filter "until=24h" --force diff --git a/.github/workflows/validate-pull-request.yml b/.github/workflows/validate-pull-request.yml new file mode 100644 index 00000000000..86a8648ad59 --- /dev/null +++ b/.github/workflows/validate-pull-request.yml @@ -0,0 +1,102 @@ +name: Validate Pull Request + +on: [pull_request] + +concurrency: + group: ${{ github.ref }}-validate-pr-group + cancel-in-progress: true + +jobs: + # Builds affected apps and runs unit tests + build_test: + name: Build and Test + runs-on: ubuntu-latest + + services: + redis: + image: redis:6-alpine + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + postgres: + image: timescale/timescaledb:latest-pg14 + env: + POSTGRES_USER: maybe + POSTGRES_PASSWORD: maybe + POSTGRES_DB: maybe_local + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout repo + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set NX commit SHAs for affected commands + uses: nrwl/nx-set-shas@v2 # derive appropriate SHAs for base and head for `nx affected` commands + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '18' + cache: 'yarn' + + - name: Install node_modules + run: yarn install --frozen-lockfile + + - name: Run unit tests + run: yarn nx affected --target=test --parallel=5 --testPathPattern='^(?!.*integration).*$' + + - name: Build affected apps + run: yarn nx affected --target=build --parallel=5 + + - name: Setup env + run: | + cat << EOF > .env + NX_AUTH0_CLIENT_SECRET=${{ secrets.NX_AUTH0_CLIENT_SECRET }} + NX_AUTH0_MGMT_CLIENT_SECRET=${{ secrets.NX_AUTH0_MGMT_CLIENT_SECRET }} + NX_DATABASE_SECRET=${{ secrets.NX_DATABASE_SECRET }} + NX_SESSION_SECRET=${{ secrets.NX_SESSION_SECRET }} + NX_PLAID_SECRET=${{ secrets.NX_PLAID_SECRET }} + NX_POLYGON_API_KEY=${{ secrets.NX_POLYGON_API_KEY }} + NX_FINICITY_APP_KEY=${{ secrets.NX_FINICITY_APP_KEY }} + NX_FINICITY_PARTNER_SECRET=${{ secrets.NX_FINICITY_PARTNER_SECRET }} + NX_STRIPE_SECRET_KEY=${{ secrets.NX_STRIPE_SECRET_KEY }} + NX_STRIPE_WEBHOOK_SECRET=${{ secrets.NX_STRIPE_WEBHOOK_SECRET }} + NX_PLAID_WEBHOOK_URL=none + NX_CONVERTKIT_SECRET=none + NX_DATABASE_URL=postgresql://maybe:maybe@localhost:5432/maybe_local?connection_limit=32&pool_timeout=20 + NX_REDIS_URL=redis://localhost:6379 + EOF + - name: Run local DB migration for testing + run: yarn prisma:migrate:deploy + + - name: Run integration tests + run: sudo yarn dev:ci:test --testPathPattern='^.*\.integration\.spec\.ts$' + + - name: Start apps + run: yarn nx run-many --parallel --target=serve --projects=client,server,workers & + + - name: Run end-to-end tests + if: "!contains(github.event.head_commit.message, 'skip-e2e')" + run: | + sudo yarn cypress install + yarn wait-on -t 120000 http://localhost:4200 + sudo yarn dev:ci:e2e --env.WEBHOOK_TYPE 'mock' + - name: Upload test artifacts + if: failure() + uses: actions/upload-artifact@v2 + with: + name: cypress-artifacts + path: dist/cypress/apps/e2e diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..9dedd063234 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +**/dist +**/tmp +**/out-tsc + +# dependencies +**/node_modules + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings +exceptions.log +error.log +combined.log +/tools/output + +# System Files +.DS_Store +Thumbs.db + +# ENV +**/.env +**/.env.local + +aws/maybe-app/cdk.out + +# Next.js +.next + +# nx +migrations.json + +# Shouldn't happen, but backup since we have a script that generates these locally +*.pem \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000000..d2ae35e84b0 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..d6e7d56e904 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +# Add files here to ignore them from prettier formatting +/dist +/coverage +/auth0/tenant.yaml \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..6ba6e183269 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "trailingComma": "es5", + "printWidth": 100, + "tabWidth": 4, + "semi": false, + "singleQuote": true, + "overrides": [ + { + "files": "auth0/rules/**", + "options": { + "semi": true + } + } + ] +} diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000000..94d414c2355 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,11 @@ +module.exports = { + stories: [], + addons: ['@storybook/addon-essentials'], + // uncomment the property below if you want to apply some webpack config globally + // webpackFinal: async (config, { configType }) => { + // // Make whatever fine-grained changes you need that should apply to all storybook configs + + // // Return the altered config + // return config; + // }, +} diff --git a/.storybook/tsconfig.json b/.storybook/tsconfig.json new file mode 100644 index 00000000000..41baf1bcc0b --- /dev/null +++ b/.storybook/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.base.json", + "exclude": ["../**/*.spec.js", "../**/*.spec.ts", "../**/*.spec.tsx", "../**/*.spec.jsx"], + "include": ["../**/*"] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..b440d565f4d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "nrwl.angular-console", + "esbenp.prettier-vscode", + "firsttris.vscode-jest-runner", + "dbaeumer.vscode-eslint", + "prisma.prisma" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000000..d421ec64238 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,47 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "pwa-node", + "request": "launch", + "name": "Jest run current file", + "program": "${workspaceFolder}/node_modules/.bin/nx", + "cwd": "${workspaceFolder}", + "args": [ + "test", + "--testPathPattern=${fileBasenameNoExtension}", + "--runInBand", + "--skip-nx-cache" + ], + "skipFiles": ["/**", "${workspaceFolder/node_modules/**/*}"], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen", + "env": { + "IS_VSCODE_DEBUG": "true", + "NX_DATABASE_URL": "postgresql://maybe:maybe@localhost:5432/maybe_local" + } + }, + { + "name": "server debug", + "type": "node", + "request": "attach", + "restart": false, + "port": 9228, + "address": "localhost", + "localRoot": "${workspaceFolder}", + "skipFiles": ["/**"], + "remoteRoot": "/app" + }, + { + "name": "workers debug", + "type": "node", + "request": "attach", + "restart": false, + "port": 9227, + "address": "localhost", + "localRoot": "${workspaceFolder}", + "skipFiles": ["/**"], + "remoteRoot": "/app" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..b76293a894d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "css.customData": [".vscode/css_custom_data.json"], + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000000..a841813bc94 --- /dev/null +++ b/README.md @@ -0,0 +1,80 @@ +# Quick Start + +For an overview of this repository, please [see the wiki](https://github.com/maybe-finance/maybe-app/wiki) + +## System Prerequisites + +- Docker (if not using Docker, you will need Node LTS 14.7.x and Postgres 13.x) +- (Optional, highly recommended) - Install the [NX Console](https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console) for [using the nx client](#nrwl-nx-overview) + +## Run the app locally + +### Setup ENV + +``` +cp .env.example .env +``` + +A working local development `.env` file can be found in 1Password under the "Engineering" folder. + +### With Docker (preferred) + +#### Start server and client apps + +``` +yarn install +yarn dev +``` + +#### Migrate DB + +In a separate terminal, run the following command. This will connect to the Postgres DB running inside Docker and run all the migrations in `/prisma/migrations`. + +``` +yarn prisma:migrate +``` + +You will also want to seed the database (includes account types and subtypes for categorization). + +``` +yarn prisma:seed +``` + +### Manually + +_NOTE: Make sure Postgres 13.x is running on your machine_ + +``` +yarn install +nx serve client # Terminal 1 +nx serve server # Terminal 2 +yarn prisma:migrate && yarn prisma:seed # Terminal 3 - after apps are running +``` + +# Reference + +## Deployments and CI/CD + +[See this wiki page](https://github.com/maybe-finance/maybe-app/wiki/Render-Deployments) for an overview of how deployments work. + +## Authentication + +[See this wiki page](https://github.com/maybe-finance/maybe-app/wiki/Auth0) for an explanation of how authentication/authorization works in this codebase. + +## BullMQ Message Queue + +[See this wiki page](https://github.com/maybe-finance/maybe-app/wiki/Background-Workers) for an overview of BullMQ and how it is used within the repo. + +## Feature Flags + +[See this wiki page](https://github.com/maybe-finance/maybe-app/wiki/Feature-Flags) for an overview of how we use feature flags. + +## Testing Intercom Locally + +``` +yarn dev:services:all +yarn dev +ngrok http --region=us --hostname=localhost.maybe.co 4200 +``` + +Visit `https://localhost.maybe.co` diff --git a/apps/.gitkeep b/apps/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/advisor/.eslintrc.json b/apps/advisor/.eslintrc.json new file mode 100644 index 00000000000..e1e03519ded --- /dev/null +++ b/apps/advisor/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "extends": [ + "plugin:@nrwl/nx/react-typescript", + "../../.eslintrc.json", + "next", + "next/core-web-vitals" + ], + "ignorePatterns": ["!**/*", "styles.css", "**/*.csv", "**/public/*", "**/.next/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@next/next/no-img-element": "off", + "@next/next/no-html-link-for-pages": "off" + } + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ], + "env": { + "jest": true + }, + "settings": { + "next": { + "rootDir": "apps/advisor" + } + } +} diff --git a/apps/advisor/components/AdvisorMessageInput.tsx b/apps/advisor/components/AdvisorMessageInput.tsx new file mode 100644 index 00000000000..531fcc66d99 --- /dev/null +++ b/apps/advisor/components/AdvisorMessageInput.tsx @@ -0,0 +1,198 @@ +import { useState, type FormEventHandler } from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import Placeholder from '@tiptap/extension-placeholder' +import { RiAttachment2 } from 'react-icons/ri' +import type { Conversation } from '@prisma/client' +import type { O } from 'ts-toolbelt' +import { Button, Tooltip } from '@maybe-finance/design-system' +import { RichTextEditorMenuBar, UploadFile } from '@maybe-finance/client/shared' +import { trpc } from '../lib/trpc' +import Uppy from '@uppy/core' +import { DashboardModal, useUppy } from '@uppy/react' +import ScreenCapture from '@uppy/screen-capture' +import Webcam from '@uppy/webcam' +import UppyS3 from '@uppy/aws-s3' +import '@uppy/core/dist/style.css' +import '@uppy/dashboard/dist/style.css' +import '@uppy/screen-capture/dist/style.css' +import '@uppy/webcam/dist/style.css' + +type MessageFormData = O.AtLeast<{ + body: string | null + media: { + fileId: string + type: string + key: string + } +}> + +export default function AdvisorMessageInput({ + conversationId, + initialValue = {}, + onSubmit, +}: { + conversationId: Conversation['id'] + initialValue?: Partial + onSubmit(data: MessageFormData): Promise +}) { + const [showUploadModal, setShowUploadModal] = useState(false) + + // form state + const [media, setMedia] = useState(initialValue.media) + const [isSubmitting, setIsSubmitting] = useState(false) + const [error, setError] = useState() + + const editor = useEditor( + { + extensions: [ + StarterKit.configure({ + heading: { + levels: [1], + }, + }), + Placeholder.configure({ + placeholder: 'Type message...', + emptyEditorClass: 'placeholder text-gray-100 caret-white', + }), + ], + content: initialValue.body, + editorProps: { + attributes: { + class: 'flex-1 prose prose-light prose-sm dark:prose-invert prose-headings:text-lg leading-tight focus:outline-none', + }, + }, + }, + [initialValue.body] + ) + + const signUrl = trpc.messages.signS3Url.useMutation() + + const uppy = useUppy(() => + new Uppy({ restrictions: { maxNumberOfFiles: 1 } }) + .use(ScreenCapture) + .use(Webcam) + .use(UppyS3, { + getUploadParameters(file) { + return signUrl.mutateAsync({ + conversationId, + mimeType: file.type, + }) + }, + }) + ) + + uppy.on('upload-success', (file) => { + if (!file?.meta?.key) throw new Error('Did not generate S3 key') + if (!file?.type) throw new Error('No file type provided') + + setMedia({ + fileId: file.id, + key: file.meta.key as string, + type: file.type!, + }) + setShowUploadModal(false) + }) + + const handleSubmit: FormEventHandler = (e) => { + e.preventDefault() + + // TipTap defaults to `

` so we use `editor.isEmpty` to see if the user entered text or not + const body = !editor || editor.isEmpty ? null : editor.getHTML() + if (!body && !media) { + setError(`Message or media upload is required`) + return + } + + setIsSubmitting(true) + setError(undefined) + + onSubmit({ body, media }) + .then(() => { + editor!.commands.clearContent() + setMedia(undefined) + }) + .catch((err) => { + console.error('error submitting form', err) + setError(`Error submitting form`) + }) + .finally(() => setIsSubmitting(false)) + } + + return ( + <> + setShowUploadModal(false)} + uppy={uppy} + plugins={['ScreenCapture', 'Webcam']} + /> + +
+ {/* text toolbar */} +
+ +
+ + {/* text editor */} +
+ +
+ + {/* upload thumbnails */} + {media && ( +
    +
  • + { + uppy.removeFile(media.fileId) + setMedia(undefined) + }} + /> +
  • +
+ )} + + {/* bottom bar */} +
+ {/* action buttons */} +
+ + + + {/* + + */} +
+ + {/* error message */} + {error &&

{error}

} + + {/* submit */} + +
+
+ + ) +} diff --git a/apps/advisor/components/Conversation.tsx b/apps/advisor/components/Conversation.tsx new file mode 100644 index 00000000000..b5ea1b8a15c --- /dev/null +++ b/apps/advisor/components/Conversation.tsx @@ -0,0 +1,81 @@ +import { Attachment, RelativeTime, RichText } from '@maybe-finance/client/shared' +import type { RouterOutput } from '../lib/trpc' + +type Props = { + me: RouterOutput['users']['me'] + conversation: RouterOutput['advisor']['conversations']['get'] +} + +export default function Conversation({ me, conversation }: Props) { + return ( +
    + {conversation.messages.map((message, idx) => { + const isMe = message.userId === me.id + const advisor = message.user?.advisor + + return ( +
  • + {/* border */} + {idx !== conversation.messages.length - 1 && ( +
  • + ) + })} +
+ ) +} diff --git a/apps/advisor/components/ConversationUserDetails.tsx b/apps/advisor/components/ConversationUserDetails.tsx new file mode 100644 index 00000000000..a1c1dc2084e --- /dev/null +++ b/apps/advisor/components/ConversationUserDetails.tsx @@ -0,0 +1,446 @@ +import { Fragment, useState } from 'react' +import Tippy from '@tippyjs/react/headless' +import orderBy from 'lodash/orderBy' +import type { IconType } from 'react-icons' +import { + RiEditLine, + RiLineChartLine, + RiMoneyDollarBoxLine, + RiScales3Line, + RiShareBoxFill, +} from 'react-icons/ri' +import { GiPalmTree } from 'react-icons/gi' +import { NumberUtil, AccountUtil, DateUtil, ATAUtil, type SharedType } from '@maybe-finance/shared' +import { Button, Badge, Tab } from '@maybe-finance/design-system' +import TrendBadge from './TrendBadge' +import RiskSlider from './RiskSlider' +import type { RouterOutput } from '../lib/trpc' +import Link from 'next/link' +import EditUser from './EditUser' + +const CLIENT_URL = process.env.NX_CLIENT_URL || 'http://localhost:4200' + +const goals: Record = { + save: [RiMoneyDollarBoxLine, 'I want to save up for something'], + retire: [GiPalmTree, 'I would like to retire comfortably'], + debt: [RiScales3Line, 'I need to pay off debt'], + invest: [RiLineChartLine, 'I need help investing'], +} + +type Props = { + conversation: RouterOutput['advisor']['conversations']['get'] +} + +export default function ConversationUserDetails({ conversation }: Props) { + const [isEditing, setIsEditing] = useState(false) + const { + user: { insights, ...userProfile }, + } = conversation + + const riskProfile = ATAUtil.calcRiskProfile( + ATAUtil.riskQuestions, + (conversation.user.riskAnswers ?? []) as SharedType.RiskAnswer[] + ) + + return ( +
+ setIsEditing(false)} /> + {/* Header */} +
+

Details

+
+ + {/* Status */} +
+
+
+
Status
+
+ + {conversation.status} + +
+
+
+
Assistants
+
N/A
+
+ + {conversation.accountId && ( + + )} + + {conversation.planId && ( + + )} +
+
+ + {/* Client Info */} + +
+

Client

+ +
+

{conversation.user.name}

+ + + + +
+ {conversation.user.dob && ( +

+ Age {DateUtil.dobToAge(conversation.user.dob)} +

+ )} +
+ + {/* Goals */} +
+

Goals

+
+ {conversation.user.goals?.length ? ( +
    + {conversation.user.goals?.map((goal, idx) => { + const goalInfo = goals[goal] + const [Icon, description] = goalInfo ? goalInfo : [null, goal] + + return ( +
  • + {Icon && } + {description} +
  • + ) + })} +
+ ) : ( +

N/A

+ )} +
+
+ + {/* Notes */} +
+

Notes

+
+

{conversation.user.userNotes || 'N/A'}

+
+
+ + {/* Risk profile */} +
+

Risk profile

+
+ {riskProfile ? ( +
+ +
+ ) : ( + 'N/A' + )} +
+
+ + {/* Insights */} +
+
+
+
Net Worth
+
+ + {NumberUtil.format(insights.netWorthToday, 'short-currency')} + + +
+
+
+
Assets
+
+ {NumberUtil.format(insights.debtAsset.asset, 'short-currency')} +
+
+
+
Liabilities
+
+ {NumberUtil.format(insights.debtAsset.debt, 'short-currency')} +
+
+
+
+ Debt-to-Asset +
+
+ {NumberUtil.format(insights.debtAsset.ratio, 'percent', { + signDisplay: 'auto', + })} +
+
+
+
+ Debt-to-Income +
+
+ {NumberUtil.format(insights.debtIncome.ratio, 'percent', { + signDisplay: 'auto', + })} +
+
+
+
+ Income (Monthly) +
+
+ {NumberUtil.format( + insights.transactionSummary.income, + 'short-currency' + )} +
+
+
+
+ Expenses (Monthly) +
+
+ {NumberUtil.format( + insights.transactionSummary.expenses, + 'short-currency' + )} +
+
+
+
Safety Net
+
+ {formatSafetyNet(insights.safetyNet)} +
+
+
+
+ Payments (Monthly) +
+
+ {NumberUtil.format( + insights.transactionSummary.payments, + 'short-currency' + )} +
+
+
+ + ( +
+
+                                {JSON.stringify(insights, null, 2)}
+                            
+
+ )} + trigger="click" + interactive + > + +
+
+ + {/* Asset / Debt breakdown */} +
+ + + Assets + Debts + Holdings + + + {[ + insights.accountSummary.filter((r) => r.classification === 'asset'), + insights.accountSummary.filter((r) => r.classification === 'liability'), + ].map((rows, idx) => ( + + + + + + + + + + + {orderBy(rows, (row) => row.allocation, 'desc').map( + (row) => ( + + + + + + ) + )} + +
+ Type + + Allocation + + Amount +
+ { + AccountUtil.CATEGORIES[row.category] + .plural + } + + {NumberUtil.format( + row.allocation, + 'percent', + { + signDisplay: 'never', + } + )} + + {NumberUtil.format( + row.balance, + 'short-currency', + { signDisplay: 'never' } + )} +
+
+ ))} + +
+ + + + + + + + {insights.holdingBreakdown.map( + ({ category, allocation, value, holdings }) => ( + + + + + + + {holdings.map( + ({ security, allocation, value }) => ( + + + + + + ) + )} + + ) + )} + +
+ Type + + Allocation + + Amount +
{category} + {NumberUtil.format( + allocation, + 'percent', + { signDisplay: 'never' } + )} + + {NumberUtil.format( + value, + 'short-currency', + { signDisplay: 'never' } + )} +
+ {security.symbol || + security.name} + + {NumberUtil.format( + allocation, + 'percent', + { signDisplay: 'never' } + )} + + {NumberUtil.format( + value, + 'short-currency', + { signDisplay: 'never' } + )} +
+
+
+
+
+
+
+ ) +} + +function formatSafetyNet({ months }: Props['conversation']['user']['insights']['safetyNet']) { + return months.lt(24) + ? `${months.toFixed(0)} months` + : months.lte(120) + ? `${months.divToInt(12).toFixed(0)} years` + : '>10 years' +} diff --git a/apps/advisor/components/Conversations.tsx b/apps/advisor/components/Conversations.tsx new file mode 100644 index 00000000000..a5a0c55959f --- /dev/null +++ b/apps/advisor/components/Conversations.tsx @@ -0,0 +1,229 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { useState } from 'react' +import { DateTime } from 'luxon' +import cn from 'classnames' +import { RiCheckLine, RiReplyFill } from 'react-icons/ri' +import { Tab } from '@maybe-finance/design-system' +import { useRichTextPreview } from '@maybe-finance/client/shared' +import type { RouterOutput } from '../lib/trpc' +import { trpc } from '../lib/trpc' + +type Conversation = RouterOutput['advisor']['conversations']['getAll'][0] + +const tabs: { + label: string + filter(conversation: Conversation): boolean +}[] = [ + { label: 'All', filter: () => true }, + { label: 'Open', filter: (c) => c.status === 'open' }, + { label: 'Closed', filter: (c) => c.status === 'closed' }, +] + +export default function Conversations({ children }) { + const router = useRouter() + + const [tab, setTab] = useState(1) + + const meQuery = trpc.users.me.useQuery() + + const conversationsQuery = trpc.advisor.conversations.getAll.useQuery() + const conversations = conversationsQuery.data ?? [] + + const filteredConversations = conversations.filter(tabs[tab].filter) + + return ( +
+ {/* Primary column */} +
+

+ Conversation +

+ + {children} +
+ + {/* Left sidebar (hidden on smaller screens) */} + +
+ ) +} + +export function ConversationPreview({ conversation }: { conversation: Conversation }) { + const message = conversation.lastMessage + const textPreview = useRichTextPreview(message?.body) + + return ( +

+ {textPreview || (message?.mediaSrc ? '' : '')} +

+ ) +} + +export function ConversationStatusIndicator({ + me, + conversation, +}: { + me?: RouterOutput['users']['me'] + conversation: Conversation +}) { + const status = + conversation.status === 'closed' + ? 'closed' + : conversation.lastMessage + ? 'in-progress' + : 'new' + + if (status === 'closed') + return ( + + Closed + + ) + + if (conversation.lastMessage?.user?.advisor) { + const isMe = conversation.lastMessage.userId === me?.id + + return ( +
+ {isMe && } + advisor avatar +
+ ) + } + + return ( +
+ {status === 'in-progress' && ( +
+ )} +
+ ) +} diff --git a/apps/advisor/components/EditUser.tsx b/apps/advisor/components/EditUser.tsx new file mode 100644 index 00000000000..c19322ed20a --- /dev/null +++ b/apps/advisor/components/EditUser.tsx @@ -0,0 +1,107 @@ +import { Button, DialogV2, Input, InputCurrency, Listbox } from '@maybe-finance/design-system' +import type { User } from '@prisma/client' +import { Controller, useForm } from 'react-hook-form' +import { toast } from 'react-hot-toast' +import { trpc } from '../lib/trpc' +import { taxMap } from '../lib/util' + +type EditUserProps = { + user: Pick + isOpen: boolean + onClose(): void +} +export default function EditUser({ user, isOpen, onClose }: EditUserProps) { + const utils = trpc.useContext() + const updateUser = trpc.advisor.users.update.useMutation({ + onSuccess() { + toast.success('User saved') + utils.advisor.users.get.invalidate() + onClose() + }, + onError() { + toast.error('Failed to save user') + }, + }) + + const { id, taxStatus, incomeType, dependents, grossIncome } = user + + return ( + + { + updateUser.mutate({ + userId: id, + taxStatus: taxStatus || null, + incomeType: incomeType || null, + dependents: dependents ? +dependents : null, + grossIncome: grossIncome || null, + }) + }} + /> + + ) +} + +type FormFields = Pick +type Props = { + defaultValues: FormFields + onSubmit(data: FormFields): void +} +function EditUserForm({ defaultValues, onSubmit }: Props) { + const { handleSubmit, control, register } = useForm({ defaultValues }) + return ( +
+ + + { + return ( + <> + + + {taxMap[(field.value as string) ?? '']} + + + {Object.entries(taxMap).map(([key, title]) => ( + + {title} + + ))} + + + + ) + }} + /> + + { + return ( + + ) + }} + /> + + + + + + ) +} diff --git a/apps/advisor/components/Layout.tsx b/apps/advisor/components/Layout.tsx new file mode 100644 index 00000000000..0f3d895a4e9 --- /dev/null +++ b/apps/advisor/components/Layout.tsx @@ -0,0 +1,213 @@ +import { Fragment, useState } from 'react' +import Link from 'next/link' +import { useUser } from '@auth0/nextjs-auth0/client' +import { Dialog, Transition } from '@headlessui/react' +import { Menu } from '@maybe-finance/design-system' +import { + RiSettings3Line as SettingsIcon, + RiShutDownLine as LogoutIcon, + RiCloseLine, + RiMenuLine, + RiInboxLine, + RiUserSearchLine, +} from 'react-icons/ri' + +const navigation = [ + { name: 'Inbox', href: '/', icon: RiInboxLine }, + { name: 'Directory', href: '/users', icon: RiUserSearchLine }, +] + +export default function Layout({ children }) { + const { user } = useUser() + + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + + return ( +
+ + + +
+ + +
+ + + +
+ +
+
+
+
+ Maybe Finance Logo +
+ +
+
+ +
+ +
+
+

+ {user?.name} +

+

+ Account Settings +

+
+ +
+
+
+ +
+
+
+ + {/* Static sidebar for desktop */} +
+
+
+
+
+ + Maybe Finance Logo + +
+ +
+
+ + + + + + } href="/settings"> + Settings + + } destructive> + Logout + + + +
+
+
+
+ +
+ {/* Mobile top navigation */} +
+
+
+ Maybe Finance Logo +
+
+ +
+
+
+ +
{children}
+
+
+ ) +} diff --git a/apps/advisor/components/Meta.tsx b/apps/advisor/components/Meta.tsx new file mode 100644 index 00000000000..af85f0daf15 --- /dev/null +++ b/apps/advisor/components/Meta.tsx @@ -0,0 +1,34 @@ +import Head from 'next/head' +import React from 'react' + +export default function Meta() { + return ( + + {/* */} + Maybe Advisor + + + + {/* */} + + + + + + + {/* */} + + + {/* */} + + + + {/* */} + + + + ) +} diff --git a/apps/advisor/components/RiskSlider.tsx b/apps/advisor/components/RiskSlider.tsx new file mode 100644 index 00000000000..ba22adc9d6d --- /dev/null +++ b/apps/advisor/components/RiskSlider.tsx @@ -0,0 +1,108 @@ +import { ParentSize } from '@visx/responsive' +import { scaleLinear, scaleQuantile } from '@visx/scale' + +const riskScale = scaleQuantile({ + domain: [1, 2, 3, 4], + range: ['Low', 'Moderate', 'High'], +}) + +// Shows a readonly slider with a tooltip (score is 1-4 sliding scale from least to most aggressive) +export default function RiskSlider({ score }: { score: number }) { + const margin = { + left: 40, + right: 40, + top: 5, + bottom: 5, + } + + return ( +
+ + {({ width, height }) => { + const xScaleIndicator = scaleLinear({ + domain: [1, 4], + range: [margin.left, width - margin.right], + }) + + return ( + + + + + + + + + + + + + {riskScale(score)} + + + + {['10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%'].map( + (v) => ( + + ) + )} + + + + + + + Cautious + + + Aggressive + + + ) + }} + +
+ ) +} diff --git a/apps/advisor/components/Toaster.tsx b/apps/advisor/components/Toaster.tsx new file mode 100644 index 00000000000..150fbfe05c4 --- /dev/null +++ b/apps/advisor/components/Toaster.tsx @@ -0,0 +1,26 @@ +import { Toaster as RHToaster, resolveValue } from 'react-hot-toast' +import cn from 'classnames' +import { Toast } from '@maybe-finance/design-system' + +type Props = { + className?: string +} + +export default function Toaster({ className }: Props) { + return ( + + {(t) => ( + + {resolveValue(t.message, t)} + + )} + + ) +} diff --git a/apps/advisor/components/TrendBadge.tsx b/apps/advisor/components/TrendBadge.tsx new file mode 100644 index 00000000000..88ce94cb2e4 --- /dev/null +++ b/apps/advisor/components/TrendBadge.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react' +import type { BadgeProps } from '@maybe-finance/design-system' +import { Badge } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' +import { NumberUtil } from '@maybe-finance/shared' + +type TrendBadgeProps = { + trend: SharedType.Trend + size?: BadgeProps['size'] +} + +export default function TrendBadge({ trend, size = 'sm' }: TrendBadgeProps) { + const [mode, setMode] = useState<'percent' | 'amount'>('percent') + + return ( + setMode((prev) => (prev === 'amount' ? 'percent' : 'amount'))} + > + {mode === 'percent' + ? NumberUtil.format(trend.percentage, 'percent', { + maximumFractionDigits: 2, + signDisplay: 'exceptZero', + }) + : NumberUtil.format(trend.amount, 'short-currency', { + signDisplay: 'exceptZero', + })} + + ) +} diff --git a/apps/advisor/components/Unauthorized.tsx b/apps/advisor/components/Unauthorized.tsx new file mode 100644 index 00000000000..2f58b394d1c --- /dev/null +++ b/apps/advisor/components/Unauthorized.tsx @@ -0,0 +1,29 @@ +import { Button } from '@maybe-finance/design-system' +import Link from 'next/link' +import Image from 'next/image' + +export default function Unauthorized() { + return ( +
+ maybe logo + +

+ Oops! +

+ +

+ You are not authorized to view this page. This app is for our registered advisors + only. +

+ +
+ + + + + + +
+
+ ) +} diff --git a/apps/advisor/components/UserList.tsx b/apps/advisor/components/UserList.tsx new file mode 100644 index 00000000000..164fbf62500 --- /dev/null +++ b/apps/advisor/components/UserList.tsx @@ -0,0 +1,89 @@ +import { type PropsWithChildren, useState } from 'react' +import { groupBy } from 'lodash' +import { trpc } from '../lib/trpc' +import Link from 'next/link' +import UserSearch from './UserSearch' + +// No typings available for this module +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fuzzysearch = require('fuzzysearch') + +export default function Users({ children }: PropsWithChildren) { + const users = trpc.advisor.users.getAll.useQuery() + const [query, setQuery] = useState('') + + const userList = (users.data ?? []).filter((u) => + fuzzysearch(query.toLowerCase(), u.name?.toLowerCase()) + ) + const groupedUsers = groupBy(userList, (user) => user.lastName?.at(0)) + + return ( +
+ {/* Primary column */} +
+

+ User +

+ + {children} +
+ + {/* Left sidebar (hidden on smaller screens) */} + +
+ ) +} diff --git a/apps/advisor/components/UserSearch.tsx b/apps/advisor/components/UserSearch.tsx new file mode 100644 index 00000000000..79ffb2d655a --- /dev/null +++ b/apps/advisor/components/UserSearch.tsx @@ -0,0 +1,25 @@ +import { RiSearch2Line } from 'react-icons/ri' + +type Props = { onChange(query: string): void } +export default function UserSearch({ onChange }: Props) { + return ( +
+
+ +
+
+
+ onChange(e.target.value)} + type="search" + className="block w-full rounded-md bg-transparent border-gray-300 pl-10 focus:border-gray-500 focus:ring-gray-500 sm:text-sm placeholder:text-gray-200" + placeholder="Search" + /> +
+
+
+ ) +} diff --git a/apps/advisor/index.d.ts b/apps/advisor/index.d.ts new file mode 100644 index 00000000000..032c115ccda --- /dev/null +++ b/apps/advisor/index.d.ts @@ -0,0 +1,6 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +declare module '*.svg' { + const content: any + export const ReactComponent: any + export default content +} diff --git a/apps/advisor/jest.config.ts b/apps/advisor/jest.config.ts new file mode 100644 index 00000000000..db267deabc6 --- /dev/null +++ b/apps/advisor/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'advisor', + preset: '../../jest.preset.js', + transform: { + '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/apps/advisor', +} diff --git a/apps/advisor/lib/trpc.ts b/apps/advisor/lib/trpc.ts new file mode 100644 index 00000000000..d5344b342e8 --- /dev/null +++ b/apps/advisor/lib/trpc.ts @@ -0,0 +1,25 @@ +import { createTRPCNext } from '@trpc/next' +import { httpBatchLink, loggerLink } from '@trpc/client' +import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server' +import { superjson } from '@maybe-finance/shared' +// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries +import type { AppRouter } from '@maybe-finance/trpc' + +export type RouterInput = inferRouterInputs +export type RouterOutput = inferRouterOutputs + +export const trpc = createTRPCNext({ + config() { + return { + links: [ + loggerLink({ + enabled: (opts) => + process.env.NODE_ENV === 'development' || + (opts.direction === 'down' && opts.result instanceof Error), + }), + httpBatchLink({ url: '/api/trpc' }), + ], + transformer: superjson, + } + }, +}) diff --git a/apps/advisor/lib/util.ts b/apps/advisor/lib/util.ts new file mode 100644 index 00000000000..f73e00343c0 --- /dev/null +++ b/apps/advisor/lib/util.ts @@ -0,0 +1,29 @@ +export const householdMap = { + single: 'Single income, no dependents', + singleWithDependents: 'Single income, at least one dependent', + dual: 'Dual income, no dependents', + dualWithDependents: 'Dual income, at least one dependent', + retired: 'Retired or financially independent', +} + +export const maybeGoalsMap = { + aggregate: 'See all my accounts in one place', + advice: 'Get advice from a financial advisor', + plan: 'Build plans for retirement and other milestones', +} + +export const goalsMap = { + retire: 'I would like to retire comfortably', + debt: 'I need to pay off debt', + save: 'I want to save up for something', + invest: 'I need help investing', +} + +export const taxMap = { + '': 'None', + single: 'Single', + married_joint: 'Married filing jointly', + married_separate: 'Married filing separately', + head_of_household: 'Head of Household', + qualifying_widow: 'Qualifying Widow(er) with dependent', +} diff --git a/apps/advisor/next-env.d.ts b/apps/advisor/next-env.d.ts new file mode 100644 index 00000000000..4f11a03dc6c --- /dev/null +++ b/apps/advisor/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/apps/advisor/next.config.js b/apps/advisor/next.config.js new file mode 100644 index 00000000000..9d9437ffdb0 --- /dev/null +++ b/apps/advisor/next.config.js @@ -0,0 +1,27 @@ +//@ts-check + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { withNx } = require('@nrwl/next/plugins/with-nx') + +/** + * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions} + **/ +const nextConfig = { + nx: { + // Set this to true if you would like to to use SVGR + // See: https://github.com/gregberge/svgr + svgr: false, + }, + swcMinify: false, + async redirects() { + return [ + { + source: '/', + destination: '/conversations', + permanent: false, + }, + ] + }, +} + +module.exports = withNx(nextConfig) diff --git a/apps/advisor/pages/_app.tsx b/apps/advisor/pages/_app.tsx new file mode 100644 index 00000000000..0f3548369bc --- /dev/null +++ b/apps/advisor/pages/_app.tsx @@ -0,0 +1,54 @@ +import type { ComponentType, FC, ReactElement } from 'react' +import type { AppProps } from 'next/app' +import { type UserProfile, UserProvider } from '@auth0/nextjs-auth0/client' +import Meta from '../components/Meta' +import { trpc } from '../lib/trpc' +import './styles.css' +import type { SharedType } from '@maybe-finance/shared' +import Unauthorized from '../components/Unauthorized' +import Toaster from '../components/Toaster' + +function App({ + Component, + pageProps, +}: AppProps & { + Component: AppProps['Component'] & { + getLayout?: (component: ReactElement) => JSX.Element + } +}) { + const Page = withRoleAuthz(Component, { + roles: ['Admin', 'Advisor'], + user: pageProps.user, + getLayout: Component.getLayout ?? ((page) => page), + }) + + return ( + <> + + + {} + + ) +} + +export default trpc.withTRPC(App) + +const withRoleAuthz =

( + Page: ComponentType

, + opts: { + roles: SharedType.UserRole[] + getLayout(component: ReactElement): JSX.Element + user?: UserProfile + } +): FC

=> { + return function hoc(props) { + const userRoles = (opts.user?.['https://maybe.co/roles'] ?? []) as SharedType.UserRole[] + const match = opts.roles.some((role) => userRoles.includes(role)) + + if (match) { + return opts.getLayout() + } else { + return + } + } +} diff --git a/apps/advisor/pages/api/auth/[...auth0].ts b/apps/advisor/pages/api/auth/[...auth0].ts new file mode 100644 index 00000000000..7bacdd29f6a --- /dev/null +++ b/apps/advisor/pages/api/auth/[...auth0].ts @@ -0,0 +1,10 @@ +import { handleAuth, handleLogin } from '@auth0/nextjs-auth0' + +export default handleAuth({ + login: handleLogin({ + authorizationParams: { + audience: process.env.AUTH0_AUDIENCE || 'https://maybe-finance-api/v1', + scope: process.env.AUTH0_SCOPE || 'openid profile email offline_access', + }, + }), +}) diff --git a/apps/advisor/pages/api/trpc/[...trpc].ts b/apps/advisor/pages/api/trpc/[...trpc].ts new file mode 100644 index 00000000000..16813325386 --- /dev/null +++ b/apps/advisor/pages/api/trpc/[...trpc].ts @@ -0,0 +1,25 @@ +import { getAccessToken, withApiAuthRequired } from '@auth0/nextjs-auth0' + +const API_URL = process.env.API_URL || 'http://localhost:3333' + +export default withApiAuthRequired(async function trpc(req, res) { + const { accessToken } = await getAccessToken(req, res) + if (!accessToken) { + throw new Error(`failed to get access token`) + } + + // construct tRPC request URL (/api/trpc/xyz -> {API_URL}/trpc/xyz) + const url = new URL(req.url!.replace(/^\/api\//i, ''), API_URL) + + const response = await fetch(url, { + method: req.method, + body: req.method === 'POST' ? JSON.stringify(req.body) : null, // tRPC only uses GET|POST + headers: { + accept: 'application/json', + 'content-type': 'application/json', + authorization: `Bearer ${accessToken}`, + }, + }) + + return res.status(response.status).json(await response.json()) +}) diff --git a/apps/advisor/pages/conversations/[id].tsx b/apps/advisor/pages/conversations/[id].tsx new file mode 100644 index 00000000000..bdca8f00529 --- /dev/null +++ b/apps/advisor/pages/conversations/[id].tsx @@ -0,0 +1,368 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { Fragment, useRef, useState } from 'react' +import { + RiUserAddLine, + RiUserReceivedLine, + RiCheckboxCircleLine, + RiRefreshLine, + RiArrowDownSLine, + RiArrowUpSLine, +} from 'react-icons/ri' +import toast from 'react-hot-toast' +import { withPageAuthRequired } from '@auth0/nextjs-auth0' +import { Button, LoadingSpinner, RTEditor } from '@maybe-finance/design-system' +import { ATAUtil } from '@maybe-finance/shared' +import Conversations from '../../components/Conversations' +import ConversationComponent from '../../components/Conversation' +import ConversationUserDetails from '../../components/ConversationUserDetails' +import Layout from '../../components/Layout' +import AdvisorMessageInput from '../../components/AdvisorMessageInput' +import { trpc } from '../../lib/trpc' +import { Disclosure, Transition } from '@headlessui/react' +import cn from 'classnames' +import { useEditor } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import Placeholder from '@tiptap/extension-placeholder' +import type { Conversation } from '@prisma/client' +import { useLastUpdated } from '@maybe-finance/client/shared' + +type AdvisorNotesProps = { + conversationId: Conversation['id'] +} + +function AdvisorNotes({ conversationId }: AdvisorNotesProps) { + const utils = trpc.useContext() + const note = trpc.advisor.conversations.getNote.useQuery(conversationId, { + staleTime: Infinity, + }) + + const upsertNote = trpc.advisor.conversations.upsertNote.useMutation({ + onSuccess() { + toast.success('Saved notes') + utils.advisor.conversations.getNote.invalidate() + }, + onError() { + toast.success('Did not save note') + }, + }) + + const lastUpdated = useLastUpdated(note.data?.updatedAt, false) + + const editor = useEditor( + { + extensions: [ + StarterKit.configure({ + heading: { + levels: [1], + }, + }), + Placeholder.configure({ + placeholder: 'Enter private advisor note...', + emptyEditorClass: 'placeholder text-gray-100 caret-white', + }), + ], + content: note.data?.body, + editorProps: { + attributes: { + class: 'flex-1 prose prose-light prose-sm dark:prose-invert prose-headings:text-lg leading-tight focus:outline-none', + }, + }, + }, + [note.data] + ) + + if (!editor) return

Unable to load editor

+ + return ( +
+ + +
+ Last saved: {lastUpdated} + +
+
+ ) +} + +export const getServerSideProps = withPageAuthRequired() + +export default function ConversationPage() { + const router = useRouter() + const utils = trpc.useContext() + + const conversationEndRef = useRef(null) + + const [value, setValue] = useState(null) + + // ToDo: figure out auto-scrolling behavior + const scrollToBottom = () => { + conversationEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + } + + const conversationQuery = trpc.advisor.conversations.get.useQuery(+router.query.id!, { + enabled: !!router.query.id, + }) + + const meQuery = trpc.users.me.useQuery() + + const updateConversation = trpc.advisor.conversations.update.useMutation({ + async onSuccess() { + await utils.advisor.conversations.invalidate() + }, + }) + + const addAdvisor = trpc.advisor.conversationAdvisors.create.useMutation({ + async onSuccess() { + toast.success('Joined conversation') + await utils.advisor.conversations.invalidate() + }, + }) + + const removeAdvisor = trpc.advisor.conversationAdvisors.delete.useMutation({ + async onSuccess() { + toast.success('Left conversation') + await utils.advisor.conversations.invalidate() + }, + }) + + const sendMessage = trpc.messages.create.useMutation({ + async onSuccess() { + toast.success('Message sent') + await utils.advisor.conversations.invalidate() + }, + }) + + if (conversationQuery.isLoading || meQuery.isLoading) { + return ( +
+ +
+ ) + } + + if (!conversationQuery.data || !meQuery.data) { + return ( +
+

Not Found

+

+ We can't find that conversation. +

+
+ ) + } + + const conversation = conversationQuery.data + const me = meQuery.data + const advisor = me.advisor + + const inConversation = conversation.advisors.some((a) => a.advisorId === advisor?.id) + + return ( +
+
+ {/* Top section */} +
+ {/* Mobile navbar */} + + + {/* Toolbar*/} +
+
+
+ {advisor && + (inConversation ? ( + + ) : ( + + ))} +
+
+ {conversation.status === 'open' ? ( + + ) : ( + + )} +
+
+
+ + {/* Message header */} +
+

+ {conversation.title} +

+

+ Started by{' '} + {conversation.user.name} on{' '} + +

+
+
+ +
+ + {({ open }) => ( + <> + +
+ Advisor Notes + {open && ( + + These notes are only visible to you + + )} +
+ +
+ + + + + + + )} +
+
+ +
+ +
+
+ +
+ { + return sendMessage.mutateAsync({ + conversationId: conversation.id, + type: media ? ATAUtil.mimeToMessageType(media.type) : 'text', + body, + mediaSrc: media?.key, + }) + }} + /> +
+
+ + +
+ ) +} + +ConversationPage.getLayout = (page) => ( + + {page} + +) diff --git a/apps/advisor/pages/conversations/index.tsx b/apps/advisor/pages/conversations/index.tsx new file mode 100644 index 00000000000..7c8f543109c --- /dev/null +++ b/apps/advisor/pages/conversations/index.tsx @@ -0,0 +1,22 @@ +import { withPageAuthRequired } from '@auth0/nextjs-auth0' +import { RiInboxLine } from 'react-icons/ri' +import Conversations from '../../components/Conversations' +import Layout from '../../components/Layout' + +export const getServerSideProps = withPageAuthRequired() + +export default function ConversationsPage() { + return ( +
+ +

Inbox

+

Select a conversation to get started!

+
+ ) +} + +ConversationsPage.getLayout = (page) => ( + + {page} + +) diff --git a/apps/advisor/pages/settings.tsx b/apps/advisor/pages/settings.tsx new file mode 100644 index 00000000000..b7559888dce --- /dev/null +++ b/apps/advisor/pages/settings.tsx @@ -0,0 +1,10 @@ +import { withPageAuthRequired } from '@auth0/nextjs-auth0' +import Layout from '../components/Layout' + +export const getServerSideProps = withPageAuthRequired() + +export default function SettingsPage() { + return
ToDo
+} + +SettingsPage.getLayout = (page) => {page} diff --git a/apps/advisor/pages/styles.css b/apps/advisor/pages/styles.css new file mode 100644 index 00000000000..899c9424f07 --- /dev/null +++ b/apps/advisor/pages/styles.css @@ -0,0 +1,147 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +/* https://tailwindcss.com/docs/adding-base-styles#using-css */ +@layer base { + @font-face { + font-family: 'Monument Extended'; + font-weight: 100; + src: url('/assets/fonts/monument/MonumentExtended-Thin.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 200; + src: url('/assets/fonts/monument/MonumentExtended-Light.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 300; + src: url('/assets/fonts/monument/MonumentExtended-Book.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 400; + src: url('/assets/fonts/monument/MonumentExtended-Regular.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 500; + src: url('/assets/fonts/monument/MonumentExtended-Medium.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 700; + src: url('/assets/fonts/monument/MonumentExtended-Bold.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 800; + src: url('/assets/fonts/monument/MonumentExtended-Black.woff2') format('woff2'); + } + + @font-face { + font-family: 'Monument Extended'; + font-weight: 900; + src: url('/assets/fonts/monument/MonumentExtended-Heavy.woff2') format('woff2'); + } + + @font-face { + font-family: 'Inter'; + src: url('/assets/fonts/inter/Inter-Variable.woff2') format('woff2-variations'); + font-weight: 100 900; + } + + body { + @apply text-white bg-black; + overflow: hidden; + } + + input { + outline: 0; + } + + /* This prevents inputs from having a white background when user autofills them + TODO: Move this into design system + https://stackoverflow.com/a/41148494 */ + input:-webkit-autofill, + input:-webkit-autofill:hover, + input:-webkit-autofill:focus { + border: none !important; + caret-color: white !important; + -webkit-text-fill-color: white !important; + -webkit-box-shadow: 0 0 0px 1000px transparent inset !important; + transition: background-color 5000s ease-in-out 0s; + } + + h1 { + @apply text-5xl font-bold font-display; + } + + h2 { + @apply text-4xl font-bold font-display; + } + + h3 { + @apply text-3xl font-bold font-display; + } + + h4 { + @apply text-2xl font-bold font-display; + } + + h5 { + @apply text-lg font-bold font-display; + } + + h6 { + @apply text-base font-bold font-display; + } + + .scrollbar-none { + overflow: -moz-scrollbars-none; + } + + .scrollbar-none::-webkit-scrollbar { + width: 0 !important; + } + + .custom-gray-scroll::-webkit-scrollbar { + height: 14px; + width: 14px; + } + + .custom-gray-scroll::-webkit-scrollbar-thumb { + border: 4px solid rgba(0, 0, 0, 0); + background-clip: content-box; + @apply bg-gray-200 rounded-full; + } + + .custom-gray-scroll { + overflow: overlay !important; + } + + @-moz-document url-prefix() { + .custom-gray-scroll { + overflow: auto !important; + } + } +} + +/* tiptap / prosemirror customization */ +.ProseMirror:focus { + outline: none; +} + +.ProseMirror p.placeholder:first-child::before { + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} diff --git a/apps/advisor/pages/users/[userId].tsx b/apps/advisor/pages/users/[userId].tsx new file mode 100644 index 00000000000..94995b47c06 --- /dev/null +++ b/apps/advisor/pages/users/[userId].tsx @@ -0,0 +1,529 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { type ReactNode, useState, Fragment, useMemo } from 'react' +import { RiArrowLeftSLine, RiFileCopyLine } from 'react-icons/ri' +import { withPageAuthRequired } from '@auth0/nextjs-auth0' +import { Button, LoadingSpinner, RTEditor } from '@maybe-finance/design-system' +import Layout from '../../components/Layout' +import Users from '../../components/UserList' +import EditUser from '../../components/EditUser' +import { ConversationPreview, ConversationStatusIndicator } from '../../components/Conversations' +import { type RouterOutput, trpc } from '../../lib/trpc' +import cn from 'classnames' +import { ATAUtil, Geo, NumberUtil, type SharedType } from '@maybe-finance/shared' +import { Tab } from '@headlessui/react' +import type { ConversationNote, User } from '@prisma/client' +import { taxMap, goalsMap, householdMap, maybeGoalsMap } from '../../lib/util' +import StarterKit from '@tiptap/starter-kit' +import { useEditor } from '@tiptap/react' +import { DateTime } from 'luxon' +import { BrowserUtil } from '@maybe-finance/client/shared' +import { type ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table' + +export const getServerSideProps = withPageAuthRequired() + +const tabs = [ + { name: 'Profile', href: 'profile' }, + { name: 'Investments', href: 'financials' }, + { name: 'My Conversations', href: 'conversations' }, + { name: 'My Notes', href: 'advisor-notes' }, +] + +type PanelContentProps = { + user: User +} + +function UserProfile({ + user: { + state, + country, + household, + maybeGoals, + maybeGoalsDescription, + goals, + userNotes, + auth0Id, + taxStatus, + grossIncome, + incomeType, + dependents, + riskAnswers, + }, +}: PanelContentProps) { + const riskProfile = ATAUtil.calcRiskProfile( + ATAUtil.riskQuestions, + (riskAnswers ?? []) as SharedType.RiskAnswer[] + ) + + return ( +
+
User Info
+
+ s.code === state)?.name}, ${ + Geo.countries.find((c) => c.code === country)?.name + }`} + /> + + + + + + + + +
+ +
Goals
+
+ +
    + {maybeGoals.map((mg) => ( +
  • {maybeGoalsMap[mg]}
  • + ))} +
+ {maybeGoalsDescription &&

{maybeGoalsDescription}

} +
+ } + /> + + +
    + {goals + .filter((g) => !!g) + .map((g) => ( +
  • {goalsMap[g] ? goalsMap[g] : g}
  • + ))} +
+ {userNotes &&

{userNotes}

} +
+ } + /> + + + ) +} + +function UserConversations() { + const router = useRouter() + const conversations = trpc.advisor.users.getConversations.useQuery(+router.query.userId!, { + enabled: !!router.query.userId, + }) + + if (conversations.isLoading) + return

Loading conversations...

+ if (conversations.isError) + return

Failed to load conversations

+ if (!conversations.data.length) + return ( +

+ You have not had any conversations with this user yet +

+ ) + + return ( + <> + {conversations.data.map((conversation) => ( +
+
+
+
{conversation.title}
+ +
+ + +
+ + + + +
+ ))} + + ) +} + +function AdvisorNote({ note }: { note: ConversationNote }) { + const editor = useEditor( + { + extensions: [ + StarterKit.configure({ + heading: { + levels: [1], + }, + }), + ], + editable: false, + content: note.body, + editorProps: { + attributes: { + class: 'flex-1 prose prose-light prose-sm dark:prose-invert prose-headings:text-lg leading-tight focus:outline-none', + }, + }, + }, + [note] + ) + + return +} + +function AdvisorNotes() { + const router = useRouter() + const notes = trpc.advisor.users.getNotes.useQuery(+router.query.userId!, { + enabled: !!router.query.userId, + }) + + if (notes.isLoading) + return

Loading notes...

+ if (notes.isError) return

Failed to load notes

+ if (!notes.data.length) + return ( +

+ You have not left any notes for this user yet. +

+ ) + + return ( + <> + {notes.data.map((note) => ( +
+
+

+ Note from{' '} + {DateTime.fromJSDate(note.createdAt, { zone: 'utc' }).toFormat( + 'MMM dd, yyyy' + )} +

+ + + +
+ +
+ ))} + + ) +} + +function HoldingsTable({ data }: { data: any }) { + const columns = useMemo< + ColumnDef< + RouterOutput['advisor']['users']['getHoldings']['holdings'][number][number][number] + >[] + >( + () => [ + { + header: 'Security', + accessorFn(row) { + return `${row.symbol ? `(${row.symbol})` : ''} ${row.security_name}` + }, + cell({ getValue }) { + return ( + + {getValue() as any} + + ) + }, + }, + { + header: 'Quantity', + accessorFn(row) { + return NumberUtil.format(row.quantity, 'decimal') + }, + }, + { + header: 'Value', + accessorFn(row) { + return NumberUtil.format(row.value, 'currency') + }, + }, + { + header: 'Cost Basis', + accessorFn(row) { + return NumberUtil.format(row.cost_basis, 'currency') + }, + }, + { + header: 'Cost Basis / share', + accessorFn(row) { + return NumberUtil.format(row.cost_basis_per_share, 'currency') + }, + }, + { + header: 'Price', + accessorFn(row) { + return NumberUtil.format(row.price, 'currency') + }, + }, + { + header: 'Price Prev', + accessorFn(row) { + return NumberUtil.format(row.price_prev, 'currency') + }, + }, + ], + [] + ) + const table = useReactTable({ + columns, + data, + getCoreRowModel: getCoreRowModel(), + }) + + return ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ {flexRender(header.column.columnDef.header, header.getContext())} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+ ) +} + +function Holdings({ userId }: { userId: User['id'] }) { + const holdings = trpc.advisor.users.getHoldings.useQuery(userId) + + if (holdings.isLoading) + return

Loading holdings...

+ if (holdings.isError) return

Failed to load holdings

+ + return ( +
+
+

Holdings by institution

+
+ Copy all as CSV + +
+
+
+ {Object.entries(holdings.data.holdings).map(([connection, accountHoldings]) => ( +
+
{connection}
+ {Object.entries(accountHoldings).map(([account, holdings]) => ( +
+
+

{account}

+
+
+ +
+
+ ))} +
+ ))} +
+
+ ) +} + +export default function UserPage() { + const router = useRouter() + + const [isEditing, setIsEditing] = useState(false) + + const currentTabIdx = tabs.findIndex((t) => t.href === router.asPath.split('#').at(-1)) + + const user = trpc.advisor.users.get.useQuery(+router.query.userId!, { + enabled: !!router.query.userId, + }) + + if (user.isLoading) { + return ( +
+ +
+ ) + } + + if (user.isError) { + return ( +
+

Not Found

+

+ We can't find that conversation. +

+
+ ) + } + + const { picture, name } = user.data + + return ( + <> + setIsEditing(false)} /> +
+ + +
+ {/* Profile header */} +
+
+
+
+ profile picture +
+ +
+
+
+
+

+ {name} +

+
+
+
+
+

{name}

+
+
+
+ + {/* Tabs */} + +
+ 0 ? currentTabIdx : 0} + onChange={(idx) => { + router.push(`/users/${router.query.userId}#${tabs[idx].href}`) + }} + > +
+ +
+ {tabs.map((tab, idx) => ( + + cn( + selected + ? 'border-cyan-500 text-white' + : 'border-transparent text-gray-200 hover:text-white hover:border-cyan', + 'whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm focus-visible:outline-none' + ) + } + > + {tab.name} + + ))} +
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+ + ) +} + +UserPage.getLayout = (page) => ( + + {page} + +) + +function Data({ title, value }: { title: string; value: ReactNode }) { + return ( +
+
{title}
+
{value}
+
+ ) +} diff --git a/apps/advisor/pages/users/index.tsx b/apps/advisor/pages/users/index.tsx new file mode 100644 index 00000000000..883bfedba17 --- /dev/null +++ b/apps/advisor/pages/users/index.tsx @@ -0,0 +1,76 @@ +import { withPageAuthRequired } from '@auth0/nextjs-auth0' +import { RiUserSearchLine } from 'react-icons/ri' +import UserList from '../../components/UserList' +import Layout from '../../components/Layout' +import UserSearch from '../../components/UserSearch' +import { useState } from 'react' +import { trpc } from '../../lib/trpc' +import Link from 'next/link' + +// No typings available for this module +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fuzzysearch = require('fuzzysearch') + +export const getServerSideProps = withPageAuthRequired() + +export default function UsersPage() { + const users = trpc.advisor.users.getAll.useQuery() + const [query, setQuery] = useState('') + + const userList = (users.data ?? []).filter((u) => + fuzzysearch(query.toLowerCase(), u.name?.toLowerCase()) + ) + + return ( +
+
+ +
+ {query ? ( +
+
    + {userList.length ? ( + userList.map((user) => ( + setQuery('')} + className="flex items-center gap-3 p-4 rounded cursor-pointer hover:bg-gray-600" + > + {user.name + +
    + {user.name} + + {user.email} + +
    + + )) + ) : ( +
    +

    No users found

    +
    + )} +
+
+ ) : ( +
+ +

Users

+

Select a user to see their profile

+
+ )} +
+ ) +} + +UsersPage.getLayout = (page) => ( + + {page} + +) diff --git a/apps/advisor/postcss.config.js b/apps/advisor/postcss.config.js new file mode 100644 index 00000000000..59d8933b567 --- /dev/null +++ b/apps/advisor/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: { config: './apps/advisor/tailwind.config.js' }, + autoprefixer: {}, + }, +} diff --git a/apps/advisor/public/.gitkeep b/apps/advisor/public/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/advisor/public/assets/android-chrome-192x192.png b/apps/advisor/public/assets/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..70132a2add45bc714fdb1f4a114693afc8b38d2e GIT binary patch literal 5306 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 literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/android-chrome-512x512.png b/apps/advisor/public/assets/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..85076bf0292362638609a6b8e1659158b9f3d01d GIT binary patch literal 14471 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 aDlcG@njCp3Cz5n$0-&O(p-?Gj9`Ij=sBjbj literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/favicon-16x16.png b/apps/advisor/public/assets/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..1792601f67b50687d0426ede8001d0efe1ea2335 GIT binary patch literal 1151 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>Zfh0Br6h9z2tCMeCGw;SK?kxqJI|?{&^go{}IxP~?LFqmZOHE2jj%{;9ina+tgC?a0&B#)8)0|tX8o} ziT)D8{z*{=j9O3s{cFfSE5cViVPcQDZfAco8&_g;vzvBZOKXKvL?E;Bt#y?@v(6mg z=l0D?)RBnH$Osa$OwM#a@b2eeO=j~vWN=Esm`v<6afld@pN$vk+__kpui(xX|Z!h zrG#YV&mBJ`B^{ZVnw$#i=a>z8t*f)ywcXXt*wV$r&CB`y_4DQ9 z?e)X|GqfF8aPQuO3HR>ZyRhNIy?Y%VZ7W{PxUu6$%aJ8drd-+brN^gj&6_!Q_WWr& zG)L*%qe+)G6}5GpTJ>tytzD~5ReftacIuQ`+p}rY(#pPF+xCs`+qreqw&fkFV`V7W zXS8K{Xpa}rf2t*}5hW>!C8<`)MX5lF!N|bKP}jgj*T^u$(9Fu%z{=QM+rY@mz~Jn{ z<)=_IZ(KQX>fz)zI;m)=)6pr)S%02C&hvRbpV#xe-_QH`{Pp?dAEbM#sccsP0H{$Z zWIs94-&+|WpQ&8#M>!M|Tzy;tXt{veh*A1(#!~!z0O0Nh06GW27r6wz0pKJ7fW>G4 z>@NeL%Pzm`>nJx6hiRTM3m0b0sy{1CA$WM^vFsg z6C>QW&@kqin4l6|egImVvb5_|^_mr7md)WA|(0&Ly9jhUwB3(MrY2 z*@O`Z!iJq&b)Wb3W$JJx&gG2yQAj1)15v=TYjO!M8e6rqDQz|2E%C;o$nG{+mtqtW zFHqGgen4|h@GSC2(iQr7VHBg0iLVDF7`;d=tVZ%SdV?ta#xKv5I!OE3ZVATE2J+q~ zsriQQ>96%^eB)AGXE+}>q2FuE-dau_&_i&c3bS6iLc*Bx^|tPgg+bHYTY)JPWbbnV zhXDPe$Eg+Al;ypbMm1gpvAVxpR65_%UR@tE*LsH}l1OTz$8>`UNJh-SZ#7S2Bznn)1b8_}PB=TI;s}Opk#3q%$YbsN z+*RWs8JPz|tvTs!|D+r6-Rah)-oE_dHfuV1EjBH)iAXMy(z^C-wc#_2} z3G-~G*}h9F+NREQ$6^N;!<%qn&%NoJ+bzlp#n$)x%nfm+zu5dn4V`>fKS4pMz=WpO zvk&_Wpn~xG&TtP-D8F>Hv>IZ44#p6PW z<{gLqoDPL=?}-@6E-B%l@;n|`zz-Qa-GHCo`G!8)ta}Q_fHCIzWw{2wk1!--wW3zt zeaabX0U~aoW9!}xS*$qJFl&x)*EzyfgF*my@it{(m(+RIaxN?93Vdcwb{28^@<)_s zdVAhRe3Ybuf7mDpR_rYH)fLpsGPbm|6imi{P8Tx{9ceR%(nYQP;oAg?A9yCTJlu z>kpoehC`mbhMdSi=o9XUmD>|9!m`!coNCFlafSV01)y^CF#$ABSjs%V^V0JLXQb+b zE`3{O;!PT6T~VBI0($MlQ(x!(b!WY+y%;uipcZQq1>ps$$vSsK)A?5HK#yZby{e3` zm5Ege+}J77B6^w5s4gJQR=PI)$hIgy3Log>{hDpv2mJs!IDW3x%Rxv%)Bxvq>uYf1rw+f3Y!Wj7%$XfTnDYPu&}WqzcuE2!&-gXblbE@EPnzf9QP1T zTnvY4ADhCI16UF)i8z84&XO2Fw6P}<>`As*f~7ryFkE%*(SHI-$KzP>>Hi6 zqe&~)f@`A5kX26{9Mc+r9-Mgguy?3;XD@OPqFnXDgGF5p&IdQ^i}DqK>P{y&yG3XJ E3*m`7LjV8( literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/favicon.ico b/apps/advisor/public/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..f171cb79933e1999aaa066c9a0b6733f2375f3d2 GIT binary patch literal 15086 zcmeI33vd+W9l-Z;NiO8cLkfYI9DxKVrQ)DGEG2|Tr=12OI@8)tr3r74M=&%3I&zR8 z@)D#JrM7kIgQ6`EC=sfIo~Q#@z)~0)3c~TIPzuL|M;jm{{r&fD&&_7{vI#TH)VZDc z@&CU6>-+ZGz0J4ZwG4S z9b`v9+Hi#WE`3`EC*Z6Fr_gVPZ(E%MHW$oWk<< zgYggtZ$KT?+n@@cWV6Q6KML1vMBMg{tjkYLuXro-MH zLNZ*ZivwAIlGxhz}%IHTx zjmcS<;Rmo7#BOX`bk9OxXf*a( zp0Atq7Y~bpxMmQKujMnk1^HObYS}?s55mh()lA;T|8WSp71%;w~+r3H&JVg@=@_xP5yur2-hzE2MIeG$3z4)Yx*{u|nO zG$rvZy~Fn8JU)aZX$!gH4)Yx$zGGAU*6jG+2Pf{J&6~u|(&ZaU{Xz@#)e$GxFGkB} zN8&HPx3}QqX7CBV@4|FY$6nf^EYGn*s08t`i`;|HKwCR~Hu?}33rUdd|2cAu1!B|u zbk7}=eDZuv_7{cnNt_wt6QmW=_V3o%p1xjvwlF4ea+EW0q7kuyGozYyZL9zFup{6j zkK{8@eQK2D-w%C%?3M$23*zC&@Fr}rU<3LhX!KrUYzLp-uo5c4EZ=7Ii=nGAwk37) zNWZQ@eG}AR5C3M(+QROJeNE)i+IbJZd(86K)e)Znf3ew~w7VD5tny`&dCO0G3Eqoug%4Jtij+t=NM!UE1@kGjty(b*CkxRRl zog?M5W23Dkm;rJf{$yzJyxIO)MLnY<5FLT&2t-HV|9J%J)k9J4RC=-kX*Du)PxH9~@|P^l z>k7!gh@|ex3hI4%HCeT;#_a`MUZ%WcvPu4Jox#Xn)EotUPFE#E8oWc)+sKZ zQm-hriqosi7wPaQ_jq*0(*MlK) z!w?96r`6;v2IuGz9f7+z0_=Cnw;dfozRTzmLdvq|YdYUV-v(rlS0j^hcwI){ee?++ zb}hFi)%Pj%vgdjj&KQtA`BG@GZa_T`c7yR-jZ#!0I9dfSiyf;>OC4Hym+S8hh&+t7KyE5yz%BrlRruV7V^L3p*cCO9%H1<~+ z`(mHZIUX~&cder$w~nt~ehcg2&5)F`><_7H%*gM6?8UtSuNx5EbC4zN zA|HgcpyktYE<@K%>s!dTE4pJ~wznoa=|g*vJ#H%>O)vdx3m-=Cm3`sxm{INnmm>Jf zd1S-s2tHc-mH1@BkqExBmml7Tkuod+$^WH+J=6z649H%f?8Bdja|T3r2-LM9>lIIIr7|aDtD~&jJFe1P{JRV9 zB$VcEjVbH4S()2@jj~F;uU=QmWAIJa@?g)#F9-jQ5We?}{kS?MuTAxY{Jwz+`Te2a z<#9s?u+}?JW$dtlf${nMt0(5)fAxvM;qMKuP5*8}^~52)1LKDF5BnAm8z8=tLvms7 zb$!O5cw>9|I7q_7P=&JsAVL}*xVf>X_k6PZl)z^r* zi2sF9j7VZ4wPR*uovS0PhK~$?34I>-Z@SP1-YtR8~ zgN#w=7DkAV;eJ`jIstD_y|)?oSkz~<8VORU7Cf#qDnc<#}9bOi3^2n5#^8vnbX zCd;7&m`cKen!E+#;R~d?yhX82uNz8IU0PJYA&VQbkl|si#+N7h+BDz%T9-FZg|g_( z4q2y=362}W>lC)1lmy+SgLj3tpuKN~_wCW8fV`7rg8E!SI-os>zt~dY6ZjI&!FG`U ziy-eOzX8#SZK0eFG8^`UbfTB{n++8?H+EO#-rNH(m*>`|F6>?xTakOKoci9N{?;^! z$JUAZ2G~uEE5JGsY^y~NhzPYEoolyj#ee#+L2Jwu_|%SPbyO?CZnHRm00!& zdgku42L3tenQ@gVAn<&rK)kS{;rh@)Gfo}q5rJ+mQ zp73Nb^VV|aq28u#S9#(at|*c%lCn=*2c;_)pRI@sDlzokUNQ6?VHunV zu>;v?u8B_e4CQZAI&^?I$twqC*-MnUkJv2t%Y)u`_uqP1I}MTCM^TbXg7u0nigKV- TQBEyZ6mNM@cZRZ{`HTD? literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/generalsans/GeneralSans-Variable.woff2 b/apps/advisor/public/assets/fonts/generalsans/GeneralSans-Variable.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..34abc8c8bbd2759d1bc66bedb4603106a7c0e710 GIT binary patch literal 39032 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 JcJ0|0002iame>FQ literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/inter/Inter-Variable.woff2 b/apps/advisor/public/assets/fonts/inter/Inter-Variable.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..365eedc50cd0f46ea35a3176335fc67b51123fb4 GIT binary patch literal 324864 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≫bY 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≥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

6&qaNDxLwZnq2 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

ycBey{rl;kDc{Ai+(=LzE1TWNYW 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

w%k_tC9o+J?jlEWgdzIe0b`MG#H=N&ZEd~ha9 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 P*0S}5wluvYuZ{r##KtAz literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/monument/MonumentExtended-Heavy.woff2 b/apps/advisor/public/assets/fonts/monument/MonumentExtended-Heavy.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ceb90adcd02f5de6668314f196d4dc37dac99844 GIT binary patch literal 25988 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

Xj*n((*G|AVYgaqD+Lju0N+=zAi4ub%m_ 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 literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/monument/MonumentExtended-Medium.woff2 b/apps/advisor/public/assets/fonts/monument/MonumentExtended-Medium.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e2275a8fd3889089dc6c366b0fb963c437d5cf46 GIT binary patch literal 23968 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 L?()iVY+?rh3F;Wt literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/monument/MonumentExtended-Regular.woff2 b/apps/advisor/public/assets/fonts/monument/MonumentExtended-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..43dedd7ebd6de420017aa0b0e9724b0bb78e8ed4 GIT binary patch literal 24128 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 PKz_*W$Sc#q!94%~1UJA^ literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/fonts/monument/MonumentExtended-Thin.woff2 b/apps/advisor/public/assets/fonts/monument/MonumentExtended-Thin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f4dfa5953d71800f76263e3a3a977bf204b256e3 GIT binary patch literal 22896 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 literal 0 HcmV?d00001 diff --git a/apps/advisor/public/assets/images/advisor-avatar.png b/apps/advisor/public/assets/images/advisor-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..64fd904b84f2393bb21b4f31c8d863ed72065211 GIT binary patch literal 11027 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 N002ovPDHLkV1g + + + + + + + + + + + + + + + diff --git a/apps/advisor/public/assets/site.webmanifest b/apps/advisor/public/assets/site.webmanifest new file mode 100644 index 00000000000..2fad1a2f7dd --- /dev/null +++ b/apps/advisor/public/assets/site.webmanifest @@ -0,0 +1,20 @@ +{ + "name": "Maybe Advisor", + "short_name": "Maybe Advisor", + "description": "Modern day financial planning & wealth management", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#242629", + "background_color": "#242629", + "display": "standalone" +} diff --git a/apps/advisor/tailwind.config.js b/apps/advisor/tailwind.config.js new file mode 100644 index 00000000000..ac4d09a5cc4 --- /dev/null +++ b/apps/advisor/tailwind.config.js @@ -0,0 +1,69 @@ +const { join } = require('path') +const merge = require('lodash/fp/merge') + +// https://blog.nrwl.io/setup-next-js-to-use-tailwind-with-nx-849b7e21d8d0 +const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind') + +// TODO: Figure out how to simplify this import with scope +const designSystemConfig = require(__dirname + '/../../libs/design-system/tailwind.config.js') + +module.exports = merge(designSystemConfig, { + content: [ + join(__dirname, 'pages/**/*.{js,ts,jsx,tsx}'), + join(__dirname, 'components/**/*.{js,ts,jsx,tsx}'), + ...createGlobPatternsForDependencies(__dirname, '/**/!(*.stories|*.spec).{tsx,ts,jsx,js}'), + ], + theme: { + extend: { + transitionProperty: { + width: 'width', + }, + typography: () => { + const { white, cyan, gray } = designSystemConfig.theme.colors + return { + light: { + css: { + '--tw-prose-body': white, + '--tw-prose-headings': white, + '--tw-prose-lead': white, + '--tw-prose-links': cyan['600'], + '--tw-prose-bold': white, + '--tw-prose-counters': white, + '--tw-prose-bullets': gray['50'], + '--tw-prose-hr': gray['500'], + '--tw-prose-quotes': gray['50'], + '--tw-prose-quote-borders': gray['50'], + '--tw-prose-captions': white, + '--tw-prose-code': gray['200'], + '--tw-prose-pre-code': gray['100'], + '--tw-prose-pre-bg': gray['600'], + '--tw-prose-th-borders': gray['50'], + '--tw-prose-td-borders': gray['50'], + '--tw-prose-invert-body': white, + '--tw-prose-invert-headings': white, + '--tw-prose-invert-lead': white, + '--tw-prose-invert-links': cyan['600'], + '--tw-prose-invert-bold': white, + '--tw-prose-invert-counters': white, + '--tw-prose-invert-bullets': gray['50'], + '--tw-prose-invert-hr': gray['500'], + '--tw-prose-invert-quotes': gray['50'], + '--tw-prose-invert-quote-borders': gray['50'], + '--tw-prose-invert-captions': white, + '--tw-prose-invert-code': gray['200'], + '--tw-prose-invert-pre-code': gray['100'], + '--tw-prose-invert-pre-bg': gray['600'], + '--tw-prose-invert-th-borders': gray['50'], + '--tw-prose-invert-td-borders': gray['50'], + }, + }, + } + }, + }, + }, + plugins: [ + require('@tailwindcss/line-clamp'), + require('@tailwindcss/forms'), + require('@tailwindcss/typography'), + ], +}) diff --git a/apps/advisor/tsconfig.json b/apps/advisor/tsconfig.json new file mode 100644 index 00000000000..1222fbec4b6 --- /dev/null +++ b/apps/advisor/tsconfig.json @@ -0,0 +1,33 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "preserve", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "types": ["node", "jest"], + "strict": true, + "strictNullChecks": true, + "noImplicitAny": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "resolveJsonModule": true, + "isolatedModules": true, + "incremental": true, + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + "**/*.js", + "**/*.jsx", + "next-env.d.ts", + ".next/types/**/*.ts", + "../../custom-express.d.ts" + ], + "exclude": ["node_modules", "jest.config.ts"] +} diff --git a/apps/advisor/tsconfig.spec.json b/apps/advisor/tsconfig.spec.json new file mode 100644 index 00000000000..ba2591c95d0 --- /dev/null +++ b/apps/advisor/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"], + "jsx": "react" + }, + "include": [ + "jest.config.ts", + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts" + ] +} diff --git a/apps/client/.eslintrc.json b/apps/client/.eslintrc.json new file mode 100644 index 00000000000..e1e03519ded --- /dev/null +++ b/apps/client/.eslintrc.json @@ -0,0 +1,34 @@ +{ + "extends": [ + "plugin:@nrwl/nx/react-typescript", + "../../.eslintrc.json", + "next", + "next/core-web-vitals" + ], + "ignorePatterns": ["!**/*", "styles.css", "**/*.csv", "**/public/*", "**/.next/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@next/next/no-img-element": "off", + "@next/next/no-html-link-for-pages": "off" + } + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ], + "env": { + "jest": true + }, + "settings": { + "next": { + "rootDir": "apps/advisor" + } + } +} diff --git a/apps/client/components/APM.tsx b/apps/client/components/APM.tsx new file mode 100644 index 00000000000..96262fecb67 --- /dev/null +++ b/apps/client/components/APM.tsx @@ -0,0 +1,53 @@ +import { useAuth0 } from '@auth0/auth0-react' +import { useIntercom } from '@maybe-finance/client/shared' +import { useLDClient } from 'launchdarkly-react-client-sdk' +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import * as Sentry from '@sentry/react' + +export default function APM() { + const { user } = useAuth0() + const router = useRouter() + const ld = useLDClient() + const intercom = useIntercom() + + // Boot intercom + useEffect(() => { + const isBooted = intercom.boot() + + const handleRouteChange = () => { + if (isBooted) { + intercom.update() + } + } + + router.events.on('routeChangeComplete', handleRouteChange) + + return () => router.events.off('routeChangeComplete', handleRouteChange) + }, [intercom, router.events]) + + // Identify Sentry user + useEffect(() => { + if (user) { + Sentry.setUser({ + id: user.sub, + email: user.email, + }) + } + }, [user]) + + // Identify LaunchDarkly user + useEffect(() => { + if (ld && user) { + ld.waitUntilReady().then(() => { + ld.identify({ + key: user.sub, + email: user.email, + name: user.name, + }) + }) + } + }, [ld, user]) + + return null +} diff --git a/apps/client/components/Maintenance.tsx b/apps/client/components/Maintenance.tsx new file mode 100644 index 00000000000..545be9fb6a5 --- /dev/null +++ b/apps/client/components/Maintenance.tsx @@ -0,0 +1,80 @@ +export default function Maintenance() { + return ( +

+ ) +} diff --git a/apps/client/components/Meta.tsx b/apps/client/components/Meta.tsx new file mode 100644 index 00000000000..6328946ae39 --- /dev/null +++ b/apps/client/components/Meta.tsx @@ -0,0 +1,62 @@ +import Head from 'next/head' +import React from 'react' +import env from '../env' + +export default function Meta() { + return ( + + {/* */} + Maybe + + + + {/* */} + + + + + + + {/* */} + + + + + + + {/* */} + + + {/* */} + + + + {/* */} + + + + + + {/* Intercom */} + + Maybe Finance + diff --git a/apps/server/src/app/app.ts b/apps/server/src/app/app.ts new file mode 100644 index 00000000000..4f99a2dd420 --- /dev/null +++ b/apps/server/src/app/app.ts @@ -0,0 +1,184 @@ +import type { RequestHandler } from 'express' +import express from 'express' +import cors from 'cors' +import morgan from 'morgan' +import * as Sentry from '@sentry/node' +import * as SentryTracing from '@sentry/tracing' +import * as trpcExpress from '@trpc/server/adapters/express' +import { appRouter, createTRPCContext } from './trpc' +/** + * In Express 4.x, asynchronous errors are NOT automatically passed to next(). This middleware is a small + * wrapper around Express that enables automatic async error handling + * + * Benefit: Eliminates the need for try / catch blocks in routes (i.e. `next(err)` will automatically be called on failed Promise) + * + * When stable Express 5.x is released, this won't be necessary - https://github.com/expressjs/express/issues/4543#issuecomment-789256044 + */ +import 'express-async-errors' +import logger from './lib/logger' +import prisma from './lib/prisma' +import { + defaultErrorHandler, + validateAuth0Jwt, + superjson, + authErrorHandler, + maintenance, + identifySentryUser, + devOnly, +} from './middleware' +import { + usersRouter, + accountsRouter, + connectionsRouter, + adminRouter, + webhooksRouter, + plaidRouter, + accountRollupRouter, + valuationsRouter, + institutionsRouter, + finicityRouter, + transactionsRouter, + holdingsRouter, + securitiesRouter, + plansRouter, + toolsRouter, + conversationsRouter, + messagesRouter, + notificationsRouter, + publicRouter, + e2eRouter, +} from './routes' +import env from '../env' + +const app = express() + +// put health check before maintenance and other middleware +app.get('/health', (_req, res) => { + res.status(200).json({ status: 'OK' }) +}) + +if (process.env.NODE_ENV !== 'test') { + maintenance(app) +} + +// Mostly defaults recommended by quickstart +// - https://docs.sentry.io/platforms/node/guides/express/ +// - https://docs.sentry.io/platforms/node/guides/express/performance/ +Sentry.init({ + dsn: env.NX_SENTRY_DSN, + environment: env.NX_SENTRY_ENV, + maxValueLength: 8196, + integrations: [ + new Sentry.Integrations.Http({ tracing: true }), + new SentryTracing.Integrations.Express({ app }), + new SentryTracing.Integrations.Postgres(), + new SentryTracing.Integrations.Prisma({ client: prisma }), + ], + tracesSampler: (ctx) => { + return ctx.request?.method === 'OPTIONS' ? false : ctx.parentSampled ?? true + }, +}) + +app.use(Sentry.Handlers.requestHandler()) +app.use(Sentry.Handlers.tracingHandler()) + +app.get('/', (req, res) => { + res.render('pages/index', { error: req.query.error }) +}) + +// Only Auth0 users with a role of "admin" can view these pages (i.e. Maybe Employees) +app.use(express.static(__dirname + '/assets')) + +const origin = [env.NX_CLIENT_URL, env.NX_ADVISOR_URL, ...env.NX_CORS_ORIGINS] +logger.info(`CORS origins: ${origin}`) +app.use(cors({ origin })) +app.options('*', cors() as RequestHandler) + +app.set('view engine', 'ejs').set('views', __dirname + '/app/admin/views') +app.use('/admin', adminRouter) + +app.use( + morgan(env.NX_MORGAN_LOG_LEVEL, { + stream: { + write: function (message: string) { + logger.http(message.trim()) // Trim because Morgan and Logger both add \n, so avoid duplicates here + }, + }, + }) +) + +// Stripe webhooks require a raw request body +app.use('/v1/stripe', express.raw({ type: 'application/json' })) + +app.use(express.urlencoded({ extended: true })) +app.use(express.json({ limit: '50mb' })) // Finicity sends large response bodies for webhooks + +// ========================================= +// API ⬇️ +// ========================================= + +app.use( + '/trpc', + validateAuth0Jwt, + trpcExpress.createExpressMiddleware({ + router: appRouter, + createContext: createTRPCContext, + }) +) + +/** + * This intercepts the express.json() middleware and modifies both the outgoing and incoming requests + * + * It is necessary because our models use Date, BigInt, and other non serializable JSON types + * + * Outgoing requests are serialized, and will be in the format { data: { json, meta }, ...rest } + * Incoming requests are deserialized (client sends a serialized object { json, meta }), and attached to req.body + */ +app.use(superjson) + +// Public routes +// Keep this route public for Render health checks - https://render.com/docs/deploys#health-checks +app.get('/v1', (_req, res) => { + res.status(200).json({ msg: 'API Running' }) +}) + +app.get('/debug-sentry', function mainHandler(_req, _res) { + throw new Error('Server sentry is working correctly') +}) + +app.use('/tools', devOnly, toolsRouter) + +app.use('/v1', webhooksRouter) + +app.use('/v1', publicRouter) + +// All routes AFTER this line are protected via OAuth +app.use('/v1', validateAuth0Jwt) + +// Private routes +app.use('/v1/users', usersRouter) +app.use('/v1/e2e', e2eRouter) +app.use('/v1/plaid', plaidRouter) +app.use('/v1/finicity', finicityRouter) +app.use('/v1/accounts', accountsRouter) +app.use('/v1/account-rollup', accountRollupRouter) +app.use('/v1/connections', connectionsRouter) +app.use('/v1/valuations', valuationsRouter) +app.use('/v1/institutions', institutionsRouter) +app.use('/v1/transactions', transactionsRouter) +app.use('/v1/holdings', holdingsRouter) +app.use('/v1/securities', securitiesRouter) +app.use('/v1/plans', plansRouter) +app.use('/v1/conversations', conversationsRouter) +app.use('/v1/messages', messagesRouter) +app.use('/v1/notifications', notificationsRouter) + +// Sentry must be the *first* handler +app.use(identifySentryUser) +app.use(Sentry.Handlers.errorHandler()) + +// Errors will pass through in order listed, these MUST be at the bottom of this server file +app.use(authErrorHandler) // Handles auth/authz specific errors +app.use(defaultErrorHandler) // Fallback handler + +export default app diff --git a/apps/server/src/app/lib/ability.ts b/apps/server/src/app/lib/ability.ts new file mode 100644 index 00000000000..4f784f5a196 --- /dev/null +++ b/apps/server/src/app/lib/ability.ts @@ -0,0 +1,165 @@ +import type { Subjects } from '@casl/prisma' +import { PrismaAbility, accessibleBy } from '@casl/prisma' +import type { AbilityClass } from '@casl/ability' +import { AbilityBuilder, ForbiddenError } from '@casl/ability' +import type { SharedType } from '@maybe-finance/shared' +import type { + User, + Account, + AccountBalance, + AccountConnection, + Transaction, + Valuation, + Holding, + InvestmentTransaction, + Security, + SecurityPricing, + Institution, + ProviderInstitution, + Plan, + Conversation, + Advisor, + Message, + ConversationAdvisor, +} from '@prisma/client' + +type CRUDActions = 'create' | 'read' | 'update' | 'delete' +type AppActions = CRUDActions | 'manage' + +type PrismaSubjects = Subjects<{ + User: User + Account: Account + AccountBalance: AccountBalance + AccountConnection: AccountConnection + Transaction: Transaction + Valuation: Valuation + Security: Security + SecurityPricing: SecurityPricing + Holding: Holding + InvestmentTransaction: InvestmentTransaction + Institution: Institution + ProviderInstitution: ProviderInstitution + Plan: Omit + Conversation: Conversation + ConversationAdvisor: ConversationAdvisor + Message: Message + AdvisorNotes: { userId: User['id'] } +}> +type AppSubjects = PrismaSubjects | 'all' + +type AppAbility = PrismaAbility<[AppActions, AppSubjects]> + +export default function defineAbilityFor( + user: (Pick & { roles: SharedType.UserRole[]; advisor: Advisor | null }) | null +) { + const { can, build } = new AbilityBuilder(PrismaAbility as AbilityClass) + + if (user) { + if (user.roles.includes('Admin')) { + can('manage', 'Account') + can('manage', 'AccountConnection') + can('manage', 'Valuation') + can('manage', 'User') + can('manage', 'Institution') + can('manage', 'Plan') + can('manage', 'Holding') + can('manage', 'Conversation') + can('manage', 'ConversationAdvisor') + can('manage', 'Message') + can('manage', 'AdvisorNotes') + } + + if (user.advisor?.approvalStatus === 'approved') { + // Conversation + // ToDo: add filter once we lock down advisor conversation permissions eg. { advisors: { some: { advisorId: user.advisor.id } } } + can('read', 'Conversation') + can('update', 'Conversation') + + // ConversationAdvisor + can('create', 'ConversationAdvisor') + can('delete', 'ConversationAdvisor') + + // User.AdvisorNotes + // ToDo: add filter once we lock down advisor conversation permissions eg. { userId: { in: user.advisor.conversations.map((c) => c.userId) } } + can('manage', 'AdvisorNotes') + } + + // Account + can('create', 'Account') + can('read', 'Account', { userId: user.id }) + can('read', 'Account', { accountConnection: { is: { userId: user.id } } }) + can('update', 'Account', { userId: user.id }) + can('update', 'Account', { accountConnection: { is: { userId: user.id } } }) + can('delete', 'Account', { userId: user.id }) + can('delete', 'Account', { accountConnection: { is: { userId: user.id } } }) + + // Valuation + can('create', 'Valuation') + can('read', 'Valuation', { account: { is: { userId: user.id } } }) + can('update', 'Valuation', { account: { is: { userId: user.id } } }) + can('delete', 'Valuation', { account: { is: { userId: user.id } } }) + + // AccountConnection + can('create', 'AccountConnection') + can('read', 'AccountConnection', { userId: user.id }) + can('update', 'AccountConnection', { userId: user.id }) + can('delete', 'AccountConnection', { userId: user.id }) + + // User + can('read', 'User', { id: user.id }) + can('update', 'User', { id: user.id }) + can('delete', 'User', { id: user.id }) + + // Institution + can('read', 'Institution') + + // Security + can('read', 'Security') + + // Transaction + can('read', 'Transaction', { account: { is: { userId: user.id } } }) + can('read', 'Transaction', { + account: { is: { accountConnection: { is: { userId: user.id } } } }, + }) + can('update', 'Transaction', { account: { is: { userId: user.id } } }) + can('update', 'Transaction', { + account: { is: { accountConnection: { is: { userId: user.id } } } }, + }) + + // Holding + can('read', 'Holding', { account: { is: { userId: user.id } } }) + can('read', 'Holding', { + account: { is: { accountConnection: { is: { userId: user.id } } } }, + }) + can('update', 'Holding', { account: { is: { userId: user.id } } }) + can('update', 'Holding', { + account: { is: { accountConnection: { is: { userId: user.id } } } }, + }) + + // Plan + can('create', 'Plan') + can('read', 'Plan', { userId: user.id }) + can('update', 'Plan', { userId: user.id }) + can('delete', 'Plan', { userId: user.id }) + + // Conversation + can('create', 'Conversation') + can('read', 'Conversation', { userId: user.id }) + can('update', 'Conversation', { userId: user.id }) + can('delete', 'Conversation', { userId: user.id }) + + // Message + can('update', 'Message', { userId: user.id }) + can('delete', 'Message', { userId: user.id }) + } + + const ability = build() + + return { + can: ability.can, + throwUnlessCan: (...args: Parameters) => { + ForbiddenError.from(ability).throwUnlessCan(...args) + }, + where: accessibleBy(ability), + } +} diff --git a/apps/server/src/app/lib/auth0.ts b/apps/server/src/app/lib/auth0.ts new file mode 100644 index 00000000000..1c96ffffa83 --- /dev/null +++ b/apps/server/src/app/lib/auth0.ts @@ -0,0 +1,18 @@ +import type { SharedType } from '@maybe-finance/shared' +import { ManagementClient } from 'auth0' +import env from '../../env' + +/** + * Management API Documentation + * - https://auth0.com/docs/api/management/v2 + * - https://auth0.github.io/node-auth0/module-management.ManagementClient.html + */ +export const managementClient = new ManagementClient< + SharedType.MaybeAppMetadata, + SharedType.MaybeUserMetadata +>({ + domain: env.NX_AUTH0_DOMAIN, + clientId: env.NX_AUTH0_MGMT_CLIENT_ID, + clientSecret: env.NX_AUTH0_MGMT_CLIENT_SECRET, + scope: 'read:users update:users delete:users', +}) diff --git a/apps/server/src/app/lib/convertKit.ts b/apps/server/src/app/lib/convertKit.ts new file mode 100644 index 00000000000..7bdac90a93d --- /dev/null +++ b/apps/server/src/app/lib/convertKit.ts @@ -0,0 +1,66 @@ +import type { SharedType } from '@maybe-finance/shared' +import type { AxiosInstance } from 'axios' +import axios from 'axios' +import env from '../../env' + +class ConvertKitApi { + private axios: AxiosInstance + + constructor(private readonly apiSecret: string) { + this.axios = axios.create({ + baseURL: 'https://api.convertkit.com/v3', + }) + } + + async getSubscription(subscriberId: number | null) { + // Until we have the id stored in DB, assume no subscription + if (!subscriberId) { + return { + isSubscribed: false, + } + } + + const res = await this.axios.get<{ subscriber: SharedType.ConvertKitSubscriber }>( + `/subscribers/${subscriberId}`, + { + params: { + api_secret: this.apiSecret, + }, + } + ) + + return { + isSubscribed: res.data ? res.data.subscriber.state === 'active' : false, + subscriber: res.data.subscriber, + } + } + + async subscribe(email: string) { + const res = await this.axios.post<{ subscription: SharedType.ConvertKitSubscription }>( + '/forms/2279973/subscribe', // The main mailing list ID + { + api_secret: this.apiSecret, + email, + } + ) + + return res.data + } + + async unsubscribe(email: string) { + const res = await this.axios.put<{ subscriber: SharedType.ConvertKitSubscriber }>( + '/unsubscribe', + { + api_secret: this.apiSecret, + email, + } + ) + + return res.data + } +} + +// Prevent multiple instances of S3 client +const convertKit = new ConvertKitApi(env.NX_CONVERTKIT_SECRET) + +export default convertKit diff --git a/apps/server/src/app/lib/endpoint.ts b/apps/server/src/app/lib/endpoint.ts new file mode 100644 index 00000000000..fa540e18a2e --- /dev/null +++ b/apps/server/src/app/lib/endpoint.ts @@ -0,0 +1,374 @@ +import type { IFeatureFlagService, IMarketDataService } from '@maybe-finance/server/shared' +import type { + IAccountQueryService, + IInstitutionService, + IInsightService, + ISecurityPricingService, + IPlanService, + IEmailService, +} from '@maybe-finance/server/features' +import { MessageService } from '@maybe-finance/server/features' +import { + CryptoService, + EndpointFactory, + QueueService, + PgService, + LaunchDarklyFeatureFlagService, + PolygonMarketDataService, + CacheService, + ServerUtil, + RedisCacheBackend, + BullQueueFactory, + InMemoryQueueFactory, +} from '@maybe-finance/server/shared' +import type { Request } from 'express' +import Redis from 'ioredis' +import { + AccountService, + AccountConnectionService, + UserService, + EmailService, + AccountQueryService, + ValuationService, + InstitutionService, + PlaidService, + AccountConnectionProviderFactory, + BalanceSyncStrategyFactory, + ValuationBalanceSyncStrategy, + TransactionBalanceSyncStrategy, + InvestmentTransactionBalanceSyncStrategy, + PlaidETL, + FinicityService, + FinicityETL, + InstitutionProviderFactory, + FinicityWebhookHandler, + PlaidWebhookHandler, + InsightService, + SecurityPricingService, + TransactionService, + HoldingService, + LoanBalanceSyncStrategy, + PlanService, + ProjectionCalculator, + ConversationService, + StripeWebhookHandler, +} from '@maybe-finance/server/features' +import { SharedType } from '@maybe-finance/shared' +import prisma from './prisma' +import ldClient from './ldClient' +import plaid, { getPlaidWebhookUrl } from './plaid' +import finicity, { getFinicityTxPushUrl, getFinicityWebhookUrl } from './finicity' +import stripe from './stripe' +import convertKit from './convertKit' +import postmark from './postmark' +import { managementClient } from './auth0' +import s3 from './s3' +import secretsClient from './secretsClient' +import defineAbilityFor from './ability' +import env from '../../env' +import logger from '../lib/logger' + +// shared services + +const redis = new Redis(env.NX_REDIS_URL, { + retryStrategy: ServerUtil.redisRetryStrategy({ maxAttempts: 5 }), +}) + +const featureFlagService: IFeatureFlagService = new LaunchDarklyFeatureFlagService(ldClient) + +export const queueService = new QueueService( + logger.child({ service: 'QueueService' }), + process.env.NODE_ENV === 'test' + ? new InMemoryQueueFactory() + : new BullQueueFactory(logger.child({ service: 'BullQueueFactory' }), env.NX_REDIS_URL) +) + +export const emailService: IEmailService = new EmailService( + logger.child({ service: 'EmailService' }), + postmark, + { + from: env.NX_POSTMARK_FROM_ADDRESS, + replyTo: env.NX_POSTMARK_REPLY_TO_ADDRESS, + } +) + +const cryptoService = new CryptoService(env.NX_DATABASE_SECRET) + +const pgService = new PgService(logger.child({ service: 'PgService' }), env.NX_DATABASE_URL) + +const cacheService = new CacheService( + logger.child({ service: 'CacheService' }), + new RedisCacheBackend(redis) +) + +const marketDataService: IMarketDataService = new PolygonMarketDataService( + logger.child({ service: 'PolygonMarketDataService' }), + env.NX_POLYGON_API_KEY, + cacheService +) + +const securityPricingService: ISecurityPricingService = new SecurityPricingService( + logger.child({ service: 'SecurityPricingService' }), + prisma, + marketDataService +) + +const insightService: IInsightService = new InsightService( + logger.child({ service: 'InsightService' }), + prisma +) + +const planService: IPlanService = new PlanService( + prisma, + new ProjectionCalculator(), + insightService +) + +// providers + +const plaidService = new PlaidService( + logger.child({ service: 'PlaidService' }), + prisma, + plaid, + new PlaidETL( + logger.child({ service: 'PlaidETL' }), + prisma, + plaid, + cryptoService, + marketDataService + ), + cryptoService, + getPlaidWebhookUrl(), + env.NX_CLIENT_URL_CUSTOM || env.NX_CLIENT_URL +) + +const finicityService = new FinicityService( + logger.child({ service: 'FinicityService' }), + prisma, + finicity, + new FinicityETL(logger.child({ service: 'FinicityETL' }), prisma, finicity), + getFinicityWebhookUrl(), + env.NX_FINICITY_ENV === 'sandbox' +) + +// account-connection + +const accountConnectionProviderFactory = new AccountConnectionProviderFactory({ + plaid: plaidService, + finicity: finicityService, +}) + +const transactionStrategy = new TransactionBalanceSyncStrategy( + logger.child({ service: 'TransactionBalanceSyncStrategy' }), + prisma +) + +const investmentTransactionStrategy = new InvestmentTransactionBalanceSyncStrategy( + logger.child({ service: 'InvestmentTransactionBalanceSyncStrategy' }), + prisma +) + +const valuationStrategy = new ValuationBalanceSyncStrategy( + logger.child({ service: 'ValuationBalanceSyncStrategy' }), + prisma +) + +const loanStrategy = new LoanBalanceSyncStrategy( + logger.child({ service: 'LoanBalanceSyncStrategy' }), + prisma +) + +const balanceSyncStrategyFactory = new BalanceSyncStrategyFactory({ + INVESTMENT: investmentTransactionStrategy, + DEPOSITORY: transactionStrategy, + CREDIT: transactionStrategy, + LOAN: loanStrategy, + PROPERTY: valuationStrategy, + VEHICLE: valuationStrategy, + OTHER_ASSET: valuationStrategy, + OTHER_LIABILITY: valuationStrategy, +}) + +const accountConnectionService = new AccountConnectionService( + logger.child({ service: 'AccountConnectionService' }), + prisma, + accountConnectionProviderFactory, + balanceSyncStrategyFactory, + securityPricingService, + queueService.getQueue('sync-account-connection') +) + +// account + +const accountQueryService: IAccountQueryService = new AccountQueryService( + logger.child({ service: 'AccountQueryService' }), + pgService +) + +const accountService = new AccountService( + logger.child({ service: 'AccountService' }), + prisma, + accountQueryService, + queueService.getQueue('sync-account'), + queueService.getQueue('sync-account-connection'), + balanceSyncStrategyFactory +) + +// user + +const userService = new UserService( + logger.child({ service: 'UserService' }), + prisma, + accountQueryService, + balanceSyncStrategyFactory, + queueService.getQueue('sync-user'), + queueService.getQueue('purge-user'), + managementClient, + stripe +) + +// institution + +const institutionProviderFactory = new InstitutionProviderFactory({ + PLAID: plaidService, + FINICITY: finicityService, +}) + +const institutionService: IInstitutionService = new InstitutionService( + logger.child({ service: 'InstitutionService' }), + prisma, + pgService, + institutionProviderFactory +) + +// valuation + +const valuationService = new ValuationService( + logger.child({ service: 'ValuationService' }), + prisma, + accountQueryService +) + +// transaction + +const transactionService = new TransactionService( + logger.child({ service: 'TransactionService' }), + prisma +) + +// holding + +const holdingService = new HoldingService(logger.child({ service: 'HoldingService' }), prisma) + +// conversation + +const conversationService = new ConversationService( + logger.child({ service: 'ConversationService' }), + prisma, + queueService +) + +const messageService = new MessageService( + logger.child({ service: 'MessageService' }), + prisma, + queueService +) + +// webhooks + +const plaidWebhooks = new PlaidWebhookHandler( + logger.child({ service: 'PlaidWebhookHandler' }), + prisma, + plaid, + accountConnectionService, + queueService +) + +const finicityWebhooks = new FinicityWebhookHandler( + logger.child({ service: 'FinicityWebhookHandler' }), + prisma, + finicity, + accountConnectionService, + getFinicityTxPushUrl() +) + +const stripeWebhooks = new StripeWebhookHandler( + logger.child({ service: 'StripeWebhookHandler' }), + prisma, + stripe +) + +// helper function for parsing JWT and loading User record +async function getCurrentUser(jwt: NonNullable) { + if (!jwt.sub) throw new Error(`jwt missing sub`) + if (!jwt['https://maybe.co/email']) throw new Error(`jwt missing email`) + + const user = + (await prisma.user.findUnique({ + where: { auth0Id: jwt.sub }, + include: { advisor: true }, + })) ?? + (await prisma.user.upsert({ + where: { auth0Id: jwt.sub }, + create: { + auth0Id: jwt.sub, + email: jwt['https://maybe.co/email'], + picture: jwt[SharedType.Auth0CustomNamespace.Picture], + firstName: jwt[SharedType.Auth0CustomNamespace.UserMetadata]?.['firstName'], + lastName: jwt[SharedType.Auth0CustomNamespace.UserMetadata]?.['lastName'], + }, + update: {}, + include: { advisor: true }, + })) + + return { + ...user, + roles: jwt[SharedType.Auth0CustomNamespace.Roles] ?? [], + primaryIdentity: jwt[SharedType.Auth0CustomNamespace.PrimaryIdentity] ?? {}, + userMetadata: jwt[SharedType.Auth0CustomNamespace.UserMetadata] ?? {}, + appMetadata: jwt[SharedType.Auth0CustomNamespace.AppMetadata] ?? {}, + } +} + +export async function createContext(req: Request) { + const user = req.user ? await getCurrentUser(req.user) : null + + return { + prisma, + plaid, + stripe, + convertKit, + s3, + secretsClient, + managementClient, + logger, + user, + ability: defineAbilityFor(user), + accountService, + transactionService, + holdingService, + accountConnectionService, + userService, + valuationService, + institutionService, + cryptoService, + featureFlagService, + queueService, + plaidService, + plaidWebhooks, + finicityService, + finicityWebhooks, + stripeWebhooks, + insightService, + marketDataService, + planService, + conversationService, + messageService, + emailService, + } +} + +export default new EndpointFactory({ + createContext, + onSuccess: (req, res, data) => res.status(200).superjson(data), +}) diff --git a/apps/server/src/app/lib/finicity.ts b/apps/server/src/app/lib/finicity.ts new file mode 100644 index 00000000000..63ccb5ca68c --- /dev/null +++ b/apps/server/src/app/lib/finicity.ts @@ -0,0 +1,21 @@ +import { FinicityApi } from '@maybe-finance/finicity-api' +import { getWebhookUrl } from './webhook' +import env from '../../env' + +const finicity = new FinicityApi( + env.NX_FINICITY_APP_KEY, + env.NX_FINICITY_PARTNER_ID, + env.NX_FINICITY_PARTNER_SECRET +) + +export default finicity + +export async function getFinicityWebhookUrl() { + const webhookUrl = await getWebhookUrl() + return `${webhookUrl}/v1/finicity/webhook` +} + +export async function getFinicityTxPushUrl() { + const webhookUrl = await getWebhookUrl() + return `${webhookUrl}/v1/finicity/txpush` +} diff --git a/apps/server/src/app/lib/ldClient.ts b/apps/server/src/app/lib/ldClient.ts new file mode 100644 index 00000000000..80d866c4bad --- /dev/null +++ b/apps/server/src/app/lib/ldClient.ts @@ -0,0 +1,6 @@ +import { init } from 'launchdarkly-node-server-sdk' +import env from '../../env' + +const ldClient = init(env.NX_LD_SDK_KEY, { offline: process.env.NODE_ENV === 'test' }) + +export default ldClient diff --git a/apps/server/src/app/lib/logger.ts b/apps/server/src/app/lib/logger.ts new file mode 100644 index 00000000000..fdd3af41ea7 --- /dev/null +++ b/apps/server/src/app/lib/logger.ts @@ -0,0 +1,23 @@ +import { createLogger } from '@maybe-finance/server/shared' +import ldClient from './ldClient' + +const logger = createLogger({ + level: 'info', +}) + +function setLevel() { + ldClient + .variation('server-log-level', { key: 'anonymous-server', anonymous: true }, 'info') + .then((level) => { + logger.level = level + logger[level](`Server logger using level: ${level}`) + }) +} + +// Don't configure for Jest +if (process.env.NODE_ENV !== 'test') { + ldClient.waitForInitialization().then(setLevel) + ldClient.on('update:server-log-level', setLevel) +} + +export default logger diff --git a/apps/server/src/app/lib/multerS3Storage.ts b/apps/server/src/app/lib/multerS3Storage.ts new file mode 100644 index 00000000000..2803ffd2a25 --- /dev/null +++ b/apps/server/src/app/lib/multerS3Storage.ts @@ -0,0 +1,77 @@ +/** + * Custom storage engine for S3 + * @see https://github.com/expressjs/multer/blob/master/StorageEngine.md + * + * A simplified version of the `multer-s3` engine that properly supports + * MD5 digests to satisfy S3 Object Lock PutObject requirements + */ + +import type { StorageEngine } from 'multer' +import type { Logger } from 'winston' +import { v4 as uuid } from 'uuid' +import mime from 'mime-types' +import crypto from 'crypto' +import type { Request } from 'express' +import type { S3Service } from '@maybe-finance/server/shared' + +type S3StorageOpts = { + s3: S3Service + getFilename?: (req: Request) => string +} + +class S3Storage implements StorageEngine { + constructor(private readonly opts: S3StorageOpts, private readonly logger: Logger) { + if (!opts.s3) throw new Error('Must provide S3Client instance') + } + + _handleFile(...params: Parameters) { + const [req, file, callback] = params + + const chunks: any[] = [] + + file.stream.on('data', (chunk) => chunks.push(chunk)) + file.stream.on('end', () => { + const Body = Buffer.concat(chunks) + const md5Hash = crypto.createHash('md5').update(Body).digest('base64') + + const filename = this.opts.getFilename ? this.opts.getFilename(req) : null + const Key = `${filename ?? uuid()}.${mime.extension(file.mimetype)}` + + this.logger.info(`Multer uploading file key=${Key} size=${Body.length}`) + + this.opts.s3 + .upload({ + bucketKey: 'private', + Key, + Body, + ContentMD5: md5Hash, + }) + .then(() => { + callback(null, { + size: Body.length, + path: Key, + mimetype: file.mimetype, + }) + }) + }) + } + + _removeFile(...params: Parameters) { + const [req, file, callback] = params + + this.logger.warn(`Multer removing file ${file.path}`) + + // Since object lock is turned on, this will create a new version, but never delete/overwrite + this.opts.s3 + .delete({ + bucketKey: 'private', + Key: file.path, + }) + .then(() => callback(null)) + .catch((err) => callback(err)) + } +} + +export default function (opts: S3StorageOpts, logger: Logger) { + return new S3Storage(opts, logger) as StorageEngine +} diff --git a/apps/server/src/app/lib/plaid.ts b/apps/server/src/app/lib/plaid.ts new file mode 100644 index 00000000000..2f9ee222ab2 --- /dev/null +++ b/apps/server/src/app/lib/plaid.ts @@ -0,0 +1,23 @@ +import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid' +import { getWebhookUrl } from './webhook' +import env from '../../env' + +// https://plaid.com/docs/api/versioning/#how-to-set-your-api-version +const configuration = new Configuration({ + basePath: PlaidEnvironments[env.NX_PLAID_ENV], + baseOptions: { + headers: { + 'PLAID-CLIENT-ID': env.NX_PLAID_CLIENT_ID, + 'PLAID-SECRET': env.NX_PLAID_SECRET, + }, + }, +}) + +const plaid = new PlaidApi(configuration) + +export default plaid + +export async function getPlaidWebhookUrl() { + const webhookUrl = await getWebhookUrl() + return `${webhookUrl}/v1/plaid/webhook` +} diff --git a/apps/server/src/app/lib/postmark.ts b/apps/server/src/app/lib/postmark.ts new file mode 100644 index 00000000000..48eddf1bd7a --- /dev/null +++ b/apps/server/src/app/lib/postmark.ts @@ -0,0 +1,6 @@ +import { ServerClient } from 'postmark' +import env from '../../env' + +const postmark = new ServerClient(env.NX_POSTMARK_API_TOKEN) + +export default postmark diff --git a/apps/server/src/app/lib/prisma.ts b/apps/server/src/app/lib/prisma.ts new file mode 100644 index 00000000000..41219914b24 --- /dev/null +++ b/apps/server/src/app/lib/prisma.ts @@ -0,0 +1,50 @@ +import { PrismaClient } from '@prisma/client' +import { DbUtil } from '@maybe-finance/server/shared' +import globalLogger from './logger' + +const logger = globalLogger.child({ service: 'PrismaClient' }) + +// https://stackoverflow.com/a/68328402 +declare global { + var prisma: PrismaClient // eslint-disable-line +} + +function createPrismaClient() { + const prisma = new PrismaClient({ + log: [ + { emit: 'event', level: 'query' }, + { emit: 'event', level: 'info' }, + { emit: 'event', level: 'warn' }, + { emit: 'event', level: 'error' }, + ], + }) + + prisma.$on('query', ({ query, params, duration, ...data }) => { + logger.silly(`Query: ${query}, Params: ${params}, Duration: ${duration}`, { ...data }) + }) + + prisma.$on('info', ({ message, ...data }) => { + logger.info(message, { ...data }) + }) + + prisma.$on('warn', ({ message, ...data }) => { + logger.warn(message, { ...data }) + }) + + prisma.$on('error', ({ message, ...data }) => { + logger.error(message, { ...data }) + }) + + prisma.$use(DbUtil.slowQueryMiddleware(logger)) + + return prisma +} + +// Prevent multiple instances of Prisma Client in development +// https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#prevent-hot-reloading-from-creating-new-instances-of-prismaclient +// https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/instantiate-prisma-client#the-number-of-prismaclient-instances-matters +const prisma = global.prisma || createPrismaClient() + +if (process.env.NODE_ENV === 'development') global.prisma = prisma + +export default prisma diff --git a/apps/server/src/app/lib/s3.ts b/apps/server/src/app/lib/s3.ts new file mode 100644 index 00000000000..4538b40814d --- /dev/null +++ b/apps/server/src/app/lib/s3.ts @@ -0,0 +1,15 @@ +import { S3Client } from '@aws-sdk/client-s3' +import { S3Service } from '@maybe-finance/server/shared' +import env from '../../env' + +// https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html +const s3Client = new S3Client({ + region: 'us-west-2', +}) + +const s3Service = new S3Service(s3Client, { + public: env.NX_CDN_PUBLIC_BUCKET, + private: env.NX_CDN_PRIVATE_BUCKET, +}) + +export default s3Service diff --git a/apps/server/src/app/lib/secretsClient.ts b/apps/server/src/app/lib/secretsClient.ts new file mode 100644 index 00000000000..3522e937a8d --- /dev/null +++ b/apps/server/src/app/lib/secretsClient.ts @@ -0,0 +1,7 @@ +import { SecretsManagerClient } from '@aws-sdk/client-secrets-manager' + +const secretsClient = new SecretsManagerClient({ + region: 'us-west-2', +}) + +export default secretsClient diff --git a/apps/server/src/app/lib/stripe.ts b/apps/server/src/app/lib/stripe.ts new file mode 100644 index 00000000000..d38338e3744 --- /dev/null +++ b/apps/server/src/app/lib/stripe.ts @@ -0,0 +1,6 @@ +import Stripe from 'stripe' +import env from '../../env' + +const stripe = new Stripe(env.NX_STRIPE_SECRET_KEY, { apiVersion: '2022-08-01' }) + +export default stripe diff --git a/apps/server/src/app/lib/types.ts b/apps/server/src/app/lib/types.ts new file mode 100644 index 00000000000..5939d3a1634 --- /dev/null +++ b/apps/server/src/app/lib/types.ts @@ -0,0 +1,27 @@ +import type { Response, Request, NextFunction } from 'express' + +interface TypedRequest extends Omit { + body: T +} + +interface TypedResponse extends Response { + superjson(data: T): TypedResponse +} + +export type DefaultHandler = ( + req: TypedRequest, + res: TypedResponse, + next: NextFunction +) => void + +// GET requests have optional request type, mandatory response type +export type GetHandler = ( + req: TypedRequest, // eslint-disable-line + res: TypedResponse, + next: NextFunction +) => void + +// Aliases for semantics and convenience +export type PostHandler = DefaultHandler +export type PutHandler = DefaultHandler +export type DeleteHandler = DefaultHandler diff --git a/apps/server/src/app/lib/webhook.ts b/apps/server/src/app/lib/webhook.ts new file mode 100644 index 00000000000..5e58cc0883c --- /dev/null +++ b/apps/server/src/app/lib/webhook.ts @@ -0,0 +1,21 @@ +import axios from 'axios' +import isCI from 'is-ci' +import env from '../../env' +import logger from './logger' + +export async function getWebhookUrl(): Promise { + if (process.env.NODE_ENV === 'development' && !isCI && !env.NX_WEBHOOK_URL) { + const ngrokUrl = await axios.get(`${env.NX_NGROK_URL}/api/tunnels`).then((res) => { + const httpsTunnel = res.data.tunnels.find((t) => t.proto === 'https') + return httpsTunnel.public_url + }) + + logger.info(`Generated dynamic ngrok webhook URL: ${ngrokUrl}`) + + return ngrokUrl + } + + logger.info(`Using ${env.NX_WEBHOOK_URL ? env.NX_WEBHOOK_URL : env.NX_API_URL} for webhook URL`) + + return env.NX_WEBHOOK_URL || env.NX_API_URL +} diff --git a/apps/server/src/app/middleware/auth-error-handler.ts b/apps/server/src/app/middleware/auth-error-handler.ts new file mode 100644 index 00000000000..15f4e3e6dd2 --- /dev/null +++ b/apps/server/src/app/middleware/auth-error-handler.ts @@ -0,0 +1,18 @@ +import type { ErrorRequestHandler } from 'express' +import { ForbiddenError } from '@casl/ability' + +export const authErrorHandler: ErrorRequestHandler = (err, req, res, next) => { + if (err instanceof ForbiddenError) { + return res.status(403).json({ + errors: [ + { + status: '403', + title: 'Unauthorized', + detail: err.message, + }, + ], + }) + } + + next(err) +} diff --git a/apps/server/src/app/middleware/dev-only.ts b/apps/server/src/app/middleware/dev-only.ts new file mode 100644 index 00000000000..529e83b9d1b --- /dev/null +++ b/apps/server/src/app/middleware/dev-only.ts @@ -0,0 +1,9 @@ +import createError from 'http-errors' + +export const devOnly = (_req, _res, next) => { + if (process.env.NODE_ENV !== 'development') { + return next(createError(401, 'Route only available in dev mode')) + } + + next() +} diff --git a/apps/server/src/app/middleware/error-handler.ts b/apps/server/src/app/middleware/error-handler.ts new file mode 100644 index 00000000000..141cc25076f --- /dev/null +++ b/apps/server/src/app/middleware/error-handler.ts @@ -0,0 +1,36 @@ +import type { ErrorRequestHandler } from 'express' +import type { SharedType } from '@maybe-finance/shared' +import logger from '../lib/logger' +import { ErrorUtil } from '@maybe-finance/server/shared' + +export const defaultErrorHandler: ErrorRequestHandler = async (err, req, res, _next) => { + const parsedError = ErrorUtil.parseError(err) + + // A custom redirect if user tries to access admin dashboard without Admin role (see /apps/server/src/app/admin/admin-router.ts) + if (parsedError.message === 'ADMIN_UNAUTHORIZED') { + return res.redirect('/?error=invalid-credentials') + } + + const errors: SharedType.ErrorResponse = { + errors: [ + { + status: parsedError.statusCode || '500', + title: parsedError.message, + }, + ], + } + + logger.error(`[default-express-handler] ${parsedError.message}`, { + metadata: parsedError.metadata, + stackTrace: parsedError.stackTrace, + user: req.user?.sub, + request: { + method: req.method, + url: req.url, + }, + }) + + logger.debug(parsedError.stackTrace) + + res.status(+(parsedError.statusCode || 500)).json(errors) +} diff --git a/apps/server/src/app/middleware/identify-user.ts b/apps/server/src/app/middleware/identify-user.ts new file mode 100644 index 00000000000..6fe763bfab5 --- /dev/null +++ b/apps/server/src/app/middleware/identify-user.ts @@ -0,0 +1,10 @@ +import type { ErrorRequestHandler } from 'express' +import * as Sentry from '@sentry/node' + +export const identifySentryUser: ErrorRequestHandler = (err, req, _res, next) => { + Sentry.setUser({ + auth0Id: req.user?.sub, + }) + + next(err) +} diff --git a/apps/server/src/app/middleware/index.ts b/apps/server/src/app/middleware/index.ts new file mode 100644 index 00000000000..8e60cf38e1e --- /dev/null +++ b/apps/server/src/app/middleware/index.ts @@ -0,0 +1,9 @@ +export * from './dev-only' +export * from './error-handler' +export * from './auth-error-handler' +export * from './superjson' +export * from './validate-auth0-jwt' +export * from './validate-plaid-jwt' +export * from './validate-finicity-signature' +export { default as maintenance } from './maintenance' +export * from './identify-user' diff --git a/apps/server/src/app/middleware/maintenance.ts b/apps/server/src/app/middleware/maintenance.ts new file mode 100644 index 00000000000..22c3d25bf22 --- /dev/null +++ b/apps/server/src/app/middleware/maintenance.ts @@ -0,0 +1,46 @@ +import type { Express } from 'express' +import ldClient from '../lib/ldClient' + +type MaintenanceOptions = { + statusCode?: number + path?: string + featureKey?: string +} + +export default function maintenance( + app: Express, + { statusCode = 503, path = '/maintenance', featureKey = 'maintenance' }: MaintenanceOptions = {} +) { + let enabled = false + + function loadFeatureFlag() { + ldClient + .waitForInitialization() + .then((ld) => { + ld.variation(featureKey, { key: 'anonymous-server', anonymous: true }, false).then( + (flag) => (enabled = flag) + ) + }) + .catch((err) => { + console.error(`error loading feature flag`, err) + }) + } + + loadFeatureFlag() + + ldClient.on(`update:${featureKey}`, () => { + loadFeatureFlag() + }) + + app.get(path, async (req, res) => { + res.status(200).json({ enabled }) + }) + + app.use(async (req, res, next) => { + if (enabled) { + return res.status(statusCode).json({ message: 'Maintenance in progress' }) + } + + next() + }) +} diff --git a/apps/server/src/app/middleware/superjson.ts b/apps/server/src/app/middleware/superjson.ts new file mode 100644 index 00000000000..40a1b7fd7af --- /dev/null +++ b/apps/server/src/app/middleware/superjson.ts @@ -0,0 +1,19 @@ +import type { RequestHandler } from 'express' +import type { SharedType } from '@maybe-finance/shared' +import { superjson as sj } from '@maybe-finance/shared' + +export const superjson: RequestHandler = (req, res, next) => { + // Client *should* make requests with valid superjson format, { json: any, meta?: any } + if ('json' in req.body) { + req.body = sj.deserialize(req.body) + } + + const _json = res.json.bind(res) + res.superjson = (data) => { + const serialized = sj.serialize(data) + const responsePayload: SharedType.SuccessResponse = { data: serialized } + return _json(responsePayload) + } + + next() +} diff --git a/apps/server/src/app/middleware/validate-auth0-jwt.ts b/apps/server/src/app/middleware/validate-auth0-jwt.ts new file mode 100644 index 00000000000..6855df1ae68 --- /dev/null +++ b/apps/server/src/app/middleware/validate-auth0-jwt.ts @@ -0,0 +1,22 @@ +import type { GetVerificationKey } from 'express-jwt' +import { expressjwt as jwt } from 'express-jwt' +import jwks from 'jwks-rsa' +import env from '../../env' + +/** + * The user will authenticate on the frontend SPA (React) via Authorization Code Flow with PKCE + * and receive an access token. This token is passed in HTTP headers and validated on the backend + * via this middleware + */ +export const validateAuth0Jwt = jwt({ + requestProperty: 'user', + secret: jwks.expressJwtSecret({ + cache: true, + rateLimit: true, + jwksRequestsPerMinute: 5, + jwksUri: `https://${env.NX_AUTH0_CUSTOM_DOMAIN}/.well-known/jwks.json`, + }) as GetVerificationKey, + audience: env.NX_AUTH0_AUDIENCE, // This is a unique identifier from Auth0 (not a valid URL) + issuer: `https://${env.NX_AUTH0_CUSTOM_DOMAIN}/`, + algorithms: ['RS256'], +}) diff --git a/apps/server/src/app/middleware/validate-finicity-signature.ts b/apps/server/src/app/middleware/validate-finicity-signature.ts new file mode 100644 index 00000000000..72f42c30389 --- /dev/null +++ b/apps/server/src/app/middleware/validate-finicity-signature.ts @@ -0,0 +1,21 @@ +import type { RequestHandler } from 'express' +import crypto from 'crypto' +import env from '../../env' + +/** + * middleware to validate the `x-finicity-signature` header + * + * https://docs.finicity.com/connect-and-mvs-webhooks/ + */ +export const validateFinicitySignature: RequestHandler = (req, res, next) => { + const signature = crypto + .createHmac('sha256', env.NX_FINICITY_PARTNER_SECRET) + .update(JSON.stringify(req.body)) + .digest('hex') + + if (req.get('x-finicity-signature') !== signature) { + throw new Error('invalid finicity signature') + } + + next() +} diff --git a/apps/server/src/app/middleware/validate-plaid-jwt.ts b/apps/server/src/app/middleware/validate-plaid-jwt.ts new file mode 100644 index 00000000000..decf6a94161 --- /dev/null +++ b/apps/server/src/app/middleware/validate-plaid-jwt.ts @@ -0,0 +1,25 @@ +import { expressjwt as jwt } from 'express-jwt' +import jwtDecode from 'jwt-decode' +import jwkToPem from 'jwk-to-pem' +import plaid from '../lib/plaid' + +// https://plaid.com/docs/api/webhooks/webhook-verification/#webhook_verification_keyget +export const validatePlaidJwt = jwt({ + requestProperty: 'plaidAuth', // Will attach the decoded payload to `req.plaidAuth` + algorithms: ['ES256'], + getToken(req) { + // https://plaid.com/docs/api/webhooks/webhook-verification/#extract-the-jwt-header + return req.headers['plaid-verification'] as string + }, + async secret(req, token) { + const { kid } = jwtDecode<{ alg: string; kid: string; typ: string }>( + req.headers['plaid-verification'] as string, + { header: true } + ) + + const res = await plaid.webhookVerificationKeyGet({ key_id: kid }) + + // Need to convert JSON => PEM in order to use jsonwebtoken lib to verify - https://github.com/auth0/node-jsonwebtoken/issues/43 + return jwkToPem(res.data.key) + }, +}) diff --git a/apps/server/src/app/routes/account-rollup.router.ts b/apps/server/src/app/routes/account-rollup.router.ts new file mode 100644 index 00000000000..d7274b7acaf --- /dev/null +++ b/apps/server/src/app/routes/account-rollup.router.ts @@ -0,0 +1,23 @@ +import { Router } from 'express' +import { z } from 'zod' +import { DateUtil } from '@maybe-finance/shared' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.get( + '/', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.dateTransform), + end: z.string().transform(DateUtil.dateTransform), + }) + .partial(), + resolve: ({ ctx, input: { start, end } }) => { + return ctx.accountService.getAccountRollup(ctx.user!.id, start, end) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/accounts.router.ts b/apps/server/src/app/routes/accounts.router.ts new file mode 100644 index 00000000000..25ecc3c1b88 --- /dev/null +++ b/apps/server/src/app/routes/accounts.router.ts @@ -0,0 +1,402 @@ +import type { Account } from '@prisma/client' +import type { SharedType } from '@maybe-finance/shared' +import { Router } from 'express' +import { subject } from '@casl/ability' +import { z } from 'zod' +import { DateUtil } from '@maybe-finance/shared' +import { + AccountCreateSchema, + AccountUpdateSchema, + InvestmentTransactionCategorySchema, +} from '@maybe-finance/server/features' +import endpoint from '../lib/endpoint' +import keyBy from 'lodash/keyBy' +import merge from 'lodash/merge' + +const router = Router() + +router.get( + '/', + endpoint.create({ + resolve: async ({ ctx }) => { + return ctx.accountService.getAll(ctx.user!.id) + }, + }) +) + +router.post( + '/', + endpoint.create({ + input: AccountCreateSchema, + resolve: async ({ input, ctx }) => { + ctx.ability.throwUnlessCan('create', 'Account') + + let account: Account + + switch (input.type) { + case 'LOAN': { + const { currentBalance, ...rest } = input + + account = await ctx.accountService.create({ + ...rest, + userId: ctx.user!.id, + currentBalanceProvider: currentBalance, + currentBalanceStrategy: 'current', + }) + + break + } + default: { + const { + valuations: { originalBalance, currentBalance, currentDate }, + startDate, + ...rest + } = input + + const initialValuations = [ + { + source: 'manual', + date: startDate!, + amount: originalBalance, + }, + ] + + if ( + startDate && + currentBalance && + !DateUtil.isSameDate(DateUtil.datetimeTransform(startDate), currentDate) + ) { + initialValuations.push({ + source: 'manual', + date: currentDate.toJSDate(), + amount: currentBalance, + }) + } + + account = await ctx.accountService.create( + { + ...rest, + userId: ctx.user!.id, + currentBalanceProvider: currentBalance, + currentBalanceStrategy: 'current', + }, + { + create: initialValuations, + } + ) + + break + } + } + + await ctx.accountService.syncBalances(account.id) + + return account + }, + }) +) + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const account = await ctx.accountService.getAccountDetails(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return account + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: AccountUpdateSchema, + resolve: async ({ input, ctx, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Account', account)) + const updatedAccount = await ctx.accountService.update(account.id, { + ...input.data, + ...('currentBalance' in input.data + ? { + currentBalance: undefined, + currentBalanceProvider: input.data.currentBalance, + currentBalanceStrategy: 'current', + } + : {}), + }) + await ctx.accountService.syncBalances(updatedAccount.id) + return updatedAccount + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('Account', account)) + return ctx.accountService.delete(account.id) + }, + }) +) + +router.get( + '/:id/balances', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.dateTransform), + end: z.string().transform(DateUtil.dateTransform), + }) + .partial(), + resolve: async ({ ctx, input, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.accountService.getBalances(account.id, input.start, input.end) + }, + }) +) + +router.get( + '/:id/returns', + endpoint.create({ + input: z.object({ + start: z.string().transform((v) => DateUtil.datetimeTransform(v)), + end: z.string().transform((v) => DateUtil.datetimeTransform(v)), + compare: z + .string() + .optional() + .transform((v) => v?.split(',')), // in format of /accounts/:id/returns?compare=VOO,AAPL,TSLA + }), + resolve: async ({ ctx, input, req }): Promise => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + + const returnSeries: SharedType.AccountReturnTimeSeriesData[] = + await ctx.accountService.getReturns( + account.id, + input.start.toISODate(), + input.end.toISODate() + ) + + const baseSeries = { + interval: 'days' as SharedType.TimeSeriesInterval, + start: input.start.toISODate(), + end: input.end.toISODate(), + } + + if (!input.compare || input.compare.length < 1) + return { + ...baseSeries, + data: returnSeries, + } + + const comparisonData = await Promise.allSettled( + input.compare.map(async (ticker) => { + return { + ticker, + pricing: await ctx.marketDataService.getDailyPricing( + { symbol: ticker, plaidType: null, currencyCode: 'USD' }, + input.start, + input.end + ), + } + }) + ) + + const comparisonPrices = comparisonData + .filter( + ( + data + ): data is PromiseFulfilledResult<{ + ticker: string + pricing: SharedType.DailyPricing[] + }> => { + if (data.status === 'rejected') { + ctx.logger.warn('Unable to generate comparison data', { + reason: data.reason, + }) + } + + return data.status === 'fulfilled' + } + ) + .map((data) => { + return data.value.pricing.map((price) => ({ + date: price.date.toISODate(), + [data.value.ticker]: price.priceClose + .dividedBy(data.value.pricing[0].priceClose) + .minus(1), + })) + }) + + // Performs a "left join" of prices by ticker + const merged: Record< + string, + SharedType.AccountReturnResponse['data'][number]['comparisons'] + > = merge( + keyBy( + returnSeries.map((v) => ({ date: v.date })), // ensures we have a key for every single day + 'date' + ), + ...comparisonPrices.map((prices) => keyBy(prices, 'date')) + ) + + return { + ...baseSeries, + data: returnSeries.map((rs) => { + return { + ...rs, + comparisons: merged[rs.date], + } + }), + } + }, + }) +) + +router.get( + '/:id/transactions', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.datetimeTransform), + end: z.string().transform(DateUtil.datetimeTransform), + page: z.string().transform((val) => parseInt(val)), + }) + .partial(), + resolve: async ({ ctx, input, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.accountService.getTransactions( + account.id, + input.page, + input.start, + input.end + ) + }, + }) +) + +router.get( + '/:id/valuations', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.datetimeTransform), + end: z.string().transform(DateUtil.datetimeTransform), + }) + .partial(), + resolve: async ({ ctx, input, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.valuationService.getValuations(account.id, input.start, input.end) + }, + }) +) + +router.post( + '/:id/valuations', + endpoint.create({ + input: z.object({ + date: z.string().transform(DateUtil.datetimeTransform), + amount: z.number(), + }), + resolve: async ({ ctx, input: { date, amount }, req }) => { + const account = await ctx.accountService.get(+req.params.id) + + ctx.ability.throwUnlessCan('update', subject('Account', account)) + + if (!date) throw new Error('Invalid valuation date') + + const valuation = await ctx.valuationService.createValuation({ + amount, + date: date.toJSDate(), + accountId: +req.params.id, + source: 'manual', + }) + + await ctx.accountService.syncBalances(+req.params.id) + + return valuation + }, + }) +) + +router.get( + '/:id/holdings', + endpoint.create({ + input: z + .object({ + page: z.string().transform((val) => parseInt(val)), + }) + .partial(), + resolve: async ({ ctx, input: { page }, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.accountService.getHoldings(account.id, page) + }, + }) +) + +router.get( + '/:id/investment-transactions', + endpoint.create({ + input: z + .object({ + page: z.string().transform((val) => parseInt(val)), + start: z.string().transform(DateUtil.datetimeTransform).optional(), + end: z.string().transform(DateUtil.datetimeTransform).optional(), + category: InvestmentTransactionCategorySchema.optional(), + }) + .partial(), + resolve: async ({ ctx, input, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.accountService.getInvestmentTransactions( + account.id, + input.page, + input.start, + input.end, + input.category + ) + }, + }) +) + +router.get( + '/:id/insights', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Account', account)) + return ctx.insightService.getAccountInsights({ accountId: account.id }) + }, + }) +) + +router.post( + '/:id/sync', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Account', account)) + return ctx.accountService.sync(+req.params.id) + }, + }) +) + +// Syncs account balances without triggering a background worker (syncs balances much faster, ideal for smaller updates such as editing an account valuation) +router.post( + '/:id/sync/balances', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const account = await ctx.accountService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Account', account)) + return ctx.accountService.syncBalances(+req.params.id) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/admin.router.ts b/apps/server/src/app/routes/admin.router.ts new file mode 100644 index 00000000000..f4b7567b086 --- /dev/null +++ b/apps/server/src/app/routes/admin.router.ts @@ -0,0 +1,82 @@ +import { Router } from 'express' +import { auth, claimCheck } from 'express-openid-connect' +import { createBullBoard } from '@bull-board/api' +import { BullAdapter } from '@bull-board/api/bullAdapter' +import { ExpressAdapter } from '@bull-board/express' +import { AuthUtil, BullQueue } from '@maybe-finance/server/shared' +import { SharedType } from '@maybe-finance/shared' +import { queueService } from '../lib/endpoint' +import env from '../../env' + +const router = Router() + +const serverAdapter = new ExpressAdapter().setBasePath('/admin/bullmq') + +createBullBoard({ + queues: queueService.allQueues + .filter((q): q is BullQueue => q instanceof BullQueue) + .map((q) => new BullAdapter(q.queue)), + serverAdapter, +}) + +const isProd = process.env.NODE_ENV === 'production' && process.env.IS_PULL_REQUEST !== 'true' + +const prodCookieConfig = isProd + ? { + session: { + cookie: { + domain: '.maybe.co', + path: '/admin', + }, + }, + } + : {} + +// This will ensure that only Auth0 users with the "admin" role can visit these pages +router.use( + auth({ + authRequired: true, + idpLogout: true, // Logout of Auth0 provider + auth0Logout: isProd, // Same as idpLogout, but for custom domain + secret: env.NX_SESSION_SECRET, + baseURL: `${env.NX_API_URL}/admin`, + clientID: env.NX_AUTH0_CLIENT_ID, + clientSecret: env.NX_AUTH0_CLIENT_SECRET, + issuerBaseURL: `https://${env.NX_AUTH0_CUSTOM_DOMAIN}`, + authorizationParams: { + response_type: 'code', + audience: env.NX_AUTH0_AUDIENCE, + scope: 'openid profile email', + }, + routes: { + postLogoutRedirect: env.NX_API_URL, + }, + ...prodCookieConfig, + }) +) + +/** + * Auth0 requires all custom claims to be namespaced + * @see https://auth0.com/docs/security/tokens/json-web-tokens/create-namespaced-custom-claims + * + * This is the namespace that has been set in the "Rules" section of Maybe's Auth0 dashboard + * + * The rule used below is called "Add Roles to ID token", and will attach an array of roles + * that are assigned to an Auth0 user under the https://maybe.co/roles namespace + * + * @see https://auth0.com/docs/authorization/authorization-policies/sample-use-cases-rules-with-authorization#add-user-roles-to-tokens + */ + +const adminClaimCheck = claimCheck((_req, claims) => AuthUtil.verifyRoleClaims(claims, 'Admin')) + +router.get('/', adminClaimCheck, (req, res) => { + res.render('pages/dashboard', { + user: req.oidc.user?.name, + role: req.oidc.idTokenClaims?.[SharedType.Auth0CustomNamespace.Roles], + }) +}) + +// Visit /admin/bullmq to see BullMQ Dashboard +router.use('/bullmq', adminClaimCheck, serverAdapter.getRouter()) + +export default router diff --git a/apps/server/src/app/routes/connections.router.ts b/apps/server/src/app/routes/connections.router.ts new file mode 100644 index 00000000000..0c198817a72 --- /dev/null +++ b/apps/server/src/app/routes/connections.router.ts @@ -0,0 +1,217 @@ +import { Router } from 'express' +import { SandboxItemFireWebhookRequestWebhookCodeEnum } from 'plaid' +import type { SharedType } from '@maybe-finance/shared' +import { subject } from '@casl/ability' +import { z } from 'zod' +import endpoint from '../lib/endpoint' +import { devOnly } from '../middleware' + +const router = Router() + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('AccountConnection', connection)) + return connection + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: z.object({ syncStatus: z.enum(['IDLE', 'PENDING', 'SYNCING']) }), + resolve: async ({ input, ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + const updatedConnection = await ctx.accountConnectionService.update( + connection.id, + input + ) + return updatedConnection + }, + }) +) + +router.post( + '/:id/plaid/link-update-completed', + endpoint.create({ + input: z.object({ + status: z.enum(['success', 'exit']), + }), + resolve: async ({ ctx, input, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + + await ctx.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + plaidNewAccountsAvailable: false, + }, + }) + + if (input.status === 'success') { + await ctx.accountConnectionService.sync(connection.id) + } + }, + }) +) + +router.post( + '/:id/disconnect', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + return ctx.accountConnectionService.disconnect(connection.id) + }, + }) +) + +router.post( + '/:id/reconnect', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + return ctx.accountConnectionService.reconnect(connection.id) + }, + }) +) + +router.post( + '/:id/plaid/link-token', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const token = await ctx.plaidService.createLinkTokenForUpdateMode( + ctx.user!.id, + +req.params.id, + req.query.mode as SharedType.PlaidLinkUpdateMode + ) + + await ctx.plaidService.cacheLinkToken(ctx.user!.id, token) + + return { token } + }, + }) +) + +router.post( + '/:id/finicity/fix-connect', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const { link } = await ctx.finicityService.generateFixConnectUrl( + ctx.user!.id, + +req.params.id + ) + + return { link } + }, + }) +) + +router.post( + '/:id/sync', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + return ctx.accountConnectionService.sync(connection.id) + }, + }) +) + +router.post( + '/:id/sync/:sync', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('AccountConnection', connection)) + + switch (req.params.sync) { + case 'balances': { + await ctx.accountConnectionService.syncBalances(connection.id) + break + } + case 'securities': { + await ctx.accountConnectionService.syncSecurities(connection.id) + break + } + default: + throw new Error(`unknown sync command: ${req.params.sync}`) + } + + return connection + }, + }) +) + +router.post( + '/:id/plaid/sandbox/fire-webhook', + devOnly, + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + + if (!connection.plaidAccessToken) + throw new Error(`connection does not have a plaidAccessToken`) + + // https://plaid.com/docs/api/sandbox/#sandboxitemfire_webhook + await ctx.plaid.sandboxItemFireWebhook({ + access_token: ctx.cryptoService.decrypt(connection.plaidAccessToken), + webhook_code: SandboxItemFireWebhookRequestWebhookCodeEnum.DefaultUpdate, + }) + + return { success: true } + }, + }) +) + +router.post( + '/:id/plaid/sandbox/item-reset-login', + devOnly, + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + + if (!connection.plaidAccessToken) + throw new Error(`connection does not have a plaidAccessToken`) + + await ctx.plaid.sandboxItemResetLogin({ + access_token: ctx.cryptoService.decrypt(connection.plaidAccessToken), + }) + + // Triggers the reset login error + await ctx.accountConnectionService.sync(+req.params.id) + + return { success: true } + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const connection = await ctx.accountConnectionService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('AccountConnection', connection)) + return ctx.accountConnectionService.delete(connection.id) + }, + }) +) + +router.delete( + '/', + devOnly, + endpoint.create({ + resolve: async ({ ctx }) => { + await ctx.prisma.accountConnection.deleteMany({ where: { userId: ctx.user!.id } }) + return { success: true } + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/conversations.router.ts b/apps/server/src/app/routes/conversations.router.ts new file mode 100644 index 00000000000..bdd474a994d --- /dev/null +++ b/apps/server/src/app/routes/conversations.router.ts @@ -0,0 +1,165 @@ +import { type Request, Router } from 'express' +import { subject } from '@casl/ability' +import { + ConversationCreateSchema, + ConversationUpdateSchema, + MessageCreateSchema, + Sandbox, +} from '@maybe-finance/server/features' +import endpoint from '../lib/endpoint' +import { devOnly } from '../middleware' +import multer from 'multer' +import multerS3Storage from '../lib/multerS3Storage' +import env from '../../env' +import logger from '../lib/logger' +import s3 from '../lib/s3' +import { ATAUtil } from '@maybe-finance/shared' +import { GetSecretValueCommand } from '@aws-sdk/client-secrets-manager' + +const router = Router() + +router.get( + '/', + endpoint.create({ + async resolve({ ctx }) { + return ctx.conversationService.getAll(ctx.user!.id) + }, + }) +) + +router.post( + '/', + endpoint.create({ + input: ConversationCreateSchema, + resolve: async ({ input, ctx }) => { + ctx.ability.throwUnlessCan('create', 'Conversation') + + const { initialMessage, ...conversation } = input + + return ctx.conversationService.create({ + ...conversation, + userId: ctx.user!.id, + messages: initialMessage + ? { + create: { + ...initialMessage, + userId: ctx.user!.id, // required so audit trigger has access to row-level user_id + }, + } + : undefined, + }) + }, + }) +) + +router.get( + '/:id', + endpoint.create({ + async resolve({ ctx, req }) { + const privKeyRes = await ctx.secretsClient.send( + new GetSecretValueCommand({ + SecretId: env.NX_CDN_SIGNER_SECRET_ID, + }) + ) + + if (!privKeyRes.SecretString) { + throw new Error( + 'Failed to obtain private key for url signing. Make sure key is stored in secrets manager and IAM user can access it from app.' + ) + } + + const conversation = await ctx.conversationService.get(+req.params.id, { + cdnUrl: env.NX_CDN_URL, + pubKeyId: env.NX_CDN_SIGNER_PUBKEY_ID, + privKey: privKeyRes.SecretString, + }) + + ctx.ability.throwUnlessCan('read', subject('Conversation', conversation)) + return conversation + }, + }) +) + +router.patch( + '/:id', + endpoint.create({ + input: ConversationUpdateSchema, + async resolve({ ctx, req, input }) { + const conversation = await ctx.conversationService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Conversation', conversation)) + return ctx.conversationService.update(conversation.id, input) + }, + }) +) + +router.post( + '/:id/messages', + // Uploads message attachments to our S3 CDN storage bucket + multer({ + storage: multerS3Storage( + { + s3, + getFilename: (req: Request) => { + return ATAUtil.generateS3Filename(+req.params.id) + }, + }, + logger.child({ service: 'MulterS3Storage' }) + ), + limits: { + // this is a fallback - size is checked client-side and handled there + fileSize: 1_000_000 * 10, // 10 MB + files: 1, + }, + }).single('attachment'), + endpoint.create({ + input: MessageCreateSchema, + async resolve({ ctx, req, input }) { + const conversation = await ctx.conversationService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Conversation', conversation)) + + return ctx.messageService.create( + { + ...input, + userId: ctx.user!.id, // required so audit trigger has access to row-level user_id + conversationId: conversation.id, + mediaSrc: req.file?.path, + }, + ctx.user!.id !== conversation.userId + ) + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + async resolve({ ctx, req }) { + const conversation = await ctx.conversationService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('Conversation', conversation)) + return ctx.conversationService.delete(conversation.id) + }, + }) +) + +// Endpoint for quickly generating and regenerating conversations to test +router.post( + '/sandbox', + devOnly, + endpoint.create({ + input: Sandbox.SandboxSchema, + resolve: async ({ ctx, input }) => { + switch (input.action) { + case 'assign-advisor': + await Sandbox.assignAdvisor(input.conversationId, ctx.prisma) + break + case 'reset': + await Sandbox.resetConversations({ ...input, userId: ctx.user!.id }, ctx.prisma) + break + } + + return { action: input.action, success: true } + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/e2e.router.ts b/apps/server/src/app/routes/e2e.router.ts new file mode 100644 index 00000000000..31004185924 --- /dev/null +++ b/apps/server/src/app/routes/e2e.router.ts @@ -0,0 +1,79 @@ +import type { OnboardingState } from '@maybe-finance/server/features' +import { Router } from 'express' +import { DateTime } from 'luxon' +import { z } from 'zod' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.use((req, res, next) => { + const roles = req.user?.['https://maybe.co/roles'] + + if (roles?.includes('CIUser') || roles?.includes('Admin')) { + next() + } else { + res.status(401).send('Route only available to CIUser and Admin roles') + } +}) + +// Validation endpoint for Cypress +router.get( + '/', + endpoint.create({ + resolve: async () => { + return { success: true } + }, + }) +) + +router.post( + '/plaid/connect', + endpoint.create({ + input: z.object({ + username: z.enum(['custom_ci_pos_nw']).default('custom_ci_pos_nw'), + }), + resolve: async ({ ctx, input: { username } }) => { + return ctx.plaidService.createSandboxAccount(ctx.user!.id, username) + }, + }) +) + +router.post( + '/reset', + endpoint.create({ + input: z.object({ + mainOnboardingDisabled: z.boolean().default(true), + sidebarOnboardingDisabled: z.boolean().default(true), + trialLapsed: z.boolean().default(false), + }), + resolve: async ({ ctx, input }) => { + ctx.logger.debug(`Resetting CI user ${ctx.user!.auth0Id}`) + + await ctx.prisma.$transaction([ + ctx.prisma.$executeRaw`DELETE FROM "user" WHERE auth0_id=${ctx.user!.auth0Id};`, + ctx.prisma.user.create({ + data: { + auth0Id: ctx.user!.auth0Id, + email: 'REPLACE_THIS', + dob: new Date('1990-01-01'), + linkAccountDismissedAt: new Date(), // ensures our auto-account link doesn't trigger + + // Override onboarding flows to be complete for e2e testing + onboarding: { + main: { markedComplete: input.mainOnboardingDisabled, steps: [] }, + sidebar: { markedComplete: input.sidebarOnboardingDisabled, steps: [] }, + } as OnboardingState, + + trialEnd: input.trialLapsed + ? DateTime.now().minus({ days: 1 }).toJSDate() + : undefined, + }, + }), + ]) + + return { success: true } + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/finicity.router.ts b/apps/server/src/app/routes/finicity.router.ts new file mode 100644 index 00000000000..38044d1b547 --- /dev/null +++ b/apps/server/src/app/routes/finicity.router.ts @@ -0,0 +1,31 @@ +import { z } from 'zod' +import { Router } from 'express' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.post( + '/connect-url', + endpoint.create({ + input: z.object({ + institutionId: z.string(), + }), + resolve: async ({ ctx, input }) => { + return await ctx.finicityService.generateConnectUrl(ctx.user!.id, input.institutionId) + }, + }) +) + +router.post( + '/institutions/sync', + endpoint.create({ + resolve: async ({ ctx }) => { + ctx.ability.throwUnlessCan('manage', 'Institution') + await ctx.queueService + .getQueue('sync-institution') + .add('sync-finicity-institutions', {}) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/holdings.router.ts b/apps/server/src/app/routes/holdings.router.ts new file mode 100644 index 00000000000..7d2df4b235c --- /dev/null +++ b/apps/server/src/app/routes/holdings.router.ts @@ -0,0 +1,49 @@ +import { Router } from 'express' +import { subject } from '@casl/ability' +import { HoldingUpdateInputSchema } from '@maybe-finance/server/features' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const holding = await ctx.holdingService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Holding', holding)) + + return ctx.holdingService.getHoldingDetails(holding.id) + }, + }) +) + +router.get( + '/:id/insights', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const holding = await ctx.holdingService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Holding', holding)) + + return ctx.insightService.getHoldingInsights({ holding }) + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: HoldingUpdateInputSchema, + resolve: async ({ input, ctx, req }) => { + const holding = await ctx.holdingService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Holding', holding)) + + const updatedHolding = await ctx.holdingService.update(+req.params.id, input) + + await ctx.accountService.syncBalances(holding.accountId) + + return updatedHolding + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/index.ts b/apps/server/src/app/routes/index.ts new file mode 100644 index 00000000000..dd2eea8925c --- /dev/null +++ b/apps/server/src/app/routes/index.ts @@ -0,0 +1,20 @@ +export { default as accountsRouter } from './accounts.router' +export { default as accountRollupRouter } from './account-rollup.router' +export { default as connectionsRouter } from './connections.router' +export { default as usersRouter } from './users.router' +export { default as webhooksRouter } from './webhooks.router' +export { default as plaidRouter } from './plaid.router' +export { default as finicityRouter } from './finicity.router' +export { default as adminRouter } from './admin.router' +export { default as valuationsRouter } from './valuations.router' +export { default as institutionsRouter } from './institutions.router' +export { default as transactionsRouter } from './transactions.router' +export { default as holdingsRouter } from './holdings.router' +export { default as securitiesRouter } from './securities.router' +export { default as plansRouter } from './plans.router' +export { default as toolsRouter } from './tools.router' +export { default as conversationsRouter } from './conversations.router' +export { default as messagesRouter } from './messages.router' +export { default as notificationsRouter } from './notifications.router' +export { default as publicRouter } from './public.router' +export { default as e2eRouter } from './e2e.router' diff --git a/apps/server/src/app/routes/institutions.router.ts b/apps/server/src/app/routes/institutions.router.ts new file mode 100644 index 00000000000..d0012700eca --- /dev/null +++ b/apps/server/src/app/routes/institutions.router.ts @@ -0,0 +1,54 @@ +import { Router } from 'express' +import { z } from 'zod' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.get( + '/', + endpoint.create({ + input: z + .object({ + q: z.string(), + page: z.string().transform((val) => parseInt(val)), + }) + .partial(), + resolve: async ({ ctx, input }) => { + ctx.ability.throwUnlessCan('read', 'Institution') + + return ctx.institutionService.getAll({ query: input.q, page: input.page }) + }, + }) +) + +router.post( + '/sync', + endpoint.create({ + resolve: async ({ ctx }) => { + ctx.ability.throwUnlessCan('update', 'Institution') + + // Sync all Plaid + Finicity institutions + await ctx.queueService.getQueue('sync-institution').addBulk([ + { name: 'sync-plaid-institutions', data: {} }, + { name: 'sync-finicity-institutions', data: {} }, + ]) + + return { success: true } + }, + }) +) + +router.post( + '/deduplicate', + endpoint.create({ + resolve: async ({ ctx }) => { + ctx.ability.throwUnlessCan('manage', 'Institution') + + await ctx.institutionService.deduplicateInstitutions() + + return { success: true } + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/messages.router.ts b/apps/server/src/app/routes/messages.router.ts new file mode 100644 index 00000000000..d1f3f9bf188 --- /dev/null +++ b/apps/server/src/app/routes/messages.router.ts @@ -0,0 +1,31 @@ +import { Router } from 'express' +import { subject } from '@casl/ability' +import { MessageUpdateSchema } from '@maybe-finance/server/features' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.patch( + '/:id', + endpoint.create({ + input: MessageUpdateSchema, + async resolve({ ctx, req, input }) { + const message = await ctx.messageService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Message', message)) + return ctx.messageService.update(message.id, input) + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + async resolve({ ctx, req }) { + const message = await ctx.messageService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('Message', message)) + return ctx.messageService.delete(message.id) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/notifications.router.ts b/apps/server/src/app/routes/notifications.router.ts new file mode 100644 index 00000000000..3099358af5d --- /dev/null +++ b/apps/server/src/app/routes/notifications.router.ts @@ -0,0 +1,66 @@ +import { Router } from 'express' +import { z } from 'zod' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.put( + '/ask-the-advisor', + endpoint.create({ + input: z + .object({ + ataAll: z.boolean(), + ataSubmitted: z.boolean(), + ataReview: z.boolean(), + ataUpdate: z.boolean(), + ataClosed: z.boolean(), + ataExpire: z.boolean(), + }) + .partial(), + resolve: async ({ ctx, input }) => { + return ctx.prisma.user.update({ + where: { id: ctx.user!.id }, + data: input, + }) + }, + }) +) + +router.get( + '/convertkit/subscription', + endpoint.create({ + resolve: async ({ ctx }) => { + return ctx.convertKit.getSubscription(ctx.user!.convertKitId) + }, + }) +) + +router.post( + '/convertkit/subscribe', + endpoint.create({ + resolve: async ({ ctx }) => { + const auth0User = await ctx.managementClient.getUser({ id: ctx.user!.auth0Id }) + + const { subscription } = await ctx.convertKit.subscribe(auth0User.email!) + + await ctx.userService.update(ctx.user!.id, { + convertKitId: subscription.subscriber.id, + }) + + return subscription + }, + }) +) + +router.post( + '/convertkit/unsubscribe', + endpoint.create({ + resolve: async ({ ctx }) => { + const auth0User = await ctx.managementClient.getUser({ id: ctx.user!.auth0Id }) + const { subscriber } = await ctx.convertKit.unsubscribe(auth0User.email!) + return subscriber + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/plaid.router.ts b/apps/server/src/app/routes/plaid.router.ts new file mode 100644 index 00000000000..9f325715582 --- /dev/null +++ b/apps/server/src/app/routes/plaid.router.ts @@ -0,0 +1,91 @@ +import { Router } from 'express' +import { z } from 'zod' +import endpoint from '../lib/endpoint' +import axios from 'axios' +import { devOnly } from '../middleware' + +const router = Router() + +router.get( + '/status', + endpoint.create({ + resolve: async () => { + const { data } = await axios.get('https://status.plaid.com/api/v2/status.json') + return data + }, + }) +) + +/** + * Get link token for OAuth re-initialization + * + * @see https://plaid.com/docs/link/oauth/#reinitializing-link + */ +router.get( + '/link-token', + endpoint.create({ + resolve: async ({ ctx }) => { + const token = await ctx.plaidService.getLinkToken(ctx.user!.id) + return { token } + }, + }) +) + +router.post( + '/link-token', + endpoint.create({ + input: z.object({ + institutionId: z.string().optional(), + }), + resolve: async ({ ctx }) => { + // Step 1: Create a Link token for the client to use + // https://plaid.com/docs/api/tokens/#linktokencreate + const token = await ctx.plaidService.createLinkToken(ctx.user!.id) + + await ctx.plaidService.cacheLinkToken(ctx.user!.id, token) + + return { token } + }, + }) +) + +router.post( + '/exchange-public-token', + endpoint.create({ + input: z.object({ + token: z.string(), + institution: z.object({ + name: z.string(), + institution_id: z.string(), + }), + }), + resolve: ({ input: { token, institution }, ctx }) => { + // Step 2: Exchange public token provided to the client for a permanent access token + // https://plaid.com/docs/api/tokens/#itempublic_tokenexchange + return ctx.plaidService.exchangePublicToken(ctx.user!.id, token, institution) + }, + }) +) + +router.post( + '/sandbox/quick-add', + devOnly, + endpoint.create({ + input: z.object({ username: z.string().optional() }), + resolve: async ({ ctx, input: { username } }) => { + return ctx.plaidService.createSandboxAccount(ctx.user!.id, username) + }, + }) +) + +router.post( + '/institutions/sync', + endpoint.create({ + resolve: async ({ ctx }) => { + ctx.ability.throwUnlessCan('manage', 'Institution') + await ctx.queueService.getQueue('sync-institution').add('sync-plaid-institutions', {}) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/plans.router.ts b/apps/server/src/app/routes/plans.router.ts new file mode 100644 index 00000000000..0d32512cef8 --- /dev/null +++ b/apps/server/src/app/routes/plans.router.ts @@ -0,0 +1,164 @@ +import { Router } from 'express' +import { subject } from '@casl/ability' +import { + PlanCreateSchema, + PlanTemplateSchema, + PlanUpdateSchema, +} from '@maybe-finance/server/features' +import endpoint from '../lib/endpoint' +import { DateUtil, PlanUtil } from '@maybe-finance/shared' + +const router = Router() + +router.get( + '/', + endpoint.create({ + resolve: async ({ ctx }) => { + const { plans } = await ctx.planService.getAll(ctx.user!.id) + + if (plans.length > 0) return { plans } + + /** + * Generate a default plan so the user can start using the plan feature + * without requiring any data inputs up-front. + * + * Defaults to "Retirement" template + */ + const plan = await ctx.planService.createWithTemplate(ctx.user!, { + type: 'retirement', + data: { + // Defaults to user age of 30 and retirement age of 65 + retirementYear: DateUtil.ageToYear( + PlanUtil.RETIREMENT_MILESTONE_AGE, + DateUtil.dobToAge(ctx.user?.dob) ?? PlanUtil.DEFAULT_AGE + ), + }, + }) + + return { + plans: [plan], + } + }, + }) +) + +router.post( + '/', + endpoint.create({ + input: PlanCreateSchema, + resolve: async ({ input: { events, milestones, ...data }, ctx }) => { + ctx.ability.throwUnlessCan('create', 'Plan') + + return await ctx.planService.create({ + ...data, + userId: ctx.user!.id, + events: { create: events }, + milestones: { create: milestones }, + }) + }, + }) +) + +/** Create a new plan using a pre-defined template */ +router.post( + '/template', + endpoint.create({ + input: PlanTemplateSchema, + resolve: async ({ input, ctx }) => { + ctx.ability.throwUnlessCan('create', 'Plan') + + return await ctx.planService.createWithTemplate(ctx.user!, input) + }, + }) +) + +/** + * Update an existing plan using a pre-defined template + * + * Can be used to reset a template to defaults or + * add milestone-templates to an existing plan + */ +router.put( + '/:id/template', + endpoint.create({ + input: PlanTemplateSchema, + resolve: async ({ ctx, req, input }) => { + const plan = await ctx.planService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Plan', plan)) + + const shouldReset = req.query.reset === 'true' + + return await ctx.planService.updateWithTemplate(plan.id, input, shouldReset) + }, + }) +) + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const plan = await ctx.planService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Plan', plan)) + return plan + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: PlanUpdateSchema, + resolve: async ({ input: { events, milestones, ...data }, ctx, req }) => { + const plan = await ctx.planService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Plan', plan)) + const updatedPlan = await ctx.planService.update(plan.id, { + ...data, + events: events + ? { + create: events.create, + update: events.update + ? events.update.map(({ id, data }) => ({ where: { id }, data })) + : undefined, + deleteMany: events.delete ? { id: { in: events.delete } } : undefined, + } + : undefined, + milestones: milestones + ? { + create: milestones.create, + update: milestones.update + ? milestones.update.map(({ id, data }) => ({ where: { id }, data })) + : undefined, + deleteMany: milestones.delete + ? { id: { in: milestones.delete } } + : undefined, + } + : undefined, + }) + return updatedPlan + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const plan = await ctx.planService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('Plan', plan)) + return ctx.planService.delete(plan.id) + }, + }) +) + +router.get( + '/:id/projections', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const plan = await ctx.planService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Plan', plan)) + return ctx.planService.projections(plan.id) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/public.router.ts b/apps/server/src/app/routes/public.router.ts new file mode 100644 index 00000000000..e5cfdc97453 --- /dev/null +++ b/apps/server/src/app/routes/public.router.ts @@ -0,0 +1,21 @@ +import { Router } from 'express' +import env from '../../env' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.get( + '/users/card/:memberId', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const memberId = req.params.memberId + if (!memberId) throw new Error('No memberId provided for member details.') + + const clientUrl = env.NX_CLIENT_URL_CUSTOM || env.NX_CLIENT_URL + + return ctx.userService.getMemberCard(memberId, clientUrl) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/securities.router.ts b/apps/server/src/app/routes/securities.router.ts new file mode 100644 index 00000000000..35a1ce12624 --- /dev/null +++ b/apps/server/src/app/routes/securities.router.ts @@ -0,0 +1,48 @@ +import { Router } from 'express' +import { DateTime } from 'luxon' +import endpoint from '../lib/endpoint' + +const router = Router() + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + ctx.ability.throwUnlessCan('read', 'Security') + + return await ctx.prisma.security.findUniqueOrThrow({ + where: { id: +req.params.id }, + include: { + pricing: { + where: { + date: { + gte: DateTime.now().minus({ weeks: 52 }).toJSDate(), + lte: DateTime.now().toJSDate(), + }, + }, + orderBy: { + date: 'asc', + }, + }, + }, + }) + }, + }) +) + +router.get( + '/:id/details', + endpoint.create({ + resolve: async ({ ctx, req }) => { + ctx.ability.throwUnlessCan('read', 'Security') + + const security = await ctx.prisma.security.findUniqueOrThrow({ + where: { id: +req.params.id }, + }) + + return await ctx.marketDataService.getSecurityDetails(security) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/tools.router.ts b/apps/server/src/app/routes/tools.router.ts new file mode 100644 index 00000000000..80857456c7c --- /dev/null +++ b/apps/server/src/app/routes/tools.router.ts @@ -0,0 +1,155 @@ +import { Router } from 'express' +import _ from 'lodash' +import type Decimal from 'decimal.js' +import type { ProjectionInput } from '@maybe-finance/server/features' +import { AssetValue, monteCarlo, ProjectionCalculator } from '@maybe-finance/server/features' +import { StatsUtil } from '@maybe-finance/shared' + +const router = Router() + +const params: Record = { + stocks: ['0.05', '0.186'], + bonds: ['0.02', '0.052'], + cash: ['-0.02', '0.05'], + crypto: ['1.0', '1.0'], + property: ['0.1', '0.2'], + other: ['-0.02', '0'], +} + +function getInput(scenario: string, randomized = false): ProjectionInput { + const Value = (value, mean, stddev) => new AssetValue(value, mean, randomized ? stddev : 0) + + const scenarios: Record = { + portfolio_vizualizer: { + years: 30, + assets: [ + { + id: 'stock', + value: Value(800_000, ...params.stocks), + }, + { + id: 'bonds', + value: Value(150_000, ...params.bonds), + }, + { + id: 'cash', + value: Value(50_000, ...params.cash), + }, + ], + liabilities: [], + events: [ + { id: 'income', value: new AssetValue(100_000), end: 2032 }, + { id: 'expenses', value: new AssetValue(-60_000) }, + ], + milestones: [ + { id: 'retirement', type: 'net-worth', expenseMultiple: 25, expenseYears: 1 }, + ], + }, + debug: { + years: 56, + assets: [ + { + id: 'cash', + value: Value('283221', ...params.cash), + }, + { + id: 'other', + value: Value('221332', ...params.other), + }, + { + id: 'property', + value: Value('1300000', ...params.property), + }, + { + id: 'stocks', + value: Value('1421113', ...params.stocks), + }, + ], + liabilities: [], + events: [ + { + id: '3', + value: new AssetValue('-10000', '0.01'), + start: 2022, + end: 2072, + }, + { + id: '4', + value: new AssetValue('12000'), + start: 2050, + end: 2072, + }, + { + id: '5', + value: new AssetValue('17148'), + start: 2050, + end: 2072, + }, + { + id: '6', + value: new AssetValue('-120000', '0.01'), + start: 2022, + end: 2076, + }, + ], + milestones: [ + { + id: '2', + type: 'year', + year: 2057, + }, + ], + }, + } + + return scenarios[scenario] +} + +router.post('/projections', (req, res) => { + const calculator = new ProjectionCalculator() + + const scenario = 'debug' + const N = 500 + const tiles = ['0.1', '0.25', '0.5', '0.75', '0.9'] + + const inputTheo = getInput(scenario, false) + const inputRandomized = getInput(scenario, true) + + const theo = calculator.calculate(inputTheo) + const simulations = monteCarlo(() => calculator.calculate(inputRandomized), { n: N }) + + const simulationsWithStats = _.zipWith(...simulations, (...series) => { + const year = series[0].year + const netWorths = series.map((d) => d.netWorth) + + return { + year, + percentiles: StatsUtil.quantiles(netWorths, tiles), + successRate: StatsUtil.rateOf(netWorths, (nw) => nw.gt(0)), + ci95: StatsUtil.confidenceInterval(netWorths), + avg: StatsUtil.mean(netWorths), + netWorths: _.sortBy(netWorths, (nw) => +nw), + stddev: StatsUtil.stddev(netWorths), + } + }) + + const simulationsByPercentile = tiles.map((percentile, idx) => ({ + percentile, + simulation: simulationsWithStats.map(({ year, percentiles }) => ({ + year, + netWorth: percentiles[idx], + })), + })) + + const result = { + theo, + simulations, + simulationsWithStats, + simulationsByPercentile, + } + + // res.set('cache-control', 'public, max-age=60') + res.status(200).json(result) +}) + +export default router diff --git a/apps/server/src/app/routes/transactions.router.ts b/apps/server/src/app/routes/transactions.router.ts new file mode 100644 index 00000000000..19ce823bee2 --- /dev/null +++ b/apps/server/src/app/routes/transactions.router.ts @@ -0,0 +1,49 @@ +import { Router } from 'express' +import { subject } from '@casl/ability' +import endpoint from '../lib/endpoint' +import { + TransactionPaginateParams, + TransactionUpdateInputSchema, +} from '@maybe-finance/server/features' + +const router = Router() + +router.get( + '/', + endpoint.create({ + input: TransactionPaginateParams, + resolve: async ({ ctx, input }) => { + return ctx.transactionService.getAll(ctx.user!.id, input.pageIndex, input.pageSize) + }, + }) +) + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const transaction = await ctx.transactionService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Transaction', transaction)) + return transaction + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: TransactionUpdateInputSchema, + resolve: async ({ input, ctx, req }) => { + const transaction = await ctx.transactionService.get(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Transaction', transaction)) + + const updatedTransaction = await ctx.transactionService.update(+req.params.id, input) + + await ctx.accountService.syncBalances(transaction.accountId) + + return updatedTransaction + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/users.router.ts b/apps/server/src/app/routes/users.router.ts new file mode 100644 index 00000000000..4d702a06d5b --- /dev/null +++ b/apps/server/src/app/routes/users.router.ts @@ -0,0 +1,665 @@ +import { Router } from 'express' +import axios from 'axios' +import type { UnlinkAccountsParamsProvider } from 'auth0' +import { keyBy, mapValues, uniqBy } from 'lodash' +import { subject } from '@casl/ability' +import { z } from 'zod' +import { DateUtil, type SharedType } from '@maybe-finance/shared' +import endpoint from '../lib/endpoint' +import env from '../../env' +import crypto from 'crypto' +import { DateTime } from 'luxon' +import { + type OnboardingState, + type RegisteredStep, + UpdateOnboardingSchema, +} from '@maybe-finance/server/features' + +const router = Router() + +router.get( + '/', + endpoint.create({ + resolve: async ({ ctx }) => { + return ctx.userService.get(ctx.user!.id) + }, + }) +) + +router.get( + '/onboarding/:flow', + endpoint.create({ + resolve: async ({ ctx, req }) => { + let onboarding: SharedType.OnboardingResponse + + switch (req.params.flow) { + case 'main': + onboarding = await ctx.userService.buildMainOnboarding(ctx.user!.id) + break + case 'sidebar': + onboarding = await ctx.userService.buildSidebarOnboarding(ctx.user!.id) + break + default: + throw new Error(`${req.params.flow} is not a valid onboarding flow key`) + } + + const { steps, currentStep, progress, isComplete, isMarkedComplete } = onboarding + + return { + steps, + currentStep, + progress, + isComplete, + isMarkedComplete, + } + }, + }) +) + +router.put( + '/onboarding', + endpoint.create({ + input: UpdateOnboardingSchema, + resolve: async ({ ctx, input }) => { + const user = await ctx.prisma.user.findFirstOrThrow({ + where: { id: ctx.user!.id }, + select: { id: true, onboarding: true }, + }) + + const onboardingState = user.onboarding as OnboardingState | null + + // Initialize onboarding state + const onboarding = onboardingState + ? onboardingState + : ({ + main: { markedComplete: false, steps: [] }, + sidebar: { markedComplete: false, steps: [] }, + } as OnboardingState) + + input.updates.forEach((update: RegisteredStep) => { + const oldStepIdx = onboarding[input.flow].steps.findIndex( + (step) => step.key === update.key + ) + + // Create or update + if (oldStepIdx < 0) { + onboarding[input.flow].steps.push(update) + } else { + onboarding[input.flow].steps[oldStepIdx] = update + } + }) + + if (input.flow === 'sidebar' && input.markedComplete != null) { + onboarding['sidebar'].markedComplete = input.markedComplete + } + + ctx.logger.info( + `User onboarding updated. flow=${input.flow} updated=${input.updates.length} user=${ + ctx.user!.id + }`, + input + ) + + return ctx.prisma.user.update({ + where: { id: ctx.user!.id }, + data: { onboarding }, + }) + }, + }) +) + +router.get( + '/auth0-profile', + endpoint.create({ + resolve: async ({ ctx }) => { + return ctx.userService.getAuth0Profile(ctx.user!) + }, + }) +) + +router.put( + '/auth0-profile', + endpoint.create({ + input: z.object({ + enrolled_mfa: z.boolean(), + }), + resolve: ({ input, ctx }) => { + return ctx.managementClient.updateUser( + { id: ctx.user!.auth0Id }, + { user_metadata: { enrolled_mfa: input.enrolled_mfa } } + ) + }, + }) +) + +router.get( + '/subscription', + endpoint.create({ + resolve: async ({ ctx }) => { + if (!ctx.user || !ctx.user.id) { + throw new Error('User not found') + } + + return ctx.userService.getSubscription(ctx.user.id) + }, + }) +) + +router.get( + '/intercom', + endpoint.create({ + resolve: async ({ ctx }) => { + if (!ctx.user || !ctx.user.id) { + throw new Error('User not found') + } + + return ctx.userService.getIntercomMetadata(ctx.user.id, env.NX_INTERCOM_SECRET) + }, + }) +) + +router.put( + '/', + endpoint.create({ + input: z + .object({ + monthlyDebtUser: z.number().nullable(), + monthlyIncomeUser: z.number().nullable(), + monthlyExpensesUser: z.number().nullable(), + goals: z.string().array(), + riskAnswers: z + .object({ questionKey: z.string(), choiceKey: z.string() }) + .array() + .min(1), + userNotes: z.string(), + household: z.enum([ + 'single', + 'singleWithDependents', + 'dual', + 'dualWithDependents', + 'retired', + ]), + country: z.string().nullable(), + state: z.string().nullable(), + maybeGoals: z.enum(['aggregate', 'advice', 'plan']).array(), + maybeGoalsDescription: z.string().nullable(), + maybe: z.string().nullable(), + title: z.string().nullable(), + firstName: z.string(), + lastName: z.string(), + dob: z.string().transform((d) => DateUtil.datetimeTransform(d).toJSDate()), + linkAccountDismissedAt: z.date(), + }) + .partial(), + resolve: ({ input, ctx }) => { + if (!ctx.user || !ctx.user.id) { + throw new Error('Could not update user. User not found') + } + + return ctx.userService.update(ctx.user.id, input) + }, + }) +) + +router.get( + '/net-worth', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.dateTransform), + end: z.string().transform(DateUtil.dateTransform), + }) + .partial(), + resolve: ({ ctx, input: { start, end } }) => { + return ctx.userService.getNetWorthSeries(ctx.user!.id, start, end) + }, + }) +) + +router.get( + '/:id/net-worth', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.dateTransform), + end: z.string().transform(DateUtil.dateTransform), + }) + .partial(), + resolve: async ({ ctx, req, input: { start, end } }) => { + const user = await ctx.userService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('User', user)) + return ctx.userService.getNetWorthSeries(user.id, start, end) + }, + }) +) + +router.get( + '/net-worth/:date', + endpoint.create({ + resolve: ({ ctx, req }) => { + return ctx.userService.getNetWorth( + ctx.user!.id, + DateUtil.dateTransform(req.params.date) + ) + }, + }) +) + +router.get( + '/:id/net-worth/:date', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const user = await ctx.userService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('User', user)) + return ctx.userService.getNetWorth(user.id, DateUtil.dateTransform(req.params.date)) + }, + }) +) + +router.get( + '/:id/account-rollup', + endpoint.create({ + input: z + .object({ + start: z.string().transform(DateUtil.dateTransform), + end: z.string().transform(DateUtil.dateTransform), + }) + .partial(), + resolve: async ({ ctx, input: { start, end }, req }) => { + const user = await ctx.userService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('User', user)) + return ctx.accountService.getAccountRollup(user.id, start, end) + }, + }) +) + +router.get( + '/insights', + endpoint.create({ + resolve: ({ ctx }) => { + return ctx.insightService.getUserInsights({ userId: ctx.user!.id }) + }, + }) +) + +router.get( + '/:id/insights', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const user = await ctx.userService.get(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('User', user)) + return ctx.insightService.getUserInsights({ userId: user.id }) + }, + }) +) + +router.post( + '/link-accounts', + endpoint.create({ + input: z.object({ + secondaryJWT: z.string(), + secondaryProvider: z.string(), + }), + resolve: async ({ input, ctx }) => { + return ctx.userService.linkAccounts(ctx.user!.auth0Id, input.secondaryProvider, { + token: input.secondaryJWT, + domain: env.NX_AUTH0_CUSTOM_DOMAIN, + audience: env.NX_AUTH0_AUDIENCE, + }) + }, + }) +) + +router.post( + '/unlink-account', + endpoint.create({ + input: z.object({ + secondaryAuth0Id: z.string(), + secondaryProvider: z.string(), + }), + resolve: async ({ input, ctx }) => { + return ctx.userService.unlinkAccounts( + ctx.user!.auth0Id, + input.secondaryAuth0Id, + input.secondaryProvider as UnlinkAccountsParamsProvider + ) + }, + }) +) + +router.post( + '/resend-verification-email', + endpoint.create({ + input: z.object({ + auth0Id: z.string().optional(), + }), + resolve: async ({ input, ctx }) => { + const auth0Id = input.auth0Id ?? ctx.user?.auth0Id + if (!auth0Id) throw new Error('User not found') + + await ctx.managementClient.sendEmailVerification({ user_id: auth0Id }) + + ctx.logger.info(`Sent verification email to ${auth0Id}`) + + return { success: true } + }, + }) +) + +router.put( + '/change-password', + endpoint.create({ + input: z.object({ + newPassword: z.string(), + currentPassword: z.string(), + }), + resolve: async ({ input, ctx, req }) => { + if (!req.user || !req.user.sub) { + throw new Error('Unable to update password. No user found.') + } + + const user = await ctx.managementClient.getUser({ id: req.user.sub }) + + const { newPassword, currentPassword } = input + + /** + * Auth0 doesn't have a verify password endpoint on the Management API, so this is a secure way to + * verify that the old password was valid before changing it. Why they don't have this feature still? ¯\_(ツ)_/¯ + * + * @see https://community.auth0.com/t/change-password-validation/8158/10 + */ + try { + // If this succeeds, we know the old password was correct + await axios.post( + `https://${env.NX_AUTH0_DOMAIN}/oauth/token`, + { + grant_type: 'password', + username: user.email, + password: currentPassword, + audience: env.NX_AUTH0_AUDIENCE, + client_id: env.NX_AUTH0_CLIENT_ID, + client_secret: env.NX_AUTH0_CLIENT_SECRET, + }, + { headers: { 'content-type': 'application/json' } } + ) + } catch (err) { + let errMessage = 'Could not reset password' + + if (axios.isAxiosError(err)) { + errMessage = + err.response?.status === 401 + ? 'Invalid password, please try again' + : errMessage + } + + // Do not log the full error here, the user's password could be in it! + ctx.logger.error('Could not reset password') + + return { success: false, error: errMessage } + } + + // https://auth0.com/docs/connections/database/password-change#use-the-management-api + await ctx.managementClient.updateUser( + { id: req.user?.sub }, + { password: newPassword, connection: 'Username-Password-Authentication' } + ) + + return { success: true } + }, + }) +) + +router.post( + '/checkout-session', + endpoint.create({ + input: z.object({ + plan: z.string(), + }), + resolve: async ({ ctx, req, input }) => { + if (!req.user?.sub || !ctx.user) { + throw new Error('Unable to create checkout session. No user found.') + } + + const session = await ctx.stripe.checkout.sessions.create({ + line_items: [ + { + price: + input.plan === 'yearly' + ? env.NX_STRIPE_PREMIUM_YEARLY_PRICE_ID + : env.NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID, + quantity: 1, + }, + ], + mode: 'subscription', + success_url: `${req.headers.origin}/settings?tab=billing&status=success`, + cancel_url: `${req.headers.origin}/settings?tab=billing&status=cancelled`, + allow_promotion_codes: true, + + client_reference_id: req.user.sub, + + // Provide customer ID or user email, not both + ...(ctx.user.stripeCustomerId + ? { + customer: ctx.user.stripeCustomerId, + } + : { + customer_email: ( + await ctx.managementClient.getUser({ id: req.user.sub }) + ).email, + }), + }) + + if (!session.url) throw new Error('Failed to create checkout session with URL.') + + return { url: session.url } + }, + }) +) + +router.post( + '/customer-portal-session', + endpoint.create({ + resolve: async ({ ctx, req }) => { + if (!req.user?.sub || !ctx.user || !ctx.user.stripeCustomerId) { + throw new Error('Unable to create customer portal session. No user/customer found.') + } + + const session = await ctx.stripe.billingPortal.sessions.create({ + customer: ctx.user.stripeCustomerId, + return_url: `${req.headers.origin}/settings?tab=billing`, + }) + + if (!session.url) throw new Error('Failed to create customer portal session with URL.') + + return { url: session.url } + }, + }) +) + +/** + * Fetches the latest public version of each agreement + */ +router.get( + '/agreements/newest', + endpoint.create({ + input: z.object({ type: z.enum(['public', 'user']) }), + resolve: async ({ ctx, input }) => { + const agreements = await (input.type === 'user' + ? ctx.userService.getSignedAgreements(ctx.user!.id) + : ctx.userService.getNewestAgreements()) + + return agreements.map((agreement) => ({ + ...agreement, + url: `${env.NX_CDN_URL}/${agreement.src}`, + })) + }, + }) +) + +router.post( + '/agreements/sign', + endpoint.create({ + input: z.object({ agreementIds: z.number().array().length(5) }), + resolve: async ({ ctx, input }) => { + return ctx.userService.signAgreements(ctx.user!.id, input.agreementIds, ctx.s3) + }, + }) +) + +/** + * Idempotent, admin-only route that should be run each time we update a legal agreement + * + * - Sends email notifications to users when agreements are updated + * - Records acknowledgement in S3 + * - Bumps all successful users to latest agreement set in DB + */ +router.post( + '/agreements/notify-email', + endpoint.create({ + resolve: async ({ ctx }) => { + ctx.ability.throwUnlessCan('manage', 'User') + + const outdatedAgreements = await ctx.prisma.$queryRaw< + { + email: string + first_name: string + user_id: number + current_agreement_id: number + newest_agreement_id: number + }[] + >` + WITH signed_agreements AS ( + SELECT DISTINCT ON (sa.user_id, a.type) + u.email, + u.first_name, + sa.user_id, + sa.agreement_id AS current_agreement_id, + na.id AS newest_agreement_id + FROM signed_agreement sa + LEFT JOIN "user" u ON u.id = sa.user_id + LEFT JOIN agreement a ON a.id = sa.agreement_id + LEFT JOIN LATERAL ( + SELECT DISTINCT ON (a.type) + a.id, a.type + FROM agreement a + WHERE a.active + ORDER BY a.type, a.revision DESC + ) na ON na.type = a.type + ORDER BY sa.user_id, a.type, a.revision DESC + ) + SELECT * + FROM signed_agreements + WHERE current_agreement_id <> newest_agreement_id; + ` + + if (!outdatedAgreements.length) { + ctx.logger.info('All users have signed latest agreements, skipping email') + return { + updatedAgreementCount: 0, + } + } + + ctx.logger.info(`Updating ${outdatedAgreements.length} outdated agreements`) + + const newestAgreements = (await ctx.userService.getNewestAgreements()).map((a) => ({ + ...a, + url: `${env.NX_CDN_URL}/${a.src}`, + })) + + // Only send 1 email per user that will cover all 4 agreements + const uniqueAgreements = uniqBy(outdatedAgreements, 'user_id') + + // Send users a templated update email notifying them of document change + const batchResponse = await ctx.emailService.sendTemplate( + uniqueAgreements.map((agreement) => ({ + to: agreement.email, + template: { + alias: 'agreements-update', + model: { + name: agreement.first_name ?? '', + urls: mapValues(keyBy(newestAgreements, 'type'), (a) => a.url), + }, + }, + })) + ) + + // Save audit records of email sends + ctx.logger.info(`Agreement update emails sent`, batchResponse) + + const Body = Buffer.from(JSON.stringify(batchResponse)) + const Key = `private/agreements/email-receipts/${DateTime.now().toISO()}-agreements-update-email-receipt.txt` + await ctx.s3.upload({ + bucketKey: 'private', + Key, + Body, + ContentMD5: crypto.createHash('md5').update(Body).digest('base64'), + }) + + ctx.logger.info(`Agreement email receipt uploaded to S3 key=${Key}`) + + // Find all successful emails and create new agreement signatures for each + const successfulUpdateEmails = batchResponse + .filter((result) => result.ErrorCode === 0 && result.To) + .map((v) => v.To!) + + const agreementsToAcknowledge = outdatedAgreements.filter( + (oa) => successfulUpdateEmails.find((email) => email === oa.email) != null + ) + + // Our signature record pointer in DB + await ctx.prisma.signedAgreement.createMany({ + data: agreementsToAcknowledge.map((oa) => ({ + userId: oa.user_id, + agreementId: oa.newest_agreement_id, + src: Key, + })), + }) + + return { + updatedAgreementCount: successfulUpdateEmails.length, + } + }, + }) +) + +router.delete( + '/', + endpoint.create({ + input: z.object({ + confirm: z.literal(true), + }), + resolve: async ({ ctx }) => { + const { id } = ctx.user! + ctx.ability.throwUnlessCan('delete', subject('User', ctx.user!)) + await ctx.userService.delete(id) + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + input: z.object({ + confirm: z.literal(true), + }), + resolve: async ({ ctx, req }) => { + const user = await ctx.userService.get(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('User', user)) + await ctx.userService.delete(user.id) + }, + }) +) + +router.get( + '/card', + endpoint.create({ + resolve: async ({ ctx }) => { + return ctx.userService.getMemberCard( + ctx.user!.memberId, + env.NX_CLIENT_URL_CUSTOM || env.NX_CLIENT_URL + ) + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/valuations.router.ts b/apps/server/src/app/routes/valuations.router.ts new file mode 100644 index 00000000000..fed933ac877 --- /dev/null +++ b/apps/server/src/app/routes/valuations.router.ts @@ -0,0 +1,64 @@ +import { Router } from 'express' +import { z } from 'zod' +import { subject } from '@casl/ability' +import endpoint from '../lib/endpoint' +import { DateUtil } from '@maybe-finance/shared' + +const router = Router() + +router.get( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const valuation = await ctx.valuationService.getValuation(+req.params.id) + ctx.ability.throwUnlessCan('read', subject('Valuation', valuation)) + + return valuation + }, + }) +) + +router.put( + '/:id', + endpoint.create({ + input: z + .object({ + date: z.string().transform((d) => DateUtil.datetimeTransform(d).toJSDate()), + amount: z.number(), + }) + .optional(), + resolve: async ({ ctx, input, req }) => { + const valuation = await ctx.valuationService.getValuation(+req.params.id) + ctx.ability.throwUnlessCan('update', subject('Valuation', valuation)) + + if (!input) return valuation + + const updatedValuation = await ctx.valuationService.updateValuation(+req.params.id, { + date: input.date, + ...(input.amount && { amount: input.amount }), + }) + + await ctx.accountService.syncBalances(updatedValuation.accountId) + + return updatedValuation + }, + }) +) + +router.delete( + '/:id', + endpoint.create({ + resolve: async ({ ctx, req }) => { + const valuation = await ctx.valuationService.getValuation(+req.params.id) + ctx.ability.throwUnlessCan('delete', subject('Valuation', valuation)) + + const deletedValuation = await ctx.valuationService.deleteValuation(+req.params.id) + + await ctx.accountService.syncBalances(deletedValuation.accountId) + + return deletedValuation + }, + }) +) + +export default router diff --git a/apps/server/src/app/routes/webhooks.router.ts b/apps/server/src/app/routes/webhooks.router.ts new file mode 100644 index 00000000000..958e18fe902 --- /dev/null +++ b/apps/server/src/app/routes/webhooks.router.ts @@ -0,0 +1,134 @@ +import { Router } from 'express' +import { z } from 'zod' +import type { FinicityTypes } from '@maybe-finance/finicity-api' +import { validatePlaidJwt, validateFinicitySignature } from '../middleware' +import endpoint from '../lib/endpoint' +import stripe from '../lib/stripe' +import env from '../../env' + +const router = Router() + +router.post( + '/plaid/webhook', + process.env.NODE_ENV !== 'development' ? validatePlaidJwt : (_req, _res, next) => next(), + endpoint.create({ + input: z + .object({ + webhook_type: z.string(), + webhook_code: z.string(), + item_id: z.string(), + }) + .passthrough(), + async resolve({ input, ctx }) { + const { webhook_type, webhook_code, item_id, ...data } = input + + ctx.logger.info( + `rx[plaid_webhook] type=${webhook_type} code=${webhook_code} item=${item_id}`, + data + ) + + await ctx.plaidWebhooks.handleWebhook(input) + + return { status: 'ok' } + }, + }) +) + +router.post( + '/finicity/webhook', + process.env.NODE_ENV !== 'development' + ? validateFinicitySignature + : (_req, _res, next) => next(), + endpoint.create({ + input: z + .object({ + eventType: z.string(), + eventId: z.string().optional(), + customerId: z.string().optional(), + payload: z.record(z.any()).optional(), + }) + .passthrough(), + async resolve({ input, ctx }) { + const { eventType, eventId, customerId } = input + + ctx.logger.info( + `rx[finicity_webhook] event eventType=${eventType} eventId=${eventId} customerId=${customerId}` + ) + + // May contain sensitive info, only print at the debug level + ctx.logger.debug(`rx[finicity_webhook] event payload`, input) + + try { + await ctx.finicityWebhooks.handleWebhook(input as FinicityTypes.WebhookData) + } catch (err) { + // record error but don't throw, otherwise Finicity Connect behaves weird + ctx.logger.error(`[finicity_webhook] error handling webhook`, err) + } + + return { status: 'ok' } + }, + }) +) + +router.get('/finicity/txpush', (req, res) => { + const { txpush_verification_code } = req.query + if (!txpush_verification_code) { + return res.status(400).send('request missing txpush_verification_code') + } + + return res.status(200).contentType('text/plain').send(txpush_verification_code) +}) + +router.post( + '/finicity/txpush', + endpoint.create({ + input: z + .object({ + event: z.record(z.any()), // for now we'll just cast this to the appropriate type + }) + .passthrough(), + async resolve({ input: { event }, ctx }) { + const ev = event as FinicityTypes.TxPushEvent + + ctx.logger.info(`rx[finicity_txpush] event class=${ev.class} type=${ev.type}`) + + // May contain sensitive info, only print at the debug level + ctx.logger.debug(`rx[finicity_txpush] event payload`, event) + + await ctx.finicityWebhooks.handleTxPushEvent(ev) + + return { status: 'ok' } + }, + }) +) + +router.post( + '/stripe/webhook', + endpoint.create({ + async resolve({ req, ctx }) { + if (!req.headers['stripe-signature']) + throw new Error('Missing `stripe-signature` header') + + let event + try { + event = stripe.webhooks.constructEvent( + req.body, + req.headers['stripe-signature'], + env.NX_STRIPE_WEBHOOK_SECRET + ) + } catch (err) { + ctx.logger.error(`Failed to construct Stripe event`, err) + throw new Error('Failed to construct Stripe event') + } + + ctx.logger.info(`rx[stripe_webhook] type=${event.type} id=${event.id}`, event.data) + + await ctx.stripeWebhooks.handleWebhook(event) + + return { status: 'ok' } + }, + onSuccess: (_, res, data) => res.status(200).json(data), + }) +) + +export default router diff --git a/apps/server/src/app/trpc.ts b/apps/server/src/app/trpc.ts new file mode 100644 index 00000000000..72c10b149ef --- /dev/null +++ b/apps/server/src/app/trpc.ts @@ -0,0 +1,429 @@ +import * as trpc from '@trpc/server' +import type * as trpcExpress from '@trpc/server/adapters/express' +import { z } from 'zod' +import { subject } from '@casl/ability' +import _, { chain, groupBy } from 'lodash' +import sanitizeHtml from 'sanitize-html' +import { ATAUtil, superjson } from '@maybe-finance/shared' +import { createContext } from './lib/endpoint' +import mime from 'mime-types' +import env from '../env' +import { createPresignedPost } from '@aws-sdk/s3-presigned-post' +import { ServerUtil } from '@maybe-finance/server/shared' +import { GetSecretValueCommand } from '@aws-sdk/client-secrets-manager' +import { writeToString } from '@fast-csv/format' +import type { Prisma } from '@prisma/client' + +export async function createTRPCContext({ req }: trpcExpress.CreateExpressContextOptions) { + return createContext(req) +} + +type Context = trpc.inferAsyncReturnType + +const t = trpc.initTRPC.context().create({ + transformer: superjson, +}) + +/** + * Middleware + */ +const isUser = t.middleware(({ ctx, next }) => { + if (!ctx.user) { + throw new trpc.TRPCError({ code: 'UNAUTHORIZED', message: 'You must be a user' }) + } + + return next({ + ctx: { + ...ctx, + user: ctx.user, + }, + }) +}) + +/** + * Routers + */ +const Id = z.number().int() + +const advisorRouter = t.router({ + users: t.router({ + get: t.procedure + .use(isUser) + .input(Id) + .query(async ({ ctx, input }) => { + return ctx.prisma.user.findUniqueOrThrow({ + where: { id: input }, + }) + }), + update: t.procedure + .use(isUser) + .input( + z.object({ + userId: z.number(), + dependents: z.number().min(0).nullable(), + taxStatus: z + .enum([ + 'single', + 'married_joint', + 'married_separate', + 'head_of_household', + 'qualifying_widow', + ]) + .nullable(), + incomeType: z.string().nullable(), + grossIncome: z.number().nullable(), + }) + ) + .mutation(async ({ ctx, input }) => { + const { userId, ...data } = input + return ctx.prisma.user.update({ + where: { id: userId }, + data, + }) + }), + getAll: t.procedure.use(isUser).query(({ ctx }) => { + return ctx.prisma.user.findMany({ + where: { conversations: { some: {} } }, + }) + }), + getConversations: t.procedure + .use(isUser) + .input(Id) + .query(async ({ ctx, input }) => { + const advisorId = ctx.user!.advisor?.id + if (!advisorId) throw new Error('Could not find advisor') + + const conversations = await ctx.prisma.conversation.findMany({ + where: { + userId: input, + advisors: { some: { advisorId } }, + }, + include: { + user: { select: { id: true } }, + messages: { + include: { + user: { select: { advisor: true } }, + }, + take: 1, + orderBy: { createdAt: 'desc' }, + }, + }, + }) + + return _(conversations) + .map(({ messages, ...conversation }) => ({ + ...conversation, + lastMessage: messages.at(0) ?? null, + })) + .orderBy((c) => c.lastMessage?.createdAt ?? c.createdAt, 'desc') + .value() + }), + getNotes: t.procedure + .use(isUser) + .input(Id) + .query(({ ctx, input }) => { + return ctx.prisma.conversationNote.findMany({ + where: { userId: ctx.user!.id, conversation: { userId: input } }, + orderBy: { createdAt: 'desc' }, + }) + }), + getHoldings: t.procedure + .use(isUser) + .input(Id) + .query(async ({ ctx, input }) => { + const holdings = await ctx.prisma.$queryRaw< + { + account_id: number + institution_name: number + account_name: string + security_name: string + symbol: string + quantity: Prisma.Decimal + value: Prisma.Decimal + cost_basis: Prisma.Decimal | null + cost_basis_per_share: Prisma.Decimal | null + price: Prisma.Decimal | null + price_prev: Prisma.Decimal | null + }[] + >` + SELECT + a.id AS account_id, + ac.name as institution_name, + a.name AS account_name, + s.name AS security_name, + s.symbol, + he.quantity, + he.value, + he.cost_basis, + he.cost_basis_per_share, + he.price, + he.price_prev + FROM + "user" u + LEFT JOIN account_connection ac ON ac.user_id = u.id + LEFT JOIN account a ON a.account_connection_id = ac.id + INNER JOIN holdings_enriched he ON he.account_id = a.id + LEFT JOIN "security" s ON s.id = he.security_id + WHERE + u.id = ${input} + ORDER BY + a.name, + he.value DESC; + ` + + return { + csv: await writeToString(holdings, { headers: true }), + holdings: chain(holdings) + .groupBy('institution_name') + .mapValues((v) => groupBy(v, 'account_name')) + .value(), + } + }), + }), + conversations: t.router({ + getAll: t.procedure.use(isUser).query(async ({ ctx }) => { + const conversations = await ctx.prisma.conversation.findMany({ + where: ctx.ability.where.Conversation, + include: { + user: { select: { id: true } }, + messages: { + include: { + user: { select: { advisor: true } }, + }, + take: 1, + orderBy: { createdAt: 'desc' }, + }, + }, + }) + + return _(conversations) + .map(({ messages, ...conversation }) => ({ + ...conversation, + lastMessage: messages.at(0) ?? null, + })) + .orderBy((c) => c.lastMessage?.createdAt ?? c.createdAt, 'desc') + .value() + }), + get: t.procedure + .use(isUser) + .input(Id) + .query(async ({ ctx, input }) => { + const conversation = await ctx.prisma.conversation.findUniqueOrThrow({ + where: { id: input }, + include: { + user: true, + messages: { + include: { + user: { + include: { advisor: true }, + }, + }, + orderBy: { createdAt: 'asc' }, + }, + advisors: { + include: { advisor: true }, + }, + }, + }) + ctx.ability.throwUnlessCan('read', subject('Conversation', conversation)) + + const insights = await ctx.insightService.getUserInsights({ + userId: conversation.user.id, + }) + + const privKeyRes = await ctx.secretsClient.send( + new GetSecretValueCommand({ + SecretId: env.NX_CDN_SIGNER_SECRET_ID, + }) + ) + + if (!privKeyRes.SecretString) { + throw new Error( + 'Failed to obtain private key for url signing. Make sure key is stored in secrets manager and IAM user can access it from app.' + ) + } + + return { + ...conversation, + messages: conversation.messages.map((msg) => + ServerUtil.mapMessage(msg, { + cdnUrl: env.NX_CDN_URL, + pubKeyId: env.NX_CDN_SIGNER_PUBKEY_ID, + privKey: privKeyRes.SecretString!, + }) + ), + user: { + ...conversation.user, + insights, + }, + } + }), + update: t.procedure + .use(isUser) + .input(z.object({ id: Id, status: z.enum(['open', 'closed']) })) + .mutation(async ({ ctx, input }) => { + const { id, ...data } = input + const conversation = await ctx.prisma.conversation.findUniqueOrThrow({ + where: { id }, + }) + ctx.ability.throwUnlessCan('update', subject('Conversation', conversation)) + return ctx.prisma.conversation.update({ + where: { id: conversation.id }, + data, + }) + }), + getNote: t.procedure + .use(isUser) + .input(Id) + .query(({ ctx, input }) => { + return ctx.prisma.conversationNote.findUnique({ + where: { + userId_conversationId: { userId: ctx.user!.id, conversationId: input }, + }, + }) + }), + upsertNote: t.procedure + .use(isUser) + .input( + z.object({ + body: z.string(), + conversationId: z.number(), + }) + ) + .mutation(async ({ ctx, input }) => { + return ctx.prisma.conversationNote.upsert({ + where: { + userId_conversationId: { + userId: ctx.user!.id, + conversationId: input.conversationId, + }, + }, + create: { + body: input.body, + userId: ctx.user!.id, + conversationId: input.conversationId, + }, + update: { body: input.body }, + }) + }), + }), + conversationAdvisors: t.router({ + create: t.procedure + .use(isUser) + .input(z.object({ conversationId: Id, advisorId: Id })) + .mutation(async ({ ctx, input }) => { + const conversation = await ctx.prisma.conversation.findUniqueOrThrow({ + where: { id: input.conversationId }, + }) + ctx.ability.throwUnlessCan('create', 'ConversationAdvisor') + const advisor = await ctx.prisma.conversationAdvisor.create({ + data: { + conversationId: conversation.id, + advisorId: input.advisorId, + }, + }) + + // Notify user that the advisor has seen their message + await ctx.queueService.getQueue('send-email').add('send-email', { + type: 'conversation-notification', + notification: { + type: 'review', + conversationId: conversation.id, + }, + }) + + return advisor + }), + delete: t.procedure + .use(isUser) + .input(z.object({ conversationId: Id, advisorId: Id })) + .mutation(async ({ ctx, input }) => { + const conversation = await ctx.prisma.conversation.findUniqueOrThrow({ + where: { id: input.conversationId }, + }) + ctx.ability.throwUnlessCan('delete', 'ConversationAdvisor') + return ctx.prisma.conversationAdvisor.delete({ + where: { + conversationId_advisorId: { + conversationId: conversation.id, + advisorId: input.advisorId, + }, + }, + }) + }), + }), +}) + +export const appRouter = t.router({ + advisor: advisorRouter, + users: t.router({ + me: t.procedure.use(isUser).query(({ ctx }) => ctx.user), + }), + messages: t.router({ + signS3Url: t.procedure + .use(isUser) + .input( + z.object({ + conversationId: z.number(), + mimeType: z.string().optional(), + }) + ) + .mutation(async ({ ctx, input }) => { + const { url, fields } = await createPresignedPost(ctx.s3.cli, { + Bucket: ctx.s3.buckets['private'], + Key: + ATAUtil.generateS3Filename(input.conversationId) + + (input.mimeType ? `.${mime.extension(input.mimeType)}` : ''), + Fields: { + success_action_status: '201', + }, + }) + + return { + method: 'POST', + url, + fields, + } + }), + create: t.procedure + .use(isUser) + .input( + z.object({ + conversationId: Id, + type: z.enum(['text', 'audio', 'video']), + body: z + .string() + .transform((s) => sanitizeHtml(s)) + .nullish(), + mediaSrc: z.string().nullish(), + }) + ) + .mutation(async ({ ctx, input: { conversationId, ...data } }) => { + const conversation = await ctx.prisma.conversation.findUniqueOrThrow({ + where: { id: conversationId }, + }) + ctx.ability.throwUnlessCan('update', subject('Conversation', conversation)) + + const newMessage = await ctx.messageService.create( + { + ...data, + conversationId: conversation.id, + userId: ctx.user.id, + }, + ctx.user.id !== conversation.userId + ) + + return newMessage + }), + delete: t.procedure + .use(isUser) + .input(Id) + .mutation(async ({ ctx, input }) => { + const message = await ctx.messageService.get(input) + ctx.ability.throwUnlessCan('delete', subject('Message', message)) + return ctx.prisma.message.delete({ where: { id: message.id } }) + }), + }), +}) + +export type AppRouter = typeof appRouter diff --git a/apps/server/src/assets/maybe.svg b/apps/server/src/assets/maybe.svg new file mode 100644 index 00000000000..be6ed53f50f --- /dev/null +++ b/apps/server/src/assets/maybe.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/apps/server/src/assets/script.js b/apps/server/src/assets/script.js new file mode 100644 index 00000000000..7cc51254d7d --- /dev/null +++ b/apps/server/src/assets/script.js @@ -0,0 +1,9 @@ +// eslint-disable-next-line +function login() { + window.location.href = '/admin/login' +} + +// eslint-disable-next-line +function logout() { + window.location.href = '/admin/logout' +} diff --git a/apps/server/src/assets/styles.css b/apps/server/src/assets/styles.css new file mode 100644 index 00000000000..e21360aa1d1 --- /dev/null +++ b/apps/server/src/assets/styles.css @@ -0,0 +1,51 @@ +a { + text-decoration: none; +} + +.links { + display: flex; + justify-content: center; + min-width: 350px; +} + +.links .btn { + margin-left: 10px; +} + +body { + background-color: #242629; + height: 100vh; + color: white; +} + +.unauthorized { + background-color: #f85c41; + padding: 10px 20px; + color: #f8f9fa; + border-radius: 5px; + margin-bottom: 40px; + text-align: center; + max-width: 250px; +} + +.container { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + transform: translateY(-60px); +} + +.btn { + background-color: #3bc9db; + padding: 10px; + color: #242629; + border: none; + border-radius: 5px; +} + +.btn:hover { + background-color: rgba(102, 217, 232, 0.9); + cursor: pointer; +} diff --git a/apps/server/src/env.ts b/apps/server/src/env.ts new file mode 100644 index 00000000000..e3b76c0a834 --- /dev/null +++ b/apps/server/src/env.ts @@ -0,0 +1,104 @@ +import { z } from 'zod' + +const toOriginArray = (s?: string) => { + if (!s) return [] + + const originList = (s || '').split(',').map((s) => s.trim()) + + return originList.map((origin) => { + const originParts = origin.split('.') + + // Search for the specific pattern: domain.tld (e.g. maybe.co) and enable wildcard access on domain + if (originParts.length === 2) { + return new RegExp(`${originParts[0]}\\.${originParts[1]}`) + } else { + return origin + } + }) +} + +const envSchema = z.object({ + NX_API_URL: z.string().url().default('http://localhost:3333'), + NX_CDN_URL: z.string().url().default('https://staging-cdn.maybe.co'), + NX_WEBHOOK_URL: z.string().url().optional(), + + NX_CLIENT_URL: z.string().url().default('http://localhost:4200'), + NX_CLIENT_URL_CUSTOM: z.string().url().default('http://localhost:4200'), + + NX_ADVISOR_URL: z.string().url().default('http://localhost:4201'), + + NX_REDIS_URL: z.string().default('redis://localhost:6379'), + + NX_DATABASE_URL: z.string(), + NX_DATABASE_SECRET: z.string(), + NX_SESSION_SECRET: z.string(), + + NX_NGROK_URL: z.string().default('http://localhost:4551'), + + // Dev doesn't have a custom domain, so replace with the original dev URL + NX_AUTH0_DOMAIN: z.string().default('REPLACE_THIS'), + NX_AUTH0_CUSTOM_DOMAIN: z.string().default('REPLACE_THIS'), + NX_AUTH0_AUDIENCE: z.string().default('https://maybe-finance-api/v1'), + NX_AUTH0_CLIENT_ID: z.string().default('REPLACE_THIS'), + NX_AUTH0_CLIENT_SECRET: z.string(), + NX_AUTH0_MGMT_CLIENT_ID: z.string().default('REPLACE_THIS'), + NX_AUTH0_MGMT_CLIENT_SECRET: z.string(), + + NX_PLAID_CLIENT_ID: z.string().default('REPLACE_THIS'), + NX_PLAID_SECRET: z.string(), + NX_PLAID_ENV: z.string().default('sandbox'), + + NX_FINICITY_APP_KEY: z.string(), + NX_FINICITY_PARTNER_ID: z.string().default('REPLACE_THIS'), + NX_FINICITY_PARTNER_SECRET: z.string(), + NX_FINICITY_ENV: z.string().default('sandbox'), + + NX_SENTRY_DSN: z.string().optional(), + NX_SENTRY_ENV: z.string().optional(), + + NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'), + + NX_POLYGON_API_KEY: z.string().default(''), + + NX_PORT: z.string().default('3333'), + NX_CORS_ORIGINS: z.string().default('https://localhost.maybe.co').transform(toOriginArray), + + NX_MORGAN_LOG_LEVEL: z + .string() + .default(process.env.NODE_ENV === 'development' ? 'dev' : 'combined'), + + NX_INTERCOM_SECRET: z.string().optional(), + + NX_STRIPE_SECRET_KEY: z + .string() + .default( + 'REPLACE_THIS' + ), + NX_STRIPE_WEBHOOK_SECRET: z + .string() + .default('whsec_REPLACE_THIS'), + NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID: z.string().default('price_REPLACE_THIS'), + NX_STRIPE_PREMIUM_YEARLY_PRICE_ID: z.string().default('price_REPLACE_THIS'), + + NX_CDN_PRIVATE_BUCKET: z + .string() + .default('REPLACE_THIS'), + NX_CDN_PUBLIC_BUCKET: z + .string() + .default('REPLACE_THIS'), + + // Key to secrets manager value + NX_CDN_SIGNER_SECRET_ID: z.string().default('/apps/maybe-app/CLOUDFRONT_SIGNER1_PRIV'), + + // Key to Cloudfront pub key + NX_CDN_SIGNER_PUBKEY_ID: z.string().default('REPLACE_THIS'), + + NX_CONVERTKIT_SECRET: z.string(), + NX_POSTMARK_FROM_ADDRESS: z.string().default('account@maybe.co'), + NX_POSTMARK_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'), + NX_POSTMARK_API_TOKEN: z.string().default('REPLACE_THIS'), +}) + +const env = envSchema.parse(process.env) + +export default env diff --git a/apps/server/src/environments/environment.prod.ts b/apps/server/src/environments/environment.prod.ts new file mode 100644 index 00000000000..3ec78232fbf --- /dev/null +++ b/apps/server/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +} diff --git a/apps/server/src/environments/environment.ts b/apps/server/src/environments/environment.ts new file mode 100644 index 00000000000..3ce6715d1f2 --- /dev/null +++ b/apps/server/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +} diff --git a/apps/server/src/main.ts b/apps/server/src/main.ts new file mode 100644 index 00000000000..f88e3287351 --- /dev/null +++ b/apps/server/src/main.ts @@ -0,0 +1,26 @@ +import type { AddressInfo } from 'net' +import env from './env' +import app from './app/app' +import logger from './app/lib/logger' +import ldClient from './app/lib/ldClient' +import * as Sentry from '@sentry/node' + +process.on('uncaughtException', function (error) { + Sentry.captureException(error) + logger.error('server: uncaught exception', error) +}) + +process.on('unhandledRejection', (reason, promise) => { + Sentry.captureException(reason) + logger.error(`server: unhandled promise rejection: ${promise}: ${reason}`) +}) + +const server = app.listen(env.NX_PORT, () => { + logger.info(`🚀 API listening at http://localhost:${(server.address() as AddressInfo).port}`) +}) + +// Handle SIGTERM coming from ECS Fargate +process.on('SIGTERM', () => server.close()) + +server.on('close', () => ldClient.close()) +server.on('error', (err) => logger.error('Server failed to start from main.ts', err)) diff --git a/apps/server/tsconfig.app.json b/apps/server/tsconfig.app.json new file mode 100644 index 00000000000..371b72daf8a --- /dev/null +++ b/apps/server/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["node", "express"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts", "../../custom-express.d.ts"] +} diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json new file mode 100644 index 00000000000..3f672a82fce --- /dev/null +++ b/apps/server/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + "noImplicitAny": false, + "strict": true, + "strictNullChecks": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/server/tsconfig.spec.json b/apps/server/tsconfig.spec.json new file mode 100644 index 00000000000..bbed603e4e7 --- /dev/null +++ b/apps/server/tsconfig.spec.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": ["**/*.spec.ts", "**/*.test.ts", "../../custom-express.d.ts", "jest.config.ts"] +} diff --git a/apps/workers/.eslintrc.json b/apps/workers/.eslintrc.json new file mode 100644 index 00000000000..3bbbc79d34a --- /dev/null +++ b/apps/workers/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*", "Dockerfile"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts"], + "rules": { + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }] + } + } + ] +} diff --git a/apps/workers/Dockerfile b/apps/workers/Dockerfile new file mode 100644 index 00000000000..7bee491f4c6 --- /dev/null +++ b/apps/workers/Dockerfile @@ -0,0 +1,21 @@ +# ------------------------------------------ +# BUILD STAGE +# ------------------------------------------ +# Full Node image must be used for node_modules install +# TODO - Upgrade to v18.x (LTS) when this issue is resolved - https://github.com/prisma/prisma/issues/10649#issuecomment-1249209025 +FROM node:16.18.1 as builder + +WORKDIR /app +COPY ./dist/apps/workers ./yarn.lock ./prisma ./ +RUN yarn install --production + +# ------------------------------------------ +# PROD STAGE +# ------------------------------------------ +FROM node:16.18.1-slim as prod + +# Used for container health checks +RUN apt-get update && apt-get install curl -y +WORKDIR /app +USER node +COPY --from=builder /app . diff --git a/apps/workers/jest.config.ts b/apps/workers/jest.config.ts new file mode 100644 index 00000000000..092c9f1b1e5 --- /dev/null +++ b/apps/workers/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'workers', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'js', 'html'], + coverageDirectory: '../../coverage/apps/workers', +} diff --git a/apps/workers/src/app/__tests__/finicity.integration.spec.ts b/apps/workers/src/app/__tests__/finicity.integration.spec.ts new file mode 100644 index 00000000000..b56e632c711 --- /dev/null +++ b/apps/workers/src/app/__tests__/finicity.integration.spec.ts @@ -0,0 +1,317 @@ +import fs from 'fs' +import type { User } from '@prisma/client' +import { FinicityTestData } from '../../../../../tools/test-data' +import { FinicityApi, type FinicityTypes } from '@maybe-finance/finicity-api' +jest.mock('@maybe-finance/finicity-api') +import { + FinicityETL, + FinicityService, + type IAccountConnectionProvider, +} from '@maybe-finance/server/features' +import { createLogger, etl } from '@maybe-finance/server/shared' +import prisma from '../lib/prisma' +import { resetUser } from './helpers/user.test-helper' +import { transports } from 'winston' + +const logger = createLogger({ level: 'debug', transports: [new transports.Console()] }) +const finicity = jest.mocked(new FinicityApi('APP_KEY', 'PARTNER_ID', 'PARTNER_SECRET')) + +/** mock implementation of finicity's pagination logic which as of writing uses 1-based indexing */ +function finicityPaginate(data: T[], start = 1, limit = 1_000): T[] { + const startIdx = Math.max(0, start - 1) + return data.slice(startIdx, startIdx + limit) +} + +describe('Finicity', () => { + let user: User + + beforeEach(async () => { + jest.clearAllMocks() + + user = await resetUser(prisma) + }) + + it('syncs connection', async () => { + finicity.getCustomerAccounts.mockResolvedValue({ accounts: FinicityTestData.accounts }) + + finicity.getAccountTransactions.mockImplementation(({ accountId, start, limit }) => { + const transactions = FinicityTestData.transactions.filter( + (t) => t.accountId === +accountId + ) + + const page = finicityPaginate(transactions, start, limit) + + return Promise.resolve({ + transactions: page, + found: transactions.length, + displaying: page.length, + moreAvailable: page.length < transactions.length ? 'true' : 'false', + fromDate: '1588939200', + toDate: '1651492800', + sort: 'desc', + }) + }) + + const finicityETL = new FinicityETL(logger, prisma, finicity) + + const service: IAccountConnectionProvider = new FinicityService( + logger, + prisma, + finicity, + finicityETL, + '', + true + ) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST_FINICITY', + type: 'finicity', + finicityInstitutionId: 'REPLACE_THIS', + finicityInstitutionLoginId: 'REPLACE_THIS', + }, + }) + + await service.sync(connection) + + const { accounts } = await prisma.accountConnection.findUniqueOrThrow({ + where: { + id: connection.id, + }, + include: { + accounts: { + include: { + transactions: true, + investmentTransactions: true, + holdings: true, + valuations: true, + }, + }, + }, + }) + + expect(accounts).toHaveLength(FinicityTestData.accounts.length) + + // eslint-disable-next-line + const [auto, mortgage, roth, brokerage, loc, credit, savings, checking] = + FinicityTestData.accounts + + // mortgage + const mortgageAccount = accounts.find((a) => a.finicityAccountId === mortgage.id)! + expect(mortgageAccount.transactions).toHaveLength( + FinicityTestData.transactions.filter((t) => t.accountId === +mortgage.id).length + ) + expect(mortgageAccount.holdings).toHaveLength(0) + expect(mortgageAccount.valuations).toHaveLength(0) + expect(mortgageAccount.investmentTransactions).toHaveLength(0) + + // brokerage + const brokerageAccount = accounts.find((a) => a.finicityAccountId === brokerage.id)! + expect(brokerageAccount.transactions).toHaveLength(0) + expect(brokerageAccount.holdings).toHaveLength(brokerage.position!.length) + expect(brokerageAccount.valuations).toHaveLength(0) + expect(brokerageAccount.investmentTransactions).toHaveLength( + FinicityTestData.transactions.filter((t) => t.accountId === +brokerage.id).length + ) + + // credit + const creditAccount = accounts.find((a) => a.finicityAccountId === credit.id)! + expect(creditAccount.transactions).toHaveLength( + FinicityTestData.transactions.filter((t) => t.accountId === +credit.id).length + ) + expect(creditAccount.holdings).toHaveLength(0) + expect(creditAccount.valuations).toHaveLength(0) + expect(creditAccount.investmentTransactions).toHaveLength(0) + + // savings + const savingsAccount = accounts.find((a) => a.finicityAccountId === savings.id)! + expect(savingsAccount.transactions).toHaveLength( + FinicityTestData.transactions.filter((t) => t.accountId === +savings.id).length + ) + expect(savingsAccount.holdings).toHaveLength(0) + expect(savingsAccount.valuations).toHaveLength(0) + expect(savingsAccount.investmentTransactions).toHaveLength(0) + + // checking + const checkingAccount = accounts.find((a) => a.finicityAccountId === checking.id)! + expect(checkingAccount.transactions).toHaveLength( + FinicityTestData.transactions.filter((t) => t.accountId === +checking.id).length + ) + expect(checkingAccount.holdings).toHaveLength(0) + expect(checkingAccount.valuations).toHaveLength(0) + expect(checkingAccount.investmentTransactions).toHaveLength(0) + }) + + it('syncs Betterment investment account', async () => { + finicity.getCustomerAccounts.mockResolvedValue({ + accounts: [FinicityTestData.bettermentAccount], + }) + + finicity.getAccountTransactions.mockImplementation(({ accountId, start, limit }) => { + const transactions = FinicityTestData.bettermentTransactions.filter( + (t) => t.accountId === +accountId + ) + + const page = finicityPaginate(transactions, start, limit) + + return Promise.resolve({ + transactions: page, + found: transactions.length, + displaying: page.length, + moreAvailable: page.length < transactions.length ? 'true' : 'false', + fromDate: '1588939200', + toDate: '1651492800', + sort: 'desc', + }) + }) + + const finicityETL = new FinicityETL(logger, prisma, finicity) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST[Betterment]', + type: 'finicity', + finicityInstitutionId: FinicityTestData.bettermentAccount.institutionId, + finicityInstitutionLoginId: + FinicityTestData.bettermentAccount.institutionLoginId.toString(), + }, + }) + + await etl(finicityETL, connection) + }) + + it('syncs investment transactions w/o securities', async () => { + finicity.getCustomerAccounts.mockResolvedValue({ + accounts: [FinicityTestData.accounts.find((a) => a.type === 'investment')!], + }) + + finicity.getAccountTransactions.mockImplementation(({ accountId, start, limit }) => { + const transactions: FinicityTypes.Transaction[] = [ + { + id: 1, + amount: 123, + accountId: +accountId, + customerId: 123, + status: 'active', + description: 'VANGUARD INST INDEX', + memo: 'Contributions', + type: 'Contributions', + unitQuantity: 8.283, + postedDate: 1674043200, + transactionDate: 1674043200, + createdDate: 1674707388, + tradeDate: 1674025200, + settlementDate: 1674043200, + investmentTransactionType: 'contribution', + }, + { + id: 2, + amount: -3.21, + accountId: +accountId, + customerId: 123, + status: 'active', + description: 'VANGUARD TARGET 2045', + memo: 'RECORDKEEPING FEE', + type: 'RECORDKEEPING FEE', + unitQuantity: 0.014, + postedDate: 1672747200, + transactionDate: 1672747200, + createdDate: 1674707388, + tradeDate: 1672729200, + settlementDate: 1672747200, + investmentTransactionType: 'fee', + }, + { + id: 3, + amount: -1.23, + accountId: +accountId, + customerId: 123, + status: 'active', + description: 'VANGUARD INST INDEX', + memo: 'Realized Gain/Loss', + type: 'Realized Gain/Loss', + unitQuantity: 0e-8, + postedDate: 1672747200, + transactionDate: 1672747200, + createdDate: 1674707388, + tradeDate: 1672729200, + settlementDate: 1672747200, + investmentTransactionType: 'other', + }, + ] + + const page = finicityPaginate(transactions, start, limit) + + return Promise.resolve({ + transactions: page, + found: transactions.length, + displaying: page.length, + moreAvailable: page.length < transactions.length ? 'true' : 'false', + fromDate: '1588939200', + toDate: '1651492800', + sort: 'desc', + }) + }) + + const finicityETL = new FinicityETL(logger, prisma, finicity) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST[Betterment]', + type: 'finicity', + finicityInstitutionId: FinicityTestData.bettermentAccount.institutionId, + finicityInstitutionLoginId: + FinicityTestData.bettermentAccount.institutionLoginId.toString(), + }, + }) + + await etl(finicityETL, connection) + + const accounts = await prisma.account.findMany({ + where: { + accountConnectionId: connection.id, + }, + include: { + holdings: true, + transactions: true, + investmentTransactions: true, + }, + }) + expect(accounts).toHaveLength(1) + + const account = accounts[0] + expect(account.holdings).toHaveLength(0) + expect(account.transactions).toHaveLength(0) + expect(account.investmentTransactions).toHaveLength(3) + }) + + /** + * This test is for debugging w/ real data locally + */ + it.skip('debug', async () => { + const data = (name: string) => + JSON.parse( + fs.readFileSync(`${process.env.NX_TEST_DATA_FOLDER}/finicity/${name}.json`, 'utf-8') + ) + + finicity.getCustomerAccounts.mockResolvedValue(data('accounts')) + finicity.getAccountTransactions.mockResolvedValue(data('transactions')) + + const finicityETL = new FinicityETL(logger, prisma, finicity) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST[DEBUG]', + type: 'finicity', + finicityInstitutionId: '123', + finicityInstitutionLoginId: '123', + }, + }) + + await etl(finicityETL, connection) + }) +}) diff --git a/apps/workers/src/app/__tests__/helpers/user.test-helper.ts b/apps/workers/src/app/__tests__/helpers/user.test-helper.ts new file mode 100644 index 00000000000..d581e3a6082 --- /dev/null +++ b/apps/workers/src/app/__tests__/helpers/user.test-helper.ts @@ -0,0 +1,25 @@ +import type { PrismaClient, User } from '@prisma/client' + +export async function resetUser( + prisma: PrismaClient, + auth0Id = 'auth0|workers-integration-test-id' +): Promise { + // eslint-disable-next-line + const [_, __, ___, user] = await prisma.$transaction([ + prisma.$executeRaw`DELETE FROM "user" WHERE auth0_id=${auth0Id};`, + + // Deleting a user does not cascade to securities, so delete all security records + prisma.$executeRaw`DELETE from security;`, + prisma.$executeRaw`DELETE from security_pricing;`, + + prisma.user.create({ + data: { + auth0Id, + email: 'test@example.com', + finicityCustomerId: 'TEST', + }, + }), + ]) + + return user +} diff --git a/apps/workers/src/app/__tests__/plaid.integration.spec.ts b/apps/workers/src/app/__tests__/plaid.integration.spec.ts new file mode 100644 index 00000000000..3d5aebb44df --- /dev/null +++ b/apps/workers/src/app/__tests__/plaid.integration.spec.ts @@ -0,0 +1,285 @@ +import fs from 'fs' +import type { User } from '@prisma/client' +import type { IAccountConnectionProvider } from '@maybe-finance/server/features' +import type { IMarketDataService } from '@maybe-finance/server/shared' +import { PlaidService, PlaidETL } from '@maybe-finance/server/features' +import { createLogger, transports } from 'winston' +import { PlaidApi } from 'plaid' +jest.mock('plaid') +import { CryptoService, etl } from '@maybe-finance/server/shared' +import { TestUtil } from '@maybe-finance/shared' +import prisma from '../lib/prisma' +import { resetUser } from './helpers/user.test-helper' +import { uniqBy } from 'lodash' +import { PlaidTestData } from '../../../../../tools/test-data' + +const logger = createLogger({ level: 'debug', transports: [new transports.Console()] }) +const crypto = new CryptoService('SECRET') +const marketDataService: Pick = { + getOptionDetails: async () => ({ sharesPerContract: 100 }), +} + +const plaid = jest.mocked(new PlaidApi()) +const plaidETL = new PlaidETL(logger, prisma, plaid, crypto, marketDataService) +const service: IAccountConnectionProvider = new PlaidService( + logger, + prisma, + plaid, + plaidETL, + crypto, + 'PLAID_WEBHOOK_URL', + 'CLIENT_URL' +) + +// When debugging, we don't want the tests to time out +if (process.env.IS_VSCODE_DEBUG === 'true') { + jest.setTimeout(100000) +} + +describe('Plaid', () => { + let user: User + + beforeEach(async () => { + jest.clearAllMocks() + + user = await resetUser(prisma) + }) + + it('syncs connection', async () => { + plaid.accountsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + accounts: [ + PlaidTestData.checkingAccount, + PlaidTestData.creditAccount, + PlaidTestData.brokerageAccount, + ], + item: PlaidTestData.item, + request_id: '', + }) + ) + + plaid.transactionsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.checkingAccount, PlaidTestData.creditAccount], + transactions: [ + ...PlaidTestData.checkingTransactions, + ...PlaidTestData.creditTransactions, + ], + total_transactions: + PlaidTestData.checkingTransactions.length + + PlaidTestData.creditTransactions.length, + item: PlaidTestData.item, + request_id: '', + }) + ) + + plaid.investmentsTransactionsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.brokerageAccount], + investment_transactions: PlaidTestData.investmentTransactions, + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + total_investment_transactions: PlaidTestData.investmentTransactions.length, + item: PlaidTestData.item, + request_id: '', + }) + ) + + plaid.investmentsHoldingsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.brokerageAccount], + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + item: PlaidTestData.item, + request_id: '', + }) + ) + + plaid.liabilitiesGet.mockResolvedValue( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.creditAccount], + liabilities: { + credit: [PlaidTestData.creditCardLiability], + mortgage: null, + student: null, + }, + item: PlaidTestData.item, + request_id: '', + }) + ) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST_PLAID', + type: 'plaid', + plaidAccessToken: 'abcdef', + }, + }) + + await service.sync(connection) + + const { accounts } = await prisma.accountConnection.findUniqueOrThrow({ + where: { + id: connection.id, + }, + include: { + accounts: { + include: { + balances: { + where: PlaidTestData.testDates.prismaWhereFilter, + orderBy: { date: 'asc' }, + }, + transactions: true, + holdings: true, + valuations: true, + investmentTransactions: true, + }, + }, + }, + }) + + expect(accounts).toHaveLength(3) + + // checking + const checkingAccount = accounts.find( + (a) => a.plaidAccountId === PlaidTestData.checkingAccount.account_id + )! + expect(checkingAccount.transactions).toHaveLength(PlaidTestData.checkingTransactions.length) + expect(checkingAccount.holdings).toHaveLength(0) + expect(checkingAccount.valuations).toHaveLength(0) + expect(checkingAccount.investmentTransactions).toHaveLength(0) + + // credit + const creditAccount = accounts.find( + (a) => a.plaidAccountId === PlaidTestData.creditAccount.account_id + )! + expect(creditAccount.transactions).toHaveLength(PlaidTestData.creditTransactions.length) + expect(creditAccount.holdings).toHaveLength(0) + expect(creditAccount.valuations).toHaveLength(0) + expect(creditAccount.investmentTransactions).toHaveLength(0) + expect(creditAccount.plaidLiability).toMatchObject({}) + + // brokerage + const brokerageAccount = accounts.find( + (a) => a.plaidAccountId === PlaidTestData.brokerageAccount.account_id + )! + expect(brokerageAccount.transactions).toHaveLength(0) + expect(brokerageAccount.holdings).toHaveLength(PlaidTestData.holdings.length) + expect(brokerageAccount.valuations).toHaveLength(0) + expect(brokerageAccount.investmentTransactions).toHaveLength( + PlaidTestData.investmentTransactions.length + ) + }) + + /** + * Example input: Wealthfront account with multiple 529 Savings Plans + * + * Cash accounts have same account and security, but are individual holdings with different qty + * + * * 529 Savings Plan 1 + * * Cash Account 1 + * * Cash Account 2 + * * 529 Savings Plan 2 + * * Cash Account 1 + * * Cash Account 2 + */ + it('syncs Wealthfront account with duplicate cash holdings', async () => { + plaid.accountsGet.mockResolvedValue(PlaidTestData.Wealthfront1.accountsGetResponse) + plaid.transactionsGet.mockResolvedValue(PlaidTestData.Wealthfront1.transactionsGetResponse) + plaid.investmentsTransactionsGet.mockResolvedValue( + PlaidTestData.Wealthfront1.investmentTransactionsGetResponse + ) + plaid.investmentsHoldingsGet.mockResolvedValue( + PlaidTestData.Wealthfront1.holdingsGetResponse + ) + plaid.liabilitiesGet.mockRejectedValue(PlaidTestData.Wealthfront1.liabilitiesGetResponse) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST_PLAID', + type: 'plaid', + plaidAccessToken: 'abcdef', + }, + }) + + await service.sync(connection) + + const { accounts } = await prisma.accountConnection.findUniqueOrThrow({ + where: { + id: connection.id, + }, + include: { + accounts: { + include: { + balances: true, + transactions: true, + holdings: true, + valuations: true, + investmentTransactions: true, + }, + orderBy: { plaidAccountId: 'asc' }, + }, + }, + }) + + expect(accounts).toHaveLength(4) + + const [_529Savings1, _529Savings2, investmentAccount, _529Savings3] = accounts + + // All 529 accounts should have multiple cash holdings + expect(_529Savings1.holdings).toHaveLength(7) + expect(_529Savings2.holdings).toHaveLength(7) + expect(_529Savings3.holdings).toHaveLength(7) + + // 529 accounts have multiple holdings, but all the same security + expect(uniqBy(_529Savings1.holdings, (h) => h.securityId)).toHaveLength(1) + expect(uniqBy(_529Savings2.holdings, (h) => h.securityId)).toHaveLength(1) + expect(uniqBy(_529Savings3.holdings, (h) => h.securityId)).toHaveLength(1) + + // Investment account has no duplicate securities + expect(investmentAccount.holdings).toHaveLength(8) + expect(uniqBy(investmentAccount.holdings, (h) => h.securityId)).toHaveLength(8) + }) + + /** + * This test is for debugging w/ real data locally + */ + it.skip('debug', async () => { + const data = (name: string) => + JSON.parse( + fs.readFileSync(`${process.env.NX_TEST_DATA_FOLDER}/plaid/${name}.json`, 'utf-8') + ) + + plaid.accountsGet.mockResolvedValue(TestUtil.axiosSuccess(data('accounts'))) + plaid.transactionsGet.mockResolvedValue(TestUtil.axiosSuccess(data('transactions'))) + plaid.investmentsTransactionsGet.mockResolvedValue( + TestUtil.axiosSuccess(data('investment-transactions')) + ) + plaid.investmentsHoldingsGet.mockResolvedValue(TestUtil.axiosSuccess(data('holdings'))) + plaid.liabilitiesGet.mockRejectedValue( + TestUtil.axios400Error({ + display_message: null, + documentation_url: 'https://plaid.com/docs/?ref=error#item-errors', + error_code: 'PRODUCTS_NOT_SUPPORTED', + error_message: + 'the following products are not supported by this institution: ["liabilities"]', + error_type: 'ITEM_ERROR', + request_id: 'abc', + suggested_action: null, + }) + ) + + const connection = await prisma.accountConnection.create({ + data: { + userId: user.id, + name: 'TEST[DEBUG]', + type: 'plaid', + plaidAccessToken: 'abcdef', + }, + }) + + await etl(plaidETL, connection) + }) +}) diff --git a/apps/workers/src/app/__tests__/queue.integration.spec.ts b/apps/workers/src/app/__tests__/queue.integration.spec.ts new file mode 100644 index 00000000000..a30bfa6a9b9 --- /dev/null +++ b/apps/workers/src/app/__tests__/queue.integration.spec.ts @@ -0,0 +1,223 @@ +// ===================================================== +// Keep these imports above the rest to avoid errors +// ===================================================== +import type { SharedType } from '@maybe-finance/shared' +import type { AccountsGetResponse, TransactionsGetResponse } from 'plaid' +import type { AccountConnection, User } from '@prisma/client' +import { TestUtil } from '@maybe-finance/shared' +import { PlaidTestData } from '../../../../../tools/test-data' +import { Prisma } from '@prisma/client' +import prisma from '../lib/prisma' +import { default as _plaid } from '../lib/plaid' +import nock from 'nock' +import { DateTime } from 'luxon' +import { resetUser } from './helpers/user.test-helper' + +// Import the workers process +import '../../main' +import { queueService, securityPricingService } from '../lib/di' + +jest.mock('plaid') +jest.mock('launchdarkly-node-server-sdk') + +// For TypeScript support +const plaid = jest.mocked(_plaid) + +let user: User | null +let connection: AccountConnection + +// When debugging, we don't want the tests to time out +if (process.env.IS_VSCODE_DEBUG === 'true') { + jest.setTimeout(100000) +} + +beforeAll(() => { + nock.disableNetConnect() + + nock('https://api.polygon.io') + .get((uri) => uri.includes('v2/aggs/ticker/AAPL/range/1/day')) + .reply(200, PlaidTestData.AAPL) + .persist() + + nock('https://api.polygon.io') + .get((uri) => uri.includes('v2/aggs/ticker/WMT/range/1/day')) + .reply(200, PlaidTestData.WMT) + .persist() + + nock('https://api.polygon.io') + .get((uri) => uri.includes('v2/aggs/ticker/VOO/range/1/day')) + .reply(200, PlaidTestData.VOO) + .persist() +}) + +beforeEach(async () => { + jest.clearAllMocks() + + user = await resetUser(prisma) + + connection = await prisma.accountConnection.create({ + data: { + name: 'Chase Test', + type: 'plaid' as SharedType.AccountConnectionType, + plaidItemId: 'test-plaid-item-workers', + plaidInstitutionId: 'ins_3', + plaidAccessToken: + 'U2FsdGVkX1+WMq9lfTS9Zkbgrn41+XT1hvSK5ain/udRPujzjVCAx/lyPG7EumVZA+nVKXPauGwI+d7GZgtqTA9R3iCZNusU6LFPnmFOCE4=', // need correct encoding here + userId: user.id, + syncStatus: 'PENDING', + }, + }) +}) + +describe('Message queue tests', () => { + it('Creates the correct number of queues', () => { + expect(queueService.allQueues.map((q) => q.name)).toEqual([ + 'sync-user', + 'sync-account', + 'sync-account-connection', + 'sync-security', + 'purge-user', + 'sync-institution', + 'send-email', + ]) + }) + + it('Should handle sync errors', async () => { + const syncQueue = queueService.getQueue('sync-account-connection') + + plaid.accountsGet.mockRejectedValueOnce('forced error for Jest tests') + + await syncQueue.add('sync-connection', { accountConnectionId: connection.id }) + + const updatedConnection = await prisma.accountConnection.findUnique({ + where: { id: connection.id }, + }) + + expect(plaid.accountsGet).toHaveBeenCalledTimes(1) + expect(updatedConnection?.status).toEqual('ERROR') + }) + + it('Should run all sync-account-connection queue jobs', async () => { + const syncQueue = queueService.getQueue('sync-account-connection') + + let cnx = await prisma.accountConnection.findUnique({ where: { id: connection.id } }) + + expect(cnx?.status).toEqual('OK') + expect(cnx?.syncStatus).toEqual('PENDING') + + await syncQueue.add('sync-connection', { accountConnectionId: connection.id }) + + cnx = await prisma.accountConnection.findUnique({ + where: { id: connection.id }, + }) + + expect(cnx?.syncStatus).toEqual('IDLE') + }) + + xit('Should sync connected transaction account', async () => { + const syncQueue = queueService.getQueue('sync-account-connection') + + // Mock will return a basic banking checking account + plaid.accountsGet.mockResolvedValueOnce( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.checkingAccount], + item: PlaidTestData.item, + request_id: 'bkVE1BHWMAZ9Rnr', + }) as any + ) + + plaid.transactionsGet.mockResolvedValueOnce( + TestUtil.axiosSuccess({ + accounts: [PlaidTestData.checkingAccount], + transactions: PlaidTestData.checkingTransactions, + item: PlaidTestData.item, + total_transactions: PlaidTestData.checkingTransactions.length, + request_id: '45QSn', + }) as any + ) + + await syncQueue.add('sync-connection', { accountConnectionId: connection.id }) + + expect(plaid.accountsGet).toHaveBeenCalledTimes(1) + expect(plaid.transactionsGet).toHaveBeenCalledTimes(1) + + const item = await prisma.accountConnection.findUniqueOrThrow({ + where: { id: connection.id }, + include: { + accounts: { + include: { + balances: { + where: PlaidTestData.testDates.prismaWhereFilter, + orderBy: { date: 'asc' }, + }, + transactions: true, + holdings: true, + valuations: true, + investmentTransactions: true, + }, + }, + }, + }) + + expect(item.accounts).toHaveLength(1) + + const [account] = item.accounts + + expect(account.transactions).toHaveLength(PlaidTestData.checkingTransactions.length) + expect(account.balances.map((b) => b.balance)).toEqual( + [ + 3630, + 5125, + 5125, + 5125, + 5125, + 5125, + 5125, + 5125, + 5125, + 5125, + 5115, + 5115, + 5115, + 5089.45, + 5089.45, + PlaidTestData.checkingAccount.balances.current!, + ].map((v) => new Prisma.Decimal(v)) + ) + expect(account.holdings).toHaveLength(0) + expect(account.valuations).toHaveLength(0) + expect(account.investmentTransactions).toHaveLength(0) + }) + + it('Should sync valid security prices', async () => { + const security = await prisma.security.create({ + data: { + name: 'Walmart Inc.', + symbol: 'WMT', + cusip: '93114210310', + pricingLastSyncedAt: new Date(), + }, + }) + + await securityPricingService.sync(security) + + const prices = await prisma.securityPricing.findMany({ + where: { securityId: security.id }, + orderBy: { date: 'asc' }, + }) + + expect(prices).toHaveLength(PlaidTestData.WMT.results.length) + + expect( + prices.map((p) => ({ + date: DateTime.fromJSDate(p.date, { zone: 'utc' }).toISODate(), + price: p.priceClose.toNumber(), + })) + ).toEqual( + PlaidTestData.WMT.results.map((p) => ({ + date: DateTime.fromMillis(p.t, { zone: 'utc' }).toISODate(), + price: p.c, + })) + ) + }) +}) diff --git a/apps/workers/src/app/__tests__/security-sync.integration.spec.ts b/apps/workers/src/app/__tests__/security-sync.integration.spec.ts new file mode 100644 index 00000000000..93b78067188 --- /dev/null +++ b/apps/workers/src/app/__tests__/security-sync.integration.spec.ts @@ -0,0 +1,73 @@ +import { PrismaClient } from '@prisma/client' +import winston from 'winston' +import Redis from 'ioredis' +import nock from 'nock' +import type { ISecurityPricingService } from '@maybe-finance/server/features' +import { SecurityPricingService } from '@maybe-finance/server/features' +import type { IMarketDataService } from '@maybe-finance/server/shared' +import { + RedisCacheBackend, + CacheService, + ServerUtil, + PolygonMarketDataService, +} from '@maybe-finance/server/shared' +import { PolygonTestData } from '../../../../../tools/test-data' + +const prisma = new PrismaClient() + +const redis = new Redis(process.env.NX_REDIS_URL as string, { + retryStrategy: ServerUtil.redisRetryStrategy({ maxAttempts: 1 }), +}) + +beforeAll(() => { + nock.disableNetConnect() + + nock('https://api.polygon.io') + .get((uri) => uri.includes('/v2/snapshot/locale/us/markets/stocks/tickers')) + .reply(200, PolygonTestData.snapshotAllTickers) + .persist() +}) + +afterAll(async () => { + await Promise.allSettled([prisma.$disconnect(), redis.disconnect()]) +}) + +describe('security pricing sync', () => { + let securityPricingService: ISecurityPricingService + + beforeEach(async () => { + const logger = winston.createLogger({ + level: 'debug', + transports: new winston.transports.Console({ format: winston.format.simple() }), + }) + + const cacheService = new CacheService( + logger.child({ service: 'CacheService' }), + new RedisCacheBackend(redis) + ) + + const marketDataService: IMarketDataService = new PolygonMarketDataService( + logger.child({ service: 'PolygonMarketDataService' }), + 'TEST', + cacheService + ) + + securityPricingService = new SecurityPricingService( + logger.child({ service: 'SecurityPricingService' }), + prisma, + marketDataService + ) + + // reset db records + await prisma.security.deleteMany() + await prisma.security.createMany({ + data: [{ symbol: 'AAPL' }, { symbol: 'VOO' }], + }) + }) + + it('syncs', async () => { + // sync 2x to catch any possible caching I/O issues + await securityPricingService.syncAll() + await securityPricingService.syncAll() + }) +}) diff --git a/apps/workers/src/app/lib/auth0.ts b/apps/workers/src/app/lib/auth0.ts new file mode 100644 index 00000000000..1c96ffffa83 --- /dev/null +++ b/apps/workers/src/app/lib/auth0.ts @@ -0,0 +1,18 @@ +import type { SharedType } from '@maybe-finance/shared' +import { ManagementClient } from 'auth0' +import env from '../../env' + +/** + * Management API Documentation + * - https://auth0.com/docs/api/management/v2 + * - https://auth0.github.io/node-auth0/module-management.ManagementClient.html + */ +export const managementClient = new ManagementClient< + SharedType.MaybeAppMetadata, + SharedType.MaybeUserMetadata +>({ + domain: env.NX_AUTH0_DOMAIN, + clientId: env.NX_AUTH0_MGMT_CLIENT_ID, + clientSecret: env.NX_AUTH0_MGMT_CLIENT_SECRET, + scope: 'read:users update:users delete:users', +}) diff --git a/apps/workers/src/app/lib/di.ts b/apps/workers/src/app/lib/di.ts new file mode 100644 index 00000000000..2f0b5c32af4 --- /dev/null +++ b/apps/workers/src/app/lib/di.ts @@ -0,0 +1,290 @@ +import type { + IAccountConnectionProcessor, + IAccountProcessor, + IAccountQueryService, + IAccountService, + ISecurityPricingProcessor, + IInstitutionService, + IUserProcessor, + ISecurityPricingService, + IUserService, + IEmailService, + IEmailProcessor, +} from '@maybe-finance/server/features' +import { + AccountConnectionProcessor, + AccountConnectionProviderFactory, + AccountConnectionService, + AccountProcessor, + AccountProviderFactory, + AccountQueryService, + AccountService, + BalanceSyncStrategyFactory, + FinicityETL, + FinicityService, + InstitutionProviderFactory, + InstitutionService, + InvestmentTransactionBalanceSyncStrategy, + LoanBalanceSyncStrategy, + PlaidETL, + PlaidService, + PropertyService, + SecurityPricingProcessor, + SecurityPricingService, + TransactionBalanceSyncStrategy, + UserProcessor, + UserService, + ValuationBalanceSyncStrategy, + VehicleService, + EmailService, + EmailProcessor, + TransactionService, +} from '@maybe-finance/server/features' +import type { IFeatureFlagService, IMarketDataService } from '@maybe-finance/server/shared' +import { + BullQueueFactory, + CacheService, + CryptoService, + InMemoryQueueFactory, + LaunchDarklyFeatureFlagService, + PgService, + PolygonMarketDataService, + QueueService, + RedisCacheBackend, + ServerUtil, +} from '@maybe-finance/server/shared' +import Redis from 'ioredis' +import logger from './logger' +import prisma from './prisma' +import plaid from './plaid' +import finicity from './finicity' +import ldClient from './ldClient' +import postmark from './postmark' +import { managementClient } from './auth0' +import stripe from './stripe' +import env from '../../env' +import { BullQueueEventHandler, WorkerErrorHandlerService } from '../services' + +// shared services + +const redis = new Redis(env.NX_REDIS_URL, { + retryStrategy: ServerUtil.redisRetryStrategy({ maxAttempts: 5 }), +}) + +export const featureFlagService: IFeatureFlagService = new LaunchDarklyFeatureFlagService(ldClient) +export const cryptoService = new CryptoService(env.NX_DATABASE_SECRET) +export const pgService = new PgService(logger.child({ service: 'PgService' }), env.NX_DATABASE_URL) + +export const queueService = new QueueService( + logger.child({ service: 'QueueService' }), + process.env.NODE_ENV === 'test' + ? new InMemoryQueueFactory() + : new BullQueueFactory( + logger.child({ service: 'BullQueueFactory' }), + env.NX_REDIS_URL, + new BullQueueEventHandler(logger.child({ service: 'BullQueueEventHandler' }), prisma) + ) +) + +const cacheService = new CacheService( + logger.child({ service: 'CacheService' }), + new RedisCacheBackend(redis) +) + +export const marketDataService: IMarketDataService = new PolygonMarketDataService( + logger.child({ service: 'PolygonMarketDataService' }), + env.NX_POLYGON_API_KEY, + cacheService +) + +export const securityPricingService: ISecurityPricingService = new SecurityPricingService( + logger.child({ service: 'SecurityPricingService' }), + prisma, + marketDataService +) + +// providers + +const plaidService = new PlaidService( + logger.child({ service: 'PlaidService' }), + prisma, + plaid, + new PlaidETL( + logger.child({ service: 'PlaidETL' }), + prisma, + plaid, + cryptoService, + marketDataService + ), + cryptoService, + '', + '' +) + +const finicityService = new FinicityService( + logger.child({ service: 'FinicityService' }), + prisma, + finicity, + new FinicityETL(logger.child({ service: 'FinicityETL' }), prisma, finicity), + '', + env.NX_FINICITY_ENV === 'sandbox' +) + +const propertyService = new PropertyService(logger.child({ service: 'PropertyService' })) + +const vehicleService = new VehicleService(logger.child({ service: 'VehicleService' })) + +// account-connection + +const accountConnectionProviderFactory = new AccountConnectionProviderFactory({ + plaid: plaidService, + finicity: finicityService, +}) + +const transactionStrategy = new TransactionBalanceSyncStrategy( + logger.child({ service: 'TransactionBalanceSyncStrategy' }), + prisma +) + +const investmentTransactionStrategy = new InvestmentTransactionBalanceSyncStrategy( + logger.child({ service: 'InvestmentTransactionBalanceSyncStrategy' }), + prisma +) + +const valuationStrategy = new ValuationBalanceSyncStrategy( + logger.child({ service: 'ValuationBalanceSyncStrategy' }), + prisma +) + +const loanStrategy = new LoanBalanceSyncStrategy( + logger.child({ service: 'LoanBalanceSyncStrategy' }), + prisma +) + +const balanceSyncStrategyFactory = new BalanceSyncStrategyFactory({ + INVESTMENT: investmentTransactionStrategy, + DEPOSITORY: transactionStrategy, + CREDIT: transactionStrategy, + LOAN: loanStrategy, + PROPERTY: valuationStrategy, + VEHICLE: valuationStrategy, + OTHER_ASSET: valuationStrategy, + OTHER_LIABILITY: valuationStrategy, +}) + +export const accountConnectionService = new AccountConnectionService( + logger.child({ service: 'AccountConnectionService' }), + prisma, + accountConnectionProviderFactory, + balanceSyncStrategyFactory, + securityPricingService, + queueService.getQueue('sync-account-connection') +) + +const transactionService = new TransactionService( + logger.child({ service: 'TransactionService' }), + prisma +) + +export const accountConnectionProcessor: IAccountConnectionProcessor = + new AccountConnectionProcessor( + logger.child({ service: 'AccountConnectionProcessor' }), + accountConnectionService, + transactionService, + accountConnectionProviderFactory + ) + +// account + +export const accountQueryService: IAccountQueryService = new AccountQueryService( + logger.child({ service: 'AccountQueryService' }), + pgService +) + +export const accountService: IAccountService = new AccountService( + logger.child({ service: 'AccountService' }), + prisma, + accountQueryService, + queueService.getQueue('sync-account'), + queueService.getQueue('sync-account-connection'), + balanceSyncStrategyFactory +) + +const accountProviderFactory = new AccountProviderFactory({ + // Since these are not in use yet, just commenting out for now + // property: propertyService, + // vehicle: vehicleService, +}) + +export const accountProcessor: IAccountProcessor = new AccountProcessor( + logger.child({ service: 'AccountProcessor' }), + accountService, + accountProviderFactory +) + +// user + +export const userService: IUserService = new UserService( + logger.child({ service: 'UserService' }), + prisma, + accountQueryService, + balanceSyncStrategyFactory, + queueService.getQueue('sync-user'), + queueService.getQueue('purge-user'), + managementClient, + stripe +) + +export const userProcessor: IUserProcessor = new UserProcessor( + logger.child({ service: 'UserProcessor' }), + prisma, + userService, + accountService, + accountConnectionService, + accountConnectionProviderFactory +) + +// security-pricing + +export const securityPricingProcessor: ISecurityPricingProcessor = new SecurityPricingProcessor( + logger.child({ service: 'SecurityPricingProcessor' }), + securityPricingService +) + +// institution + +const institutionProviderFactory = new InstitutionProviderFactory({ + PLAID: plaidService, + FINICITY: finicityService, +}) + +export const institutionService: IInstitutionService = new InstitutionService( + logger.child({ service: 'InstitutionService' }), + prisma, + pgService, + institutionProviderFactory +) + +// worker services + +export const workerErrorHandlerService = new WorkerErrorHandlerService( + logger.child({ service: 'WorkerErrorHandlerService' }) +) + +// send-email + +export const emailService: IEmailService = new EmailService( + logger.child({ service: 'EmailService' }), + postmark, + { + from: env.NX_POSTMARK_FROM_ADDRESS, + replyTo: env.NX_POSTMARK_REPLY_TO_ADDRESS, + } +) + +export const emailProcessor: IEmailProcessor = new EmailProcessor( + logger.child({ service: 'EmailProcessor' }), + prisma, + managementClient, + emailService +) diff --git a/apps/workers/src/app/lib/finicity.ts b/apps/workers/src/app/lib/finicity.ts new file mode 100644 index 00000000000..d46f3d01131 --- /dev/null +++ b/apps/workers/src/app/lib/finicity.ts @@ -0,0 +1,10 @@ +import { FinicityApi } from '@maybe-finance/finicity-api' +import env from '../../env' + +const finicity = new FinicityApi( + env.NX_FINICITY_APP_KEY, + env.NX_FINICITY_PARTNER_ID, + env.NX_FINICITY_PARTNER_SECRET +) + +export default finicity diff --git a/apps/workers/src/app/lib/ldClient.ts b/apps/workers/src/app/lib/ldClient.ts new file mode 100644 index 00000000000..80d866c4bad --- /dev/null +++ b/apps/workers/src/app/lib/ldClient.ts @@ -0,0 +1,6 @@ +import { init } from 'launchdarkly-node-server-sdk' +import env from '../../env' + +const ldClient = init(env.NX_LD_SDK_KEY, { offline: process.env.NODE_ENV === 'test' }) + +export default ldClient diff --git a/apps/workers/src/app/lib/logger.ts b/apps/workers/src/app/lib/logger.ts new file mode 100644 index 00000000000..3cc38f7c802 --- /dev/null +++ b/apps/workers/src/app/lib/logger.ts @@ -0,0 +1,23 @@ +import { createLogger } from '@maybe-finance/server/shared' +import ldClient from './ldClient' + +const logger = createLogger({ + level: 'info', +}) + +function setLevel() { + ldClient + .variation('workers-log-level', { key: 'anonymous-server', anonymous: true }, 'info') + .then((level) => { + logger.level = level + logger[level](`Workers logger using level: ${level}`) + }) +} + +// Don't configure for Jest +if (process.env.NODE_ENV !== 'test') { + ldClient.waitForInitialization().then(setLevel) + ldClient.on('update:workers-log-level', setLevel) +} + +export default logger diff --git a/apps/workers/src/app/lib/plaid.ts b/apps/workers/src/app/lib/plaid.ts new file mode 100644 index 00000000000..c13716aed73 --- /dev/null +++ b/apps/workers/src/app/lib/plaid.ts @@ -0,0 +1,17 @@ +import { Configuration, PlaidApi, PlaidEnvironments } from 'plaid' +import env from '../../env' + +// https://plaid.com/docs/api/versioning/#how-to-set-your-api-version +const configuration = new Configuration({ + basePath: PlaidEnvironments[env.NX_PLAID_ENV], + baseOptions: { + headers: { + 'PLAID-CLIENT-ID': env.NX_PLAID_CLIENT_ID, + 'PLAID-SECRET': env.NX_PLAID_SECRET, + }, + }, +}) + +const plaid = new PlaidApi(configuration) + +export default plaid diff --git a/apps/workers/src/app/lib/postmark.ts b/apps/workers/src/app/lib/postmark.ts new file mode 100644 index 00000000000..48eddf1bd7a --- /dev/null +++ b/apps/workers/src/app/lib/postmark.ts @@ -0,0 +1,6 @@ +import { ServerClient } from 'postmark' +import env from '../../env' + +const postmark = new ServerClient(env.NX_POSTMARK_API_TOKEN) + +export default postmark diff --git a/apps/workers/src/app/lib/prisma.ts b/apps/workers/src/app/lib/prisma.ts new file mode 100644 index 00000000000..d65fa91a7ac --- /dev/null +++ b/apps/workers/src/app/lib/prisma.ts @@ -0,0 +1,50 @@ +import { PrismaClient } from '@prisma/client' +import { DbUtil } from '@maybe-finance/server/shared' +import globalLogger from './logger' + +const logger = globalLogger.child({ service: 'PrismaClient' }) + +// https://stackoverflow.com/a/68328402 +declare global { + var prisma: PrismaClient | undefined // eslint-disable-line +} + +function createPrismaClient() { + const prisma = new PrismaClient({ + log: [ + { emit: 'event', level: 'query' }, + { emit: 'event', level: 'info' }, + { emit: 'event', level: 'warn' }, + { emit: 'event', level: 'error' }, + ], + }) + + prisma.$on('query', ({ query, params, duration, ...data }) => { + logger.silly(`Query: ${query}, Params: ${params}, Duration: ${duration}`, { ...data }) + }) + + prisma.$on('info', ({ message, ...data }) => { + logger.info(message, { ...data }) + }) + + prisma.$on('warn', ({ message, ...data }) => { + logger.warn(message, { ...data }) + }) + + prisma.$on('error', ({ message, ...data }) => { + logger.error(message, { ...data }) + }) + + prisma.$use(DbUtil.slowQueryMiddleware(logger)) + + return prisma +} + +// Prevent multiple instances of Prisma Client in development +// https://www.prisma.io/docs/guides/performance-and-optimization/connection-management#prevent-hot-reloading-from-creating-new-instances-of-prismaclient +// https://www.prisma.io/docs/concepts/components/prisma-client/working-with-prismaclient/instantiate-prisma-client#the-number-of-prismaclient-instances-matters +const prisma = global.prisma || createPrismaClient() + +if (process.env.NODE_ENV === 'development') global.prisma = prisma + +export default prisma diff --git a/apps/workers/src/app/lib/s3.ts b/apps/workers/src/app/lib/s3.ts new file mode 100644 index 00000000000..4538b40814d --- /dev/null +++ b/apps/workers/src/app/lib/s3.ts @@ -0,0 +1,15 @@ +import { S3Client } from '@aws-sdk/client-s3' +import { S3Service } from '@maybe-finance/server/shared' +import env from '../../env' + +// https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-environment.html +const s3Client = new S3Client({ + region: 'us-west-2', +}) + +const s3Service = new S3Service(s3Client, { + public: env.NX_CDN_PUBLIC_BUCKET, + private: env.NX_CDN_PRIVATE_BUCKET, +}) + +export default s3Service diff --git a/apps/workers/src/app/lib/stripe.ts b/apps/workers/src/app/lib/stripe.ts new file mode 100644 index 00000000000..d38338e3744 --- /dev/null +++ b/apps/workers/src/app/lib/stripe.ts @@ -0,0 +1,6 @@ +import Stripe from 'stripe' +import env from '../../env' + +const stripe = new Stripe(env.NX_STRIPE_SECRET_KEY, { apiVersion: '2022-08-01' }) + +export default stripe diff --git a/apps/workers/src/app/services/bull-queue-event-handler.ts b/apps/workers/src/app/services/bull-queue-event-handler.ts new file mode 100644 index 00000000000..9f80c8d13f0 --- /dev/null +++ b/apps/workers/src/app/services/bull-queue-event-handler.ts @@ -0,0 +1,117 @@ +import type { PrismaClient, User } from '@prisma/client' +import type { Job } from 'bull' +import type { Logger } from 'winston' +import type { BullQueue, IBullQueueEventHandler } from '@maybe-finance/server/shared' +import * as Sentry from '@sentry/node' +import { ErrorUtil } from '@maybe-finance/server/shared' + +const printJob = (job: Job) => + `Job{queue=${job.queue.name} id=${job.id} name=${job.name} ts=${ + job.timestamp + } data=${JSON.stringify(job.data)}}` + +export class BullQueueEventHandler implements IBullQueueEventHandler { + constructor(private readonly logger: Logger, private readonly prisma: PrismaClient) {} + + onQueueCreated({ queue }: BullQueue) { + // https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#events + queue.on('active', (job, _jobPromise) => { + this.logger.info(`[job.active] ${printJob(job)}`) + }) + + queue.on('completed', (job, _result) => { + this.logger.info(`[job.completed] ${printJob(job)}`) + }) + + queue.on('stalled', async (job) => { + this.logger.warn(`[job.stalled] ${printJob(job)}`) + }) + + queue.on('lock-extension-failed', (job, err) => { + this.logger.warn(`[job.lock-extension-failed] ${printJob(job)}`, { err }) + }) + + queue.on('progress', (job, progress) => { + this.logger.info(`[job.progress] ${printJob(job)}`, { progress }) + }) + + queue.on('failed', async (job, error) => { + this.logger.error(`[job.failed] ${printJob(job)}`, { error }) + + const user = await this.getUserFromJob(job) + + Sentry.withScope((scope) => { + scope.setUser(user ? {} : null) + + scope.setTags({ + 'queue.name': job.queue.name, + 'job.name': job.name, + }) + + scope.setContext('Job Info', { + queue: job.queue.name, + job: job.name, + attempts: job.attemptsMade, + data: job.data, + }) + + const err = ErrorUtil.parseError(error) + + Sentry.captureException(error, { + level: 'error', + tags: err.sentryTags, + contexts: err.sentryContexts, + }) + }) + }) + + queue.on('error', async (error) => { + this.logger.error(`[queue.error]`, { error }) + + const err = ErrorUtil.parseError(error) + + Sentry.captureException(error, { + level: 'error', + tags: err.sentryTags, + contexts: { + ...err.sentryContexts, + queue: { name: queue.name }, + }, + }) + }) + } + + private async getUserFromJob(job: Job) { + let user: Pick | undefined + + try { + if (job.queue.name === 'sync-account' && 'accountId' in job.data) { + const account = await this.prisma.account.findUniqueOrThrow({ + where: { id: job.data.accountId }, + include: { + accountConnection: { include: { user: true } }, + user: true, + }, + }) + + user = account.user ?? account.accountConnection?.user + } + + if (job.queue.name === 'sync-account-connection' && 'accountConnectionId' in job.data) { + const accountConnection = await this.prisma.accountConnection.findUniqueOrThrow({ + where: { id: job.data.accountConnectionId }, + include: { + user: true, + }, + }) + + user = accountConnection.user + } + + return user + } catch (err) { + // Gracefully return if no user identified successfully + return null + } + } +} diff --git a/apps/workers/src/app/services/index.ts b/apps/workers/src/app/services/index.ts new file mode 100644 index 00000000000..e343bcbf01a --- /dev/null +++ b/apps/workers/src/app/services/index.ts @@ -0,0 +1,2 @@ +export * from './worker-error.service' +export * from './bull-queue-event-handler' diff --git a/apps/workers/src/app/services/worker-error.service.ts b/apps/workers/src/app/services/worker-error.service.ts new file mode 100644 index 00000000000..260c4ac1cfa --- /dev/null +++ b/apps/workers/src/app/services/worker-error.service.ts @@ -0,0 +1,28 @@ +import type { Logger } from 'winston' +import * as Sentry from '@sentry/node' +import { ErrorUtil } from '@maybe-finance/server/shared' + +type WorkerErrorContext = { variant: 'unhandled'; error: unknown } + +export class WorkerErrorHandlerService { + constructor(private readonly logger: Logger) {} + + async handleWorkersError(ctx: WorkerErrorContext) { + const err = ErrorUtil.parseError(ctx.error) + + switch (ctx.variant) { + case 'unhandled': + this.logger.error(`[workers-unhandled] ${err.message}`, { error: err.metadata }) + + Sentry.captureException(ctx.error, { + level: 'error', + tags: err.sentryTags, + contexts: err.sentryContexts, + }) + + break + default: + return + } + } +} diff --git a/apps/workers/src/assets/.gitkeep b/apps/workers/src/assets/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/workers/src/env.ts b/apps/workers/src/env.ts new file mode 100644 index 00000000000..8333001d8ee --- /dev/null +++ b/apps/workers/src/env.ts @@ -0,0 +1,50 @@ +import { z } from 'zod' + +const envSchema = z.object({ + NX_PORT: z.string().default('3334'), + + NX_DATABASE_URL: z.string(), + NX_DATABASE_SECRET: z.string(), + + NX_PLAID_ENV: z.string().default('sandbox'), + NX_PLAID_CLIENT_ID: z.string().default('REPLACE_THIS'), + NX_PLAID_SECRET: z.string(), + + NX_FINICITY_APP_KEY: z.string(), + NX_FINICITY_PARTNER_ID: z.string().default('REPLACE_THIS'), + NX_FINICITY_PARTNER_SECRET: z.string(), + NX_FINICITY_ENV: z.string().default('sandbox'), + + NX_SENTRY_DSN: z.string().optional(), + NX_SENTRY_ENV: z.string().optional(), + + NX_LD_SDK_KEY: z.string().default('REPLACE_THIS'), + + NX_REDIS_URL: z.string().default('redis://localhost:6379'), + + NX_POLYGON_API_KEY: z.string().default(''), + + NX_AUTH0_DOMAIN: z.string().default('REPLACE_THIS'), + NX_AUTH0_MGMT_CLIENT_ID: z.string().default('REPLACE_THIS'), + NX_AUTH0_MGMT_CLIENT_SECRET: z.string(), + + NX_POSTMARK_FROM_ADDRESS: z.string().default('account@maybe.co'), + NX_POSTMARK_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'), + NX_POSTMARK_API_TOKEN: z.string().default('REPLACE_THIS'), + NX_STRIPE_SECRET_KEY: z + .string() + .default( + 'sk_test_REPLACE_THIS' + ), + + NX_CDN_PRIVATE_BUCKET: z + .string() + .default('REPLACE_THIS'), + NX_CDN_PUBLIC_BUCKET: z + .string() + .default('REPLACE_THIS'), +}) + +const env = envSchema.parse(process.env) + +export default env diff --git a/apps/workers/src/environments/environment.prod.ts b/apps/workers/src/environments/environment.prod.ts new file mode 100644 index 00000000000..3ec78232fbf --- /dev/null +++ b/apps/workers/src/environments/environment.prod.ts @@ -0,0 +1,3 @@ +export const environment = { + production: true, +} diff --git a/apps/workers/src/environments/environment.ts b/apps/workers/src/environments/environment.ts new file mode 100644 index 00000000000..3ce6715d1f2 --- /dev/null +++ b/apps/workers/src/environments/environment.ts @@ -0,0 +1,3 @@ +export const environment = { + production: false, +} diff --git a/apps/workers/src/main.ts b/apps/workers/src/main.ts new file mode 100644 index 00000000000..df51f56b429 --- /dev/null +++ b/apps/workers/src/main.ts @@ -0,0 +1,210 @@ +import express from 'express' +import cors from 'cors' +import * as Sentry from '@sentry/node' +import * as SentryTracing from '@sentry/tracing' +import { BullQueue } from '@maybe-finance/server/shared' +import logger from './app/lib/logger' +import prisma from './app/lib/prisma' +import { + accountConnectionProcessor, + accountProcessor, + institutionService, + queueService, + securityPricingProcessor, + userProcessor, + emailProcessor, + workerErrorHandlerService, +} from './app/lib/di' +import env from './env' + +// Defaults from quickstart - https://docs.sentry.io/platforms/node/ +Sentry.init({ + dsn: env.NX_SENTRY_DSN, + environment: env.NX_SENTRY_ENV, + maxValueLength: 8196, + integrations: [ + new Sentry.Integrations.Http({ tracing: true }), + new SentryTracing.Integrations.Postgres(), + new SentryTracing.Integrations.Prisma({ client: prisma }), + ], + tracesSampleRate: 1.0, +}) + +const syncUserQueue = queueService.getQueue('sync-user') +const syncConnectionQueue = queueService.getQueue('sync-account-connection') +const syncAccountQueue = queueService.getQueue('sync-account') +const syncSecurityQueue = queueService.getQueue('sync-security') +const purgeUserQueue = queueService.getQueue('purge-user') +const syncInstitutionQueue = queueService.getQueue('sync-institution') +const sendEmailQueue = queueService.getQueue('send-email') + +syncUserQueue.process( + 'sync-user', + async (job) => { + await userProcessor.sync(job.data) + }, + { concurrency: 4 } +) + +syncAccountQueue.process( + 'sync-account', + async (job) => { + await accountProcessor.sync(job.data) + }, + { concurrency: 4 } +) + +/** + * sync-account-connection queue + */ +syncConnectionQueue.process( + 'sync-connection', + async (job) => { + await accountConnectionProcessor.sync(job.data, async (progress) => { + try { + await job.progress(progress) + } catch (e) { + logger.warn('Failed to update SYNC_CONNECTION job progress', job.data) + } + }) + }, + { concurrency: 4 } +) + +/** + * sync-security queue + */ +syncSecurityQueue.process( + 'sync-all-securities', + async () => await securityPricingProcessor.syncAll() +) + +/** + * purge-user queue + */ +purgeUserQueue.process( + 'purge-user', + async (job) => { + await userProcessor.delete(job.data) + }, + { concurrency: 4 } +) + +/** + * sync-all-securities queue + */ +// Start repeated job for syncing securities (Bull won't duplicate it as long as the repeat options are the same) +syncSecurityQueue.add( + 'sync-all-securities', + {}, + { + repeat: { cron: '*/5 * * * *' }, // Run every 5 minutes + } +) + +/** + * sync-institution queue + */ +syncInstitutionQueue.process( + 'sync-plaid-institutions', + async () => await institutionService.sync('PLAID') +) + +syncInstitutionQueue.process( + 'sync-finicity-institutions', + async () => await institutionService.sync('FINICITY') +) + +syncInstitutionQueue.add( + 'sync-plaid-institutions', + {}, + { + repeat: { cron: '0 */24 * * *' }, // Run every 24 hours + } +) + +syncInstitutionQueue.add( + 'sync-finicity-institutions', + {}, + { + repeat: { cron: '0 */24 * * *' }, // Run every 24 hours + } +) + +/** + * send-email queue + */ +sendEmailQueue.process('send-email', async (job) => await emailProcessor.send(job.data)) + +sendEmailQueue.add( + 'send-email', + { type: 'trial-reminders' }, + { repeat: { cron: '0 */12 * * *' } } // Run every 12 hours +) + +sendEmailQueue.add( + 'send-email', + { type: 'conversation-expirations' }, + { repeat: { cron: '0 */12 * * *' } } // Run every 12 hours +) + +// Fallback - usually triggered by errors not handled (or thrown) within the Bull event handlers (see above) +process.on( + 'uncaughtException', + async (error) => + await workerErrorHandlerService.handleWorkersError({ variant: 'unhandled', error }) +) + +// Fallback - usually triggered by errors not handled (or thrown) within the Bull event handlers (see above) +process.on( + 'unhandledRejection', + async (error) => + await workerErrorHandlerService.handleWorkersError({ variant: 'unhandled', error }) +) + +const app = express() + +app.use(cors()) + +// Make sure that at least 1 of the queues is ready and Redis is connected properly +app.get('/health', (_req, res, _next) => { + syncConnectionQueue + .isHealthy() + .then((isHealthy) => { + if (isHealthy) { + res.status(200).json({ success: true, message: 'Queue is healthy' }) + } else { + res.status(500).json({ success: false, message: 'Queue is not healthy' }) + } + }) + .catch((err) => { + console.log(err) + res.status(500).json({ success: false, message: 'Queue health check failed' }) + }) +}) + +const server = app.listen(env.NX_PORT, () => { + logger.info(`Worker health server started on port ${env.NX_PORT}`) +}) + +function onShutdown() { + logger.info('[shutdown.start]') + + server.close() + + // shutdown queues + Promise.allSettled( + queueService.allQueues + .filter((q): q is BullQueue => q instanceof BullQueue) + .map((q) => q.queue.close()) + ).finally(() => { + logger.info('[shutdown.complete]') + process.exit() + }) +} + +process.on('SIGINT', onShutdown) +process.on('SIGTERM', onShutdown) +process.on('exit', (code) => logger.info(`[exit] code=${code}`)) + +logger.info(`🚀 worker started`) diff --git a/apps/workers/tsconfig.app.json b/apps/workers/tsconfig.app.json new file mode 100644 index 00000000000..b1fa50db043 --- /dev/null +++ b/apps/workers/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.test-helper.ts", "jest.config.ts"], + "include": ["**/*.ts", "../../custom-express.d.ts"] +} diff --git a/apps/workers/tsconfig.json b/apps/workers/tsconfig.json new file mode 100644 index 00000000000..3f672a82fce --- /dev/null +++ b/apps/workers/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + "noImplicitAny": false, + "strict": true, + "strictNullChecks": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/apps/workers/tsconfig.spec.json b/apps/workers/tsconfig.spec.json new file mode 100644 index 00000000000..274b70baff3 --- /dev/null +++ b/apps/workers/tsconfig.spec.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.d.ts", + "**/*.test-helper.ts", + "jest.config.ts" + ] +} diff --git a/auth0/README.md b/auth0/README.md new file mode 100644 index 00000000000..6c0692b6cb8 --- /dev/null +++ b/auth0/README.md @@ -0,0 +1,101 @@ +## Quick Start + +### Setting up env locally + +``` +AUTH0_ENV=development +AUTH0_DEPLOY_CLIENT_SECRET= +POSTMARK_SMTP_PASS= +``` + +- `AUTH0_ENV` - This is either `development`, `staging`, or `production`. This should **always** be `development` when working locally. +- `AUTH0_DEPLOY_CLIENT_SECRET` - The secret for the `auth0-deploy-cli-extension` application in Auth0 dashboard +- `POSTMARK_SMTP_PASS` - Go to Postmark => Servers => "Mail Server" => Message Streams => "Default Transactional Message Stream" => Settings + +You will need to install the Auth0 Client to test templates (you might need to change the commands depending on your platform): + +```bash +# Linux example +wget -c https://github.com/auth0/auth0-cli/releases/download/v0.11.2/auth0-cli_0.11.2_Linux_x86_64.tar.gz -O - | sudo tar -xz -C /usr/local/bin/ +``` + +### How deployments work + +Per the [Auth0 docs](https://github.com/auth0/auth0-deploy-cli/tree/master/examples/directory), this repository uses Github Actions to define each Auth0 tenant configuration. + +Maybe has 3 tenants: + +1. `maybe-finance-development` +2. `maybe-finance-staging` +3. `maybe-finance-production` + +On each push to a branch with `auth0` in it (e.g. `someuser/pr-title-auth0`), the configuration in `tenant.yaml` will be deployed to the **staging** tenant. + +On each push to `main`, the configuration in `tenant.yaml` will be deployed to the **production** tenant. + +These rules are defined in `.github/workflows/deploy-auth0-staging.yml` and `.github/workflows/deploy-auth0-prod.yml` respectively. + +## Editing and Testing + +### `tenant.yaml` + +The `tenant.yaml` file will accept any options present in the [Auth0 Management API](https://auth0.com/docs/api/management/v2). + +[Here is a sample `tenant.yaml` file](https://github.com/auth0/auth0-deploy-cli/blob/master/examples/yaml/tenant.yaml). + +For example, you can define tenant-wide settings using the Management API [tenant endpoint](https://auth0.com/docs/api/management/v2#!/Tenants/tenant_settings_route) (abbreviated): + +```json +# Abbreviated Management API V2 tenant endpoint GET response +{ + "flags": { + "revoke_refresh_token_grant": false, + ... + }, + "friendly_name": "My Company", + "picture_url": "https://mycompany.org/logo.png", + "support_email": "support@mycompany.org", + ... +} +``` + +```yaml +# tenant.yaml + +tenant: + flags: + revoke_refresh_token_grant: false + friendly_name: Maybe Finance + picture_url: https://assets.maybe.co/images/maybe.svg + support_email: hello@maybe.co +``` + +### Testing custom templates + +Testing custom templates (`/auth0/emailTemplates` and `/auth0/pages`) happens in 3 steps: + +1. Run `live-server` with `yarn auth0:edit`. You can make HTML/CSS changes in this view +2. To deploy to the dev tenant, run `yarn auth0:deploy` (make sure your `.env` is setup per instructions at top of this README) +3. To test the new deployment, run `yarn auth0:test` + +Unfortunately, you will have to deploy **every time** you make changes to properly test since Auth0 does not have many developer tools. + +References: + +- Auth0 client reference - https://github.com/auth0/auth0.js/tree/master/example +- Auth0 developer tool docs - https://auth0.github.io/auth0-cli/ +- Relevant Auth0 docs - https://auth0.com/docs/brand-and-customize/universal-login-page-templates#using-the-auth0-cli- + +### Password Reset + +Of special note is the `/auth0/pages/password_reset.html` page. Auth0 currently does not have an API for password resets, but an Auth0 employee created an +[open source example](https://github.com/auth0/auth0-custom-password-reset-hosted-page) of how to tap into the login page endpoints to customize it. If this page ever breaks (due to changes in internal Auth0 API) we can easily revert back to Universal PW reset in `tenant.yaml`: + +```yaml +emailTemplates: + - template: reset_email + body: ./emailTemplates/reset_email.html + enabled: false # CHANGE THIS +``` + +Setting this to false will revert to the default Auth0 password reset widget (not Maybe branded, but fully functional) diff --git a/auth0/config.js b/auth0/config.js new file mode 100644 index 00000000000..0d9821f065e --- /dev/null +++ b/auth0/config.js @@ -0,0 +1,96 @@ +require('dotenv').config() +const path = require('path') +const cli = require('auth0-deploy-cli') +const env = require('./env') + +// CI is always set to true in Github Actions environment +if (process.env.ENV === 'production' && !process.env.CI) { + throw new Error('Cannot deploy to production outside of CI/CD workflow!') +} + +let AUTH0_DOMAIN +let AUTH0_CUSTOM_DOMAIN +let AUTH0_CLIENT_ID +let CLIENT_BASE_URLS +let SERVER_BASE_URLS +let ADVISOR_BASE_URLS +let ADMIN_ROLE_ID +let BETA_TESTER_ROLE_ID + +const trustedOrigins = ['https://*.maybe.co', 'https://*.vercel.app'] +const logoutOrigins = [...trustedOrigins] + +switch (env.AUTH0_ENV) { + case 'development': + AUTH0_DOMAIN = 'REPLACE_THIS' + AUTH0_CUSTOM_DOMAIN = AUTH0_DOMAIN + AUTH0_CLIENT_ID = 'REPLACE_THIS' + // 8484 is for the local auth0-client testing + CLIENT_BASE_URLS = [ + 'http://localhost:4200', + 'http://localhost:8484', + 'https://localhost.maybe.co', + ] + CLIENT_LOGOUT_URLS = [...logoutOrigins, 'http://localhost:4200'] + SERVER_BASE_URLS = ['http://localhost:3333'] + ADVISOR_BASE_URLS = ['http://localhost:4201'] + ADMIN_ROLE_ID = 'REPLACE_THIS' + BETA_TESTER_ROLE_ID = 'REPLACE_THIS' + break + case 'staging': + AUTH0_DOMAIN = 'REPLACE_THIS' + AUTH0_CUSTOM_DOMAIN = AUTH0_DOMAIN + AUTH0_CLIENT_ID = 'REPLACE_THIS' + CLIENT_BASE_URLS = ['https://staging-app.maybe.co', ...trustedOrigins] + CLIENT_LOGOUT_URLS = logoutOrigins + SERVER_BASE_URLS = ['https://staging-api.maybe.co'] + ADVISOR_BASE_URLS = ['https://staging-advisor.maybe.co', ...trustedOrigins] + ADMIN_ROLE_ID = 'REPLACE_THIS' + BETA_TESTER_ROLE_ID = 'REPLACE_THIS' + break + case 'production': + AUTH0_DOMAIN = 'REPLACE_THIS' + AUTH0_CUSTOM_DOMAIN = 'login.maybe.co' + AUTH0_CLIENT_ID = 'REPLACE_THIS' + CLIENT_BASE_URLS = ['https://app.maybe.co', ...trustedOrigins] + CLIENT_LOGOUT_URLS = logoutOrigins + SERVER_BASE_URLS = ['https://api.maybe.co'] + ADVISOR_BASE_URLS = ['https://advisor.maybe.co', ...trustedOrigins] + ADMIN_ROLE_ID = 'REPLACE_THIS' + BETA_TESTER_ROLE_ID = 'REPLACE_THIS' + break + default: + throw new Error("Invalid environment: should be 'development' | 'staging' | 'production'") +} + +// https://auth0.com/docs/deploy/deploy-cli-tool/import-export-tenant-configuration-to-yaml-file#example-configuration-file +module.exports = { + config: { + AUTH0_DOMAIN: AUTH0_CUSTOM_DOMAIN, + AUTH0_CLIENT_ID, + AUTH0_CLIENT_SECRET: env.AUTH0_DEPLOY_CLIENT_SECRET, + + /* If something exists in the tenant, but NOT the tenant.yaml file, the resource in the + tenant will NOT be deleted (hence, `false`) - keeping this set to false as a safeguard */ + AUTH0_ALLOW_DELETE: false, + + // https://auth0.com/docs/deploy/deploy-cli-tool/environment-variables-and-keyword-mappings + AUTH0_KEYWORD_REPLACE_MAPPINGS: { + // While the JWT is issued from login.maybe.co in production, the management API still must use the default auth0.com domain + AUTH0_DOMAIN, + CLIENT_BASE_URLS, + CLIENT_LOGOUT_URLS, + SERVER_BASE_URLS, + SERVER_CALLBACK_URLS: SERVER_BASE_URLS.map((url) => `${url}/admin/callback`), + ADVISOR_BASE_URLS, + ADVISOR_CALLBACK_URLS: ADVISOR_BASE_URLS.map((url) => `${url}/api/auth/callback`), + POSTMARK_SMTP_PASS: env.POSTMARK_SMTP_PASS, + ADMIN_ROLE_ID: ADMIN_ROLE_ID, + BETA_TESTER_ROLE_ID: BETA_TESTER_ROLE_ID, + APPLE_SIGN_IN_SECRET_KEY: env.APPLE_SIGN_IN_SECRET_KEY, + }, + }, + input_file: path.join(__dirname, 'tenant.yaml'), + sync: cli.export, + deploy: cli.deploy, +} diff --git a/auth0/deploy.js b/auth0/deploy.js new file mode 100644 index 00000000000..e5ab1a47cee --- /dev/null +++ b/auth0/deploy.js @@ -0,0 +1,15 @@ +const { config, deploy, input_file } = require('./config') + +deploy({ + config: { + ...config, + + // The deploy client only works with the DEFAULT Auth0 domain, NOT with custom domains + AUTH0_DOMAIN: config.AUTH0_KEYWORD_REPLACE_MAPPINGS.AUTH0_DOMAIN, + }, + input_file, +}) + .then(() => + console.log(`Deployed ${config.AUTH0_KEYWORD_REPLACE_MAPPINGS.AUTH0_DOMAIN} successfully!`) + ) + .catch((err) => console.log(`Deploy failed: ${err}`)) diff --git a/auth0/emailTemplates/reset_email.html b/auth0/emailTemplates/reset_email.html new file mode 100644 index 00000000000..c94b83195df --- /dev/null +++ b/auth0/emailTemplates/reset_email.html @@ -0,0 +1,432 @@ + + + + + + + + + Password Reset + + + + + + + + Use this link to reset your password. The link is only valid for 24 hours. + + + + + + + diff --git a/auth0/emailTemplates/verify_email.html b/auth0/emailTemplates/verify_email.html new file mode 100644 index 00000000000..55126c23aae --- /dev/null +++ b/auth0/emailTemplates/verify_email.html @@ -0,0 +1,427 @@ + + + + + + + + + Verify Email + + + + + + + + Verify your account and start using Maybe today! + + + + + + + diff --git a/auth0/env.js b/auth0/env.js new file mode 100644 index 00000000000..e23a4665d55 --- /dev/null +++ b/auth0/env.js @@ -0,0 +1,12 @@ +const z = require('zod') + +const envSchema = z.object({ + AUTH0_DEPLOY_CLIENT_SECRET: z.string(), + AUTH0_ENV: z.string().default('development'), + POSTMARK_SMTP_PASS: z.string(), + APPLE_SIGN_IN_SECRET_KEY: z.string(), +}) + +const env = envSchema.parse(process.env) + +module.exports = env diff --git a/auth0/pages/guardian_multifactor.html b/auth0/pages/guardian_multifactor.html new file mode 100644 index 00000000000..f9f56b018c9 --- /dev/null +++ b/auth0/pages/guardian_multifactor.html @@ -0,0 +1,95 @@ + + + + + + MFA Auth Maybe Finance + + + + + + + +
+
+
+ +
+
+
+
+ + + + + + diff --git a/auth0/pages/login.html b/auth0/pages/login.html new file mode 100644 index 00000000000..a4b3232c788 --- /dev/null +++ b/auth0/pages/login.html @@ -0,0 +1,685 @@ + + + + + + Maybe Login + + + + + + + + + + + + + + + + + + + + +
+ + diff --git a/auth0/pages/password_reset.html b/auth0/pages/password_reset.html new file mode 100644 index 00000000000..fe32958e271 --- /dev/null +++ b/auth0/pages/password_reset.html @@ -0,0 +1,427 @@ + + + + + + + + + + + + + +
+ + + + + Something went wrong. Please try again. +
+
+ + + + + Your password was reset. +
+
+
+
+ + +
+ +
+
+ + + + + +
+ + + + +
+
+ + + + +
+ + + Back to login +
+
+
+
+ + + + + diff --git a/auth0/rules/assignRolesOnLogin.js b/auth0/rules/assignRolesOnLogin.js new file mode 100644 index 00000000000..60dd0bfde7a --- /dev/null +++ b/auth0/rules/assignRolesOnLogin.js @@ -0,0 +1,45 @@ +function assignRolesOnLogin(user, context, callback) { + // This rule does not apply to unverified users - never assign a privileged role without verification! + if (!user.email || !user.email_verified) { + return callback(null, user, context); + } + + const maybeEmailDomain = 'maybe.co'; + const emailSplit = user.email.split('@'); + const isMaybeEmployee = emailSplit[emailSplit.length - 1].toLowerCase() === maybeEmailDomain; + + if (!isMaybeEmployee) { + return callback(null, user, context); + } + + // Use latest version that is allowed here - https://auth0-extensions.github.io/canirequire/#auth0 + const ManagementClient = require('auth0@2.35.0').ManagementClient; + + const cli = new ManagementClient({ + token: auth0.accessToken, + domain: auth0.domain, + }); + + const admins = ['REPLACE_THIS']; + + const rolesToAssign = []; + + // https://auth0.com/docs/rules/configuration#use-the-configuration-object + if (admins.includes(user.email)) { + rolesToAssign.push(configuration.ADMIN_ROLE_ID); + } + + // https://auth0.com/docs/rules/configuration#use-the-configuration-object + if (isMaybeEmployee) { + rolesToAssign.push(configuration.BETA_TESTER_ROLE_ID); + } + + // If we make it here, we know the user has verified their email and their email is in the Maybe Finance Gmail domain + cli.assignRolestoUser({ id: user.user_id }, { roles: rolesToAssign }, function (err) { + if (err) { + console.log(err); + } + + return callback(null, user, context); + }); +} diff --git a/auth0/rules/enhanceIdToken.js b/auth0/rules/enhanceIdToken.js new file mode 100644 index 00000000000..098f7d9f3d3 --- /dev/null +++ b/auth0/rules/enhanceIdToken.js @@ -0,0 +1,53 @@ +function enhanceIdToken(user, context, callback) { + // Does not have to be a valid URL, just has to be unique and start with http / https + const namespace = 'https://maybe.co'; + + const assignedRoles = (context.authorization || {}).roles; + + let idTokenClaims = context.idToken || {}; + let accessTokenClaims = context.accessToken || {}; + + let identityClaim; + + if (user.identities && user.identities.length) { + const primaryIdentities = user.identities.filter((identity) => { + // https://auth0.com/docs/manage-users/user-accounts/user-account-linking#how-it-works + const isSecondary = 'profileData' in identity; + + return !isSecondary; + }); + + if (primaryIdentities.length === 0) { + identityClaim = undefined; + } + + // Based on prior checks, this should represent the primary identity + const primaryIdentity = primaryIdentities[0]; + + identityClaim = { + connection: primaryIdentity.connection, + provider: primaryIdentity.provider, + isSocial: primaryIdentity.isSocial, + }; + } + + // Access token claims are populated on the parsed server-side JWT + accessTokenClaims[`${namespace}/name`] = user.name; + accessTokenClaims[`${namespace}/email`] = user.email; + accessTokenClaims[`${namespace}/picture`] = user.picture; + accessTokenClaims[`${namespace}/roles`] = assignedRoles; + accessTokenClaims[`${namespace}/user-metadata`] = user.user_metadata; + accessTokenClaims[`${namespace}/app-metadata`] = user.app_metadata; + accessTokenClaims[`${namespace}/primary-identity`] = identityClaim; + + // ID token claims are populated in the parsed client-side React hook + idTokenClaims[`${namespace}/roles`] = assignedRoles; + idTokenClaims[`${namespace}/user-metadata`] = user.user_metadata; + idTokenClaims[`${namespace}/app-metadata`] = user.app_metadata; + idTokenClaims[`${namespace}/primary-identity`] = identityClaim; + + context.idToken = idTokenClaims; + context.accessToken = accessTokenClaims; + + return callback(null, user, context); +} diff --git a/auth0/rules/mfaAuth.js b/auth0/rules/mfaAuth.js new file mode 100644 index 00000000000..4c00d9fe86f --- /dev/null +++ b/auth0/rules/mfaAuth.js @@ -0,0 +1,23 @@ +// https://auth0.com/docs/secure/multi-factor-authentication/customize-mfa +function mfaAuth(user, context, callback) { + const ENABLED_CLIENT_IDS = [ + 'REPLACE_THIS', + ]; + + // Only enable MFA on the Next.js app (client IDs above) + if (ENABLED_CLIENT_IDS.indexOf(context.clientID) !== -1) { + // This makes MFA optional for users (they can enroll via their profile within the app) + if (user.user_metadata && user.user_metadata.enrolled_mfa) { + context.multifactor = { + // See options here - https://auth0.com/docs/secure/multi-factor-authentication/customize-mfa#use-rules + // `any` is the generic option (i.e. Google Authenticator or something similar) + provider: 'any', + + // If set to true, MFA will turn off for 30 days for the user's current browser + allowRememberBrowser: true, + }; + } + } + + return callback(null, user, context); +} diff --git a/auth0/rules/updateUserMetadata.js b/auth0/rules/updateUserMetadata.js new file mode 100644 index 00000000000..c031c4e3a28 --- /dev/null +++ b/auth0/rules/updateUserMetadata.js @@ -0,0 +1,27 @@ +function updateUserMetadata(user, context, callback) { + // Use latest version that is allowed here (2.4.0 as of today) - https://auth0-extensions.github.io/canirequire/#auth0 + const ManagementClient = require('auth0@2.35.0').ManagementClient; + + const cli = new ManagementClient({ + token: auth0.accessToken, + domain: auth0.domain, + }); + + const metadata = { + firstName: + (user.user_metadata && user.user_metadata.firstName) || + user.first_name || + user.given_name || + '', + lastName: + (user.user_metadata && user.user_metadata.lastName) || + user.last_name || + user.family_name || + '', + }; + + // Maps data from various identity providers to a normalized identity + cli.updateUserMetadata({ id: user.user_id }, metadata, function (err, updatedUser) { + return callback(null, updatedUser, context); + }); +} diff --git a/auth0/rules/verifyUserOnPasswordReset.js b/auth0/rules/verifyUserOnPasswordReset.js new file mode 100644 index 00000000000..44f067a9ced --- /dev/null +++ b/auth0/rules/verifyUserOnPasswordReset.js @@ -0,0 +1,34 @@ +// If the user successfully performs a password reset, we *know* their email is valid, so go ahead and verify it if not already verified +function verifyUserWithPasswordReset(user, context, callback) { + const request = require('request'); + const userApiUrl = auth0.baseUrl + '/users/'; + + // This rule is only for Auth0 databases + if (context.connectionStrategy !== 'auth0') { + return callback(null, user, context); + } + + if (user.email_verified || !user.last_password_reset) { + return callback(null, user, context); + } + + // Set email verified if a user has already updated his/her password + request.patch( + { + url: userApiUrl + user.user_id, + headers: { + Authorization: 'Bearer ' + auth0.accessToken, + }, + json: { email_verified: true }, + timeout: 5000, + }, + function (err, response, body) { + // Setting email verified isn't propagated to id_token in this + // authentication cycle so explicitly set it to true given no errors. + context.idToken.email_verified = !err && response.statusCode === 200; + + // Return with success at this point. + return callback(null, user, context); + } + ); +} diff --git a/auth0/sync.js b/auth0/sync.js new file mode 100644 index 00000000000..49736cfad12 --- /dev/null +++ b/auth0/sync.js @@ -0,0 +1,26 @@ +const { config, sync } = require('./config') + +const firstArg = process.argv.slice(2)[0] + +if (firstArg === '--force') { + sync({ + config: { + ...config, + + // The deploy client only works with the DEFAULT Auth0 domain, NOT with custom domains + AUTH0_DOMAIN: config.AUTH0_KEYWORD_REPLACE_MAPPINGS.AUTH0_DOMAIN, + }, + format: 'yaml', + output_folder: __dirname, + }) + .then(() => + console.log( + `Synced ${config.AUTH0_KEYWORD_REPLACE_MAPPINGS.AUTH0_DOMAIN} successfully!` + ) + ) + .catch((err) => console.log(`Sync failed: ${err}`)) +} else { + console.log( + 'Syncing not recommended (see README.md). Instead, you should make changes locally and deploy to the tenant. \n\nIf you are sure you want to do this, run: \n\n`node sync --force`\n' + ) +} diff --git a/auth0/tenant.yaml b/auth0/tenant.yaml new file mode 100644 index 00000000000..7f332be2ddb --- /dev/null +++ b/auth0/tenant.yaml @@ -0,0 +1,405 @@ +rules: + - name: mfaAuth + script: ./rules/mfaAuth.js + stage: login_success + enabled: true + order: 1 + - name: verifyUserOnPasswordReset + script: ./rules/verifyUserOnPasswordReset.js + stage: login_success + enabled: true + order: 2 + - name: assignRolesOnLogin + script: ./rules/assignRolesOnLogin.js + stage: login_success + enabled: true + order: 3 + - name: updateUserMetadata + script: ./rules/updateUserMetadata.js + stage: login_success + enabled: true + order: 4 + - name: enhanceIdToken + script: ./rules/enhanceIdToken.js + stage: login_success + enabled: true + order: 5 + + +rulesConfigs: + - key: "ADMIN_ROLE_ID" + value: '##ADMIN_ROLE_ID##' + - key: "BETA_TESTER_ROLE_ID" + value: '##BETA_TESTER_ROLE_ID##' + +hooks: [] + +pages: + - name: guardian_multifactor + enabled: false + html: ./pages/guardian_multifactor.html + - name: login + enabled: true + html: ./pages/login.html + - name: password_reset + enabled: true + html: ./pages/password_reset.html + +resourceServers: + - name: maybe-finance-api + identifier: https://maybe-finance-api/v1 + allow_offline_access: true + signing_alg: RS256 + skip_consent_for_verifiable_first_party_clients: true + token_lifetime: 86400 # 24 hours + token_lifetime_for_web: 7200 + +clients: + - name: Maybe + app_type: spa + allowed_clients: [] + callbacks: @@CLIENT_BASE_URLS@@ + allowed_logout_urls: @@CLIENT_LOGOUT_URLS@@ + allowed_origins: @@CLIENT_BASE_URLS@@ + web_origins: @@CLIENT_BASE_URLS@@ + client_aliases: [] + cross_origin_auth: false + custom_login_page_on: true + grant_types: + - authorization_code + - implicit + - refresh_token + - password + - http://auth0.com/oauth/grant-type/password-realm + is_first_party: true + is_token_endpoint_ip_header_trusted: false + jwt_configuration: + alg: RS256 + lifetime_in_seconds: 36000 + secret_encoded: false + native_social_login: + apple: + enabled: false + facebook: + enabled: false + oidc_conformant: true + refresh_token: + expiration_type: expiring + leeway: 0 + token_lifetime: 2592000 # 30 days + idle_token_lifetime: 1296000 # 15 days + infinite_token_lifetime: false + infinite_idle_token_lifetime: false + rotation_type: rotating + sso_disabled: false + token_endpoint_auth_method: none + + - name: Maybe Admin + allowed_clients: [] + app_type: regular_web + callbacks: @@SERVER_CALLBACK_URLS@@ + allowed_logout_urls: @@SERVER_BASE_URLS@@ + allowed_origins: @@SERVER_BASE_URLS@@ + web_origins: @@SERVER_BASE_URLS@@ + client_aliases: [] + cross_origin_auth: false + custom_login_page_on: true + grant_types: + - authorization_code + - implicit + - refresh_token + - client_credentials + - password + is_first_party: true + is_token_endpoint_ip_header_trusted: false + jwt_configuration: + alg: RS256 + lifetime_in_seconds: 36000 + secret_encoded: false + native_social_login: + apple: + enabled: false + facebook: + enabled: false + oidc_conformant: true + refresh_token: + expiration_type: non-expiring + leeway: 0 + infinite_token_lifetime: true + infinite_idle_token_lifetime: true + token_lifetime: 31557600 + idle_token_lifetime: 2592000 + rotation_type: non-rotating + sso_disabled: false + token_endpoint_auth_method: client_secret_post + + - name: Maybe Advisor + allowed_clients: [] + app_type: regular_web + callbacks: @@ADVISOR_CALLBACK_URLS@@ + allowed_logout_urls: @@ADVISOR_BASE_URLS@@ + allowed_origins: @@ADVISOR_BASE_URLS@@ + web_origins: @@ADVISOR_BASE_URLS@@ + client_aliases: [] + cross_origin_auth: false + custom_login_page_on: true + grant_types: + - authorization_code + - implicit + - refresh_token + - client_credentials + - password + is_first_party: true + is_token_endpoint_ip_header_trusted: false + jwt_configuration: + alg: RS256 + lifetime_in_seconds: 36000 + secret_encoded: false + native_social_login: + apple: + enabled: false + facebook: + enabled: false + oidc_conformant: true + refresh_token: + expiration_type: non-expiring + leeway: 0 + infinite_token_lifetime: true + infinite_idle_token_lifetime: true + token_lifetime: 31557600 + idle_token_lifetime: 2592000 + rotation_type: non-rotating + sso_disabled: false + token_endpoint_auth_method: client_secret_post + + - name: auth0-deploy-cli-extension + app_type: non_interactive + cross_origin_auth: false + custom_login_page_on: true + grant_types: + - client_credentials + is_first_party: true + is_token_endpoint_ip_header_trusted: false + jwt_configuration: + alg: RS256 + lifetime_in_seconds: 36000 + secret_encoded: false + oidc_conformant: true + refresh_token: + expiration_type: non-expiring + leeway: 0 + infinite_token_lifetime: true + infinite_idle_token_lifetime: true + token_lifetime: 31557600 + idle_token_lifetime: 2592000 + rotation_type: non-rotating + sso_disabled: false + token_endpoint_auth_method: client_secret_post + + - name: server-m2m-cli + allowed_clients: [] + allowed_origins: [] + app_type: non_interactive + callbacks: [] + client_aliases: [] + cross_origin_auth: false + custom_login_page_on: true + grant_types: + - client_credentials + is_first_party: true + is_token_endpoint_ip_header_trusted: false + jwt_configuration: + alg: RS256 + lifetime_in_seconds: 36000 + secret_encoded: false + native_social_login: + apple: + enabled: false + facebook: + enabled: false + oidc_conformant: true + refresh_token: + expiration_type: non-expiring + leeway: 0 + infinite_token_lifetime: true + infinite_idle_token_lifetime: true + token_lifetime: 31557600 + idle_token_lifetime: 2592000 + rotation_type: non-rotating + sso_disabled: false + token_endpoint_auth_method: client_secret_post + +databases: + - name: Username-Password-Authentication + strategy: auth0 + enabled_clients: + - auth0-deploy-cli-extension + - Maybe + - Maybe Admin + - Maybe Advisor + is_domain_connection: false + options: + mfa: + active: true + return_enroll_settings: true + passwordPolicy: good + strategy_version: 2 + brute_force_protection: true + realms: + - Username-Password-Authentication + +connections: + - name: google-oauth2 + strategy: google-oauth2 + enabled_clients: + - auth0-deploy-cli-extension + - Maybe + - Maybe Admin + - Maybe Advisor + - server-m2m-cli + is_domain_connection: false + options: + email: true + scope: + - email + - profile + profile: true + - name: apple + strategy: apple + enabled_clients: + - auth0-deploy-cli-extension + - Maybe + - Maybe Admin + - Maybe Advisor + - server-m2m-cli + is_domain_connection: false + options: + client_id: co.maybe.webapp + app_secret: @@APPLE_SIGN_IN_SECRET_KEY@@ + team_id: 8TQ4KDWF2S + kid: 47WD7B27JK + email: true + name: true + scope: + - email + - name + +tenant: + enabled_locales: + - en + # https://auth0.com/docs/authorization/flows/call-your-api-using-resource-owner-password-flow#configure-tenant + default_directory: Username-Password-Authentication + default_audience: https://maybe-finance-api/v1 + flags: + new_universal_login_experience_enabled: true + universal_login: true + revoke_refresh_token_grant: false + disable_clickjack_protection_headers: false + friendly_name: Maybe Finance + picture_url: >- + https://assets.maybe.co/images/maybe.svg + universal_login: + colors: + page_background: '#242629' + primary: '#3bc9db' + support_email: hello@maybe.co + +emailProvider: + name: smtp + credentials: + smtp_host: smtp.postmarkapp.com + smtp_port: 587 + smtp_user: REPLACE_THIS + smtp_pass: '##POSTMARK_SMTP_PASS##' + default_from_address: account@maybe.co + enabled: true + +emailTemplates: + - template: reset_email + body: ./emailTemplates/reset_email.html + enabled: true + from: reset@maybe.co + subject: Reset your password + syntax: liquid + urlLifetimeInSeconds: 86400 + - template: verify_email + body: ./emailTemplates/verify_email.html + enabled: true + from: 'account@maybe.co' + resultUrl: '{{ application.callback_domain }}' + subject: 'Verify your email for Maybe Finance' + syntax: liquid + urlLifetimeInSeconds: 432000 + +clientGrants: + - client_id: server-m2m-cli + audience: https://##AUTH0_DOMAIN##/api/v2/ + scope: + - read:users + - update:users + - delete:users + - read:users_app_metadata + - update:users_app_metadata + - delete:users_app_metadata + - create:users_app_metadata + +guardianFactors: + - name: duo + enabled: false + - name: email + enabled: true + - name: otp + enabled: true + - name: push-notification + enabled: false + - name: recovery-code + enabled: true + - name: sms + enabled: false + - name: webauthn-platform + enabled: false + - name: webauthn-roaming + enabled: false + +guardianFactorProviders: [] + +guardianFactorTemplates: [] + +guardianPolicies: + policies: [] + +guardianPhoneFactorSelectedProvider: + provider: auth0 + +guardianPhoneFactorMessageTypes: + message_types: [] + +roles: + - name: Admin + description: Maybe Employee Admin + permissions: [] + - name: CIUser + description: Identifies our CI users for e2e testing + permissions: [] + - name: Advisor + description: Verified financial advisors that can login to ATA dashboard and respond to user questions + permissions: [] + +branding: + colors: + page_background: '#242629' + primary: '#3bc9db' + logo_url: >- + https://assets.maybe.co/images/maybe.svg + +prompts: + universal_login_experience: new + identifier_first: false + +migrations: {} + +actions: [] + +triggers: {} + +organizations: [] diff --git a/aws/maybe-app/.gitignore b/aws/maybe-app/.gitignore new file mode 100644 index 00000000000..f60797b6a91 --- /dev/null +++ b/aws/maybe-app/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/maybe-app/.npmignore b/aws/maybe-app/.npmignore new file mode 100644 index 00000000000..c1d6d45dcf3 --- /dev/null +++ b/aws/maybe-app/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/maybe-app/README.md b/aws/maybe-app/README.md new file mode 100644 index 00000000000..1fdc9af6fab --- /dev/null +++ b/aws/maybe-app/README.md @@ -0,0 +1,19 @@ +## Local deployment + +**PROD should not be deployed locally** + +To test a local deployment: + +1. Set `~/.aws/credentials` file to have a profile called `[maybe_tools]` which should have credentials of IAM user in the tools account (which can cross-deploy to staging/prod accounts). Additional security creds can be generated in AWS dashboard. + +``` +[maybe_tools] +aws_access_key_id= +aws_secret_access_key= +region=us-east-1 +output=json +``` + +2. Run `yarn cdk:ls` to verify everything is working correctly + +3. Run `yarn cdk deploy [SomeStack] --profile=maybe_tools` to deploy a certain stack in the **staging** environment. Production should not be deployed locally ever. diff --git a/aws/maybe-app/bin/maybe-app.ts b/aws/maybe-app/bin/maybe-app.ts new file mode 100644 index 00000000000..aaa38e9ab53 --- /dev/null +++ b/aws/maybe-app/bin/maybe-app.ts @@ -0,0 +1,93 @@ +#!/usr/bin/env node +import 'source-map-support/register' +import * as cdk from 'aws-cdk-lib' +import { getContext } from '../lib/utils/get-context' +import { SharedStack } from '../lib/stacks/shared-stack' +import { ServerStack } from '../lib/stacks/server-stack' +import { WorkersStack } from '../lib/stacks/workers-stack' +import { StackProps } from 'aws-cdk-lib' +import { ToolsStack } from '../lib/stacks/tools-stack' + +const app = new cdk.App() + +const ctx = getContext(app) + +const { AWS_ACCOUNT, DEFAULT_AWS_REGION, ENV_NAME, sharedEnv, stackContexts } = ctx + +const commonStackProps: StackProps = { + env: { + region: DEFAULT_AWS_REGION, + account: AWS_ACCOUNT, + }, + tags: { + environment: ENV_NAME, + 'deploy-type': 'cdk', + }, +} + +if (ENV_NAME === 'tools') { + new ToolsStack( + app, + 'ToolsStack', + { + ...commonStackProps, + stackName: `ci-cd-deployments-tools-stack`, + description: 'Resources used for CI/CD, such as self-hosted runners and pipelines', + }, + { + DEFAULT_AWS_REGION, + sharedEnv, + ...stackContexts.Tools, + } + ) +} + +if (ENV_NAME === 'staging' || ENV_NAME === 'production') { + const sharedStack = new SharedStack( + app, + 'SharedStack', + { + ...commonStackProps, + stackName: `maybe-app-${ENV_NAME}-shared-stack`, + description: + 'Common infrastructure used by all services including VPC, Redis, and ECS Cluster', + }, + { + DEFAULT_AWS_REGION, + sharedEnv, + ...stackContexts.Shared, + } + ) + + new ServerStack( + app, + 'ServerStack', + { + ...commonStackProps, + stackName: `maybe-app-${ENV_NAME}-server-stack`, + description: 'ECS Fargate RESTful API with ALB, Cloudfront, and WAF', + sharedStack, + }, + { + DEFAULT_AWS_REGION, + sharedEnv, + ...stackContexts.Server, + } + ) + + new WorkersStack( + app, + 'WorkersStack', + { + ...commonStackProps, + stackName: `maybe-app-${ENV_NAME}-workers-stack`, + description: 'Bull.js workers on ECS Fargate', + sharedStack, + }, + { + DEFAULT_AWS_REGION, + sharedEnv, + ...stackContexts.Workers, + } + ) +} diff --git a/aws/maybe-app/cdk.context.json b/aws/maybe-app/cdk.context.json new file mode 100644 index 00000000000..f62e1d6117a --- /dev/null +++ b/aws/maybe-app/cdk.context.json @@ -0,0 +1,20 @@ +{ + "availability-zones:account=180027004049:region=us-west-2": [ + "us-west-2a", + "us-west-2b", + "us-west-2c", + "us-west-2d" + ], + "availability-zones:account=541001830411:region=us-west-2": [ + "us-west-2a", + "us-west-2b", + "us-west-2c", + "us-west-2d" + ], + "availability-zones:account=604977304163:region=us-west-2": [ + "us-west-2a", + "us-west-2b", + "us-west-2c", + "us-west-2d" + ] +} diff --git a/aws/maybe-app/cdk.json b/aws/maybe-app/cdk.json new file mode 100644 index 00000000000..e60c895dabe --- /dev/null +++ b/aws/maybe-app/cdk.json @@ -0,0 +1,214 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/maybe-app.ts", + "watch": { + "include": ["**"], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, + "@aws-cdk/core:stackRelativeExports": true, + "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, + "@aws-cdk/aws-lambda:recognizeVersionProps": true, + "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:target-partitions": ["aws", "aws-cn"], + "environments": { + "tools": { + "ENV_NAME": "tools", + "AWS_ACCOUNT": "REPLACE_THIS", + "DEFAULT_AWS_REGION": "us-west-2", + "sharedEnv": {}, + "stackContexts": { + "Tools": { + "VPC": { + "IPAllowList": [ + "" + ] + }, + "GithubRunner": { + "SSMKeyArn": "REPLACE_THIS", + "GithubTokenArn": "REPLACE_THIS" + } + } + } + }, + "staging": { + "ENV_NAME": "staging", + "AWS_ACCOUNT": "REPLACE_THIS", + "DEFAULT_AWS_REGION": "us-west-2", + "sharedEnv": { + "NX_API_URL": "https://staging-api.maybe.co", + "NX_CDN_URL": "https://staging-cdn.maybe.co", + "NODE_ENV": "production", + "NX_AUTH0_AUDIENCE": "https://maybe-finance-api/v1", + "NX_PLAID_CLIENT_ID": "REPLACE_THIS", + "NX_PLAID_ENV": "sandbox", + "NX_FINICITY_PARTNER_ID": "REPLACE_THIS", + "NX_FINICITY_ENV": "sandbox", + "NX_CLIENT_URL": "https://staging-app.maybe.co", + "NX_CLIENT_URL_CUSTOM": "https://staging-app.maybe.co", + "NX_ADVISOR_URL": "https://staging-advisor.maybe.co", + "NX_AUTH0_DOMAIN": "REPLACE_THIS", + "NX_AUTH0_CUSTOM_DOMAIN": "REPLACE_THIS", + "NX_AUTH0_CLIENT_ID": "REPLACE_THIS", + "NX_AUTH0_MGMT_CLIENT_ID": "REPLACE_THIS", + "NX_SENTRY_ENV": "staging", + "NX_POSTMARK_FROM_ADDRESS": "account@maybe.co" + }, + "stackContexts": { + "Shared": { + "PeeringCnxId": "REPLACE_THIS", + "PeeringCnxCidr": "10.1.0.0/16", + "VPC": { + "NATCount": 1, + "IPAllowList": [ + "" + ] + }, + "Redis": { + "size": "cache.t4g.small", + "count": 1 + }, + "Cloudfront": { + "CertificateArn": "REPLACE_THIS", + "CNAMES": ["staging-cdn.maybe.co"] + }, + "UploadOrigins": [ + "http://localhost:4200", + "http://localhost:4201", + "https://staging-app.maybe.co" + ] + }, + "Server": { + "Container": { + "Env": { + "NX_PORT": 3333, + "NX_CORS_ORIGINS": "maybe.co,vercel.app", + "NX_MORGAN_LOG_LEVEL": "combined", + "NX_SENTRY_DSN": "REPLACE_THIS" + } + }, + "CertificateArn": "REPLACE_THIS", + "DesiredTaskCount": 1, + "WAFArn": "REPLACE_THIS", + "ComputeCapacity": { + "cpu": 512, + "memory": "1024" + }, + "Cloudfront": { + "CNAMES": ["staging-api.maybe.co"], + "CertificateArn": "REPLACE_THIS" + } + }, + "Workers": { + "Container": { + "Env": { + "NX_PORT": 3334, + "NX_SENTRY_DSN": "REPLACE_THIS" + } + }, + "DesiredTaskCount": 1, + "ComputeCapacity": { + "cpu": 512, + "memory": "1024" + } + } + } + }, + "production": { + "ENV_NAME": "production", + "AWS_ACCOUNT": "REPLACE_THIS", + "DEFAULT_AWS_REGION": "us-west-2", + "sharedEnv": { + "NODE_ENV": "production", + "NX_AUTH0_AUDIENCE": "https://maybe-finance-api/v1", + "NX_PLAID_CLIENT_ID": "REPLACE_THIS", + "NX_PLAID_ENV": "production", + "NX_FINICITY_PARTNER_ID": "REPLACE_THIS", + "NX_FINICITY_ENV": "production", + "NX_API_URL": "https://api.maybe.co", + "NX_CDN_URL": "https://cdn.maybe.co", + "NX_CLIENT_URL": "https://app.maybe.co", + "NX_CLIENT_URL_CUSTOM": "https://app.maybe.co", + "NX_ADVISOR_URL": "https://advisor.maybe.co", + "NX_AUTH0_DOMAIN": "REPLACE_THIS", + "NX_AUTH0_CUSTOM_DOMAIN": "login.maybe.co", + "NX_AUTH0_CLIENT_ID": "REPLACE_THIS", + "NX_AUTH0_MGMT_CLIENT_ID": "REPLACE_THIS", + "NX_SENTRY_ENV": "production", + "NX_POSTMARK_FROM_ADDRESS": "account@maybe.co", + "NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID": "REPLACE_THIS", + "NX_STRIPE_PREMIUM_YEARLY_PRICE_ID": "REPLACE_THIS" + }, + "stackContexts": { + "Shared": { + "PeeringCnxId": "REPLACE_THIS", + "PeeringCnxCidr": "10.2.0.0/16", + "VPC": { + "NATCount": 2, + "IPAllowList": [ + "" + ] + }, + "Redis": { + "size": "cache.m6g.2xlarge", + "count": 1 + }, + "Cloudfront": { + "CertificateArn": "REPLACE_THIS", + "CNAMES": ["cdn.maybe.co"] + }, + "UploadOrigins": ["https://advisor.maybe.co", "https://app.maybe.co"] + }, + "Server": { + "Container": { + "Env": { + "NX_PORT": 3333, + "NX_CORS_ORIGINS": "maybe.co", + "NX_MORGAN_LOG_LEVEL": "combined", + "NX_SENTRY_DSN": "REPLACE_THIS" + } + }, + "CertificateArn": "REPLACE_THIS", + "DesiredTaskCount": 1, + "WAFArn": "REPLACE_THIS", + "ComputeCapacity": { + "cpu": 4096, + "memory": "8192" + }, + "Cloudfront": { + "CNAMES": ["api.maybe.co"], + "CertificateArn": "REPLACE_THIS" + } + }, + "Workers": { + "Container": { + "Env": { + "NX_PORT": 3334, + "NX_SENTRY_DSN": "REPLACE_THIS" + } + }, + "DesiredTaskCount": 1, + "ComputeCapacity": { + "cpu": 4096, + "memory": "8192" + } + } + } + } + } + } +} diff --git a/aws/maybe-app/jest.config.js b/aws/maybe-app/jest.config.js new file mode 100644 index 00000000000..129eba16273 --- /dev/null +++ b/aws/maybe-app/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, +} diff --git a/aws/maybe-app/lib/constructs/github-actions-runner/configure-github-runner.sh b/aws/maybe-app/lib/constructs/github-actions-runner/configure-github-runner.sh new file mode 100755 index 00000000000..22e27470fe7 --- /dev/null +++ b/aws/maybe-app/lib/constructs/github-actions-runner/configure-github-runner.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +# Go to home directory +cd /home/ubuntu + +# Install necessary packages +apt-get update +apt-get install unzip jq -y +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +unzip awscliv2.zip +./aws/install +export PATH="$(which aws):$PATH" +source ~/.bashrc +aws --version + +# Initializes the Github runner +export RUNNER_ALLOW_RUNASROOT="1" # This allows the root user to set everything up +mkdir actions-runner && cd actions-runner +curl -o actions-runner-linux-x64-2.291.1.tar.gz -L https://github.com/actions/runner/releases/download/v2.291.1/actions-runner-linux-x64-2.291.1.tar.gz +echo "1bde3f2baf514adda5f8cf2ce531edd2f6be52ed84b9b6733bf43006d36dcd4c actions-runner-linux-x64-2.291.1.tar.gz" | shasum -a 256 -c +tar xzf ./actions-runner-linux-x64-2.291.1.tar.gz + +# Grabs Github API key from Secure SSM param +# Make sure there is a secure parameter called '/github/api-token' in format githubusername:githubpersonalaccesstoken (with "repo" scopes enabled) +GITHUB_API_TOKEN=$(aws ssm get-parameter --name /github/api-token --region us-west-2 --with-decryption | jq -r '.Parameter.Value') + +# Uses token to get a runner registration token, which is used to configure the runner +ADD_RUNNER_TOKEN=$(curl -u $GITHUB_API_TOKEN \ + -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/maybe-finance/maybe-app/actions/runners/registration-token | jq -r '.token') + +./config.sh --url https://github.com/maybe-finance/maybe-app --labels aws --unattended --token $ADD_RUNNER_TOKEN + +./svc.sh install + +# Install Node v.16.15 and yarn +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" + +nvm install v16.15.0 +nvm use v16.15.0 +nvm alias default v16.15.0 +npm install -g yarn + +# Need to add these binaries to the runner's `.path` file, which is what the runner uses to locate packages +# https://github.com/actions/setup-node/issues/182#issuecomment-718233039 +echo $PATH > .path +echo -n ":/home/ubuntu/.nvm/versions/node/v16.15.0/bin" >> .path + +# Install Docker +apt-get update +apt-get install \ + ca-certificates \ + curl \ + gnupg \ + lsb-release -y +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null +apt-get update +apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y + +# https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user +# Give non-root user ability to run docker commands +usermod -aG docker ubuntu +newgrp docker + +# Start the Github runner as a service +sudo ./svc.sh start diff --git a/aws/maybe-app/lib/constructs/github-actions-runner/github-actions-runner.ts b/aws/maybe-app/lib/constructs/github-actions-runner/github-actions-runner.ts new file mode 100644 index 00000000000..dd9e016b862 --- /dev/null +++ b/aws/maybe-app/lib/constructs/github-actions-runner/github-actions-runner.ts @@ -0,0 +1,104 @@ +import { + BlockDeviceVolume, + Instance, + InstanceClass, + InstanceSize, + InstanceType, + IVpc, + MachineImage, + Peer, + Port, + SecurityGroup, + SubnetType, +} from 'aws-cdk-lib/aws-ec2' +import { + ManagedPolicy, + PolicyDocument, + PolicyStatement, + Role, + ServicePrincipal, +} from 'aws-cdk-lib/aws-iam' +import { Construct } from 'constructs' +import { ToolsStackContext } from '../../utils/get-context' +import * as path from 'path' +import { readFileSync } from 'fs' + +export interface GithubActionsRunnerProps { + vpc: IVpc +} + +/** + * Registers an on-demand EC2 instance for running GH Actions workflows + */ +export class GithubActionsRunner extends Construct { + constructor( + scope: Construct, + id: string, + props: GithubActionsRunnerProps, + ctx: ToolsStackContext + ) { + super(scope, id) + + const runnerSG = new SecurityGroup(this, 'RunnerSecurityGroup', { + vpc: props.vpc, + }) + + ctx.VPC.IPAllowList.forEach((ip, index) => { + runnerSG.addIngressRule(Peer.ipv4(`${ip}/32`), Port.tcp(22)) + }) + + // Details retrieved from console, see command below: + // aws ec2 describe-images --region us-west-2 --image-ids ami-0cfa91bdbc3be780c + const ami = { + id: 'ami-0cfa91bdbc3be780c', + deviceName: '/dev/sda1', + } + + const runner = new Instance(this, 'GithubActionsRunner', { + vpc: props.vpc, + vpcSubnets: { subnetType: SubnetType.PUBLIC }, + machineImage: MachineImage.genericLinux({ + [ctx.DEFAULT_AWS_REGION]: ami.id, + }), + instanceType: InstanceType.of(InstanceClass.T3A, InstanceSize.MEDIUM), + securityGroup: runnerSG, + // This role allows for Session Manager connections - https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html + role: new Role(this, 'SessionManagerRole', { + assumedBy: new ServicePrincipal('ec2.amazonaws.com'), + description: 'Allows Session Manager to connect to EC2 instance', + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), + ], + inlinePolicies: { + // Allows EC2 instance to grab the Github PAT and make a call to get a "create runner" registration token from Github API + ReadGithubKey: new PolicyDocument({ + statements: [ + new PolicyStatement({ + actions: ['kms:Decrypt'], + resources: [ctx.GithubRunner.SSMKeyArn], + }), + new PolicyStatement({ + actions: ['ssm:GetParameters'], + resources: [ctx.GithubRunner.GithubTokenArn], + }), + ], + }), + }, + }), + blockDevices: [ + { + deviceName: ami.deviceName, + volume: BlockDeviceVolume.ebs(100), + }, + ], + userDataCausesReplacement: true, + }) + + runner.addUserData( + readFileSync(path.join(__dirname, './configure-github-runner.sh'), 'utf8') + ) + + // Associate the keypair for SSH connections - https://stackoverflow.com/a/60713522/7437737 + runner.instance.addPropertyOverride('KeyName', 'gh-actions-runner-key') // key was manually created in Console, available in 1Password + } +} diff --git a/aws/maybe-app/lib/constructs/jump-box.ts b/aws/maybe-app/lib/constructs/jump-box.ts new file mode 100644 index 00000000000..9e6ea537e1f --- /dev/null +++ b/aws/maybe-app/lib/constructs/jump-box.ts @@ -0,0 +1,60 @@ +import { + Instance, + InstanceClass, + InstanceSize, + InstanceType, + IVpc, + MachineImage, + Peer, + Port, + SecurityGroup, + SubnetType, +} from 'aws-cdk-lib/aws-ec2' +import { ManagedPolicy, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam' +import { Construct } from 'constructs' +import { SharedStackContext } from '../utils/get-context' + +export interface JumpBoxProps { + vpc: IVpc +} + +/** + * Creates a "Jump Box", or "Bastion" EC2 instance in public subnet for connections to Postgres + */ +export class JumpBox extends Construct { + public readonly redisUrl: string + + constructor(scope: Construct, id: string, props: JumpBoxProps, ctx: SharedStackContext) { + super(scope, id) + + const jumpBoxSecurityGroup = new SecurityGroup(this, 'JumpBoxSecurityGroup', { + vpc: props.vpc, + }) + + // Allow SSH connections to specified IP addresses + ctx.VPC.IPAllowList.forEach((ip, index) => { + jumpBoxSecurityGroup.addIngressRule(Peer.ipv4(`${ip}/32`), Port.tcp(22)) + }) + + const jumpBox = new Instance(this, 'JumpBox', { + vpc: props.vpc, + vpcSubnets: { subnetType: SubnetType.PUBLIC }, + machineImage: MachineImage.genericLinux({ + [ctx.DEFAULT_AWS_REGION]: 'ami-0cfa91bdbc3be780c', + }), + instanceType: InstanceType.of(InstanceClass.T2, InstanceSize.MICRO), + securityGroup: jumpBoxSecurityGroup, + // This role allows for Session Manager connections - https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager.html + role: new Role(this, 'SessionManagerRole', { + assumedBy: new ServicePrincipal('ec2.amazonaws.com'), + description: 'Allows Session Manager to connect to EC2 instance', + managedPolicies: [ + ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'), + ], + }), + }) + + // Associate the keypair for SSH connections - https://stackoverflow.com/a/60713522/7437737 + jumpBox.instance.addPropertyOverride('KeyName', 'jumpbox-key') // key was manually created in Console, available in 1Password + } +} diff --git a/aws/maybe-app/lib/constructs/network-acl.ts b/aws/maybe-app/lib/constructs/network-acl.ts new file mode 100644 index 00000000000..5deb6c4cfb4 --- /dev/null +++ b/aws/maybe-app/lib/constructs/network-acl.ts @@ -0,0 +1,130 @@ +import { + AclCidr, + AclTraffic, + Action, + IVpc, + NetworkAcl, + SubnetType, + TrafficDirection, +} from 'aws-cdk-lib/aws-ec2' +import { Construct } from 'constructs' + +export interface NetworkACLConfigProps { + vpc: IVpc + authorizedIPs?: string[] +} + +/** + * Creates the "best practice" network ACL config for a public/private subnet VPC (some rules are more lenient than the docs suggest) + * + * @see https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html#nacl-rules-scenario-2 + */ +export class NetworkACLConfig extends Construct { + public readonly redisUrl: string + + constructor(scope: Construct, id: string, props: NetworkACLConfigProps) { + super(scope, id) + + const publicNetworkACL = new NetworkAcl(this, 'PublicNetworkACL', { + vpc: props.vpc, + subnetSelection: { subnetType: SubnetType.PUBLIC }, + }) + + // -------------------------------- + // Inbound Public Entries + // -------------------------------- + publicNetworkACL.addEntry('InboundHTTPPublic', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 101, + traffic: AclTraffic.tcpPort(80), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + + publicNetworkACL.addEntry('InboundHTTPSPublic', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 110, + traffic: AclTraffic.tcpPort(443), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + + // Individual IP address inbound access (20 reserved slots) + // http://checkip.amazonaws.com/ + if (props.authorizedIPs?.length) { + props.authorizedIPs.forEach((ip, index) => { + publicNetworkACL.addEntry(`IPAllowPublic${index}`, { + cidr: AclCidr.ipv4(`${ip}/32`), + ruleNumber: 111 + index, + traffic: AclTraffic.tcpPort(22), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + }) + } + + publicNetworkACL.addEntry('EphemeralInboundPublic', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 140, + traffic: AclTraffic.tcpPortRange(1024, 65535), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + + // -------------------------------- + // Outbound Public Entries + // -------------------------------- + + publicNetworkACL.addEntry('AllowAllOutbound', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 100, + traffic: AclTraffic.allTraffic(), + direction: TrafficDirection.EGRESS, + ruleAction: Action.ALLOW, + }) + + if (props.vpc.privateSubnets.length) { + const privateNetworkACL = new NetworkAcl(this, 'PrivateNetworkACL', { + vpc: props.vpc, + subnetSelection: { subnetType: SubnetType.PRIVATE_WITH_EGRESS }, + }) + + // -------------------------------- + // Inbound Private Entries + // -------------------------------- + + // Allow all inbound traffic from public subnets + props.vpc + .selectSubnets({ subnetType: SubnetType.PUBLIC }) + .subnets.forEach((sn, index) => { + privateNetworkACL.addEntry(`InboundPrivate${index}`, { + cidr: AclCidr.ipv4(sn.ipv4CidrBlock), + ruleNumber: 101 + index, + traffic: AclTraffic.allTraffic(), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + }) + + privateNetworkACL.addEntry('InboundEphemeralPrivate', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 110, + traffic: AclTraffic.tcpPortRange(1024, 65535), + direction: TrafficDirection.INGRESS, + ruleAction: Action.ALLOW, + }) + + // -------------------------------- + // Outbound Private Entries + // -------------------------------- + + privateNetworkACL.addEntry('AllowAllEgressPrivate', { + cidr: AclCidr.anyIpv4(), + ruleNumber: 100, + traffic: AclTraffic.allTraffic(), + direction: TrafficDirection.EGRESS, + ruleAction: Action.ALLOW, + }) + } + } +} diff --git a/aws/maybe-app/lib/constructs/redis.ts b/aws/maybe-app/lib/constructs/redis.ts new file mode 100644 index 00000000000..66fa0f2fb78 --- /dev/null +++ b/aws/maybe-app/lib/constructs/redis.ts @@ -0,0 +1,73 @@ +import { IVpc, Peer, Port, SecurityGroup, SubnetType } from 'aws-cdk-lib/aws-ec2' +import { CfnCacheCluster, CfnCacheClusterProps, CfnSubnetGroup } from 'aws-cdk-lib/aws-elasticache' +import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs' +import { Construct } from 'constructs' + +export interface RedisProps { + vpc: IVpc + redisConfig: Pick +} + +export class Redis extends Construct { + public readonly redisUrl: string + + constructor(scope: Construct, id: string, props: RedisProps) { + super(scope, id) + + const redisSecurityGroup = new SecurityGroup(this, 'RedisSecurityGroup', { + vpc: props.vpc, + description: 'Redis instance security group', + allowAllOutbound: true, + }) + + const privateSubnets = props.vpc.selectSubnets({ + subnetType: SubnetType.PRIVATE_WITH_EGRESS, + }) + + const redisSubnetGroup = new CfnSubnetGroup(this, 'RedisSubnetGroup', { + description: 'Subnet group for Redis cluster', + subnetIds: privateSubnets.subnetIds, + cacheSubnetGroupName: 'redis-subnet', + }) + + const REDIS_PORT = 6379 + + const { logGroupName } = new LogGroup(this, 'RedisLogGroup', { + retention: RetentionDays.TWO_WEEKS, + }) + + const redisCluster = new CfnCacheCluster(this, 'RedisCluster', { + engine: 'redis', + cacheNodeType: props.redisConfig.cacheNodeType, + numCacheNodes: props.redisConfig.numCacheNodes, + autoMinorVersionUpgrade: true, + port: REDIS_PORT, + vpcSecurityGroupIds: [redisSecurityGroup.securityGroupId], + cacheSubnetGroupName: 'redis-subnet', + logDeliveryConfigurations: [ + { + destinationDetails: { cloudWatchLogsDetails: { logGroup: logGroupName } }, + destinationType: 'cloudwatch-logs', + logFormat: 'json', + logType: 'slow-log', + }, + { + destinationDetails: { + cloudWatchLogsDetails: { logGroup: logGroupName }, + }, + destinationType: 'cloudwatch-logs', + logFormat: 'json', + logType: 'engine-log', + }, + ], + }) + + // Require the subnet group to be created before the cluster + redisCluster.node.addDependency(redisSubnetGroup) + + // Open incoming traffic to the Redis port (e.g. 6379) + redisSecurityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(REDIS_PORT)) + + this.redisUrl = `redis://${redisCluster.attrRedisEndpointAddress}:${redisCluster.attrRedisEndpointPort}` + } +} diff --git a/aws/maybe-app/lib/stacks/server-stack.ts b/aws/maybe-app/lib/stacks/server-stack.ts new file mode 100644 index 00000000000..8a8a1bc6dc1 --- /dev/null +++ b/aws/maybe-app/lib/stacks/server-stack.ts @@ -0,0 +1,331 @@ +import { Duration, Stack, StackProps, Tags } from 'aws-cdk-lib' + +import { NetworkMode, Platform } from 'aws-cdk-lib/aws-ecr-assets' +import { + ContainerImage, + FargateService, + FargateTaskDefinition, + LogDriver, + PropagatedTagSource, + Secret as ECSSecret, +} from 'aws-cdk-lib/aws-ecs' +import { + ApplicationLoadBalancer, + ApplicationProtocol, + ApplicationProtocolVersion, + IApplicationLoadBalancer, + ListenerAction, + SslPolicy, +} from 'aws-cdk-lib/aws-elasticloadbalancingv2' +import { Certificate } from 'aws-cdk-lib/aws-certificatemanager' +import { StringParameter } from 'aws-cdk-lib/aws-ssm' +import { Construct } from 'constructs' +import { ServerStackContext } from '../utils/get-context' +import { + AllowedMethods, + CachePolicy, + Distribution, + OriginProtocolPolicy, + OriginRequestPolicy, + OriginSslPolicy, + PriceClass, + ViewerProtocolPolicy, +} from 'aws-cdk-lib/aws-cloudfront' +import { LoadBalancerV2Origin } from 'aws-cdk-lib/aws-cloudfront-origins' +import { Secret } from 'aws-cdk-lib/aws-secretsmanager' +import { SharedStack } from './shared-stack' +import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam' + +interface ServerStackProps extends StackProps { + sharedStack: SharedStack +} + +export class ServerStack extends Stack { + public readonly loadBalancer: IApplicationLoadBalancer + + constructor(scope: Construct, id: string, props: ServerStackProps, ctx: ServerStackContext) { + super(scope, id, props) + + const { + redisUrl, + userAccessKeyId, + userAccessSecretArn, + publicBucketName, + privateBucketName, + vpc, + cluster, + signerPubKeyId, + signerSecretId, + } = props.sharedStack + + const taskDefinition = new FargateTaskDefinition(this, 'ServerTaskDefinition', { + memoryLimitMiB: +ctx.ComputeCapacity.memory, + cpu: ctx.ComputeCapacity.cpu, + }) + + // CDK does not add these permissions by default - https://github.com/aws/aws-cdk/issues/17156 + // https://docs.aws.amazon.com/AmazonECS/latest/userguide/specifying-sensitive-data-secrets.html#secrets-iam + taskDefinition.addToExecutionRolePolicy( + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['secretsmanager:GetSecretValue'], + resources: [userAccessSecretArn], + }) + ) + + taskDefinition.addContainer('ServerContainer', { + logging: LogDriver.awsLogs({ streamPrefix: 'server-container' }), + image: ContainerImage.fromAsset('../../', { + file: 'apps/server/Dockerfile', + networkMode: NetworkMode.HOST, + target: 'prod', + platform: Platform.LINUX_AMD64, // explicitly define so can build on Macbook M1 (https://stackoverflow.com/a/71102144) + }), + environment: { + AWS_ACCESS_KEY_ID: userAccessKeyId, + NX_REDIS_URL: redisUrl, + NX_CDN_PUBLIC_BUCKET: publicBucketName, + NX_CDN_PRIVATE_BUCKET: privateBucketName, + NX_CDN_SIGNER_SECRET_ID: signerSecretId, + NX_CDN_SIGNER_PUBKEY_ID: signerPubKeyId, + ...ctx.sharedEnv, + ...ctx.Container.Env, + }, + portMappings: [{ containerPort: +ctx.Container.Env.NX_PORT }], + entryPoint: ['sh', '-c'], + command: ['/bin/sh -c "node ./main.js"'], + healthCheck: { + command: [ + 'CMD-SHELL', + `curl --fail -s http://localhost:${ctx.Container.Env.NX_PORT}/health || exit 1`, + ], + interval: Duration.seconds(10), + retries: 3, + startPeriod: Duration.seconds(10), + timeout: Duration.seconds(5), + }, + stopTimeout: Duration.seconds(10), // waits 10 seconds after SIGTERM to fire SIGKILL + secrets: { + NX_DATABASE_URL: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes(this, 'DatabaseUrlParam', { + parameterName: '/apps/maybe-app/NX_DATABASE_URL', + }) + ), + NX_DATABASE_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'DatabaseSecretParam', + { + parameterName: '/apps/maybe-app/NX_DATABASE_SECRET', + } + ) + ), + NX_SESSION_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'SessionSecretParam', + { + parameterName: '/apps/maybe-app/NX_SESSION_SECRET', + } + ) + ), + NX_AUTH0_CLIENT_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'Auth0ClientSecretParam', + { + parameterName: '/apps/maybe-app/NX_AUTH0_CLIENT_SECRET', + } + ) + ), + NX_AUTH0_MGMT_CLIENT_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'Auth0MgmtClientSecretParam', + { + parameterName: '/apps/maybe-app/NX_AUTH0_MGMT_CLIENT_SECRET', + } + ) + ), + NX_PLAID_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes(this, 'PlaidSecretParam', { + parameterName: '/providers/NX_PLAID_SECRET', + }) + ), + NX_FINICITY_APP_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'FinicityAppKeyParam', + { + parameterName: '/providers/NX_FINICITY_APP_KEY', + } + ) + ), + NX_FINICITY_PARTNER_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'FinicityPartnerSecretParam', + { + parameterName: '/providers/NX_FINICITY_PARTNER_SECRET', + } + ) + ), + NX_LD_SDK_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'LaunchDarklySDKKeyParam', + { + parameterName: '/providers/NX_LD_SDK_KEY', + } + ) + ), + NX_POLYGON_API_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'PolygonApiKeyParam', + { + parameterName: '/providers/NX_POLYGON_API_KEY', + } + ) + ), + NX_INTERCOM_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'IntercomSecretParam', + { + parameterName: '/providers/NX_INTERCOM_SECRET', + } + ) + ), + NX_STRIPE_SECRET_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'StripeSecretKeyParam', + { + parameterName: '/providers/NX_STRIPE_SECRET_KEY', + } + ) + ), + NX_STRIPE_WEBHOOK_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'StripeWebhookSecretParam', + { + parameterName: '/providers/NX_STRIPE_WEBHOOK_SECRET', + } + ) + ), + NX_CONVERTKIT_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'ConvertKitKeyParam', + { + parameterName: '/providers/NX_CONVERTKIT_KEY', + } + ) + ), + NX_CONVERTKIT_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'ConvertKitSecretParam', + { + parameterName: '/providers/NX_CONVERTKIT_SECRET', + } + ) + ), + NX_POSTMARK_API_TOKEN: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'PostmarkApiTokenParam', + { + parameterName: '/providers/NX_POSTMARK_API_TOKEN', + } + ) + ), + // references the User access key created in SharedStack + AWS_SECRET_ACCESS_KEY: ECSSecret.fromSecretsManager( + Secret.fromSecretPartialArn(this, 'AwsSdkUserSecret', userAccessSecretArn) + ), + }, + }) + + const server = new FargateService(this, 'ServerService', { + cluster, // reference to SharedStack + taskDefinition, + enableECSManagedTags: true, + propagateTags: PropagatedTagSource.SERVICE, + desiredCount: ctx.DesiredTaskCount, + circuitBreaker: { + rollback: true, + }, + }) + + this.loadBalancer = new ApplicationLoadBalancer(this, 'ServerLoadBalancer', { + vpc, // reference to SharedStack + internetFacing: true, + }) + + const httpsListener = this.loadBalancer.addListener('PublicListener', { + protocol: ApplicationProtocol.HTTPS, + port: 443, + open: true, + sslPolicy: SslPolicy.RECOMMENDED, + }) + + httpsListener.addCertificates('ServerLBCertificates', [ + Certificate.fromCertificateArn(this, 'ServerCertificate', ctx.CertificateArn), + ]) + + // Point the load balancer at the ECS task + httpsListener.addTargets('ECS', { + protocol: ApplicationProtocol.HTTP, + protocolVersion: ApplicationProtocolVersion.HTTP1, + targets: [server], + deregistrationDelay: Duration.seconds(10), + healthCheck: { + path: '/health', + healthyThresholdCount: 2, + timeout: Duration.seconds(3), + interval: Duration.seconds(5), + }, + }) + + // Redirect all HTTP traffic to HTTPs + this.loadBalancer.addListener('RedirectToHTTPSListener', { + protocol: ApplicationProtocol.HTTP, + port: 80, + open: true, + defaultAction: ListenerAction.redirect({ + port: '443', + protocol: ApplicationProtocol.HTTPS, + permanent: true, + }), + }) + + // WAF => Cloudfront => ALB + new Distribution(this, 'ServerCloudfrontDistribution', { + comment: 'ALB for ECS Fargate server', + defaultBehavior: { + origin: new LoadBalancerV2Origin(this.loadBalancer, { + protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY, + originSslProtocols: [OriginSslPolicy.TLS_V1_1], + }), + allowedMethods: AllowedMethods.ALLOW_ALL, + cachePolicy: CachePolicy.CACHING_DISABLED, + originRequestPolicy: OriginRequestPolicy.ALL_VIEWER, + viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + }, + priceClass: PriceClass.PRICE_CLASS_100, + webAclId: ctx.WAFArn, + certificate: Certificate.fromCertificateArn( + this, + 'CloudfrontCertificate', + ctx.Cloudfront.CertificateArn + ), + domainNames: ctx.Cloudfront.CNAMES, + }) + + // Put a tag on this service so we can track it easier in Cost Explorer + Tags.of(server).add('ecs-service-name', 'server') + } +} diff --git a/aws/maybe-app/lib/stacks/shared-stack.ts b/aws/maybe-app/lib/stacks/shared-stack.ts new file mode 100644 index 00000000000..845a171cc3d --- /dev/null +++ b/aws/maybe-app/lib/stacks/shared-stack.ts @@ -0,0 +1,229 @@ +import { RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib' +import { Certificate } from 'aws-cdk-lib/aws-certificatemanager' +import { Distribution, KeyGroup, PriceClass, PublicKey } from 'aws-cdk-lib/aws-cloudfront' +import { S3Origin } from 'aws-cdk-lib/aws-cloudfront-origins' +import { + CfnRoute, + GatewayVpcEndpointAwsService, + InterfaceVpcEndpointAwsService, + IVpc, + Vpc, +} from 'aws-cdk-lib/aws-ec2' +import { Cluster, ICluster } from 'aws-cdk-lib/aws-ecs' +import { AccessKey, Policy, PolicyStatement, User } from 'aws-cdk-lib/aws-iam' +import { Bucket, CfnBucket, HttpMethods } from 'aws-cdk-lib/aws-s3' +import { Secret } from 'aws-cdk-lib/aws-secretsmanager' +import { Construct } from 'constructs' +import { JumpBox } from '../constructs/jump-box' +import { NetworkACLConfig } from '../constructs/network-acl' +import { Redis } from '../constructs/redis' +import { SharedStackContext } from '../utils/get-context' +import { StringParameter } from 'aws-cdk-lib/aws-ssm' + +interface SharedStackProps extends StackProps {} + +/** + * Shared resources that workloads are deployed to + * + * @see - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html + */ +export class SharedStack extends Stack { + public readonly vpc: IVpc + public readonly cluster: ICluster + public readonly redisUrl: string + public readonly userAccessSecretArn: string + public readonly userAccessKeyId: string + public readonly privateBucketName: string + public readonly publicBucketName: string + public readonly signerSecretId: string + public readonly signerPubKeyId: string + + constructor(scope: Construct, id: string, props: SharedStackProps, ctx: SharedStackContext) { + super(scope, id, props) + + this.vpc = new Vpc(this, 'MaybeAppVPC', { + maxAzs: 2, + natGateways: ctx.VPC.NATCount, + }) + + new NetworkACLConfig(this, 'MaybeAppVPCNetworkACLConfig', { + vpc: this.vpc, + authorizedIPs: ctx.VPC.IPAllowList, + }) + + // Fargate >= 1.4.0 requires S3 Gateway, ECR, ECR Docker, and Cloudwatch Logs + // https://docs.aws.amazon.com/AmazonECR/latest/userguide/vpc-endpoints.html#ecr-vpc-endpoint-considerations + this.vpc.addGatewayEndpoint('S3GatewayEndpoint', { + service: GatewayVpcEndpointAwsService.S3, + }) + + this.vpc.addInterfaceEndpoint('ECRInterfaceEndpoint', { + service: InterfaceVpcEndpointAwsService.ECR, + }) + + this.vpc.addInterfaceEndpoint('DockerInterfaceEndpoint', { + service: InterfaceVpcEndpointAwsService.ECR_DOCKER, + }) + + // Containers write logs directly to Cloudwatch + this.vpc.addInterfaceEndpoint('CloudwatchLogsInterfaceEndpoint', { + service: InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, + }) + + // Open a communication channel with TimescaleDB + if (ctx.PeeringCnxId) { + const addPeeringRoute = (routeTableId: string, index: number, subnetType: string) => { + new CfnRoute(this, `PeeringRoute-${subnetType}-${index}`, { + destinationCidrBlock: ctx.PeeringCnxCidr, + routeTableId, + vpcPeeringConnectionId: ctx.PeeringCnxId, + }) + } + + // Allows our private services to connect to DB + this.vpc.privateSubnets.forEach((subnet, index) => + addPeeringRoute(subnet.routeTable.routeTableId, index, 'private') + ) + + // This allows our JumpBox SSH tunnel to work + this.vpc.publicSubnets.forEach((subnet, index) => + addPeeringRoute(subnet.routeTable.routeTableId, index, 'public') + ) + } + + // The ECS Cluster that all ECS services and tasks are deployed to + this.cluster = new Cluster(this, 'ECSCluster', { vpc: this.vpc }) + + const redis = new Redis(this, 'Redis', { + vpc: this.vpc, + redisConfig: { + numCacheNodes: ctx.Redis.count, + cacheNodeType: ctx.Redis.size, + }, + }) + + this.redisUrl = redis.redisUrl + + // For connecting to DB through EC2 instance as SSH tunnel + new JumpBox(this, 'JumpBox', { vpc: this.vpc }, ctx) + + // Where we put public assets like fonts, logos, etc. + // Everything stored here is publicly available over our CDN + const publicBucket = new Bucket(this, 'Public', { + removalPolicy: RemovalPolicy.RETAIN, + }) + + // WORM compliant bucket to store CDN assets such as client agreements, AMA uploads + const privateBucket = new Bucket(this, 'Assets', { + versioned: true, + removalPolicy: RemovalPolicy.RETAIN, + cors: [ + { + allowedHeaders: [ + 'Authorization', + 'x-amz-date', + 'x-amz-content-sha256', + 'content-type', + ], + allowedMethods: [HttpMethods.GET, HttpMethods.POST], + allowedOrigins: ctx.UploadOrigins, + }, + ], + }) + + const privateBucketRef = privateBucket.node.defaultChild as CfnBucket + privateBucketRef.addPropertyOverride('ObjectLockEnabled', true) + privateBucketRef.addPropertyOverride('ObjectLockConfiguration.ObjectLockEnabled', 'Enabled') + privateBucketRef.addPropertyOverride( + 'ObjectLockConfiguration.Rule.DefaultRetention.Years', + 5 + ) + privateBucketRef.addPropertyOverride( + 'ObjectLockConfiguration.Rule.DefaultRetention.Mode', + 'GOVERNANCE' + ) + + const signerPublicKey = StringParameter.fromStringParameterName( + this, + 'SignerPKParam', + '/apps/maybe-app/CLOUDFRONT_SIGNER1_PUB' + ) + + const signerPubKey = new PublicKey(this, 'SignerPub', { + encodedKey: signerPublicKey.stringValue, + }) + + // server app will grab this ID as a reference + this.signerPubKeyId = signerPubKey.publicKeyId + + const signerPrivateKey = Secret.fromSecretNameV2( + this, + 'SignerPriv', + '/apps/maybe-app/CLOUDFRONT_SIGNER1_PRIV' + ) + + // server app will dynamically grab this (via SDK) by its ID to sign urls + this.signerSecretId = signerPrivateKey.secretName + + // This group holds public keys that can sign URLs and cookies to serve restricted content from the Distribution + const keyGroup = new KeyGroup(this, 'KeyGroup', { + items: [signerPubKey], + }) + + const distribution = new Distribution(this, 'Distribution', { + comment: 'Maybe App Cloudfront CDN', + domainNames: ctx.Cloudfront.CNAMES, + certificate: Certificate.fromCertificateArn( + this, + 'CdnCert', + ctx.Cloudfront.CertificateArn + ), + defaultBehavior: { + origin: new S3Origin(publicBucket), + }, + additionalBehaviors: { + // All content served here requires signed urls to view + '/private/*': { + origin: new S3Origin(privateBucket), + trustedKeyGroups: [keyGroup], + }, + }, + priceClass: PriceClass.PRICE_CLASS_100, + }) + + /** + * User that AWS SDK will use within server and workers apps + * + * Permissions added as-needed here. + */ + const user = new User(this, 'SdkUser') + user.attachInlinePolicy( + new Policy(this, 'SdkUserPolicy', { + statements: [ + new PolicyStatement({ + actions: ['s3:PutObject', 's3:GetObject', 's3:GetObjectAttributes'], + resources: [`${privateBucket.bucketArn}/*`, `${publicBucket.bucketArn}/*`], + }), + new PolicyStatement({ + actions: ['secretsmanager:GetSecretValue'], + // Can access any key stored under the /apps/maybe-app path + resources: [ + `arn:aws:secretsmanager:${this.region}:${this.account}:secret:/apps/maybe-app/*`, + ], + }), + ], + }) + ) + + // Access key secret safely stored in secrets manager (and retrieved in server/workers stacks) + const accessKey = new AccessKey(this, 'SdkUserAcessKey', { user }) + const accessKeySecret = new Secret(this, 'SdkUserAccessKeySecret', { + secretStringValue: accessKey.secretAccessKey, + }) + + this.userAccessKeyId = accessKey.accessKeyId + this.userAccessSecretArn = accessKeySecret.secretArn + this.privateBucketName = privateBucket.bucketName + this.publicBucketName = publicBucket.bucketName + } +} diff --git a/aws/maybe-app/lib/stacks/tools-stack.ts b/aws/maybe-app/lib/stacks/tools-stack.ts new file mode 100644 index 00000000000..b826fda140f --- /dev/null +++ b/aws/maybe-app/lib/stacks/tools-stack.ts @@ -0,0 +1,30 @@ +import { Stack, StackProps } from 'aws-cdk-lib' +import { SubnetType, Vpc } from 'aws-cdk-lib/aws-ec2' +import { Construct } from 'constructs' +import { GithubActionsRunner } from '../constructs/github-actions-runner/github-actions-runner' +import { NetworkACLConfig } from '../constructs/network-acl' +import { ToolsStackContext } from '../utils/get-context' + +interface ToolsStackProps extends StackProps {} + +/** + * Resources deployed to the Deployments account for CI/CD purposes + */ +export class ToolsStack extends Stack { + constructor(scope: Construct, id: string, props: ToolsStackProps, ctx: ToolsStackContext) { + super(scope, id, props) + + const vpc = new Vpc(this, 'ToolsVPC', { + maxAzs: 1, + natGateways: 0, + subnetConfiguration: [{ subnetType: SubnetType.PUBLIC, name: 'public' }], + }) + + new NetworkACLConfig(this, 'ToolsNetworkACL', { + vpc, + authorizedIPs: ctx.VPC.IPAllowList, + }) + + new GithubActionsRunner(this, 'GithubActionsRunnerInstance', { vpc }, ctx) + } +} diff --git a/aws/maybe-app/lib/stacks/workers-stack.ts b/aws/maybe-app/lib/stacks/workers-stack.ts new file mode 100644 index 00000000000..7d4679904e1 --- /dev/null +++ b/aws/maybe-app/lib/stacks/workers-stack.ts @@ -0,0 +1,188 @@ +import { Duration, Stack, StackProps, Tags } from 'aws-cdk-lib' +import { NetworkMode, Platform } from 'aws-cdk-lib/aws-ecr-assets' +import { + ContainerImage, + FargateService, + FargateTaskDefinition, + LogDrivers, + PropagatedTagSource, + Secret as ECSSecret, +} from 'aws-cdk-lib/aws-ecs' +import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam' +import { Secret } from 'aws-cdk-lib/aws-secretsmanager' +import { StringParameter } from 'aws-cdk-lib/aws-ssm' +import { Construct } from 'constructs' +import { WorkersStackContext } from '../utils/get-context' +import { SharedStack } from './shared-stack' + +interface WorkersStackProps extends StackProps { + sharedStack: SharedStack +} + +export class WorkersStack extends Stack { + constructor(scope: Construct, id: string, props: WorkersStackProps, ctx: WorkersStackContext) { + super(scope, id, props) + + const { + redisUrl, + userAccessKeyId, + userAccessSecretArn, + publicBucketName, + privateBucketName, + cluster, + } = props.sharedStack + + const workerTask = new FargateTaskDefinition(this, 'WorkerTaskDefinition', { + memoryLimitMiB: +ctx.ComputeCapacity.memory, + cpu: ctx.ComputeCapacity.cpu, + }) + + // CDK does not add these permissions by default - https://github.com/aws/aws-cdk/issues/17156 + // https://docs.aws.amazon.com/AmazonECS/latest/userguide/specifying-sensitive-data-secrets.html#secrets-iam + workerTask.addToExecutionRolePolicy( + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['secretsmanager:GetSecretValue'], + resources: [userAccessSecretArn], + }) + ) + + workerTask.addContainer('worker-container', { + logging: LogDrivers.awsLogs({ streamPrefix: 'WorkerLog' }), + image: ContainerImage.fromAsset('../../', { + file: 'apps/workers/Dockerfile', + networkMode: NetworkMode.HOST, + target: 'prod', + platform: Platform.LINUX_AMD64, // explicitly define so can build on Macbook M1 (https://stackoverflow.com/a/71102144) + }), + environment: { + AWS_ACCESS_KEY_ID: userAccessKeyId, + NX_REDIS_URL: redisUrl, + NX_CDN_PUBLIC_BUCKET: publicBucketName, + NX_CDN_PRIVATE_BUCKET: privateBucketName, + ...ctx.sharedEnv, + ...ctx.Container.Env, + }, + portMappings: [{ containerPort: +ctx.Container.Env.NX_PORT }], + entryPoint: ['sh', '-c'], + command: ['/bin/sh -c "node ./main.js"'], + healthCheck: { + command: [ + 'CMD-SHELL', + `curl --fail -s http://localhost:${ctx.Container.Env.NX_PORT}/health || exit 1`, + ], + interval: Duration.seconds(10), + retries: 3, + startPeriod: Duration.seconds(10), + timeout: Duration.seconds(5), + }, + stopTimeout: Duration.seconds(10), + secrets: { + NX_DATABASE_URL: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes(this, 'DatabaseUrlParam', { + parameterName: '/apps/maybe-app/NX_DATABASE_URL', + }) + ), + NX_DATABASE_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'DatabaseSecretParam', + { + parameterName: '/apps/maybe-app/NX_DATABASE_SECRET', + } + ) + ), + NX_AUTH0_MGMT_CLIENT_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'Auth0MgmtClientSecretParam', + { + parameterName: '/apps/maybe-app/NX_AUTH0_MGMT_CLIENT_SECRET', + } + ) + ), + NX_PLAID_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes(this, 'PlaidSecretParam', { + parameterName: '/providers/NX_PLAID_SECRET', + }) + ), + NX_FINICITY_APP_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'FinicityAppKeyParam', + { + parameterName: '/providers/NX_FINICITY_APP_KEY', + } + ) + ), + NX_FINICITY_PARTNER_SECRET: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'FinicityPartnerSecretParam', + { + parameterName: '/providers/NX_FINICITY_PARTNER_SECRET', + } + ) + ), + NX_LD_SDK_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'LaunchDarklySDKKeyParam', + { + parameterName: '/providers/NX_LD_SDK_KEY', + } + ) + ), + NX_POLYGON_API_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'PolygonApiKeyParam', + { + parameterName: '/providers/NX_POLYGON_API_KEY', + } + ) + ), + NX_POSTMARK_API_TOKEN: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'PostmarkApiTokenParam', + { + parameterName: '/providers/NX_POSTMARK_API_TOKEN', + } + ) + ), + NX_STRIPE_SECRET_KEY: ECSSecret.fromSsmParameter( + StringParameter.fromSecureStringParameterAttributes( + this, + 'StripeSecretKeyParam', + { + parameterName: '/providers/NX_STRIPE_SECRET_KEY', + } + ) + ), + // references the User access key created in SharedStack + AWS_SECRET_ACCESS_KEY: ECSSecret.fromSecretsManager( + Secret.fromSecretPartialArn(this, 'AwsSdkUserSecret', userAccessSecretArn) + ), + }, + }) + + const workerService = new FargateService(this, 'WorkerService', { + cluster, // reference from SharedStack + taskDefinition: workerTask, + + enableECSManagedTags: true, + propagateTags: PropagatedTagSource.SERVICE, + + // ECS deployment config + // https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-type-ecs.html#deployment-circuit-breaker + desiredCount: ctx.DesiredTaskCount, + circuitBreaker: { + rollback: true, + }, + }) + + // Tag service for easier tracking in cost explorer + Tags.of(workerService).add('ecs-service-name', 'workers') + } +} diff --git a/aws/maybe-app/lib/utils/get-context.ts b/aws/maybe-app/lib/utils/get-context.ts new file mode 100644 index 00000000000..84a872082f1 --- /dev/null +++ b/aws/maybe-app/lib/utils/get-context.ts @@ -0,0 +1,232 @@ +import { App } from 'aws-cdk-lib' +import { z } from 'zod' + +const toStr = (v: number) => v.toString() + +/** + * Fargate compute capacity schema + * + * @see https://docs.aws.amazon.com/AmazonECS/latest/userguide/fargate-task-defs.html#fargate-tasks-size + */ +const fargateComputeSchema = z.union([ + z + .object({ + cpu: z.literal(512), + memory: z.enum(['1024', '2048', '3072', '4096']), + }) + .strict(), + z + .object({ + cpu: z.literal(1024), + memory: z.enum(['2048', '3072', '4096', '5120', '6144', '7168', '8192']), + }) + .strict(), + z + .object({ + cpu: z.literal(2048), + memory: z.enum(['4096', '5120', '6144', '7168', '8192', '9216', '10240']), + }) + .strict(), + z + .object({ + cpu: z.literal(4096), + memory: z.enum(['4096', '5120', '6144', '7168', '8192', '9216', '10240']), + }) + .strict(), +]) + +const maybeAppSharedEnv = z + .object({ + NX_API_URL: z.string(), + NX_CDN_URL: z.string(), + NODE_ENV: z.enum(['production', 'development']), + NX_AUTH0_AUDIENCE: z.literal('https://maybe-finance-api/v1'), + NX_PLAID_CLIENT_ID: z.literal('REPLACE_THIS'), + NX_PLAID_ENV: z.string(), + NX_FINICITY_PARTNER_ID: z.literal('REPLACE_THIS'), + NX_FINICITY_ENV: z.enum(['sandbox', 'production']), + NX_CLIENT_URL: z.string(), + NX_CLIENT_URL_CUSTOM: z.string(), + NX_ADVISOR_URL: z.string(), + NX_AUTH0_DOMAIN: z.string(), + NX_AUTH0_CUSTOM_DOMAIN: z.string(), + NX_AUTH0_CLIENT_ID: z.string(), + NX_AUTH0_MGMT_CLIENT_ID: z.string(), + NX_SENTRY_ENV: z.string(), + NX_POSTMARK_FROM_ADDRESS: z.string(), + NX_STRIPE_PREMIUM_YEARLY_PRICE_ID: z.string().optional(), + NX_STRIPE_PREMIUM_MONTHLY_PRICE_ID: z.string().optional(), + }) + .strict() + +const maybeAppSharedStackContext = z + .object({ + PeeringCnxId: z.string().optional(), + PeeringCnxCidr: z.string(), + VPC: z + .object({ + NATCount: z.number().lte(2), + IPAllowList: z.string().array().max(20), // Limit to 20 addresses to avoid rule priority conflicts + }) + .strict(), + Redis: z + .object({ + size: z.enum([ + 'cache.t4g.micro', + 'cache.t4g.small', + 'cache.t4g.medium', + 'cache.m5.large', + 'cache.m6g.large', + 'cache.m6g.xlarge', + 'cache.m6g.2xlarge', + ]), + count: z.number().lte(5), + }) + .strict(), + Cloudfront: z + .object({ + CertificateArn: z.string(), // located in us-east-1 region + CNAMES: z.string().array(), + }) + .strict(), + UploadOrigins: z.array(z.string()).min(1), + }) + .strict() + +const maybeAppServerStackContext = z.object({ + CertificateArn: z.string(), + DesiredTaskCount: z.number(), + WAFArn: z.string(), + ComputeCapacity: fargateComputeSchema, + Cloudfront: z + .object({ + CNAMES: z.string().array(), + CertificateArn: z.string(), // located in us-east-1 region + }) + .strict(), + Container: z + .object({ + Env: z + .object({ + NX_CORS_ORIGINS: z.string(), + NX_MORGAN_LOG_LEVEL: z.string(), + NX_PORT: z.number().transform(toStr), + NX_SENTRY_DSN: z.string(), + }) + .strict(), + }) + .strict(), +}) + +const maybeAppWorkersStackContext = z + .object({ + DesiredTaskCount: z.number(), + ComputeCapacity: fargateComputeSchema, + Container: z + .object({ + Env: z + .object({ + NX_PORT: z.number().transform(toStr), + NX_SENTRY_DSN: z.string(), + }) + .strict(), + }) + .strict(), + }) + .strict() + +const maybeAppStackContexts = z + .object({ + Shared: maybeAppSharedStackContext, + Server: maybeAppServerStackContext, + Workers: maybeAppWorkersStackContext, + }) + .strict() + +const stagingEnvSchema = z + .object({ + ENV_NAME: z.literal('staging'), + AWS_ACCOUNT: z.literal('REPLACE_THIS'), + DEFAULT_AWS_REGION: z.literal('us-west-2'), + sharedEnv: maybeAppSharedEnv, + stackContexts: maybeAppStackContexts, + }) + .strict() + +const prodEnvSchema = z + .object({ + ENV_NAME: z.literal('production'), + AWS_ACCOUNT: z.literal('541001830411'), + DEFAULT_AWS_REGION: z.literal('us-west-2'), + sharedEnv: maybeAppSharedEnv, + stackContexts: maybeAppStackContexts, + }) + .strict() + +const toolsStackContexts = z + .object({ + Tools: z.object({ + VPC: z + .object({ + IPAllowList: z.string().array().max(20), + }) + .strict(), + GithubRunner: z + .object({ + SSMKeyArn: z.string(), + GithubTokenArn: z.string(), + }) + .strict(), + }), + }) + .strict() + +const toolsSharedEnv = z.object({}).strict() + +const toolsEnvSchema = z + .object({ + ENV_NAME: z.literal('tools'), + AWS_ACCOUNT: z.literal('REPLACE_THIS'), + DEFAULT_AWS_REGION: z.literal('us-west-2'), + sharedEnv: toolsSharedEnv, + stackContexts: toolsStackContexts, + }) + .strict() + +// Determines what account the configuration is deployed to +const envSchema = z + .object({ + tools: toolsEnvSchema, + staging: stagingEnvSchema, + production: prodEnvSchema, + }) + .strict() + +export const getContext = (app: App) => { + const envKeySchema = z.enum(['tools', 'staging', 'production']) + const envKey = envKeySchema.parse(process.env.CDK_ENV) + + const envRaw = app.node.tryGetContext('environments') + if (!envRaw) throw new Error(`cdk.json is in an invalid config state`) + + const env = envSchema.parse(envRaw) + + console.log(`Environment: ${envKey}`) + console.log(JSON.stringify(env[envKey], null, 2)) + + return env[envKey] +} + +type MaybeAppContext = { sharedEnv: z.infer } & { + DEFAULT_AWS_REGION: string +} & TContext + +export type SharedStackContext = MaybeAppContext['Shared']> +export type ServerStackContext = MaybeAppContext['Server']> +export type WorkersStackContext = MaybeAppContext['Workers']> + +type ToolsContext = { sharedEnv: z.infer } & { + DEFAULT_AWS_REGION: string +} & TContext + +export type ToolsStackContext = ToolsContext['Tools']> diff --git a/aws/maybe-app/package.json b/aws/maybe-app/package.json new file mode 100644 index 00000000000..1968bae294a --- /dev/null +++ b/aws/maybe-app/package.json @@ -0,0 +1,30 @@ +{ + "name": "maybe-app", + "version": "0.1.0", + "bin": { + "maybe-app": "bin/maybe-app.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "CDK_ENV=staging cdk", + "cdk:ls": "yarn cdk ls --profile=maybe_tools", + "cdk:deploy:shared": "yarn cdk deploy SharedStack --profile=maybe_tools" + }, + "devDependencies": { + "@types/jest": "^26.0.10", + "@types/node": "10.17.27", + "aws-cdk": "^2.52.0", + "jest": "^26.4.2", + "ts-jest": "^26.2.0", + "ts-node": "^10.9.1", + "typescript": "^4.6.4" + }, + "dependencies": { + "aws-cdk-lib": "^2.52.0", + "constructs": "^10.0.0", + "source-map-support": "^0.5.16", + "zod": "^3.14.4" + } +} diff --git a/aws/maybe-app/test/maybe-app.test.ts b/aws/maybe-app/test/maybe-app.test.ts new file mode 100644 index 00000000000..40797d2e579 --- /dev/null +++ b/aws/maybe-app/test/maybe-app.test.ts @@ -0,0 +1,202 @@ +import * as cdk from 'aws-cdk-lib' +import { Template } from 'aws-cdk-lib/assertions' +import { SharedStack } from '../lib/stacks/shared-stack' +import { ServerStack } from '../lib/stacks/server-stack' +import { WorkersStack } from '../lib/stacks/workers-stack' +import { getContext } from '../lib/utils/get-context' + +// Workaround - by default, context is not automatically passed to tests +// https://github.com/aws/aws-cdk/issues/5149#issuecomment-1084788745 +import cdkJsonRaw from '../cdk.json' + +// ====================================================================== +// ===================== STAGING TESTS ================================== +// ====================================================================== + +test('Shared staging stack created', () => { + process.env['CDK_ENV'] = 'staging' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedStagingStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const template = Template.fromStack(sharedStack) + + // VPC created correctly + template.resourceCountIs('AWS::EC2::VPC', 1) + template.resourceCountIs('AWS::EC2::Subnet', 4) + template.resourceCountIs('AWS::EC2::NatGateway', 1) + template.resourceCountIs('AWS::EC2::RouteTable', 4) + template.resourceCountIs('AWS::EC2::EIP', 1) + template.resourceCountIs('AWS::EC2::InternetGateway', 1) + + // Redis cluster created + template.resourceCountIs('AWS::ElastiCache::CacheCluster', 1) + + // ECS cluster created + template.resourceCountIs('AWS::ECS::Cluster', 1) +}) + +test('Server staging stack created', () => { + process.env['CDK_ENV'] = 'staging' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedStagingStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const serverStack = new ServerStack( + app, + 'ServerStagingStack', + { + sharedStack, + }, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Server } + ) + + const template = Template.fromStack(serverStack) + + template.resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1) + template.resourceCountIs('AWS::ElasticLoadBalancingV2::Listener', 2) + template.resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1) + template.resourceCountIs('AWS::ECS::TaskDefinition', 1) + template.resourceCountIs('AWS::ECS::Service', 1) +}) + +test('Workers staging stack created', () => { + process.env['CDK_ENV'] = 'staging' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedStagingStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const workersStack = new WorkersStack( + app, + 'WorkersStagingStack', + { + sharedStack, + }, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Workers } + ) + + const template = Template.fromStack(workersStack) + + template.resourceCountIs('AWS::ECS::TaskDefinition', 1) + template.resourceCountIs('AWS::ECS::Service', 1) +}) + +// ====================================================================== +// ================== PRODUCTION TESTS ================================== +// ====================================================================== + +test('Shared production stack created', () => { + process.env['CDK_ENV'] = 'production' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const template = Template.fromStack(sharedStack) + + // VPC created correctly + template.resourceCountIs('AWS::EC2::VPC', 1) + template.resourceCountIs('AWS::EC2::Subnet', 4) + template.resourceCountIs('AWS::EC2::NatGateway', 2) + template.resourceCountIs('AWS::EC2::RouteTable', 4) + template.resourceCountIs('AWS::EC2::EIP', 2) + template.resourceCountIs('AWS::EC2::InternetGateway', 1) + + // Redis cluster created + template.resourceCountIs('AWS::ElastiCache::CacheCluster', 1) + + // ECS cluster created + template.resourceCountIs('AWS::ECS::Cluster', 1) +}) + +test('Server production stack created', () => { + process.env['CDK_ENV'] = 'production' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedProductionStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const serverStack = new ServerStack( + app, + 'ServerProductionStack', + { + sharedStack, + }, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Server } + ) + + const template = Template.fromStack(serverStack) + + template.resourceCountIs('AWS::ElasticLoadBalancingV2::LoadBalancer', 1) + template.resourceCountIs('AWS::ElasticLoadBalancingV2::Listener', 2) + template.resourceCountIs('AWS::ElasticLoadBalancingV2::TargetGroup', 1) + template.resourceCountIs('AWS::ECS::TaskDefinition', 1) + template.resourceCountIs('AWS::ECS::Service', 1) +}) + +test('Workers production stack created', () => { + process.env['CDK_ENV'] = 'production' + + const app = new cdk.App({ context: cdkJsonRaw.context }) + + const { DEFAULT_AWS_REGION, sharedEnv, stackContexts } = getContext(app) + + const sharedStack = new SharedStack( + app, + 'SharedProductionStack', + {}, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Shared } + ) + + const workersStack = new WorkersStack( + app, + 'WorkersProductionStack', + { + sharedStack, + }, + { DEFAULT_AWS_REGION, sharedEnv, ...(stackContexts as any).Workers } + ) + + const template = Template.fromStack(workersStack) + + template.resourceCountIs('AWS::ECS::TaskDefinition', 1) + template.resourceCountIs('AWS::ECS::Service', 1) +}) diff --git a/aws/maybe-app/tsconfig.json b/aws/maybe-app/tsconfig.json new file mode 100644 index 00000000000..81101628fcc --- /dev/null +++ b/aws/maybe-app/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": ["es2018"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": ["./node_modules/@types"], + "resolveJsonModule": true, + "esModuleInterop": true + }, + "exclude": ["node_modules", "cdk.out"] +} diff --git a/aws/maybe-app/yarn.lock b/aws/maybe-app/yarn.lock new file mode 100644 index 00000000000..f43b398e359 --- /dev/null +++ b/aws/maybe-app/yarn.lock @@ -0,0 +1,3657 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + +"@aws-cdk/asset-awscli-v1@^2.2.9": + version "2.2.21" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.21.tgz#636990a08783e41428a54027253bddc57138ed4e" + integrity sha512-EOy2ho4R19qJ00ghf4mezWlXW8xeUSFOqupRnTEAp07z8QnGhLFf6CiPGbBhVWvevCIu/PC/CTnmfRSMlbNLgg== + +"@aws-cdk/asset-kubectl-v20@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz#d01c1efb867fb7f2cfd8c8b230b8eae16447e156" + integrity sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw== + +"@aws-cdk/asset-node-proxy-agent-v5@^2.0.15": + version "2.0.27" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.27.tgz#9ea5adb1501db2eb89e124ec83df422a51d9ad18" + integrity sha512-BBqOLyfGL2xH4sxHkNA7IVxikdgE/R/BkHkFuN902kDzDZ0GH/04HYV6UiVMwj+5SsjQozzaIKT00BzNIN1qOg== + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/compat-data@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2" + integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ== + +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.9.tgz#6bae81a06d95f4d0dec5bb9d74bbc1f58babdcfe" + integrity sha512-5ug+SfZCpDAkVp9SFIZAzlW18rlzsOcJGaetCjkySnrXXDUw9AR8cDUm1iByTmdWM6yxX6/zycaV76w3YTF2gw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-module-transforms" "^7.17.7" + "@babel/helpers" "^7.17.9" + "@babel/parser" "^7.17.9" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + 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.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" + integrity sha512-rAdDousTwxbIxbz5I7GEQ3lUip+xVCXooZNbsydCWs3xA7ZsYOv+CFRdzGxRX78BmQHu9B1Eso59AOZQOJDEdQ== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-compilation-targets@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.7.tgz#a3c2924f5e5f0379b356d4cfb313d1414dc30e46" + integrity sha512-UFzlz2jjd8kroj0hmCFV5zr+tQPi1dpC2cRsDV/3IEW8bJfCPrPpmcSN6ZS8RqIq4LXcmpipCQFPddyFA5Yc7w== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-validator-option" "^7.16.7" + browserslist "^4.17.5" + semver "^6.3.0" + +"@babel/helper-environment-visitor@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" + integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-function-name@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12" + integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg== + dependencies: + "@babel/template" "^7.16.7" + "@babel/types" "^7.17.0" + +"@babel/helper-hoist-variables@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" + integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-transforms@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.7.tgz#3943c7f777139e7954a5355c815263741a9c1cbd" + integrity sha512-VmZD99F3gNTYB7fJRDTi+u6l/zxY0BE6OIxPSU7a50s6ZUQkHwSDmV92FfM+oCG0pZRVojGYhkR8I0OGeCVREw== + dependencies: + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-simple-access" "^7.17.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/helper-validator-identifier" "^7.16.7" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + +"@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.8.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" + integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== + +"@babel/helper-simple-access@^7.17.7": + version "7.17.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.17.7.tgz#aaa473de92b7987c6dfa7ce9a7d9674724823367" + integrity sha512-txyMCGroZ96i+Pxr3Je3lzEJjqwaRC9buMUgtomcrLe5Nd0+fk1h0LLA+ixUF5OW7AhHuQ7Es1WcQJZmZsz2XA== + dependencies: + "@babel/types" "^7.17.0" + +"@babel/helper-split-export-declaration@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" + integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/helper-validator-option@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" + integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== + +"@babel/helpers@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.9.tgz#b2af120821bfbe44f9907b1826e168e819375a1a" + integrity sha512-cPCt915ShDWUEzEp3+UNRktO2n6v49l5RSnG9M5pS24hA+2FAc5si+Pn1i4VVbQQ+jh+bIZhPFQOJOzbrOYY1Q== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.9" + "@babel/types" "^7.17.0" + +"@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" + integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== + +"@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-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/template@^7.16.7", "@babel/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.1.0", "@babel/traverse@^7.17.3", "@babel/traverse@^7.17.9": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.9.tgz#1f9b207435d9ae4a8ed6998b2b82300d83c37a0d" + integrity sha512-PQO8sDIJ8SIwipTPiR71kJQCKQYB5NGImbOviK8K+kg5xkNSYXLBupuX9QhatFowrsvo9Hj8WgArg3W7ijNAQw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.9" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.17.9" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.9" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.16.7", "@babel/types@^7.17.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@balena/dockerignore@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" + integrity sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q== + +"@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== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@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@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.6.2.tgz#4e04bc464014358b03ab4937805ee36a0aeb98f2" + integrity sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.6.2" + jest-util "^26.6.2" + slash "^3.0.0" + +"@jest/core@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.6.3.tgz#7639fcb3833d748a4656ada54bde193051e45fad" + integrity sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/reporters" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-changed-files "^26.6.2" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-resolve-dependencies "^26.6.3" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + jest-watcher "^26.6.2" + micromatch "^4.0.2" + p-each-series "^2.1.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.6.2.tgz#ba364cc72e221e79cc8f0a99555bf5d7577cf92c" + integrity sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA== + dependencies: + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + +"@jest/fake-timers@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.6.2.tgz#459c329bcf70cee4af4d7e3f3e67848123535aad" + integrity sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA== + dependencies: + "@jest/types" "^26.6.2" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +"@jest/globals@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.6.2.tgz#5b613b78a1aa2655ae908eba638cc96a20df720a" + integrity sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/types" "^26.6.2" + expect "^26.6.2" + +"@jest/reporters@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.6.2.tgz#1f518b99637a5f18307bd3ecf9275f6882a667f6" + integrity sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.4" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.3" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^26.6.2" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^7.0.0" + optionalDependencies: + node-notifier "^8.0.0" + +"@jest/source-map@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" + integrity sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.4" + source-map "^0.6.0" + +"@jest/test-result@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.6.2.tgz#55da58b62df134576cc95476efa5f7949e3f5f18" + integrity sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^26.6.3": + version "26.6.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz#98e8a45100863886d074205e8ffdc5a7eb582b17" + integrity sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw== + dependencies: + "@jest/test-result" "^26.6.2" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-runner "^26.6.3" + jest-runtime "^26.6.3" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.6.tgz#4ac237f4dabc8dd93330386907b97591801f7352" + integrity sha512-R7xHtBSNm+9SyvpJkdQl+qrM3Hm2fea3Ef197M3mUug+v+yR+Rhfbs7PBtcBUVnIWJ4JcAdjvij+c8hXS9p5aw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.11" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec" + integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg== + +"@jridgewell/trace-mapping@0.3.9", "@jridgewell/trace-mapping@^0.3.0": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@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@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": + 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.4", "@types/babel__traverse@^7.0.6": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.0.tgz#7a9b80f712fe2052bc20da153ff1e552404d8e4b" + integrity sha512-r8aveDbd+rzGP+ykSdF3oPuTVRWRfbBiHl0rVDM2yNEmSMXfkObQLV46b4RnCv3Lra51OlfnZhkkFaDl2MIRaA== + dependencies: + "@babel/types" "^7.3.0" + +"@types/graceful-fs@^4.1.2": + 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/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@^26.0.10": + version "26.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== + dependencies: + jest-diff "^26.0.0" + pretty-format "^26.0.0" + +"@types/node@*": + version "17.0.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.27.tgz#f4df3981ae8268c066e8f49995639f855469081e" + integrity sha512-4/Ke7bbWOasuT3kceBZFGakP1dYN2XFd8v2l9bqF2LNWrmeU07JLpp56aEeG6+Q3olqO5TvXpW0yaiYnZJ5CXg== + +"@types/node@10.17.27": + version "10.17.27" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.27.tgz#391cb391c75646c8ad2a7b6ed3bbcee52d1bdf19" + integrity sha512-J0oqm9ZfAXaPdwNXMMgAhylw5fhmXkToJd06vuDUSAgEDZ/n/69/69UmyBZbc+zT34UnShuDSBqvim3SPnozJg== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/prettier@^2.0.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.0.tgz#efcbd41937f9ae7434c714ab698604822d890759" + integrity sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw== + +"@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/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@^15.0.0": + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + dependencies: + "@types/yargs-parser" "*" + +abab@^2.0.3, abab@^2.0.5: + 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-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-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +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.2.4: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + +acorn@^8.4.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +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" + +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.0, 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" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.3: + 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@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +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" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +aws-cdk-lib@^2.52.0: + version "2.52.0" + resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.52.0.tgz#6467144aba3ba5db21d127519984b8ab426960ca" + integrity sha512-6pIPiDiSsyPAmRxf/EzIHoHkTEfkhr9GPhQgIWAUmlTnQ2hirvdSPpOzqyNUcE9DOEzrir9Kg3kjVqVa0my13Q== + dependencies: + "@aws-cdk/asset-awscli-v1" "^2.2.9" + "@aws-cdk/asset-kubectl-v20" "^2.1.1" + "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.15" + "@balena/dockerignore" "^1.0.2" + case "1.6.3" + fs-extra "^9.1.0" + ignore "^5.2.0" + jsonschema "^1.4.1" + minimatch "^3.1.2" + punycode "^2.1.1" + semver "^7.3.8" + yaml "1.10.2" + +aws-cdk@^2.52.0: + version "2.52.0" + resolved "https://registry.yarnpkg.com/aws-cdk/-/aws-cdk-2.52.0.tgz#432820388bbf981a3c16004333cf7337bd615de1" + integrity sha512-48MO8W2l+m+vGWmV3yPO67MBP/U1AjmNDhiWpxvGUcPuHHNe6C86/m5yhieXmB8j/WeMxG6p7GsLcjQtwoFvTA== + optionalDependencies: + fsevents "2.3.2" + +babel-jest@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.6.3.tgz#d87d25cb0037577a0c89f82e5755c5d293c01056" + integrity sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA== + dependencies: + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + slash "^3.0.0" + +babel-plugin-istanbul@^6.0.0: + 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@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz#8185bd030348d254c6d7dd974355e6a28b21e62d" + integrity sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@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@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz#747872b1171df032252426586881d62d31798fee" + integrity sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ== + dependencies: + babel-plugin-jest-hoist "^26.6.2" + babel-preset-current-node-syntax "^1.0.0" + +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== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +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@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +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.17.5: + version "4.20.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf" + integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg== + dependencies: + caniuse-lite "^1.0.30001332" + electron-to-chromium "^1.4.118" + escalade "^3.1.1" + node-releases "^2.0.3" + picocolors "^1.0.0" + +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.x, 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== + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +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@^5.0.0, 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.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001332: + version "1.0.30001332" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" + integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +case@1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9" + integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ== + +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@^4.0.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== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cjs-module-lexer@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f" + integrity sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +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== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +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 sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +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== + +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" + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +constructs@^10.0.0: + version "10.1.173" + resolved "https://registry.yarnpkg.com/constructs/-/constructs-10.1.173.tgz#fdb8bab9c117054dfa2ebf5979386a459105ebb2" + integrity sha512-8J4T2gBxjlqSyb0U8IRShxQcYyihXXTdvmAQytjFOLLxUFlfQS8rLkrgN7N5qArFYPfTDTNEGMqf0fSZY+onUA== + +convert-source-map@^1.4.0, 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" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0: + 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" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +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" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1: + 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.3.3: + 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" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +decimal.js@^10.2.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + +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-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +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== + +diff-sequences@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" + integrity sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + +electron-to-chromium@^1.4.118: + version "1.4.122" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.122.tgz#56e518e8c4433876b01d4460eac0f653841ed510" + integrity sha512-VuLNxTIt8sBWIT2sd186xPd18Y8KcK8myLd9nMdSJOYZwFUxxbLVmX/T1VX+qqaytRlrYYQv39myxJdXtu7Ysw== + +emittery@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" + integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== + +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== + +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +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" + +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 sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +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== + +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" + +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== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +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== + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + 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 sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.6.2.tgz#c6b996bf26bf3fe18b67b2d0f51fc981ba934417" + integrity sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA== + dependencies: + "@jest/types" "^26.6.2" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-regex-util "^26.0.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 sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.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: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +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" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +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" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= + dependencies: + map-cache "^0.2.2" + +fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +fsevents@2.3.2, fsevents@^2.1.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== + +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.1: + 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-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@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + 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" + +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== + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +growly@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" + integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +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-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +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" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + +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@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + 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@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +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-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 sha1-khi5srkoojixPcT7a21XbyMUU+o= + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + 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== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.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 sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.8.1: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +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-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= + dependencies: + kind-of "^3.0.2" + +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-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +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-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +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-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= + +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@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + +istanbul-lib-instrument@^5.0.4: + 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.0.2: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jest-changed-files@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" + integrity sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ== + dependencies: + "@jest/types" "^26.6.2" + execa "^4.0.0" + throat "^5.0.0" + +jest-cli@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.6.3.tgz#43117cfef24bc4cd691a174a8796a532e135e92a" + integrity sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg== + dependencies: + "@jest/core" "^26.6.3" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.4" + import-local "^3.0.2" + is-ci "^2.0.0" + jest-config "^26.6.3" + jest-util "^26.6.2" + jest-validate "^26.6.2" + prompts "^2.0.1" + yargs "^15.4.1" + +jest-config@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.6.3.tgz#64f41444eef9eb03dc51d5c53b75c8c71f645349" + integrity sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg== + dependencies: + "@babel/core" "^7.1.0" + "@jest/test-sequencer" "^26.6.3" + "@jest/types" "^26.6.2" + babel-jest "^26.6.3" + chalk "^4.0.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.6.2" + jest-environment-node "^26.6.2" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.6.3" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + micromatch "^4.0.2" + pretty-format "^26.6.2" + +jest-diff@^26.0.0, jest-diff@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" + integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== + dependencies: + detect-newline "^3.0.0" + +jest-each@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.6.2.tgz#02526438a77a67401c8a6382dfe5999952c167cb" + integrity sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.6.2" + pretty-format "^26.6.2" + +jest-environment-jsdom@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz#78d09fe9cf019a357009b9b7e1f101d23bd1da3e" + integrity sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + jsdom "^16.4.0" + +jest-environment-node@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.6.2.tgz#824e4c7fb4944646356f11ac75b229b0035f2b0c" + integrity sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag== + dependencies: + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + jest-mock "^26.6.2" + jest-util "^26.6.2" + +jest-get-type@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" + integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-jasmine2@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz#adc3cf915deacb5212c93b9f3547cd12958f2edd" + integrity sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg== + dependencies: + "@babel/traverse" "^7.1.0" + "@jest/environment" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^26.6.2" + is-generator-fn "^2.0.0" + jest-each "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-runtime "^26.6.3" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + pretty-format "^26.6.2" + throat "^5.0.0" + +jest-leak-detector@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz#7717cf118b92238f2eba65054c8a0c9c653a91af" + integrity sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg== + dependencies: + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-matcher-utils@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz#8e6fd6e863c8b2d31ac6472eeb237bc595e53e7a" + integrity sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw== + dependencies: + chalk "^4.0.0" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + pretty-format "^26.6.2" + +jest-message-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" + integrity sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" + micromatch "^4.0.2" + pretty-format "^26.6.2" + slash "^3.0.0" + stack-utils "^2.0.2" + +jest-mock@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.6.2.tgz#d6cb712b041ed47fe0d9b6fc3474bc6543feb302" + integrity sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + +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@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-resolve-dependencies@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz#6680859ee5d22ee5dcd961fe4871f59f4c784fb6" + integrity sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg== + dependencies: + "@jest/types" "^26.6.2" + jest-regex-util "^26.0.0" + jest-snapshot "^26.6.2" + +jest-resolve@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.6.2.tgz#a3ab1517217f469b504f1b56603c5bb541fbb507" + integrity sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ== + dependencies: + "@jest/types" "^26.6.2" + chalk "^4.0.0" + graceful-fs "^4.2.4" + jest-pnp-resolver "^1.2.2" + jest-util "^26.6.2" + read-pkg-up "^7.0.1" + resolve "^1.18.1" + slash "^3.0.0" + +jest-runner@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.6.3.tgz#2d1fed3d46e10f233fd1dbd3bfaa3fe8924be159" + integrity sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" + exit "^0.1.2" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-docblock "^26.0.0" + jest-haste-map "^26.6.2" + jest-leak-detector "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + jest-runtime "^26.6.3" + jest-util "^26.6.2" + jest-worker "^26.6.2" + source-map-support "^0.5.6" + throat "^5.0.0" + +jest-runtime@^26.6.3: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.6.3.tgz#4f64efbcfac398331b74b4b3c82d27d401b8fa2b" + integrity sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw== + dependencies: + "@jest/console" "^26.6.2" + "@jest/environment" "^26.6.2" + "@jest/fake-timers" "^26.6.2" + "@jest/globals" "^26.6.2" + "@jest/source-map" "^26.6.2" + "@jest/test-result" "^26.6.2" + "@jest/transform" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + cjs-module-lexer "^0.6.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.4" + jest-config "^26.6.3" + jest-haste-map "^26.6.2" + jest-message-util "^26.6.2" + jest-mock "^26.6.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.6.2" + jest-snapshot "^26.6.2" + jest-util "^26.6.2" + jest-validate "^26.6.2" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.4.1" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.6.2.tgz#f3b0af1acb223316850bd14e1beea9837fb39c84" + integrity sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.6.2" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.6.2" + graceful-fs "^4.2.4" + jest-diff "^26.6.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.6.2" + jest-matcher-utils "^26.6.2" + jest-message-util "^26.6.2" + jest-resolve "^26.6.2" + natural-compare "^1.4.0" + pretty-format "^26.6.2" + semver "^7.3.2" + +jest-util@^26.1.0, jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-validate@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec" + integrity sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ== + dependencies: + "@jest/types" "^26.6.2" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + leven "^3.1.0" + pretty-format "^26.6.2" + +jest-watcher@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.6.2.tgz#a5b683b8f9d68dbcb1d7dae32172d2cca0592975" + integrity sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ== + dependencies: + "@jest/test-result" "^26.6.2" + "@jest/types" "^26.6.2" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^26.6.2" + string-length "^4.0.1" + +jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest@^26.4.2: + version "26.6.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.6.3.tgz#40e8fdbe48f00dfa1f0ce8121ca74b88ac9148ef" + integrity sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q== + dependencies: + "@jest/core" "^26.6.3" + import-local "^3.0.2" + jest-cli "^26.6.3" + +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" + +jsdom@^16.4.0: + version "16.7.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" + integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.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== + +json5@2.x, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonschema@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +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== + +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.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +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" + +lodash@4.x, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +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" + +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, make-error@^1.1.1: + 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" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + dependencies: + object-visit "^1.0.0" + +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== + +micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.2: + 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== + +minimatch@^3.0.4, 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.1.1, minimist@^1.2.0: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@1.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +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== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-notifier@^8.0.0: + version "8.0.2" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5" + integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg== + dependencies: + growly "^1.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" + shellwords "^0.1.1" + uuid "^8.3.0" + which "^2.0.2" + +node-releases@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.3.tgz#225ee7488e4a5e636da8da52854844f9d716ca96" + integrity sha512-maHFz6OLqYxz+VQyCAtA3PTX4UP/53pa05fyDNc9CwjvJ0yEh6+xBwKsgCxMNhS8taUKBFYxfuiaD9U/55iFaw== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= + dependencies: + remove-trailing-separator "^1.0.1" + +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== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0: + 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.0" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" + integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= + dependencies: + isobject "^3.0.0" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +onetime@^5.1.0: + 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" + +p-each-series@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.2.0.tgz#105ab0357ce72b202a8a8b94933672657b5e2a9a" + integrity sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA== + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= + +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-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-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== + +parse-json@^5.0.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@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= + +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 sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +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== + +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.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pirates@^4.0.1: + 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" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +pretty-format@^26.0.0, pretty-format@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" + integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== + dependencies: + "@jest/types" "^26.6.2" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^17.0.1" + +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" + +psl@^1.1.33: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +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== + +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== + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +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@^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-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= + +resolve@^1.10.0, resolve@^1.18.1: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +rimraf@^3.0.0: + 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" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +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== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +"semver@2 || 3 || 4 || 5", semver@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.x, semver@^7.3.2: + 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== + +semver@^7.3.8: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +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@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +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== + +shellwords@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" + integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== + +signal-exit@^3.0.0, signal-exit@^3.0.2: + 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== + +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== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.5.16, source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= + +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.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" + integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stack-utils@^2.0.2: + 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" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +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: + 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" + +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@^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-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +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== + +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.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-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.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== + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +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" + +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== + +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 sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +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" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +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@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" + integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== + dependencies: + punycode "^2.1.1" + +ts-jest@^26.2.0: + version "26.5.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" + integrity sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + jest-util "^26.1.0" + json5 "2.x" + lodash "4.x" + make-error "1.x" + mkdirp "1.x" + semver "7.x" + yargs-parser "20.x" + +ts-node@^10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.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 sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + 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.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-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +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.6.4: + version "4.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" + integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.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== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-to-istanbul@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz#30898d1a7fa0c84d225a2c1434fb958f290883c1" + integrity sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7, walker@~1.0.5: + 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@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" + integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== + dependencies: + iconv-lite "0.4.24" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" + integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" + integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg== + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1, which@^2.0.2: + 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: + 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@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + 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 sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.6: + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" + integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== + +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== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +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@20.x: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs@^15.4.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +zod@^3.14.4: + version "3.19.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473" + integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA== diff --git a/babel.config.json b/babel.config.json new file mode 100644 index 00000000000..af8aa6b9db5 --- /dev/null +++ b/babel.config.json @@ -0,0 +1,3 @@ +{ + "babelrcRoots": ["*"] +} diff --git a/custom-express.d.ts b/custom-express.d.ts new file mode 100644 index 00000000000..e43bcf0f4f7 --- /dev/null +++ b/custom-express.d.ts @@ -0,0 +1,31 @@ +import express, { Send, Response, Request } from 'express' +import { SharedType } from '@maybe-finance/shared' + +// Because this is a module, need to escape from module scope and enter global scope so declaration merging works correctly +declare global { + namespace Express { + interface Request { + // Add custom properties here (i.e. if props are defined with middleware) + user?: User & SharedType.MaybeCustomClaims + } + + interface Response { + json(data: any): Send + superjson(data: any): Send + } + + // express-jwt already adds a `user` prop to `req` object, we just need to define it + // This is the structure of the Auth0 user object - https://auth0.com/docs/users/user-profiles/user-profile-structure + // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/96d20a6a47593b83b0331a0a3f163a39aba523aa/types/express-jwt/index.d.ts#L69 + interface User + extends Partial<{ + iss: string + sub: string + aud: string[] + iat: number + exp: number + azp: string + scope: string + }> {} + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000000..a44a0258667 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +--- +version: '3.9' + +services: + postgres: + container_name: postgres + profiles: [services] + image: timescale/timescaledb:latest-pg14 + ports: + - 5432:5432 + environment: + POSTGRES_USER: maybe + POSTGRES_PASSWORD: maybe + POSTGRES_DB: maybe_local + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + container_name: redis + profiles: [services] + image: redis:6.2-alpine + ports: + - 6379:6379 + command: 'redis-server --bind 0.0.0.0' + + ngrok: + env_file: .env + image: shkoliar/ngrok:latest + profiles: [ngrok] + container_name: ngrok + ports: + - 4551:4551 + environment: + - DOMAIN=host.docker.internal + - PORT=3333 + - AUTH_TOKEN=${NGROK_AUTH_TOKEN} + - DEBUG=true + + stripe: + container_name: stripe + image: stripe/stripe-cli:latest + profiles: [stripe] + command: listen --forward-to host.docker.internal:3333/v1/stripe/webhook --log-level warn + extra_hosts: + - 'host.docker.internal:host-gateway' + environment: + - STRIPE_API_KEY=${STRIPE_SECRET_KEY} + tty: true + +volumes: + postgres_data: diff --git a/jest.config.ts b/jest.config.ts new file mode 100644 index 00000000000..9a3e8d148a0 --- /dev/null +++ b/jest.config.ts @@ -0,0 +1,5 @@ +const { getJestProjects } = require('@nrwl/jest') + +export default { + projects: getJestProjects(), +} diff --git a/jest.preset.js b/jest.preset.js new file mode 100644 index 00000000000..831de0a8d24 --- /dev/null +++ b/jest.preset.js @@ -0,0 +1,3 @@ +const nxPreset = require('@nrwl/jest/preset').default + +module.exports = { ...nxPreset } diff --git a/libs/.gitkeep b/libs/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/client/features/.babelrc b/libs/client/features/.babelrc new file mode 100644 index 00000000000..ac4757ac35f --- /dev/null +++ b/libs/client/features/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/client/features/.eslintrc.json b/libs/client/features/.eslintrc.json new file mode 100644 index 00000000000..bd2c1567c7c --- /dev/null +++ b/libs/client/features/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/client/features/jest.config.ts b/libs/client/features/jest.config.ts new file mode 100644 index 00000000000..2aa0694048d --- /dev/null +++ b/libs/client/features/jest.config.ts @@ -0,0 +1,10 @@ +/* eslint-disable */ +export default { + displayName: 'client-features', + preset: '../../../jest.preset.js', + transform: { + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../coverage/libs/client/features', +} diff --git a/libs/client/features/src/account/AccountMenu.tsx b/libs/client/features/src/account/AccountMenu.tsx new file mode 100644 index 00000000000..a7b88e442b9 --- /dev/null +++ b/libs/client/features/src/account/AccountMenu.tsx @@ -0,0 +1,52 @@ +import type { SharedType } from '@maybe-finance/shared' +import { BrowserUtil, useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import { Menu } from '@maybe-finance/design-system' +import { RiDeleteBin5Line, RiPencilLine, RiRefreshLine } from 'react-icons/ri' +import { useRouter } from 'next/router' +import { useAuth0 } from '@auth0/auth0-react' + +type Props = { + account?: SharedType.AccountDetail +} + +export function AccountMenu({ account }: Props) { + const { user } = useAuth0() + const { editAccount, deleteAccount } = useAccountContext() + const { useSyncAccount } = useAccountApi() + + const router = useRouter() + const syncAccount = useSyncAccount() + + if (!account) return null + + return ( + + + + + + } onClick={() => editAccount(account)}> + Edit + + {BrowserUtil.hasRole(user, 'Admin') && ( + } + destructive + onClick={() => syncAccount.mutate(account.id)} + > + Sync + + )} + {!account.accountConnectionId && ( + } + destructive + onClick={() => deleteAccount(account, () => router.push('/'))} + > + Delete + + )} + + + ) +} diff --git a/libs/client/features/src/account/AccountsSidebar.tsx b/libs/client/features/src/account/AccountsSidebar.tsx new file mode 100644 index 00000000000..da00135274a --- /dev/null +++ b/libs/client/features/src/account/AccountsSidebar.tsx @@ -0,0 +1,338 @@ +import type { AccordionRowProps } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' +import { useCallback, useMemo } from 'react' +import { + RiAlertLine, + RiCloseFill, + RiInformationLine as InfoIcon, + RiInformationLine, +} from 'react-icons/ri' +import { AnimatePresence, motion } from 'framer-motion' +import { AccordionRow, LoadingPlaceholder, TrendLine } from '@maybe-finance/design-system' +import classNames from 'classnames' +import type DecimalJS from 'decimal.js' +import { AiOutlineExclamationCircle } from 'react-icons/ai' +import { + useAccountApi, + useQueryParam, + useUserAccountContext, + useProviderStatus, + useAccountContext, + useLocalStorage, +} from '@maybe-finance/client/shared' +import { NumberUtil } from '@maybe-finance/shared' + +function SidebarAccountsLoader() { + return ( +
+ {Array(6) + .fill(0) + .map((_, idx) => { + return ( +
+ +
+ ) + })} +
+ ) +} + +export default function AccountsSidebar() { + const { useAccountRollup } = useAccountApi() + const activeAccountId = useQueryParam('accountId', 'number') + + const providerStatus = useProviderStatus() + + const { someConnectionsSyncing, someAccountsSyncing, connectionsSyncing, syncProgress } = + useUserAccountContext() + + const { dateRange } = useAccountContext() + + const connectionsStatus = useMemo( + () => ({ + syncing: someAccountsSyncing || someConnectionsSyncing, + pending: + connectionsSyncing.filter((connection) => connection.syncStatus === 'PENDING') + .length > 0, + }), + [someAccountsSyncing, someConnectionsSyncing, connectionsSyncing] + ) + + const { error, data } = useAccountRollup(dateRange) + + const isLoading = useMemo(() => { + if (error) { + return false + } + + if (!connectionsStatus.syncing && data) { + return false + } + + // If any connection is syncing and there are > 1 accounts, show the accounts + if (connectionsStatus.syncing && data && data.length > 0) { + return false + } + + return true + }, [data, error, connectionsStatus]) + + const [toggleState, setToggleState] = useLocalStorage<{ [key: string]: boolean }>( + 'ACCOUNTS_LIST_TOGGLE_STATE', + {} + ) + + const updateToggleState = useCallback( + (key: string, isExpanded: boolean) => { + setToggleState({ + ...toggleState, + [key]: isExpanded, + }) + }, + [toggleState, setToggleState] + ) + + if (error) { + return ( +
+ +

Unable to load accounts

+
+ ) + } + + if (!isLoading && (!data || !data.length)) { + return ( +
+ +

No accounts found

+
+ ) + } + + return ( +
+ + {syncProgress && ( + + {syncProgress.description}... +
+
+ {syncProgress.progress ? ( + + ) : ( + + )} +
+
+
+ )} +
+ {providerStatus.statusMessage && ( +
+ + + + {providerStatus.isCollapsed ? ( + <> +

Data provider service disruption

+ providerStatus.expand()}> + + + + ) : ( + <> +

{providerStatus.statusMessage}

+ providerStatus.dismiss()}> + + + + )} +
+ )} + {isLoading || !data ? ( + + ) : ( + data.map(({ key: classification, title, balances, items }) => ( + updateToggleState(title, isExpanded)} + expanded={toggleState[title] !== false} + syncing={items.some(({ items }) => items.some((a) => a.syncing))} + > + {items.map(({ key: category, title, balances, items }) => ( + updateToggleState(title, isExpanded)} + expanded={toggleState[title] !== false} + level={1} + syncing={items.some((a) => a.syncing)} + > + {items.map(({ id, name, mask, connection, balances, syncing }) => ( + + ))} + + ))} + + )) + )} +
+ ) +} + +function AccountsSidebarRow({ + label, + level = 0, + balances, + institutionName, + accountMask, + inverted = false, + syncing = false, + active = false, + url, + ...rest +}: AccordionRowProps & { + label: string + balances: { date: string; balance: SharedType.Decimal }[] + institutionName?: string | null + accountMask?: string | null + inverted?: boolean + syncing?: boolean + active?: boolean + url?: string +}) { + const startBalance = balances[0].balance + const endBalance = balances[balances.length - 1].balance + + const percentChange = NumberUtil.calculatePercentChange(startBalance, endBalance) + + let isPositive = balances.length > 1 && (endBalance as DecimalJS).gt(startBalance as DecimalJS) + if (inverted) isPositive = !isPositive + + const overlayClassName = ['!bg-gray-400', '!bg-gray-600', '!bg-gray-700'][level] + + // Hide flat lines or inifite + const hasValidValue = !percentChange.isZero() && percentChange.isFinite() + + return ( + +
+

{label}

+ + {(institutionName || accountMask) && ( +
+ {institutionName && ( + {institutionName} + )} + {accountMask && ( + +  ···· {accountMask} + + )} +
+ )} +
+ + {balances.length && ( +
+ +
+ {syncing + ? '$X,XXX,XXX' + : NumberUtil.format(endBalance, 'currency', { + minimumFractionDigits: 0, + maximumFractionDigits: 0, + })} +
+
+ {(balances.length > 1 || syncing) && hasValidValue && ( +
+ + {!syncing && ( +
+ ({ + key: date, + value: balance.toNumber(), + }))} + /> +
+ )} + + {syncing + ? '+XXX%' + : NumberUtil.format(percentChange, 'percent')} + +
+
+ )} +
+ )} + + } + /> + ) +} diff --git a/libs/client/features/src/account/PageTitle.tsx b/libs/client/features/src/account/PageTitle.tsx new file mode 100644 index 00000000000..257b7137810 --- /dev/null +++ b/libs/client/features/src/account/PageTitle.tsx @@ -0,0 +1,57 @@ +import type { SharedType } from '@maybe-finance/shared' +import { SmallDecimals, TrendBadge } from '@maybe-finance/client/shared' +import { LoadingPlaceholder } from '@maybe-finance/design-system' + +type Props = { + isLoading: boolean + title?: string + value?: string + trend?: SharedType.Trend + trendLabel?: string + trendNegative?: boolean +} + +export function PageTitle({ isLoading, title, value, trend, trendLabel, trendNegative }: Props) { + return ( +
+ Placeholder Title} + > + {title &&

{title}

} +
+ + + + + } + > +

+ +

+
+ + } + > + + {trend && ( + + )} + + +
+ ) +} diff --git a/libs/client/features/src/account/index.ts b/libs/client/features/src/account/index.ts new file mode 100644 index 00000000000..7381bd2a70a --- /dev/null +++ b/libs/client/features/src/account/index.ts @@ -0,0 +1,3 @@ +export * from './AccountMenu' +export { default as AccountSidebar } from './AccountsSidebar' +export * from './PageTitle' diff --git a/libs/client/features/src/accounts-list/Account.tsx b/libs/client/features/src/accounts-list/Account.tsx new file mode 100644 index 00000000000..b1ace80df3c --- /dev/null +++ b/libs/client/features/src/accounts-list/Account.tsx @@ -0,0 +1,163 @@ +import type { SharedType } from '@maybe-finance/shared' +import cn from 'classnames' +import { Button, Menu, Toggle, Tooltip } from '@maybe-finance/design-system' +import { useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import { NumberUtil, AccountUtil } from '@maybe-finance/shared' +import { DateTime } from 'luxon' +import { useCallback, useEffect, useMemo, useState } from 'react' +import debounce from 'lodash/debounce' + +type AccountProps = { + account: SharedType.Account + readonly?: boolean + onEdit?(): void + editLabel?: string + canDelete?: boolean + showAccountDescription?: boolean +} + +export default function Account({ + account, + readonly = false, + onEdit, + editLabel = 'Edit', + canDelete = false, + showAccountDescription = true, +}: AccountProps) { + const { useSyncAccount, useAccountBalances } = useAccountApi() + + const { deleteAccount } = useAccountContext() + + const syncAccount = useSyncAccount() + const accountBalancesQuery = useAccountBalances({ + id: account.id, + start: DateTime.now().toISODate(), + end: DateTime.now().toISODate(), + }) + + let accountTypeName = AccountUtil.getAccountTypeName(account.category, account.subcategory) + accountTypeName = accountTypeName + ? accountTypeName.charAt(0).toUpperCase() + accountTypeName.slice(1) + : null + + const renderAccountDescription = () => { + if (!showAccountDescription) return null + + if (!accountTypeName && !account.mask) return null + + return ( +
+ {accountTypeName ?? 'Account'} + {account.mask && <> ending in ···· {account.mask}} +
+ ) + } + + return ( +
  • + +
    +
    +
    {account.name}
    + {renderAccountDescription()} +
    +
    + {onEdit && ( + + + + )} + {canDelete && ( + + + + )} + {process.env.NODE_ENV === 'development' && ( + + + + + + syncAccount.mutate(account.id)} + > + Sync + + + + )} +
    +
    + {accountBalancesQuery.data ? ( + + {NumberUtil.format(accountBalancesQuery.data.today?.balance, 'currency')} + + ) : ( + ... + )} +
  • + ) +} + +function AccountToggle({ + account, + disabled, +}: { + account: SharedType.Account + disabled: boolean +}): JSX.Element { + const { useUpdateAccount } = useAccountApi() + const { mutateAsync } = useUpdateAccount() + + const [isActive, setIsActive] = useState(account.isActive) + + useEffect(() => setIsActive(account.isActive), [account.isActive]) + + const debouncedMutate = useMemo( + () => + debounce(async (checked: boolean) => { + try { + await mutateAsync({ + id: account.id, + data: { data: { isActive: checked } }, + }) + } catch (e) { + setIsActive(!checked) + } + }, 500), + [mutateAsync, account.id] + ) + + const onChange = useCallback( + (checked: boolean) => { + setIsActive(checked) + debouncedMutate(checked) + }, + [debouncedMutate] + ) + + return ( + + ) +} diff --git a/libs/client/features/src/accounts-list/AccountDevTools.tsx b/libs/client/features/src/accounts-list/AccountDevTools.tsx new file mode 100644 index 00000000000..e51c0095ddb --- /dev/null +++ b/libs/client/features/src/accounts-list/AccountDevTools.tsx @@ -0,0 +1,68 @@ +import Link from 'next/link' +import { + useAccountConnectionApi, + useInstitutionApi, + usePlaidApi, + BrowserUtil, +} from '@maybe-finance/client/shared' + +export function AccountDevTools() { + const { useSandboxQuickAdd } = usePlaidApi() + const { useDeleteAllConnections } = useAccountConnectionApi() + const { useSyncInstitutions, useDeduplicateInstitutions } = useInstitutionApi() + + const sandboxQuickAdd = useSandboxQuickAdd() + const deleteAllConnections = useDeleteAllConnections() + const syncInstitutions = useSyncInstitutions() + const deduplicateInstitutions = useDeduplicateInstitutions() + + return process.env.NODE_ENV === 'development' ? ( +
    +
    + Dev Tools +
    +

    + This section along with anything in red text will + NOT show in production and are solely for making testing easier. +

    +
    +

    Actions:

    + + + + BullMQ Dashboard + + + + +
    +
    + ) : null +} + +export default AccountDevTools diff --git a/libs/client/features/src/accounts-list/AccountGroup.tsx b/libs/client/features/src/accounts-list/AccountGroup.tsx new file mode 100644 index 00000000000..825bab70896 --- /dev/null +++ b/libs/client/features/src/accounts-list/AccountGroup.tsx @@ -0,0 +1,47 @@ +import { Disclosure } from '@headlessui/react' +import { Button } from '@maybe-finance/design-system' +import classNames from 'classnames' + +type AccountGroupProps = { + title: string + subtitle: React.ReactNode + content: React.ReactNode + menu?: React.ReactNode + footer?: React.ReactNode +} + +export function AccountGroup({ title, subtitle, content, menu, footer }: AccountGroupProps) { + return ( + + {({ open }) => ( + <> +
    +
    +

    {title}

    +

    {subtitle}

    +
    +
    + {menu} + + + +
    +
    + + +
    {content}
    +
    {footer}
    +
    + + )} +
    + ) +} diff --git a/libs/client/features/src/accounts-list/AccountGroupContainer.tsx b/libs/client/features/src/accounts-list/AccountGroupContainer.tsx new file mode 100644 index 00000000000..5160edc15fe --- /dev/null +++ b/libs/client/features/src/accounts-list/AccountGroupContainer.tsx @@ -0,0 +1,22 @@ +import type { PropsWithChildren } from 'react' + +export type AccountGroupContainerProps = PropsWithChildren<{ + title: string + subtitle?: string +}> + +export function AccountGroupContainer({ + title, + subtitle = '', + children, +}: AccountGroupContainerProps) { + return ( +
    +
    +
    {title}
    +

    {subtitle}

    +
    +
      {children}
    +
    + ) +} diff --git a/libs/client/features/src/accounts-list/ConnectedAccountGroup.tsx b/libs/client/features/src/accounts-list/ConnectedAccountGroup.tsx new file mode 100644 index 00000000000..aa2708b8c22 --- /dev/null +++ b/libs/client/features/src/accounts-list/ConnectedAccountGroup.tsx @@ -0,0 +1,279 @@ +import { useCallback } from 'react' +import { Button, Menu } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' +import Account from './Account' +import { AccountGroup } from './AccountGroup' +import { RiLinkUnlink as UnlinkIcon, RiRefreshLine, RiHistoryLine } from 'react-icons/ri' +import { + useAccountConnectionApi, + useAccountContext, + useAxiosWithAuth, + useLastUpdated, +} from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import { AiOutlineSync, AiOutlineExclamationCircle } from 'react-icons/ai' +import { RiDownloadLine } from 'react-icons/ri' +import PlaidLinkUpdateButton from './PlaidLinkUpdateButton' + +export interface ConnectedAccountGroupProps { + connection: SharedType.ConnectionWithAccounts +} + +export function ConnectedAccountGroup({ connection }: ConnectedAccountGroupProps) { + const { axios } = useAxiosWithAuth() + + const { editAccount } = useAccountContext() + + const { + useDisconnectConnection, + useSyncConnection, + usePlaidLinkUpdateCompleted, + useDeleteConnection, + useUpdateConnection, + } = useAccountConnectionApi() + + const disconnectConnection = useDisconnectConnection() + const deleteConnection = useDeleteConnection() + const syncConnection = useSyncConnection() + const updateConnection = useUpdateConnection() + const plaidLinkUpdateCompleted = usePlaidLinkUpdateCompleted(connection.id) + + const onLinkUpdateSuccess = useCallback(() => { + plaidLinkUpdateCompleted.mutate('success') + }, [plaidLinkUpdateCompleted]) + + const onLinkUpdateExit = useCallback(() => { + plaidLinkUpdateCompleted.mutate('exit') + }, [plaidLinkUpdateCompleted]) + + const hasNewAccountsAvailable = connection.plaidNewAccountsAvailable + const hasLoginError = + connection.status === 'ERROR' && + (connection.plaidConsentExpiration || + (connection.plaidError as any)?.error_code === 'ITEM_LOGIN_REQUIRED') + + const { status, message } = useAccountConnectionStatus(connection) + + return ( + 0 ? ( +
      + {connection.accounts.map((account) => ( + editAccount(account)} + editLabel="Edit" + /> + ))} +
    + ) : ( +

    + {connection.syncStatus === 'PENDING' || connection.syncStatus === 'SYNCING' + ? 'Your accounts are currently syncing. Please check back later.' + : 'No accounts found. Try syncing again.'} +

    + ) + } + menu={ + <> + {process.env.NODE_ENV === 'development' && ( + + + + + + { + axios.post( + `/connections/${connection.id}/plaid/sandbox/item-reset-login` + ) + }} + > + Reset Login + + + axios.post( + `/connections/${connection.id}/plaid/sandbox/fire-webhook` + ) + } + > + Fire Webhook + + + axios.post(`/connections/${connection.id}/sync/balances`) + } + > + Sync Balances + + + axios.post(`/connections/${connection.id}/sync/investments`) + } + > + Sync Investments + + deleteConnection.mutate(connection.id)} + > + Delete permanently + + + + )} + + + + + + } + destructive + onClick={() => disconnectConnection.mutate(connection.id)} + > + Disconnect account + + + + + } + footer={ +
    +
    {message}
    +
    + {connection.syncStatus === 'IDLE' && hasLoginError ? ( + + ) : connection.syncStatus === 'IDLE' && hasNewAccountsAvailable ? ( + + ) : null} + + {/* Provide user a fallback if their connection gets "stuck" in the syncing state */} + {connection.syncStatus !== 'IDLE' && ( + + )} + + +
    +
    + } + /> + ) +} + +function useAccountConnectionStatus(connection: SharedType.ConnectionWithAccounts): { + status?: React.ReactNode + message?: React.ReactNode +} { + const lastUpdated = useLastUpdated(DateTime.fromJSDate(connection.updatedAt)) + + switch (connection.syncStatus) { + case 'PENDING': + case 'SYNCING': { + return { + status: Syncing..., + } + } + } + + switch (connection.status) { + case 'OK': { + if (connection.plaidNewAccountsAvailable) { + return { + status: ( +
    + +

    Account update available

    +
    + ), + message: ( +
    + +

    + Since the last sync there were a few updates. Update to pull in the + latest account data. +

    +
    + ), + } + } + + return { + status: lastUpdated, + message: null, + } + } + case 'ERROR': { + const hasLoginError = + connection.plaidConsentExpiration || + (connection.plaidError as any)?.error_code === 'ITEM_LOGIN_REQUIRED' + + return { + status: ( +
    + +

    + {hasLoginError + ? 'Unable to connect your account' + : 'Syncing issue detected'} +

    +
    + ), + message: ( +
    + +

    + {hasLoginError + ? 'Please try reconnecting your account to pull in the latest account data.' + : 'Please try syncing again'} +

    +
    + ), + } + } + } + + return {} +} diff --git a/libs/client/features/src/accounts-list/DeleteConnectionDialog.tsx b/libs/client/features/src/accounts-list/DeleteConnectionDialog.tsx new file mode 100644 index 00000000000..ffc1adfb89b --- /dev/null +++ b/libs/client/features/src/accounts-list/DeleteConnectionDialog.tsx @@ -0,0 +1,48 @@ +import { useAccountConnectionApi } from '@maybe-finance/client/shared' +import { Alert, Button, Dialog } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' + +export interface DeleteConnectionDialogProps { + connection: SharedType.ConnectionWithAccounts + isOpen: boolean + onClose: () => void +} + +export function DeleteConnectionDialog({ + connection, + isOpen, + onClose, +}: DeleteConnectionDialogProps) { + const { useDeleteConnection } = useAccountConnectionApi() + + const deleteConnection = useDeleteConnection() + + return ( + + Delete account? + + + This action cannot be undone + +

    + Deleting {connection.name} will permanently + remove {connection.accounts.length} accounts{' '} + and all other related data. This will impact other views such as your net worth + dashboard. +

    +
    + + +
    +
    +
    + ) +} diff --git a/libs/client/features/src/accounts-list/DisconnectedAccountGroup.tsx b/libs/client/features/src/accounts-list/DisconnectedAccountGroup.tsx new file mode 100644 index 00000000000..038a5d5a136 --- /dev/null +++ b/libs/client/features/src/accounts-list/DisconnectedAccountGroup.tsx @@ -0,0 +1,67 @@ +import type { SharedType } from '@maybe-finance/shared' +import { Menu } from '@maybe-finance/design-system' +import Account from './Account' +import { AccountGroup } from './AccountGroup' +import { RiMore2Fill, RiLink } from 'react-icons/ri' +import { FaRegTrashAlt } from 'react-icons/fa' +import { DeleteConnectionDialog } from './DeleteConnectionDialog' +import { useState } from 'react' +import { useAccountConnectionApi, useLastUpdated } from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' + +type DisconnectedAccountGroupProps = { + connection: SharedType.ConnectionWithAccounts +} + +export function DisconnectedAccountGroup({ connection }: DisconnectedAccountGroupProps) { + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) + + const { useReconnectConnection } = useAccountConnectionApi() + const reconnect = useReconnectConnection() + + const lastUpdatedString = useLastUpdated(DateTime.fromJSDate(connection.updatedAt)) + + return ( + <> + + {connection.accounts.map((account) => ( + + ))} + + } + menu={ + + + + + + } + onClick={() => reconnect.mutate(connection.id)} + > + Reconnect account + + } + destructive + onClick={() => setDeleteDialogOpen(true)} + > + Delete permanently + + + + } + /> + + setDeleteDialogOpen(false)} + /> + + ) +} diff --git a/libs/client/features/src/accounts-list/FinicityFixConnectButton.tsx b/libs/client/features/src/accounts-list/FinicityFixConnectButton.tsx new file mode 100644 index 00000000000..46cd57f1246 --- /dev/null +++ b/libs/client/features/src/accounts-list/FinicityFixConnectButton.tsx @@ -0,0 +1,36 @@ +import { + useAccountConnectionApi, + useAccountContext, + useFinicity, +} from '@maybe-finance/client/shared' +import { Button } from '@maybe-finance/design-system' + +type FinicityFixConnectButtonProps = { + accountConnectionId: number +} + +export default function FinicityFixConnectButton({ + accountConnectionId, +}: FinicityFixConnectButtonProps) { + const { launch } = useFinicity() + const { setAccountManager } = useAccountContext() + + const { useCreateFinicityFixConnectUrl } = useAccountConnectionApi() + + const createFixConnectUrl = useCreateFinicityFixConnectUrl({ + onSuccess({ link }) { + launch(link) + setAccountManager({ view: 'idle' }) + }, + }) + + return ( + + ) +} diff --git a/libs/client/features/src/accounts-list/ManualAccountGroup.tsx b/libs/client/features/src/accounts-list/ManualAccountGroup.tsx new file mode 100644 index 00000000000..b4a29e42c30 --- /dev/null +++ b/libs/client/features/src/accounts-list/ManualAccountGroup.tsx @@ -0,0 +1,41 @@ +import type { SharedType } from '@maybe-finance/shared' +import { DateTime } from 'luxon' +import maxBy from 'lodash/maxBy' +import { useLastUpdated, useAccountContext } from '@maybe-finance/client/shared' +import Account from './Account' +import { AccountGroup } from './AccountGroup' + +type ManualAccountGroupProps = { + title: string + subtitle: string + accounts: SharedType.Account[] +} + +export function ManualAccountGroup({ title, accounts }: ManualAccountGroupProps) { + const { editAccount } = useAccountContext() + + // Use the most recently updated manual account in the group + const lastUpdatedString = useLastUpdated( + DateTime.fromJSDate(maxBy(accounts, (a) => a.updatedAt)?.updatedAt || new Date()) + ) + + return ( + + {accounts.map((account) => ( + editAccount(account)} + showAccountDescription={false} + /> + ))} + + } + /> + ) +} diff --git a/libs/client/features/src/accounts-list/PlaidLinkUpdateButton.tsx b/libs/client/features/src/accounts-list/PlaidLinkUpdateButton.tsx new file mode 100644 index 00000000000..7ee54aaee99 --- /dev/null +++ b/libs/client/features/src/accounts-list/PlaidLinkUpdateButton.tsx @@ -0,0 +1,50 @@ +import type { PlaidLinkOnSuccess, PlaidLinkOnExit } from 'react-plaid-link' +import { useAccountConnectionApi } from '@maybe-finance/client/shared' +import { Button } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' +import { useEffect } from 'react' +import { usePlaidLink } from 'react-plaid-link' + +type PlaidLinkUpdateButtonProps = { + accountConnectionId: number + onSuccess: PlaidLinkOnSuccess + onExit?: PlaidLinkOnExit + mode: SharedType.PlaidLinkUpdateMode +} + +export function PlaidLinkUpdateButton({ + accountConnectionId, + onSuccess, + onExit, + mode, +}: PlaidLinkUpdateButtonProps) { + const { useCreatePlaidLinkToken } = useAccountConnectionApi() + + const createLinkToken = useCreatePlaidLinkToken(mode) + + const token = createLinkToken.data?.token ?? null + + const { ready, open } = usePlaidLink({ + token, + onSuccess, + onExit, + }) + + useEffect(() => { + if (ready) { + open() + } + }, [ready, open]) + + return ( + + ) +} + +export default PlaidLinkUpdateButton diff --git a/libs/client/features/src/accounts-list/index.ts b/libs/client/features/src/accounts-list/index.ts new file mode 100644 index 00000000000..cc41fc67a53 --- /dev/null +++ b/libs/client/features/src/accounts-list/index.ts @@ -0,0 +1,6 @@ +export * from './Account' +export * from './AccountGroupContainer' +export * from './DisconnectedAccountGroup' +export * from './ManualAccountGroup' +export * from './AccountDevTools' +export * from './ConnectedAccountGroup' diff --git a/libs/client/features/src/accounts-manager/AccountTypeGrid.tsx b/libs/client/features/src/accounts-manager/AccountTypeGrid.tsx new file mode 100644 index 00000000000..17db16d1e04 --- /dev/null +++ b/libs/client/features/src/accounts-manager/AccountTypeGrid.tsx @@ -0,0 +1,103 @@ +import type { IconType } from 'react-icons' +import type { BoxIconVariant } from '@maybe-finance/client/shared' + +import { + RiBankLine, + RiBitCoinLine, + RiCarLine, + RiFolderLine, + RiHome2Line, + RiLineChartLine, +} from 'react-icons/ri' +import { BoxIcon } from '@maybe-finance/client/shared' + +export type AccountSelectorView = + | 'default' + | 'search' + | 'banks' + | 'brokerages' + | 'crypto' + | 'manual' + | 'property-form' + | 'vehicle-form' + +function AccountTypeGridItem({ + icon, + variant, + title, + type, + onClick, +}: { + icon: IconType + variant: BoxIconVariant + title: string + type: AccountSelectorView + onClick: (view: AccountSelectorView) => void +}) { + return ( +
    onClick(type)} + data-testid={`${type}-add-account`} + > + +

    {title}

    +
    + ) +} + +const items = [ + { + type: 'banks', + title: 'Bank account', + icon: RiBankLine, + variant: 'blue', + }, + { + type: 'crypto', + title: 'Crypto', + icon: RiBitCoinLine, + variant: 'orange', + }, + { + type: 'brokerages', + title: 'Investment', + icon: RiLineChartLine, + variant: 'teal', + }, + { + type: 'vehicle-form', + title: 'Vehicle', + icon: RiCarLine, + variant: 'grape', + }, + { + type: 'property-form', + title: 'Real estate', + icon: RiHome2Line, + variant: 'pink', + }, + { + type: 'manual', + title: 'Manual account', + icon: RiFolderLine, + variant: 'yellow', + }, +] + +export function AccountTypeGrid({ onChange }: { onChange: (view: AccountSelectorView) => void }) { + return ( +
    + {items.map((item) => ( + + ))} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/AccountTypeSelector.tsx b/libs/client/features/src/accounts-manager/AccountTypeSelector.tsx new file mode 100644 index 00000000000..2b7c81f86e5 --- /dev/null +++ b/libs/client/features/src/accounts-manager/AccountTypeSelector.tsx @@ -0,0 +1,238 @@ +import { useState, useRef, useEffect } from 'react' +import { RiFolderLine, RiHandCoinLine, RiLockLine, RiSearchLine } from 'react-icons/ri' +import maxBy from 'lodash/maxBy' +import { + BoxIcon, + useAccountContext, + useDebounce, + usePlaid, + useFinicity, +} from '@maybe-finance/client/shared' + +import { Input } from '@maybe-finance/design-system' +import InstitutionGrid from './InstitutionGrid' +import { AccountTypeGrid } from './AccountTypeGrid' +import InstitutionList, { MIN_QUERY_LENGTH } from './InstitutionList' + +const SEARCH_DEBOUNCE_MS = 300 + +export default function AccountTypeSelector({ + view, + onViewChange, +}: { + view: string + onViewChange: (view: string) => void +}) { + const { setAccountManager } = useAccountContext() + + const [searchQuery, setSearchQuery] = useState('') + const debouncedSearchQuery = useDebounce(searchQuery, SEARCH_DEBOUNCE_MS) + + const showInstitutionList = + searchQuery.length >= MIN_QUERY_LENGTH && + debouncedSearchQuery.length >= MIN_QUERY_LENGTH && + view !== 'manual' + + const { openPlaid } = usePlaid() + const { openFinicity } = useFinicity() + + const inputRef = useRef(null) + + useEffect(() => { + if (inputRef.current) { + inputRef.current.focus() + } + }, []) + + return ( +
    + {/* Search */} + {view !== 'manual' && view !== 'crypto' && ( + } + inputClassName="pl-10" + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} + ref={inputRef} + /> + )} + + {showInstitutionList && ( + { + const providerInstitution = maxBy(providers, (p) => p.rank) + if (!providerInstitution) { + alert('No provider found for institution') + return + } + + switch (providerInstitution.provider) { + case 'PLAID': + openPlaid(providerInstitution.providerId) + break + case 'FINICITY': + openFinicity(providerInstitution.providerId) + break + default: + break + } + }} + onAddManualAccountClick={() => onViewChange('manual')} + /> + )} + + {view === 'default' && !showInstitutionList && ( +
    + { + // Some actions go directly to a form while others go to a second modal view for refinement of criteria + switch (view) { + case 'property-form': + setAccountManager({ view: 'add-property', defaultValues: {} }) + break + case 'vehicle-form': + setAccountManager({ view: 'add-vehicle', defaultValues: {} }) + break + default: + // Go to a view below + onViewChange(view) + } + }} + /> +
    + + + +

    + Adding your accounts is a big step. That's why we take this + seriously. No one can access your accounts but you. Your information is + always protected and secure. +

    +
    +
    + )} + + {(view === 'banks' || view === 'brokerages' || view === 'crypto') && + !showInstitutionList && ( +
    + {view === 'crypto' && ( +

    + At the moment we don't have integrations for crypto exchanges or + assets, so for the time being you'll need to enter your portfolio + manually. +

    + )} + { + // Crypto exchanges not supported yet, go directly to add asset form + if (view === 'crypto') { + setAccountManager({ + view: 'add-asset', + defaultValues: { + name: cryptoExchangeName, + categoryUser: 'crypto', + }, + }) + + return + } + + if (!data) return + + switch (data.provider) { + case 'PLAID': + openPlaid(data.providerId) + break + case 'FINICITY': + openFinicity(data.providerId) + break + default: + break + } + }} + /> +
    + )} + + {(view === 'banks' || + view === 'brokerages' || + view === 'crypto' || + showInstitutionList) && ( +

    + Can't find your institution?{' '} + { + switch (view) { + case 'banks': + setAccountManager({ + view: 'add-asset', + defaultValues: { categoryUser: 'cash', name: 'Cash' }, + }) + break + case 'brokerages': + setAccountManager({ + view: 'add-asset', + defaultValues: { + categoryUser: 'investment', + name: 'Investment', + }, + }) + break + case 'crypto': + setAccountManager({ + view: 'add-asset', + defaultValues: { + categoryUser: 'crypto', + name: 'Cryptocurrency', + }, + }) + break + default: + onViewChange('manual') + } + }} + > + Add it manually + +

    + )} + + {view === 'manual' && ( +
    +
    + setAccountManager({ + view: 'add-asset', + defaultValues: { name: debouncedSearchQuery }, + }) + } + data-testid="manual-add-asset" + > + +

    Manual Asset

    +
    +
    + setAccountManager({ + view: 'add-liability', + defaultValues: { name: debouncedSearchQuery }, + }) + } + data-testid="manual-add-debt" + > + +

    Manual Debt

    +
    +
    + )} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/AccountValuationFormFields.tsx b/libs/client/features/src/accounts-manager/AccountValuationFormFields.tsx new file mode 100644 index 00000000000..7bffc6f531e --- /dev/null +++ b/libs/client/features/src/accounts-manager/AccountValuationFormFields.tsx @@ -0,0 +1,71 @@ +import type { AccountClassification } from '@prisma/client' + +import { Controller } from 'react-hook-form' +import { InputCurrency, DatePicker } from '@maybe-finance/design-system' +import { BrowserUtil } from '@maybe-finance/client/shared' + +export type AccountValuationFieldProps = { + control: any + classification?: AccountClassification + currentBalanceEditable?: boolean +} + +export function AccountValuationFormFields({ + control, + classification = 'asset', + currentBalanceEditable = true, +}: AccountValuationFieldProps) { + return ( + <> + ( + + )} + /> + +
    + val >= 0 }} + render={({ field, fieldState: { error } }) => ( + + )} + /> + + {currentBalanceEditable && ( + ( + + )} + /> + )} +
    + + ) +} diff --git a/libs/client/features/src/accounts-manager/AccountsManager.tsx b/libs/client/features/src/accounts-manager/AccountsManager.tsx new file mode 100644 index 00000000000..f071f48be47 --- /dev/null +++ b/libs/client/features/src/accounts-manager/AccountsManager.tsx @@ -0,0 +1,117 @@ +import { useAccountContext } from '@maybe-finance/client/shared' +import { Dialog } from '@maybe-finance/design-system' + +import AccountTypeSelector from './AccountTypeSelector' +import { AddAsset } from './asset' +import { AddLiability } from './liability' +import { AddProperty } from './property' +import { AddVehicle } from './vehicle' +import EditAccount from './EditAccount' +import { DeleteAccount } from './DeleteAccount' +import { RiArrowLeftLine } from 'react-icons/ri' +import { useEffect, useMemo, useState } from 'react' + +export function AccountsManager() { + const { accountManager: am, setAccountManager } = useAccountContext() + + const [subView, setSubView] = useState('default') + + useEffect(() => { + if (am.view === 'idle') { + setSubView('default') + } + }, [am.view]) + + const accountTitle = () => { + switch (subView) { + case 'banks': + return 'Add bank' + case 'crypto': + return 'Add crypto' + case 'brokerages': + return 'Add investment' + default: + return 'Add account' + } + } + + const view = useMemo(() => { + switch (am.view) { + case 'add-account': + return { + title: accountTitle(), + component: , + } + case 'edit-account': + return { + title: 'Edit account', + component: , + } + case 'delete-account': + return { + title: 'Delete account', + component: ( + + ), + } + case 'add-asset': + return { + title: 'Manual asset', + component: , + } + case 'add-liability': + return { + title: 'Manual debt', + component: , + } + case 'add-property': + return { + title: 'Add real estate', + component: , + } + case 'add-vehicle': + return { + title: 'Add vehicle', + component: , + } + case 'custom': + return { title: 'Custom account', component: am.component } + default: + return null + } + }, [am, subView]) + + if (!view) return null + + return ( + setAccountManager({ view: 'idle' })} + showCloseButton={am.view !== 'delete-account'} + > + +
    + {!(am.view === 'add-account' && subView === 'default') && + am.view !== 'delete-account' && ( + + )} + {view.title} +
    +
    + {view.component} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/DeleteAccount.tsx b/libs/client/features/src/accounts-manager/DeleteAccount.tsx new file mode 100644 index 00000000000..1907e9e0178 --- /dev/null +++ b/libs/client/features/src/accounts-manager/DeleteAccount.tsx @@ -0,0 +1,45 @@ +import { useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import { Button, Dialog } from '@maybe-finance/design-system' + +export interface DeleteAccountProps { + accountId: number + accountName: string + onDelete?: () => void +} + +export function DeleteAccount({ accountId, accountName, onDelete }: DeleteAccountProps) { + const { setAccountManager } = useAccountContext() + + const { useDeleteAccount } = useAccountApi() + const deleteAccount = useDeleteAccount() + + return ( +
    + +

    + Deleting {accountName} will permanently + remove this account and all other related data. This will impact other views + such as your net worth dashboard. +

    +
    + + +
    +
    +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/EditAccount.tsx b/libs/client/features/src/accounts-manager/EditAccount.tsx new file mode 100644 index 00000000000..25b9eac3f4d --- /dev/null +++ b/libs/client/features/src/accounts-manager/EditAccount.tsx @@ -0,0 +1,47 @@ +import { useAccountApi } from '@maybe-finance/client/shared' +import { EditAsset } from './asset' +import { EditLiability } from './liability' +import { EditProperty } from './property' +import { EditVehicle } from './vehicle' +import { EditConnectedAccount } from './connected' + +export default function EditAccount({ accountId }: { accountId?: number }) { + const { useAccount } = useAccountApi() + const accountQuery = useAccount(accountId!, { enabled: !!accountId }) + + if (!accountId) return null + + if (accountQuery.data && accountQuery.data.type === 'LOAN') { + return + } + + if (accountQuery.data && accountQuery.data.provider !== 'user') { + return + } + + return ( +
    + {accountQuery.data && accountQuery.data.provider === 'user' ? ( +
    + {accountQuery.data.type === 'PROPERTY' && ( + + )} + + {accountQuery.data.type === 'VEHICLE' && ( + + )} + + {accountQuery.data.type === 'OTHER_ASSET' && ( + + )} + + {['OTHER_LIABILITY', 'CREDIT'].includes(accountQuery.data.type) && ( + + )} +
    + ) : ( +

    Loading account details...

    + )} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/InstitutionGrid.tsx b/libs/client/features/src/accounts-manager/InstitutionGrid.tsx new file mode 100644 index 00000000000..7d420193bdb --- /dev/null +++ b/libs/client/features/src/accounts-manager/InstitutionGrid.tsx @@ -0,0 +1,173 @@ +import { BrowserUtil } from '@maybe-finance/client/shared' +import { useMemo } from 'react' +import Image from 'next/legacy/image' +import type { SharedType } from '@maybe-finance/shared' + +type GridImage = { + src: string + alt: string + institution?: Pick +} + +const banks: GridImage[] = [ + { + src: 'chase-bank.png', + alt: 'Chase Bank', + institution: { + provider: 'PLAID', + providerId: 'ins_56', + }, + }, + { + src: 'capital-one.png', + alt: 'Capital One Bank', + institution: { + provider: 'PLAID', + providerId: 'ins_128026', + }, + }, + { + src: 'wells-fargo.png', + alt: 'Wells Fargo Bank', + institution: { + provider: 'PLAID', + providerId: 'ins_127991', + }, + }, + { + src: 'american-express.png', + alt: 'American Express Bank', + institution: { + provider: 'PLAID', + providerId: 'ins_10', + }, + }, + { + src: 'bofa.png', + alt: 'Bank of America', + institution: { + provider: 'PLAID', + providerId: 'ins_127989', + }, + }, + { + src: 'usaa-bank.png', + alt: 'USAA Bank', + institution: { + provider: 'PLAID', + providerId: 'ins_7', + }, + }, +] + +const brokerages: GridImage[] = [ + { + src: 'robinhood.png', + alt: 'Robinhood', + institution: { + provider: 'PLAID', + providerId: 'ins_54', + }, + }, + { + src: 'fidelity.png', + alt: 'Fidelity', + institution: { + provider: 'FINICITY', + providerId: '9913', + }, + }, + { + src: 'vanguard.png', + alt: 'Vanguard', + institution: { + provider: 'FINICITY', + providerId: '3078', + }, + }, + { + src: 'wealthfront.png', + alt: 'Wealthfront', + institution: { + provider: 'PLAID', + providerId: 'ins_115617', + }, + }, + { + src: 'betterment.png', + alt: 'Betterment', + institution: { + provider: 'PLAID', + providerId: 'ins_115605', + }, + }, + { + src: 'interactive-brokers.png', + alt: 'Interactive Brokers', + institution: { + provider: 'PLAID', + providerId: 'ins_116530', + }, + }, +] + +const cryptoExchanges: GridImage[] = [ + { src: 'coinbase.png', alt: 'Coinbase' }, + { src: 'binance.png', alt: 'Binance' }, + { src: 'cash-app.png', alt: 'Cash App' }, + { src: 'kraken.png', alt: 'Kraken' }, + { src: 'crypto-dot-com.png', alt: 'Crypto Dot Com' }, + { src: 'ftx.png', alt: 'FTX Exchange' }, +] + +export default function InstitutionGrid({ + type, + onClick, +}: { + type: 'crypto' | 'banks' | 'brokerages' + onClick: ( + institution?: Pick, + cryptoExchangeName?: string + ) => void +}) { + const imageList = useMemo(() => { + switch (type) { + case 'crypto': + return cryptoExchanges + case 'brokerages': + return brokerages + case 'banks': + return banks + } + }, [type]) + + return ( +
    + {imageList.map((img) => ( + {img.alt} { + switch (type) { + case 'crypto': + onClick(undefined, img.alt) + break + case 'brokerages': + case 'banks': + onClick(img.institution) + break + default: + throw new Error('Invalid institution type') + } + }} + /> + ))} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/InstitutionList.tsx b/libs/client/features/src/accounts-manager/InstitutionList.tsx new file mode 100644 index 00000000000..2a2bb02dc39 --- /dev/null +++ b/libs/client/features/src/accounts-manager/InstitutionList.tsx @@ -0,0 +1,120 @@ +import type { SharedType } from '@maybe-finance/shared' +import { useMemo } from 'react' +import { InfiniteScroll, useInstitutionApi } from '@maybe-finance/client/shared' +import { Button, LoadingSpinner } from '@maybe-finance/design-system' + +export const MIN_QUERY_LENGTH = 2 + +export default function InstitutionList({ + searchQuery = '', + onClick, + onAddManualAccountClick, +}: { + searchQuery?: string + onClick(institution: SharedType.Institution): void + onAddManualAccountClick(): void +}) { + const { useInstitutions } = useInstitutionApi() + + const institutionsQuery = useInstitutions( + { search: searchQuery }, + { enabled: searchQuery.length >= MIN_QUERY_LENGTH } + ) + + const institutions = useMemo(() => { + if (!institutionsQuery.data?.pages) return [] + + // Flatten pages + return institutionsQuery.data.pages.reduce( + (institutions, page) => [...institutions, ...page.institutions], + [] as SharedType.Institution[] + ) + }, [institutionsQuery.data]) + + return ( +
    + institutionsQuery.fetchNextPage()} + hasMore={institutionsQuery.hasNextPage} + > +
    + {institutionsQuery?.data && ( +
      + {institutions.map((institution) => ( +
    • onClick(institution)} + > +
      +
      + {institution.logoUrl || institution.logo ? ( + {`${institution.name} + ) : ( +
      + )} +
      +
      +
      + + {institution.name} + + {institution.url && ( + + {institution.url.replace( + /^(https?:\/\/)?(www\.)?/, + '' + )} + + )} +
      +
    • + ))} +
    + )} + + {(institutionsQuery.isLoading || institutionsQuery.isFetchingNextPage) && ( +
    + +
    + )} +
    +
    + {institutionsQuery?.data && !institutionsQuery.isLoading && !institutions.length && ( +
    + + No institutions found + +

    + There were no institutions matching "{searchQuery}". Try another search + term, or add it as a manual account. +

    + +
    + )} +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/asset/AddAsset.tsx b/libs/client/features/src/accounts-manager/asset/AddAsset.tsx new file mode 100644 index 00000000000..13be44d88eb --- /dev/null +++ b/libs/client/features/src/accounts-manager/asset/AddAsset.tsx @@ -0,0 +1,41 @@ +import { + type CreateAssetFields, + useAccountApi, + useAccountContext, +} from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import AssetForm from './AssetForm' + +export function AddAsset({ defaultValues }: { defaultValues: Partial }) { + const { setAccountManager } = useAccountContext() + const { useCreateAccount } = useAccountApi() + const createAccount = useCreateAccount() + + return ( +
    + { + await createAccount.mutateAsync({ + type: 'OTHER_ASSET', + valuations: { + originalBalance, + currentBalance, + currentDate: DateTime.now().toISODate(), + }, + ...rest, + }) + + setAccountManager({ view: 'idle' }) + }} + /> +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/asset/AssetForm.tsx b/libs/client/features/src/accounts-manager/asset/AssetForm.tsx new file mode 100644 index 00000000000..b002a11b982 --- /dev/null +++ b/libs/client/features/src/accounts-manager/asset/AssetForm.tsx @@ -0,0 +1,108 @@ +import type { CreateAssetFields, UpdateAssetFields } from '@maybe-finance/client/shared' +import type { AccountType } from '@prisma/client' +import { Controller, useForm } from 'react-hook-form' +import { Button, Input, Listbox } from '@maybe-finance/design-system' +import { AccountUtil, DateUtil } from '@maybe-finance/shared' +import { AccountValuationFormFields } from '../AccountValuationFormFields' +import { useMemo } from 'react' + +type Props = + | { + mode: 'create' + accountType?: never + defaultValues: CreateAssetFields + onSubmit(data: CreateAssetFields): void + } + | { + mode: 'update' + accountType?: AccountType + defaultValues: UpdateAssetFields + onSubmit(data: UpdateAssetFields): void + } + +export default function AssetForm({ mode, defaultValues, onSubmit, accountType }: Props) { + const { register, watch, control, handleSubmit, formState } = useForm< + CreateAssetFields & UpdateAssetFields + >({ + mode: 'onChange', + defaultValues, + }) + + const { errors, isSubmitting, isValid } = formState + const [startDate] = watch(['startDate']) + const currentBalanceEditable = !startDate || !DateUtil.isToday(startDate) + const categoryList = useMemo(() => { + const { cash, investment, crypto, valuable, other } = AccountUtil.CATEGORIES + + if (mode === 'create') { + return [cash, investment, crypto, valuable, other] + } else { + return AccountUtil.CATEGORY_MAP[accountType!] + } + }, [mode, accountType]) + + return ( +
    +
    +
    Details
    +
    + +
    + +
    + { + return ( + + + {AccountUtil.CATEGORIES[field.value].plural} + + + {categoryList.map((category) => ( + + {category.plural} + + ))} + + + ) + }} + /> +
    +
    + + {mode === 'create' && ( +
    +
    Valuation
    +
    + +
    +
    + )} + + +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/asset/EditAsset.tsx b/libs/client/features/src/accounts-manager/asset/EditAsset.tsx new file mode 100644 index 00000000000..14d352dcbd0 --- /dev/null +++ b/libs/client/features/src/accounts-manager/asset/EditAsset.tsx @@ -0,0 +1,32 @@ +import type { SharedType } from '@maybe-finance/shared' +import { useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import AssetForm from './AssetForm' + +export function EditAsset({ account }: { account: SharedType.AccountDetail }) { + const { setAccountManager } = useAccountContext() + + const { useUpdateAccount } = useAccountApi() + const updateAccount = useUpdateAccount() + + return ( + { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: account.type, + ...data, + }, + }, + }) + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/asset/index.ts b/libs/client/features/src/accounts-manager/asset/index.ts new file mode 100644 index 00000000000..c8a09d62afb --- /dev/null +++ b/libs/client/features/src/accounts-manager/asset/index.ts @@ -0,0 +1,2 @@ +export * from './AddAsset' +export * from './EditAsset' diff --git a/libs/client/features/src/accounts-manager/connected/ConnectedAccountForm.tsx b/libs/client/features/src/accounts-manager/connected/ConnectedAccountForm.tsx new file mode 100644 index 00000000000..2380e5d5ccd --- /dev/null +++ b/libs/client/features/src/accounts-manager/connected/ConnectedAccountForm.tsx @@ -0,0 +1,88 @@ +import type { AccountCategory, AccountType } from '@prisma/client' + +import { Controller, useForm } from 'react-hook-form' +import { Button, DatePicker, Input, Listbox } from '@maybe-finance/design-system' +import { AccountUtil } from '@maybe-finance/shared' +import { BrowserUtil } from '@maybe-finance/client/shared' + +type FormData = { + name: string + categoryUser: AccountCategory + startDate: string | null +} + +type Props = { + accountType: AccountType + defaultValues: FormData + onSubmit(data: FormData): void +} + +export default function ConnectedAccountForm({ defaultValues, onSubmit, accountType }: Props) { + const { + control, + register, + handleSubmit, + formState: { isSubmitting, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues, + }) + + return ( +
    + + +
    + ( + + + {AccountUtil.CATEGORIES[field.value].plural} + + + {AccountUtil.CATEGORY_MAP[accountType].map((category) => ( + + {category.plural} + + ))} + + + )} + /> +
    + +
    + BrowserUtil.validateFormDate(d, { required: false }), + }} + render={({ field, fieldState: { error } }) => { + return ( + + ) + }} + /> +
    + + +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/connected/EditConnectedAccount.tsx b/libs/client/features/src/accounts-manager/connected/EditConnectedAccount.tsx new file mode 100644 index 00000000000..9767e57dcd5 --- /dev/null +++ b/libs/client/features/src/accounts-manager/connected/EditConnectedAccount.tsx @@ -0,0 +1,35 @@ +import { DateUtil, type SharedType } from '@maybe-finance/shared' +import { useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import ConnectedAccountForm from './ConnectedAccountForm' + +export function EditConnectedAccount({ account }: { account: SharedType.AccountDetail }) { + const { setAccountManager } = useAccountContext() + + const { useUpdateAccount } = useAccountApi() + const updateAccount = useUpdateAccount() + + return ( + { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: account.type, + ...data, + }, + }, + }) + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/connected/index.ts b/libs/client/features/src/accounts-manager/connected/index.ts new file mode 100644 index 00000000000..dfba817c6b4 --- /dev/null +++ b/libs/client/features/src/accounts-manager/connected/index.ts @@ -0,0 +1 @@ +export * from './EditConnectedAccount' diff --git a/libs/client/features/src/accounts-manager/index.ts b/libs/client/features/src/accounts-manager/index.ts new file mode 100644 index 00000000000..ec57ab58a43 --- /dev/null +++ b/libs/client/features/src/accounts-manager/index.ts @@ -0,0 +1 @@ +export * from './AccountsManager' diff --git a/libs/client/features/src/accounts-manager/liability/AddLiability.tsx b/libs/client/features/src/accounts-manager/liability/AddLiability.tsx new file mode 100644 index 00000000000..e97a4830ef9 --- /dev/null +++ b/libs/client/features/src/accounts-manager/liability/AddLiability.tsx @@ -0,0 +1,80 @@ +import { + type CreateLiabilityFields, + useAccountApi, + useAccountContext, +} from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import LiabilityForm from './LiabilityForm' + +export function AddLiability({ defaultValues }: { defaultValues: Partial }) { + const { setAccountManager } = useAccountContext() + const { useCreateAccount } = useAccountApi() + const createAccount = useCreateAccount() + + return ( + { + switch (categoryUser) { + case 'loan': + await createAccount.mutateAsync({ + name, + type: 'LOAN', + categoryUser: 'loan', + currentBalance, + startDate, + loanUser: { + originationDate: startDate, + originationPrincipal: originalBalance, + maturityDate, + interestRate: { + type: interestType, + rate: + interestType === 'fixed' ? interestRate! / 100 : undefined, + }, + loanDetail: { + type: loanType, + }, + }, + }) + break + default: + await createAccount.mutateAsync({ + type: categoryUser === 'credit' ? 'CREDIT' : 'OTHER_LIABILITY', + categoryUser, + name, + startDate, + valuations: { + originalBalance, + currentBalance, + currentDate: DateTime.now().toISODate(), + }, + }) + } + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/liability/EditLiability.tsx b/libs/client/features/src/accounts-manager/liability/EditLiability.tsx new file mode 100644 index 00000000000..4bec06cc3be --- /dev/null +++ b/libs/client/features/src/accounts-manager/liability/EditLiability.tsx @@ -0,0 +1,96 @@ +import { DateUtil, type SharedType } from '@maybe-finance/shared' +import { useAccountApi, useAccountContext } from '@maybe-finance/client/shared' +import LiabilityForm from './LiabilityForm' +import { DateTime } from 'luxon' + +export function EditLiability({ account }: { account: SharedType.AccountDetail }) { + const { setAccountManager } = useAccountContext() + + const { useUpdateAccount } = useAccountApi() + const updateAccount = useUpdateAccount() + + const defaultLoanValues = { + maturityDate: account.loan?.maturityDate ?? '', + interestRate: + account.loan?.interestRate.type === 'fixed' + ? account.loan.interestRate.rate + ? account.loan.interestRate.rate * 100 + : null + : null, + loanType: account.loan?.loanDetail.type ?? null, + interestType: account.loan?.interestRate.type ?? null, + originalBalance: account.loan?.originationPrincipal ?? null, + currentBalance: account.currentBalance?.toNumber() ?? null, + startDate: DateUtil.dateTransform(account.startDate), + } + + return ( + { + switch (categoryUser) { + case 'loan': { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: 'LOAN', + name, + categoryUser, + currentBalance, + startDate, + loanUser: { + originationDate: startDate, + originationPrincipal: originalBalance, + maturityDate, + interestRate: { + type: interestType, + rate: + interestType === 'fixed' + ? interestRate! / 100 + : undefined, + }, + loanDetail: { type: loanType }, + }, + }, + }, + }) + break + } + default: { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: categoryUser === 'credit' ? 'CREDIT' : 'OTHER_LIABILITY', + categoryUser, + name, + }, + }, + }) + } + } + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/liability/LiabilityForm.tsx b/libs/client/features/src/accounts-manager/liability/LiabilityForm.tsx new file mode 100644 index 00000000000..8579f88ac2b --- /dev/null +++ b/libs/client/features/src/accounts-manager/liability/LiabilityForm.tsx @@ -0,0 +1,264 @@ +import type { AccountType } from '@prisma/client' +import { Controller, useForm } from 'react-hook-form' +import { Button, Input, Listbox } from '@maybe-finance/design-system' +import { AccountUtil, DateUtil } from '@maybe-finance/shared' +import { AccountValuationFormFields } from '../AccountValuationFormFields' +import type { CreateLiabilityFields, UpdateLiabilityFields } from '@maybe-finance/client/shared' +import { NumericFormat } from 'react-number-format' +import { DateTime } from 'luxon' + +const loanTypes = [ + { + label: 'Home Loan', + value: 'mortgage', + }, + { + label: 'Student Loan', + value: 'student', + }, + { + label: 'Other Loan', + value: 'other', + }, +] + +const loanInterestTypes = [ + { + label: 'Fixed', + value: 'fixed', + }, + { + label: 'Variable', + value: 'variable', + }, + { + label: 'Adjustable Rate Mortgage', + value: 'arm', + }, +] + +type Props = + | { + mode: 'create' + accountType?: never + defaultValues: CreateLiabilityFields + onSubmit(data: CreateLiabilityFields): void + } + | { + mode: 'update' + accountType: AccountType + defaultValues: UpdateLiabilityFields + onSubmit(data: UpdateLiabilityFields): void + } + +export default function LiabilityForm({ mode, defaultValues, onSubmit, accountType }: Props) { + const { + register, + watch, + control, + handleSubmit, + formState: { errors, isSubmitting, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues, + }) + + const [startDate, interestType, maturityDate, categoryUser] = watch([ + 'startDate', + 'interestType', + 'maturityDate', + 'categoryUser', + ]) + + const currentBalanceEditable = !DateUtil.isToday(startDate) + + const unroundedTerm = + maturityDate && startDate + ? DateUtil.datetimeTransform(maturityDate) + .diff(DateUtil.datetimeTransform(startDate), 'months') + .toObject().months + : null + + const loanTerm = unroundedTerm ? Math.round(unroundedTerm) : null + + // If in update mode, certain categories cannot be changed (e.g. cannot change from "other" to a "loan" after account has been created) + const categoryList = + mode === 'create' ? AccountUtil.LIABILITY_CATEGORIES : AccountUtil.CATEGORY_MAP[accountType] + + return ( +
    +
    Details
    + +
    + + + ( + + + {AccountUtil.CATEGORIES[field.value].plural} + + + {categoryList.map((category) => ( + + {category.plural} + + ))} + + + )} + /> +
    + + {categoryUser === 'loan' &&
    Loan Terms
    } + +
    + {categoryUser === 'loan' && ( + <> + ( + + + {loanTypes.find((t) => t.value === field.value)?.label} + + + {loanTypes.map((type) => ( + + {type.label} + + ))} + + + )} + /> + + ( + + + { + loanInterestTypes.find((t) => t.value === field.value) + ?.label + } + + + {loanInterestTypes.map((type) => ( + + {type.label} + + ))} + + + )} + /> + + )} + + {(mode === 'create' || (mode === 'update' && categoryUser === 'loan')) && ( +
    + {categoryUser !== 'loan' && ( +
    Balance
    + )} + +
    + )} + + {categoryUser === 'loan' && ( + <> +
    + ( + months + } + placeholder="360" + allowNegative={false} + value={loanTerm} + onValueChange={(value) => { + const newMaturityDate = DateUtil.datetimeTransform( + startDate + ) + ?.plus({ months: value.floatValue }) + .toISODate() + + if (newMaturityDate) { + field.onChange(newMaturityDate) + } + }} + /> + )} + /> + + {interestType === 'fixed' && ( + !val || val >= 0 }} + render={({ field }) => ( + { + field.onChange(value.value) + }} + /> + )} + /> + )} +
    + + {interestType !== 'fixed' && ( +

    + We are still working on full support for non-standard loan terms. + Your details will be saved, but your metrics shown will be limited. +

    + )} + + )} +
    + + +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/liability/index.ts b/libs/client/features/src/accounts-manager/liability/index.ts new file mode 100644 index 00000000000..648ca9bd3ec --- /dev/null +++ b/libs/client/features/src/accounts-manager/liability/index.ts @@ -0,0 +1,2 @@ +export * from './AddLiability' +export * from './EditLiability' diff --git a/libs/client/features/src/accounts-manager/property/AddProperty.tsx b/libs/client/features/src/accounts-manager/property/AddProperty.tsx new file mode 100644 index 00000000000..880b69159cd --- /dev/null +++ b/libs/client/features/src/accounts-manager/property/AddProperty.tsx @@ -0,0 +1,61 @@ +import { + useAccountContext, + useAccountApi, + type CreatePropertyFields, +} from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import PropertyForm from './PropertyForm' + +export function AddProperty({ defaultValues }: { defaultValues: Partial }) { + const { setAccountManager } = useAccountContext() + const { useCreateAccount } = useAccountApi() + const createAccount = useCreateAccount() + + return ( +
    + { + await createAccount.mutateAsync({ + type: 'PROPERTY', + categoryUser: 'property', + name: line1, + startDate, + valuations: { + originalBalance, + currentBalance, + currentDate: DateTime.now().toISODate(), + }, + propertyMeta: { + address: { + line1, + city, + state, + zip, + }, + }, + }) + + setAccountManager({ view: 'idle' }) + }} + /> +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/property/EditProperty.tsx b/libs/client/features/src/accounts-manager/property/EditProperty.tsx new file mode 100644 index 00000000000..a806c727770 --- /dev/null +++ b/libs/client/features/src/accounts-manager/property/EditProperty.tsx @@ -0,0 +1,45 @@ +import type { SharedType } from '@maybe-finance/shared' +import { + type UpdatePropertyFields, + useAccountApi, + useAccountContext, +} from '@maybe-finance/client/shared' +import PropertyForm from './PropertyForm' + +export function EditProperty({ account }: { account: SharedType.AccountDetail }) { + const { setAccountManager } = useAccountContext() + + const { useUpdateAccount } = useAccountApi() + const updateAccount = useUpdateAccount() + + return ( + { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: account.type, + categoryUser: 'property', + name: line1, + propertyMeta: { + address: { + line1, + city, + state, + zip, + }, + }, + ...rest, + }, + }, + }) + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/property/PropertyForm.tsx b/libs/client/features/src/accounts-manager/property/PropertyForm.tsx new file mode 100644 index 00000000000..7506c670a7e --- /dev/null +++ b/libs/client/features/src/accounts-manager/property/PropertyForm.tsx @@ -0,0 +1,99 @@ +import type { CreatePropertyFields, UpdatePropertyFields } from '@maybe-finance/client/shared' +import { Button, Input } from '@maybe-finance/design-system' +import { DateUtil } from '@maybe-finance/shared' +import { useForm } from 'react-hook-form' +import { AccountValuationFormFields } from '../AccountValuationFormFields' + +type Props = + | { + mode: 'create' + defaultValues: CreatePropertyFields + onSubmit(data: CreatePropertyFields): void + } + | { + mode: 'update' + defaultValues: UpdatePropertyFields + onSubmit(data: UpdatePropertyFields): void + } + +export default function PropertyForm({ mode, defaultValues, onSubmit }: Props) { + const { + register, + control, + handleSubmit, + watch, + formState: { errors, isSubmitting, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues, + }) + + const startDate = watch('startDate') + const currentBalanceEditable = !DateUtil.isToday(startDate) + + return ( +
    +
    +
    Location
    +
    + + + + + + +
    + + + +
    +
    +
    + + {mode === 'create' && ( +
    +
    Valuation
    +
    + +
    +
    + )} + + +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/property/index.ts b/libs/client/features/src/accounts-manager/property/index.ts new file mode 100644 index 00000000000..03aad0974f6 --- /dev/null +++ b/libs/client/features/src/accounts-manager/property/index.ts @@ -0,0 +1,2 @@ +export * from './AddProperty' +export * from './EditProperty' diff --git a/libs/client/features/src/accounts-manager/vehicle/AddVehicle.tsx b/libs/client/features/src/accounts-manager/vehicle/AddVehicle.tsx new file mode 100644 index 00000000000..a5a8c88d6a2 --- /dev/null +++ b/libs/client/features/src/accounts-manager/vehicle/AddVehicle.tsx @@ -0,0 +1,56 @@ +import { + type CreateVehicleFields, + useAccountApi, + useAccountContext, +} from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import VehicleForm from './VehicleForm' + +export function AddVehicle({ defaultValues }: { defaultValues: Partial }) { + const { setAccountManager } = useAccountContext() + const { useCreateAccount } = useAccountApi() + const createAccount = useCreateAccount() + + return ( +
    + { + await createAccount.mutateAsync({ + type: 'VEHICLE', + categoryUser: 'vehicle', + valuations: { + originalBalance, + currentBalance, + currentDate: DateTime.now().toISODate(), + }, + vehicleMeta: { + make, + model, + year: parseInt(year), + }, + name: `${make} ${model}`, + startDate, + }) + + setAccountManager({ view: 'idle' }) + }} + /> +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/vehicle/EditVehicle.tsx b/libs/client/features/src/accounts-manager/vehicle/EditVehicle.tsx new file mode 100644 index 00000000000..50f82ee0f83 --- /dev/null +++ b/libs/client/features/src/accounts-manager/vehicle/EditVehicle.tsx @@ -0,0 +1,42 @@ +import type { SharedType } from '@maybe-finance/shared' +import { + type UpdateVehicleFields, + useAccountApi, + useAccountContext, +} from '@maybe-finance/client/shared' +import VehicleForm from './VehicleForm' + +export function EditVehicle({ account }: { account: SharedType.AccountDetail }) { + const { setAccountManager } = useAccountContext() + + const { useUpdateAccount } = useAccountApi() + const updateAccount = useUpdateAccount() + + return ( + { + await updateAccount.mutateAsync({ + id: account.id, + data: { + provider: account.provider, + data: { + type: account.type, + categoryUser: 'vehicle', + name: `${make} ${model}`, + vehicleMeta: { + make, + model, + year: parseInt(year), + }, + ...rest, + }, + }, + }) + + setAccountManager({ view: 'idle' }) + }} + /> + ) +} diff --git a/libs/client/features/src/accounts-manager/vehicle/VehicleForm.tsx b/libs/client/features/src/accounts-manager/vehicle/VehicleForm.tsx new file mode 100644 index 00000000000..8ca790e7242 --- /dev/null +++ b/libs/client/features/src/accounts-manager/vehicle/VehicleForm.tsx @@ -0,0 +1,93 @@ +import type { CreateVehicleFields, UpdateVehicleFields } from '@maybe-finance/client/shared' +import { Button, Input } from '@maybe-finance/design-system' +import { DateUtil } from '@maybe-finance/shared' +import { useForm } from 'react-hook-form' +import { AccountValuationFormFields } from '../AccountValuationFormFields' + +type Props = + | { + mode: 'create' + defaultValues: CreateVehicleFields + onSubmit(data: CreateVehicleFields): void + } + | { + mode: 'update' + defaultValues: UpdateVehicleFields + onSubmit(data: UpdateVehicleFields): void + } + +export default function VehicleForm({ mode, defaultValues, onSubmit }: Props) { + const { + register, + control, + handleSubmit, + watch, + formState: { errors, isSubmitting, isValid }, + } = useForm({ + mode: 'onChange', + defaultValues, + }) + + const startDate = watch('startDate') + const currentBalanceEditable = !startDate || !DateUtil.isToday(startDate) + + return ( +
    +
    +
    Details
    +
    + + + + + + v != null && + parseInt(v) > 1800 && + parseInt(v) < new Date().getFullYear() + 2, + })} + /> +
    +
    + + {mode === 'create' && ( +
    +
    Valuation
    +
    + +
    +
    + )} + + +
    + ) +} diff --git a/libs/client/features/src/accounts-manager/vehicle/index.ts b/libs/client/features/src/accounts-manager/vehicle/index.ts new file mode 100644 index 00000000000..49497498bfd --- /dev/null +++ b/libs/client/features/src/accounts-manager/vehicle/index.ts @@ -0,0 +1,2 @@ +export * from './AddVehicle' +export * from './EditVehicle' diff --git a/libs/client/features/src/ask-the-advisor/AdvisorCard.tsx b/libs/client/features/src/ask-the-advisor/AdvisorCard.tsx new file mode 100644 index 00000000000..fc1e9826902 --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/AdvisorCard.tsx @@ -0,0 +1,108 @@ +import type { SharedType } from '@maybe-finance/shared' +import { Badge } from '@maybe-finance/design-system' +import classNames from 'classnames' +import upperFirst from 'lodash/upperFirst' +import { RiUserSearchLine } from 'react-icons/ri' + +export type AdvisorCardProps = { + advisor: SharedType.AdvisorProfile + status: 'online' | 'offline' + mode?: 'default' | 'wide' | 'wide-standalone' +} + +export function AdvisorCard({ mode = 'default', ...props }: AdvisorCardProps) { + return mode === 'default' ? ( + + ) : ( + + ) +} + +export function NoAdvisorCardDesktop({ + mode = 'default', +}: { + mode?: 'default' | 'wide-standalone' +}) { + return ( +
    +

    Assigned advisor

    +
    + +
    +

    + Currently finding you an advisor who can best answer your question... +

    + + {/* TODO - add this when we enable email support */} + {/*
    + + We‘ll email you once we find a match +
    */} +
    + ) +} + +function AdvisorCardDesktop({ status, advisor }: AdvisorCardProps) { + return ( +
    +

    Assigned advisor

    + +
    +
    + {`${advisor.fullName}-advisor-avatar`} +
    + +

    {advisor.fullName}

    +

    {advisor.title}

    +
    + ) +} + +function AdvisorCardWide({ status, advisor, mode }: AdvisorCardProps) { + return ( +
    +

    Assigned advisor

    + +
    +
    + {`${advisor.fullName}-advisor-avatar`} +
    + +
    +
    + {advisor.fullName} + +
    {' '} + {upperFirst(status)} +
    +
    +

    {advisor.title}

    +
    +
    +
    + ) +} diff --git a/libs/client/features/src/ask-the-advisor/Conversation.tsx b/libs/client/features/src/ask-the-advisor/Conversation.tsx new file mode 100644 index 00000000000..f35f54e9fe7 --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/Conversation.tsx @@ -0,0 +1,273 @@ +import type { SharedType } from '@maybe-finance/shared' +import { Attachment, RichText, type BoxIconVariant } from '@maybe-finance/client/shared' +import { AudioPlayer, VideoPlayer } from '@maybe-finance/client/shared' +import { BoxIcon, useLastUpdated } from '@maybe-finance/client/shared' +import { + RiLockLine, + RiNotificationBadgeLine, + RiQuestionnaireLine, + RiUserSearchLine, +} from 'react-icons/ri' +import { createContext, useContext, useMemo } from 'react' +import classNames from 'classnames' + +export type ConversationProps = { + conversation: SharedType.ConversationWithDetail +} + +type ConversationContextType = { + conversation: Omit + primaryAdvisor?: SharedType.AdvisorProfile + secondaryAdvisors?: SharedType.AdvisorProfile[] +} + +export const ConversationContext = createContext(undefined) + +export const useConversation = () => { + const context = useContext(ConversationContext) + if (context === undefined) { + throw new Error('useConversation must be used within a ConversationProvider') + } + return context +} + +function Message({ + message, + isFirst, + showBorder, +}: { + message: SharedType.ConversationWithDetail['messages'][0] + showBorder: boolean + isFirst: boolean +}) { + const lastUpdated = useLastUpdated(message.updatedAt, false) + const advisor = message.user?.advisor + + const messageDetail = useMemo(() => { + const variant = advisor ? 'indigo' : 'cyan' + + switch (message.type) { + case 'text': + return { + title: isFirst + ? 'Submitted question' + : advisor + ? `${advisor.fullName} left a message` + : 'You left a message', + icon: RiQuestionnaireLine, + variant, + body: ( +
    + + {message.mediaSrc && } +
    + ), + } + case 'video': + return { + title: advisor + ? `${advisor.fullName} left a video message` + : 'You left a video message', + icon: RiNotificationBadgeLine, + variant, + body: ( +
    + + +
    + ), + } + case 'audio': + return { + title: advisor + ? `${advisor.fullName} left an audio message` + : 'You left an audio message', + icon: RiNotificationBadgeLine, + variant, + body: ( +
    + + +
    + ), + } + default: + throw new Error('Invalid message type: ' + message.type) + } + }, [message, advisor, isFirst]) + + return ( +
    + {/* Icon and vertical line */} +
    + {advisor ? ( + advisor-avatar + ) : ( + + )} + + {showBorder && ( +
    + +
    + )} +
    + +
    +

    + {messageDetail.title} + · {lastUpdated} +

    + + {messageDetail.body} +
    +
    + ) +} + +function Update({ update, showBorder }: { update: TimelineUpdate['update']; showBorder: boolean }) { + const lastUpdated = useLastUpdated(update.timestamp, false) + + const updateDetails = useMemo(() => { + switch (update.type) { + case 'advisor-assigned': + return { + icon: RiUserSearchLine, + variant: 'grape', + title: 'Advisor assigned, question in review', + message: + 'An advisor has been assigned to this question. You should receive an answer shortly.', + } + case 'conversation-closed': + return { + icon: RiLockLine, + variant: 'red', + title: 'Conversation closed', + message: 'This conversation has been closed and locked for new responses.', + } + default: + throw new Error('Invalid update type: ' + update.type) + } + }, [update.type]) + + return ( +
    + {/* Icon and vertical line */} +
    + + + {showBorder && ( +
    + +
    + )} +
    + +
    +

    + {updateDetails.title} + · {lastUpdated} +

    + +

    {updateDetails.message}

    +
    +
    + ) +} + +type TimelineUpdate = { + type: 'update' + update: { + type: 'advisor-assigned' | 'conversation-closed' + timestamp: Date + } +} + +type TimelineMessage = { + type: 'message' + message: SharedType.ConversationWithDetail['messages'][0] +} + +type TimelineItem = TimelineMessage | TimelineUpdate + +export function Conversation({ conversation }: ConversationProps) { + const primaryAdvisor = conversation.advisors?.[0]?.advisor + const secondaryAdvisors = + conversation.advisors.length > 1 + ? conversation.advisors.slice(1).map((a) => a?.advisor) + : [] + + const timeline = useMemo(() => { + const updates: TimelineUpdate[] = [] + + if (conversation.status === 'closed') { + updates.push({ + type: 'update', + update: { + type: 'conversation-closed', + timestamp: conversation.updatedAt, + }, + }) + } + + if (primaryAdvisor) { + updates.push({ + type: 'update', + update: { + type: 'advisor-assigned', + timestamp: conversation.advisors?.[0]?.createdAt, + }, + }) + } + + const messages: TimelineMessage[] = conversation.messages.map((message) => ({ + type: 'message', + message, + })) + + // Merges messages and events in reverse chronological order + return [...messages, ...updates].sort((a, b) => { + const aTime = a.type === 'message' ? a.message.createdAt : a.update.timestamp + const bTime = b.type === 'message' ? b.message.createdAt : b.update.timestamp + + return bTime.valueOf() - aTime.valueOf() + }) + }, [conversation, primaryAdvisor]) + + return ( + +
    + {timeline.map((item, idx) => + item.type === 'message' ? ( + + ) : ( + + ) + )} +
    +
    + ) +} diff --git a/libs/client/features/src/ask-the-advisor/ConversationCard.tsx b/libs/client/features/src/ask-the-advisor/ConversationCard.tsx new file mode 100644 index 00000000000..8af9266c24e --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/ConversationCard.tsx @@ -0,0 +1,76 @@ +import type { SharedType } from '@maybe-finance/shared' +import { useRichTextPreview } from '@maybe-finance/client/shared' +import { DateTime } from 'luxon' +import { useRouter } from 'next/router' +import { RiCheckboxMultipleLine, RiFileSearchLine, RiUserSearchLine } from 'react-icons/ri' +import { AdvisorCard } from './AdvisorCard' +import { ConversationMenu } from './ConversationMenu' +import { QuestionTag } from './QuestionTag' + +type ConversationCardProps = { + conversation: SharedType.ConversationWithMessageSummary +} + +export function ConversationCard({ conversation }: ConversationCardProps) { + const messagePreview = useRichTextPreview( + conversation.lastMessage?.userId === conversation.userId + ? conversation.lastMessage.body + : null + ) + + // For now, keep advisors online during business hours until we implement a live status indicator + const status = DateTime.now().hour >= 8 && DateTime.now().hour <= 16 ? 'online' : 'offline' + const assignedAdvisor = conversation.advisors?.[0]?.advisor + + const router = useRouter() + + return ( +
    router.push(`/ask-the-advisor/${conversation.id}`)} + > +
    + {conversation.status === 'closed' ? ( + + ) : conversation.advisors.length > 0 ? ( + + ) : ( + + )} + + {conversation.status !== 'closed' && ( +
    e.stopPropagation()}> + +
    + )} +
    +
    +
    {conversation.title}
    + {messagePreview && ( +

    {messagePreview}

    + )} +
    + {assignedAdvisor ? ( + + ) : ( +
    +

    + You will see your advisor here when we match them to your question ... +

    +
    + )} +
    + ) +} diff --git a/libs/client/features/src/ask-the-advisor/ConversationMenu.tsx b/libs/client/features/src/ask-the-advisor/ConversationMenu.tsx new file mode 100644 index 00000000000..abfd7c19ebe --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/ConversationMenu.tsx @@ -0,0 +1,42 @@ +import type { Conversation } from '@prisma/client' +import { useConversationApi } from '@maybe-finance/client/shared' +import { Menu } from '@maybe-finance/design-system' +import { RiCheckDoubleLine } from 'react-icons/ri' + +type Props = { + conversationId: Conversation['id'] +} + +export function ConversationMenu({ conversationId }: Props) { + const { useUpdateConversation, useSandbox } = useConversationApi() + const sandbox = useSandbox() + const update = useUpdateConversation() + + return ( + + + + + + } + onClick={() => { + update.mutate({ id: conversationId, data: { status: 'closed' } }) + }} + > + Mark as complete + + + {/* Dev only utils */} + {process.env.NODE_ENV === 'development' && ( + sandbox.mutate({ action: 'assign-advisor', conversationId })} + > + DEV ONLY: Assign advisor + + )} + + + ) +} diff --git a/libs/client/features/src/ask-the-advisor/DevOnlyMenu.tsx b/libs/client/features/src/ask-the-advisor/DevOnlyMenu.tsx new file mode 100644 index 00000000000..fd1786a5c8d --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/DevOnlyMenu.tsx @@ -0,0 +1,33 @@ +import { useConversationApi, useUserApi } from '@maybe-finance/client/shared' + +export function DevOnlyMenu() { + const { useSendAgreementsEmail } = useUserApi() + const updateAgreements = useSendAgreementsEmail() + + const { useSandbox } = useConversationApi() + const sandbox = useSandbox() + + return ( +
    +

    Dev only menu

    +
    + + +
    +
    + ) +} diff --git a/libs/client/features/src/ask-the-advisor/InlineQuestionCardGroup.tsx b/libs/client/features/src/ask-the-advisor/InlineQuestionCardGroup.tsx new file mode 100644 index 00000000000..baa4ec68b36 --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/InlineQuestionCardGroup.tsx @@ -0,0 +1,93 @@ +import { useState } from 'react' +import type { IconType } from 'react-icons' +import { RiCloseFill } from 'react-icons/ri' +import AnimateHeight from 'react-animate-height' +import { NewConversationDialog } from './NewConversationDialog' +import { useLocalStorage, useUserApi } from '@maybe-finance/client/shared' +import { Button } from '@maybe-finance/design-system' +import type { Account, Plan } from '@prisma/client' +import Link from 'next/link' + +type InlineQuestion = { + icon: IconType + title: string + description?: string +} + +type InlineQuestionCardGroupProps = { + id: string + questions: InlineQuestion[] + heading?: string + subheading?: string + accountId?: Account['id'] + planId?: Plan['id'] + className?: string +} + +export function InlineQuestionCardGroup({ + id, + questions, + heading, + subheading, + accountId, + planId, + className, +}: InlineQuestionCardGroupProps) { + const { useSubscription } = useUserApi() + const subscription = useSubscription() + + const [selectedQuestion, setSelectedQuestion] = useState(null) + + return ( + <> +
    + {(heading || subheading) && ( +
    + {heading &&
    {heading}
    } + {subheading &&

    {subheading}

    } +
    + )} +
    + {questions.map(({ icon: Icon, title, description }, idx) => ( +
    + +
    + {title} + {description &&

    {description}

    } +
    +
    + {!subscription.data?.subscribed ? ( + + + + ) : ( + + )} +
    +
    + ))} +
    +
    + + setSelectedQuestion(null)} + title={selectedQuestion ?? ''} + accountId={accountId} + planId={planId} + /> + + ) +} diff --git a/libs/client/features/src/ask-the-advisor/NewConversationDialog.tsx b/libs/client/features/src/ask-the-advisor/NewConversationDialog.tsx new file mode 100644 index 00000000000..57c37c9e429 --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/NewConversationDialog.tsx @@ -0,0 +1,141 @@ +import { useRouter } from 'next/router' +import { useForm } from 'react-hook-form' +import classNames from 'classnames' +import toast from 'react-hot-toast' +import { EditorContent, useEditor } from '@tiptap/react' +import Placeholder from '@tiptap/extension-placeholder' +import StarterKit from '@tiptap/starter-kit' +import type { Account, Plan } from '@prisma/client' +import { useConversationApi } from '@maybe-finance/client/shared' +import { Button, DialogV2, Input } from '@maybe-finance/design-system' + +type NewConversationDialogProps = { + title?: string + open: boolean + onClose: () => void + accountId?: Account['id'] + planId?: Plan['id'] +} + +export function NewConversationDialog({ + title, + open, + onClose, + accountId, + planId, +}: NewConversationDialogProps) { + const router = useRouter() + const { useCreateConversation } = useConversationApi() + + const createConversation = useCreateConversation({ + onSuccess(conversation) { + toast.success(`Question submitted!`) + + router.push(`/ask-the-advisor/${conversation.id}`) + }, + }) + + return ( + + {open && ( + + createConversation.mutateAsync({ + title, + initialMessage: body + ? { + type: 'text', // TODO: handle audio and video + body, + } + : undefined, + accountId, + planId, + }) + } + /> + )} + + ) +} + +function NewConversationForm({ + initialValues, + onSubmit, +}: { + initialValues?: Partial<{ title: string }> + onSubmit(data: { title: string; body: string | null }): Promise +}) { + // new question form state + const { register, handleSubmit, formState } = useForm<{ title: string }>({ + defaultValues: initialValues, + }) + + const editor = useEditor( + { + extensions: [ + StarterKit.configure({ + heading: { + levels: [1], + }, + }), + Placeholder.configure({ + placeholder: + 'e.g. I have a spouse and 2 children, so any advice I receive will need to take that into consideration', + emptyEditorClass: 'placeholder text-gray-100 caret-white', + }), + ], + editorProps: { + attributes: { + class: 'flex-1 prose prose-light prose-sm dark:prose-invert prose-headings:text-lg leading-tight focus:outline-none', + }, + }, + }, + [] + ) + + return ( +
    + onSubmit({ + title, + body: !editor || editor.isEmpty ? null : editor.getHTML(), + }) + )} + > +

    + Feel free to add more context to your question to make sure the advisor has the full + picture before answering. +

    + +
    + +
    + +
    + +
    + +

    + This is optional but it helps our advisors out. +

    + + +
    + ) +} diff --git a/libs/client/features/src/ask-the-advisor/OnboardingOverlay.tsx b/libs/client/features/src/ask-the-advisor/OnboardingOverlay.tsx new file mode 100644 index 00000000000..6aea596e5cc --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/OnboardingOverlay.tsx @@ -0,0 +1,33 @@ +import Link from 'next/link' +import { RiDraftLine } from 'react-icons/ri' +import { Button } from '@maybe-finance/design-system' +import { BlurredContentOverlay, useUserApi } from '@maybe-finance/client/shared' +import { useRouter } from 'next/router' + +export function OnboardingOverlay() { + const router = useRouter() + const { useProfile, useSubscription } = useUserApi() + + const userProfile = useProfile() + const subscription = useSubscription() + + const requiresOnboarding = + !userProfile.isLoading && + (!userProfile.data?.goals.length || !userProfile.data?.riskAnswers.length) + + return requiresOnboarding && subscription.data?.subscribed ? ( + +

    + We need to ask a few questions to get context around your goals and risk tolerance, + both of which help our advisors give you the best advice. It'll take less than 60 + seconds. +

    + + + +
    + ) : null +} diff --git a/libs/client/features/src/ask-the-advisor/QuestionCard.tsx b/libs/client/features/src/ask-the-advisor/QuestionCard.tsx new file mode 100644 index 00000000000..2282ec9ebf3 --- /dev/null +++ b/libs/client/features/src/ask-the-advisor/QuestionCard.tsx @@ -0,0 +1,147 @@ +import type { ReactNode } from 'react' +import { Button, FractionalCircle } from '@maybe-finance/design-system' +import classNames from 'classnames' +import { useForm } from 'react-hook-form' +import { + RiAlertLine, + RiCoinLine, + RiLineChartLine, + RiOpenArmLine, + RiParentLine, + RiPercentLine, + RiScales3Line, + RiWallet3Line, +} from 'react-icons/ri' +import { QuestionTag } from './QuestionTag' + +export type QuestionCategory = + | 'tax' + | 'portfolio' + | 'budgeting' + | 'debt' + | 'risk' + | 'retirement' + | 'saving' + | 'estate' + +type QuestionTag = { + key: QuestionCategory + tag: ReactNode +} + +const questions: QuestionTag[] = [ + { + key: 'tax', + tag: , + }, + { + key: 'portfolio', + tag: ( + + ), + }, + { + key: 'budgeting', + tag: , + }, + { + key: 'debt', + tag: , + }, + { + key: 'retirement', + tag: , + }, + { + key: 'risk', + tag: , + }, + { + key: 'saving', + tag: , + }, + { + key: 'estate', + tag: , + }, +] + +type Props = { + title: string + subtitle: string + category: QuestionCategory + onClick(): void +} + +export function QuestionCard({ title, subtitle, category, onClick }: Props) { + return ( +
    +
    {title}
    +

    {subtitle}

    + {questions.find((q) => q.key === category)?.tag} +
    + ) +} + +type QuestionFormData = { + question: string +} + +type QuestionCardFormProps = { + onSubmit(data: QuestionFormData): void +} + +export function QuestionCardForm({ onSubmit }: QuestionCardFormProps) { + const { + register, + handleSubmit, + watch, + formState: { isValid, isDirty }, + } = useForm({ + mode: 'onChange', + }) + + const question = watch('question') + + return ( +
    +
    Write your own question
    +
    + + +
    + + + ) +} diff --git a/libs/client/shared/src/components/dialogs/NonUSDDialog.tsx b/libs/client/shared/src/components/dialogs/NonUSDDialog.tsx new file mode 100644 index 00000000000..74c4938c02b --- /dev/null +++ b/libs/client/shared/src/components/dialogs/NonUSDDialog.tsx @@ -0,0 +1,24 @@ +import { Button, Dialog } from '@maybe-finance/design-system' + +export interface NonUSDDialogProps { + isOpen: boolean + onClose: () => void +} + +export function NonUSDDialog({ isOpen, onClose }: NonUSDDialogProps) { + return ( + + Connection Aborted + +

    + Unfortunately, we're currently only supporting connections to USD accounts + for the duration of this beta. Once we're out of beta, our goal is to + support as many institutions, in as many different regions as possible. +

    + +
    +
    + ) +} diff --git a/libs/client/shared/src/components/dialogs/index.ts b/libs/client/shared/src/components/dialogs/index.ts new file mode 100644 index 00000000000..ea60cae2cea --- /dev/null +++ b/libs/client/shared/src/components/dialogs/index.ts @@ -0,0 +1,3 @@ +export * from './FeedbackDialog' +export * from './ConfirmDialog' +export * from './NonUSDDialog' diff --git a/libs/client/shared/src/components/explainers/ExplainerExternalLink.tsx b/libs/client/shared/src/components/explainers/ExplainerExternalLink.tsx new file mode 100644 index 00000000000..27cd87ee09c --- /dev/null +++ b/libs/client/shared/src/components/explainers/ExplainerExternalLink.tsx @@ -0,0 +1,27 @@ +import Link from 'next/link' +import type { ReactNode } from 'react' +import type { IconType } from 'react-icons' + +export type ExplainerExternalLinkProps = { + icon: IconType + href: string + children: ReactNode +} + +export function ExplainerExternalLink({ + icon: Icon, + href, + children, +}: ExplainerExternalLinkProps): JSX.Element { + return ( + + + {children} + + ) +} diff --git a/libs/client/shared/src/components/explainers/ExplainerInfoBlock.tsx b/libs/client/shared/src/components/explainers/ExplainerInfoBlock.tsx new file mode 100644 index 00000000000..cddac66bab6 --- /dev/null +++ b/libs/client/shared/src/components/explainers/ExplainerInfoBlock.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from 'react' + +export type ExplainerInfoBlockProps = { + title: string | ReactNode + children: ReactNode +} + +export function ExplainerInfoBlock({ title, children }: ExplainerInfoBlockProps): JSX.Element { + return ( +
    + {title} +
    {children}
    +
    + ) +} diff --git a/libs/client/shared/src/components/explainers/ExplainerPerformanceBlock.tsx b/libs/client/shared/src/components/explainers/ExplainerPerformanceBlock.tsx new file mode 100644 index 00000000000..4282969901c --- /dev/null +++ b/libs/client/shared/src/components/explainers/ExplainerPerformanceBlock.tsx @@ -0,0 +1,38 @@ +import classNames from 'classnames' +import type { ReactNode } from 'react' + +const variants = { + teal: { + color: 'text-teal', + className: 'from-teal/[0.15]', + }, + red: { + color: 'text-red', + className: 'from-red/[0.1]', + }, + yellow: { + color: 'text-yellow', + className: 'from-yellow/[0.1]', + }, +} + +export type ExplainerPerformanceBlockProps = { + variant: keyof typeof variants + children: ReactNode | ((color: string) => ReactNode) +} + +export function ExplainerPerformanceBlock({ + variant, + children, +}: ExplainerPerformanceBlockProps): JSX.Element { + return ( +
    + {typeof children === 'function' ? children(variants[variant].color) : children} +
    + ) +} diff --git a/libs/client/shared/src/components/explainers/ExplainerSection.tsx b/libs/client/shared/src/components/explainers/ExplainerSection.tsx new file mode 100644 index 00000000000..049c987d5a1 --- /dev/null +++ b/libs/client/shared/src/components/explainers/ExplainerSection.tsx @@ -0,0 +1,23 @@ +import { forwardRef } from 'react' +import type { ReactNode, Ref } from 'react' +import classNames from 'classnames' + +export type ExplainerSectionProps = { + title: string | ReactNode + children: ReactNode + className?: string +} + +function ExplainerSection( + { title, children, className }: ExplainerSectionProps, + ref: Ref +): JSX.Element { + return ( +
    +
    {title}
    +
    {children}
    +
    + ) +} + +export default forwardRef(ExplainerSection) diff --git a/libs/client/shared/src/components/explainers/index.ts b/libs/client/shared/src/components/explainers/index.ts new file mode 100644 index 00000000000..240da14c303 --- /dev/null +++ b/libs/client/shared/src/components/explainers/index.ts @@ -0,0 +1,4 @@ +export * from './ExplainerExternalLink' +export * from './ExplainerInfoBlock' +export * from './ExplainerPerformanceBlock' +export { default as ExplainerSection } from './ExplainerSection' diff --git a/libs/client/shared/src/components/generic/BoxIcon.tsx b/libs/client/shared/src/components/generic/BoxIcon.tsx new file mode 100644 index 00000000000..166dad499a6 --- /dev/null +++ b/libs/client/shared/src/components/generic/BoxIcon.tsx @@ -0,0 +1,44 @@ +import type { IconType } from 'react-icons' + +import classNames from 'classnames' + +const Size = { + sm: 'w-6 h-6 rounded-sm', + md: 'w-[36px] h-[36px] rounded-lg', + lg: 'w-12 h-12 rounded-xl', +} + +const Variant = { + cyan: 'bg-cyan text-cyan', + orange: 'bg-orange text-orange', + teal: 'bg-teal text-teal', + grape: 'bg-grape text-grape', + pink: 'bg-pink text-pink', + yellow: 'bg-yellow text-yellow', + blue: 'bg-blue text-blue', + red: 'bg-red text-red', + indigo: 'bg-indigo text-indigo', +} + +export type BoxIconVariant = keyof typeof Variant +export type BoxIconSize = keyof typeof Size + +export type BoxIconProps = { + icon: IconType + variant?: BoxIconVariant + size?: BoxIconSize +} + +export function BoxIcon({ icon: Icon, size = 'lg', variant = 'cyan' }: BoxIconProps) { + return ( +
    + +
    + ) +} diff --git a/libs/client/shared/src/components/generic/Confetti.tsx b/libs/client/shared/src/components/generic/Confetti.tsx new file mode 100644 index 00000000000..fbab2d61044 --- /dev/null +++ b/libs/client/shared/src/components/generic/Confetti.tsx @@ -0,0 +1,188 @@ +import React, { useRef, useEffect, useCallback, memo } from 'react' +import ReactDOM from 'react-dom' +import classNames from 'classnames' +import { useFrame } from '../../hooks/useFrame' + +export type Particle = { + color: string + x: number + y: number + width: number + height: number + scaleY: number + rotation: number + velocityX: number + velocityY: number +} + +export type ConfettiProps = { + /** Number of confetti particles */ + amount?: number + + /** Whether to continue respawning particles at the top of the screen */ + respawn?: boolean + + /** Array of particle colors */ + colors?: string[] + + /** Amount of resistance slowing down horizontal velocity */ + drag?: number + + /** Rate at which vertical velocity increases */ + gravity?: number + + /** Amount of back-and-forth horizontal motion */ + sway?: number + + /** Rate at which particles rotate along their x-axes */ + flutter?: number + + /** Whether to randomly rotate particles along their z-axes */ + rotate?: boolean + + extendGenerateParticle?: (particle: Particle, context: CanvasRenderingContext2D) => Particle + renderParticle?: (particle: Particle, context: CanvasRenderingContext2D) => void + resizeDebounceTimeout?: number + className?: string +} + +function ConfettiBase({ + amount = 100, + respawn = true, + colors = ['#4cc9f0', '#4361ee', '#7209b7', '#f12980'], + drag = 0.05, + gravity = 0.015, + sway = 1, + flutter = 0.05, + rotate = true, + extendGenerateParticle, + renderParticle, + resizeDebounceTimeout = 250, + className, + ...rest +}: ConfettiProps): JSX.Element { + const canvasRef = useRef(null) + + const particles = useRef([]) + + const generateParticle = useCallback( + (context: CanvasRenderingContext2D): Particle => { + const particle = { + color: colors[Math.floor(Math.random() * colors.length)], + x: Math.random() * context.canvas.width, + y: + gravity > 0 + ? -Math.random() * context.canvas.height + : Math.random() * context.canvas.height + context.canvas.height, + width: Math.random() * 10 + 5, + height: Math.random() * 10 + 5, + scaleY: 1, + rotation: rotate ? Math.random() * 2 * Math.PI : 0, + velocityX: Math.random() * sway * 50 - sway * 25, + velocityY: Math.random() * gravity * 500, + } + + return extendGenerateParticle ? extendGenerateParticle(particle, context) : particle + }, + [colors, gravity, rotate, sway, extendGenerateParticle] + ) + + const generateParticles = useCallback( + (context: CanvasRenderingContext2D, amount: number): Particle[] => { + const particles: Particle[] = [] + + for (let i = 0; i < amount; ++i) { + particles.push(generateParticle(context)) + } + + return particles + }, + [generateParticle] + ) + + useFrame((_, rawDeltaTime) => { + const context = canvasRef.current?.getContext('2d') + if (!context) return + + const deltaTime = rawDeltaTime / 16.7 // Normalize to 60 FPS + + context.clearRect(0, 0, context.canvas.width, context.canvas.height) + particles.current = particles.current.map((particle) => { + // Slow down x-velocity over time + particle.velocityX -= particle.velocityX * drag * deltaTime + + // Add randomly positive/negative value to make the particle sway + particle.velocityX += (Math.random() > 0.5 ? 1 : -1) * Math.random() * sway * deltaTime + + // Increase y-velocity with gravity + particle.velocityY += gravity * deltaTime + + // Spin/flutter particle as it falls + particle.scaleY = Math.cos(particle.y * flutter * deltaTime) + + particle.x += particle.velocityX * deltaTime + particle.y += particle.velocityY * deltaTime + + const width = particle.width, + height = particle.height * particle.scaleY + + context.save() + context.translate(particle.x, particle.y) + context.rotate(particle.rotation) + context.fillStyle = particle.color + renderParticle + ? renderParticle(particle, context) + : context.fillRect(-width / 2, -height / 2, width, height) + context.restore() + + const buffer = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) / 2 + const outOfBounds = + gravity > 0 ? particle.y - buffer > context.canvas.height : particle.y + buffer < 0 + + return respawn && outOfBounds ? generateParticle(context) : particle + }) + }) + + const updateCanvasSize = React.useCallback(() => { + if (canvasRef.current) { + canvasRef.current.width = window.innerWidth + canvasRef.current.height = window.innerHeight + } + }, []) + + // Keep canvas size updated with window + useEffect(() => { + updateCanvasSize() + + let timeout: number | null = null + const handleResize = () => { + timeout !== null && window.clearTimeout(timeout) + timeout = window.setTimeout(updateCanvasSize, resizeDebounceTimeout) + } + + window.addEventListener('resize', handleResize) + return () => window.removeEventListener('resize', handleResize) + }, [resizeDebounceTimeout, updateCanvasSize]) + + // Generate particles + useEffect(() => { + if (canvasRef.current) { + const context = canvasRef.current?.getContext('2d') + if (context) particles.current = generateParticles(context, amount) + } + }, [amount, generateParticles]) + + return ReactDOM.createPortal( + , + document.body + ) +} + +export const Confetti = memo(ConfettiBase) diff --git a/libs/client/shared/src/components/generic/FeedbackButton.tsx b/libs/client/shared/src/components/generic/FeedbackButton.tsx new file mode 100644 index 00000000000..c3611ff01eb --- /dev/null +++ b/libs/client/shared/src/components/generic/FeedbackButton.tsx @@ -0,0 +1,14 @@ +import { Button } from '@maybe-finance/design-system' +import { useState } from 'react' +import { FeedbackDialog } from '../dialogs' + +export function FeedbackButton() { + const [isOpen, setIsOpen] = useState(false) + + return ( +
    + + setIsOpen(false)} /> +
    + ) +} diff --git a/libs/client/shared/src/components/generic/InfiniteScroll.tsx b/libs/client/shared/src/components/generic/InfiniteScroll.tsx new file mode 100644 index 00000000000..03b4bad170e --- /dev/null +++ b/libs/client/shared/src/components/generic/InfiniteScroll.tsx @@ -0,0 +1,67 @@ +import type { FC, PropsWithChildren } from 'react' +import InfiniteScroll from 'react-infinite-scroller' + +/** + * The react-infinite-scroller package is no longer actively maintained and does + * not expose these props, so restating them here and wrapping the component in a + * React 18 type-safe way. + * + * @see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210 + */ +export type InfiniteScrollProps = { + /** + * Name of the element that the component should render as. + * Defaults to 'div'. + */ + element?: React.ReactNode | string | undefined + /** + * Whether there are more items to be loaded. Event listeners are removed if false. + * Defaults to false. + */ + hasMore?: boolean | undefined + /** + * Whether the component should load the first set of items. + * Defaults to true. + */ + initialLoad?: boolean | undefined + /** + * Whether new items should be loaded when user scrolls to the top of the scrollable area. + * Default to false. + */ + isReverse?: boolean | undefined + /** + * A callback for when more items are requested by the user. + * Page param is next page index. + */ + loadMore(page: number): void + /** + * The number of the first page to load, with the default of 0, the first page is 1. + * Defaults to 0. + */ + pageStart?: number | undefined + /** + * The distance in pixels before the end of the items that will trigger a call to loadMore. + * Defaults to 250. + */ + threshold?: number | undefined + /** + * Proxy to the useCapture option of the added event listeners. + * Defaults to false. + */ + useCapture?: boolean | undefined + /** + * Add scroll listeners to the window, or else, the component's parentNode. + * Defaults to true. + */ + useWindow?: boolean | undefined + /** + * Loader component for indicating "loading more". + */ + loader?: React.ReactElement | undefined + /** + * Override method to return a different scroll listener if it's not the immediate parent of InfiniteScroll. + */ + getScrollParent?(): HTMLElement | null +} + +export default InfiniteScroll as unknown as FC> diff --git a/libs/client/shared/src/components/generic/InsightGroup.tsx b/libs/client/shared/src/components/generic/InsightGroup.tsx new file mode 100644 index 00000000000..5fffd0db0a3 --- /dev/null +++ b/libs/client/shared/src/components/generic/InsightGroup.tsx @@ -0,0 +1,181 @@ +import type { PropsWithChildren, ReactNode } from 'react' +import type { ClientType } from '../..' +import { useLocalStorage } from '../..' + +import { createContext, useMemo, useContext } from 'react' +import { Badge, Listbox, LoadingPlaceholder, Tooltip } from '@maybe-finance/design-system' +import { RiQuestionLine, RiSettings4Line } from 'react-icons/ri' +import { AnimatePresence, motion } from 'framer-motion' +import groupBy from 'lodash/groupBy' +import random from 'lodash/random' +import classNames from 'classnames' + +export type InsightCardOption = { + id: string + display: string + category: string + tooltip: string +} + +const InsightGroupContext = createContext<{ selectedInsights: InsightCardOption[] } | undefined>( + undefined +) + +export type InsightGroupProps = PropsWithChildren<{ + id: string + options: InsightCardOption[] + initialInsights: InsightCardOption['id'][] +}> + +function InsightGroup({ id, options, initialInsights, children }: InsightGroupProps) { + const [selectedInsights, setSelectedInsights] = useLocalStorage( + `SELECTED_INSIGHTS_GROUP_${id}`, + initialInsights + ) + + /** + * Returns grouped arrays of insight cards + * + * ['key', [, ]] + */ + const insights = useMemo<[string, InsightCardOption[]][]>(() => { + const groups: { [key: string]: InsightCardOption[] } = groupBy(options, 'category') + return Object.entries(groups) + }, [options]) + + return ( + selectedInsights.includes(o.id)) }} + > +
    +
    +
    highlights
    + + + Customize + + + {insights.map(([category, insights]) => ( +
    + + {category} + + {insights.map((insight) => ( + + {insight.display} + + ))} +
    + ))} +
    +
    +
    + {selectedInsights.length ? ( +
    + {children} +
    + ) : ( +

    Please select insights to show

    + )} +
    +
    + ) +} + +type InsightCardProps = PropsWithChildren<{ + id: string + isLoading: boolean + status?: ClientType.MetricStatus + headerRight?: ReactNode + placeholder?: ReactNode + onClick?: () => void +}> + +function Card({ + id, + children, + isLoading, + status, + headerRight, + placeholder, + onClick, +}: InsightCardProps) { + const ctx = useContext(InsightGroupContext) + + if (!ctx) throw new Error('Must use Insight Card within group') + + const card = useMemo(() => ctx.selectedInsights.find((insight) => insight.id === id), [ctx, id]) + + return ( + + {card && ( + +
    +

    {card.display}

    + {card.tooltip}
    } + className="max-w-[350px]" + > + + + + +
    + {status === 'under-construction' ? ( + + ) : status === 'coming-soon' ? ( + + ) : ( + headerRight + )} +
    +
    +
    + {!status || status === 'under-construction' ? ( +

    + We're currently fixing this to make sure we show you accurate + figures. +

    + ) : status === 'active' && !isLoading ? ( + children + ) : ( + +
    +
    + +
    + {placeholder ? ( + placeholder + ) : ( + <> +

    {random(10, 100, true).toFixed(2)}

    +

    Placeholder subtext overlay

    + + )} +
    +
    + + )} +
    + + )} + + ) +} + +export default Object.assign(InsightGroup, { Card }) diff --git a/libs/client/shared/src/components/generic/InsightPopout.tsx b/libs/client/shared/src/components/generic/InsightPopout.tsx new file mode 100644 index 00000000000..f9b99cf1a46 --- /dev/null +++ b/libs/client/shared/src/components/generic/InsightPopout.tsx @@ -0,0 +1,23 @@ +import { Button } from '@maybe-finance/design-system' +import type { ReactNode } from 'react' +import { RiCloseFill } from 'react-icons/ri' +import { usePopoutContext } from '../../providers' + +export type InsightPopoutProps = { + children: ReactNode +} + +export function InsightPopout({ children }: InsightPopoutProps) { + const { close } = usePopoutContext() + + return ( +
    +
    + +
    +
    {children}
    +
    + ) +} diff --git a/libs/client/shared/src/components/generic/ProfileCircle.tsx b/libs/client/shared/src/components/generic/ProfileCircle.tsx new file mode 100644 index 00000000000..c9bf9dc3d95 --- /dev/null +++ b/libs/client/shared/src/components/generic/ProfileCircle.tsx @@ -0,0 +1,37 @@ +import classNames from 'classnames' +import { useUserApi } from '../../api' + +const getProfileInitials = (nameString: string): string => { + const parts = nameString.split(' ') + if (parts.length === 1) { + return parts[0].charAt(0) + } + + return parts[0].charAt(0) + parts[1].charAt(0) +} + +export type ProfileCircleProps = { + interactive?: boolean + className?: string +} + +export function ProfileCircle({ interactive = true, className }: ProfileCircleProps) { + const { useProfile } = useUserApi() + const profile = useProfile() + + const firstName = profile.data?.firstName + const lastName = profile.data?.lastName + const name = !firstName ? lastName : !lastName ? firstName : `${firstName} ${lastName}` + + return ( +
    + {getProfileInitials(name ?? 'M')} +
    + ) +} diff --git a/libs/client/shared/src/components/generic/RelativeTime.tsx b/libs/client/shared/src/components/generic/RelativeTime.tsx new file mode 100644 index 00000000000..8f48c2acf94 --- /dev/null +++ b/libs/client/shared/src/components/generic/RelativeTime.tsx @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react' +import { DateTime } from 'luxon' + +export default function RelativeTime({ time }: { time: Date | DateTime }) { + const dateTime = DateTime.isDateTime(time) ? time : DateTime.fromJSDate(time) + + const [now, setNow] = useState(DateTime.now()) + + useEffect(() => { + const interval = setInterval(() => setNow(DateTime.now()), 60_000) + return () => clearInterval(interval) + }, []) + + return ( + + ) +} diff --git a/libs/client/shared/src/components/generic/TakeoverBackground.tsx b/libs/client/shared/src/components/generic/TakeoverBackground.tsx new file mode 100644 index 00000000000..5b503dd6019 --- /dev/null +++ b/libs/client/shared/src/components/generic/TakeoverBackground.tsx @@ -0,0 +1,285 @@ +export function TakeoverBackground({ className }: { className: string }) { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/libs/client/shared/src/components/generic/Toaster.tsx b/libs/client/shared/src/components/generic/Toaster.tsx new file mode 100644 index 00000000000..b0c416ed0c1 --- /dev/null +++ b/libs/client/shared/src/components/generic/Toaster.tsx @@ -0,0 +1,41 @@ +import type { ToastVariant } from '@maybe-finance/design-system' +import { Toast } from '@maybe-finance/design-system' +import { Toaster as ReactToaster, resolveValue } from 'react-hot-toast' +import classNames from 'classnames' + +const toastTypeMap: { [type: string]: ToastVariant } = Object.freeze({ + success: 'success', + error: 'error', +}) + +export interface ToasterProps { + mobile: boolean + sidebarOffset?: string +} + +export function Toaster({ mobile, sidebarOffset }: ToasterProps) { + return ( + + {(toastData) => ( + + {resolveValue(toastData.message, toastData)} + + )} + + ) +} + +export default Toaster diff --git a/libs/client/shared/src/components/generic/TrendBadge.tsx b/libs/client/shared/src/components/generic/TrendBadge.tsx new file mode 100644 index 00000000000..097ff25233a --- /dev/null +++ b/libs/client/shared/src/components/generic/TrendBadge.tsx @@ -0,0 +1,66 @@ +import { Badge, BadgeVariant } from '@maybe-finance/design-system' +import type { SharedType } from '@maybe-finance/shared' +import { NumberUtil } from '@maybe-finance/shared' +import cn from 'classnames' + +type TrendBadgeProps = { + trend: SharedType.Trend + negative?: boolean + badgeSize?: 'sm' | 'md' + amountSize?: 'sm' | 'md' + label?: string + displayAmount?: boolean +} + +const BadgeVariant = (negative: boolean): Record => ({ + down: negative ? 'teal' : 'red', + up: negative ? 'red' : 'teal', + flat: 'gray', +}) + +const AmountVariant = (negative: boolean): Record => ({ + down: negative ? 'text-teal' : 'text-red', + up: negative ? 'text-red' : 'text-teal', + flat: 'text-white', +}) + +const AmountSizeVariant = Object.freeze({ + sm: 'text-sm', + md: 'text-base', +}) + +export function TrendBadge({ + trend, + negative = false, + label, + badgeSize = 'md', + amountSize, + displayAmount = false, +}: TrendBadgeProps) { + return ( +
    + + {NumberUtil.format(trend.percentage, 'percent', { + maximumFractionDigits: 2, + })} + + + {displayAmount && ( + + {NumberUtil.format(trend.amount, 'currency', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + signDisplay: 'exceptZero', + })} + + )} + + {label && {label}} +
    + ) +} diff --git a/libs/client/shared/src/components/generic/index.ts b/libs/client/shared/src/components/generic/index.ts new file mode 100644 index 00000000000..df90b1a4712 --- /dev/null +++ b/libs/client/shared/src/components/generic/index.ts @@ -0,0 +1,14 @@ +export * from './FeedbackButton' +export * from './TrendBadge' +export * from './Toaster' +export * from './BoxIcon' +export * from './small-decimals' +export * from './InsightPopout' +export * from './TakeoverBackground' +export * from './ProfileCircle' +export * from './Confetti' +export type { InsightCardOption } from './InsightGroup' +export { default as InsightGroup } from './InsightGroup' +export { default as RelativeTime } from './RelativeTime' +export type { InfiniteScrollProps } from './InfiniteScroll' +export { default as InfiniteScroll } from './InfiniteScroll' diff --git a/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.test.tsx b/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.test.tsx new file mode 100644 index 00000000000..11e5c9f7a01 --- /dev/null +++ b/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.test.tsx @@ -0,0 +1,27 @@ +import { render, screen } from '@testing-library/react' +import SmallDecimals from './SmallDecimals' + +describe('SmallDecimals', () => { + describe('when using the correct value format', () => { + it('formats decimals', () => { + render() + + expect(screen.getByTestId('decimals').textContent).toBe('.13') + }) + }) + describe('when not using the correct value format', () => { + it('does not apply decimals formatting but render value #1', () => { + render() + + expect(screen.queryByTestId('decimals')).toBeFalsy() + expect(screen.getByText('something')).toBeTruthy() + }) + + it('does not apply decimals formatting but render value #2', () => { + render() + + expect(screen.queryByTestId('decimals')).toBeFalsy() + expect(screen.getByText('100')).toBeTruthy() + }) + }) +}) diff --git a/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.tsx b/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.tsx new file mode 100644 index 00000000000..3f19f557881 --- /dev/null +++ b/libs/client/shared/src/components/generic/small-decimals/SmallDecimals.tsx @@ -0,0 +1,33 @@ +type SmallDecimalsProps = { + value?: string + className?: string +} + +export const SmallDecimals = ({ + value = '', + className = 'text-gray-100 text-2xl', +}: SmallDecimalsProps): JSX.Element => { + const isValidValue = /(\.\d\d)$/.test(value) + + if (!isValidValue) { + return ( + <> + {value} + + + ) + } + + const [integer, decimal] = value.split('.') + + return ( + <> + {integer} + + .{decimal} + + + ) +} + +export default SmallDecimals diff --git a/libs/client/shared/src/components/generic/small-decimals/index.ts b/libs/client/shared/src/components/generic/small-decimals/index.ts new file mode 100644 index 00000000000..7caa7421fc5 --- /dev/null +++ b/libs/client/shared/src/components/generic/small-decimals/index.ts @@ -0,0 +1 @@ +export * from './SmallDecimals' diff --git a/libs/client/shared/src/components/index.ts b/libs/client/shared/src/components/index.ts new file mode 100644 index 00000000000..98e9becc76a --- /dev/null +++ b/libs/client/shared/src/components/index.ts @@ -0,0 +1,9 @@ +export * from './overlays' +export * from './generic' +export * from './dialogs' +export * from './loaders' +export * from './charts' +export * from './tables' +export * from './explainers' +export * from './chat' +export * from './cards' diff --git a/libs/client/shared/src/components/loaders/MainContentLoader.tsx b/libs/client/shared/src/components/loaders/MainContentLoader.tsx new file mode 100644 index 00000000000..db870fe90fe --- /dev/null +++ b/libs/client/shared/src/components/loaders/MainContentLoader.tsx @@ -0,0 +1,17 @@ +import { LoadingSpinner } from '@maybe-finance/design-system' +import { Overlay } from '../overlays' + +export interface MainContentLoaderProps { + message?: string +} + +export function MainContentLoader({ message }: MainContentLoaderProps) { + return ( + +
    + + {message &&

    {message}

    } +
    +
    + ) +} diff --git a/libs/client/shared/src/components/loaders/index.ts b/libs/client/shared/src/components/loaders/index.ts new file mode 100644 index 00000000000..b0e9f408746 --- /dev/null +++ b/libs/client/shared/src/components/loaders/index.ts @@ -0,0 +1 @@ +export * from './MainContentLoader' diff --git a/libs/client/shared/src/components/overlays/BlurredContentOverlay.tsx b/libs/client/shared/src/components/overlays/BlurredContentOverlay.tsx new file mode 100644 index 00000000000..8c72ff5a4a4 --- /dev/null +++ b/libs/client/shared/src/components/overlays/BlurredContentOverlay.tsx @@ -0,0 +1,44 @@ +import type { PropsWithChildren } from 'react' +import { motion } from 'framer-motion' +import type { IconType } from 'react-icons' +import { Overlay } from './Overlay' +import classNames from 'classnames' + +export type BlurredContentOverlayProps = PropsWithChildren<{ + title: string + icon?: IconType + className?: string +}> + +export function BlurredContentOverlay({ + title, + icon: Icon, + children, + className, +}: BlurredContentOverlayProps) { + return ( + +
    + + {Icon && ( +
    + +
    + )} +

    {title}

    +
    {children}
    +
    +
    +
    + ) +} diff --git a/libs/client/shared/src/components/overlays/ErrorFallbackOverlay.tsx b/libs/client/shared/src/components/overlays/ErrorFallbackOverlay.tsx new file mode 100644 index 00000000000..66f5ba0a7fe --- /dev/null +++ b/libs/client/shared/src/components/overlays/ErrorFallbackOverlay.tsx @@ -0,0 +1,20 @@ +import type { FallbackProps } from 'react-error-boundary' +import { Button } from '@maybe-finance/design-system' +import { Overlay } from './Overlay' + +export function ErrorFallback({ resetErrorBoundary: _ }: FallbackProps) { + return ( + +
    +
    + Maybe Finance Logo +

    Oops! Something went wrong.

    + +
    +
    +
    + ) +} diff --git a/libs/client/shared/src/components/overlays/MainContentOverlay.tsx b/libs/client/shared/src/components/overlays/MainContentOverlay.tsx new file mode 100644 index 00000000000..0dbc8183531 --- /dev/null +++ b/libs/client/shared/src/components/overlays/MainContentOverlay.tsx @@ -0,0 +1,26 @@ +import type { PropsWithChildren } from 'react' +import { Button } from '@maybe-finance/design-system' +import { Overlay } from './Overlay' + +export type MainContentOverlayProps = PropsWithChildren<{ + title: string + actionText: string + onAction: () => void +}> + +export function MainContentOverlay({ + children, + title, + actionText, + onAction, +}: MainContentOverlayProps) { + return ( + +
    +

    {title}

    +
    {children}
    + +
    +
    + ) +} diff --git a/libs/client/shared/src/components/overlays/Overlay.tsx b/libs/client/shared/src/components/overlays/Overlay.tsx new file mode 100644 index 00000000000..4ba215e6f61 --- /dev/null +++ b/libs/client/shared/src/components/overlays/Overlay.tsx @@ -0,0 +1,14 @@ +import type { ReactNode } from 'react' +import { createPortal } from 'react-dom' +import { useLayoutContext } from '../../providers' + +export function Overlay({ children }: { children: ReactNode }) { + const { overlayContainer } = useLayoutContext() + + return overlayContainer?.current ? ( + createPortal(children, overlayContainer.current) + ) : ( + // eslint-disable-next-line react/jsx-no-useless-fragment + <>{children} + ) +} diff --git a/libs/client/shared/src/components/overlays/index.ts b/libs/client/shared/src/components/overlays/index.ts new file mode 100644 index 00000000000..0a6ae4730f7 --- /dev/null +++ b/libs/client/shared/src/components/overlays/index.ts @@ -0,0 +1,4 @@ +export * from './BlurredContentOverlay' +export * from './ErrorFallbackOverlay' +export * from './MainContentOverlay' +export * from './Overlay' diff --git a/libs/client/shared/src/components/tables/data-table/DataTable.tsx b/libs/client/shared/src/components/tables/data-table/DataTable.tsx new file mode 100644 index 00000000000..3cb91ea2797 --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/DataTable.tsx @@ -0,0 +1,124 @@ +import type { TableMeta } from './types' +import type { ColumnDef, PaginationState, OnChangeFn } from '@tanstack/react-table' +import { useMemo } from 'react' +import { getCoreRowModel, useReactTable, flexRender } from '@tanstack/react-table' +import { DefaultCell } from './DefaultCell' +import { Button } from '@maybe-finance/design-system' +import { RiArrowLeftLine, RiArrowRightLine } from 'react-icons/ri' + +export interface DataTableProps { + data: any[] + columns: Array> + mutateFn: TableMeta['mutateFn'] + autoResetPage: boolean + defaultColumn?: Partial> + paginationOpts?: { + pagination: PaginationState + pageCount: number + onChange: OnChangeFn + } +} + +export function DataTable({ + columns, + data, + mutateFn, + defaultColumn, + paginationOpts, +}: DataTableProps) { + // Properties defined here will be shared across all columns + const defaultColumnInternal = useMemo>>( + () => ({ + cell: DefaultCell, + enableResizing: true, + minSize: 175, + ...defaultColumn, + }), + [defaultColumn] + ) + + const table = useReactTable({ + data, + columns, + defaultColumn: defaultColumnInternal, + getCoreRowModel: getCoreRowModel(), + meta: { mutateFn } as TableMeta, + ...(paginationOpts && { + state: { + pagination: paginationOpts.pagination, + }, + manualPagination: true, + onPaginationChange: paginationOpts.onChange, + pageCount: paginationOpts.pageCount, + }), + }) + + return ( +
    + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
    + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} +
    + {flexRender(cell.column.columnDef.cell, cell.getContext())} +
    + + {paginationOpts && ( +
    + + + {table.getState().pagination.pageIndex + 1} / {table.getPageCount()} + + +
    + )} +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/DefaultCell.tsx b/libs/client/shared/src/components/tables/data-table/DefaultCell.tsx new file mode 100644 index 00000000000..11bbea2cbb2 --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/DefaultCell.tsx @@ -0,0 +1,9 @@ +import type { CellProps } from './types' + +export function DefaultCell({ getValue }: CellProps) { + return ( +
    +

    {getValue()}

    +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/EditableBooleanCell.tsx b/libs/client/shared/src/components/tables/data-table/EditableBooleanCell.tsx new file mode 100644 index 00000000000..c2dfcc1e8f4 --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/EditableBooleanCell.tsx @@ -0,0 +1,26 @@ +import { useEffect, useState } from 'react' + +export type EditableBooleanCellProps = { + initialValue: boolean + onSubmit(value: boolean): void +} + +export function EditableBooleanCell({ initialValue, onSubmit }: EditableBooleanCellProps) { + const [value, setValue] = useState(initialValue) + + useEffect(() => { + setValue(initialValue) + }, [initialValue]) + + return ( +
    + onSubmit(e.currentTarget.checked)} + /> +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/EditableCell.tsx b/libs/client/shared/src/components/tables/data-table/EditableCell.tsx new file mode 100644 index 00000000000..3e8a318ed1d --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/EditableCell.tsx @@ -0,0 +1,54 @@ +import type { TableMeta, ColumnMeta, CellProps } from './types' +import { useMemo } from 'react' +import { EditableStringCell } from './EditableStringCell' +import { EditableDateCell } from './EditableDateCell' +import { EditableDropdownCell } from './EditableDropdownCell' +import { EditableBooleanCell } from './EditableBooleanCell' + +export function EditableCell({ table, row, column, getValue }: CellProps) { + const tableMeta = useMemo(() => table.options.meta as TableMeta, [table]) + const columnMeta = useMemo(() => column.columnDef.meta as ColumnMeta, [column]) + + switch (columnMeta?.type) { + case 'string': { + return ( + tableMeta.mutateFn(row, column.id, value)} + /> + ) + } + case 'date': { + return ( + tableMeta.mutateFn(row, column.id, value)} + /> + ) + } + case 'dropdown': { + return ( + tableMeta.mutateFn(row, column.id, value)} + /> + ) + } + case 'boolean': + return ( + tableMeta.mutateFn(row, column.id, value)} + /> + ) + default: { + throw new Error('Invalid editable cell configuration') + } + } +} diff --git a/libs/client/shared/src/components/tables/data-table/EditableDateCell.tsx b/libs/client/shared/src/components/tables/data-table/EditableDateCell.tsx new file mode 100644 index 00000000000..3fdebc14fac --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/EditableDateCell.tsx @@ -0,0 +1,85 @@ +import { useCallback } from 'react' +import cn from 'classnames' +import { DateTime } from 'luxon' +import { Controller, useForm } from 'react-hook-form' +import { RiCheckLine, RiCloseLine } from 'react-icons/ri' +import { PatternFormat } from 'react-number-format' + +// This component interacts with date values as strings in the format YYYY-MM-DD +export type EditableDateCellProps = { + initialValue?: string + onSubmit(value: string): void +} + +export function EditableDateCell({ initialValue, onSubmit }: EditableDateCellProps) { + const fromFormattedDate = useCallback((value: string) => { + return DateTime.fromFormat(value, 'MM / dd / yyyy') + }, []) + + const { control, handleSubmit, reset } = useForm({ + defaultValues: { cellValue: initialValue }, + reValidateMode: 'onSubmit', + }) + + return ( +
    +
    { + if (cellValue) { + onSubmit(fromFormattedDate(cellValue).toFormat('yyyy-MM-dd')) + } + })} + > + { + if (!v) return false + + const dateObj = fromFormattedDate(v) + const minDate = DateTime.fromISO('1980-01-01') + const maxDate = DateTime.local() + + if (!dateObj.isValid) return false + if (dateObj < minDate) return false + if (dateObj > maxDate) return false + + return true + }, + }} + render={({ field: { onChange, ...field }, fieldState }) => ( + onChange(v.formattedValue)} + format="## / ## / ####" + mask={['M', 'M', 'D', 'D', 'Y', 'Y', 'Y', 'Y']} + placeholder="MM / DD / YYYY" + className={cn( + 'w-full bg-transparent text-base border-0 focus:ring-0', + fieldState.invalid && 'text-red' + )} + autoComplete="off" + /> + )} + /> + +
    + + +
    + +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/EditableDropdownCell.tsx b/libs/client/shared/src/components/tables/data-table/EditableDropdownCell.tsx new file mode 100644 index 00000000000..549fd420c4d --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/EditableDropdownCell.tsx @@ -0,0 +1,96 @@ +import type { Modifier } from 'react-popper' +import { useEffect, useMemo, useState } from 'react' +import { Listbox } from '@headlessui/react' +import cn from 'classnames' +import { RiArrowDownSLine } from 'react-icons/ri' +import { usePopper } from 'react-popper' + +export type EditableDropdownCellProps = { + initialValue: string + options: string[] + onSubmit: (value: string) => void + formatFn?: (option: string) => string +} + +export function EditableDropdownCell({ + initialValue, + options, + onSubmit, + formatFn, +}: EditableDropdownCellProps) { + const [value, setValue] = useState(initialValue) + + const popperSameWidth = useMemo>( + () => ({ + name: 'sameWidth', + enabled: true, + phase: 'beforeWrite', + requires: ['computeStyles'], + fn: ({ state }) => { + state.styles.popper.width = `${state.rects.reference.width}px` + }, + effect: ({ state }) => { + state.elements.popper.style.width = `${ + (state.elements.reference as HTMLElement).offsetWidth + }px` + }, + }), + [] + ) + + useEffect(() => { + setValue(initialValue) + }, [initialValue]) + + const [referenceElement, setReferenceElement] = useState() + const [popperElement, setPopperElement] = useState() + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: 'bottom-start', + modifiers: [popperSameWidth], + }) + + return ( +
    + { + setValue(value) + onSubmit(value) + }} + > + + {formatFn ? formatFn(value) : value} + + + + + {options.map((option) => ( + + {({ selected }) => ( + + )} + + ))} + + +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/EditableStringCell.tsx b/libs/client/shared/src/components/tables/data-table/EditableStringCell.tsx new file mode 100644 index 00000000000..752bade0ebf --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/EditableStringCell.tsx @@ -0,0 +1,49 @@ +import { useEffect, useState } from 'react' +import { RiCheckLine, RiCloseLine } from 'react-icons/ri' + +export type EditableStringCellProps = { + initialValue: string + onSubmit: (value: string) => void +} + +export function EditableStringCell({ initialValue, onSubmit }: EditableStringCellProps) { + const [value, setValue] = useState(initialValue) + + useEffect(() => { + setValue(initialValue) + }, [initialValue]) + + return ( +
    +
    + setValue(e.target.value)} + /> + +
    + + +
    +
    +
    + ) +} diff --git a/libs/client/shared/src/components/tables/data-table/index.ts b/libs/client/shared/src/components/tables/data-table/index.ts new file mode 100644 index 00000000000..a04853f275d --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/index.ts @@ -0,0 +1,7 @@ +export * from './DataTable' +export * from './DefaultCell' +export * from './EditableStringCell' +export * from './EditableDropdownCell' +export * from './EditableDateCell' +export * from './EditableCell' +export * as TableType from './types' diff --git a/libs/client/shared/src/components/tables/data-table/types.ts b/libs/client/shared/src/components/tables/data-table/types.ts new file mode 100644 index 00000000000..863a699cfed --- /dev/null +++ b/libs/client/shared/src/components/tables/data-table/types.ts @@ -0,0 +1,24 @@ +import type { Cell, Column, Row, Table } from '@tanstack/react-table' + +export type TableMeta = { + mutateFn: (row: Row, key: string, value: any) => void +} + +export type ColumnMeta = + | { + type: 'string' | 'date' | 'boolean' + } + | { + type: 'dropdown' + options: string[] | ((row: Row) => string[]) + formatFn?: (option: string) => string + } + | undefined + +export type CellProps = { + table: Table + row: Row + column: Column + cell: Cell + getValue: () => any +} diff --git a/libs/client/shared/src/components/tables/index.ts b/libs/client/shared/src/components/tables/index.ts new file mode 100644 index 00000000000..dde3c9c4c83 --- /dev/null +++ b/libs/client/shared/src/components/tables/index.ts @@ -0,0 +1 @@ +export * from './data-table' diff --git a/libs/client/shared/src/hooks/index.ts b/libs/client/shared/src/hooks/index.ts new file mode 100644 index 00000000000..c11f27e3b3e --- /dev/null +++ b/libs/client/shared/src/hooks/index.ts @@ -0,0 +1,14 @@ +export * from './useAxiosWithAuth' +export * from './useDebounce' +export * from './useFinicity' +export * from './useIntercom' +export * from './useInterval' +export * from './useLastUpdated' +export * from './useLocalStorage' +export * from './useLogger' +export * from './useQueryParam' +export * from './useScreenSize' +export * from './useAccountNotifications' +export * from './usePlaid' +export * from './useProviderStatus' +export * from './useModalManager' diff --git a/libs/client/shared/src/hooks/useAccountNotifications.ts b/libs/client/shared/src/hooks/useAccountNotifications.ts new file mode 100644 index 00000000000..22db83a1346 --- /dev/null +++ b/libs/client/shared/src/hooks/useAccountNotifications.ts @@ -0,0 +1,33 @@ +import { useMemo } from 'react' +import { useAccountApi } from '../api' + +export function useAccountNotifications() { + const { useAccounts } = useAccountApi() + const accountsQuery = useAccounts() + + const accountsNotification = useMemo(() => { + if (!accountsQuery.data) return null + + if ( + accountsQuery.data.connections.some( + (connection) => + connection.status === 'ERROR' && + (connection.plaidConsentExpiration || connection.plaidError) + ) + ) { + return 'error' + } + + if ( + accountsQuery.data.connections.some( + (connection) => connection.plaidNewAccountsAvailable + ) + ) { + return 'update' + } + + return null + }, [accountsQuery]) + + return accountsNotification +} diff --git a/libs/client/shared/src/hooks/useAxiosWithAuth.ts b/libs/client/shared/src/hooks/useAxiosWithAuth.ts new file mode 100644 index 00000000000..04a72d493bf --- /dev/null +++ b/libs/client/shared/src/hooks/useAxiosWithAuth.ts @@ -0,0 +1,12 @@ +import { useContext } from 'react' +import { AxiosContext, type AxiosContextValue } from '../providers/AxiosProvider' + +export const useAxiosWithAuth: () => AxiosContextValue = () => { + const axiosInstance = useContext(AxiosContext) + + if (!axiosInstance) { + throw new Error('Axios provider configured incorrectly') + } + + return axiosInstance +} diff --git a/libs/client/shared/src/hooks/useDebounce.ts b/libs/client/shared/src/hooks/useDebounce.ts new file mode 100644 index 00000000000..850c7c8a1e0 --- /dev/null +++ b/libs/client/shared/src/hooks/useDebounce.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react' + +/** + * Returns a value updated to match the provided value after it hasn't changed for the specified delay + */ +export function useDebounce(value: T, delayMilliseconds: number) { + const [debouncedValue, setDebouncedValue] = useState(value) + + useEffect(() => { + const timeout = setTimeout(() => { + setDebouncedValue(value) + }, delayMilliseconds) + + return () => clearTimeout(timeout) + }, [value, delayMilliseconds]) + + return debouncedValue +} diff --git a/libs/client/shared/src/hooks/useFinicity.ts b/libs/client/shared/src/hooks/useFinicity.ts new file mode 100644 index 00000000000..4f0aed0c14e --- /dev/null +++ b/libs/client/shared/src/hooks/useFinicity.ts @@ -0,0 +1,85 @@ +import { useCallback } from 'react' +import toast from 'react-hot-toast' +import * as Sentry from '@sentry/react' +import type { + ConnectCancelEvent, + ConnectDoneEvent, + ConnectErrorEvent, + ConnectRouteEvent, +} from '@finicity/connect-web-sdk' +import { useFinicityApi } from '../api' +import { useAccountContext, useUserAccountContext } from '../providers' +import { useLogger } from './useLogger' +import { BrowserUtil } from '..' + +export function useFinicity() { + const logger = useLogger() + + const { useGenerateConnectUrl } = useFinicityApi() + const generateConnectUrl = useGenerateConnectUrl() + + const { setExpectingAccounts } = useUserAccountContext() + const { setAccountManager } = useAccountContext() + + const launch = useCallback( + async (linkOrPromise: string | Promise) => { + const toastId = toast.loading('Initializing Finicity...', { duration: 10_000 }) + + const [{ FinicityConnect }, link] = await Promise.all([ + import('@finicity/connect-web-sdk'), + linkOrPromise, + ]) + + toast.dismiss(toastId) + + FinicityConnect.launch(link, { + onDone(evt: ConnectDoneEvent) { + logger.debug(`Finicity Connect onDone event`, evt) + BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_DONE', { + ...evt, + }) + setExpectingAccounts(true) + }, + onError(evt: ConnectErrorEvent) { + logger.error(`Finicity Connect exited with error`, evt) + Sentry.captureEvent({ + level: 'error', + message: 'FINICITY_CONNECT_ERROR', + tags: { + 'finicity.error.code': evt.code, + 'finicity.error.reason': evt.reason, + }, + }) + BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ERROR', { + ...evt, + }) + }, + onCancel(evt: ConnectCancelEvent) { + logger.debug(`Finicity Connect onCancel event`, evt) + BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_CANCEL', { + ...evt, + }) + }, + onUser(evt: any) { + BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_USER_ACTION', { + ...evt, + }) + }, + onRoute(evt: ConnectRouteEvent) { + BrowserUtil.trackIntercomEvent('FINICITY_CONNECT_ROUTE_EVENT', { + ...evt, + }) + }, + }) + }, + [logger, setExpectingAccounts] + ) + + return { + launch, + openFinicity: async (institutionId: string) => { + launch(generateConnectUrl.mutateAsync(institutionId)) + setAccountManager({ view: 'idle' }) + }, + } +} diff --git a/libs/client/shared/src/hooks/useFrame.ts b/libs/client/shared/src/hooks/useFrame.ts new file mode 100644 index 00000000000..2e338da4776 --- /dev/null +++ b/libs/client/shared/src/hooks/useFrame.ts @@ -0,0 +1,23 @@ +import { useEffect, useRef } from 'react' + +export function useFrame(callback: (time: DOMHighResTimeStamp, deltaTime: number) => void) { + const frameRequest = useRef() + const previousTimeRef = useRef() + + const animate = (time: DOMHighResTimeStamp) => { + if (previousTimeRef.current !== undefined) { + const deltaTime = time - previousTimeRef.current + callback(time, deltaTime) + } + previousTimeRef.current = time + frameRequest.current = requestAnimationFrame(animate) + } + + useEffect(() => { + frameRequest.current = requestAnimationFrame(animate) + return () => { + if (frameRequest.current !== undefined) cancelAnimationFrame(frameRequest.current) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) +} diff --git a/libs/client/shared/src/hooks/useIntercom.ts b/libs/client/shared/src/hooks/useIntercom.ts new file mode 100644 index 00000000000..6074250334e --- /dev/null +++ b/libs/client/shared/src/hooks/useIntercom.ts @@ -0,0 +1,50 @@ +import { useCallback } from 'react' +import { useAuth0 } from '@auth0/auth0-react' +import type { SharedType } from '@maybe-finance/shared' +import { useUserApi } from '../api' +import { BrowserUtil } from '..' + +export function useIntercom() { + const { user, isAuthenticated } = useAuth0() + + const { useIntercomMetadata } = useUserApi() + const { data: intercomMetadata } = useIntercomMetadata({ enabled: isAuthenticated }) + + const boot = useCallback( + (data?: BrowserUtil.IntercomData) => { + if (!user?.sub || !intercomMetadata?.hash) return false + + BrowserUtil.bootIntercom({ + user_id: user.sub, + user_hash: intercomMetadata.hash, + email: user.email, + name: user.name, + last_request_at: Math.floor(new Date().getTime() / 1000), + ...data, + }) + + return true + }, + [user, intercomMetadata] + ) + + const update = useCallback( + (data?: BrowserUtil.IntercomData, updateLastRequestAt = true) => { + if (!user?.sub || !intercomMetadata?.hash) return + + BrowserUtil.updateIntercom({ + user_id: user.sub, + user_hash: intercomMetadata.hash, + email: user.email, + name: user.name, + last_request_at: updateLastRequestAt + ? Math.floor(new Date().getTime() / 1000) + : undefined, + ...data, + }) + }, + [user, intercomMetadata] + ) + + return { boot, update } +} diff --git a/libs/client/shared/src/hooks/useInterval.ts b/libs/client/shared/src/hooks/useInterval.ts new file mode 100644 index 00000000000..ff22d3e9594 --- /dev/null +++ b/libs/client/shared/src/hooks/useInterval.ts @@ -0,0 +1,18 @@ +import { useEffect, useLayoutEffect, useRef } from 'react' + +export function useInterval(callback: () => any, delay?: number | false | null) { + const savedCallback = useRef(callback) + + // remember the latest callback if it changes. + useLayoutEffect(() => { + savedCallback.current = callback + }, [callback]) + + useEffect(() => { + // don't schedule if no delay is specified + if (!delay) return + + const id = setInterval(() => savedCallback.current(), delay) + return () => clearInterval(id) + }, [delay]) +} diff --git a/libs/client/shared/src/hooks/useLastUpdated.ts b/libs/client/shared/src/hooks/useLastUpdated.ts new file mode 100644 index 00000000000..97f5085704d --- /dev/null +++ b/libs/client/shared/src/hooks/useLastUpdated.ts @@ -0,0 +1,32 @@ +import { DateTime } from 'luxon' +import { useEffect, useState } from 'react' + +export const useLastUpdated: (lastUpdated?: DateTime | Date, showPrefix?: boolean) => string = ( + lastUpdated, + showPrefix = true +) => { + const lastUpdatedNormalized = DateTime.isDateTime(lastUpdated) + ? lastUpdated + : lastUpdated + ? DateTime.fromJSDate(lastUpdated) + : null + + const [lastUpdateString, setLastUpdateString] = useState(lastUpdatedNormalized?.toRelative()) + + useEffect(() => { + const initialVal = lastUpdatedNormalized?.toRelative() + setLastUpdateString( + initialVal === '0 seconds ago' + ? 'just now' + : lastUpdatedNormalized?.toRelative() ?? 'Never' + ) + + const intervalRef = setInterval(() => { + setLastUpdateString(lastUpdatedNormalized?.toRelative() ?? 'Never') + }, 60000) + + return () => clearInterval(intervalRef) + }, [lastUpdatedNormalized]) + + return (showPrefix ? 'Updated ' : '') + lastUpdateString || '' +} diff --git a/libs/client/shared/src/hooks/useLocalStorage.ts b/libs/client/shared/src/hooks/useLocalStorage.ts new file mode 100644 index 00000000000..7494e29a962 --- /dev/null +++ b/libs/client/shared/src/hooks/useLocalStorage.ts @@ -0,0 +1,32 @@ +import type { Dispatch, SetStateAction } from 'react' +import { useEffect, useState } from 'react' + +export function useLocalStorage(key: string, initialValue: T): [T, Dispatch>] { + const [value, setValue] = useState(() => { + if (typeof window !== 'undefined') { + try { + const storedValue = localStorage.getItem(key) + + if (storedValue) { + return JSON.parse(storedValue) + } + } catch (e) { + console.warn(`Failed to get ${key} from local storage`, e) + } + } + + return initialValue + }) + + useEffect(() => { + if (typeof window !== 'undefined') { + try { + localStorage.setItem(key, JSON.stringify(value)) + } catch (e) { + console.warn(`Failed to set ${key} in local storage`, e) + } + } + }, [key, value]) + + return [value, setValue] +} diff --git a/libs/client/shared/src/hooks/useLogger.ts b/libs/client/shared/src/hooks/useLogger.ts new file mode 100644 index 00000000000..3c5f6ab3ceb --- /dev/null +++ b/libs/client/shared/src/hooks/useLogger.ts @@ -0,0 +1,12 @@ +import { useContext } from 'react' +import { LogProviderContext } from '../providers/LogProvider' + +export const useLogger = () => { + const logger = useContext(LogProviderContext) + + if (!logger) { + throw new Error('Logger configured incorrectly') + } + + return logger.logger +} diff --git a/libs/client/shared/src/hooks/useModalManager.ts b/libs/client/shared/src/hooks/useModalManager.ts new file mode 100644 index 00000000000..66c9a7734f2 --- /dev/null +++ b/libs/client/shared/src/hooks/useModalManager.ts @@ -0,0 +1,18 @@ +import { createContext, useContext } from 'react' + +export type ModalKey = 'linkAuth0Accounts' +export type ModalManagerAction = + | { type: 'open'; key: ModalKey; props: any } + | { type: 'close'; key: ModalKey } +export type ModalManagerContext = { + dispatch(action: ModalManagerAction): void +} +export const ModalManagerContext = createContext(null) + +export function useModalManager() { + const ctx = useContext(ModalManagerContext) + + if (!ctx) throw new Error('useModalManager must be used from within ') + + return ctx +} diff --git a/libs/client/shared/src/hooks/usePlaid.ts b/libs/client/shared/src/hooks/usePlaid.ts new file mode 100644 index 00000000000..c063b444894 --- /dev/null +++ b/libs/client/shared/src/hooks/usePlaid.ts @@ -0,0 +1,134 @@ +import * as Sentry from '@sentry/react' +import { DateTime } from 'luxon' +import { useEffect, useState } from 'react' +import { usePlaidLink } from 'react-plaid-link' +import { BrowserUtil } from '..' +import { usePlaidApi } from '../api' +import { useAccountContext } from '../providers' +import { useLogger } from './useLogger' +import toast from 'react-hot-toast' +import axios from 'axios' +import { useRouter } from 'next/router' + +/** + * Opens Plaid link + * + * Depending on mode set, different behavior: + * + * - `default` - user explicitly opens link flow by clicking + * - `oauth` - Link flow is implicitly re-initialized based on a prior link session + * + * @see https://plaid.com/docs/link/oauth/ + */ +export function usePlaid(mode: 'default' | 'oauth' = 'default') { + const logger = useLogger() + const router = useRouter() + + const [clientToken, setClientToken] = useState(null) + + const { setAccountManager } = useAccountContext() + const { useExchangePublicToken, useCreateLinkToken, useGetLinkToken } = usePlaidApi() + + const exchangePublicToken = useExchangePublicToken() + const createLinkToken = useCreateLinkToken() + const getLinkToken = useGetLinkToken({ enabled: mode === 'oauth' }) + + const { + ready: isPlaidLinkReady, + open: openPlaidLink, + error: plaidError, + } = usePlaidLink({ + token: mode === 'default' ? clientToken : getLinkToken.data?.token ?? null, + receivedRedirectUri: mode === 'default' ? undefined : window.location.href, + onSuccess: async (publicToken, metadata) => { + try { + await exchangePublicToken.mutateAsync({ + token: publicToken, + institution: metadata.institution, + }) + + if (mode === 'oauth') { + router.push('/accounts') + } + } catch (e) { + if (axios.isAxiosError(e)) { + if (e.response?.data.errors[0].title === 'USD_ONLY') { + toast.error('Non-USD accounts are not supported yet.') + } + + if (e.response?.data.errors[0].title === 'MAX_ACCOUNT_CONNECTIONS') { + toast.error( + 'You have reached the maximum number of connections per user account. Please reach out to us or delete an existing connection to continue.', + { + duration: 12000, + } + ) + } + } + } + }, + // https://plaid.com/docs/link/web/#onexit + onExit: (error, metadata) => { + if (error) { + const { error_code, error_type, error_message, display_message } = error + BrowserUtil.trackIntercomEvent(`PLAID_LINK_EXIT_ERROR`, { + error_type, + error_code, + error_message, + display_message, + reference: 'https://plaid.com/docs/errors/', + }) + } + + BrowserUtil.trackIntercomEvent('PLAID_EXIT_EVENT', { + ...error, + ...metadata, + }) + }, + // https://plaid.com/docs/link/web/#onevent + onEvent: (event, metadata) => { + // Capture all events to Sentry (will be grouped) + Sentry.captureEvent({ + level: event === 'ERROR' ? 'error' : 'info', + message: `PLAID_LINK_${event}`, + timestamp: DateTime.fromISO(metadata.timestamp).toSeconds(), + tags: { + ...metadata, + }, + }) + + // Capture all events to Intercom + BrowserUtil.trackIntercomEvent(event, metadata) + + logger.debug( + `Plaid link event: ${event} for session ID ${metadata.link_session_id}`, + metadata + ) + }, + }) + + useEffect(() => { + if (isPlaidLinkReady) { + openPlaidLink() + + if (mode === 'default') { + setAccountManager({ view: 'idle' }) + } else { + console.debug('Re-initialized Plaid link successfully with OAuth') + } + } + }, [isPlaidLinkReady, openPlaidLink, setAccountManager, mode, getLinkToken.data]) + + return { + openPlaid: async (institutionId: string) => { + toast('Initializing Plaid...', { duration: 2_000 }) + + if (mode === 'default') { + const { token } = await createLinkToken.mutateAsync({ institutionId }) + setClientToken(token) + } + }, + isPlaidLinkReady, + fetchTokenError: mode === 'oauth' && (getLinkToken.failureCount > 1 || plaidError != null), + } +} diff --git a/libs/client/shared/src/hooks/useProviderStatus.ts b/libs/client/shared/src/hooks/useProviderStatus.ts new file mode 100644 index 00000000000..22bffd7feee --- /dev/null +++ b/libs/client/shared/src/hooks/useProviderStatus.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react' +import { usePlaidApi } from '../api' + +export function useProviderStatus() { + const { usePlaidStatus } = usePlaidApi() + const plaidStatus = usePlaidStatus() + + const [isCollapsed, setIsCollapsed] = useState(false) + const [statusMessage, setStatusMessage] = useState('') + + useEffect(() => { + const plaidIndicator = plaidStatus.data?.status?.indicator + + if (plaidIndicator && plaidIndicator !== 'none') { + setStatusMessage( + 'Plaid, one of our data providers, is experiencing downtime. As a result, some of your accounts may not sync correctly.' + ) + } + }, [plaidStatus]) + + return { + isCollapsed, + statusMessage, + dismiss: () => setIsCollapsed(true), + expand: () => setIsCollapsed(false), + } +} diff --git a/libs/client/shared/src/hooks/useQueryParam.ts b/libs/client/shared/src/hooks/useQueryParam.ts new file mode 100644 index 00000000000..75782b54060 --- /dev/null +++ b/libs/client/shared/src/hooks/useQueryParam.ts @@ -0,0 +1,38 @@ +import { DateTime } from 'luxon' +import { useRouter } from 'next/router' + +export function useQueryParam(key: string, type: 'string'): string | undefined +export function useQueryParam(key: string, type: 'string[]'): string[] +export function useQueryParam(key: string, type: 'number'): number | undefined +export function useQueryParam(key: string, type: 'date'): Date | undefined +export function useQueryParam(key: string, type: 'boolean'): boolean +export function useQueryParam( + key: string, + type: 'string' | 'string[]' | 'number' | 'date' | 'boolean' +): string | string[] | number | Date | boolean | undefined { + const { query, isReady } = useRouter() + + if (!isReady) return undefined + + const value = query[key] + + switch (type) { + case 'string': + return Array.isArray(value) ? value[0] : value + case 'string[]': + return Array.isArray(value) ? value : value ? [value] : [] + case 'number': + if (!value || typeof value !== 'string') return undefined + return value ? parseInt(value) : undefined + case 'date': { + if (!value || typeof value !== 'string') return undefined + const date = DateTime.fromISO(value) + return date.isValid ? date.toISODate() : undefined + } + case 'boolean': + if (!value || typeof value !== 'string') return false + return value ? ['y', 'yes', 'true', '1'].includes(value.toLowerCase().trim()) : false + default: + throw Error(`unhandled param type: ${type}`) + } +} diff --git a/libs/client/shared/src/hooks/useScreenSize.ts b/libs/client/shared/src/hooks/useScreenSize.ts new file mode 100644 index 00000000000..cdc03499b61 --- /dev/null +++ b/libs/client/shared/src/hooks/useScreenSize.ts @@ -0,0 +1,14 @@ +import { useMediaQuery } from 'react-responsive' + +// Starting very simple, will likely add a Tablet designation in the future +export enum SCREEN { + MOBILE = 'MOBILE', + DESKTOP = 'DESKTOP', +} + +export const useScreenSize = (cb?: () => void): SCREEN => { + // TODO: find a way to get breakpoint from Tailwind config + const isDesktop = useMediaQuery({ query: `(min-width: 1024px)` }, undefined, cb) + + return isDesktop ? SCREEN.DESKTOP : SCREEN.MOBILE +} diff --git a/libs/client/shared/src/index.ts b/libs/client/shared/src/index.ts new file mode 100644 index 00000000000..b3beaad93f8 --- /dev/null +++ b/libs/client/shared/src/index.ts @@ -0,0 +1,6 @@ +export * from './hooks' +export * from './providers' +export * from './components' +export * from './api' +export * as ClientType from './types' +export * as BrowserUtil from './utils' diff --git a/libs/client/shared/src/providers/AccountContextProvider.tsx b/libs/client/shared/src/providers/AccountContextProvider.tsx new file mode 100644 index 00000000000..510a4690408 --- /dev/null +++ b/libs/client/shared/src/providers/AccountContextProvider.tsx @@ -0,0 +1,115 @@ +import type { SetStateAction, Dispatch, ReactNode, PropsWithChildren } from 'react' +import type { SharedType } from '@maybe-finance/shared' +import type { DateRange } from '@maybe-finance/design-system' +import { createContext, useState, useContext } from 'react' +import { DateTime } from 'luxon' +import type { AccountCategory } from '@prisma/client' + +export type AccountValuationFields = { + startDate: string | null + originalBalance: number | null + currentBalance: number | null +} + +// Property +type PropertyMetadataValues = { + line1: string + city: string + state: string + zip: string +} +export type CreatePropertyFields = PropertyMetadataValues & AccountValuationFields +export type UpdatePropertyFields = PropertyMetadataValues + +// Vehicle +type VehicleValues = { make: string; model: string; year: string } + +export type CreateVehicleFields = VehicleValues & AccountValuationFields +export type UpdateVehicleFields = VehicleValues + +// Other +type AssetValues = { name: string; categoryUser: AccountCategory } +export type CreateAssetFields = AssetValues & AccountValuationFields +export type UpdateAssetFields = AssetValues + +// Loan +type LiabilityValues = { name: string; categoryUser: AccountCategory } +type LoanValues = { + name: string + maturityDate: string + interestType: SharedType.Loan['interestRate']['type'] | null + loanType: SharedType.Loan['loanDetail']['type'] | null + interestRate: number | null +} +export type CreateLiabilityFields = LiabilityValues & LoanValues & AccountValuationFields +export type UpdateLiabilityFields = CreateLiabilityFields + +type AccountManager = + | { view: 'idle' } + | { view: 'add-plaid'; linkToken: string } + | { view: 'add-finicity' } + | { view: 'add-account' } + | { view: 'add-property'; defaultValues: Partial } + | { view: 'add-vehicle'; defaultValues: Partial } + | { view: 'add-asset'; defaultValues: Partial } + | { view: 'add-liability'; defaultValues: Partial } + | { view: 'edit-account'; accountId: number } + | { view: 'delete-account'; accountId: number; accountName: string; onDelete?: () => void } + | { view: 'custom'; component: ReactNode } + +export interface AccountContext { + accountManager: AccountManager + setAccountManager: Dispatch> + addAccount(): void + editAccount(account: SharedType.Account): void + deleteAccount(account: SharedType.Account, onDelete?: () => void): void + dateRange: DateRange + setDateRange(newDateRange: DateRange | ((prevDateRange: DateRange) => DateRange)): void +} + +export const AccountContext = createContext(undefined) + +export function useAccountContext() { + const context = useContext(AccountContext) + + if (!context) { + throw new Error('useAccountContext() must be used within ') + } + + return context +} + +export function AccountContextProvider({ children }: PropsWithChildren<{}>) { + const [accountManager, setAccountManager] = useState({ + view: 'idle', + }) + + // Homepage and sidebar shared date range (defaults to "Prior month") + const [dateRange, setDateRange] = useState({ + start: DateTime.now().minus({ days: 30 }).toISODate(), + end: DateTime.now().toISODate(), + }) + + return ( + setAccountManager({ view: 'add-account' }), + editAccount: (account) => + setAccountManager({ view: 'edit-account', accountId: account.id }), + deleteAccount: (account, onDelete) => + setAccountManager({ + view: 'delete-account', + accountId: account.id, + accountName: account.name, + onDelete, + }), + }} + > + {children} + + ) +} diff --git a/libs/client/shared/src/providers/AuthProvider.tsx b/libs/client/shared/src/providers/AuthProvider.tsx new file mode 100644 index 00000000000..e0254d6730e --- /dev/null +++ b/libs/client/shared/src/providers/AuthProvider.tsx @@ -0,0 +1,64 @@ +import type { Auth0ContextInterface } from '@auth0/auth0-react' +import { createContext } from 'react' +import type { PropsWithChildren } from 'react' +import { Auth0Provider, type Auth0ProviderOptions } from '@auth0/auth0-react' +import { useRouter } from 'next/router' + +export const linkAuth0AccountCtx = createContext( + null +) as Auth0ProviderOptions['context'] + +/** + * Auth0 Context Provider + * + * Why 2 configs? + * + * For user account linking, we need two contexts so that when the secondary + * user is authenticated prior to linking, it doesn't log the primary user out. + * + * @see https://github.com/auth0/auth0-react/issues/425#issuecomment-1303619555 + */ +export function AuthProvider({ children }: PropsWithChildren) { + const router = useRouter() + + const sharedConfig: Auth0ProviderOptions = { + domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN || 'REPLACE_THIS', + clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID || 'REPLACE_THIS', + onRedirectCallback: (appState) => router.replace(appState?.returnTo || '/'), + authorizationParams: { + audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE || 'https://maybe-finance-api/v1', + screen_hint: router.pathname === '/register' ? 'signup' : 'login', + }, + } + + const isBrowser = typeof window !== 'undefined' + + return ( + + + {children} + + + ) +} diff --git a/libs/client/shared/src/providers/AxiosProvider.tsx b/libs/client/shared/src/providers/AxiosProvider.tsx new file mode 100644 index 00000000000..0daae1c313f --- /dev/null +++ b/libs/client/shared/src/providers/AxiosProvider.tsx @@ -0,0 +1,139 @@ +import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' +import type { SharedType } from '@maybe-finance/shared' +import { superjson } from '@maybe-finance/shared' +import { createContext, type PropsWithChildren, useCallback, useMemo } from 'react' +import { useAuth0 } from '@auth0/auth0-react' +import Axios from 'axios' +import * as Sentry from '@sentry/react' +import { useRouter } from 'next/router' + +type CreateInstanceOptions = { + getToken?: () => Promise + axiosOptions?: AxiosRequestConfig + serialize?: boolean + deserialize?: boolean +} + +export type AxiosContextValue = { + getToken: () => Promise + defaultBaseUrl: string + axios: AxiosInstance + createInstance: (options?: CreateInstanceOptions) => AxiosInstance +} + +export const AxiosContext = createContext(undefined) + +// Factory fn to create an instance for ad-hoc request types (e.g. multipart/form-data) +function createInstance(options?: CreateInstanceOptions) { + const instance = Axios.create(options?.axiosOptions) + + instance.interceptors.request.use(async (config) => { + if (options?.getToken) { + const token = await options.getToken() + + if (token) { + if (config.headers) { + config.headers.Authorization = `Bearer ${token}` + } + + // For local testing convenience + if (process.env.NODE_ENV === 'development') { + ;(window as any).JWT = token + } + } + } + + if (options?.serialize) { + return { ...config, data: superjson.serialize(config.data) } + } else { + return config + } + }) + + if (options?.deserialize) { + instance.interceptors.response.use((response: AxiosResponse) => { + if (response.data) { + const payload = response.data + + if ('data' in payload) { + return { ...response, data: superjson.deserialize(payload.data) } + } else { + // Don't deserialize an error response + return response + } + } else { + // Don't deserialize a No Content response (i.e. 204) + return response + } + }) + } + + return instance +} + +/** + * Injects the Auth0 access token into every axios request + * + * @see https://github.com/auth0/auth0-react/issues/266#issuecomment-919222402 + */ +export function AxiosProvider({ children }: PropsWithChildren) { + // Rather than storing access token in localStorage (insecure), we use this method to retrieve it prior to making API calls + const { getAccessTokenSilently, isAuthenticated, loginWithRedirect } = useAuth0() + const router = useRouter() + + const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3333' + + const getToken = useCallback(async () => { + if (!isAuthenticated) return null + + try { + const token = await getAccessTokenSilently() + return token + } catch (err) { + const authErr = + err && typeof err === 'object' && 'error' in err && typeof err['error'] === 'string' + ? err['error'] + : null + const isRecoverable = authErr + ? [ + 'mfa_required', + 'consent_required', + 'interaction_required', + 'login_required', + ].includes(authErr) + : false + + if (isRecoverable) { + await loginWithRedirect({ appState: { returnTo: router.asPath } }) + } else { + Sentry.captureException(err) + } + + return null + } + }, [isAuthenticated, getAccessTokenSilently, loginWithRedirect, router]) + + // Expose a default instance with auth, superjson, headers + const defaultInstance = useMemo(() => { + const defaultHeaders = { 'Content-Type': 'application/json' } + return createInstance({ + getToken, + axiosOptions: { baseURL: `${API_URL}/v1`, headers: defaultHeaders }, + serialize: true, + deserialize: true, + }) + }, [getToken, API_URL]) + + return ( + + {children} + + ) +} diff --git a/libs/client/shared/src/providers/LayoutContextProvider.tsx b/libs/client/shared/src/providers/LayoutContextProvider.tsx new file mode 100644 index 00000000000..6050942a73c --- /dev/null +++ b/libs/client/shared/src/providers/LayoutContextProvider.tsx @@ -0,0 +1,19 @@ +import type { PropsWithChildren, RefObject } from 'react' +import { createContext, useContext } from 'react' + +const LayoutContext = createContext<{ overlayContainer: RefObject | undefined }>({ + overlayContainer: undefined, +}) + +export function useLayoutContext() { + return useContext(LayoutContext) +} + +export function LayoutContextProvider({ + overlayContainer, + children, +}: PropsWithChildren<{ + overlayContainer: RefObject +}>) { + return {children} +} diff --git a/libs/client/shared/src/providers/LogProvider.tsx b/libs/client/shared/src/providers/LogProvider.tsx new file mode 100644 index 00000000000..9aae2e4fd7a --- /dev/null +++ b/libs/client/shared/src/providers/LogProvider.tsx @@ -0,0 +1,10 @@ +import type { PropsWithChildren } from 'react' +import { createContext } from 'react' + +export type Logger = Pick + +export const LogProviderContext = createContext<{ logger: Logger }>({ logger: console }) + +export function LogProvider({ logger, children }: PropsWithChildren<{ logger: Logger }>) { + return {children} +} diff --git a/libs/client/shared/src/providers/PopoutProvider.tsx b/libs/client/shared/src/providers/PopoutProvider.tsx new file mode 100644 index 00000000000..5c7809154c8 --- /dev/null +++ b/libs/client/shared/src/providers/PopoutProvider.tsx @@ -0,0 +1,34 @@ +import type { PropsWithChildren, ReactNode } from 'react' +import { createContext, useContext, useState } from 'react' + +export interface PopoutContext { + popoutContents: ReactNode | null + open(component: ReactNode | null): void + close(): void +} + +export const PopoutContext = createContext(undefined) + +export function usePopoutContext() { + const context = useContext(PopoutContext) + + if (!context) throw new Error('usePopoutContext() must be used within ') + + return context +} + +export function PopoutProvider({ children }: PropsWithChildren<{}>) { + const [popoutContents, setPopoutContents] = useState(null) + + return ( + setPopoutContents(null), + }} + > + {children} + + ) +} diff --git a/libs/client/shared/src/providers/QueryProvider.tsx b/libs/client/shared/src/providers/QueryProvider.tsx new file mode 100644 index 00000000000..e0b119ca049 --- /dev/null +++ b/libs/client/shared/src/providers/QueryProvider.tsx @@ -0,0 +1,45 @@ +import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' +import * as Sentry from '@sentry/react' +import Axios from 'axios' + +export interface QueryClientProviderProps { + children: React.ReactNode +} + +const queryClient = new QueryClient({ + queryCache: new QueryCache({ + onError: (error, query) => { + if (Axios.isAxiosError(error)) { + const axiosMessage = Axios.isAxiosError(error) + ? error.response?.data?.errors?.[0]?.title + : null + + Sentry.captureException( + axiosMessage ? new Error(axiosMessage, { cause: error.cause }) : error, + (scope) => { + scope.setTransactionName( + `Query with key: [ ${query.queryKey.join(' > ')} ] failed` + ) + scope.setContext('errors', { + errors: JSON.stringify(error.response?.data?.errors), + }) + + return scope + } + ) + } else { + Sentry.captureException(error) + } + }, + }), +}) + +export function QueryProvider({ children }: QueryClientProviderProps) { + return ( + + {children} + + + ) +} diff --git a/libs/client/shared/src/providers/UserAccountContextProvider.tsx b/libs/client/shared/src/providers/UserAccountContextProvider.tsx new file mode 100644 index 00000000000..7a35ff0f838 --- /dev/null +++ b/libs/client/shared/src/providers/UserAccountContextProvider.tsx @@ -0,0 +1,168 @@ +import type { PropsWithChildren, SetStateAction, Dispatch } from 'react' +import type { SharedType } from '@maybe-finance/shared' +import { createContext, useState, useContext, useEffect, useMemo } from 'react' +import { useQueryClient } from '@tanstack/react-query' +import { useAccountApi } from '../api' +import toast from 'react-hot-toast' +import uniqBy from 'lodash/uniqBy' +import { useInterval } from '../hooks' +import { invalidateAccountQueries } from '../utils' + +export interface UserAccountContext { + isReady: boolean + noAccounts: boolean + allAccountsDisabled: boolean + someConnectionsSyncing: boolean + someAccountsSyncing: boolean + accountSyncing: (accountId: SharedType.Account['id']) => boolean + accountsSyncing: SharedType.Account[] + connectionsSyncing: SharedType.ConnectionWithAccounts[] + syncProgress?: SharedType.AccountSyncProgress + expectingAccounts: boolean + setExpectingAccounts: Dispatch> +} + +export const UserAccountContext = createContext(undefined) + +export function useUserAccountContext() { + const context = useContext(UserAccountContext) + + if (!context) { + throw new Error('useUserAccountContext() must be used within ') + } + + return context +} + +export function UserAccountContextProvider({ children }: PropsWithChildren<{}>) { + const queryClient = useQueryClient() + + const { useAccounts } = useAccountApi() + + const [noAccounts, setNoAccounts] = useState(false) + const [allAccountsDisabled, setAllAccountsDisabled] = useState(false) + const [someConnectionsSyncing, setSomeConnectionsSyncing] = useState(false) + const [someAccountsSyncing, setSomeAccountsSyncing] = useState(false) + const [accountsSyncing, setAccountsSyncing] = useState([]) + const [connectionsSyncing, setConnectionsSyncing] = useState< + (SharedType.ConnectionWithAccounts & SharedType.ConnectionWithSyncProgress)[] + >([]) + const [expectingAccounts, setExpectingAccounts] = useState(false) + + const syncProgress: SharedType.AccountSyncProgress | undefined = useMemo(() => { + if (expectingAccounts) { + return { description: 'Importing accounts', progress: 0 } + } + + if (!connectionsSyncing.length) { + return undefined + } + + const allSyncProgress = connectionsSyncing + .map(({ syncProgress }) => syncProgress) + .filter((sp): sp is SharedType.AccountSyncProgress => sp != null) + .sort((a, b) => Number(a.progress) - Number(b.progress) ?? 0) + + return allSyncProgress.length + ? allSyncProgress[0] + : { description: 'Importing accounts', progress: 0.1 } + }, [connectionsSyncing, expectingAccounts]) + + const accountsQuery = useAccounts({ + onSuccess: ({ accounts, connections }) => { + // determine connections to show "successfully synced" notification for + accounts + .filter( + (a) => a.syncStatus === 'IDLE' && accountsSyncing.some((sc) => sc.id === a.id) + ) + .forEach((account) => { + toast.success(`${account.name} synced`) + }) + + // An account is "syncing" if the account itself is syncing OR its parent connection is syncing ("implicitly syncing") + const individualAccountsSyncing = accounts.filter((a) => a.syncStatus !== 'IDLE') + const childAccountsSyncing = connections + .filter((c) => c.syncStatus !== 'IDLE') + .flatMap((c) => c.accounts) + + const newAccountsSyncing = uniqBy( + [...individualAccountsSyncing, ...childAccountsSyncing], + 'id' + ) + + setAccountsSyncing(newAccountsSyncing) + + connections + .filter( + (c) => + c.syncStatus === 'IDLE' && connectionsSyncing.some((sc) => sc.id === c.id) + ) + .forEach((connection) => { + connection.status === 'ERROR' + ? toast.error(`${connection.name} failed to sync`) + : toast.success(`${connection.name} synced`) + }) + + setConnectionsSyncing( + connections.filter((c) => c.syncStatus === 'PENDING' || c.syncStatus === 'SYNCING') + ) + + setNoAccounts( + accounts.length === 0 && connections.every((c) => c.accounts.length === 0) + ) + + const allAccountsDisabled = accounts.every((a) => !a.isActive) + const allConnectionsDisabled = connections.every((c) => + c.accounts.every((a) => !a.isActive) + ) + + setAllAccountsDisabled(allAccountsDisabled && allConnectionsDisabled) + + const someConnectionsSyncing = connections.some((c) => c.syncStatus !== 'IDLE') + const someAccountsSyncing = + accounts.some((a) => a.syncStatus !== 'IDLE') || + connections.some( + (c) => c.syncStatus !== 'IDLE' && c.accounts.some((a) => a.isActive) + ) + + setSomeConnectionsSyncing(someConnectionsSyncing) + setSomeAccountsSyncing(someAccountsSyncing) + }, + }) + + useInterval( + () => invalidateAccountQueries(queryClient), + someAccountsSyncing || someConnectionsSyncing || expectingAccounts ? 2_000 : undefined + ) + + useEffect(() => { + if (!someAccountsSyncing) invalidateAccountQueries(queryClient) + }, [queryClient, someAccountsSyncing]) + + useEffect( + () => setExpectingAccounts(false), + [accountsQuery.data?.connections.length, accountsQuery.data?.accounts.length] + ) + + return ( + { + return !!accountsSyncing.find((a) => a.id === accountId) + }, + accountsSyncing, + connectionsSyncing, + syncProgress, + expectingAccounts, + setExpectingAccounts, + }} + > + {children} + + ) +} diff --git a/libs/client/shared/src/providers/index.ts b/libs/client/shared/src/providers/index.ts new file mode 100644 index 00000000000..3a82ada872a --- /dev/null +++ b/libs/client/shared/src/providers/index.ts @@ -0,0 +1,8 @@ +export * from './AxiosProvider' +export * from './LayoutContextProvider' +export * from './LogProvider' +export * from './QueryProvider' +export * from './AccountContextProvider' +export * from './UserAccountContextProvider' +export * from './PopoutProvider' +export * from './AuthProvider' diff --git a/libs/client/shared/src/types/client-side-feature-flags.ts b/libs/client/shared/src/types/client-side-feature-flags.ts new file mode 100644 index 00000000000..7d704965499 --- /dev/null +++ b/libs/client/shared/src/types/client-side-feature-flags.ts @@ -0,0 +1,5 @@ +export type MetricStatus = 'coming-soon' | 'under-construction' | 'active' + +export type ClientSideFeatureFlag = Partial<{ + maintenance: boolean +}> diff --git a/libs/client/shared/src/types/index.ts b/libs/client/shared/src/types/index.ts new file mode 100644 index 00000000000..786fee6d88b --- /dev/null +++ b/libs/client/shared/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './client-side-feature-flags' +export * from './react-types' diff --git a/libs/client/shared/src/types/react-types.ts b/libs/client/shared/src/types/react-types.ts new file mode 100644 index 00000000000..0cf22266699 --- /dev/null +++ b/libs/client/shared/src/types/react-types.ts @@ -0,0 +1,5 @@ +import type { ReactElement, ReactHTML, ReactNode } from 'react' + +export type WithChildrenRenderProps = Props & { + children?: ReactNode | ((props: RenderProps) => ReactElement) +} diff --git a/libs/client/shared/src/utils/account-utils.ts b/libs/client/shared/src/utils/account-utils.ts new file mode 100644 index 00000000000..2bef2471549 --- /dev/null +++ b/libs/client/shared/src/utils/account-utils.ts @@ -0,0 +1,78 @@ +import type { SharedType } from '@maybe-finance/shared' +import type { AccountCategory } from '@prisma/client' +import type { QueryClient } from '@tanstack/react-query' +import { DateTime } from 'luxon' +import { + RiBankCard2Line, + RiBankLine, + RiBitCoinLine, + RiCarLine, + RiFolderLine, + RiHandCoinLine, + RiHomeLine, + RiLineChartLine, + RiVipDiamondLine, +} from 'react-icons/ri' + +export function getCategoryColorClassName(category: AccountCategory) { + return ( + ( + { + cash: 'text-blue', + investment: 'text-teal', + crypto: 'text-orange', + property: 'text-pink', + vehicle: 'text-grape', + valuable: 'text-green', + loan: 'text-red', + credit: 'text-indigo', + other: 'text-cyan', + } as Record + )[category] ?? 'text-cyan' + ) +} + +export function getCategoryIcon(category: AccountCategory) { + return ( + ( + { + cash: RiBankLine, + investment: RiLineChartLine, + crypto: RiBitCoinLine, + property: RiHomeLine, + vehicle: RiCarLine, + valuable: RiVipDiamondLine, + loan: RiHandCoinLine, + credit: RiBankCard2Line, + other: RiFolderLine, + } as Record + )[category] ?? RiFolderLine + ) +} + +/** + * Invalidates account queries and optionally account aggregate queries (i.e. net worth, insights) + */ +export function invalidateAccountQueries(queryClient: QueryClient, aggregates = true) { + queryClient.invalidateQueries(['accounts']) + queryClient.invalidateQueries(['users', 'onboarding']) + + if (aggregates) { + queryClient.invalidateQueries(['users', 'net-worth']) + queryClient.invalidateQueries(['users', 'insights']) + } +} + +export function formatLoanTerm({ + originationDate, + maturityDate, +}: Pick) { + const start = DateTime.fromISO(originationDate!) + const end = DateTime.fromISO(maturityDate!) + const months = end.diff(start, 'months').toObject().months! + + // if the total months is within 1 month of a year, display years + return months % 12 < 1 || months % 12 > 11 + ? `${Math.round(months / 12)} years` + : `${Math.round(months)} months` +} diff --git a/libs/client/shared/src/utils/agreement-utils.ts b/libs/client/shared/src/utils/agreement-utils.ts new file mode 100644 index 00000000000..4b8d3a5f381 --- /dev/null +++ b/libs/client/shared/src/utils/agreement-utils.ts @@ -0,0 +1,18 @@ +import type { AgreementType } from '@prisma/client' + +export function agreementName(type: AgreementType): string { + switch (type) { + case 'fee': + return 'Limited Scope Advisory Agreement' + case 'form_adv_2a': + return 'Form ADV Part 2A' + case 'form_adv_2b': + return 'Form ADV Part 2B' + case 'form_crs': + return 'Form CRS' + case 'privacy_policy': + return 'Advisor Privacy Policy' + default: + return type + } +} diff --git a/libs/client/shared/src/utils/auth-utils.ts b/libs/client/shared/src/utils/auth-utils.ts new file mode 100644 index 00000000000..34ed8852229 --- /dev/null +++ b/libs/client/shared/src/utils/auth-utils.ts @@ -0,0 +1,7 @@ +import type { User } from '@auth0/auth0-react' + +export function hasRole(user: User | null | undefined, role: 'Admin' | 'Advisor'): boolean { + if (!user) return false + const roles = user['https://maybe.co/roles'] + return roles && Array.isArray(roles) && roles.includes(role) +} diff --git a/libs/client/shared/src/utils/browser-utils.ts b/libs/client/shared/src/utils/browser-utils.ts new file mode 100644 index 00000000000..0af3ac8c4d4 --- /dev/null +++ b/libs/client/shared/src/utils/browser-utils.ts @@ -0,0 +1,55 @@ +import type { + PlaidLinkError, + PlaidLinkOnEventMetadata, + PlaidLinkOnExitMetadata, +} from 'react-plaid-link' + +export async function copyToClipboard(text: string) { + if ('clipboard' in navigator) { + return navigator.clipboard.writeText(text) + } else { + return document.execCommand('copy', true, text) + } +} + +export function getLocalStorageSession(key: string, initialValue: TData) { + return { + getLocalStorageItem: () => { + if (typeof window === 'undefined') return initialValue + + const item = window.localStorage.getItem(key) + + return item ? (JSON.parse(item) as TData) : initialValue + }, + setLocalStorageItem: (data: TData | ((data: TData) => TData)) => { + if (typeof window === 'undefined') return + + const previousValue = window.localStorage.getItem(key) + + const isFunc = data instanceof Function + + window.localStorage.setItem( + key, + JSON.stringify( + isFunc ? data(previousValue ? JSON.parse(previousValue) : initialValue) : data + ) + ) + }, + } +} + +export function prepareSentryContext( + plaidMetadata: PlaidLinkOnExitMetadata | PlaidLinkOnEventMetadata, + plaidError?: PlaidLinkError | null +): { [key: string]: any } | undefined { + if (!plaidError && !plaidMetadata) return undefined + + // The keys here will be the headers in Sentry context sections (hence the formatted spacing) + const errorContext = plaidError ? { 'Plaid Error': plaidError } : undefined + const metadataContext = plaidMetadata ? { 'Plaid Metadata': plaidMetadata } : undefined + + return { + ...errorContext, + ...metadataContext, + } +} diff --git a/libs/client/shared/src/utils/form-utils.ts b/libs/client/shared/src/utils/form-utils.ts new file mode 100644 index 00000000000..0f57e902f80 --- /dev/null +++ b/libs/client/shared/src/utils/form-utils.ts @@ -0,0 +1,39 @@ +import { DateTime } from 'luxon' +import { DateUtil } from '@maybe-finance/shared' +import defaults from 'lodash/defaults' + +const { MIN_SUPPORTED_DATE, MAX_SUPPORTED_DATE, isToday } = DateUtil + +type ValidateFormDateOpts = { + required?: boolean + minDate?: string + maxDate?: string +} + +export function validateFormDate(date: string | null, opts?: ValidateFormDateOpts) { + const _opts = defaults({}, opts) + + if (!date) { + const isRequired = _opts.required ?? true + return isRequired ? 'Valid date required' : true + } + + const _date = DateTime.fromISO(date) + + if (!_date.isValid) return 'Invalid date' + + const minDate = _opts.minDate ? DateTime.fromISO(_opts.minDate) : MIN_SUPPORTED_DATE + const maxDate = _opts.maxDate ? DateTime.fromISO(_opts.maxDate) : MAX_SUPPORTED_DATE + + if (_date < minDate) { + return `Date must be ${minDate.toFormat('MMM dd yyyy')} or later` + } + + if (_date.endOf('day') > maxDate.endOf('day')) { + return isToday(maxDate.toISODate(), DateTime.utc()) + ? 'Date cannot be in future' + : `Date must be ${maxDate.toFormat('MMM yyyy')} or earlier` + } + + return true +} diff --git a/libs/client/shared/src/utils/image-loaders.ts b/libs/client/shared/src/utils/image-loaders.ts new file mode 100644 index 00000000000..aa4941bd47e --- /dev/null +++ b/libs/client/shared/src/utils/image-loaders.ts @@ -0,0 +1,17 @@ +import type { ImageLoaderProps } from 'next/legacy/image' + +export function enhancerizerLoader({ src, width }: ImageLoaderProps): string { + const parsed = JSON.parse(src) as { [key: string]: string | number } + parsed.width ??= width + parsed.height ??= width + + const queryString = Object.entries(parsed) + .map((pair) => pair.map(encodeURIComponent).join('=')) + .join('&') + + return `https://enhancerizer.maybe.co/images?${queryString}` +} + +export function s3Loader({ src }: Pick): string { + return `https://assets.maybe.co/images/${src}` +} diff --git a/libs/client/shared/src/utils/index.ts b/libs/client/shared/src/utils/index.ts new file mode 100644 index 00000000000..9aabdd80b9b --- /dev/null +++ b/libs/client/shared/src/utils/index.ts @@ -0,0 +1,7 @@ +export * from './image-loaders' +export * from './intercom' +export * from './browser-utils' +export * from './account-utils' +export * from './agreement-utils' +export * from './form-utils' +export * from './auth-utils' diff --git a/libs/client/shared/src/utils/intercom.ts b/libs/client/shared/src/utils/intercom.ts new file mode 100644 index 00000000000..73817c57d91 --- /dev/null +++ b/libs/client/shared/src/utils/intercom.ts @@ -0,0 +1,36 @@ +export type IntercomData = { + user_id?: string + user_hash?: string + email?: string + name?: string + + last_request_at?: number + + 'Manual Accounts'?: number + 'Connected Accounts'?: number + Connections?: number +} + +export function bootIntercom(data?: IntercomData) { + const w = window as any + w.Intercom('boot', { + app_id: w.INTERCOM_APP_ID, + ...data, + }) +} + +export function updateIntercom(data?: IntercomData) { + ;(window as any).Intercom('update', { + ...data, + }) +} + +export function trackIntercomEvent(name: string, data: Record) { + ;(window as any).Intercom('trackEvent', name, { + ...data, + }) +} + +export function showIntercom() { + ;(window as any).Intercom('show') +} diff --git a/libs/client/shared/tsconfig.json b/libs/client/shared/tsconfig.json new file mode 100644 index 00000000000..9c06f2fd4cc --- /dev/null +++ b/libs/client/shared/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/client/shared/tsconfig.lib.json b/libs/client/shared/tsconfig.lib.json new file mode 100644 index 00000000000..5f85a42404f --- /dev/null +++ b/libs/client/shared/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx", "jest.config.ts"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/client/shared/tsconfig.spec.json b/libs/client/shared/tsconfig.spec.json new file mode 100644 index 00000000000..b6ee8c9ce47 --- /dev/null +++ b/libs/client/shared/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/libs/design-system/.babelrc b/libs/design-system/.babelrc new file mode 100644 index 00000000000..ac4757ac35f --- /dev/null +++ b/libs/design-system/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@nrwl/react/babel", + { + "runtime": "automatic", + "useBuiltIns": "usage" + } + ] + ], + "plugins": [] +} diff --git a/libs/design-system/.eslintrc.json b/libs/design-system/.eslintrc.json new file mode 100644 index 00000000000..1f5ec76fbb9 --- /dev/null +++ b/libs/design-system/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/design-system/.storybook/main.js b/libs/design-system/.storybook/main.js new file mode 100644 index 00000000000..c6d714230c2 --- /dev/null +++ b/libs/design-system/.storybook/main.js @@ -0,0 +1,24 @@ +const rootMain = require('../../../.storybook/main') + +module.exports = { + ...rootMain, + + core: { ...rootMain.core, builder: 'webpack5' }, + stories: [ + ...rootMain.stories, + '../docs/**/*.stories.mdx', + '../src/lib/**/*.stories.mdx', + '../src/lib/**/*.stories.@(js|jsx|ts|tsx)', + ], + addons: [...rootMain.addons, '@nrwl/react/plugins/storybook'], + webpackFinal: async (config, { configType }) => { + // apply any global webpack configs that might have been specified in .storybook/main.js + if (rootMain.webpackFinal) { + config = await rootMain.webpackFinal(config, { configType }) + } + + // add your own webpack tweaks if needed + + return config + }, +} diff --git a/libs/design-system/.storybook/manager.js b/libs/design-system/.storybook/manager.js new file mode 100644 index 00000000000..2495bcf4ba3 --- /dev/null +++ b/libs/design-system/.storybook/manager.js @@ -0,0 +1,6 @@ +import { addons } from '@storybook/addons' +import theme from './theme' + +addons.setConfig({ + theme, +}) diff --git a/libs/design-system/.storybook/preview.js b/libs/design-system/.storybook/preview.js new file mode 100644 index 00000000000..b5526e388f9 --- /dev/null +++ b/libs/design-system/.storybook/preview.js @@ -0,0 +1,31 @@ +import '../assets/styles.css' + +import theme from './theme' + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + viewMode: 'docs', // Show Docs tab by default + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, + backgrounds: { + default: 'dark', + values: [ + { + name: 'dark', + value: '#16161A', + }, + ], + }, + docs: { + theme, + }, + options: { + storySort: { + order: ['Getting Started', ['About', 'Colors', 'Typography'], 'Components'], + }, + }, +} diff --git a/libs/design-system/.storybook/public/favicon.ico b/libs/design-system/.storybook/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..545f4eee2137320e3ac6550375270276aa53fd2d GIT binary patch literal 15086 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 In|g`+FVBKSzW@LL literal 0 HcmV?d00001 diff --git a/libs/design-system/.storybook/theme.js b/libs/design-system/.storybook/theme.js new file mode 100644 index 00000000000..f292e68bcd8 --- /dev/null +++ b/libs/design-system/.storybook/theme.js @@ -0,0 +1,18 @@ +import { create } from '@storybook/theming' +import logo from '../assets/logo.svg' + +export default create({ + base: 'dark', + + brandTitle: 'Maybe', + brandUrl: 'https://maybe.co', + brandImage: logo, + + fontBase: '"General Sans", sans-serif', + + colorPrimary: '#4361EE', + colorSecondary: '#F12980', + + appBg: '#1C1C20', + appContentBg: '#16161A', +}) diff --git a/libs/design-system/.storybook/tsconfig.json b/libs/design-system/.storybook/tsconfig.json new file mode 100644 index 00000000000..03ac16921af --- /dev/null +++ b/libs/design-system/.storybook/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "emitDecoratorMetadata": true, + "outDir": "" + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts", + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "../**/*.spec.ts", + "../**/*.test.ts", + "../**/*.spec.js", + "../**/*.test.js", + "../**/*.spec.tsx", + "../**/*.test.tsx", + "../**/*.spec.jsx", + "../**/*.test.jsx", + "jest.config.ts" + ], + "include": ["../src/**/*", "*.js", "../docs"] +} diff --git a/libs/design-system/README.md b/libs/design-system/README.md new file mode 100644 index 00000000000..59b806856c8 --- /dev/null +++ b/libs/design-system/README.md @@ -0,0 +1,5 @@ +# Maybe Design System + +Components and patterns used at [Maybe](https://maybe.co) + +[designsystem.maybe.co](https://designsystem.maybe.co) diff --git a/libs/design-system/assets/fonts/inter/Inter-Variable.woff2 b/libs/design-system/assets/fonts/inter/Inter-Variable.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..365eedc50cd0f46ea35a3176335fc67b51123fb4 GIT binary patch literal 324864 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≫bY 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≥WH>mXX2x~)`+}IJu;;+Vs!_lt3eGFY7bo!VMXfVAU$4%xp#RwwS z`f|s`Ry3%#8E<|WpDI-1`RFCYYA-KrwCh*}Pe&{-PmHh=NTc^reaw>|PgY(tONo}8 zaN;JTKjMB~D$~WtLxL!)X%&{sP_nP)1Y;sIxbR*{T==FVtnOInzxtYZY_Hz`OE5J(NstWl?bcY&>a+&JieF-64+>2d22bPpzm&_Ri-wlGUC%sN-&?pV$gX! zI*#e*sOd_lB;HF(GmJ?lWub}wEH(xzStt5IHyvGGUS$j_39mFT*kpv$=bWiH5W7vz zL2?{n?84&}-cfxJlv8}i6D(fRdF}?|Z)0xe-PDNv-MZUby;r@Q)~rNA=!i$yjm+b~ zTyukt9yoJ^-~*+WB99K`o;c9j5)6e=c}QEON{QeFfe&i3yztD{@k}c_q2S__^7Zy&O_G}lK##R4X=&`^807%~kj&=K z%ipfwUnphz0*t1zlzd)5U~seL8s$mZ)Ymf?R#=2}kY0T{3PCK}UC~iXiD025lW~z| zidq8hK+$g$1N8l~qip?9>HXjkiFp~%uby6K?9jL}l%cY#b)x#4?9SU8-X*p(8RE{% zWDDLU?4{<|@2B$rF$=uJYY+L#r6Q%RNT*>sJMhKT1mfq3BJraeQRWZIi)TA+mdq9M z!TF2a)wQ&$o25--pb6Mei|6E6wr$r&5<`2|Fw?(CIe#kd=)*>O-1Ou4#;oV5kuR$n zJV`w3fo5+=XXiO~A{8D2*l+ylF7KIV}uib{Lfu z6o!Nb6_A$t7%GFAUuVehPoeUT{_<#)i5k$^p6I2xhJZEh4}nzGcIU-f>o-u3vd1Ql zT0;6Q)T185!z3)RHEa}>NuPsCyiR3xM^k}N4MW40Xf5;lo)eZrK6=n(qdYgz=ai59 z0x*E)@s{{$N8}2s8O@LUiSKR^M+3%_Nu{#)316@4^}ct^Wv<<{+TY_>Dg5H1{Ze4z)YIVpzu-1v4aM$cdXlYj%sG*_&glGV?QnJ7xye0fayT z9tEArX)tB)W(U#23K1kwfFc}7`gIM^1dY%P7vNF^SWC@xoOqe*yLiig3q-~eT< zla=Od1{}c-MWZ_j0r%rV%k>$gI=17Q06$FwzsAA#t1XnS*R;yK^||ECmBPQjN@6Q# zvhm+bQXQuA2)hL(RHtOJ- z*kwG0bMJ1PlJh7b@801uj9RZAgpoxGfcv_Om0|A8R=x-v} zZ*w8x>L`BSjI2R;$p)_X;giA-3~EjxSANP-v6g{kQOlKta=y8IT>@t_8L)>Mub3Ei z{-DqKvx_V3I?CpX*tI{SPd)o!JW##ANu_0Ub}^;N$+^N4m7$vUZaMjV;@;l$rIO0# zZ^?zz=_maEK3fEPFx;aSVy^d06Q_1v_bCP|1SP8Z0EI1FdhRXE4=;4)r6n0nk#yLzc&?&RG|9D&7p)fMLV(-Dp*%ucj9m`#UUdTqA zDY(*WeKT~Rl_E$CztyqY(K8AXoL-tt9Uu&7c51c}27^W_R~J*!Si?U>S=S*@}cKc*^z|wKDL?3${o={DkelNL9{d& zZ@K=h&TIPS4%-{+!Q1~fNM!{82#`PoF9b;IHhhG7u84oC+xpu-=?$Ygufu1& zJF*O43mlm)hrgQ<2cwGZ16{8*uN5OVh;S}n<)WaM-@7aO)tgo=vF&K-@9#HC4l|j( zh6Xyd{1hptAMG@K%Hh-K^9HS{sBLTW?TM48m3KCQVhIRt;|L?}umZ4(e8actL)0>U z>NYrhP9skRsFevl!zO;$2sn6wxz)ngB4|uB7?3*k`y`q9C-1>Of{g_WBs1;xbDkI9 zY)_chS1P^@nCcDr0M@v{mNO&Fj{wE-#e@cfkWUgmq_zIs`AT1T=)M1RFK+o?Tl~ew zb7oqi)q-Kea`US$3jV;M-#Fv#zSu!rTUM3JGvZ znpEpJNIAzYa(&Kg%`3%DpOpWdvr{=QXyiAh{vv5OrPq`!Dp#8E8QeDg z@O{obp|lpK!HZrI#Hmc(?vm3-W`s%d8pzl4o1mN99yx%CVs60WIYqsxGXq`a?&bYm zZ$T*sdLs2Tbf)O|r|3hx`U#Xd<;Y<|PSYHgXt*vO-=e9*AL%!EZ7H8dJ*+{ zs9idaSyO+D8EjwE;vAz6KhtC{?()>rXDH{{cK@+I6+3BOc@5h#KKme*%k{+5JZFc- z5cha$W1DjhrmO+dMNNmNC0T`AUwTb(_l|gdW4w zW@TyN`MW+L_;Nk={?K-JM|Y?{Iqpc@`u%~6`7&vL4Ev7-Vz*u7`9WZ6{qvQ?9L?Cyj3 zw`&_2@2Yj<3y1jg$>!AbXbw zv=@9_5|NK_#{2p-5?J*tH49o-?gtNrW8+Q_EXVvS;q<%wd9Q<(@$YflW^we|9BP0>&m(Mo@%$L0cDdTmN2##LIf zblZKeSd}5;2OPn(H+-!r@x;Ug*yM-ck5$W8@u6E<4$c?!{Ay;o_>?*RzlQnyzKtuE zcv9ho3f9iCZ)KV~zqGlwnBExVz&J^yWCy%QSkS}Oq^?ucozg(h4A0Y9GS5#cKpG$z z+W=1?0Wo=!($Uv?;*rUdKe6OaUmJ5% zDR=R{gCId@nM{R)nsnyT-3Lq4<{Q?B`7`=)|1os#Ud<7jIcr%&ow(6s|8|0yK9V;J^+zSRsJgv8sag zp={EVs9}3`#63r*O%n$&rT{cCWqjh4p(!#BW$1%EJWV{ovJW6fGt+n700#jAY-8y% zhyneRuCqoDSUdx!`tgH0W@PD!C*$p*Ee{1XNQnu?@cxDT6$t-_P^No+-u ze+H({zsQ8VD_@CoYiz>uy!Y#g2XvpR&1W*@!IvTln~9zmEV-WYL$JUj8pyeKqY#yI+T>y4F_HvxlOcRDJie2kn#9HrNJ60Sqr+U|gLQ zE5fMfcfCNBZTyXiC#d5f+LG!!JfkEy$T^-PwyY20fDm=a}5UU1l?l*=ppl#=hxzfeLFk{$NuWJrI#t25qkb4@-^%&^Mb zn3n)NJA?-^7vd&~7L&9dBx$*)sn4lYUxDRGMh^D zywsXkC+;jxOr@W!J?nPnm{-k_NoMWREVFiInpw9o&%A0nR>ABA)o|^d2J!fYRPkKMeWq_=%Ey zPduscOdc=5AiaF^_cc@hSXe$qJ|TR0+)<*AMZJ)E3iTxFt*PtZ0gAjY;%!fXb5{6-c0TII zez7}!OoB-S+@7Zph}XBgx}t}=qKCSqhx%X-^_3p#Cq2|$kKJxXZK&I&)bl|7rHA}W z1!BK$`!vx~Y6B`#4^gCO;UiV>yYdFP`5ea1A$f+05$AZ$*|NT!6_ACJ)}2-7NAQgb zImaa9oPO!7lBtIhBZ}}*>+U!aOo!T__W1%KLL4`MExA%J0|Q?}EJ@>e9`ksf>d> z{D+D&5TREy*SVH**qgH~)Ai!|5}RDwQ*;&FkyG{+jA6?#MotW4L}C~t;Y;&&?OWQl zZ+F+eWUwDUZFcR=NjscWnC~ruPuzJXOvwubIe3(p`XsWB`h)EE2x@XRG%m8}>8ZvZ zTDfI4&gv#>c0sfIU9Og$?E8{EY=z%y-ccn}7B{H64cE>#vSh^_m6Ha>`b>1YlS_i( zcC09%N=!MFD1A~tL%uuzQVAcP!C9UQ5_v?C%9BVJA;o~GpePh5B@_u=>KAMn%~9~pnLV;{4 z6hC*k8|SX53KYq)h|iSk3Z5on3UW_v(3F;P6GKdd(5&=+c}~Vm(ffIBR8Y9Wly8GP zEQwc}HN{fLf+7KWHN%4&%S@?J8852a;(8vHH4loKBybRbe7?hP=xc>bvpKtu1|ot` zQz{Ddc*&8TvD-a$opvAJ!gL46#}8gb$Q54eqXCxVqT)*`!f3UEhEOOXMKYyCHEN=s z;kkw{4P?$RvTk6kKOiU1ZI&D915f5l^F}uhi!;4kRf{gOX5V@4_$F@{auhaUnsfTn z`EMwBt)`>Eh+BAaldv&Q#2rwgJf*Znc#h|QkFx#_iij4bXvE)Pr$l@Zf{D-R2^X)Q zv&TkQJ%!7kamHPGNl0YFh+^6)zxc!e6=DAh^3T5Gu6!Zr^@Hf^6VtzlpW-LEu=Dd; zP7$eAV5&$*N})KR5(I`BduaD+<)qjcXo`-D;PvEaEuWFMcBWL zF%~|Xm0vQeVC0b_p`t2&8+#kL4u>!pqcj#2sU=QLADZTndxnw!&M+v-1jjjqiDfe` zC;vfGVw{U}PP!a}_4#G4dBx_no?1s8BkRHO3N}ulfD?rzlt??JekXB~D&aT3ystH* z+&${xSMvNW>%10WqxuJlZ;LWaK0OxXchZzoDuwD!GQq-+XgTOkoZa7lGkuO$pbvo`o(aigv*=tD zOa_o)Dr{P$!ehcN-nLdnyga7Rm8P=DZ=4_B}f_@RM;-yg3^AyXb2{pmnr3oWH z)R&oq4;iUqU^7yAMf3jByY!CderS15y{mN-;}2NNVgoP z7e{pOK9hr>kDzK3sGQVlM%KfK@mnHb3`UMz=;~#KYI}?07{<74GQ5W5X4p+{r(d!K z&hq#Jyo!4h?ulF+Vcz~Wz7rO#2o~x)SAD${7m5qXg4(6HkS^#dT_{%R0>X^E)>poB z@edR6L{Sp9C%IA69~0;RMf<`ehNDcPqZG{V>5n638{sprz!=y~9cKD<8M7O$zCME8Fr-)T*w}bW98CHVTu$h~%{=avoaZRIVQxLx z^|!Ipf2aRW|Du1%FWSle!|+HF=AKu%M7}6Jni@>>t%wpy3Oue$yh=3pT9oBJ$Go{ zgEKjVLM{{$7cq^?E;nQelY2oznbLeXBSZ4+^hHQ{Zu(}Jem+jx49>+M4|567n(UJx zt7Tt_-GiI54i|MZ&lb+%FLy$N*r9c66JpTdZbB|!>Qx7(rF!Wr`}J~>6@}7MQv?d6 zpd^Ttm*`|%lWD^kGHxIyKNv}U&bMrnaPG?8*IwEZAC`|Xh%Rd2=#~c_qX=8)!5oMN+5oSG7h-!uZCQU@ z^dkQC*^S91*2LJidhPs)HysjHaVp(q=G(NdsCc*Gv}c+32tQ}m1uNF2I$un+-D|Z} zdtEk~6Q4kspf5GRzP-c<*VOP{S&-ZyKA>37+jGA4v`xqQnO)}9L1*iZFU;qQaW~~@02Q4xO#{-7j_3APhfComvazg;d01U1G zMga`BSYg~9V4+fk=;hU^t_r`rTXKBdio@d_IyR8|1jXOnFe5RGYn0dn6Oshn7@+}L zO_wzhIpca9{KiC6iJz7BSM4Y1C+TP3kJwMskJgXWPmEkR885n9l*czr09a`QLjbng zz#M?ZHZTg7+Euhd+;yF#ellq<_?97$FBM%eMjgVbO}yW=I|5u^tPe?u@f$ETVv3NXoSlH(-DNsLL*B47Y&s z6|^cyx?E)U6vCf_%K)dI@%eAevu!cj@UM2~tqe8~&J9qL$pu-d_oHtMTV;Rf^6TA6 zmrWa#^w{);X`jwa`i$c8F_*E*>M*Xp|J8f&tU@H^d>QLm*Nw(((NvqefUU}a96`M( zo0KR3#Ju@$K1C$>PUqLle<$$}GwfFaI;y8b@V8)qUxE?-j+s{8 z)QvK<=!7(53d%XnoXSbc!r!tCeOL=Pmyeb!B{-0D0A44&mASs$*TT(v#(g8wnLDwaOZ1A*VCvSo>k)<04`> zJL5lD)j2lbIJ8|UfF3I7m}|?>t|a0CR$&cE>+_4CSM=^#88dkpWawc5@5}&tK!v|Q z%9IfcWTgLG(NQ{Ao1BBA7DoO<67eY(J8ZO=`$E+H<)k%#9Lw zEgUVdLsq4ds$y0ht#Iwt0PZwncUsnp z_*1AhdXddlN7xP}HTg%B_9s_&$Nn7plNQ#m2W6Am6{E*0LFw)Pi{GQZ{5>e=_ij6W z*8729VSVSpAGLQi@v&8xS&^gDZ_6?tBC`BGuI%w2%!-IC6PD!}`bI=VL_|cExhBg* zmU&Y|L_|bHL_|bHL_|cM(^2wd%fq|+zTjuSnHs21UR+bR?OIy@Zk<)%Z^mGz7|&_d z^) za>z`JivZdPZGdlBav6lUgw+ki`dIQ@GADDc6FWxQEZBJ)wQ4i zP{Y53Q-edj{l5sI7 z;-?_|$t)4Qd%04=6ne|^>Q6}FH=or5b?m|~kdVBS)(D8zr;EzeF) z4m`VT#=Lv4xt;G%JkB=d9r)onUx#!$QeJ&9m+7h8_|~?2fPH)DxV*ouCqNmfxs=8W2qupP z%?TTkdk=o=XD%rUUvoBoHhKKW)n|M5*Y6(4{9WFAKHu8N)oG~N`NA12`g=t@w+Q#W zl|i3@=pQT#>(ltabGCV3-C;g?O8tW*JOhM>Wx#pz?vV2b)K$9RVyj`CCv6u<`FhNs zh|28VIdGEb{V1LT+5JoGDFVfF52?FvI8VtRByg8a)`H?5aubt#)EfM>P+3GIQTk?o zFJM4#b^7=6yR--XnI7y=fSL5dOqi4Xb06O3CP)Qm=4m4=q1?KIQYnuP#`4M5nAh7I zt#xosb=0{@dd$5MXZG`)l-%7L_D*3Tw0l9B3~F~%pt*~s<}hy{WrD@ zPhf%W*rP!Bba&_D$EKWL+CbIUM?3lN;ipt#^>gy~)}L=Uu7wI7a-AeD$sx?Y_U{7& z`GckTx7RmDYv;ttgw(mQ;9T9PT4|?xNZ1RH_l>Wnf_xk2{W;ouloNxE_ub7*V$>F&Q|SbF+8O7s3Ies8 z`^2N-)jui$L3IhXwEGnJTHvux690Gj`A@^vlw&W+!6?`kH>-VoM3|D2DGTVCCkPpr z95Gu_&STUEV~itJ2=<_VkxKIZb+9pftSQuE(!^pmMM}FbS(oR4rrnF8PyW$qf*<#@ zA>v8?L~8MeOAjUJ%bNyIO#a@G$zGC1{)Dt)9jv?xA6^EuScm?G;EgGKTJRn6P518q z!MB`mH|6|bH|*N+xMB6ln|x{XU+?03Oq1V!HGU$M#DZPLez;573WXy{E2;Rw-kz!; zNa&rb#v_hzc>iC2@tgZE_OyJhRN?tPvj5yacYLGVu!uh?fU5tai-VW{f30}Oe{NRc z&GLVL)3Bnx{*C(DfBEn5KmEV_uyFYw|NilT=U+-!pU=u)pI5053;S+)w;K4{|Fx-o z`RJuTZz|!d+lmB#dTTR!W%-Zo?cMms>UaMzxGh>R=-qpt0bGQ}&rkf-rgF`eOZ^s2 zr8ZZ7-@*9PA@F;1{+dY=_JhCw|MNS4`YW)duL@`@7XI)4Z)BJo-oD|V{EvSJTn~$L z_&ew)pO9T|Tz}Rke`8($IPmt9zxnX+)u&I8q_xeg8fp`3|Kr9NewBM0zA`-c_Rg9j zf3a#XzEM#WE|Jp6(v#1g2?V*rpMN19o?)Ar^jEznl`vi~j(P`RMWBdH(QNd zfVgEN1*`@j8yI*um;>or)`DPBsW5OG1;hhQaW-&BRCE+r7}=|PS_=uT*7IU*T9L&e|)M!?tYD3tpg3?#ALZfzyKT?9xdrT-UAdY6;vzlwUQ$D#U?(m6JTCp z#*tLe2`~Vc1A?#lqBA-J@6%;zWaWB_nZY)H#s}g@1bVt#j99t`k=_81tAX=^ajlGp z1)+@YRhE)tmvd=$kfS1eGExnr48*B5Da(0iO#XAUY|O8vXY#;V*y~E@P6YshMQIxu zmwTFcLNA2|ph~FMAqPaxD(&EI`EjVWA@k%BnT|PLdE^piJ#XN#Dv)bd%rQIIDmEFz z1FiK)I~jn-laboiISfVJKs3Y5PR)F&G_yso;hC%{ks#plM1r93fO=9RhF^d%ABcO9 zxHxb0q{lD;X;&i!2HFFo&WsF$2VFl zqL0?|K;wo+54(}GHl5~Z-63tDrJ<^u-IE|0P3mr%TLba+NFqjAIko~^9f?kz6K|ro ziC$fYl-TJQ!U3<`}g2qV80|sY zUY1S59(;6alc7*I9;(jL4AAab3yARP+gYr=s&~;?yfJf8Q$jfcMmgwO<_;_Zgj>lR zP#tQog;SOY%o1BPN{s{PwK!m5WUuP=Ik{>?vJ_(?iAJvZU6u_Q=WU}k+xEz%ajZ?4 znmSuZ>h7Ah;Lwxw(i^qskv^)WN7ZqM6z<^Mx{<(f1@hXoTzAx5cEUFuTLU0qsW%6` zKv;{S*8JooEtH5MDp+rvN+pU*%5>l{X3A^0Zrttg;QE?sMqjEC>@`wvpvnrefaSlG zoQ}{QzyJ(VIe?5T#TQ36FevrrARP#H2J}Fv#dQrm))8apbc)f>$)OR`5J`NYr?z{0 z)}%Kzd1}~hnF{4d2^O7QGGJfin?ozOVQ2TPfOIZ+>&{W@rv|%#u98Hf32*Kthi^D~ zyPeI(mWc%|vQ6~^>TeS2oCsc8lT%mhX8u=%Qe?H2k$YA`f;LoN(321InJ6b_l-WhAal&HF> zROh&)&4L(i?V~~91U-#3iWtQ~&7@sQaOECKlcI#qqTIu{BbcE;+FEfu(;&vuiJo$G z0AgvtQnR6*!Zx0&4w|l2Fv+7j<|l}OVD}NJTW9CSPrh9-Tw4wqZmD^ZdYi zsNq@q;(gHzF{~0l}hzjWX*2vWAuOY7@2+p2dwoOAKP|<#gR7 z_Cj1azXU6WG0kO(ZmoL(0{B7@41|oT5>8fPbKB{DLP3JNr2yb~qblZroCek^c!Rk> z)limT5-Z!N=EaPvh|f|1sV@RoET^|M$uAu}IW#OGgAX<;4gut9q-3x*<_&-uzN4@3 z26t#1fu@4hTYiq}nL|D7c8T38Ep#h=%ZSGk9wdYNvm_vrq67d-ZzKkzqUZ3!FprL3 zp@SJ5v~>AUQ1-TI(@ zvB+A>A|rud(MV)=j6gb=Br4HHNvUQHfM6-+eDPVXvf0GeJKO@8L^%RRIp|v2ffWGZ zR?>j99^g1v2Ou_rH=<$DD9tPmed<;@zs3${#8dSEx>xJ>po3TG*d))uQMPK9=@zNk zHl88#l{9+P=sabsmCTK2-pYaz<2cUj3GT}CnA{Cz^X<}N>L2c)S9yD)Q?6-|8^LiS z&ndGJ%IsD+ldR=M-itiKe1#WzFY;dGbI9YKD!F3Pkc9B)u{PnsbGw3Sp20KavKG8Oy=fZx32ERggJ zIU|6HAsV?EoY|{iC}0coJsd8H*?Smo95Z4|(pmyH81j=k=W^YSB&C&BWb8O;3b|Us zjSsShM1sFCNfdkebsbn@zdCK$<5>_l`DGEuI3jf5FCw%4rS|oz$8A|L;4AI`FMa3S z!_Mq+3=dIbVZK}1IEG%0b&VRJ;QmZzW0iyAaDm9e*dfg>tp zg<9gbQZt)z3=dIbVZK}1IEKEpqif7$h*fPUvaYCbdlvW&&tKR~*0+9V%bZL!S9CZ~ z_JYt%Ah#W>*@Z3Eup<{<5ou9Vqeezwx}h8WV#U?|4uSlS#*t11%`$QZq7op?3>-?b z5IDK=MxuLpQnGVCgCcbztiat&{2u!Mx)x$$F^kRZ_DZ>T$uq*bgyzpn!n3%Ig+XxS z*hGWOf^$SM(~yZn+X}q)K`nF00zTp+QMd_Cr1SOPIPb1kD@hHa5(>bI1Ki_-WXE?_ zDm*p7gNd$6%u*x-BRaZ?KT{k(NeStSQjisLC~BYBYDSVgCP|gRfNKJW-srzE?1*%p zNmoN4z#eEht>dl1huT(3Emiu8E9VMt!#4DmX;T)B%xGneR58Cx*z`kQ#$dJZrW+Sb zE!`-+QBk7&+$*!hcze&W6v3y!g9#^m7IKMYOaY{)r2r4DEU)sE-|CS6H31)8NvtwP z?Y*7#jL*zxp+mz!z=J7nqUSjE7USrQ9F?`9W6L4R)+TbEm*5!ma3Y!^z^dG^vv{9C z3zkdka=HId?cgJ&MwN&wN(rAGx7ds+$pX@K)2?sA4l||ihfR(F7iG8Yxl+Wq(hYdx zflX!>Y*a2QVerMS2zYW_w%UydH8D)S~>}i#tcc5=-jb*^@ z=d95OtAtl#jz%IWE3C4G4k#;%6W;EMC6yy1nP1kaN(3>>gd>{3&OauXw4`dVz+9`T zzOp*fx{Box?hmr^M5IgCur?H?N^MCxUYkbyv^zEL;Lm};x`uQzFw2s0<~beWqjY%=vL z=mk-Q*<~d${$KW$FQd$5vWJ9?zX-IeF;Q0NtwUcHF_g)3?5AFAy6QcT9cn?3d=~E6TZXLPzBl*peLp zSm3o+;0@Vbs`aFAm5vk0V!0m`zp*|<*^GKH-7MV@Ybnyc(@5HTr_s6$aMe?Bl>>Qa zlyp@1GyyR5s*YrV4@OcUBI~xM*P5&X%#2IIRDp=sS|Eb`=U`} zc{u`Zog+Z*f{|&#E`^%=x8Zq0}!k zP54e#S;#cW47QXbKkGbDAcbdN6|j|Io#iafz4U;$MzuNxSKLdsWlZCk?g%Q2yF!;g z1gJ0UacLMYMGg?6P!P={kzv>h9v!N=($2f#0V*&$VLxDzVyg=SFY&`ord3fi4gyjeW|MZ>)YW8Zj{kH?_mJ6c z&DJInD@$wmM?t2wSu3*y8Tdn}@yU-pb^Sry_!amCM!Pn%<50>u*W#4yd&>$OX1g{B z5lpVegx=`+ciYcbNjo^Fq}!-Ew?c10B1!A2q2@8VaZMiKX^LT8zob~+O+&VxuA z=?b?%eJ}!8#}b${9SW`epZ$l^w!rmy#3&|G2~%E>AUE34&n(i$b?6cnGS2EAu5RA` zRfi{U^o;r~5%Yj&&Q3Z6v;fCCh>s%VD2=FLZ-lR-Kbc!)x>A(ep_v9fz3`}Jv#uNc z>s_EZt<3Zv@~v$ECFBv!_F5F*vj}?HAuHeKTWscK^eM?7wU5nsrJhV?z1z}^C6oCj ztq5>oLpH7mi0-q(qG1c+2kQ`=VY15&VCwoipE?`&8TPUR$)l+vV9Cpf)FfMdx2WWj zGCR@p0w@p}X0}ms<(RXYd64_;45>`G*(TV{KVwzJFicjO^6s*&z=zBt zOc{`*gGLNM!M!;AXERMs!0vx|+VKhoM_`eUo}PbsB=bWmEz$22qj1Z@_s@5P# zy14X4L#IG8m<*HWZCI9O<8;H}hBq4Kc?6km3rt6(sW;>^tC856P1@l&GFO`!Ml&L) z(noU?v{Jp)W^7?P6P#)TATvEtnsYEK6us6;@-hG#a5Z zYc%4(X|}~VP}Lgrne80;+R4m`rr9K;ITMr%Q_aPe>uheP>R8N!q*#wtcJt)xw7~nk z_+Itufzmf{(oK1V@W0Mmrg2T<~nC@LG(5>@-l|wGWJT#%z?v z&U!2|UIe6wS6Lfb4t;idTOm_8z^R5kcBgk{N z5(|&`!eO@sUYcQ81Zh!qwu|QKFvDaqWYxmfSS^;V)hzSHF*TcFws@)W)&@~#g#@ogiD)o|Y;%$=*b$B;@qSQNU9QIMHhv&_p6zik8S*!{}ylf6A^Uey} z%|l*WX1xWjSLS%zA`?SP)0R(LsqWK>65SO0Olm#WW@`LAvwbD2)MJ(1zA;d?^_qaz z_h>u&em(9_n(21w8jaK6U+EHqB1*hLo+p`NkyIoZ&EzPmgni~Xd7)Q6vYrB@(;Ukw zvb36GF(sxB)6A#L(P@dzR9G4fgQVt6T_$`Q8UxljhTu#`s>dhZhhk_pNiSX8^w5aY zH)e1ah7c|t;V$BBqGfmN**@XeRqv2H=1erNTxdC3H&u78Z zYL?j;EKS5_X){BA%m(kXA|sFG8OYFV%pQl?u+V3VpIyj0huQOVkeY*5Kf5^!xXCG; z^LnmDc5~xsG(v0cIK{a0(9iSQJhQyF^AYLvf!Fzx)EO3GonNkfrv=pa@0}$U?WZr0 zhPs2)4)r=}SrAWy_cV4K$kS$u?oOO=-pTe(yD4_ozH=s{A{=*K=%pE6cS-7Dk z=`h7;R|u*M*<`<~Lft;G*)_gS^DK59(qWF-Lh&kvt+Lt;wq}#O?pDYrCJQ@J!gaT? zMYI-;_S`Gd#qx@`m3SycQL3ano6^p@OE10Ey?PnB-7oUKOqNcIY?j6M+8VpEC(F$< zEpNPhvT|XcIj-PLr;ki~Fo_-BW1C6(E5WPKXNA>DnHr4oT)EQ9sw350^~Bv%Np;#9 z@HKV&uU+$YEo`kO=&lu`NEgLDi}}K~7wTU6_bN?iuYqSe%`(^=4ms@OFifHs_XYPNZOFm zkahMO=4duTZy$)rHexX73#+({jf(ZsXbjc=Pa8K7ZQ`aWuxYz#hJBoFhNhlSbB4{E zd1`^z;`^4o%v&zE8i?22TAc1a6P4g;<87O4tv;~a7lBu%nT}7>VwU;7^7W8!E3|Ey z0h;~DwNu+qHkbX1?YE6&e|GIguKwx};O`T;oEVEjkk}+tOp`1GZ6$>XCQVwGOlYzc z#>wMT;H1b+X_<;J)oE(Xsmt|HP2)NQIwU2XNxHuDOzC$sXov9+mtZ_RMYREt5zxx? zP#F;@(+@HdL9I!~Bhxh)XFel_R?|#p!XclTW#$>CW2m@|X^wR{8@zJwEU}s`TPwNj zopUgmq&r8H0-ZiEnG@LygEl$LS>TOjHgn->x5#9!6xD=sznBMItufm3L@Ux|iRHW? zZKhewo1w`BgM0}2!uhq@yW74;77L(Z>|a9rM+L5TkfO#g-5oknrq^ch9e?elX{T~~gXnBhX0I;rj)T9sL*I}hog+{LRd z2X~dMlX=(JUDqsBg>N@SK5^_;s<7k2&pCFxv4}9pB2P>*UX+cOT59dI&)wGJ+dO9;AKo{* zT4Bq~wnbFxm1%ktpcQI2VT*m+P(N1fboUdm?-yr6mk$iC{xXS3co0B8@zf-U{F`&7AM>#Rafr$bb3lFm>VrQX|MKV22R=}~NROrIUvID;tl45!18 zl;||Ya9F$&qr4Ays)E<>wh^ebM?9x9(iy(U_!;RKXJXH^Jn99#nW5yHraPLHQ0COp zZN^17%tA$c3>1M`p0bEZ$|@4b$~KW*EqiZ{S2<~NmgeHjb)K6&_su*hRP*xB`;=II z==N#*$G(3rs4bAoqJ#Gx(so4N(QZMMj^{fG6^!oGvD2f@Bs$w#$SsY|OS?epVyw$h zSK3`IcHLd5W;Y;WyQ$}`Tdl&(3y*LpLb)go^J27%v-4V_p=4Jn)}_iR?GDu-s8RLZ&d$!19FZVgm`M0un$(=kZMqun^U3EL(a3^tXeziE|j3%u@QgnBdZX0fz3r)9YLXAUj!w^*=do6(lgQa>V4 zrA^n-x0O5d47Lh{aE!;Hyia0~*6#M1M|tZ)8g1Nd^NjMo@TrW?XVcfGzSY}$nLxy2 z+uFWc`f;-zAWOA3B=?hU-ah;NqAT~x7`6T8TW2{TRh=-~{ju}fu4aD;7X<DZ?mnI4tS^nK>(Wl)_V!hRTzMnl$l8bX!z$2h*k@M5 zGt}z0!eWF|<=&WOFe27-FHO=J2`JEJOoaW&9F2yovzie@mA4ick3vwY%dB0FGln!8 zvcX{{0xd>;VK-BjdSM@#j!ICf(}W1ynX%Lvu)^DDR8`(uU^H`_Vz125A06YFb`!Qa z&Vr}edmF6BV5-$?f$=N}N_F|bU`&)eO@^(r$%;FcSg+3khTM)zYKgyDlT&{YIH^ zS?gspNR+c)u8-qA59Nm{h(8L+K)DBEZ+u{0k*9~W*n&NZGFh>l_8uF_tVCj$eWjt5 z(YRa%hIW;ORmn}Tv3j*u?t8kg-cuv9riYqQwWw-s>^YZdFDiTKVcsi7ue*DLg15J1 z)U|c%P}P<0y|A9cdReLo)qiZz)iBb?d))EH*y{CLVZQMu%S|vbG&yfNu^A$oX4B0r zTVS_%*V1mwbhQMxO5uIHXV&}VG);eNIAr?_GqLH#6~1vWX~u}b%Zn7jSA#wG0t>m0v@6fN0)fbbQVf| z)_99~AFCDEnT?mHY{%JCawO)g$mKTILT>xq&*mXE!gih}ka_Wl=VLkSp*nw-Cc|vo zXDvY1zuW>(I}q()r6ZP(?K?^8w5>Cnh0uIpwsQnU9J}zii!krIq-D6vQl59^g7vN$ z4Ew@yp;RplcEiGOH&wjumYT)FY@`dvbX!xTrD(@ul*f6o>BVCWSz)>afhU%!HLIxDKxYXu7_5ywE=F!=Z!cUHTFNM@m`a|O*4FCvyW<~ z%`}=7HxIR7-V%#$%hp!)@sQRUt@GQ!wpokM>?_>2=e9ZtD*dSTbG6?u>=N$xSAP!5 z0dpePW|<^v41r+UKK%%t&~F*cGkh8*#&lSYJ{sW&;n(XN zy~g$%%=g;EWu)#H>8}60dvm_S!n#7N?sY@De#0N|H1~~qANTrLx!?3)Vh;!SsKK@! zb@*AsKj^K0-P`iMeeY=TZ~c&Gd*_7T;ltkb`bK1azsFh}^%CFfy+7akGJb;xd;ewM zrO^jx_u*!*`92GMwA{xp^ofamGU;0{_xL0*(@*I!JObV(Q=W@fBy6iHgS1G5iWmS< z5;NaX2pP-F1^+C-Xwcei8YDI~(0vJ>Du@VhcE2|`r#Uz53Zd~0CTZ0KZxLJL+o9&} zt@hWMq4kEgWnpxWfHakxu>bYb!9EftnTWZyQ_5MA3O1JE0{Tp1^@s$bB(Pd;RSrt# z_zmXuWsg-g^A{_MMK_+H-8gMTs6?QIsaoZ56;fE{Jr1s8lskwqm+g?40(hCn8mmOk zXz25BT99QQXP>}I;T+Du9ICEYb!Jamo+{wE%wBO8es)BGp__2|(LZRn2I69=s4MO` zP}U**ho|1D9KFTdllrvbfg^&O*5h6xN$L>`%c%qiuViezF5=4!sa(LS2q`p((9zUGN7HamZD!~vF)^Y(;_fHqUe=5KE_X@J-uAI9zL%2D%o}}(T`H28BvE0_QPlwQ$6ACEXJ&HW=mB^QUoMo`|K)4(OT+jNjqR< z_F}f2G1Xzjh$|$dz9Dsx0f`rK-4#kg3jA^WM^Z@Wsv#4oP3;wh2}8Kmbylp%I+QJy zz;39vW1MT9cm;dV7E1eCp zsyqL(+rH4#4H;ta9}L|zWCOps__M`dAn-Sfzg+xQ8c^!AIM1m`hKm1G{PQrM`$eL) z|BhZ9^rl!FW#c<>3V{37p+B*QqDI&e%&b6E)LG_9x8y)a53i4d zGTcgwA8`s$_wdwAa9l2mMAjouW2&03ENBo5nmIpqDl_KSD$(6clRL^^!LUrgS>t2y znRy9PAzeikdTs5-b#f8ZyheI`jJk1^!$Cq%t0(ee@t7}C#VW&0=_Q4W8-G}TJ-+0k ze!9d<)#q`Da;m2+)Om&EVHrZmU~^j>h|i3b-&v}Xp_|dnd8VWgS*_>{1OC zX3C4wRL)Z#3aTc#ffNNY0n-uN=yzKF-ig zXsK2+!$n&c&M+W#F>y*}>SLOZ1nM3-s4I``quSD!qoO|S9yQ+)NYMQVgkb#*2gfy8 zAk)|kfInN1&NbQyBJHGXQ;s5WVj2};4w%QnrkY0<8YSr=x=?{PH9To3JX=a^jymHb zEs18=P6RJ79yvsp2gA$ikpCD%C0{G)i1R(H$xuv$10k(t?WjEsG_ z2K?*E7?X=RAmMJcmFiF^3P1qo@HJ$XRH^1m`CxMAKp?FVswzrq{9b`XN(}YX6raXB znj(xcv&tR6Qu^b|>}pQUOpeGKb?vTP`--|BZEkKE5RBV9Eyo`A4;;0_3!ui(7Jv1N?pQz_+9ArhGbm{eicm%1 zV1+f_>R7zS>ZgJT-D^JhZpu8#N_fG99%?eKl8p{|3Gz{n$69EbiG)K7;=P13)WdMI zw4ip|iUW4&kUy1Fbn2CPis=qCCuJSXgbKuZRnQEu8O0Z>3TNOvXoBY6B6X_CAX;Lg zXVJ0 z=R$@HlqE1&Xl)3?31{9C2V^mhmp^|d@h>}7`Qq+%vb11T@a(0S%n&hiplyNbK%h60 zd0nUkhEK?tAQ?RSxd8hhtMT^Wr#@Ozdgp}}`n5`tLvI-gLPn!g!dsb3^_a1*%a=$T7O;yS$ipv3pcA2}hqg!R0F!+AzBDL( zAcs({H)@WB3F)a97NMq^%^?I%#SCA#v|C%X%Zq@tlo_K4X^jbD zbeD@WGLeAk{=&o-{wMLAZ{l%Qul_1=&13ut8yST7tj9z(!(&D4o=p92R=;iK0)tq4 zldYe$hYkEnjfD`pts%xs^lJqc)uHZu*pk&T9i)RA192bwW zEJg7}y|`3MPTYqI3pHf`AYI-J?+yY(GD-{KLwGeF#x=MzzPR}2HM`|=8K%oA=1IKs z@G^Sx?CzfXV5+zsTPt%s?I3s1>AD|G0kveZ^uCKMOxQ;mj6(OO-UmWm<~<*5xk#RJ z&Zz=!;ICbu-1O6!6Slj7$+P~1b(-QRETjVN>}a&+-#a}5WMd`;EgX6c{!@l|DFuWn z7Kj_OL5ncT%dPP;4hC9S<(=^Kd0Zp%pE1S+v{zqtZ#kq23j+KJl?XTbRN^l*dGyV@ zEW~Tzl`B9V1Ox2@>1znhTyL(?eM4UxP72txi895n7p9-Ow>7wc|76KBgb!!RtglRm zjnX`z9c(PX7MCm9RFJCI*kbJOi#qX71%>=CUHwP{Ei2A=r*~hsuIal{@*6E|vEH-P{t- z5|X^W?}%HSZZie05h{gu5x#!8^*UWt73sny`*~Hx{$(dZsnp{>?Ocd&GHzVQji><{ ztMas%DHgYAX6E4*PZ}=%5xEaJR1K+v{(KJ1jEGk~HZOwtpIf8nKOzDTQj2=%i2o4k z`YO=t7ZxOiDKa%3g;uZrWC98@p(UeAl}I)pYU+um{n@||dB{m?Zq}MFb8(KFo1%2D z1gug3D8Xs|IvDbuPaNDlf(KU&aj4v7SAxAe*n5p3*NA*u8mQZFILmR)k?$@pE(x?T zZ^)$Uw8w*#d>buk=Yog5gLE15_>ENb;56rB~0=S{NHT?O< zn{e&OLnv+>%bVq%j#;eY@8`CzC*6JE>mPjjVto4|IMfGKxyd}evtajCe8DRz@H*>l z9+PxPZI*_m@U9DGx9xk)sl+!EOuqFrhrVP;ip{Bz=gd||mZE{-lgx8Rvd4%R!Yfy6 zjP;%+*-h&k+!(&|K(d#uha6u?v{BW$WEZqU*gUTia_G2cE5gWNe|sP}2l`OBGK;Z8A&rC}a@CugE7^P@ zJ6{E@4Mb|O#l6SXHg)A{C-{e73SMM6*WG|^@QV~l`g#Z2<*8#-m=yoha4AA7DO5XK zXXoXkW9687K+pn9bRdIblspt|^~3<5`u-|C9bUNbpy?ok3S=70^l>kVPZUv)2Q@hA z$E40nrp<5cpqE&=>$=die}?*TMS~yxkKG;-x}? zbkovOpBZMwt4r@8I#E`gVU7seQ zxpOv!G8*q@GnXFDH{693YU^eZ@w+F$r8C1>sjCkE<>{W<%WHs1yETuXf(ZPT-Ha>Z z$^%ip%;(IAddf8a-wB<(ptE^m+#|bj#-D-KyOk`?U!W8N+AQHPIZye4Cw`~2F=u~B zny2zp?%V77%3aT# z71oK!H8~=HuN%y%=1^G;((>FHO$a~9NHU;9QU{~Mc*gOs17tAxL%p8uBkW_>=MN5K zW3+k|*G=>YW57$2SuaiMLMPFF8mEb)Y?b5M4fg)b25e6*_E!!9${PvW@IZr)Z$e}E zw;?s$P42!Va0?=WkZqb5_=5dWKQhMj41NRzUBV9|8*)hX{O3X{{I&UyG4n8w>MA6v zf+jmPV&Tz>zkGBSZQ0Yz*DM+Hx$_$(pd-vQ1yJXffMyAT#WZsbwAX)c0YDsQrWt@e zz6&Xyr<9SJC6nzwA7Fe?ASgTplrwHVqyUV^UsxbmM3{Q0O4St}Lt^*pL-+h?f0k_v z=GNNt*WtOp@51+bJ{EuMM!2@SLbNcgQyasw>l}^!vHskdr-Q5O;Rb{81_^trRbmHm zaV~N(>1@2@nO~I|tW!EH@ERicz-Lwr+_wQ$8LB>f{IYX^2Hmj_KEDm;TY$xtz*s}TVZo|Nhvm=U zHFoUQWw<*wiDb0oI8qgZ9jZnwHwH@>dTo#x-!%%A8k{En!?TT(>x0DJ;2>5QvJ0f3 z!n9&U4hbsiB?g`l@~ezDj!))P4ro{=e7x+?qrP_=q_e;)r=Q7Dri0W2&8B0_7X+`x zKK6yqih*t@i0wEuU62|t6HLU_uE-dN9fV8a{5XUAjLuBOtv`P1e- zVm}lumyJd`WA9nG3@^7%^r4F&9G28)Y!(4yp{Zgwo8MguD#?zxidF__+(%Fc&nr!U zV0xl?rujz}{Es+V@RlnF?gOrUbd&}j%@)`HpFuZ^iS@TEg5UK|ttA2Dp`P&)18&JD zrwtY~JHNx;*>ddr5Xf|wAtLACwxna(v3d=VW#oT6q{hBX*P=WcEv0Qmki}z}haVb& zNhEPb0|O*TIp=)+Cr6@(N=QS22^?I@nXdOfGbiF_@1`q&Rbm(4a5NR<|E9fXD2Xb4H-b*2%Q#^Sel3OHK{j=j?vRuCv+MjqS zA)sbC5y2b?AdtCNKh%^5o+$w#RtJ3dRYoFt3L^?YsC{Y-?M8H%dX|(a0s%ddz^Qjb z!s^Kdez+k+7KA8e6TL$ZFJvz~bSU}8Qw&L5M|yd`s0zU@o98ZMDu~&GyfNdx z%Csg@$|#0fWhQZ8cnfkTOUKuMACBum9>f1nG4vGtw!`>igkrCR`$R9#U4eSZG}Jqi zwWIRQ2|~2dzF%ox9fotUz4&!~1SB0~%@)`pDc6=s8C&vGVD)`H)+of5rjyto!u#kvswc?5m(*;LfwFhe!iqQ-@g!y~oxgbD`4_t6MdXbHagIPy8_$j%c|qT~g8(>& z2$H*S0bwUhq7O9#bP1X9+afe=$V2MDn@FK;Zn4lLd0c<^Ket2hyB!<_j`$32 zz(7Q1O$5d4mC#m6jyt;zSE2zQVIx2oUz`Zq-49Abzc01 zea38z>09Hmx36&2Q<-}j3gGJ9(U#TJ&&L^mu|wNA1#>0W;6-g#-1+Qh7W4YEG1)Oz ztLXL3X575hD4*w%-TCP{O=&e>uRRwV6OMCS`eF1%$pwde_&P0sdB9nwQrX;>!dr69 z%ZZh-!`0X8Re$E|t?^#z0l6)y35TBR#`M1$W)fi5#Q6OLIiVRST<39Cpk_KBy%S$q za51bZ@pv2iaJY{=TbG2Ya$zCjZUm7}fQ1$VH`16%Ye*-DI0vZ| zhWpGa&V6^<_EAGt$~jlFYD`56m;-Yc1c5s4Q0myLJ zBrl)7bGWP#?d@cV_yjtfFM?zM(d*!|iJ5RfS{=cCCz7D%lMBd_E}X zrFV?h2=CR>v_m#@9s3yc-~KsSzHXU&A)4CfUWljK=e`#vr_qVG@JQ_+0(OU-4A}bg zg=uQ%U#NXsQrE*I!1-1owg5A`ckjM$y!H9F-gQ3;LWgR7Z8Gfo6u*0nx-wtmC^QQc zF^(ra7W=pOEXu!W&z=`W8KBge1xgl*XpVlf+c2?N`C;6(Vkp3P&)@tCL6^LQq)ogd zrIFMp>T9ZK0|Z_^0du)fx);OwI^xhY2aCfJIFypiwG~l3y*O!||Gv3K-p#4$F?sg+ z`^Z!$rcs;&c0h|kDHlZ!63+zz>{}t|#REntV}xLZLy(-_U5&+^daY%;@mtIS!G!Om za6LjJGk?T>4&x-K<0TuN5zAsBaImTam_;1CBmxEQ!jfEPj-h<97>Xl<*vjfR5aLanyGTCLX%16mZn?SE@k%wnNB1@ES1 z?)~Iap))V;_)wk2nS_d0*+b(Dp37@C{CD>2%l+s>QY|-#MIer)mLlQ$Ui>ma*L2O+ z1Xnc>X);nB(=H*3b&h%-*NOoATmUmb%)e$(h#Llr9xC{%%cOaT2oFabKldUk{b(dm zD5C%iYpp5i+Hy6s3dlMcNB!sCP9d&*E_^|B7wM(}3)9}M_=XBq5Wm~8%$&LJa&o!e z*R(jhy^D`ZVv46Y)-6*!dZPU(p@i~e?*52=Q9QaKbhA;fv7nNii~+*r^dMB*_%lX; z@R^4bAnQvrag2ZkTL{`L6fpwOpzRSGDDG?FUx>f;uF7}4^)oACgKB}$zcKtl2!R#r zG2z93TPzOX9o(rjjm~5RclQgnP3$O>n>852-&z$L;@wVTfqil3W7vP2|mgN zlHxvt$$|_g;<-_G&?fIbu8S%dWrDc3!JZs?IFpbu`?=E!GUaJ_rNy`z7Ih5^{T`<* z6j;AOVtP4qQTV)RUbx_tQ%fs0r87W;EUdOmsjiB_;sI1HFzhDQo@V=D+TET>7~$Tf zICJPVvfq-6;#fDav#8mV%3`2H>Rr5heY0 zmK>d{m&Q~40kRCmdtmkE1F}H92Q?9dzO4_@oNY_;G{;g-0^ymiJm4Fl3B6x|Zi`dM zyt$Poh2DBix3hUCP^3XaICT*;Dbl#Q{0`--j&v$aM;bF`iHW1$!oS79z`!C;rH8#HaCcp?oNGMw0S z-ZXGLHcQo2FsB1!gDuWc2dPekR~K(qC$&k?e0A%A8VE}^(J6{8`icyjn7^|)l#I*> z5UrCUMkI&~owQ$v{#3d(C^Q%h)-Xo5T;cqam+lTVfaNg9ka}~9K|qZD%6@$Y-8pPY z(I!&pK`#npscWQ5*XvdNcO1(0-!WRuG>9vm8}eFcuJDu!&0o;)?n3wCxC~OSi_&7h zv=zrF^M{QHUY`blvCMAguYWYXXDebzf8uGN^)gDf3@0RSF(LZ{u0zW`oHk?u_E6Ea zYPJa&FgS+J8i^t*!nnE?gc35NM~dgb#haF4cxjEupov4$1a+BtbuEm`z{}WDv)^@M zj!~O#W9jaY!||=g87%P>Vml0Sk~B4t|5OUq6h)#rd9nAveum;;tF{(i#sH?!4vCR+ z(k2TFX3i&*-5Gkm_u}YX^KIR2lNjU^`b4j&D(pUy@h!>q@x0GG^n7k<%y{}*y3L|D zmzP;^_+HE|{J7M7h=CVPj-*rOF1h$$)1UvH#UCzy`zC)_3@&~PV-LUga_7}95F7Lx zJ@SI!B5~5i z+B)(uZGh9H6EJ1oY;BpwwHj_C)?ywZl2n-vv1%bOjI-e{^@DVnX5S^CW+9esaZzYJ zIS}SAQji8DwLLekhZ7n(@GAYsb9XAVq7Iy#FBe#7S$^AFJWCplst+Rftc$ZlJPVA3 zxf49!Bu?y^>5ItIVo61vqY|yxix#U^VIDDe6~&C~0f|u67E}iqrkBO>iH%DAczivN zOFzmZ;=RrmPCPp5U^0j6P&D`Cktt^@FV5XM1noVG+=+#8Ugi>8@-av)&$U@}8bXZ7~|2KAEc~ zf~W4zLhZeJ_DOX#vscRMGYaBn2kZ~2FTd`65&?C_tS>Op$>Cf=ALf)Xiso4 z&wOE|b43!eeVg#U(%LDHX{K=w*Xkh72@s zz71ctrA8P>O@c)nC2thKx}k-Im*m(^Gp3_wFkdIhLapj5UEpwaO{7W`@|J5(L;2qv zRmn=BtG>pZ$6$awxmGBv=-ay1JH#qzTaEL?T4sJ^6}8#Mh#t7(o45hgryAp?Po(ES_FKqxVr==eE*hfD?!`ifSJ+0@y?Xj z1tzTtwR?N22lkEbOHOx65I4ai*C`wHehi@mbIGef9#R_uL-)MPNbg$Ed#EC4_p%;d zVUnkVpG-j88b*0Se~bHsN5FF5jXZ7gNXgO1s#&IxJhqgp>*EV8#9+2fEzOypi~vr_ zBul02!k53>YqYp)IKbsGG_-<2WRsF31%$mL5%Z$Q@B9z6Vs0%~Bx|y$`^Cm+1fo zI6GTVHm5joHo*3)v3177C4oSppO(NsL*AhZ$#toUTdT9@pZE4!396i zU2#Mw^dQ*LEG96f@56^9`lv*rknR!t=g*j2WK?nBRcWBW(2YILeJk{p22-6|T49Gk z{Lu`!{IOi8FDFoKO>GoXJ7M^rT*YGo5W zwWO+R&h&oMM_^+~C>+}-4`dQ@KjT#j!8__MFg}$jYD)3V`q07#d{{k)DZ}(}SM3TT zeve61Ayg{o??SYUxx!roRap-UR);-ouA(**ENP3QE?5;rVZdB9b1W5XnCER9oWuKz zSy9JqKgyuO4;c2<5Xd9?G-(=>IJEQC0U6PN5RAY?FOmo&$f+aOWT5#-Bu96FAXSK_ zJ_1*zYG>@r;pt~`+c^d?z>a>4w>wuc+V;S;?HA>pG_|r zu+?LR35|-i?Xz-3w=L(lmf;JDiOlbnmbyN6DEw3JI*^LX{7ks9^N!dNp_kbLW@kwi z(8fl(I(V!yICe&{>Gkb(2S&c1pJlZ;rddZ7DinnkKm)&x-rW}DSa2|G5FoI%)H)ry zA>ps9OcaO6^_agJN34C=0Xy=}6+jnNGC@=qTPJK@4%pSz2OY6_ z1^yFJ$vg;`ADon7J>pCEY3LBz{SLKb-?<816v7T23i~Iik_Z!uaYROIM;nJLolb0M zd{uGO!;yP_W@<_lAMlxsfLPonO&>1WIjS8)pa(UPJG2m+bAw#K`-L1RDq0YM4&+{hjMSpzRSFF^wA8hYK4Ds_o!i1 z;&wTAP_i6Vs=Ru)&aVT0Ej6Qdv87rL&Em<_yHDnE5Q!MH)_WR_AgXCMK1fQ1=N*x% zp?izD^iFv1h@cPE)D{^t42H%?)k)7LHg4Aw=W!EwmAEVwGLmka2cGZGAauS>+YOjM z`3$C!aUgdBoGAWuaN716f&ko9lGZ|`CA3lOwP3(ausQ-;TE9UX2?i{bFanSxGAl|z zq#tU*k#%Gl@>hPyS~}nr*CjR-YB7H&&QGY*C!3FnI1U zD+oW4|L7p80c?!sq~gQv0erhsBD~Ot4|0v+l$3SX8V||Bf#XNzFM}Ia4hQ&S21XG_ zD##LOY|33lHBa3$i?KCkTx4)m;1{EZyAA(t8IQ+!SRpmIDsp+MR7N$_93j>bfyi-Y zuSb98z4LZc`58e_QD%fBUmkHSVkLs)HS!qt&cVZ1NMA@31Ql~M8bKU#hJA;G0hS~h zJP$|$XUIdKglS2I%KIfSYSVEJn;Of*{8_HLdi7`EQ2)Zq(UH= zj{%{85Xt0_O^_;w7ThH2Cit}Lf?0D`FwqdoHfdtpTMn?>?Hse#sz3}qV{bCL=|+=Y~7WHgUF{!l2$ z1Y*osbcbpHYzqU#K*I*vL~=I+KGul=et*he#wrVsbq{Adx(85*4lI<< zWv62f9Lmu*+*rqsf@{en695#LyPCzT=wm_G0NcUR{xl%M6{ozxQYO?H}_sO?B* z2ef@o1?MzmGT~-mqQ_*hHB1hOPu%EuS?0XY*WjFxW|OS*q~@mabxvqK7p!V7y*w(g z_h+5u@tG@B`n;a0OMjq3^)i4&*65;=96vFjRL}djy$>}`a(*@$V9yPh0;_- zai{YJR^6l@u;BdkuIA60r_Bason(*uwp-v8N5OIElw&`Z;V}Bp_YUzcAM$q!q4yJ$ z{wd!KLG&X)O-l!7tE2Zb6W^e0fGTOgZse(}mT0AKP#-TU(`X+@tYD!QqO^gy)nr1zmH^d(4Rvw?5UE1N*qZa5*SuX zS%5o>RHW4%BywiOq~SQmqbCX*os91feOA*;k?X|$YSZZimmeL+Rb(SA7m+iMuK}Ve zUP2+twB&02g?tDgghd%*1P7CI0_vx?UjqycUeGIU%;-!dP>lizQNLeV1y*7cyea%> zs-R#b`bS8XiwkegcNb4Lp}PDMdIn=84K`OTgsBg*jAA1bkip{W_ueytGIV$-=NDVe z%~oB?Igh(dINTm_H;Kb-5*6duZ`dKrM^S#gK6LYLMT(>ZCZ#@djA1xm{+{&CJ_NKLkiXH14%lR%xe6zJ#g{{)Wouv7%0sp5i zovU-O>zGgpN8UE&wn8@G`~2tH#4J&}DctzXQb6m9?mP>w4;nGf?$B;K+OL+EmuY)= z80xB?ox1Y(K-0oe8add1G_ksT1IPfpa2o#VeGX75BkrrukznZBuWH1AHXWc`HpcNk zJbnI1j^n6}WXuf}r$62w(xkhdkI*8FIr17;7DGLs`RvNVk^5J*ctMmJu0eM8LLI#> zq>)Y+)MDy#c@g_TC}-QM#6Rw@w{y78+dI0Es?#57pZ-X1h2Kg%RULWp`EMeIH7@m_ z+_=m$U3kT4=FTR>t0lNgdRuuj!Ep` zUR`!Ao#3zO&0cCui=C??{u|AnfkMh>T$^s2;Ll%SMzhn);g&ZT4=~}zg$4Nk*VGzX zk9$0E7mp^4*P#z7JAEHFdR!O9TV0#YQ6|*X=S7o2T)aT;>4T0+1(&9(`qvcN#EB9h zs4)svR4>fr4LU!#me;s-ud_lw3%|I%*r*QsgQX4A$oD(i$``#qIq$!+^G9!6 z*=hq#+?Gz$UN2lZ+qTazzMkJPwfS_r?6{rVm3J!Eg8KG7g*)h;hB>GV-@d1q4cK_{ z)q*NEJ*n@5+Fs$akQ`UDnMxyF4)^~_-b?NfAariKquyfe&=yz-Y?u!jaYT-~wjvZ+ z-JU^#$Lgle`FKzud!PKu zXmdzXEBAZuD31-Xgg7TPvjyQ*!?UA(hndgn>TXU0+Fu@3du46PLlC{A6= z56$rRU0lR81pJhURHXm!EpNveI6gDRw^aM%eo(>}{~Mny8h}4;ih8d`DaIj-5|r4G zT>PVJ%!MP^GRgV%JKe^;&mJ<-(0pAz;0K|r`75?d-P0!Iy>j7U;}i!K4Qt$mn10~& zM_3S8fAZ`MLwM~Sw*pCccwF5}N9=#RbOluU@OHf3)i0=)Dzc}B+)b`ZnSy=ynQm*$ zy2pC*Cpz*Lmn@;q8MDDb40Zb#KSD-9ELA`dkUB{aK!H+>AQ4j^lE}JNB9o}uMNw6N zpiu>ec@hA=Qj8S8l8K*#&_dLuFB5lx<7@yy6%^8B|1^=I7;tALb136*briwjA+pyp zb?}$@rfIic&k=ZFHgd?Iyg9Z%^Q;y1VzJjhcRK__8+3kowIi?|-B9?7eGnH84VF7z z{cVdOEs4&{gcTsyGZP(pV^^L1QvE4#pxH0K|Cw6_Vd1N2cSQ`y@LfFu2VB(EnkYP}uphYDcV==dlgCuVuvudTp zKx9PGjEDSsS3JtHx>k)zc7rf%#1tA(L&J{=%ur=L5D~w5bS7^?-#7BbqdfKs-&lQ} zHX08|{0lRWON5vQGKSnk=6noiVHC#~;Ybn!(DS8Y0eByM>|5q9NKddmsJI`F7_b&8?beR^GV@b+lt-R-9osgs=Ab zZl}11?bYg&sxc7{CSfa596FJzom?Nc=H;OF417V1cX4|;ca(f`SyWHxy}4|Pb! zv*g7T#Vdq7GhZ5ITj8f%EZS8(#GhlBf=XOEo?e2l^T_0oLy#+eafnXn#72v4eA^+! zQ0%Y#UN?=fbF-?Q`x7%mf}BbYWQ4et5dp`zUGRQ8p;B06-F4}Ck8oD`rFI?TUu z_IvBwEC>3|_)@D4v>!6yR`nWAn2H|6lu}8#wfFfJk4NtCyfPwKbU*Fio`=`f{N;mU z=a2%qJAQeGVUh!seZJX_kws>1ZQ}Mg6A)r@Lm zNKMkcFrvH*03qtQwN_F~7M?mn$Yr(Q#EifM0>FO;jq2YlwX8hamIU5g;1CuLSB`b( zt7R6+?GHTz>|U*dU4X604~GTnV>8MnIRH-&6dYdRo#9*m(K{?$p5ugmtP~+^YGK-D zmI59}!gf`3v+=P>(t6aMsOV$xCXcVKu`Em&3#3mMp2V3 z5rCzTP>ira$gMcNej2)jB;Yu5;$ns6`V#R&3H4b)^WJKT2jY17>u%M`_hS0wiBFx>fR8Ht)Cb~>fv zlcVS}O2K92pM5#;?gB+UM@_Uhm+QJ8_qj!=aMz)P``gL#UD(|}dh~sbI*lja7_qV} z*u{NiU2(JUZq;u;)tU{p+RZoon5a%!Nsrk*RS2O=$W@GJK=wKk7p-0(7PsL5PR6tn zF|(U|pZtPT-Dw+%!I4e>ODU0-)r_pH)n%1Jm12C1A781=lbkK~n`S?hyFX@p0p{E_ z><@Byq>oh}0rLJ%zv90)n{tIXA6gYu#Jsho)A;-DHvfKkQKwe;ZJU$QR3=noM$De- zFiBX^bRmZz2m&;n_K8-odUkz`av5V3+B~t>KlwW0#CM;yIBd_aotBX)Z}xfbUFt@B z=*&iNo(02VHZTd0w+ik%8$bMVac$W?O|uuSRCy!zc zjp;^j;=P=meBQ@ZXgdduA8ud*evW)W%Oz-RMar|yr8?XelsvqUUSWwF1}&Ue;0h*E z=EOs;fUc|9WM!XzZHYqt_4FgYy8elH$na?}DZtk?xDnBzY?aV5a~uKcW(1l(gPEeK z6^WR+GvpNMdIXd!e&Xt;!~tMOVvA6hr`bWp_!h?N&5#?44ne(}HW0QUY)3AB-z%~P z=LTimimDPkgxoC)OQw#%puL-uAZQjJRJbCP@}yVaE>}2n0(fi+OMuT_3W-+B%rg~w zJqMdoXl4aUAj-`JYVke$Y%bx3h4BTv?7=^tJe%^BqMr_s6d7!c%((Q-t~{3sU+m`r zL8{oMOSGuxJV%5a_JJ+xOZrZ9alaw@9*dMJiAfGjv(#$V@Qep6;| z+65+6dIw5>CUw6s|D%fC%wl3pRu*m{+zR_pjcOY$c)`No7gxUa(RD15eUE8>cyFWj z*|mqw4!NPU&#l!yX;BDzKBeULT%X}fIrmyKfuEZCgDBpT%-zp~T2ci1g=vTk(=QzC9k>I$#P*(Go7BU6Sg*=_+0c;kB61pfi~v*EcE?D)44y; zQJ=o(SNrR{F7#G{a`hU$!|%mnK4I0`?mUwySBr{T5|wI0K--jMSjOqw0Kyc{XWe(0 zB^|k9#}^5M0D%K@)~1qbApS*KAH_tmTc@N-$Uk_j^^xpL_ej?;O0?5b!!GO( zT`ekM4VFIJ{41?p{;@9Cx9;>~(3_1+UcX8(db@Fk6Wywszj}Y^rwgf7`w~{^>PA<4 zfbIeDoxQt|d-xmf*!@3DV@dH;;upl;O)*u=J!YoH(T}IOT}Qr0hB)U+3veM~i^Bpq zb~hZ}uVTv#L3Qa*8Afn{MJjQ)EM#K-5RJ}BF(TS63$Mm^G)&b?3EwJGN(lj<$fD%n zG3D~j{)rr!x0 zTDP2?gSL3+(8Kt3T>g=lae!6$qR(

    6&qaNDxLwZnq2 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

    ycBey{rl;kDc{Ai+(=LzE1TWNYW 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

    w%k_tC9o+J?jlEWgdzIe0b`MG#H=N&ZEd~ha9 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 z%0A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

    x^D& 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Black.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Black.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ba6726a769fa45dddb625cb6da2bfaf612876900 GIT binary patch literal 23752 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 R($B!B-X#D400961007ot=t}?q literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Bold.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e56589378a99e4461714bbc50cc1fc033a492bd8 GIT binary patch literal 24448 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≥;aS-?r{?vo_DbFN27%}o~ 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Book.ttf b/libs/design-system/assets/fonts/monument/MonumentExtended-Book.ttf new file mode 100644 index 0000000000000000000000000000000000000000..361b4ea2e29fe82e285b797a9a3e8db30faa1f22 GIT binary patch literal 62564 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff b/libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff new file mode 100644 index 0000000000000000000000000000000000000000..e6335afa44f0978fea5976082dea10cf94a698a2 GIT binary patch literal 25880 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 Or`{z1000310002%K%zbX literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Book.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..07f221535e6a8bbd360aa80ba365eac04d3ab000 GIT binary patch literal 23868 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 P*0S}5wluvYuZ{r##KtAz literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.eot b/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.eot new file mode 100644 index 0000000000000000000000000000000000000000..c9fe5948db457bf042224f7d03edcfd3320725a8 GIT binary patch literal 29333 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 F{~vH%P#^#R literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff b/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff new file mode 100644 index 0000000000000000000000000000000000000000..865ba0936d4ca6aabc179835ef79403a137719b4 GIT binary patch literal 28112 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 Up74ay&%meNB>(^b0RR910K$$~qyPW_ literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Heavy.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ceb90adcd02f5de6668314f196d4dc37dac99844 GIT binary patch literal 25988 zcmV(}K+wN;Pew9NR8&s@0A+*#3;+NC0IdK30A(=%0ssI200000000000000000000 z0000Dfzm)4ffO6td>o2e24Db)Tmd!$Bm;~z1O^3%5(gj~E36lyad^Fg&AA~#m9_aZ zpS)clH_w#2WOZtbqudx+UaLaLtCIvZJag_@|2 zOR)BBa^*svrXE>wk>(!WhyCPJuV1z3ZBq#eg(*i(4HOYx*wElC+;Dh5t`Z4H!#YEL zs6-_yQ4Rdn-XWQgCQhnp=BN~mV&cNu4ofg(3Cc(Qe%N-k;yXL9D^^j7N`w7n%V?%) z%*6kY_>-u2(9W)bCO4L;_F?hXnK(AJLr6?%m}g7a>73 zTtWyE2th8G6sQ?BK};&<=r>R`s*Y6XSDm^MG_6hcOHK|IZ0zhDKsSpB0xrOT6BTD& zIB5C;`dJ4CRkI8ZB|f_0+SS;28uaSgQER_RhZ#=N=d#FoC#Kj@_WZKVu1 za)ev}gfqmWH93vaeLQ^~u(yBz-9!>mL~#JxctnXFJpAAHzrFW)!<}B!@>ay71rnAP z6-5|D)M|Nv(F~OSGkjk8Z9jNydpLdi3IMakN)Cp}$N^uZ$>LNKfCi}H&I7dP?qAhb z9j&&U$#Ne=qih8baI~&i2g`VN_Dk)dXqQPO)$M9v0R_~gSq9Jk(38tw+kFg4?vB!{ zvpC%M2&oaK^u;-goTVGfqdXg9?!)uT*)uzN_s*ZYe|9@>Z(1^$c}Ut)qb&`Z8sedd zdj^%XsW^B7)X*TQqzy3ObxgOI)D{BgzMMC~|DIpdWo8yKx1waGf4TpW8nLQ>h*U@u zBE$ru*b-S`V;>?8+XRRrx)G5@s!CE3r9JQO-LwV_+;E0Ez@>`6>iqxbZS%pJfB*k? zWwBG*n@MRE)3LRJ6iX|h2#9v*;s65wh4NV-9C z(I;I5O3Kl43gtB0sZMFrl~M>$@(Hw^5TqNOyhN1KWcymH+fD0DH>|g98QXNwe+`|@ zo4F0WxT92saG4VhC(};lJLs-A$M~BsBtl^{QRoP1AfA4$)N_5u5=Zz|5iV+_p4vKo z;RT$a0{$UVWK@ifOr4D|sSrpv{UaRU13>VAVK|&5Nt$|~X`WzgVgA$9+47&esMN>^ zq5fdr>po`U5?S1s%Hdo=u3qoYY!u%qI_ldm9=y6R)7ky2zUQfZnw7FnT& z$vV3oblf>@{&&|y&%N~}I*36Et##B{gMo(ILz)?~&9lThI~{b&70-L!HgEaBSAH$) z4xf)D*e1uEGZa>Id!m)m{_-lQw6ChKweI>GYo+zhceU0!y48c8^``e_HoRl9PR_Dm z%b5qfAYo!q&@klUD^{V-uLA0|8Zc$vDntTB@n6AI7O}w{eNi>%#ptAmeiDo@!DJa`%dyx>Yi;y*|MRG=PKvmye?-j$ zm)MD5C2=SD#JK2zEs8x0&Pe~QTN3EtIXpmX7?4ri{VYtUNEErGU@fwaXCM<>cmm%r zksW&Xv(Lao-3&VRp){OfT zpEmcDS>!|!H|07E9p94;_qS&hyMajH!WZ(mf^@KlWxAbEuhLgSqWTH#lRT+0ajAdK zNUm2{skUlRJE41M;Fx{!F|=S0r3w`qSz*dsrDxtMH}f`bvdL_+g;N@*oa)wpeyjz@ z066D=PI=|caGRSmEN?Ep{JBm&?mTJEKjm?bGKKhJ$Je}EnqBQ*R0v8Z8dh|MLM4HL zbWpTnN%6Np2$=Jy=>}k>|r`IxD8%|P#WJ{v=~zL zZ?;ECy;U0H}l$Py-7yKbnu#@4M~$-f%n50H2XxwLq$M zW2hc$(zsaLcF!VHqo1i3S#FyrJ>z5l?VH$qjI(3&D7;bSRyd}yBTaO>nMsdf6RlX` zBSeHWht;b0hdyJ%Vp0l}N=WFqz?U$hVUt4VFD0+xU(9qCf{jvX|^Hhsn`$yqqcL%RKpxERdhdb+Skn%kA=Kc~~Bor{x8C zQ{kKLY;k3TEC*H1E@q4j%O?&=Vk&wnk z!~uiW=w=*jc=6Xa5sB7pWgAGe!9@p>6nc(0a^m>fci{!x+JPDA1p~y(327QNYA5!z z9&iwd7%C~_YsrE)fP4|82*UsR<@#|)=`Uz))Os$ebX7sP5m}y(pq?kPG6b!x9=RlN zILWolfB;~a06eB;2HkW4dqWDnm#7u>2d4|lc7u+ch(XIj4<>p0BEZlX*KrNDFhOf9 z#P1fc=lM)^_*R6XBi`>ED=4`Fxo1rV02_Ju&~-XR1h&%LXOuG1w@`9DW)_6e<=C#LUxG+ zGAO&-gSCF>h9C!fQg(h-Iz4x4*_bg?DvumKQFTG7=Yv^G*iv+cX)m~6hgJT&r3PiT zPey$MXV;K}M!h(}2+On1!CuQI*76A1!QCma?7i!((CQtiwVqnCN?6R8QXYxaZ$w;d zTOwI2mK7`giE=WHDARf{lMhnMcQDptku`geA#jjpVgZYA{hXOV2z;qq%zH6qjJ*nr z4PIu0G$?aW-?00_0Hh+QL6(3!IQC{>DEHJ@jB#lny8q-t{+p5S4zHN|mII+wM{}%? zmu#u1@+_}jwnQmy7Y$~6+$^%U7UdXCXiXs#Snv$1UV8Z0l`Be=ow2mTdQ{jS2K&Dh zwR6XlJ#VJWMmuvNnRUQZ$Jn*a&8_QDjHma0g5|c-{l4AgC~^yNjCAD}QI!=n7=^3} ziIaPdSu)r|eBa%7-tFEAz)>ZXgZOi-D1ig~@(YS)09F58UHkm(8-S$&SM4JA$GivG zL80pAO<s#mt@Q{u)rh%t2Wh{uqGzQ^+Pct z03~|ibs2lVxB{HLNQp0Ijsoo(5T`&?=6*g=Oo;^vmDvP033I@Zz(EVY3qCQ4c!DKM z(TX%!dH#?Ye?^By!SYsPb!5vAF$F~q31w~@wPp`{g#)msJh=)v%p^>2B9kQ+9ti#f z4f6c7Ta9PIg_Jha+=j+jsaLuU6CG8Mqy0hq3OFB#kSR#X20Q#UR9uBh>Fl7(m7$!{ z=aC9K47e&4jkTS~dinJiH%4mfwkyI!*Wq*WCK`4@8uTWE9t&ZHAWXQYnb?k0b(HIC zolF&;Ps-grH5dYK7YM~zp#%~&BkD__&#%Ho*h4aFEM<9n>X7wr9gT2#sw8~Go*wQgcC?%o$GkddF45A5pyI8 za7rTzJVy2)rTauxULL7%J}Z}Nsg1$;(dh;=1FGeWXR3b?BF|rWF&*m{9k{~8EZ}4f zU+Pz;?EXrrIUDz^X!Zak6+?0n(hfrrMN=6{qdo{jQ5u7nRIUotq)MnfNN@}ib@h4B z+jl5mTgs#GXX&1M+QMP!J^M}v`YK-uey5HtrAmUj1Y2sG!B`ZE2HsHB|1|-jIOeQ_ zVp?W9!kr1Kavo?X2Tg%lIScT*KOIUyU$9 zSqpcDvS12RP^j7Ou!4#UP>R(njwYb=`;9V=dcpHRqREz{{Nc{r=^4_b70g8voK z7M}FPbp?DuE!DXRp^g`$sncyk*GaYJ)Y&hK?~ZXDGWC3od)kT-T4qsCt%wRWFX~eh z&3|!U!aYa2(>&|$1+|!uo;Ac}6pBR%M`;E#nyK)9C^?fog{bpd?r9Y=MRo<@rPn!q z4_=t%dO&tp(67MzwA&FLVHHk*V=TZ?`nXWw`_3*aZ=!^(`U{KvT40> zl?oIQA<2Xi5E^N;5Md;8%@UzliFBnR@ntBJDGMP@yi`=NAVCsFllAH-#}Y>lW1SV% zW5pONgpX}@*~3#&Y*XeKn<{lqX>v}R3o5iiDK=`fN`q1A<;gGZx#pfv9_jKdAg>JR z^UgQ_j0NUbP{B+wS(S078*ipbrkY@)X=bE75c1o=%kqdlE@h6Lu9VIW$DteD zBy{@9xGs&e-Wp%3-Sy4{D~lYjBywq>fhjz0Ip4b=p8N8IJ$6xCu3cdv@&%~7 z##}|$GuI2(*Vm`l4;_!C*V43{U2ZN9m+xKsHEun+cCKTqyh`cIMhPtRa=YktROf50 zJ++!9Z)1(=_IC%m!Tw;M(dYH|9Xc{xG_Z%lVRcYuUMTvhJ;T^%ykooHkV^mOecFos`>A6}6+4qFK?Y(RKU{{}Q{tAbZ6|#NIJKG(R^_ zIU%RMvrHAKb51$-*-Eogk5{@<$#clAJhbxcUdwxZb92)wb8mU5YVWFF-$LjA%Xm7Y zX~yPTf68!Oo_Tc5ZATw#@z{vR9^Xa)hnLX*2YDF&0XUq&hDsO$9?fwg6%S!KZs0>Q zpcN82(wXw8l$xo5hG~=_`j4&oz;Ev61-U{VvLGdO(2pvWFl(!JvJpcZ>p#!OxVRbL zCUH`;POf=5WA6EtTk~W&P`;(U3>KsEYDMj?EA?&L+h*H(`?mej_My*CL5D$pLCHSk z3O6}mJgtJgg5xtk%k$4q^y7)sb!y0SwbhY|Hav7URM1EJm+tGu7KS{{(>3wU-KoW% z^X{fM`O0ECtUvZ^uj_yJh0cyDk@s4n{w<|FdcXc>>wLMGpW`tNsgVYQ$>Q)BVve)i z;3~B1Jx=46!{zoWwZpGfy6IsrdZ*tIUm-#^t>l?=<7DB511&G3zggJ&%9=yX2aXo* zI!od*H?@(vIS{LjGkKq5V1k1n2o3abC7Ik#P ze(w(^r7+R1@a}rQXy5^mMMDTIoW1=po|e&K4kpg?+*2<1Bed-~#0jO8rKSRN2hr&r0w1%fl zn^!(v3Hfv@d74r!QEu@GVt;e_w5i46c}nLVrRK%>e+1=b&{FQFG0T}N?vj4Vaxhsf zaao$ZlL3K_no!85l%ZKl$|tB=dP^m{6`OQJ`N{ zQVSa*A)2TX@B7Tc(eYXZGYNz8Ir$XY-iOD%VH~K4gD)SS3_5og$mhbB*cg6B@`HEf z6_rxK8<{gu`ORyW+m1hUrFSR67{a^-gOd#^BUiN$nkBjcI0n(u++NA2SF9MAU4A{4 z5W?3rEIP7ZquxjOV2c6 zONZ%c;QI%@Xzp9|Gvwce;fTI941KvBbU2c1(3)=X4M-wd9;g*WZX>H7wE+6(ggE3} zr8g|>#*`rG(Z8ap5>LAxv3_b zNH)75@pc^wb zz$mlVxS)Yc;$$J-!j89lpDmhZ(f6PGXK6Y3k74)$FWTDTGm^i(D=dYJwp38T|4)B) z%@Ct`9&rbSemlvOY1u)pL}B=5>@XL~mE21608;jk-Hu2I-_sPr?w=ijzhfs{TaDmH zqLiq!WJj8Ku)5yOmGM<5vFK*?kSEg(OJ*?~eW$Nur=`@+=^@nI?{Ww6SL@1#)p49; zcY%bMtrm!6{@GZR)smYNy(JyGX4`ab;!0gaqmfZXC`MWJt`jnzs#J5_>lz_s+=bd< zZD+yJst@9d6wgSd$NP_n&RrvLtRE1{m}B~u)3S*#$-GS017{^UX{sO^?v zJt@}5xu%V{^vf*KfEZSG$bD8)R5^U;lP9`b)3*=KZfOGaT@#_DB3h2N67WP{(S&N5 zU4Jo}=7R*|^{!klV9DQTl~2wPB@h!MI?uNR?i$8@_^T;OAyU}T6sNEO6wJ^P(;?p4 z2Jv|AO*S_^f=VE;eUlh-;?w1x8osn|PwP3K`LoL%NjjhX_gVt5A84X?v(MygXGZ45 zT8qIbw)0TzaT;UD(MW>sQu44jV=>K+C*gF4fUQWDm(-a&>Q2)rMZCyt!kKuihUdz~ z;$B22*1+Px#1sC~6#9*oiPof+xQUiR?dB$tN*>_a zBIr-FxRC|*OcLVfSrBh5EGIde%pSocZ;!=6!M%oJITR?RCVmK+kmsVd6l>Y+;be_j zk?juKn!jsA5vsXN_G((G3GXDAv)$N>Ndzk5cjgnHZCn;bL=5#PfwWeHdGa9pz)^1A zn-kA5aLd69blUG>Fy6~r&cli&1V*`EWh+(cW4`&9i^bM>#*itZ%+tw%3~R$z87MjOSNfq zBSkyg)!1-PPbFL*`+umX4hA;do1+I%H#*5lx=D?d z_&*wXv;dmI4d!mRSz;(=3f5Ygm;jIYu%tOEN2sY1VbM_RL$Jm3$*LzXkft^5N=D;7SuD|BXS zy;(2zAh}%GvPJ7j3f5CA*bWJ+z?sit%rcbrsC3a~2>6lL682Z`)Fq;jK9OAhG57#c zc=)_ykE%I4`G9=&|1UhRGd;7wk5GtzYwq}5won(T=r6N_J_RodPypQ3G(ZeT*S2tu z6Iq%(na{X{-tP%WL7L!~o-qCE6 zm@GP_npzI(Fehh9wJl<@B9c9VFQZuIM=Ivb@m3IA_&?D+ZG}}bse_zg>VIRZ{?7t*Ry|hug+L~dkF zCY&rKts|DPb}t$+OmopaNuZhnz(vW{?}1W#rQSsbY-#-+=i0d(Ut!1Km|2%WxlrPoMY780izGQ)x#urs1?de|yquzMAf z8KL_h`OL!7spAQxWz8gflQqS}ZhTe@y$z2ak&s5W-+k246sG&co_QF8bgf4(bM}uM7 zI*Rl|xdmh6Z;W!ek6MW|#v0wleQ9$ox~h#P`7UfchS2FrQ(1}B|16$tA4lS+)XAu> zaD7}nQg7j=8yJ_QUbiG=Q-W>ZYMafk*;vXYglvq?l@ix&qyo0a#Z$lHJ_%($mDDz*mQ5!lI8 zP`3f&sLR8NqXSiD_ruw ztdANXskdGX4WpteJPVSzCU95wg;<0xft0Bd7!J zz`XFi?fLF#KZd>ChK|Z)Q63m(e1VNBUn%_*3@x>0+>#B!?>4rv=LcQDR#p$E;*?hJ z!HgnLX;B10HETH8=srXr2VKKrWU}idz^2U+o2#yf_a2n6EAk*9Ge-;2lowbn5X{GW z4b~q&2uKGunYyk1L?`Fam6ucXh)hORvv{PSmLiK{cl?Hs%3!1;nCX@=5~4;qUZ~}n z(2eQeX*mdi&T_ugVudm&(u85rZ*CZ&p3WazYv^rDGQ2j+&QSTNl|fPbXzK7SHnq_W z2dXQ{6NUg%f!1Q3_z2<7#l2YL%8)}Fc5)vAXVQIM>>36T+6tK-Hm!mkG zmj0J9T*GqSaEVjsnx;8{9fH3Jgp5KT6FK)9(#!A;< zHdd4=!_`vX5~d-S7j$+yocJdH%t1AnHFCH4ranK2!#kbpn=}`n zsAG3K8yk`hFx%V>Wt8*CY-EzdKh)Gff?*&L1EbGclgk<1{znwGl9^f8Y<+9W4SVVz zws1!*T(g^Q*iO||I!B3H`1NZ(h%|A%$^Hw*N6`=F zn2JRD@`8rs zH~TA1=n{1zyLG)K`Nn|5d!RGzBRghr?0(xBL%wFQ#b6t}O+FJ2oRWHbycc%5{6kzR z6Ije8{tJ~g=9A=mB1`Ip@QkSC`qT-XM3EZaS~YCklw?>*6lKSPmdh=%grcO_iB>(t z%duAm-`h}NmDT~*r5Z?ulqF;)V~_|bA|bWAx>O~7Gpv`@ODd`Wqcb$7FJyKELb&cu zJZT|K0@zSBL@WDOpV`yTZEE}Mb6w;5Ykalvtllgq1- z$JF0=t6CyEtG<5YlaCel^a3(9?J!<{mAAdl)3r~^K!Ed(FI}RwmFQ9)|AvUP}d|s zp)~unVM*yy3#6!!skNVvzJ|<>&L~RGeOuA~{S(;jE$^XFW9LonLs|zrQ}aZSSRpz? z*qJH}u9ps?0~b8-VoNJ{;t^zQfoB4%|FA$CyEPgg-#>=%(S08%e0*O|#;0#?uEU^# zAwo(PoQc3Ie0;z33o1hYYo#OMYBuAbkp$8&G_OoV(RCtlAck|Gu#;l5?~|AlJbJ{F zE{s2udCy(}MX+L>Ge*G9SqZVS|9!IJ8&7q+-6qTM7m~nzeZ`xWK=wok@Ka=M#}^7o z&t1%<$LGLwKK^k#>-VL{VRKe59QD-7!Jhu14UG!>n-0|cZqh>Nl|g1~{CYp+cqEL2 z?=W$La)~*Quk7?L%=X>!D?lTEf1FMr9o^9*%^?#fr-*%fp#xw?-cxP*<_X~F)z5Kc z7W({I*wX#kHJ@Hl9w3V3$a+8akpYCuBJnE1$k3}8?EWDh@-B=0RDUp-$O(-v$dHnV z)l%~=?*)@_IeIVHQM4bzeKeO;zdVrjD@qtZ6NHAT3VHS->j?|%UE>Ms?3sklc0O;} z?}n6ccpvtcBOnFl6Kf~|hQ<%_>r2c}p9bZnClyS|mr*+ud=6lm^=!tRThdt7_{4t( z@zf-a1#^%35ehp!kek5|u7(fE)PE*MtJcVi7tf#{Ch=YaNCZcece<|Iq_OT~4zo#< zg)Xsf9M|a~qq3cEq1?4Q?P=Q$^BTOXT7WKlyV5cqUFY`#fKEx?Q!XUsvP;KMWR|1# zn<|7?vSKKTBV(qfDd-57NOUsML+F_n%GB*?-hG8;DvfW0$)}L?yiJt2Ys=(766vFU z%98d)!FOGADWug?677A_VCSNtVp!HR@lHh(J`$Na*X*9iQ3kW$Fsz225y+2XEd~*;;8yE%@>C{jdzw?DL6+$3tV%wL>Z`LE`Md z_4|7gGQmuD{ra!+@t&QMCRjI zI9QObt&b_xjU@y_jJoaAcQrbLA8ixI9s51@cb8$h{?7(=*$816nrZ{p2C5AT{;!j= zp!gcayD5{Mt;OygjdOM-TWrJ>#W0n}`7{z=%m41HwVeBhA1wucpJus){}$1Y+(f{) zReVWJyS<$P1r0TF_X&Q;jVa`Q+u4P5C;s%n8T*xd-O{wmioQO3m{7=!*`Q#&c5+bc zkcz!Xk;v)Rz9mvmip->wA61!~gO}|fi#rm6Fgm&y_ORNv_p?F9Aspgj+POQ90&rqY z{r=pwQVypL+_ah^ik*5xR8L`l&$iU+W9_-q>2z{JTMlN5GojzH2$7lGlIY|IZPfhO zQ_Ijip3r9XL~8ZWo$Px>*Gx6rrD}whQ1iknxuU99)$Z#;Os?T|pU0KJh5b0rI-0nDZ)oVTg4sJk?KxoZnmF8*bqe@-?NI42C-PyLPM++0c7jrI2p<_J=FWRt` z*rPu5vj?4a1m0YARa$ToVd6=HO}<&5#^0&2hT23e68*h^X`D%SY!;L48P74wDzK1M z;1CKU#knlsow+b$insiQai(O*g`Kaie8t0@8`MFfW9~{_K99&RZ5#hE*5y;ksN)XQ zVXOP5mclf?e|K_aMFH(+T^)0sFAHPq>2jX-vX<9%!oR#-b&FSOKZ@)Mo^|q&#F}?2p3iNq}x9YqXuUD;wA`n`x75HQ5 z3EV5}xEY!eYhI_^i;&{k*e!5L&%6r?_Klkc#7fs(cuO8=5dhLhzl0Ql2y(#F^Vja`Lm|YDRct;Vc8wm3BK7aF4jd?plsKBN>k!9hBJ{GEGO2YvcqBK^)$s3+^F@A9} zx=|B>v7#3WJbWO=t7|+XT?tYUg7cKyC6quuS7L*Bp(d|sl%#zRSkD2OME*IE8tXuC zb9IkhJIkSqLXS@@st8XoC09NUR*IPyZ!cSe*W!U}Rd)!{_<*E*g`8HfO6R#G29W0x zj%R7h=?nj>@MEg*YpTC8ER~vd6^>^${dz4&AP+|7wLNVxNo7pq$fd;Zpzxc#crP`L zCwkW!GZKpBOvL~~2>X)jezOK^LT}L~WEPa%Zpm_vSfZE-#zzy*;Vg%iOfZ#Nc-C=} zQv}|1|5sm~+YdNk+h;%UUccD_b7-ReudP#dXi#j5C2`cH%GED1FL|2-P;a_qqY_#g*}tRmQ$~YkF0P7K1dvTi&K|#xM)oYAs2hh> zyns`D-S!I$9F!~6;wYwxuC*#LIXL*9|9*ivL`@NtMMr^lb_;Z^wTbDgA^2(PlDSFA zHQv&AXTgUD4}4K+_LT|?t%eCZeET{L_5-BPh8pIKFtFTCpE>KHMP{<+NMZ4UC%%tz zboOanvC+e7`0-}bsIq5mcA&k`x;2XZ)l7F59wv#El^rt0OwzzU^Auo{XdS_bz-mg1 z_2AkZs6Xu4CtMW+1zIThNIDifh%KGZ><8UVuf1=!@5&^2rjpyhL|t7y*wUsjMJ_rR zIp-R8Vd81sm|oe$ENsdijzNaUP}ttXgzA5^i;~|vw)k64DnXp?zY9(q0htaMx1M_kW)Mfp z157KifZ^?LiN&P)gXO07PkpN~9a>6q8Z85s`*tt&YKCVKBe39O5Xjjd8A9WzgY6pM zSC7O|t$Qo|MIlymKhnKdc2wVVATrzS(11m+ZM8|sU55W*{Lje3w}i)X;N?N3`&JyJ|Q>V zplDLAS4`Oz_u9wEPqXx>bzlPobg8CiL41k!UD9m+0|hgnu*09yjIl<;^VOXvzcCoZ zz;7gpOp-gvYRuV%J`*A9Z!SG|`fw=MgvlAa@+p^~CBmAztI8%Tn5>CqLW~L9`LA&V z_Nx#N{Yh4xh-2eOdgc82YFYH^mGj#JFGUKx>P%D9XZ6X3s!)HneKWE z(wPo{-LT!l74EaX==0}E)%{@+LV=X?bkv`!)-alBpos>fqs5ucZt$3z)9Tkz2ZW_G z4e*3d@Q%&kVJ~;>W*!*;6w9Y1=8nj?i36##AOFn4vXT>)r}|@3-LT54+;?gHdt0Jg zZo(#u>x*Nv>lCBICB}yZY8(JhbcNpA(}%!L$hMClGf5o{Z^i~gk)?###k_{Q@i1J0 z*GV+6Di#t3Q#W3wF$*J2m|T}GB|NL*b>4g|lUSooR|a*P949+dp)e?03OAjMU!7%l zK{hy>$AlThm|0GlD4i828JkHYsnt{3G58ZriFg|+W~59kyGkv(68q)-+AmqwkVbJx zb`2wtX{;v0N;7(%(x%0ebaG;KM|w0L)WAeGex{83;E_+`9X|$=NXYHqQd(W9*dIwk zPzfI6O#bCuxrp$>0 z;b1(Uf|kPE*J2F%xE;P6E;Sy=2u!LC!ZtXTk4wh?kV6hWQz2!%L>!MPqo%R$~JV|v=yG<>Zs?zru%J>`|G?61@PHtlvD0SqebdK%hH%UHNUsJQqgvSDO zX~#U-3MZ|b1O5P(fF<%=t%*YliwdR~$aURoi00S;B3Wbz16p*t{E-VeKXc$px<{~E zzrVJr7jXOgWiq8ynyvRYp|o@h0sLD$W3n~WiV`zN3JV^dcok*myfC?OD&9@&g)Q~M zrm(Oj9NRW^Wm5VJDrsBlp`etAFtT2HN@RV*Q<;icyjl6+u(QXg530ViH6x4~O}K-q zYvHDq&Ir}7Wz=ufck8vRZ|>;?1ko4h+Y6B5z9z4*tu=SEvRN|1)TqK;Um@Hr-0CgV z+-igq>bsHR{0B^Fin=kJj^efPjc(H_5fScmPpjFOpgxO9Y53NGNKsRGQMV|3B~Rc} z+G_t{W(N2h?C(0`^P(VXZ>14gDu0M~=EuR^lWTkP0&cu24h&$5AooAfV(kTzBT~{{ z_+6{(Euy#RDsaS$6->GD$1%`&Oy*WJR=)1hW;lu}dvWqV(Q?iOa(FVCFW@#G0c1q< zJA&(~y%}$j&SpXzcOgdeB~D%K&GZEaa3z+4hPF?QAXMTcS<*lYn;TfS z3!Hz=*P2drJ}ad$RO*K-quc#6zcq7{M(d#w-LecknS8tG&m>}Fa@Vs>S-39HP zhu$@vs6U(&6jV%nB(B4{Ae1}Xx1hg(`bW+ySrkHF16U0{h)@d5MR@#o(wS#k zl|Hl%vt)r~QsUQ}aVQf~^+n8TlR~9*io6sHQ-x)UBnqB*a6-RC9n%dg>DVGiOAZU9 z?$5h3*%F&-w5UGxOzTY!< zqJnosPH?D+gy&{89WC8Iau7oO2(*`27-28W074L4AH6CujvkvstUGWAz@9Su*Q<36 z3o)o~Pxxycg8tzObr-k4+6art<<1rM=L$ivCF-`_Ja9wb$7PZnf_c(O+F-A8OATyk zfqAsR>Jk!SaGhR{Uy|<$6g_U~&!+A?b*eG)$;C+FlqucFYe%%9}k4!B3e=$AU zqzP%Jb2CXIIgN{g83~7kY7LH&my}5D3xEV9*nf~1K8~wwg#raoe5hA<&X?)%4N{}- zr(I{;zcGj+_E$N|x+%Mv7{A~f_2VQHVQp;mkgWcXiTSnr`LFA8{TrZTsQG!wOJe`I zi8>L3r7yOG@(bpdJov$kn%#SvcOhb=mzHZ_?39m{M1rx_rl=DE?m` z^&cbA&HH4|eY4XX1(TyfJM%CeOEhB#qcZAHb#k0x;WEZVvi@W$MVX||CC)B9ZWtwC zBBXc4PsT;ZosmJiB|o)~iTP0yXNCThR0@4qY2=VL>AB(%RrJ4$T?L(IC-~$wh!!F{ui7kJoP0^rXLFk(C|LG17H9RcZfHA8gvnW z;A36jDy@j1f@ zx1xm>g|xCuYv~RdyL?cQ_%eq$o^T>PWrcr~8|JyD-*#$5vBlz>Z>$~UDjU;*i58jP zc21%TcUzE(9e58HUmII@4l0RaHxe!IM6!&DS|i0Vw+nHx!f8|;`9$JRdg$om$x~&S z7F$Iq9!y}-pTLIpY*AJTuVdgzroFry)u=kT7wL{+dCf9{ymSYa;u)ak4XE5;SOyT! z#mJM$IMI$(tbU~f)170F*C@Nc9mNA@$N~?EI0OoKz62zcO7GK`FDww7nGP9Ke|imf zBjm4_`D;smeqX`uqr39QdGeFCU(bizY;|MaD@$P>SmGjqHe@VQv|04vg8MddHpY7p z#uj3~t7qJGX03N&vq*ulOo08o7!l=qnID{1eV(G*?C;|{JaO6Oxj5ZEQcu4>bdi`J zcXRjVK`^E&*v!1}?$?n#!MM$DTV0!TuDsfu1CI*x5DJm}n*IAocu;r`sUXjsE31}z z$0-W9s$H~>H499CS)G&?P|NFtHh~T^y+v^wy)vMt3q?SftOCn-GW`Ko=@ZY|!nx8b z%$Ks}&i#_|6PMZtEZ{F8qtjM%g=6IB6NAxP+5D1PvIjV7z3GnYB(Cn6RNe6jlW$HF&5gzDl>BOTh(!dwo0aQriyKiC6Ewao5`| z)3XLtJ^1+hUzY}lsW-3R{Z_^E?zW@y9)IPrS92aeHOlNX^!vANeB+LPDNy>Q=gRw4 zbuX(M_uc$%$SW;R@80wA&xZ43=j2su-AlKv-cfI?td@j*wQpr>2b>(rVdODe6}W-yyFT~k`Q ze1S20#iBkdO#Ng$gr|N!_VPy%`ur}33Vw6b4C>>XOACHmzZ~av)M@b1tq~SgA(EuA zs%GnR;*DABY}=UgSnJ;;N%`H{hfoo@^Z29P(D`|oMm#ZScDkqjfXLa9Lg@ikf0(!^ zuX=rz3DE%-i5)8rH@y=H)y)GBi2bky&j&}GdNnVE;=Wz(yx*-i8qF<6a}&`|lYoOX;6WPRWH7c64c`;qL@*D+kivk`Xp`W1 z9kfgE@__*F(br$)nq1BO@Zx?U7rjVrt3XSt1m9|`AMh-qW4|I5mZ18W3&ultI#YN^ z#(qKC@+OWWasScn|I9C1$l?r+SRxhQ%Gf&y((qJ_6bnm~-(BS*Mk)Cpb2#!??)``j%zf0j)Oax$Iovz}JQc%aX@;J5$5T^*1mhvgAoFO``g2ZE!n ze%J1OUVgc;gkoO+6%?;f#_BFhYmIKejdx?YEV%4#p$=F9Aq^nh`;N9)ZaN=>#zbS_ z=eruuo0zzjkn`}&1!K(;mVAdC&(a^H`ch*U_-`&Wr0OpEtdf++25!m*~%=u$|AqyB)pN_>O2(}K)-NTx%n^dHlJFJEn z)o&*1IJ`BTXbj`9-#Kq*IIw*|`BjM6OSjk{Fcbv5u#kaZzHl6frhq7vnP!yv0#KO4 zQ1~0O%RpKScbS$a{m$J2MC4n>GM^I2Dz8o3_o9OY+Inn*9%E|tJ8^wHbRzf;(cnXN zukg>dXPx^Wtz^50(eOTU3&gE^SC;NH`ATJ5^FCj_d!Jzrr_!geV@nQ8dTz-Y2lRV`BA!oSdU&ZFCcT?s66(R9rxYN#bWPt>%WkEGiY zyKZ9rw&l^r#C)ewjz!Y%pmwGP=K#BVLSo57VN!@%SBN_wUh;%C>Y+g#h#qD)vZH&+ zWqJ_PJKBGN7BjP^RAWqZC3{d!Gge~P@jOzrIr~PL{gKOG`KNs1=Hb|}2Ndw?CN$8@l zO!?+^d|`%|>z2e5umGapq?lw4Ls|g@!P~gW8@ve+1q@+jd3X?Ih!YRqpXI_cyQAiy zf#^As0N;1rCQ*8nH&%)t;y;vimkB+>tkwCu%>*I-r@@5!*Oqc^Kal*k{!fiFN@w*q2!HZ=1I@5yY zBYJ6OJ&-VGCH$~^b8S2kehbQwfRkp0e!79+OQc&5`OEO;yoOb3`X{p%lA9%Hl~!<* zyNLu|#=f#w8EkGyHpivjXbo;P-r}s)f zYpyc?~?sa{lFkage#D4r`#9<$)Jr z<9q{GYmlJn-A@igFxU1pHuM)ck+DI&yrbrewvRZ} zAPkvyj`KfM*O=H=vG3BR?|Vq5SUh&|w7?wkhVCP^k*`mEZ&05`KQWG226hsgCI=^U z3^EZb&_NINNqr0+aRR<8M0fryIQ(hPd%JXwaPyjQ-kKlvq2r5l3jD=`=Bc6ea~cmB zhfi!jZo(6ehiE}|ZECBlYTetS_JmsOUIMG?k1U@zca7Jhb8y8RoiE!_gPB(;wx>c< zS_L6Yc}F@N6i6pdO@PE4CThzl@k9=X!lGr68SYe9$;!st+ksYb|*+k}dhn7!Tt2ipK9mLrZ_1)j=~ z`I%#5{w#E`^}hJ*+=m|%OJJu%FxjNrj2)$U#>B$l9WL~{cg)698*Y^gY!~83i0F>o zNZ5e74dnl&?s`5euBW*1)#L-sji1@(nj|9T99lZtm?2y8w85NI?iR#HGphC*$_*`? z{nh1*V7->-OWe42!?3RV4QF;T#^3-)6nPXn0Lw9s(wSHmsYW={5u?t<6lPKX5~xUq z-bG|12k=^?{`kVs%Vf60Bbd}Q`?D>uK%FT5y}+z4dQHt+d{3lKmrp)Av7`C(s~Z+i z-mY$lKXYC^C8!9o)!Nd`2`!9&g+mvQqGGvOfuO4h4%LPuMu_7@G||C}nq%2C_}$cp zA+mnO^=V)nF@q<`cACOWpS>b?XIZ7tmN%{9RSn0E8>e~|_l}Xt=i(~rG@xR`veM$> zh0F33p(4~qou1dcSo^myiXPb4<#G4pSBm}@1Z{361FTx)6793Zf^(LNMbQbhZ*ga^gK`XrWcrHUMhQCyBC;-lv025qf z`tb>4HN=O2f*ov|07Wrtn+S@wv^zu;Y8~L{lJah1`|)T^+t=ULWRD(IGU{Hpc7ENP zW%wZ;LfcCCDK$(FUVM3+?8CwsK^2Kg;N+q-_FdJ}1dw z{A*slGoK=)3<&H-TIcj?YR-Ec$#&p(^dO<{?Km<2Z`~&2YAPGXqd;{cdrG* z(C>K&j`YA}rW4sNbyZXBTtWoe;tMUvPA&z!2~*^+I(*97PTH$v62*4LXy=apA`wF? z85v@VVyQnmp%hx0WQo)0CtEERI#i7m*U`%9V1c|r%+2^$wx?dXH94+$XXA8-K za`&?pa`Y31IY|5}bw1nl{p?qaL-g@0%&e%q)3IM&;UCEkj>q2tU0b{FFf<3M@bJX|#5+LaCSSOp2{@ zD5M@y%kD@uwI*wU-WGz`wHjyo52iaAQlw;*2Ia4kW-F5#+E|aU|r}8WL zolMAm^7{l_bvG~%?hiZ^hy|Vs)Cn{Sv`9f8aZDiRuj8A6j{++Lp9eMuwg$cl><;`G zI2t&~W(nonR2rK%D0c6%IACM-azq;&;vP3HM2Wz40j~hu6&zPs6ptE~wIPaGk9<80 zMcaY-YX^GzHLI?_!(qA{ckcT1pCj1!$30EUiw=g(qv_smi2k0N2bIy+S`k>g7d;$F zY9JD~U~XGD{smy|weOw!^6?jEKbZ0O;@#pI8$h}>9CmP-2)_tAfw+#Z-cpLNvMCbJ z*-()LPyEmh7S9EV;h-{dXA0O# z4!|FLL+l{DH3M;UDr2EehJ(WJCe^DHU6d0E6Ns#wpweBVF>Z<#!KZ^xXhAzMn4awk zc_Pm0rv|L*+_Saemzzbtkt1?^cQ|T&5T_wgpLorTz9)IgxZeEg+_wPIK&p0~l$iHR zc2FoyIT%D@9+og$_8E4?@Pkye_Zy?=8#XsSkrnzn@W<1QCNaL!^W`y%68AbJKb_L} z;wuS>3`LFNbimnwp9Y)_6~CEDA({^-lyFBSxlpQ_QA1eCtCY{Xa!ov5%wDk?CAxS| z#fTC46&roEl6z1^kC&3HHzAQR9GXRQ$^{f za^3qyL&#@Z4w6xJS&Vj{jgm6S0MA0xXuyWo9fb;nZUF@is42xT=Z*GXX@)=E?AiK` z%2`0lP01(H*ldv`x?_+8`YiZfZ~;3g*K8@`moBWTH5PB*_P3pSDR?DE8H`=QuS2%Z z<7peTF^|~1UM-6D8W17i=-)zthzZ0Zqu@IUM2>s8>hRG_AS0?^FsnFlgyf(FxQ3Ae z1UNLO{M%14ROV=D>R5aZt2-BF>F#CCQ{|bCE)~HWOGs0hz<>ct8+rdFW%o+m&griO# zToM|h)cp%H@)g#FLMEeEOXun<;E5lyOU2(~rCe)o)Nv(X1tUW5YyXvHD*|l6J||1r z6-d3fHos)kLMijrIAQ(0fgW=EAXx%_mTn1>esD1HOq;!>S?F&YTm4%Vn|gXTR2DNhL~%#}W%YT5C`3D=9==xNhRMn35%_u0HNA6{cX zucaslTiM`Zc8`qDA5{Ttsg?ut1Q82ior76e4VjlS@y>|Kak=p;l+X%koN=@lT*o5h z9t;NsPL?8w0u}>gIj{pkD3|&TDffDXf!wa(IetAD41-rhDSx?AL!vLNVN@Iv1>+2$ zl)6i!Xsqr;2i5AX(<`$*iefe5VMvGgvA<1}cbAlb6A)J(`4bx_;D#rZdFeuQHG)Rs zS~&_lWW^Jp#!SCz5cK6om0%N$v};#HT1s>`-M<@ZSe1e*eN=X+9U&B?p!yWT75{rH zr24!}+xrG1$Lk7LDG?sx#IHsbCUNkU@0lJDk#EXei#dMRzDHe-MnZzcW#4UD< zdmR#wXcaHJDc<%%?DV#r_kjTlABg}b#4u1I4bh!#ErcBVp1p$yFZuHPzLNac3B#2V z>FhviUjkindh}wi2S(7>fO{i$g@?N_V5O^bWAy4*7e>aKoiSYUw=5Eo$*fg~kYWmA zBIZE?!y-r$vkX!stb#mJ)sUmw(NeAg)7szkj-{G+-xOhyCK zo>1j``{VHJ!;$g_kw8ZOhFN3$z?(Mt$L=rC6rYwSYM91gOC9kdO5>Ee;m3kWohwY3 ze^!kC*+Kl1f&?7d^kNJzaxxW@j>Vj5G1XWaXOkceb0i1*frcYG3OP7Jn3Voq%-mM= z#1b@bOQ=00bA`{6Nxw0s{dX07PREq(MATfVTdpj>Yg~Z-4}o|4p7S8G&o= z7g}?KF)253JlWIG;eIFYAV7h-+|WX;R|zMoL&M9Z7Lm!;T7OD0uMxcPnLOoPJd^%h z`SM%beAPB;c1DLs-uoLvWGkz?291UoWr9hjn&}}6t+dWI`y6uCHLu#?Bj0DI7-HL8 z)93fk=q4&DMhD_oo;Yc}4L9A%HfrlmoqeyMgls5>#|RqTM*3+4A~uk*wg2Um`I8q% ztjb}8z3dW&B%d0ebo6Ik3XaWmV?Esmz(u$O|2dS%%{*6P%AR+>5A@CG4}Pd;7fMcW z`rtYw5$id4jQ9iP?IeF7RviG>?;s3`!b9T|5E5aA#ZxuMPma5A z^yQ}a{@|L0hQ$PC<(yWQ^Q30Ix_#a#HR_x0YOQrzm?4^vYHWqp;HQttGZU_q1In6K zkz^ik107EhF%M2vRt%D7IBs#S|4c9;D%r(qIvsEgmt=|E4GA&H78FZzu-w}kp~AgX zvK7m&^_z5M{@kN3apLE!vM6Sim2f!NR-(azEI4hm_nIjrLZ~75aAn|mB0hM-E4~K= z(B9ZKR;#+_rT{!$*7?w%x)+tDk~sbvn*V8a$6?lYDL?tkD;|>cIyNM9P)TbInD&bg z3Mjl>Je=d!v>kRJIy#~%7Rrvv=FkFq&-1+(SBhaf zKs8CGh-$6=Bn4_(Z?lWOuhUEy15?nrc?NsR5U!ND@Y)*X+PfUZ`=B$P zwH96F;(q22wj(LC3NVp={DV2Nzi*3k-&y`5b$mQmQp$GdmMcc{>V=k?+5^?vOjox} z*M0(grSMX;ynFqb&g%B}%~d{rKC4&1sL9soeqXomJGsgtl2(#qr&BvbIgQG2 zHgr!M<1ttTu8=@9x6=*HZQ=9^Cd!s`W6A@9y(G^&Bil|VF1w6W(RJL2`4C+xr{B@E zd0PF_C$wRsVx3mM7<;oRL=A<>Z?&YnB^xW}P{0$G^`7r9gva(X*iigr<(MC-H_{s5 zYOVa(X-aUF`h2aM={21~-rXDYZMrwvNwUSP37+{&v5M;e!qXPHL1H_$16As|d$LV^ z@Khj5`LdgPO4c*`TiRNvb~`=mTHt-iAU2~M|g<{O@Kv(vTiE==~TO=~-a zSsd;Aq5B_~fnYHB9UATHXy=d^!zmzDkjaplkon()?Lbyv1N-Z#Nt?Cv?`B|EU~h!~ zVMl^a=|XRpkmg$oaK6pzHQ5>b$iq&l`u=esga41?-;|M$s`^QsA}3UXXYg-)h3|2) zd|c|sCw%I!E1iM<|0XRAZf!gMLu}^jql62&htI<8=F*~m$DUW-ge+CUe~?bi?;KLshGTPz>jI~@pL z2yllCOYA-M8m^gx{3K)SLK*~C$%uCY5 z=~u&Qj0*zY#cf$=<*D`8RBdV;XMCt69=ukr^;5?KoT{_S3tfgT-+t}7x~kSiHn{5D z1`m0jIr{eJ&ui!U9&bN_?*D)r>6M2LKyL&Cy)kWkFsY`t8DXSd;OWwv_$8B80pXXFh9^=bZMZ!w5^EL>a44cSGU~r zcLC*IwV$fY*&``YgP#lX>cLLn6N6V*|GW6p1*N=P0lj8=Ry!XB${2saCHmCo_-*l6R z&2P33Wx(*HQul|acABwswi_6p-R1=gYe`!kwkovt+~zkHTnZ`MUUd`S8i7=s*A3(h zXO6}nuLp*2rPST!gY@_~(BBnd@BQItQG2*vK7Iez0Y2sH%8%LdABKM)-X7k;$Oy#K z_d#LqVK_K`bpu9b28O<*k&!u>-(9pHX>|?%v@7|gL;=!ZKsYr!j?<{|V$?H>Bs4K3 z!_~=Z^M9ZQ4IoWFgdyvAB{w@4KB4@CEInvNr5R@gQG-XF(Pq;qq076l;vpo&lb7DI?3R4<4l!FP4Y_tna)5H`Mj*UhaQz~ZKkrQu6 z#jSi})>p-V3(+W6wuV>|#6v5@8boyt=^XJR#y|rt8lEc!H2_F+Lv0ZlbqAQ2CthZ2 z=Rw6T^PqcL1Il_pN_9V9&WdF00w7gIVqQ)VEu-WtX=AB%y+A~W+_)6Z-Nrh~VvLj^%{yoRyb2RdH`ih-)ozsSHrM>MHe%P>pzq)&&_2WbZ)!P%S)g6#fK&$5-82ucF@c$*^#G9iWkpiuMBoTNJjBOpjm?iV&tNq?_)D(wv9%)CZx;q@r&#fZ!6x zLp`L<6`DF?G&o6x?pvZ+jRcpxm#T3|Qq!OlPAOja7cm2GGCquW{Dq=~iAC!gf`#A{ zltO(CBr;9O7$%oDj5YTA$@~^FJ%>&X)&=SN+br-6&fK*HMuFQvw_S; zn@Ma5Czac3H;FyO%wBF1hn*yMN-Kpnh;r?s#3iDrCac^vH(9y`BB?AlD-XP2?WMPD zyn~yq_dc`n#dmgo1X1j#ox-nxK^zLv=-?xfo{PxHRuih2H=&M26eq)En#V?4SlcWz zkNs-GIm#v+qK;y3q)f;7$YD{7qD2yjrZ|hpr0xuqFo%Yy(^f&mR*Qr+PM26U6I!2& zcy|NRMw{?tz6Wj~rC75=es-owrRINH4U}hCn&SmgORvLqYM)3{;=Uog<%bOOGnFH= zoiZCKvxPF7C$nubn$Xud;5SDU7BVjOBPi)fqQ+l`7EC za8g|xL*HZoRZlIwJW0>1w!il+Hh`1%Lv8)*+boerRr$#@!)s5wrr!EEYr?*%iQhhn zhYa&5MDJBY!^5O0PXIST&Rj+$=u;6Qlm#KRpN3U4($gAWdd$G+^P*LQr-8Z&a_ z##?|8k>WZN-`d#LDHyUGl24#qb!Xi>@=!NBrq_TGljfZ~TRX{AzrKJQmp>%VO}Ou= z*M9W7f4x8Ze7dAQ2H;`HSyR$*9~t<;=r>09QTIs%+O0+EP9JVb~8o*R)~yq(;xn1O~y>)zp}`$Zz?X6WXw`ec-fZoiKKhPdHg z0BDTxb}!pMjS^<883n4!bow=v`=}&`u{^)ak9iVRmz`O!GW!O*WnF z?jQ+1!4UG{IszU~oA?v^#@ez9i9fP8 z)|$0v*-K;K<1cL7QaPNl3SQZLpO#_a;E{*nwqD(e0zMnx9idPEDo$0X3$K5A#-~*9 z$%_B}Kjx>&H}CxeADj)z15S*Y8nr;C2Qs5`&m>d4!+SpPkuU7@^Lg^BP-e)d0YF*1 zoVf8BdX()e19JN+6N(mcJ>ULX(jj?qGR!y+7^+qm)kbM~m4=m?&Wo|TaV7|f->H51 zNGMogsW@}t#lVNZ0Aaf6p{Ks$43i*Hnshnw&{>`R^l>sXEwR)p)fzQ*E17%j)$H(L z1hwsaG)LGqw>|Jur+41_5;xOzw~w}l7-6`P=6gu7xdz!+j#9ZMdQhx$v~PGOMTEyk ziebiqHCuK(xN_spT&O7FBE*VJEHOqIZiJCCba%@2PmT3lvezVp@Sn$mtm$^X3I9ieKOr6<<&kdIqeZE ze(i+)2cmib@WF+3-Q9ryW(KRB*pth*BSO7_R&Cg<+B%|t{Ppgj2atY?b5gFPq#kG- zf$r)*>zVF>>xFz!Y`71uS0MlfpJogeqreJ*+Y+>yE(u}Q03S*Fz&RnDc~h$@wiKRG zea(XD9cw--v3Rkhpo`uGQbewYv9Sh^Hli1N7mpX*jBZfQMhHK2NZ0eYbBNh_xNmws z7-(xb7q9l|j*f05Amp5KftJtXzQU{4Jvis>!jJr1L)9j@uE!A%Zi3zOLLcHroN93M z60-?*{ZL0Zf55FLs^q{W`yxykRPrOf^LEOoF%F2UTX2v=SLRH4a;B^)Mtvs2#oDwx zP0}Euw~w4IV@+^$wYd&QuegOczNwSpW;yJu*AI+u! zzMHt6V7J1aokGunQ?(vPS0h{(;@@g?ZG&qmx=w>@DxvFp`gakLrG=ME6-IyGF;aR(IyWEfVB>8scjyXv1ta0do;9gD+FNb|x59 z;1960xp?sZA2~cV=>KcrUXE7<_$BhQ1IJs>0ssFJ{P#n$y8{gRIzDiTLc=Fvx;ILJ z5$M3vhs45V({(Mz4Xg^C1Q<8k%_M)+y(l(oDX)SBMTM)fIW*p2^^Nd>fF>WK5p1!v zFuxNo*d^D5`*mZpZ)hX}Y6t*K>cS<6=ye7$Nvwl}sJP~n>p@CVxIrF|wB++CD4@yr zponJd^BD@ts3+oWnGu$R=Zv`dzvqi6zmU(Ds0Qyb&O-2WI6XDzOziiFk}0v_){vH~ zHu2KTrZ?!l4S~Vg@e52k$2CrVU^u;z;{uIedk;Vx!eW+8&OacIIJZ(mw=Tz9 zcsWuP>@F!A>px_x-g3=i*^+VMBCHr?=mSnl%_OXDaEn)>+yKc^70Zg51$^ikZp2{y z0K56%XoJ3bTu(5#yFj7z@S!YarpZ+#aS@k9pEuU$#SH=l%N8$FasW<2N10?c=EyYS zaPgcoO6l$`UQs`b%Bpz)8j`4`~` zFax;%V=MtW{}^|G$3L(Ca*+c#0jvP_{}G{oQ7-@DEdIyh{m;hnKQ`8X$PN$y2>tu6 z8NlIR{C{57|Iqi}^S>E*{(15Oc>amNKLq^K{(sjB0Kfu1Hv?=r0Jb~;b$)TlO0tak zpZcZc@Ca9Tr2!idIA)}fV>B*K?#A22WN9^{d3|awX@v5^+ck=tnWIB>C&EeBt>cI@ zbQt=8;h1!b%t^MNM>hG_fsp|4du!}0(qHsmppBu3439{yKxg9z89e0VY z>2ri=gg#m}ws8gwr)dss7FH6#d2g9>xhH!)8rO#=WV0`)pG9DmdNfgsZ=pBk_uSXf|k?S2nDEOjJ8eV#56;;Kt{+> zk<-ZFcpkHrIg_HM^n`L%F?Ft{t8|SLkVR>Boj7X4pQ<(Dh*`ke;YD{GvwrNVP-`#g zw;99y0?32v1v1mRIlygRw?y%2KL(kvln7CzZCr3<7dIoe%A8iv>3~ z(R#Z_{;{D)8r8Ko5^Uq<;o}m7@Q@0r5fp7yhAxnNIE`q(hzh>=?f%Hud4)M+Zn!D`|q;zeRr*8RcGEZt){X!7q zpYX(XI|`%D7$ebqf_$3GbwE&>AAM&`Ta(K`mdI-O*3^8)pJ0)U-J(97g8qE|y*92GnsSg5{+TJMrTiHsp`fg7%v>^TDl;&FlAo2T8IRLHPJ}jrV;gxO zM(i3VFD^d}dCOXM_7UHChUELo7bJd47*YCkc(syDto1se8@cYEU5p)%?sE1P#u>BY zDegoAhV6>8$U9|;-F>;2e)L`(o;Qq>fw_{U1vK8waKfrQXw+AI03Oq^vmrz&0LlPi z!jLBZXxXAkVkL|G3<=|MyqCyxKcXxZ7^Sgk$TSj64A~cKNE;>%DQ!YAGXYEbS(#-d zRY%U+YKm|Nyx7waNUiMdaXCjgps*dYzQ>jtC#blfin7e3d2i2X8Pyjr+KbCa_pL7P z%GZ=kXykazP2<)9f+frZJSs_FH=xTXDRrV0LGrcJ25~>jfa?ZpxBC^pEyad8Vz(o4 z4VU$0V>a!syfBAX!`LLD(lrL9sBkefW3gJL0Z(XWfT{8x;Zz@~!V%=GIPOyARQkJj zF>xOVi|;^FJMntSP>5fWRMCs8n=w{+zcf2%?6$3R@iW(?S~R>;aICFV(e%-zXD<{2 zNzwY;5`AquQI->YmvS43ivek6dBqSl+%Lm<0%3e)C+a3uG!}wbZ=m>Fo-phRL^?Ew zWvDRHut|2FOa9MX(Jb_6E_7@pPpOBYv~?aO{!ODQb2fVGw_dj~!}(GNV) zceEB#Yr$MOQyqcKcgRZorbhh`VDfo80;Dh?9R)zdgbI+9SQHz#eCimoVHcrpffNT z?^4baAqs@%F#fpQp(@|^8&P-KmA0jkJ;wRX8i6X`rj-$c~@KLeNqT8YaUXHphZgUGjc5ww!$9&IRLziXeHX?QWT)I}~cM zOj%l$io~9PA5+$h2XxX3MXgdL9O@2xuz(zmccIL#Hh=-RVzeM`4yF2_Zw;I{dydDq z7imuRw<8G0v0tr3^PP5aO3YxV<=r4geJAz z6ZOO9n`|ijI(M~5F&F&tOQJ~ErZ!YCB|-W$b2GC78l8UxWept5o8Zvs|JGyTcKOfg ziM{ZPIp!GJUG%xBE%S|5TUggfP`E^lACzOSb$!%X1wk!b9J&7N5soqTanPFtd>pEt zM$3$cz7mzK$F;+zlKOR6ei*Z6vWkFLIRv(A(xP(*vF8-yK9fksI<*fwgaAWQqGE5p znWMD9D4W*5|9V#yrbmDY^fTNPuI5f0D))%1T?}8#SFEi)U z$?GHdff6Z%!e7D&ld}>no1i+vLC^G@zJrKbAwbEh`H-Ddkd7som2gRiUzGLiki-x{ zypoCFQmQwSc+$;?z_{Lm^~w9;L5wfm)^j|0P6FBhC!SOb7AK|-F!M@9=!uLLQs zLs7BQFc5rMddUoyrTLU=g+yzVJ`0 z*UFdj+F9|${Ub{(S+RAChVgN@@+Tb-ihI|vVdDWNUC!~kq^vkVQ!uheHiM(-`NX$P40@a=^dG4nMm z70HL)WxTOc17G_yInh0}`p@KgozlZ3R2xT+H|4o1?0Tw@q6*e%t>Dhd|D|0R4^l{j zJGYiVmlh*OWxuJsY~zu#R_U#ci))M2v`J|{SM`Q` zL(yB3m-(BcJcMi(n#SG?t-O>@fx3Kkeb|ilL6`O=0y^ZjU2d}dRVQ=f(ab?(^Z*bL zzK_hJgPfs$9h_*2>lFCj=7OJ%?;A0v7`wnrF8D^m7$4$z`G^ePE=|{ z%%G7(e$1q=$Gj`q#2SXua8eI`+$Oxl;J7(rbF4}=r?E}aa8kJ0B5wG_YJd8%)3br3 z-$G~lndH`9DIB2;6zyEIn;28Pxb3hyukoIZv!Smpgy`IpG^mP1$OJIePv9Jmqw2(S z%3@wkzvjqD_pVllfC@Qg4X||LAQbjF0@+aShFg%;!S{S8dNA;P1v46FINpoX%M5?TB+lbbOJdM zOKB9w4Hr&UQJvqKY=f=G&L<^XTiDA&Y(x0nG=}~MPU%%PhLh-g$v12KzpKQTer^Tr zY1H0Jg8KnI>%q!a0S?Fsm!41ai$ftd7*FZMJP0pEUJNQ}<15;Roo|jrF*B5{2j6vM zHsi*Bt8c^a`VE-tbb=EtRw~`>YL;u^yEw-|W7XC_)m7>&kV>RgKUYnXMdUrOX`1p; zq0*`?-W{I`wn-Rqb7`r`?|?cr4zsb{b2RkEKVQP-9Ij(`wHIcoRY47@X=vr70W@gc zov)s1_i$Yu)I!`Eo6fhK!xM?Sg6N#8urd$0*d|)e;%UuGqK)Qe@62d)+_1j%F!;~c_i(<4T5lH%a$(*zOx+vcBW1j-i5Fs5W`)!5L{ zGqjXk;2qT#S~L1^+3jo$l?_*4Zz=Gf1(SNKfO18+6bJ|;Lfo7{xR2IZt%COG>; zXsG%xehwra$8vhc_f2X!pcXMxD1WmAoMctEAnLCecmx6%7J-wlkxgmj)O(%-1*!09 zDPeb=nY!Oxs+M}$iaz+|P!$qyf1;gsbVCDThtgPVGUAHDOI zjhuuqtb{T@Aufo+OMr?q)Hh6w-!yGPt*(~}O)sSh|1OqYx_^(7YI^ZC&T=3{Mqh)! zY+z5#l=DqiXz=!J9oss-|Fnp?5k@LNZu`@TefoGiOXpL%(f3VWm9cMiuZ!;RN%JF) z9`hd-`AL%IbZ|H+%W3vcorpx~$NqMs`%- zYbVze93!wFb)n{7HYIZc2Zjbl?qU1sriLz3?g551q16s^Fnsq|uyI|VV03SrH+Lt!a!b8CJh9TveeohQu@PIL{h}2L+T2me|-piAycnisR zYFf_{&*7Nol{|V3v0rLJ!(gEVOR6jDo=56~o)aBlCDjb~?1?tCV!OU_=T<*|7u&`9 zaZ8DbVZlZG9M#MAIG@Q&TFo?wu=u%3wwJ3_8e1CRxm$`vC+$tgSFw)Hy116%Q!$Qy z!KmNhe1@goWTgGnvLun>6=ZGqt8~rIfYb0;0C5vSy?-oE^L#>TL2V|HWW1OFG9^wp zmQMt)C@62HfIDJ?qU1f~u!T-Z#4Y{MNj{}a65fL4c9k?KiKCgMaCtae<>Y$rQS|F& zu_S^f7K+Wbp0nklH?c)6der_l@h$Y!F#E)g)OLg@@Bqf>;sjO z&aW+fj}1Daj9C`+hrXTreO%!he(q3T^6U>sZou`@04R*n%?!;hIlpa7#`sj?`nZf2 zs&Cf>ksxn_)TF}3Rth6xmo??k9zpxO3{8CGVOpm#akecb_eg4L8HKS?YSb`V@U+6* zN}RT9n$s)QAee9|O@y0FcZPa{UGZPxyxdcW(~o}kKP!0G#j!75&q5#LRcv+YW-)Bh zGa}k4qPkY?qjb0b>WtlVVSNR8f>huolrl!dD)bHD$;~aOsFf+qyM;LCHwcBL0)?2R z4(2&MonHy;BZI#CFM);|Jxjj%*mh*%@43v-j*fq>HaEh3^@Dd(6+HH$d~nf zFyVlw418P(cB_+C{{b z*EN3aqKR=THcVGli}6YBiJLj*%ck!uu1x;r773{5gZre(h@R*J4?@Wq*hhNa#l8c@ zS!|9fqt0Jj2Gh)GKC^oQU!FI}q|Oo_{=(PwSw6q1YgP1D2b?wS;R&}mp-oicPDQ3* zy6scJLd2tKotLThR`3PQ68dCa!7NwoT?ra}xI6V`Rr*qsdEzZzaipEGgG9j_ky*W(^VhGnIgsg4pjc zdSiEteuC{R2j-v9nB#QIm_3zEUBbtCCCEZz4J1Q}KX_Ls4EgK$Mdv{!Z)R?GAQ(c> zvI5tpbqObhCQ1b(h0)A44*#Mps)K$i#xbXd?Bc6v{8u&=)Bdr0C04J&(SMLcG-QmO zF*6tC7q1PA9<)O>zJ!TRcQ8sH_8BY10b_hvA1~ z;9P$yZaB5FBKrr?E#eZi*HkbEf7sN2X!P^UCdI5N{J2Tn;>&zt7@Mc|qJO`YfhtWr z&(CTq=eLxM3FD!<_PU!z&5O2$z@4xob- zJT1b^2KZE5KZS|4jfpq}BaVcsdLU=6>&jO{hV`8pg>aEtx~;b~i0BB0h_FnMtx;L~ z6bCy2<B*R}W>0_`PDO&i4f5i*K zQ>G#wmRVF!+KJQl64QNrcRnzr5g|^j=vVqQ@%od;*`L(HF(gZv?NRI0=zUwGhKuhp z$S7!#O<=C^{VbC4h=R4FMfUhuMYqGE>Ps?shmO+By4#P7ueL*qruI&LU>#FKdTBh$QGY(DOTZ>P1yw z50W5v1j8k9;wJG(cbhjG+NXW)&hx!e6}FR>Q-zvRF@nfkyYsW-czF)KLU3L!os#T# z$#uqA;(AsTr?1CFGef?}7*&1S9RtBG2lLi(#wPff+Q(1OCMQiac>%T!gDBOxG) zXjzyy;koj4sV-oeqTM^F4Cd+Cjw>v70;UFsNNUb0+JiAi7Loo}R2tvc;-WbRW||z3 z*I)Js_!l{bWWzYazUl2XPE!91orn|+9t@SI(~?7-pY|6MhtK#MsLh}r*NQf3bdWg5 zQBCwS9v`Rhml&!9t3^*4wzywzVoB076MLq|pGkOVLaUV0F~#GxKx?pi;J<}+-6nZN z%`n0-F|wMHTe(X|7YM-(JGo}@$$l>RonaCojOLlP?xWuI3@!9hf^ct6#gc7TX*g4p za)q`e44vGd!)i`~S*2s$9Fzei65C@j&oa_RI8w5>30;xtN;=Di?!k#gCF|-aetS`c zHciYu6U);39OXo3^*eiN01c4dv zh2pB8USs9`boj=A_EYJDD0#phj8=;GeA=Z+;j_q+1@=Wc*KY@0OAWoNt?^$5s5bre zd|PJ`UY;^;;f#N?7ZL^$WM3uy%pQl3M`)e(BF8>RL{!I?Mv(^+M_;~n2VZ_zyA8KC z_-x5MZt5&*44jLGBu_CIMHLdo)O+|Fy!=77{&ld(zvKl0mwovvgOB?0E2?s$39@>D z?$0)aya+ANiQYEY8tFfx?o6aVYpd!{Z^< zmrj2=Lty+dZoI`CmGQGm|L7Ml@lO20BkKRP)V)ncTWjbRk zVBL@{35AkeeAe-ji779xok4o|UM{@>C4Kqvax+VKplp(sXnGikqyk({a@!`rl@3J@ zR~m81`_%S{@LQ)beXcR&RyAyk20mpbXC8aVlO2ish`r5sWX;~-GK1SqFmM_JSrki6 zLadx4yqM$^(@;oKB5m*XN!-{tQ$HiQ)5Hhh53R8?&qF>GE+1mk87reB!Z0`7pGW!j z$+?(a)6yC$^_x`VqOpp7r)3Y2xBGan0WJ9~A1;KgW>l{L!F>7Q03}-*ty?p+GN6qW_WCXs2MVu%vI0_87~4D^un=+}mt<%k#M z!g4yX7t05gVp_r)+w~{+Mg^IfwOW9wm{9c>m_G=)OeX~OCzw!QCy|!>a=@Vw<;Lyc zEzo6g(K_(Sda7}@W_c${n61+Jmc^FCkAgQQ%IF{?kh9^+(@R1-%C^`Ubkq~1f~ z#`6AN zP$o#z_`tw&P<{F$6;BkI7QQlE1K7;&Uex$efwQGCnnUKGm>3pL1E$6uulv*C^<57midv(x>cIBAG z{mdcDTZ#7ydj!Ia5R(_1L=6m_VN4838yv$7#axS}kqN^a-16f}q{uu9h%rs5ytqe` zEmLFVWhAdG4ZZOC5@}X$+bwrjmWzK5ho;+}oarviiIOvMF5b@_B&@r?DF4nAiO|!& zWMhEQgdJ5`rSl#{6mNI7YOOG@O=8i(+L+_8hj|q@aPlk;O|q;2a5t){N^^#pFahm@ ztxK#9J5XNvkT*w%|%k z*)k^q#-T%*l0RVIW&Wss+qS%9sNAN&PWe(r7{eF!QD=6f|7EV3-fs8u`{>9c#wwNg zq1opHsd)ObtonX#51I;3#p*ra&}6E$Ot-Udw1RF-Y27hOY{a#y7-h{$)K^YZE+eR0 zS55V|X^bm9PF_r1X&l_mJN^epvh@vr1jkq(|CX>LrgVw8x3i3VeT1F_-)>q+ z*A<||%?_;YfYJ6)%1?iw49*<=JmdgmM25OgesIZ}6n@UTm!j4Ez|MPwbBLQ1dNvj1 zG<2Zo14(Awv_ci037>^_@diFjzC)-QbHS#n6h${G@t&OWo+fz;>ATS;!$;)0M$^YjQquy zE}!dgC2KXM7-t-SzrC(W5g+CDYeNnHPZ;z{LGBOd(3Red$E{PwIKwmk7M;Wh_az}> zXx-Smsp+ePKjPL2&pxC13P>FkD`9(Sh3{X0xTrN)tfqN5W z75DX{7NKe^3#I^^!yr^a2b=-WAGYt+9h-Zhd{mpZ$v7ZvHsrT}vo%kWqr;`f7mGH? z!sISH7(ky36@2B^jEHeSf$bD$_VR9^2US5b!7578_sQ;Lq{?!}rCs6C6ur$nGFmx^ zG7!{29ua2e97Kuy2FatxaXhpMbI#B^YM_WV_<%V!GhTujQX?#xT4zD8z z_$?-da)0ckibdjtI~&OihaG0xdeh{4US*5{YCu9Q9)M-{5r7$+2}@}ZJNhKide)p@ z?KNFTk;(#t(Sau38I5krRpCT$_A(SRlvhc>U|EXev=%41IHuC1|&6$Celo8?ppGBI>LLj6Dy7#YAMS zi!0sLJ1m>~cfV}6|C+{bX4P5aCkTkRxX){3A6Gw7jIOw#Ch!=cr%|V`7Eo^ao$C;B zB-+r+G9R1>Uy-Ut)%T|l;)Mxb-eArC=(ONa_)(QThkyxuKC+>q$xij~*En_-OWOGZ z+3hARDa{DTW~3O4QVaX16hI<}KSeuP_If0WMOQ z4Tw?})8)tCWVvQvqTKR>$%(keS0f56ex&?ZBTfL;OI%RMb0M-ipH2g!ew72P(?OHy z4jHEJ1aD)!+>krFa6bR`DwG&}tMdDadRHB5A$Q*3!mg`C!Jy;P;E+iF2|1@o%({2& z`%NL=^{+Uju>colbK~r`EWwxNRC=hMl1CAY#H+mscQjtI zV58dOmXB--O?@FK8!(phlP_IaHV|Q;G)`5d{Owx&Oqu?J|Jj*x5J2DmN|;kyHrpey zfKCxu$6FN%W$qc?B|hni+I~U><8z}IeK z5J{h3=3{AKlu(MiN*6hB3dVmFUWp5t4?IzH<+R;LnKv7l%!cc}j?Ux67W%H$KosAL zCRhrK_^%}kdB+;-G|SNDbtdJn@}j9Ju&j*A=Ae^r**-qB4cRXZctqj8s-`YZeWHp9 z`kgclV{fZCZJ;=?;V0${bZ>eOXh-DppZiJO6?V*eW>A*PN{iIZLQ-Cp4kS>R_3RT| zA%1i<9nrPe2hX@|Okljj+coYo&bh^xH%&!+ki@Dtzjlie*i4Fb`{{Y{6#lM9=lqjc zen2gal*w#*&}ey{1+Q**XrFVsc{s)KlX%z5s$h?eqVc9v!a{i$bDV8x<=k9#lpdZI ziHLcEeca;@w18@dr&1o=C|1Wt@iUQ?U^Yg4M5zCzFgjOm$e(5Rl+wXxsn)P1DM}vR zhtIi#1ibm&8WkeRpzHjzr6hgfQ1?l~i9@ASY%Eyzp=ry+sfB|s1smf`cBRm7M`C$c zQ0ycoW-)SoOZL$Vm!-duksQp_@aapH4QsIH$fsMTL13lQ#4q8JP>UpDt4!!pUH?d$OL~SrvWpqNj{38 zDfgDtTjzPU%>mDXh$NxiJ@=&w#$Xx69b?ijsA$2HZ;{MK2L8}nU_r=2mZ0I;)Md`#Y<Oks0Ep7t!yO*hh|$sM#y*O>gE9a(=&#)vK?xw)8A)o{)Z{GZ z2cQP;2wijJgV^htp^Gy-jU0M~ff5r9$c?J zAWR^8heUD(j|ufMJWxcJ8&8DQzLcrXLHYwsYrA_?6j!8Q$!Qz|Q7PNC^Wq(JqB=QJ zLj&NYhIgdbyho-xIV~8>QTOL{n-9ZiS}9?w*Pvc_(lOCUC_q zMoH>6hM!)DxbX7)p3&^T>=WXmGgcDcb#)Nz-*eVwF#mW48XR6MoypBZJy1Dn0B_0_xfBLeIQz6=x)2RY2<4+bN3Oy=u)iA2+ z@MvPr^g3*1B5TrPSWL@nH}jz>X;nL+Un+Yt-$oHdg7*vp$!;Z^>M2!L-q@+FGT_#Slpvyhc3g=*P; ze9%6}Rr1Qzl@~B7iJ=*FOx!7S+K}nxNv3x8^d%@8b4(ARuM0YH~UY5yabJ)}0C*fM+kn!nY$6 zB$Y)%nIRdA0A6W9OFvAfXm-!NpjqvAx!9b9taFl;G-mA?P-9=7$tRV-GnLRU0(#_5 z5O`z_`)FWJv0y>hmE>AY3-PS)ov7$8n1*MsfKyr0yx4R#X>zN}c1mxsDJ-vj=rq{; zZiuZ!{~B&NFV~jmLVE2wPDL*KvHI=2)Re4V5G^3c4ue7465pEm?0}$N2Y$(IL02srzg*l9Y`~Gcgp(M6?aUwjRJ@xxm3qNjduO@-HhM22+ zKpdv<&;~a{LC?O)v*N1Z163+9VjP!EA7$OB?k5tv)QF$mOW5vN#PQ~cE?6mdRypip zQWRSQ*Tp$c4RW-k2r1|5tu9L6&+aKmg@LI6Gc%(j1jUjs~y`Xiz=#fz=H$`KumrF6Ded=I0ok+^S(;*{{?Plor z1_|k2r!s{dq}7z7`DC#}K03C0n`t*haiQ)g>*$&rkE;0<&A>{?PT#s*DwM+gxA5R$W z{uRYr&%2bJXl{M~SoD1mEz9kb;Ek(fL39~O9|FDnGPu}nYmjZ(b3VVZL%1Xf2AS8U z8F{Wis>9a5-`UCj#w~O+{x|>2xtm^#n@$FC1?b#giUdO~939l5TSeEh!AMNSe8h8# zJssP1t1}v2S<*O~gaGz@bUD%Q{75CJgISKV-@t@k64MuUEFa)T%hp{~)SOuk zyMgF{WKG7nxKCj_TU2bYwQyF2L85tu@)h=!x#?(jaF_M(T;S#UO=FTnqnm9bw{0Uw zhe_~6rq?YQ^3!JdbNjFP)gRWd${VVd%aKL266qXAviwWc*tVJz#0)JE>;ydXZiLD_ zaVi`Fq=6SZQB_qgp`^SzH;d*bizaS6i*l=!OAmhb#H8R&plv*ZJ~&- zMB&%K12r~xM5n^H!lwxa*jmL2fPTaxEFfiAa!7m(W@u)5(B!(t=@rFfu>k%aaD<94p7%xWK+Mf-c|4hdP2c85~h-t(0Ou$`#1GhjQr`naB#DuC(Fo?_Sk;)Br z#4YhLiBGS0_(_M3hhYR)lXINVi2&V$iimmA5641GsbMxu*NG{t^Q&J&d?4Y?57dDR zr_GIFB}z=FS<->+?WC6?gz6A%y3Q%3AGM=SdaxXuVz=U|u{t#tS~69#M4`T_JXH^y z*~|2Vq9)dgIQ^%sl+2bjzipoHa1%9CR%B=cCfy^|uHX0jGq=Q? zg?CwNOBYE`O#X3vPXKSMDUjOg-SK8@hT)Glw=_db%9qj8n4`;9nSdJusR-tTs^j%DiBkJ{tf@qIdTz#m z^zt?u6^bvGYM-vmvYB33Z_Jhn{j^m?uTlrxD%P|5KwWPyy@0bwR<70~0WlD`JU8Zt zZIQ7^WkaI%AUAdt<%Dl=d{rWOtDP{!ghY`_Rhg6mg(g6$5JsoZmk8Nz{bHp0q(_%f zm4v;eo>1uB@f?Y;WC;D~|RQn>$A){HWIXJjIY3WVh5wqx5Jnn;y z6qix^GFSafZKwwFZl^*wZb?a6gEp7(&O~trWgQ4)A@Rqyl!#-@FQJ26b-ip}Lqt2< zV*GsoQA#J}yf+{g&yKrrMgiZoiha8mR{b(kHp%(xQS-8JCeg4WJST{vPFU*|8>X1C z*WCsC*#bArk_zvPmgl0pGEprfbu8!yLU44hZuCnHoz9Ux=I#d}-H+br6#bFi!L+hi zi?pL3eR<3T1zyzO?-iFvpL8%gMPtT3!!zD>M1PtM9fC`{f+zx01eisAG{SfGw5OzY zRdgC#p8UTOYIW*T8S9B_u= zIp6%FeMhB4Uqd&ZxDLDdrp8T7mezFDlLaY+clhlcnkE}4l28z%oIw8-QDE7M3PG*o z>(eo-WP2XKGmJdHW7u@}Uwwe2qr!rt>;og%$vrzl{(Hft%(L}+WJ6`+jc2qba<<`w z^8_>RAN6KDBoc&_&z2X(C17)JolrSn*+Htw<#WhKSTm!;lQ zm7%KP^2T|@0K;EDK8*w5G6a~wxW?!hrI@@^Wb0PpGM)8e?pJE}04HQ)#)2nPJ~jdp zZC8!cR1cYWOgrZ=ZsZi!XaTZVx9m`LHDY7*eLCj#bd^4<9x>xnbh}P<5+;p7v%lh$ zA_7Pk9sHI2#d?;l%6)b6>hN4p0vsr3#qc;0j zl;;+yA4%frCBHIXgOT3@s|6G2Y}7RGm}}yO8P&GDm@vM_Z2x!qmHXinQ3(2i>37#e zAU^hzaY)GZ&z-d$Y#`YJ&JwGGCq?7G7E4naKOim<>tu7J3Q9*!Kj zA>Qe$-r&?MKVA3v5OQ%998_R^46E_FnEjKVUn#ND(-z9$%0z2~X}QJh9Y<&7Gi&Z$Un+4ZF3dvCntN5a>>%v}7+`RYV z9G!0OOm9oFM^s7C^@-!iw7-EgUw|*AjYRPfDK%@2J&GDKEK#qkvUY3yh~@C{Bw_|% z!YZ*O2M^mzcGxB_jQdyq89fzcHg=n0oopNud!@cDjQFIx`94em{aS{We^=G?%ZJL% z6l3^bEJ&e7jsf^e1@To;5+-W)rnZ1QDf@6f3oq9Lczr= zMT!{0#&_s+fu7N^usl(N*q){bBMIggaEN0KLK`%>%rW*Q|n|j06A-Zl8V7}W{07vZb@scSq!F!$T}-pllpQY zUFZOh6(3#*jFcw8O}9A<$MK2n2T>ypG+d22b)|kuyPhxQk(2I=9((!ZH5D4m@YSDj z9DNK@e4u`j0?vp<=Ldsnsliw!!MACBIX;v84!|Fk6FVPc7k-r6*IsVCXWk^*fOxq$o2K5li3InNsEH#Gp+SnhXm(iUBp~|_%F=p zSpJEr`MAim?vkLTKS=%XNb4VAw(H|T=I7c3pI!Ta?K{`v3ZyZX);%h(Y@;Xj>ZB9H*!9ug^f&8< zLlR}Uf&~~Hj;Y>m%2RxW?N2fcaJ;}l7hyipOy-&*lAT-4S0H+#n;&VQlcnROhXEg z&#_=u9+`8AcyeIESu|&ph+=DjPV_Qe^Km$Ttv#;gHvQaW%9o5D&tA#W;zP?Adju?x z1O{>g`WuPidz9GIn6^vDC`BYxBaehYxek7bvSY+Y4QwXDND7f5Heqx{ z9Xv?72}EJnYN7=`hmA+8M|OwD08LiPN?{XcE1V=0BWOsth2zi(vI>5Zx1Fh_pI|uD zq%KQc|7D^$SF;a++u0eOF*6rJ&+=Ep6Y@yHUkpm%ECIm&is6xuY`Lz|1@_8!>KpX+ z^w6^;@2j!=8B)*_vi|9qBY~NaS&)}&b_S#aM8r7TmcbwEdDiZ04=6*F^95H2O28m4Bg08%>slU_& zsDBNWd*8`M0#oC%F0>8usu61c82#ooj-ofE_*Rq{?KrsKoUnN2m$`pKPR8)#)Br94 z+kC0DcfYO#M;s@ z1RQNq7rm^)MQ$hK#OJAWTEUqDIv#%*v+OIa0RAMF0GLnqR98aIrVr`?%`!SkswTz@V3C%dK&gKi4na%d}FgI_Kv3q34K%y ztmF_L;>rveKshxlQ^T_UA+6Z`{aio2E^Z@%lmUibg|BX&W&#NAZM`yhTZJpePed3I_fy#du&m zZ=b-*?3H3{6}u9?Z61U)kt$Z+9+=-B`5DPB6gRX!g&8|?k)Zjkb*pnZklfz^23oEH z(J(*voQ-EBkD_nTcsHc5^Fm=%{?R#=b0d4yHn7CjTJWJK^<#ZoR?FN;TEc{$h%nt( z@mXfIASyz=P__!n!CA>*umaKSUa3G96{ju;wIK=AcY)pX$0|PGMbSd6p#wH^D2@jX z_Z4HJmWoO}P?S(W?9$my99MRbE8vnEc}^*I9Nr_#6z)fOF1?gQo;o>32IYY%1#jg3 z*n_u#T|yYr&+FS6Oce20!d1_LNF=$4EJ1X*i8EVTJ_#VF3nf~Uy zn69c4LHs5lpa+Mv#R#r49!r_)QMVe6KB>0JrWuU6KqCQt_rZ*7yFCnD6Yo8Qd`hl4 zUL=cO{b4W`uCG){rL^Zzz#u=eA=n3uYOQyW!<-(!f+KI)MQrGwByn{F6o`e)Lc`Hn#SS08CNuCsF zBh)og3MO%vTyDzKd#vsm%Jndf3Kw3X8G!t9e3UxZ{YnU4nlaTq&J5oIte9mApgeir z12hvFf8dfKW$b1|ocs77O6Ic?bI97>21F<&I5TmS$ibdLiM0<#W+JWM)R@U3K*3tO z5*K?^%me1BhJkWzys>Q3L&yg@)21d<2iN&B_bG{-bt#&In!IuqK<8nMC6|*8*VcMf z(Tm20G3=gmi~1vpd+Pojtw;gyYgv5LY)zaUq4puGX?mC-WkizoI1(4+>NQ;;=yC^) zq$};Z_cM?4pd%-doZRgrS@+~hepA=Jz}W?5(%vhP4*Q82pjN<$k2z1{El$XZC?%)a zSfVfJ$Pv2%1DDEVO425xAYSQMZ^1;0Hg^0j1=ay zPji@KTm=(63juDr3j!r{m-t*DWL;_%oLF{B{7^BtJgOv(K~#GB)Zny*KBel=Du}>i zlo4P504SHL^02L?N@e*-%xvPO0w-Wl11z|ffs>xi?Z z*9;V3qe4NpR&ueip&@6A-V79Bh!z6739&3yrV8qy#TC>HLZGo-EICyesHM%U`9unJ zO4R7JC9f_mb`v2Hs4Ib8AVbTLuAJJ%cdo=EyEURafRq-6ewNOx)fg49s0EF$^;_#l zTx!mhsJzt%sHqJQr45KsKmjYrYN6bLA}&yZJCQRIHXvM0u^kjb;x}tA2;EI^BXlu}#{>Zz2st+0cv3(x=}{=|!%~*b+B8vR8V41_p=|bkOaSsF**csL z$#emqV`?+IK`f1X;S{2&JdM4~nAO)K%dL%yfO;};v@k-wl-(bREMKYvqkt5NSratG zK8xgH>&!|cIg^zxglejotJ4oSl@Dny6Lrrvu;PyZTt6l4J|2Xq3a|+|Jw7@LckfctUDDv^3nvqkO zjc9!@0-&n)f^`vrki`252q|wZZ{a6qYp}!&9jZD85i0Y^{52AhQ)Gc>;?r{x-AYff zVoD7=*7(=tQ8?b6YYPKmbPV=Qff%k=Hb((jUKb2I1;Cf;D({BRuynNJRj!X{d@c$$>0mu!LaHDkj+X;95r#wMM&Fy3UeD;lDdYQ|__j_-vSgJc(0AV%ot zP0kf%G~nV;<-83t0?`0Q5;R85g@ggVC>dgv4~cSE76T#YKspVLx6Wo0g_1z=;S1q$z!O|{Y7R#=X%1Hm&VbIWxS$^(qizx-CRdXD zyo8vU+MFO@wrZwiI#@c!sQOZB(;~d~)fk>wy$ff^u&8D_4BW;@?kEjv z?lakexVkeGYRNQnST!rt$Mimdw-O!iBw<194)B-Sl&*XhWbX`&XcI3)bD+f=5gZzh z@|V?wC=YE=NwD)#f(6pQR0|;;0iTcjMQiC71Tbbu z1A_@*h*8=f6Wu(S<$${NP!C{`JUzrCE-DbB=k-kiZw~++V+9)Xl;a8+&G<-`n z_g0R=FSIr^Ov!yZWpe4FA0bp;U0-my)BG-R53JG2r0B_TVJm>Z+Pjz-L>$pg=Mmn6 z#2?P}zXbuUz);-eu=^m#%L|icdP70ZWG&pH$br;=q&tDDUO3AI&s8S2v2`BfI=HLA zr$&S`qvw#?@q$jp%gD2N?x=}JUtO05j9b9%S_M^|0q)Q{Lca?;!l!PgNx8?|)PWwt-{=WLSqj^_~j! ze1WZv5kP(C21Dk05w7}nWRsMIU^^k>es=*_62cU~tOH>KSXKui0q|BQmjZK}|I7p= z!T=J25Z3s70SBV^Kv$>#hu5e7*w?%76ao%H_%Hzmw)lPlm)pQwQE%`511>?}>kcz` z0I&yq1(^d1uzmgZ#+`gAgb8ZA$$BV;8_MJfJyi%-BSg2ENq zA>fRy6hLMXJA{Hble>m4EI0p;McBjI+f5=u&Z%y0qAke{`K*+J|{|Vyn z3?9V6X@B(#Oj2At&G7_C;+t&d-bG^6iPWu8^q9{D;_Hz-2X|*fzd(H4LlJ1;7`wT zb13ePkTnswhm!2y3il7`$`xTl5_&E2~;?>vFW*Zi=__Ropc$>wlvlfS&+Gx3lhH9%> zQu>_0^7gu{X*aZUxvf>!ST3+kNy@Mk<{VnlHdyKq%PI|7RaKP_LlRZKyfYYLnzvaH zM#!2Zc1+AVI3-@mJ#6g)n~-4g=0^& zq-0%0Q5vMKjC~IfkWpn(o>rssvb_)niwI#Ut`uL841W?=d>ZFPtHXe)Aq6h5pKY%Q1b3TD5WfHV8Z z5?|gfZPc;*X+__*D#QJ=OkdjFPyW}YBBIuvNh%Xh4MX#F4t z+w_UFV*HH2{*kc=s&g%c(X|`w%tK;Go|v!nNNiPaJsbsLRX1Jgp6NwHEGZNO9TI6c z;6Wc-FuVsIS`395(Dg>j}H{^tF z8c;`EZ)-TCP(fk3^j_DgY@d#Jm--`wqFwAUdHqxzSO*f_i(9lTc-B3#Ut~(~NdD-Y zBmPWzyrZ68oNP&U5EkJweg;LqNU{L$Odaghe-d^6P-7#D`v5+;$sGx3F%CW#;{x%} zMhyt%)$4!*;c#6p1K0p(ANB{n&EI49;k|-Z8HMv~6~6rI(E5!A!h! z4)eo-aQ0jK`sSOt@UCI|m|H<}LfXj|dJLzkg;B}u^(#JtrhPjIPVjSfB%LQG;*rl} zc~Y6^rC*JSyoJj0iaSEkRj;A@e3FF^&>A(?Q~?+&j?59`>F)SJRnf=rh3dAQkMKN{g zC)qKTcp6_%XBj+kD6*45LYlL*P$%>R>}2-xZPOkwe*-8Q0FJ#Q?+jyG4n}xB8#V;- ziOiCQtGR?mepKud9UT~WJOZx#SSX5e76gQPF*J>#CeRSX%6M_oiZ((QXhhNmosoD* zQFmy{4wM-XZzPZqL^b$^zY&4lK#RQ>YKEXmY=i9tMAZ&(d~Ra2cKW~wi%Ry@Ml0nqZ9(uZ^*bIf)6#9k8`NANoyUIv3S{-eAisVC4pf@3a?aViQswyNu zt54E%V?ASab}2bxG)I+BkJ!!5?~+v>`Zs?Y0%6Rno2UPaX!?(VMDVwR4WO=RKOShF zO_N(8In1!kuaLnpw!t1En}w0aqI{XvC2VG;C=`oc!<4x}rjh3$7yMo2q1yxp0V<5K z^dX=iM4S4#f{4R46X33y49CxIUScs1W8}Y`CKX1M*()j^J+GgapaZyBcBeT4Z2q!r3A-fD)u+|a6!N5vg^!O3U)Kc{17kK-aoF8e)tia9~zIP$sJ6}>pSNV)yb;-W|s(@d$O8&-?R29 z?+g`-x#REaLQb|IK$cQlPkjxU7)=0@4zI;t#sC8xu)b_-h`hWD9z;T+-FY*O=ynZj z+i>8&A|sA2#SD@;USkz=UZXf0NLj9qQef_=2Y$2Gu8>0K6iyG7MIcg7b83=Ha7Y}k zBzzfe9oBe&5#Vl|!a_LOc9gUc?7wt^$$!&9IhlI%LL%En465rax;Wg0)jb( zCR;5S{URaGQV)@Zu4O^w z7jBU}A*q@>h3HG(8=h!Ky#I&Ojrc1gzKG^g-gw;g6>7P?{H$NLL=ZNJo|e-uK~^aH}}@pNLPsGH3+a>z};1t&diIWfvJ65H3J{b z`TyjA1Tj&Hg<*)V)^o{+Qo#S%+CE9T7Ki9#N1Sv0M*U~JV^bXoo_@5hA_oklZAICN zfUBFh*j4VV=`vJQRudEi(NPfCzX==B7QnHB<7QR$u#2cQCk+_O)=15OD7&8>d#1xq zOA_(enO@UDaAzsZYl6!K`F#x}HaUNU4M8s0A#~O7(wW}aMi$3~tD0cd0J@UU1t~aQ zgPu%X zs_I}S33se~Y-lR1jTfBzJk3{KmP%{9iG@X3oj$Yw*Bu}p~h+gO_BC}ERAn5c~z z068IGc>6xa+eR_QImagv&k@Fe@lu9bXfE9GW~(M!ANYx4tT7O0*n1}++2VMThCgF1 zxMONU+FN&&Z5zcJ-P9a}H=yh!f-vY{rXS-Y)%U0)Np}{O7%;O_pYuJpjDMR6;hy*? zp_C5VrubyPMhhm^x!)8`{B@iEsE zXYCmDmW+63ZjUCw#;b$QS!#_B0a(Dx>y25m-CLzJGQka+J{FNbN*iKq1haFtKwF?Y zoxCq%&a0I>H*jRba%S(@!q^IHmyV+5WL^`zplle1+W-{c|o-oDyl>3Z4RoI|~vceZuA zMy2QsvV%7I$qt=i+T8JThe?GaRA~y_%<@@^%Qekrd8NQN?rF=NazQQ&1fP=$-{CFd2|Xid=;DLK-_Se49e&~8(=x4MEF2H zr%3?tfZVD@Yf)%JY@xkCqN_y+nUFp48!;LLPE&UUx288(^dSW}WEAD=a7)^q8(y9+ z)v&U2oay9fs(<6Bk*gCM)5%uw^;D#(#Z7XL5O|j$^hhM+dP&7`ALKoAd2M>55>`MYtKO|u&04D1f(w^W zJ2;ux6P-1p4O;)GgQj0ZDQMKPyG9he)kT`9vwZf#T>zw6Ldj?8ID=wvze0|}5ithP zqxKggmIG`b>MVWA`uZE|=x^ZE`^3=NZidv{G-vK#uhFLDlTF09dm*tRyeSi3%z7U$ z?oanfC>>zf>K`xeMZ`AHX#<+cP%GdaNFoh5ARQPAA4y!nNaY11loF0XDFr+jLWXgI z!SKso0^KaUR}2;f304GFQ{wyqLFC$=f$^#_?23tn7C=5v#Nh3znfQ>W09$hKL*VUk z;R6-ThdY?9)yUllG-!or*XPgq^Zoqv8&1HqTeZLkjQJV!7R>P%WS) zhpOC;jU)O`UP*9&-;e;E%;s92@dSDMQWKn&7Gi;Dlv(!*9a=A)T^W^XMk{5fBaxE{ z=xJ&I1G>};Ndv1#;ozbjOcR^dSa<|;bk@;q&zPkxjZ&5%l>fMq91}tfNQbF%fT`}V zYikCwel{&#jb#yfrw!oa7ZufAJIiaKtXgn%c~sI@1fFW>Qn<=j5m;1yb>gT#onlyj z4P4W(2wF;3f}2u?C5cpJHTew;H$+bAJ5r}ARN**NIP(0NDfJjB(D2e3!qXWgP;S&Z z!o-7Ne9Sb%9$cAH#L5$e5Ac<$CfuP7hbet!FRY{`bM2cDGU-v0WW#bq(TlT8NYxM^ zj+7%=J8dLHnI0gHk|N!Lw$fICipMBqhGLOGGX>_!LoNe*dn~=YMiDmJ8%uQ4za;y;M6 z11>0+&|AOtKjDs5Eh*REG14R|3%}icp)!Am=y2)kw|`uM>U@u)CEN~?9t^v)R7bHf z&Wt$EW0V4`ycgN9i2W~+2yn{hdS8{!wQyHB0TeH7a92Ct*sgL;w61n4RIXZ5Os-ry zIIdjwIIdo%EUsJxOt`CUoqReY|dECF1xXv0%s zW~*Y219Bs#$<2hr{71~LM5VS73kw3bXHeY@wI?K1aVu{*@h)-_d4Y{lRmtVx zJW!cZf>@&E0gegWA-_;rsi*)B@gNJ7NCNH8#9XGBlB~ga3lyjM3DstLo zO?@pkG?KGeYYSOdzcnr*dQ#EZBOxi2iwu`HPD^b_kxj&xEIPtZ_bQ@OdJ|EkP=h3M z%%h!0KU;MQK*>bUi7F{6&lsqrgcOI=xP-?jh{Yn+B3e{Y_`PH04AD9CR-!g+a0;>$Js6HSQ=mpX;6$i#uA|NN4MU_M{j^-0b1b}rk0IH7ww~zfLj{PFe{UO%>MbDn_ zmB+Qa`}B`i`a@wZ5Zfps8?UGn6+)hR+8|Ov5ZuQ2ZI!+oJ>G9G1;i**qlRSB3F1y2 zJWR(=6Ex|A@3hu8X{YCDpnaySa++6inz71i&$b%|@B>QQ4TEzu^-;i3T%etGLehe8 z2euJ_eUOYU&j{b%8$>S+sfbGy7~!qrVrk}c95XB;!^lq36KR58ki`g5LUA(#h963s zv937*@fKUejh5*M_e3AzL|JSd$^efU*zBkPv@9P9H0<-IULSEdWAzjgKLf>FAaZEnXpyD&yKa+>JnJ4Y+)vjml+ zkj)&YXyq$MDY>!2^J9fsvBL9Xmw7S5@?(V=G1D?*gV`~{@nfc9#|5xst{5@GmHyaw z1~?uJb!-^mcrnV1J997nNw!|Aw(Z#X9i=o8rh_MCiaE4Z^~6HNr3;E!p|q^r1QZ)(HQ1@3{-{^$ATr}n6w|3EL%^W!^ki!SQM{)=LN#nxP%;v znIn@SdHl0IzD(v>jC(Jx<~84X7c4wcigsUA@Z$m~$u<92AxRv4NrrE!cvbhu({ zj8VZDvk`#d@Q6BYgxbJZW{m_mKuwK+#kRt!oLB_I0FM9lr|qEm20tusTO-oz7c@Cm z03V!ZoQ&lMc)$k>rwNm;SZj}TI;0l@nGjbhf^svh1f?LYRRjZVWq|PLD-}W2G5)&) zT+v`T;tIu3Y;0vXEn5`ou}jNXrLoi%I6M7*!Se0A-Fl!!70p@vusaS-<_RD~L#kR^ zg7|fslNE3J%Igr2DZR1weMJ~#*bhJ-*LnD6KuTPAu#>qB&xU0m}?&=(D9U=)x%s`l>HY4gw2|40F zw3u~Si-Bj!QIdtr37CWuPM$zBtbr85UFP2aP#8hvVG5n)Cj`f(Iv%sbLS1>RwCe~^dKHJn7O2Wvw(1;Q!>T!A7kH`BY%ZR$)GRM#SBpZk4r;I*!lTYS-@~8tV67KMxwF&~61lIz zPW-_l;n5$%n_r0LCvUpd*Nwwt@jCxFOmJK39h~}yYVmLEA08Z+m}!qCA>#gIVxg-^|4M@Wlt)^|6>s z3uq>-ksh%~FrQY+d!+-$FpPQ1#0KdRYl0<;CrTYe=t3Hs&aW2t7Ba5Q9h>5uv3yl* zVs1K_(9I%fIRhHL75Ik1KFM~)d=ho9Z0lv6v-zuWLtOe>+ZNuAmi!j>Cp>RtAkJ;Q zitG|#Xy4z>mh41xpGkUu0KzL$EdXRYArO-6(~DUEUu8SS`5d~24E<~Ww#ZIJ{-L|t zjeTYmid-|zvulv7ZR{d_{!FSq_WQFzyICO_La$6M-R+Sf)>QhCc*galc zx?%9V+67@3@YrU5D=Cpm<68ALYa*l-aecOq${A&HZ5#u#?!Z#K$&t1GrEv+>kD-zK^ag)QmMbP&}#l5h528}qJ)y4MqC zZQ+47OPMyN26AYzfIaODX`p1x>gZ(78B93iL4jnvGHDhj1QGPTOk`LZ!IVU~ky?3S zV}UXY>4OBdnT(rcKsE89iIa25L)=i5E~NCcVu3pFl$1WelIfpYDBITcV~~?e;kQ|2 zNv(DvLx9%PB0|>fsv}MX`%zC;!m)iw3%*LZ-Jip`gR!?8j(>PMQ3{@c6q3p9m~dPlN%Y2cAwweld)7 zH52NKX7*dQC8BK?9EiWe3!fUO6t71g+SO7YEk$w4peOyPSfSPK5uorT1s>yQ=trL! zx&ZMX7xD35`8;DhYQB7ts^IV|hMnu@o@-Kz(H`9`m~BcL3kozB4Ux4645>aeBp*nn zb^^r)*spazG0>1Kfdu8uSa0BcHLY98ncw!xQg3w8Cj zH6BdLC(iLI?HiqynR6DYwTMKlE)L=t;HPD~9{EWaN_hSHG3 zPAOBc^wm?4kRl3k4IUMZsQ-!NffPysr8|ed-{B)%GYvw0Othxh7i2CCEz79h+m!$~ zLY1{JPYVx`EiYHzDHR+SnMxM6bjV?#qC(m*2+}`xBZ=&$W_HFcOmoQO6e0-Ss!`8k z2#X`70i`GuN7zy#_JyodaWa8zhquHyctqhi&=sOm1%yEn8Fp8Jq91E`ePT$#hD5sr z2x24EW0CTN0uAgYqH&%I%22VRLubGd#gkr1@X5$02HyjLpWC32A1UL1L@^klWnw-6 zV9v)?u?+1LHp8Wyz>_3S&ee<}G6=$Byq``M9_uj=W7(0A$TdudF)EiD`kfwlgxj`#oV?ugnKXru86&7<@?0PP9*UMT zpGAg+g7IKUwDZK82LnUunOzbr^@=vAM6vN|#%RSzirErvtXWFI%z1)MMB$&w&~Bmc z)QZL84>3UAm6^~_YjK8w!!ByZ8pjP|hFC)}0k?2P_Y)2A(Dw~PZHMbXbyie?jsjV~ z*okRsk_S8jafA-B!zbv4k^wpZ<>>wskPe%PaGZrm>hvq;LJj>d2^2{Q$L%bAvmChq z<-E8T9qO{>0`AJ|w56TCmO`Hae3~YwtQ_im1IQWU)p`ezi5*G{!-zmVhVg_mf#Yb) zsf>PkKLGEiO3=j*kuFn%~q!VZ$ggroj5Z17VEr5h2AUhZp5EW4r9Z^(V z*c6ZvSssHs&wX$ib#NK+G2*x|>ZrpwGo<_bopWz@I*B;Wd+-1MKANh!x9+K`Q>RXy zI(6#Y8zF@dv4~6}W5DPkgK>wOA1))c4IVIXP}J$DHOT6S`|X1(M~0Q_t}F0P-wc$DZWPht+Wa$ZPF=}>?KQa-Bj_zfe5F@d1vXDyGj;u+>7G@99M8WhU3W`&*FFi$IC!Bg+TQEpQnn) zL7^rZkNJWK%E5Cv#9SpAin_QYk)ip$eRKAA!9QJ{Thof7k!{o zUl@HjV_r7jxvpGB!JQ0hEzzlc&7QA%%%N?k--?;52ppwt&A^@V}eHz;)mrM^e0 z?@{VJQB_ctCDC9; z>^FM;E}+i>`YhTw%T)cw=+AkS`Ua)Wgkkm+N)c5rK`wz_&0^)*Ok9%rLzam-OrXAI z{{k2ByA+It^2}xU&$A$eiQfa~KpW#9S_odD6kP`X9{4br$M@qn-i#;QPIw81lb$5z z+J0iI*eMvIFezi|Mp`KM|)JN(|^`mCd zVzhLvqvq4fwQ;6hLX*iDgA{~0fzAJy@1#fsO_eCctOQmV#W{gJVeuKhzH+Qk>MZ(l z7Aw^+zdDQ(hmBVLpQ1)s@2Niqp5kc0!~MGiyf3x5I)}V-ocHrrJmL)ZORhA?#%qDW z8Pt9nJo3MBM?KPe@V`@El~958LPIbim=P=pRsPoXI_pq`deplVVHv{J2+I*xAgn}K zg|Hf74Z?QFi#rkSLiiQJ-3U7n?m@U0VJE_U2=^mAfUpbUL4=189!7WsVGqJy$liSj z`w@-?#);F&`%i=y5ne)g8Q~R#R}o%Acpc%l2yY_%12WeP>2E=>B0#H@vk~T?ABq1K zCA)wlereB-2Br(uW&=sTbW8ea=ytgzB?GSo*5LPm|E<8y{s;UY2A=Xi8+a;kC~ziF z5jgDsYv8TGZII4O0uKkW0rgejAf7G@j0^N@CCTqZZSMwNg2cWR>1OToe!-Gcx?;?DI z`VJ$U1O)2U|6!F2`xodFI1h{tqNi^KzDB)g`S(R&Pv8Q9`7fiDJ%Js8?}6`W)Py|? z{WDgW@AW%92Hf|k?IQ5o0bDi*Zb$#FMX7Uon}N7MBOop!P+z|Z97jER?^~>U$bSue zIg8rP0k4awgYY|zau-mJXaqebfYwpt1+@1K>idu|4pORVZA0F>sOco|IpTjWu*3gC zASUpV|8b%oBog%n^D1yZ!b}8D;DG>Xa{} ztAQ7=eHUDC4iE>C_b_O&2l=2ZV#4qo;Z6SnUexd$!fT-1CDd^^un{!7J#Y#zPa)rq zp1p#)d0+l?h_nWUy zqrHnQc;YzY^&I;7K;ZSjcEmeT>q+3ibp?pOP6Gxb0sLPJoHTGipYfzQe?827co(Cy z8F+0*Jr84s>;a}ki%S@b?*s4Q4mE@C&jubv`Vs=DOwaY2^%%48HH^<1Q1m38pGBR- z7pF1Xz)66P3DgF(Ku@%YT`aCgqi@i8Acd&U%+I)5!LT=@*F>GS@M}bE?{ZJxMV~=) zKw$+#3tuo*iC516JHklUteWHX0C4CDUbvlcBCP0l2K>V#7Ork5N!d|8GWCE=U)U?U{v-X+{yXiQ}9fPTM19x zzlCxe0d<;dp_&fj>Y%~*Dd^V<^qorq$65m~;@8p5`3rLIbRZFZSPzQR?=WhiURDI2 z;`XyK5Ap&7(d(WleJ$jv2*5tTynnksK6>rQBfOph=Fg!#QT!$2>ZE~JXTa>h_%M}$ zGmkFvXdKTr<1dx)8OiLF6RGpDYPQ95@N!^3;%toH1<>#iuJ;4q50Rdw5&XG-;KNHi z7ol|m$8(ThG%t7RBM(f^H|NOab)mgRZU^f%%720OE}`XfJn!_rTtZGW-f5}z^C5;_I%)GKwdz41}&k#x_oVFD1>IWZ)w0MTL$2!co3j`UpMO-P$haleCR37!$>HlrA~ z0qLR7o51tv`4^ZI#LN4cpZ<T_;v^MUN;5V&HhGY+ zvK{O&N zD6veRQIx~$j4q)YQLYgsk2FEHzWON+Y7Nzt6I^ARa(@2m->&~BI9jM!#q@2HoB<1+ z!pgr+NY@W8KXN0)L}<2BtVOsEVKc&3gzZL3*Y_aoLcs1;96)#iI4?$64qMopsP6>A z(+H;#UPYaoQ3L(kjuQ8v4T%55*MA~Nt+(h!IchoL z%iv?g3N~exLHMW)x{i4-=AsBvM_IoU%K9UMUnm>wGB$^i(sc|1tlPFW2$=}^LRddR z_)JLaMWNX`BNQR{5X!`N)*r2Y)FC)^7%6ske4k*XZCX6fH131ts12&8GmJh(HXrI0 zwm}FLMp|X06Oc{~%b$ho`3TExi*0L>Znj;=_glm6x7+Tq?ZPukvFdYrz(_Ije1D=v z8id36@^dPG8sSwVeG}<>VfP>58Z*%Lwe54;4`gz;{ljjtN7%8m=TW2I?@I9Id)3xk*+lE*Bj{uq_^2`;{FreKo|R6dTImR z?2z$HPvi$v`$NdzXMfUu(te23XN~klBYnL^{@cd=StAXW`xy71+P}1a8cAZ3)Q0OQJ>P-z1V;)2r5Q$=i?kyG3Q){=o5$ zgVIlo^fM#9i1a(hj}AYcA$6LJ)M2DCMw-y#c^mycR4&tao-c&6Gw2JxaTXb=57%V~ zlnydd@CQBT{3@j24=1HljdYfgf>%QMi=E4zYjIEMbw;|`NVgj4_7>0YG46xqb{Y4W zU(N&0C!8mcK81LufC5&JlOGGbrEN&PDv*nN1R3aF#@HZ8tIovzeNb< z2h&U7i!+fb(iU-<(QJtfTqI^bLW~76Rw8BWMT0Ea%+y~1|HSTAj{m!_aZ={Qjbh(kP z6{LaC?mwuFu;%dpg-)VUs1@yxq?zChg!GLRZ9m6Par_YCRBb6=PviIk$BzqXvQk-d z5tTJrsjU3INpF#tsUF0;0PhTt=5v|qkr)wZYj4P@xZcMt=1`8baLzfdXCIZ4Cn#TC zV6Eu{=bzv@Pv}oI;3iLSohLNHjqy>6>r@)yZw;g%mJIU^xAG0Q@(shlGKZ&;l%pak zN35qD@jSIC)^pA}YEi7Gwh#kQoWG9R6>GTEGhAv7m6D{ZOZjL0nsAU0aLyiX zJ%RHRs0QUz3G|fl)Cfj77-luY{FD3pHP`S@4K!C~hA|V2R!1!=GeIg7*JdU>m6Z>!^}Mi930Z$wYT;4v+h zOT`kr<|244mf#Un?-J;#VoEBVk_p`R1bQmThK!gg`4+eM7Pt5o_wFsO0g?=K*v&MY zOE9XIVANdZoVi@vTpg0RtCp|=3@9mo!}Vh?kL#0s{UoKpK04I~ukA4!8b}qSikn zc=;MbzQ!27rY+_i!tgXb6_?c$bj>iA7``*-T%sIt4Z}P|EsAUO9BNTqLv4u$Y76;# zj26XJ-1UUsBd%Rs!&6p0o!wVJN^>HFNu`wa6w!AQ=LKQLC`b02@; zKEBUm_dU@Qd;;#(@|YTLG2~ke`4-dRGp(3lm=13-{qLey%yy>#T?{`_kGa%Dt{+w{ zlsd!tXE^^1=YPf{a)#&5A%jNw_v@GS9(dWA}f6VoK%=LWC^_=7U2dR{NkmvG)4D%qv zJjj?o$S}X<{5Kh^$&3~6eB~Pq`6lzmn_TCc4F4u$^#*hA8%*t2xrSF6@)hQyR|pbk z?F|^m2=>X+PZh~KD;AcQ=4;^!qD#O@@Hi(k!2d_0jsISvji0F%SOKcQ%LtwfZVX7GOoH!5s!RpOQdsC7cO7LX z*%?quMzW_&G+@b!;zDque{uLZLPoJ;0Ny@u}RLx zx#tbYfvpB!Zpaof2Bx|2#b9hvz7VCo@XP2feX=KDq?|!z*bk1%nBcL|Tb9B%jz*q* z^PncdTJB`uyfp4f2lmY?KyPYrmQ22R6WKR!8vEw0gnj1`^n!fzcCc^W1MHi(mwodN zv2Whf?3?!z`{upNzIh*@Z6#O9)dn^z(Js-JXfK)egljbHfL38b&n*NGzZMDF+mQpq z2bDxVt z1@LpUh!Wso1|DSN9E_h5gpG}{u`@OfF;a}i2oUE)h-y$hlKCf!X&=p86oV0{1Me*q zOVHo7;HVg}9(`~yUqxYLZXlm9^vBBB6SW2*5JwaCis@uxIw_`;i7{4;u}&@cv;aR7 zg1qk?}HKWJ)(AK^AjOyiArUB7=D+Ubc~KM2<|C=^{;L z%1qIQxv4E?rB}FlKDy-$%*SjVA(O0?weUezB8_FD6|-pv?(V@)6Zhh$;Cb~3xQ1w? zfkOK+HjiS~SwNwqcuGh?6N|V~W_BB37n|gDK)Hn z%pLK}7wOCw$;=mV%n_0~LNP})^>z<>LL5PTK7jU!BP`4jiaEl}9MRPC-vXXE!ovMm z+il|5t+@CeE2Smp&-l!Yx^BFbEu zi}k3zY>!rnixeb#G2~Ns*&QQ8C22GY5h8djA~6;u4`X;t;&`6N@W{mRJdft_iQ{=5 z!=n_(^E`&fDvsxQJkN8<^W4Rwr+A)gJkL!$l4hRg79P(?9?v))PsQ_G}6N4$Z3#BXCC@m)q~-A6nf>%U%`?kC1Y{PLT$N3z0=D3Js zAID`J58}9D=FGaqvWnvg98cwV7RU2BUd-`wj@NR0-GX_wHF7h@TRGm&@jV>x;&>0o z2RMF$;}dubVS#*_?b=NVjx8KVa2(5V z636KrXLH=1fE{P$xExA^bGHt>52G@Fj1 z2IspOJ6^82( zDdh^WQmn$hV-4#|>#;*_fJSm1WYh++5qpsvARYcGE{jIt7XfOD3|Uz9%&?S^{&e<# zwYDcPyZ;q#qoD!*Qv7}mE$194M4B@FXpJO2U)QXqT#YwhbY80xeqcX8vCW*^r0*3>TkZ1ABLwg^S zd=r|sN_NC}n&9b8`^I5<3J&NZVfUt&szU!r&W8R~R%T>~GLez7`;5tosa(Iz8kiGQ92(CsXgXt{;mm+$GZz}o zLX1fcIH6K@k=KEv?||;I1KRg)@HJ_lM=^#cpr1U=8upveMc#$>@d>n!FQIdMEB-3; zAb@7rMC|(nvivPm98-EmkfkyQ`Wb{@{x|^WU+={)| zotVRSV@}_Nx7F^!?7tU!{llUSxVJ5KAIai=XdQbY3HOUEu@AHRG3XdapjA8xUF0An z$Z_bO{~^5K@gi{&JKj^6ZO@3F;yKKKXR&fTkG11f_@lpqz2$GQX1tC4_d8;^_?@T} z??IZJ6{E!OAx}QWI`RiGR(vF;;nl_(?AWG?3u3zXlb9_oimSwTVu4sBE@A)sBlz!c z*fGBkX?afGAg`61!F@N%4RVoOEbHZRxk4_(yC`+?YPm!%g_Qalma4DB9PwxDp?_ct zNmuC8g_!wQ$pz9QCu8-QfHzpC%4u?n951UO35Lm`7@ax(cjOBi$(d--CmA$ohraf} z_>{{XPxc2F4jXp3pmIXl?IO{gkdRmpepN6})Mi_iW|1bd24k{W*qmltt#C%i`%!)&Q3{*i7}Z<%S+1#4;nb2U*EDmy?gca756Ca(Y>U-x4pNhu%KJlE}c7d z?2z3qyIp=>Zca*8N*4OsHa)FPYI0IyLX11c9Um9#GDVxBUHBL4is@iU>k#L0d89YX zUFb&eW_jIo?ajk&VknocvfO;{#RJ^Ybx9D$N(D>i$SMAZrS0h8@?+%-kKLY~efzQU z@`dF{7UpIz%szH3d*Q;{j}0GwZ1Hd{~_b8*XQKt*V4`KEep$Yx3BG; zTr^?r(`(mGD4LO-U4;LTQsc2~(P^9&d^TX%;aqT0JSHX?>E>eDj2f=6nnjBdsb#4G zi_u2hN~kR+DNW;r$t0)aj6zmcACE=HW6ajn4xX$$k2OmcS~ZV$uK)bTx90bgy9Q_d zD8D&!M4js9Co?V9CvF$#C7HBxVkKOxb%my6`?ZpXS0482I}9D>sSwOEZSWOf9ty$K zYhA7Yvor)#rzPt!s%2YE_$<-}{L6Y)XhWOB5IqBNOivf&=k%jdn1D1niL7auf=V+h zyfbHt8V=e<4+kl*R7j`+pl^;anN1sohTXIVB{2w$$53slQ+(LgR2D zM4PL%?pL7A;~|*kT0`r8F}!9PSzVSC6NAHpxERbjw8*m#{1;(O>mc2j zc6#sDtJb6}~^7_9k_J8Mn>M3=~Um~~rp%iG4 z`9ix9{h+;WhPdizlr$?@p?8REu4a=m*IJ~ek$jU;!>#B677JsJWZ~o$M^#d*4T9o1 zw9D~9Fp~iG@ySWi(QVU`GLkc*6QdLIJlQsDatD#%j<;k1`vSVKN?a6si*mE#^u*)R z2KQTB+xWQZS~ss#r^c=F^I?<6jLsZAdh*c5OLEMZzSVQoshJ}S#-_-0|E1FM0cDMc z`VARU>c>&M1Ycg%wyHOwi_FkF10%IEDQRjDCJiUY>xEL(c@a;nX|54wv)hb;$!iIQ zr3C{tPiSVdR&9W4X5!1NI3Sxzd>LC@Tv&jGD66x|$^=)&CnOXW6c?8iM&Uj;*E@dp zvzwMYHp|)DIqKG#%XW_)_sFuDw~lg_M$CF_$);y#%QyB+DqlUU$Bf>?x7N(Kb$IE_ z9>dlQnY?!~@hkO%oY}5RT>Zmie}?w)ePeU;z1^%`96j8Lv5^y< z(;t|#_@~AKNQTCO0_2)yU_GWMJ8yB z>4SVQ+W6pOSpPwq2y7T*W1Mjjbm(QzwOA7|X^1X7ZM5J#QEPThpT28`zg(V3-LZG? z4*z}X)QJ1*>+c@{*|+M>?c48cJcJG?=(a+GJOuWsVu6mE#{zc}tZfQraWMEa8)kac zaC@^!o`XzbhA4*|2m%9wrg;#Mr+HEgUYtxJ4Iw@{H6|6!M7mtL4xZ{kth2m@E>8$^ z5?tzu2d7Ve5ZG5&1OI#W?77E(pG@+99_)i0+*uCRyW#)3v;S+11Ies48u5D^&xxh# zspj$-Fu}emtV<*hs7y5a?i7>so~M)C)gZ0$*bKpJHXAY;oX#Q|q`}r0fh9CUHVI}3 z5t7T`1JvjwEy!hsG7u(>U0QVG7B#(bms-|X6%xMcl!1RV&*?;cPOCG``6vWKebix= zs^4A#hI9cPrv8dB#Irg~ojRu16Q&C=ZL}tsg=)|h+9G{HuV;}ue+8JPdRC~R5v|;fulm&?0&}dkvLC)}CSf)YtKy5IYSU|^-fX;{N7Fsv;Q4toCW~qfb zVK!A8uqKkwPUsC#9$YatYkCJ6Tqm`o5D+%(OMnWHN^8gNa=}XL&GbV8G3E=byR+<~z*)bv*wo z_4%Jb(;M_swI+$<_~9Dlb2RCt>9{wGjks5G6M&R5k0CUSMm z$&d*yHwb1YzTAkr<<(ERVw5$hLmUWF;{)$D4#2t@co_f%EbsDoY!}NrT8xhh{25Q8 z;7Ml2eCSqYH$zE-nN)?7BM(Hf{sl(vEjvV7s-dx1^2e)xAa$U%C< zDB$ua%Uhiu3t>yAyD4bW@y z8_`^5?zV@!93>O>qR~>X_d0G<;?1r&L3&5DC_5Hm$9^v;vv4Fx4Wic2uF#FSqA<2- zE_@ew6mndHCIQ9TkldJTkZ3-V*xru5js>@nn9lF+CZ>z+K!n9wb4kM<0k$?n6T?eO z5Xf-KLYGX_^Z_@dBr{e<=sCJ2O}wiStgn*(pv0)^?eN1K?p6WjYtQKo?^$Xs5@bFadeN(a;BQ8;W5BAA!m%TE+N3R}of&bTsdZBrJ98H7KeuOAH?zCu_1i?{^#+F$( zBteifTSZciAX^f0mg}oNe4JddvHGeH`HT^z^#hvtV4r_Rv44iINJDZI~vd~34CE7yrZrp2-7@cl6Z}Nod zZbQ8ThiAB{|7s@@dhFs}$wd*){P7h!DH_*?`boARy`Rgqn&x~Au|T@Z5!f2&4fxQW zFQk2$$#_yj+dnsTAQdD)kJUWnV;^1}daUtwm<>H`W73lnP+b)7`Ao1N82et{`;}rF zoQoZ?VQ7$RpPM!7xf?e>KWoKW9F?D=ojP6V_a&CaSHdVnLU#Fg*7=7Cpa`e>$NMvkj}5e)L|;HT{CQd(Xy>Y zTIVaYbtqKN60PbA^)$7$Li0AS2b?iJuoF19g_h;PS;>O3ST{De?ebGoU~Htd#~fs_ z3```s2@63C?JrafxGa7+>3JlUaO^<5ri0*xUWn;=q%YET5T+n}DQg7uz|2e>LAK9{ z&5O7Mb|tvZsPpha5i!sbA58~4E2 zsrqb~GWPz)+2L~m^A`iIO=MZ_4q69NVAe!mH1q|`fo&bHjmEO5vksYT$dpLdQB;6E z0$3lW9gS=^aZ!;lEyhN=quf{`oGvg(Qj_rpYj=|oMBTG?z<{-*{bPLcvzXb}UOU@A zOP%u74INt7^L?!2qpN4mtoCmv^@H#nZ{RskU$xtky%mN{=x(&;KwA~CWzYr!{LK4@ z93aG!QeiWLRU7VwwJ1dA8Dyj-v#yDj^u(x0X<$q`t<@~x*lM#Tbdb6!&O-_ZeCgOm z3$x9zb+_KS&Of?$Z@F{Ms#SA<^r&^qmaSW)PF>|6J+`)Xtd8^ez;oJj;5YiB)8w*f z7>C?($^w110-QHQ?*u&Jbz@>Ywml#s`l(MqdNqRdwRy-!->ZX9HE3dGk@4~Ix$!w| z({i)SAgO7cn)3E?Kb(VlC!e9rZhvESsApImqS z^2XQX&f2Ty&6CCbyw4mh=r^~A+^w)Jk&~OmT{9!atp_9GYTs|1< z-5|ED=`c&x8?9lWQ&Rskai)(dp|{ONDn%vMi#t$fC$UZ+Sh$2k>}uu)8(JgDK*kAa zHOqNTA{QlD(6Di$Tk8V)B%2p12s;Mv45*MGD+NwQ&|;^Y+^pD~oGd$R6(xpFj&aXp zn=x6Fv%E0uDtXz0~@7|j>?0KmA)o1T| zW~Tgd=!Cq%qq`R;*zN7A%4^52STlHZR%)+;?mbfM&eY)}uAbaw(R9+Axc^Mwkve@f zE7LBtuM5GD-lW4UZ3)A4v@;zC$cQW1>iY`21Jl$Af?6$Bv!wwW3ai;^ZL+jG9JU%^ zvq657`93m2nM}>C_QMY6;gqgltBUX@o!#b`7pl-gWnp4#s=9^Nm0DKbsuGhWBEnRU zRiR~FGD27)u2@wyj=C_M14RGQUd2V-x?(_MSvAxFp9xl*uNTa`L|X_1hM~d zMD_l8CTMX?P;Fa!?Z0FR<_G^}9>?HVuTrb=ZWQlt0+;yEm-p0cm$C*=qhm>4XgDrl zd2XyYcwYm`iY^7mvlN^LFkWdQKra}Gu!NeiKPCe-M1@yd=fBD4zX{gE##QPDWABMw z54RN^w0}UEhU_8<3xP$;Jm#rxd5tFXWhR?1k%x!0tOJ@1m`Gs3L^(EL()4ROlBDTJ zIOtXo&BAHk5A9g-j?l0hEC4P9rm{90k#m3=yW1Qs}Ak(kHz6o zkN4U71|ORxn2WLOxeB$dPkr#gDc>Ehyn2UEj(zl*XC9Tc{v9_y_SlVBet{3S%kv+m z3zDx|WUIdDFr-uKF!kt5Guuz79@2Alm^yVyhY8cETeh`G^IV}G(y8@&mS_X7P)}1^ zE3~}k^)NkCn4TG8JXSl+99->ie8rY1EOj+l>L6Y~Qc|Sj$Z%=cQ6acBX~roc@lLe? zAr%BS9iY0BgL}0YYawrFg7zI+%t3R5_}UoXpXHFUp`+{Pb}7!vGe!aG(GLGtWpUlI z^0K^LdS>*9)tbfxtPrB*g%Ay{*4)i$fc_B=l23?5wA1+|g61H!DP#6Ia2vF69>qi> zLsSmTF!1_Nsq)(X{%M2#)BAN(H)9h`ez&O4{99Z_h>~LtSZhQ3^C;+x%`7|-lo2xV zz^MdVbTTgKwNnF97MhmGv_Afq2Kis`KRww0Mjv=I_8Q$wHK4WI8W&^ddX2hPpAXb7 z^WRLJ|4c2$lI9G<5dY~g^;d);p3q_HOfk)A7OIENgLIfhrk<_%W+Lc%5_HgQT{di@ zKv83(5lL1pvbr0}krVeY>e4-IU9qvct*bB#{pbH#@`(v@lYjLD_>4`M;9o5_P4Mr7 zZDy+fRaxwR$o~-D(V)3cWBDH1w25xVNX-n<{^!ONh8P-CY6$ukMm0g!kMK_`^-muz zGvrQxm#@C!m#_7HYwt0Qv54^;Z*A9N9C+WX!_;dXn$sp!57AhMsl$5^EC<5oxLyy@ zSch4pl{K#i{jEWN`=Y;gG4wd>wK&UF#*vyEQ<9IGnUU%ALf;V30t+(3xXIF-W~6Z( zbqxH+CdN3ATf!LTm7W^)N#jbjxiJG_C9Nihyk1oW{JCAG!$O@7>QBva3&D`Cqr)s! zpIrfl=%B+a1TxL!A%rsMU|NMflI4D^K12Vfinh&WbK0Ej#6^mBxD%IYRsSW81DC*y z>-?$j)aviZU&iZSAh!qSm}wRFyWSZ8b&PK_y9wco_w6&`drmyEOuO?6Ff z^$=}zm^y8aUQcr#X@<`tZQB*vYN}_2c6V!-K#J*E(3sl-f2=Wk!um_{Q^(?K>- zceuY~chF&$Yx-Grh$aS~lHEH(j6AAv0vVJ8ZmbVKbqd!8YfI=94&Dbigu~{RIF--f z_{cQYSUV&HC8<5+T97c-ze%pfsiXfqPT|tALXd12;6H_{r-&y*_HdX|(a%+?KT>^f zJsSAY`=(=oVbh*2Ik=}o`z^P0>AW&vONV{+PwwdKS>)jqXMvNDfd2;8DcVQVR?h5U zzZQ=+de+pFgN{l6aG(71`WqU{uU#H}`EU5J#G2ZJ2Pcl^wG0T4X5L$G z%(+)ujU&y=(X3DDFiY{)JdanHzYx)Y`kDz3{U#c)>@?v~Creiu?PgpU1)|H{8W`Y@7|CIbB9upqhf#V~^6|c!(Fw z`toSCV6!5j;Tr328_G9{jg(JY=BDRtxq#ORs)|DUn9nv)I&Ad>9@5-ida!=J=>Nk1 z`HTMN`pDc>GNoLmc-0M9La%AO7T7^a26nn!w%o)tzFw!X=|-ks_#O`O2rx8vb(nh7 z<}1L^oYP_Iup9ab`QEIpMW*|j*8{%9cVM+bH361}d~sb_R8pc+THACNysOMXn_V`* z%ob%P!=$N(j5>*Aw2K}N{|bw&fq-Yja|d88@#0DYnT*HVx zk=pGy9J_DCkt+W;gYUa4amwMz=`YNyzHid%`|7V&6IO5bKRTjI;hdRQ&B#bjc{a(@ zExm2yDR^Nu@4-Dlm(`;e+?L+zX;qdC8q5L}a zei(dH{fjhHQ+^oy3eAc3X*}Y1Jo4d@zNIWKCmSvp9Xq7Q;siJbW>jm?yxb0x#DcS8 zvUHehY%m8U!mY|`W&IeMGMI}-hCN~1<1zbeUf)sVRHvTvpZ};yLLjf z^R_GM+8Hd7mz|yON_McV2qP1wL6;Q%(qWRFhxIENJ#(%61;b@6>6nvWT+)4Hm!BIr z%p-l=>O%CsTK(8WSFI$Jzg#P$aV2>3JK*yUI1B78)5>BzsVQLR-Z-GQOS}dRwb27} zMJMc1ZB}7xuuJ$Z;#D?lZIlDj!dycZx5VKQk;-P1H6%KS>2+nHAXrFfb}NLIol4sO zRY|grlmttnc4`NHH5j92l~9ZLBS_UvE!*U%)H0kF>4-KfDV$c#NpB)>`#)K<8a9x$ zjva;Q*~44dv7lqOE}cP?_OaR7nK?O`vC(j4%7$kt+sE_RzmyM$Ji3o0yGJH+?DpW? zOIfTQ%t@KUr$yMtmYB|OO)g75ddJNtvvY3PHS)S!$KQ9YZ2Rcqn;&ymJv4E{eQi4& zTy^+`#?k8F?VI<+sQD3(T<_oHzp!%Mpbyr*+0|1qVDh{flgh^CPntNqGG)m`UqKts z@DUXwAKZD(8l~pWpEu9ns3N_-jnlCbfrn|II#A1itu$TCKNjs!=8&<}1C5jqUo;G! zra@>y-owE-{0OEkow(r0uYrv0bciqy&#{HU`za`_a0(M22PeR|^tiO-L^R@p$CB<` zta}`0=H~H1IVcj#mg`LR{!$eWjUWF|-O5MueZKrhR$jMv?=4#&du*$kFl0+j&6Xim z!*0auZ}$$XYJ6qWnl+m!@VbS*6o5w)|CLWMy7J%jDb93ddtyDLF2u5Q%_7S+$?}HYV)Mxq80(^=qHLze6&!{6@QoIy5c$-zM5)_aTmSCfA5KpX$k*H_ zxH|HI&D(>-4bqj@1)Y8i)pTH|p8@wY_=aCvnw$5{DJu#HyDYA4qbix>J%8(Og_Cq&}%$A*pXT1BA7|I!xHn??3R+h=SkZ<5hqD`UL-vW(b?4AQN>49h1Oq317EUxek8Zl&kf zhv)O0(DUom+fDFc`3u#Iru?vW7b$;Jei-};(#4qeDX?)&K)db4W}Wukz+`avu{2;M zHRI$1XUjGlzokHG793eg?zAvSLJcs=m;h}x@p&~DMhSG^0*$zpkuotWONgv?S^2p+ zXx|f?Q{+mDbwWPt!(k2AxY%T=A3wGloVbM(N{fe%UNWyHJN^ozG;nHsY?lmJG7EYQ z%22IF4CiYg##F66)}Ncp9PwZzH(VrxyMQz+x^1TSSYp_hRlq_Yb`Cu3a5v~Psn}~P zoFkyOk{te;%8+X?7Gyldi4w?Rw&2i}lusNtKp0Fz0rLE+E~DL6qDaWbcFPq{yDdyn zH@i;JJS^0iGtmv5!--#2R%7GPQ1`SewP3qM%)7$tL746OMKRVx0WY@dj&bBD z%njHunk^Ha+#qo>-mdA`76nPF9^?bMTcLA zuF*PPgTg%^pK-G zZV~T_&xZNJ*!j7Fhp0iNO5OFK^7U9becC+}eZImcma7T3AM$@LV`r`?8N8{w5n9v@ zVvMp#!7-Ee1QH+a zfgm-UNszJVox|g<43#ImzyvjG{`4&{2#W)Uv0-q;Qcdo z0dfem75AbKXRScM>nrU&)b$kSm#Zx193pbz3L>-3D0DXwR(Y*Gbrm zz1^)-~HF$@NJr)zGVBRsf3$H z%%TpxI~J&a_PcyC=s65}v*$43*28}pc3T>2yw_Uha6$(rkrb4+sdNY$9Ct<$IMC5o zGO2T^K#}~18&Di&W;X%T=;OpRJ}x@aP9I{?-~pQ0L1OHK&Wee2*hr_1P~nmW^ee87 zAKJdCFjnV@cEhI>#1?5$X@dr|-;7tVS<PSKsH2bHxplJpek1>tdlxE z#vJL|#H%q)yozHO%>GS)Wxci)xB5s$4ZbsgFQI1Q8xVZXf_DZ!(tYGV3cfFm`6$~n z4m!*#d9yZMKVR3T8~(#%@z=zFv|bP4iRFOs?1g9*)2*Z|67EiXRs@+6WI!h_F{Oj2 zQ&eOVWnloHbhDd%T#4HXqcb}UU%LK!UqgdWzTU^z$G=c6@9pa?7jrwn(HxDnEnCbw z=1HZm#TXo(0?Fm#mD*Bcvnq=vakvSm(7H^{#@dDTT{ch_S|$K|A*Y%FWepZ%HqwEU zP;pk4D?Tfp>~m=$naA#iz>cf}Sf9x_PC^e`BE+W68XFx07_qP8-Ln=oB(}eQ zftqmR1O7KnqH>HHOv;5%TlP4GZm`UvAWZ`&d*He-e%?>z+o%kaZe#jZi-6wtD{3 zU_SVn`V`DxrDo{k7s|i7EUGO$B^|JP+tG`lVghlbOQ@g;3UdlR%iwaL0KvvO2^>h_ z1Kt4f1Q{Y>%)ssruf{{)1U>0coPZ^Hwc)J_;NWq^=Vh}~DJ^2n-I&8yG#*r1gWJ_k z8b7hyfV`n&@>{LOV;A&D=<0A}j;jo<%LbuB^dNhjPLJhy1&rh$*$FB@k7(?7TKY66 z@+$Xl`ZTA;q$I?nx}WxGX2)2=)FG!mHf73V^-C#Uy6xb>ZQBkU*cPv=O?vFzux{N3 z3P2ToU|e~uPa}i&F|cWb@@ZYu^XqZ`7|Q4MNYAf>&sr!yte!=HY057S)w2Zt#i9JL zdRAx{P6+k!fkmL*yHJxeuw$t!bLOUC_#<$j65Wkq2UpyX9J5OHGFLgqO4|322xqQuyAaF){ZqIhBZvw$RxbkP!&$=t!we5gB zbC9lL)bQkcRzhFZ8a4+n8b4p>mY39qb^gGHb+gyG{q!ePLiAwUu1?qGxYp@$5#m+X z?~BB*${dN(4y!IciDk$_SUZiK89tmK@Zp3=hq79=8uE04P1okctBFlU-LlX|4$leF zuEFokD9OMOkJVwNSXQ>}+&ME7cBG=th26T6+Mk~*JR&1DI~OLUsL+OvwEyP5$1FB^ zKbH|^VS$~V46A0BzVW`{L~h>k>+6|uuRorbd-Qr^7vA`q!N982u%_~UeDZ|xQ*>UQ zGJgCN+L#AfS~sGCZe%-+(FTHUv7)V5+S+ysQAIreUKSU)i{p%&1k%dg}d3(}rkd6K*T!w(gf;5P4oUhpkjj;ZV^Uj zQj&h!B21leVOY5{K?AEAynD)&N9HZr-Jy5y4to}F+EcM*>b5yG`@2EiprH`H?LcB4TgzjWeo3MsXte>w*497gmpomm#c@3*6n~8rrMs%s%U`LZlq-hvPIRf?4Mc|89|?P!4WMs&%u|q$zL0t!qhfq zC^(MKhTDSUAhv|gahRR>d_;4nZQ8kD4ZvG;#S7qg>fm_D-eC0P+ zstNJl_`<>d@oK=VfrTVr7%!I3^p_Aw&O^rv<1m{wt>IiTep3Z zqj%&TTen|Vw0c8_wW|j0ethM@MfMXt`n5?a>eQt}tV24|%X&^3oHk<6kivqVd3gy= zlQXfbUrm*#n*4W|229I$4O)I0lHuhV(NfP}s{V-QFarhbOtZn#bS0nUKJWszI_y^1 z{l)M|hAo&pk|Uf~@JKGZLN!5OVhff0d4J?VSE#~daXQ1i zk}VMupM!JKMoPyJdy{4sBD~{^XM@F{9wpu)jGXR^9QiMNk$>^f%6D3yM{L{Y?EOnd zyOmG!q}pFF_UH|bJ#0}9~196&HIZu-PjHuCTghB7#J2caRkO#G1tg40a2sARNd zlNk)UF%EtQi!Ef$JAu!+(I?#sy+%i5-xi%Qx^u`osi{Acyob0$%yUe8jlQP*A%=o@ zZmzb-*sCuxeBmntb{?N@;?}Zs8lM~oOuNxA%x7n%Xl6^Y#3w!~x^xbap&fY4Vuq7N zGa_IT8!N0~Esi5{v~MB@NKB+74&~A~!ZewyA+(7oW+qCvuC63HY%Iz)h!Q@kc~>}g zEX29vPYt&_yVX38H<$Hj$pWf>v#$}=#LS#)umt98CDt?GRBOYjtB6i0w=@ThAk-~K5ZqMnV@AH&&FpI*mwqOxN)$S z2YpFrh+?k_u%UNX$a+fVMQpooKZLLLe68DkX-y`)czyWXh zDWCUJdVZbyeN%o|J&PczdClkgX-}rZFVS+F^26#`L4H9PaQf~H(a9sGlsRcWMPg=~ zf*TN+^E8e1v>@xCOifUcPRCPx3J_$&0uQz!fydT%K(J4a9Tw+&mz$35Jz)nz@Q2df zG{VT|E!29DzfIMw0o`G5ZX%;kcdhaD=-_j=AZKHUp5#xX_xEbN6F~ifFPb(P{c7&J z&ccgTvY+u?m+#B6{{B6q{CoO!P|1y_Pz%>*hQ;_wzUyID=U?Qzt_4J(kN>Mm|9Aes zRQfNL;+xN|AzhRktwlC|57zloVTq+R2?|zk9_KI7FS6I13(F^d(evxIkDKy&ob~)V z?d7KYuzD70--qZ_hJIbq7oBWzL2Dn|D1Xuy-JAn=^sJo2)pDf&*VXjb2PXP|Ek{oD zUyzwH^B@24f9(JGA3QEtW2?|!f*4m8l>mz?)|zGqS_#JST&p+S>8%QOu>7eP+>g8B zbFz)uDcSoc_F?4`{wnyNu6YjeZ?xJoS9iZ}t48_#PI^6ULLy zI`sU7std|B_rDxfHln%z<-g%k`4jGxmjA!qDXBlC_xFMBNB&p~e}1n@yE-;X=`xYD zp!S%Wy30dre-$`&x8AP)TRtnLrQseLL7x>`U&4Gde!^#^505jZ9j`MNbbh3M@_DMB zzf3iTlV9P(=s3&!Ue0Av?XQaSsbjH=fHx!>%#6h7Mn<|S<;qy-oP+e>a zpRY8*pUPyNhjNe>Hf*n2tx7Kac)K0>%HSI(bR=pDorM|Rp}{XpB;vb#Sy9TJ8)o8C zACSL7htAIMQ7LVsbxhe{_|0r3UjLt49Z8eVOz`OJ%K zFVypw<9iMmLzYSRfrcGK3BJO8wk!&g*^(utHBF-Hpk^9e84AgXlfFk7`aUHniA4mT zTcv{)tnU2&E3_ZGoe7(aQ(41#q^T(W_={b^5ybe!W%DY+3c}`yJtvF;@K-a#wG-y1 z)|J>b4IewuOWAP_SzVS&j-37b_VRTs=~!IQ6+PsPaSGzBJZsildyKZ&Qej9JOX7`M@pM2q5 zRll+U6@5n!ed2fP$EM$L$I+t*a?aRmmtKEY#j(eKg}?UR#nx@+ocWf7l(b~4yMNiN z5rd|8FL63LwQHYiO`6ny_WXGNqa!OSMvSPa80r6WM@Mw04t={94*-FUHQ+v)ThKRY z&Mi}a3(JSEf|0*cRW;?)Tru(&Dy;U!Y6m-Do>-(0Or|{o+MR8h6O$okxCMt_ zN|Gy?lwk+!8PLY?RXxfk2-*XM7b9=Ewo>F~XSBuFsuOS!936%41JNf9;4p+g4irh> zG@wHhETf5KaOOUVm&`19~ zy_;YCk-GV-%+ckt9R2L+ONGW3P2K#duIhc&BGK1GF-`Aly8iKFh`AX3xCq~+pmD^0 zLw^qqZ}`wB?Ram>eQYW~Jxr7$A--9^5@n*jRlkBGhd0(stA$Dt+O^ij1wPg>^+-;UZj*IYk`j(=$lY{z!Cb+EHFea;ik zv}O|=*cx!sOfn7|b&!)@+A*80HJDZCO8D#SU0cD!F%n;ffSt}L2g$?6O&%sXI&@~n z=D6IMu`o!Jvy?mDqkqqiUUnxxFT*59tB%o4e5PA{Fw+0vub=O|cgIb)jJS8p#vQ}2 zzvYn~y`MUA@UbBS<><FBmpC`5 zl}w+YG4#^5p`$bqo2?%4E#AVwXWlVCU@;3p>ThARI7CcKgAZQ&w02n@G?$v28}^xZ zoT_@ge3FU}b?W*H^(}auNXiV(6K_5K%)!$~lzjf+gu6!PR4wmUzh$7xI&kfEJGSna zeoIB)O*7>;t2SM;@sa%|?GH!XF0VB@rflnqvTA?9{F!s+6i)8lV@ioWu07Cs`pex8 zQTim9Cdgj&#mT0ZqS-=%zc5SZZuEd=zQRUS;4`mB@kc@ErEm;`MPUNOmUw!oU;pmq z(u2Qmd<1U<&-1@@;dME30qLutG0AHBn=*Ou^Kn_}OY@Bkl1JM5`4 zL0nq@=mm(T4~?}v=1)&g&r1hMaa_b-2EuI3@D`Pjsz?GT-kME*V}_C_g36*ttMp&* z-*9~89gjY`eU-oG>Pz6EzP)$LK62b`AAE3Irw;R~r%$h*-+o-5K7Gpi z_b>C?_f=F@R_vqsYVc1Qbl0nhf6ONOP#+AgArF{zc4xtckj?}&+QS%U0-z@M&;uF; z4>d7^f7fKTv}2`0xu3-E`Ag&=I$UZ}r|Lx=qGmfvym1gXHsv#y8`b2X#*J zf4sG#?+r6|f`8=NW*lR%(K?-Hh2%5g{C&MrF|K5-KX z4~9v)G7M(IVDy{o5%|li_!~m#75?(mBS=|}i~X2i`6j29?DuCOe%^-tIsuCQo^ zB|3R(j>k;nhl;*g9APq%BP{!CVzOZuY6ZpS`h3=Mys;heGEt7(3+zOD6S%#>!wx55 z2N*~C160VF0N^#Qjk*U-n1H zAJ8|@P;-I@(u7AW(MKJN0h*Q?y@!Sc)kLS7icT58>Nq@@M&1G<0^Xe$k>2EDqLgCj52FL=KfT6szPa1(9hj2@%<758bs z>6&}*>V4wCp1p-bOM4FLQZeMYz9aM9t~Lev<)zb?-?nDLtdjoO?l@1cuFo+Ml~@?q zthHqtdcE)FMJ_1$rLKhAOXfjA$VP^-nmQYaIvDF5Vab{p*t->^e%2gR( zMK*a%o0{rTytr@>ehQU^NvDSox+h# zleIV`PAyqCw{PFEBSIj(ZQFDTfTOzP_3{$-4la>XmgzEfmRL*aFP7u*g({~h06)V5a6?l6~_e$@sScbiJg^0!^ajqN&h@7}3h$BpeW zW$#`LLTzBa=@sx`Dzw>a^|8v;H@-2EPR$-|!+gSDfyN))3GIR-q{9*Az!d0$E4yRk z6~M8^kEdez><4W$`6Eu~9Q}DGw3f<$zyY5Tr_GH1j}8Uh9*xwItP%>)t>U1MmlRql zNZgqIPiJ|#JlWgBPX z3)_H<9Yeq&#s-54_Hvk(4FnuE%R)j3eZ`PKk@$gIzG?)FO6l5-ouUMjb0vlo%dgPp0nJ^zwRt^PQ1;( zex3I)|H9#Y+40}_bHI||&++HIlS7>gbk)XPVPl+nHxzIgP0Kjik0L>N(`SK4voQ`~ zx0QR!yjRPlSFfH@>t-qs=*d$a)5^vo6O7Aq>&fep8TTM7t~)9BBR9gQn16(N4rcS6 zP~}JOkUdnnZnbhFKp!%8fK!?=;O11Zn)`)ky_c0U&%Pj~T(x^$AN|}u@h7!x{&}J2 zaB+N9c4Ml<8CG9jDPkAg9&WK?Lgyd7@a$>D``EKD*eC2i|A_yio+ z7+S4H#|eQ{jNm9#rVQq`0Z^na3EcVi1+wfIakq7)8YEYCwo5XkcC~p%3U%@Vdp>Uq zvlk~=tn=H}W!lYkm6?S}rnZXEiZ)Zy;>^l=vpv15HF~inq1X;SSf#OAmLlh3pe-9! zm3gcj_hMRMl980UXeRVv)nkCcKLgHs_?JM)aa5#zDg>f9g?196X-I>d#2~4~;qT|$ zlVn}tuxGjm`joD=?h%J9C!q)dVUFT?mc`MnUFmj9J&F*PxS~9?d_`hdiFrxA#h$sY zZNAkquh>DX{B%1pvScR?E7Jmn<|B3XgNi%V#0=|f=tdFzBkyIt)VotQ+piw9%OA|h zM{RE?bT68RI+)1I&=WPwJY;A*!s6y*-krR~`xwLr@pg!V371h`B5&YBXbZk^ju=|O ztbi{etTd)1%5I4nl|JMp4u_Ubm;RO0$(Q`o{l`~Mr`t1?Zn9UGKKUJ>lQwSXtI@{f zFTr)(z-nOsHD*-4bcWC+9!KM&Bo6*XeTMa?DrUv=w@^$GkA|}qv%o&=nG=t)z?pz$ zB}%&n9>!=1qYGO;MOJH6A?&^to-el-9E3wcr}r7|L}tj}mrpVm@@f1&f)||P*=r*I zcqdj7l({zkQ`YKD2wJ9;;T&lQTrD?}b6=W5*?JO4NPqG^BfjrOk$x)GpnSzV2DwPQ zn10D(lJqOY)2)_dO`a@l(Ilj@EFmZ&P6<(Q#X1KTw(ya|^z3J0D~k3rx%xY}lIy%z z@e|QT0sKfcw4uqdwW<2}Eb;(8{CncNIgb-jeP1Nrbb9C6f5?uE5Z6M`Fl%*lT^kP9 z=-PbQ&dM|Q30V92N||bRO;7>cvoRLDJgDA;`mypdQDYK(fj{c)%v8O7X&t!Bjd^Mv zfYxrg;Ru){99s_12V#$a)GEN7(#8VLFXQ4w z9Oi|9B3j&*NcVE23L6eMX#yFX4OX=hF{&b{tH=HtDZJH8Wb9&4+Re!stcuA5Y6WZIz^})6bhr%kU$e}gmX4HG#b4$ z1jkO8n|g;>os!z3W=eIAT#$sgDT6SGD)K{eYMwgGO~}~@QFVNgV^O{>D;a$J0-QjM ziHIWZDGap?!l8z9C1Id6Ea=|@3?E_M11M~uNz^XmU|vJTYAIIjca*pt6VU6UEQiInWqP4 zt*GyBms(r(yGOg%=)Lcl7PPeRB-p9t^I@>o8&<#4VULGG4EiwE^^ zI17q|fH4Xye4n)k=uF{^rC7QdxkO7Jh`ymT8Dw%pn^Rr-(1~NA6`R9Mq)%xI!k*-{ zsl@W9la@b7AZ}-Z-Uq&JOHZ{XM@Qh0J32=ssaBF^eT;>85qLgTI?|=X$>>6w)!}K9 zBnXoD$(y73eclcX85g|kqHf+(ysNEkPv!0BqC49qsl6-a{#z^ew6*OjmhKuD%ri$u zn)AN4EdrXw5#PK)ecX4ecqE!&hLk{)RMNY%!0c%lU_ z5rSA@7)&g7t=OpLMic@D{(Z1mefrkDG*V#c7mmWGvXnGxED^EdPUx`ia0wb;|~LmDf$E%P4Z z>E1_me3kd1O7BB_mCpMpB91TR!w>=VFzeh^acRBcw!~0{fHM~TfZ%X85@Iw-}{a(fMX0)dq;AyxF&Afta9tYulsy;n6 zwMZ|$p9rO)#02k)-k&6JgcG?#%E`U|T;Ao&dFSrWy^IzD-owpCP*yuj7I~&a{?g7y zXr+r=xj$tQbZ5{A7P@4kgMX^YciikdNE3$5P8;qKItys{lih%3Tsce7xM5{doRGvv zXe_u_ZiXG|C|2Ptg;kb-!_-QTm;$B0VEGp*quBH+F~~4ul{mFRl$*V8wen%_-lno9 z@6UKet9OK-L0AfNc>n15)1MsNgkNMP+U8c==~6-Rt;^_T1vnFpp~ar+mfoDOO2;L+ z^tqPj$}V05i{rI`O^v}oBw&Z7;}cfC=eg2HFP1$gXo%~eq?bUCYS0urcf0|odXEcw zJUPi#f+)v{diW#RmI_Zdg{M1Hun~jV(SR$=r&$FdI#_Nr8lmwXFB@=t8{f94#&B?| z3Vj|ZXG$OOcYs05bEOl1Ppqq&cO&YCY@&v-Znc3f@N1hia2R?qPwQce8BLj#ZmiSq z$xxVtJ>iYGddXm1g9(VP$TJ!+qC{e=65A#t-*OMkonTW1iU0tcmkI+K7836gS6_syrZM@6As+WA(J>ZvYaqt`gWQIKWLtG! zLuti7=GGS^=Tzs_m;T%f(^t7Uo?dTKR!Saxwau)QSEJAANghKHM@X{DC9wGzD6xwt ziIoq!x_Vg=$6i0oW3MinaPcAUyYD%u-SPEuC3p-2OBdysFVi}%MTlGvV_;xAf&G-w zl~5;^lm7n?udnz1%KOWDj`K|3O1e~+KMP=)c_t6&7RqJV7PesLWr?#qBRw%8CK#r} zkaJwDM7)Z>yS)3adt~>ceG^Z$pgc52Y>{sVjiQ*g&IM(mbA%Nt$CfKIYGg{308oJRy4IMfhLeIf! z*B$$FsW-Fq16o|~lMj%iWgAj~=h3~1`JL`;UI>1&jNPyHz(Uk+0Ismz62xJ6ga^_E z1>(XFVib`SQ#`3@hCpq@=nlrjA7ezntiVtKmRh%;EW+Uo;(kEl)DT+#HMt?BYz*w2 z${^5){nxboe6u+k`=a?}`K6A<<~(z5Mq2d3Xq;Okl@6d8nidMFWTfD@QQ=x6Ajy|+ zePGxBVb=_&CA=`zKVWVNh_Nsl%}SB&$6W+&r+#*YkvOL_IB~Rlp_24 zmvYOJo+3xjk|jNkqMjxEu8A7HdeyRJ?d{8!t@57qe`ohaV{Hw(8g+hjU4nZd*05() z+LovsG|mET34;+m&V#;$x=|sKa!`=b9TA9qHGV;%WlhWKwpM4ev#G8oe@T92v13lkNBX|k??uT``gSnaR=wyFK2o`<3X7(&%{m8ljbC%XA{x$FaT!Cj#T7|z!`!S zkqf`e52Oo}c7>PzGe3~#-3w~+v6-~YbK0;<0WD!tmp6gUVU5JufSB;aP6i}dz!^6G zB8bZYLlFlP<8emm{fW0nYEY70|&eMtt8?a%uBbXwQ|fKoGbX(5ydz^;fdFNjq-o%0F{ z87r(TtSBi$*%sk0m=tSDN@@rxGCPXmpb>z5xNv4M>ol`nBqb0k0;JBYZZZmkRl1mH zoyC93lvljjym0BiKka?P`!-MF2{;}X(za!JUBHs_M{d0F$o)%7Gbf(AZs9T0i{czuxA7 zL+jQJ4z62A(s+`!?8W?O#jZ^`+wKf^q$S|=jIPj{Btb92cL>Q0>4@aUa3k0aA^BJJ z??qJ!8_vn7ZS+$0GCGbkYcl!;E-5385`xl`{lL(If`ambGTVY|tFRSmv8omg#7?W2 zp?s`@F%HL!Nf2oSBRRe2%!=|P+6)3Sxwm&i>6x`>jux~z3yTLgI@ji9uUVR!ob+O~ zp~BGS4R;iT)-+m{FR5$gi+i4HT)d|9z(7Mz-lmoI7JE|m>Vk%@jJoV>r^AvMAA9Gr z?T6R$+Z!?r3v9Uy!LgJ~eAaY3`tQ7W5nQ+8^%&_ zx}Pr#hWbTtSTI&PfP#J?U8_b@lh~vJ4-|!?R{X=#Jx80Gj$#3b-H|3KC$W9i>h{E| zP4W|0#lXI8@{qhrUd$Yr%IxGj%R+c7R?V-|QuROe_y1XniJvAlSoE#~%rBcNH z8)RM6%3#_ZRz6km8E3@BJyuZtK{dvT9lG@NG((K^`HJ)3z3@VCh3Uatdxq`ykweE% zwyj;aw)Nrz=f59Z5p?FnH}~6z4xP9ik=~G!Lt~~>hl+1F8g%N=V|CTt^{Y27EsHI% zWoO5RhMQfLHBBLrb=4ahR&QEXw%Cc-u<(Vh%G##j!Mp^pb0t-|`NDT$ETXplNBNZW zJZf+u%Q+9Ou{_pqoeSh!XD}8-e_I~Yr zXUF;B;R~Ie7lz;2J-BvrXinnMm76xT-IACSx_RBeZd2#Efr0xvI_?`7IMunBIp!Q&b`})v{OCbTg|9#loaLn5 zT}bqC89_{uDz~@B@B`oXXyo|*H-^u5tT{h2a-pN+!pQCa&{Vsqu*2Sd%L)6Ug2gAI z*IyVIIKOV)`GJ88>oZoL+_2$}RjW>}-*9sEeSEdkp6v`A*s|re)U3>u)6~lA_(u5? z$UBUXxGZ)qijD}zk`Wr8n6R;8JOb%}U^mF0+LaU6;Lz4wAcRU$Sg+IS&H@_|QKTOf zAzS57FY+57&Ej=grH?;en)RULLFx9442YLjd0*j+AVOT={T+ugBEIkD7O4_-5rma^ zv8V^AMM55n<_G-~*Z~CvUUL6|Rw{QFQ zeJgr;R;+NlS8&U|&W?TiIy(2++m|n2xpMh(7;EG-u4y*W<~gzQ$Am(94eI8wZ%Z}~ z1DCt>q&h?q-cxCef}1AAb4l(6no|lUa~TmtF0#l8mtQD+yZ6Mz75c^JR6L~5q>oTE zO`IJEmPD_LKtT#*OcKxtGNZX*`VHdnYNl{>ZrLbqmj4O2d0#ID6Q2u{arTMpr0?3# z&-iu}+oCcQ$^p~ZPs*&g!0!dYEMchrTN^Y?n zz%HBgnfLW6Bz=f_pKy48!&4o+$qwxs$X#)rb3arcP01KxjA zp~<5-e^HM? z5@`sMvFQcPAWuua*(wKBRe3z>cP9;#_e?&F@3znWE@dupsPRu};nIhs<%zJsfzP79 z%z81$S4lYz+DVVlSMLxXMfs0UMom(=Wq#C4J)i3KN0SL!+%2@BfImL*884Z<2Xe%( ztdl^JUZi?ZC^hgeKK{R?|Kej+nF2U2CMH#!5O)-D{|TJtiIFRmI`Luj z#+|Bifgm-6L8pZl)PF*63FC(J6hA|46eDhdNR^HemO8K)Xg9!wzKrMPa||Ao9a$kI ztx|XA&0W${F8tBBAw9*eh&;cdz6Ryu?xgfYTcm%8zeN23>nN~vkkyl*o^SDa2Wzl?9oKU!LDPvfRi));qK*sMjs4exq_-z z+7l3qH#AHVflA=qnMB%zAuJ?0GMNsp2#Tuf5KYWdm)X-+_0=O~13y{AdJ1hXv z;DJm$478;Pt#?6B_1YQVu7TJXrUL~rZdX%waq`0Kr0k3eS2KbOvKOwD?k!oj09Qj4 z<_JIDt(Nn7R2B7+uiTz3Xp8qq&)YGqqHRq^@nN7A$Ld5|D2ss&jmU@q*{2szhoqr? z39>;~0GlC5B#b=vP-@x=!C^7F)|BijQ<-!s*N>z8h2m1EWT;sb!pI0Ocl5TUIVv*; zlIJC7=0%r;rRA2SW!M)P^Gqc%agibJ=1750g3xRUohlEF)Tp-^%Z=C=ah`#`r&o`d z7OJYayo^ry=HOg!)5`b6d0rVOc+;)G6PQyrNw>XqbVFGAXUqs)HT?bhO4nxce{-hj z)h|BwzHZdPQY$IC0apAWoxIljI_oofzW8`@+=!l~z3~AYlhRl}2V;&}!9-^2YE zyE-``{}}oIljat|_jkWxD|HtT_u#Z?ARo7+d+J z?w;v)hHQjSBhQ;q{@Ly{aLY&hKf~Sm*PV18bVZsO?iBL=DeRY1o1p!8vZGBlvnOF6 zn`~jL&yON3@t0VMybyoc$ zWV5`B4+4G-jF;uJjhMCE!ZpJuSFbtk8GUP1cbbjr`&3_~KZ`s*gdQll3f0Lu_(mxk zl;V*djz1*uq57hBv{9$W_o*H9qHkBhe^_&Po(fYO)iJrtnxi->E4lB%;hpXf^T?mU z{{&^IWNxDe@ub^!5q^W_&wAF^sxX+x-TGjJ--Zi?JH@uipTmdKEoXHS@>kn1;#R4S zG2)je>)>19Fd0sK0>4OeM)+xnI|(1-{3Lb`$a#;$VU05J8Dn33jBqzOJ^|V!0!N$X zM&M^@t_yw{+)Hq7!2Jvk^uvUdi+@}A-|^%8EWgBm$bTW_N=~UwdO}W?kH~k*59(TU z>va#~Qh;QAmVUebp#FCK1NtZRFY4dWe_~i>IBxjd7;KC;W*N5_Uod`P{5&8lV0*y9 zfQtbi1f~QI20jpYDe#@ZPfZ3>q^Zqx!t{*kHPgGMKLphUbp%}s`eD#7f<6fPJUBQw zDfo2o#gO8VTSDFoZ4G@m^ar7@hQ1NDHf%6#e|TT`&hUrB{}>Sfl{Rz4+K9o3{SmiC z+#m5o#0!yWk;ReSk*6asMm`hyTI9R9)Mr^#chqpyp{SEluSLBZ^~dOd=w;E}(ZkV) zqEAL&h`tj2eDp7)Ka9zZDUG=$=I$6J=INL}#8$?(#`eVSh&>W#h>MI%ipz>~#JS>@ z#k~{vN!;i0@6BtOci+5=^R6T;OXx~?I^pHSro_h*pGo{_;x7~5P5eXR)%gMQZ<+t6 z1$!18S#aBeyBBp=;sEg|97q%Pg6r&1vQWbHDjj zOR^=`QfjHQJYjh@IX*chIX}5PxgohdxjXrdkTI5j>sB{e_wQtA&< zUrbZdo=vxtF#GCs-pJkyXFnVFQCl{uDqGV=$SpJy4eBC~M#FRLf( ziL7U{Ue5Y;)_d8>*}2)J*>%|$ZLzjwTdu9tR%dIot+#z(`!t8;yqfby&IdW4=Jw_O zIxj!(VBQ<~^YeG+f3nE2=z)Udf{udcv2RdUxUKN+LZ$H8!q*m?7CRR|Y>&5R*q7N4 z+CO*9ceosz9LF7>7OgJYQ*^rM$)eYb{!koV9ABJL+)&(JJXm~F@$JR07JpunQj%ZN zRC2b|PoWOKeMi z>Ad26qsmm}sOqUYRHan?))nlkacy$lS-q+HYE6F4TeaP_@71N%ZK^w1_i){Z^{)Ds z`qlN_^;_z9)bFjoss6V5)AbM3EA_u`NNdP%C~fF!*wb*R;qHbP8a`$R`}_N}&R;Q-`vKnn-5NOr3h4np{x77j-EabUy2 z0WoG~Ki0w=_j|psg(VgWEq4`0W-e~k!aA0Vb0BKGo+aQu05xo2HvWhf4q#^fD=i$z zQuv>>a1cw8U<^QfHyGh6Ega60C10Kq%p~2|>K@!U>>e;{{)MGOgQEkU!5!w>-8(#k zeV)FS%^NrED9Fvv&o66jHJi(P0KWG*0>BqLmCP)nS9^v>HV+M&MS361%#_z4;NG!m zvuD&?;~ClF9`4&bII>+uQS)+phX$^VfoT;t8^rOvVfX_e^vvgrU}uK#eiZLK@OJ=$ zu7=+sp8D|HhnN;U+=js(=aMm7{3}Dgt$;KmG&{%H`Q`ZKHyidZ ze|}40(36_c; zJi#VeABX81_V3)ljVyzCu+i`WdyxmSm$(Tk+nLZ%`x6faN0ZG%nT`FF{Xh0H`<%Uk zgCpTA2mLOBZ6^~}`pxPJVZp~Qe8N_27tbn}=tIHeN&v5-x0{Z-;OksuWW44&t zVI6Nir0@=&#NK6JaO?+hGq*rdz{-l*>##4piT#47@Km10(^&~CFXt7!5(n9xyo$ScHLu~dsJhL( z4&%}`wuRS2*K-inauyA|j(Is_L%fmI^Cr$&1KZAB%!i(kjrqWiS5>)F@Y-?2S> z9beDxW#3@qSRZ_X@pu*Az`MDdt>!)K-`G{&%do=ZecZ$Q`9_S{d)a=Bo%{GEz8T!j zRtS{3_%=Sk2O*KXj;&?C=iAv8h)35!?(jYz;XC*!-^q9J-MG(W5C1y&p>OcLd>`M> z5AcKh27ZVihRieydoDO%fU!Fs%9jZ&k=640X5=?QBzY32 zH2(vZ0-r?3`6lL#h3pR8FZnG7UX|U?s`yQ~G42#L_@C!D^D%yu-@=c9gZm~w!Efca zv8S*z9*FyWkFcX`jNQU+z~1)<>>zuVe~aJF@8Bm%)q#J9pW=7%)BFs-o8JTO?_Pc% zKgaLq--Rac8>||0-P`P!>|bGD_D%LH_G|VQ`>mm>XV|mTW2_o*_YMyY8mopj4h?#? znyUMTcDQ?cF|HeGdfj*fZ+OVP!%!=pOtn+*jkQ|RS~aP0bZ~S2qJjcb{S=6%QuYUNxPEhzPCneMN&6`HFC`R&&~eimrGtRT0iwf*qT;^?5|t(5!-rYAST7 zacwG?N<=Lz$m9oW*I-wxN!F-fDoI2*+>b6IB6Ny48tgRM2DjSo+!~$TD&nTzX&Utj z1PmVGnLJZ3j2|*51}^+^nV8?4P2r$goY&->}WE zMHE&W2G!U>zu3hN*@LDxG^33Ssks`4Rrp~)czcmyM8s;%t3u-+R@>m0MEeo9oLU|b{O5m!$Z49w-Y%F7L^p?vv?jRE>w#u z4ir&+L%RmGm=ZN+o2TCg<4|LIhIVXHL!;aK1ay93ezB;j!u+B@QEc#&J%NI76w=X&>#zn)c6AJtA%#;xlnswsJ$;N#)^SM@)|Y= z%fna&{@Iyb!u@!%S*n4YAzXSDS{G)iWAgXjs|vH36}GCQw$p5Gev*Z*XjTj>yE>G` ziAqLicfWaTRfi&_x}SntBkS$8_9R*?3hPu@wYBCFzFXbx%28k~#oXPWqewQZ#cIh> zWShC~vK$@5T-8ccwYj_7H7-S0yT()HYDKDU-)&Zctq4@R`xIU4?n_WtLNYrYvltLst}kHR~v!dg2#otI)cLFOnrn<8f`G1VRPJtelfS_4Ql_nDPvS`}Sd z*QE>|QeE3ys~BoKEQ*}k*>+tAfD^|$%t~u3qMV(HW~G=y#hsnzaTOXEWgtp>GAsG? zKA#|;Y3(qB8e?v=VruQ^MvR%>nJDC-kfS@X8yAu%f?7&&b+5wOIuzDSz!p4X!zYPC zNzLvj!&xsuJgH|rot=H|PK9T8c4{T)H20xAR##_^qPLlA&5AD7jWQXlTRRk^)ujYj zU8n&h>CRCMg502lxo_Oq<1*7rDsG~RKK%l&wpY<-S@5#jJZ2ulx5o4Jsi4q`j_%e( zcUxzNwbRmRR-7w4@Gg<)qM?$b7;Q>mb@nB&)q?5)c(l5#Xf9TlTakMD72b;vD8{TD zCD3LjY(r5j9qR!I@O5@~5=eKAz}95D6d1~?Yh78EsfH6|o7s?p)m(TsP^d=HyUn#@ zRyS3uAT~>+3Q^37K+=aLs@CeRQ9lv#CCQZ(q(b7UqWLEZrS@-iUB-DJxfWHFXti`^ zp%I7K#wDp%>2ufQDB(5~!E9E-s+*`}2%v_PaC&OPQ@E(F2xJs4h++l}dV#nSQQd7G z>ozM9AX|Qx=%k&&oE zw@Zo0rY?u3%KVA`eH_6{Y|>N3|Vp=f>~_;5FxVl-m9 zfy4LW<4I@|#au2IQ6d_M;N5t1K(=x;J24sUJQjtG$<9&YY~!5#co3fadA4zx`~=&$ zj{HR1xSss^ws8ab3vA;?@{?@i0pu^VjR%sSZ39`r#n|e8{zBJpI5|;gC1*Wn-u9lSw>R&z!%4Y^%$tWNBR+NwY6qJwrRFseW zG?b70bd-<$43v-jOq7rOER>J@T${OE^r$==NF3j7#^}epsd2dBE4kEo^KD9AwvvaA zxd?rt9!+=7o@sR#TWN&Hs}ZfH~82qu02LbCe=mZd`ee zQvBCI(ZYKHUVAZ_E=SiQ9#V^IfYKn%Z_nuBpeXY8>iPM=~WM36KF@EF{73-9^>WWt{3m1c?rjUYjc+U>!npod50Dh~AW zrZ0(aMs_?pN+|+Ni%ltkx0I+@3v!$5FdO@*wai8hT3HG*FSlJ{tPX)z1ULm&*e-Dq z(}sYESxGSUc-2lY6j((t6j)6#6j)=Ulke3Cbs&UE9-&U#WeDyl)P;~5cOAiTid#!? zBD9X+L})$1iO>f6W-Y+F>6;XA(>E#5L*Jx8FTpe*&_^&7@DL0I`U!>t8wFl92yGI0 zQE0Qki$YrjUKH9Y@S@N*fft1a1YQ&x6nIf+2sAFAs=)2yNpa%E*VJGYg2O~~0#jUg z8o^wPAi?ZVg9I}wVCV(G>_p;e8l)80t3d+agM2E~EWfS>3E=P4AOU;> zu$5E!?G;ZVzkO`0zZJ1m1=$m)gS@fpau!x5MY-~<#$*-iTsYJK?1)~4HEcG zNV!DK?`Abf0Ap&90FK%&1ql{cF(h7sb&^`Fo-mhny0Vo3k0PhE?)FXSIW)&&G5A6o zSWA8!)1A8f+dj^Wu1l~so<)Jn!2#T)hN2B36yUldJJAk<5F6;a!qo6n5V^CO@f3d3 z3aklk>Y(Son(+*Jelmcm5v-;&aXg)(uHftg*Ew(MZ5NPuL4TJ+4LmgKNj`a4(H+C& STyt692kk5@XVJ3h=YIk9?O7oJ literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff b/libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff new file mode 100644 index 0000000000000000000000000000000000000000..1e3536f16a5cd1f0ac955540235908ebd54b02c4 GIT binary patch literal 25860 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 z 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 I0RR910Ng-@*#H0l literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Light.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..352dff656e1ce3467ff28a55f1b9fb70c65091c7 GIT binary patch literal 23996 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

    Xj*n((*G|AVYgaqD+Lju0N+=zAi4ub%m_ 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.eot b/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.eot new file mode 100644 index 0000000000000000000000000000000000000000..113c9aef2c28811e40f7a0b17018902bf79aad54 GIT binary patch literal 27255 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

    ~5fgt@Ue5rwLw-VVL5i}=v*|Ro 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.ttf b/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..28f17c2532638dbe18cd4b10b1ed941f25b5d615 GIT binary patch literal 63236 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 CgCFAn literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Medium.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e2275a8fd3889089dc6c366b0fb963c437d5cf46 GIT binary patch literal 23968 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 L?()iVY+?rh3F;Wt literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Regular.eot b/libs/design-system/assets/fonts/monument/MonumentExtended-Regular.eot new file mode 100644 index 0000000000000000000000000000000000000000..bd9c3e0b03d77b97e5f1ea86651bf83c2c7d3eba GIT binary patch literal 27265 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 UVFd1Qq)GiT16 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 h=;s%yG? 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Regular.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..43dedd7ebd6de420017aa0b0e9724b0bb78e8ed4 GIT binary patch literal 24128 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 PKz_*W$Sc#q!94%~1UJA^ literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.eot b/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.eot new file mode 100644 index 0000000000000000000000000000000000000000..13d34807dcff80f7b73d091c55a8f21d6bbc3940 GIT binary patch literal 25895 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff b/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff new file mode 100644 index 0000000000000000000000000000000000000000..edf9c58b7d64aff302e720844c24a37f81b04d4a GIT binary patch literal 24788 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 Y?s1P2p74ay&%meNB>(^b0RR910MH6YB>(^b literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff2 b/libs/design-system/assets/fonts/monument/MonumentExtended-Thin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f4dfa5953d71800f76263e3a3a977bf204b256e3 GIT binary patch literal 22896 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 literal 0 HcmV?d00001 diff --git a/libs/design-system/assets/logo.svg b/libs/design-system/assets/logo.svg new file mode 100644 index 00000000000..af009095608 --- /dev/null +++ b/libs/design-system/assets/logo.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/design-system/assets/styles.css b/libs/design-system/assets/styles.css new file mode 100644 index 00000000000..f48e65c15c6 --- /dev/null +++ b/libs/design-system/assets/styles.css @@ -0,0 +1,57 @@ +@font-face { + font-family: 'Monument Extended'; + font-weight: 100; + src: url('fonts/monument/MonumentExtended-Thin.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 200; + src: url('fonts/monument/MonumentExtended-Light.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 300; + src: url('fonts/monument/MonumentExtended-Book.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 400; + src: url('fonts/monument/MonumentExtended-Regular.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 500; + src: url('fonts/monument/MonumentExtended-Medium.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 700; + src: url('fonts/monument/MonumentExtended-Bold.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 800; + src: url('fonts/monument/MonumentExtended-Black.woff2') format('woff2'); +} + +@font-face { + font-family: 'Monument Extended'; + font-weight: 900; + src: url('fonts/monument/MonumentExtended-Heavy.woff2') format('woff2'); +} + +@font-face { + font-family: 'Inter'; + src: url('fonts/inter/Inter-Variable.woff2') format('woff2-variations'); + font-weight: 100 900; +} + +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/libs/design-system/docs/Getting Started/About.stories.mdx b/libs/design-system/docs/Getting Started/About.stories.mdx new file mode 100644 index 00000000000..a005aa005af --- /dev/null +++ b/libs/design-system/docs/Getting Started/About.stories.mdx @@ -0,0 +1,7 @@ +import { Meta } from '@storybook/addon-docs' + + + +# Maybe Design System + +Components and patterns used at [Maybe](https://maybe.co) diff --git a/libs/design-system/docs/Getting Started/Colors.stories.mdx b/libs/design-system/docs/Getting Started/Colors.stories.mdx new file mode 100644 index 00000000000..376ab41bec2 --- /dev/null +++ b/libs/design-system/docs/Getting Started/Colors.stories.mdx @@ -0,0 +1,110 @@ +import { Meta } from '@storybook/addon-docs' +import Swatch from '../util/Swatch' +import SwatchGroup from '../util/SwatchGroup' + + + +# Colors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libs/design-system/docs/Getting Started/Typography.stories.mdx b/libs/design-system/docs/Getting Started/Typography.stories.mdx new file mode 100644 index 00000000000..d57907ce4f1 --- /dev/null +++ b/libs/design-system/docs/Getting Started/Typography.stories.mdx @@ -0,0 +1,19 @@ +import { Canvas, Meta } from '@storybook/addon-docs' +import Swatch from '../util/Swatch' +const headingClasses = 'text-white font-display font-extrabold mb-5' +const paragraphClasses = 'text-white font-sans font-regular mb-3' + + + +# Typography + + +

    + diff --git a/libs/design-system/docs/util/Swatch.tsx b/libs/design-system/docs/util/Swatch.tsx new file mode 100644 index 00000000000..f2bf01d7380 --- /dev/null +++ b/libs/design-system/docs/util/Swatch.tsx @@ -0,0 +1,18 @@ +import classNames from 'classnames' +import React from 'react' + +export interface SwatchProps { + color: string + className: string +} + +function Swatch({ color, className }: SwatchProps): JSX.Element { + return ( +
    +
    + {color} +
    + ) +} + +export default Swatch diff --git a/libs/design-system/docs/util/SwatchGroup.tsx b/libs/design-system/docs/util/SwatchGroup.tsx new file mode 100644 index 00000000000..a03524a2379 --- /dev/null +++ b/libs/design-system/docs/util/SwatchGroup.tsx @@ -0,0 +1,21 @@ +import type { ReactNode } from 'react' + +export interface SwatchGroupProps { + heading: string + description?: string + children: ReactNode +} + +function SwatchGroup({ heading, description, children }: SwatchGroupProps): JSX.Element { + return ( +
    +
    +

    {heading}

    + {description &&

    {description}

    } +
    +
    {children}
    +
    + ) +} + +export default SwatchGroup diff --git a/libs/design-system/jest.config.ts b/libs/design-system/jest.config.ts new file mode 100644 index 00000000000..dee53cbb47a --- /dev/null +++ b/libs/design-system/jest.config.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ +export default { + displayName: 'design-system', + preset: '../../jest.preset.js', + transform: { + '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/react/babel'] }], + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/design-system', + setupFilesAfterEnv: ['./jest.setup.js'], +} diff --git a/libs/design-system/jest.setup.js b/libs/design-system/jest.setup.js new file mode 100644 index 00000000000..c44951a680d --- /dev/null +++ b/libs/design-system/jest.setup.js @@ -0,0 +1 @@ +import '@testing-library/jest-dom' diff --git a/libs/design-system/package.json b/libs/design-system/package.json new file mode 100644 index 00000000000..0c5756882c2 --- /dev/null +++ b/libs/design-system/package.json @@ -0,0 +1,104 @@ +{ + "name": "@maybe-finance/design-system", + "version": "0.0.1", + "nx": { + "targets": { + "build": { + "executor": "@nrwl/web:rollup", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "outputPath": "dist/libs/design-system", + "tsConfig": "libs/design-system/tsconfig.lib.json", + "project": "libs/design-system/package.json", + "entryFile": "libs/design-system/src/index.ts", + "external": [ + "react/jsx-runtime" + ], + "rollupConfig": "@nrwl/react/plugins/bundle-rollup", + "assets": [ + { + "glob": "libs/design-system/README.md", + "input": ".", + "output": "." + } + ] + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": [ + "{options.outputFile}" + ], + "options": { + "lintFilePatterns": [ + "libs/design-system/**/*.{ts,tsx,js,jsx}" + ] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": [ + "{workspaceRoot}/coverage/libs/design-system" + ], + "options": { + "jestConfig": "libs/design-system/jest.config.ts", + "passWithNoTests": true + } + }, + "storybook": { + "executor": "@nrwl/storybook:storybook", + "options": { + "uiFramework": "@storybook/react", + "port": 4400, + "staticDir": [ + "libs/design-system/.storybook/public" + ], + "config": { + "configFolder": "libs/design-system/.storybook" + } + }, + "configurations": { + "ci": { + "quiet": true + } + } + }, + "build-storybook": { + "executor": "@nrwl/storybook:build", + "outputs": [ + "{options.outputPath}" + ], + "options": { + "uiFramework": "@storybook/react", + "outputPath": "dist/storybook/design-system", + "staticDir": [ + "libs/design-system/.storybook/public" + ], + "config": { + "configFolder": "libs/design-system/.storybook" + } + }, + "configurations": { + "ci": { + "quiet": true + } + } + }, + "deploy": { + "executor": "@nrwl/workspace:run-commands", + "options": { + "command": "node tools/scripts/triggerDesignSystemDeploy.js" + } + } + } + } +} diff --git a/libs/design-system/postcss.config.js b/libs/design-system/postcss.config.js new file mode 100644 index 00000000000..51c7f7f890f --- /dev/null +++ b/libs/design-system/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + tailwindcss: { + config: './libs/design-system/tailwind.config.js', + }, + autoprefixer: {}, + }, +} diff --git a/libs/design-system/src/index.ts b/libs/design-system/src/index.ts new file mode 100644 index 00000000000..0e02a51c17c --- /dev/null +++ b/libs/design-system/src/index.ts @@ -0,0 +1,75 @@ +export * from './lib/AccordionRow' +export type { AccordionRowProps } from './lib/AccordionRow' + +export * from './lib/Alert' +export type { AlertProps, AlertVariant } from './lib/Alert' + +export * from './lib/Badge' +export type { BadgeVariant, BadgeProps } from './lib/Badge' + +export * from './lib/Breadcrumb' +export type { BreadcrumbProps, BreadcrumbGroupProps } from './lib/Breadcrumb' + +export * from './lib/Button' +export type { ButtonVariant, ButtonProps } from './lib/Button' + +export * from './lib/Checkbox' +export type { CheckboxProps } from './lib/Checkbox' + +export * from './lib/DatePicker' +export type { DatePickerProps } from './lib/DatePicker' + +export * from './lib/FormGroup' +export type { FormGroupProps } from './lib/FormGroup' + +export * from './lib/inputs' +export type { + InputProps, + InputColorHintColor, + InputColorHintProps, + InputCurrencyProps, + InputPasswordProps, +} from './lib/inputs' + +export * from './lib/IndexTabs' + +export * from './lib/Listbox' + +export * from './lib/LoadingPlaceholder' + +export * from './lib/LoadingSpinner' + +export * from './lib/Menu' + +export * from './lib/Popover' + +export * from './lib/Tab' + +export * from './lib/Takeover' + +export * from './lib/Toast' +export type { ToastProps, ToastVariant } from './lib/Toast' + +export * from './lib/Toggle' +export type { ToggleProps } from './lib/Toggle' + +export * from './lib/Tooltip' +export type { TooltipProps } from './lib/Tooltip' + +export * from './lib/TrendLine' +export type { TrendLineProps } from './lib/TrendLine' + +export * from './lib/Dialog' +export type { DialogProps } from './lib/Dialog' + +export * from './lib/Slider' +export type { SliderProps } from './lib/Slider' + +export * from './lib/Step' + +export * from './lib/FractionalCircle' +export type { FractionalCircleProps } from './lib/FractionalCircle' + +export * from './lib/RadioGroup' + +export * from './lib/RTEditor' diff --git a/libs/design-system/src/lib/AccordionRow/AccordionRow.spec.tsx b/libs/design-system/src/lib/AccordionRow/AccordionRow.spec.tsx new file mode 100644 index 00000000000..055349ce9f1 --- /dev/null +++ b/libs/design-system/src/lib/AccordionRow/AccordionRow.spec.tsx @@ -0,0 +1,82 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { AccordionRow } from './' + +describe('AccordionRow', () => { + describe('when rendered with text content', () => { + it('should display the text', () => { + const component = render(Hello, World!) + + expect(screen.getByText('Label')).toBeInTheDocument() + expect(screen.getByText('Hello, World!')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed an `href` prop', () => { + it('should render as a link', () => { + const component = render( + + Hello, World! + + ) + + expect(screen.getByRole('link')).toHaveAttribute('href', '/to') + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed an `active` prop', () => { + it('should be highlighted', () => { + const component = render( + + Hello, World! + + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed a `collapsible` prop', () => { + it('should render a caret for expanding and collapsing when collapsible', () => { + const component = render( + + Hello, World! + + ) + + expect(component).toMatchSnapshot() + }) + + it('should not render a caret when not collapsible', () => { + const component = render( + + Hello, World! + + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when pressed', () => { + it('should call the `onClick` callback', () => { + const onClickMock = jest.fn() + render() + + fireEvent.click(screen.getByRole('button')) + expect(onClickMock).toHaveBeenCalledTimes(1) + }) + + it('should call the `onToggle` callback if collapsible', () => { + const onToggleMock = jest.fn() + render() + + fireEvent.click(screen.getByRole('button')) + expect(onToggleMock).toHaveBeenCalledWith(false) + + fireEvent.click(screen.getByRole('button')) + expect(onToggleMock).toHaveBeenCalledWith(true) + }) + }) +}) diff --git a/libs/design-system/src/lib/AccordionRow/AccordionRow.stories.tsx b/libs/design-system/src/lib/AccordionRow/AccordionRow.stories.tsx new file mode 100644 index 00000000000..3eaa471ff46 --- /dev/null +++ b/libs/design-system/src/lib/AccordionRow/AccordionRow.stories.tsx @@ -0,0 +1,30 @@ +import type { AccordionRowProps } from './AccordionRow' +import type { Story, Meta } from '@storybook/react' + +import AccordionRow from './AccordionRow' + +export default { + title: 'Components/AccordionRow', + component: AccordionRow, + parameters: { controls: { exclude: ['className', 'children'] } }, + argTypes: { + label: { + control: 'text', + description: 'List label/title', + }, + }, + args: { + label: 'Level 0', + collapsible: true, + }, +} as Meta + +const Template: Story = (args) => ( + + + + + +) + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/AccordionRow/AccordionRow.tsx b/libs/design-system/src/lib/AccordionRow/AccordionRow.tsx new file mode 100644 index 00000000000..6c1be908794 --- /dev/null +++ b/libs/design-system/src/lib/AccordionRow/AccordionRow.tsx @@ -0,0 +1,112 @@ +import type { HTMLAttributes } from 'react' +import React, { useEffect, useState } from 'react' +import AnimateHeight from 'react-animate-height' +import { RiArrowRightSFill as Caret } from 'react-icons/ri' +import Link from 'next/link' +import cn from 'classnames' + +const LevelClassMap = { + 0: { base: 'pl-3 bg-gray-500', interactions: 'hover:bg-gray-400 cursor-pointer' }, + 1: { base: 'pl-6 bg-gray-700', interactions: 'hover:bg-gray-600 cursor-pointer' }, + 2: { base: 'pl-14 bg-gray-800', interactions: 'hover:bg-gray-700 cursor-pointer' }, +} + +export interface AccordionRowProps extends HTMLAttributes { + /** Label string or node */ + label: string | React.ReactNode + + /** Whether the label should be transformed to uppercase */ + uppercase?: boolean + + /** Level for indentation and color */ + level?: keyof typeof LevelClassMap + + /** Whether the AccordionRow can be collapsed/expanded */ + collapsible?: boolean + + /** Whether the AccordionRow is currently expanded */ + expanded?: boolean + + /** Optional link href */ + href?: string + + /** Whether the row is active (highlighted) */ + active?: boolean + + onClick?: () => void + + onToggle?: (expanded: boolean) => void + + className?: string + + children?: React.ReactNode +} + +function AccordionRow({ + label, + uppercase = false, + level = 0, + collapsible = true, + expanded: expandedProp = true, + href, + active, + onClick, + onToggle, + className, + children, + ...rest +}: AccordionRowProps): JSX.Element { + const [isExpanded, setIsExpanded] = useState(expandedProp) + + useEffect(() => setIsExpanded(expandedProp), [expandedProp]) + + const handleClick = () => { + onClick && onClick() + if (!collapsible) return + + const expanded = !isExpanded + setIsExpanded(expanded) + onToggle && onToggle(expanded) + } + + const component = ( + <> +
    + {collapsible && ( +
    + +
    + )} +
    {label}
    +
    + {children && {children}} + + ) + + return href ? ( + +
    {component} + + ) : ( + component + ) +} + +export default AccordionRow diff --git a/libs/design-system/src/lib/AccordionRow/__snapshots__/AccordionRow.spec.tsx.snap b/libs/design-system/src/lib/AccordionRow/__snapshots__/AccordionRow.spec.tsx.snap new file mode 100644 index 00000000000..0f99afd950c --- /dev/null +++ b/libs/design-system/src/lib/AccordionRow/__snapshots__/AccordionRow.spec.tsx.snap @@ -0,0 +1,704 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AccordionRow when passed a \`collapsible\` prop should not render a caret when not collapsible 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    + , + "container":
    +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`AccordionRow when passed a \`collapsible\` prop should render a caret for expanding and collapsing when collapsible 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    + , + "container":
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`AccordionRow when passed an \`active\` prop should be highlighted 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    + , + "container":
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`AccordionRow when passed an \`href\` prop should render as a link 1`] = ` +Object { + "asFragment": [Function], + "baseElement": + + , + "container": , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`AccordionRow when rendered with text content should display the text 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    + , + "container":
    +
    +
    + + + + + + +
    +
    + Label +
    +
    +
    +
    + Hello, World! +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/AccordionRow/index.ts b/libs/design-system/src/lib/AccordionRow/index.ts new file mode 100644 index 00000000000..0da978bcbee --- /dev/null +++ b/libs/design-system/src/lib/AccordionRow/index.ts @@ -0,0 +1,2 @@ +export { default as AccordionRow } from './AccordionRow' +export type { AccordionRowProps } from './AccordionRow' diff --git a/libs/design-system/src/lib/Alert/Alert.stories.tsx b/libs/design-system/src/lib/Alert/Alert.stories.tsx new file mode 100644 index 00000000000..b4a78bb3d25 --- /dev/null +++ b/libs/design-system/src/lib/Alert/Alert.stories.tsx @@ -0,0 +1,53 @@ +import type { Meta, Story } from '@storybook/react' +import type { AlertProps } from './Alert' +import { RiMailCheckLine as CustomIcon } from 'react-icons/ri' + +import Alert from './Alert' + +export default { + title: 'Components/Alert', + component: Alert, + parameters: { controls: { exclude: ['onClose'] } }, + argTypes: { + variant: { + control: { type: 'select' }, + }, + icon: { + table: { + disable: true, + }, + }, + }, + args: { + children: 'Alert message!', + isVisible: true, + }, +} as Meta + +const Template: Story = (args) => { + return +} + +export const Base = Template.bind({}) + +export const Info = Template.bind({}) +Info.args = { variant: 'info' } + +export const Error = Template.bind({}) +Error.args = { variant: 'error' } + +export const Success = Template.bind({}) +Success.args = { variant: 'success' } + +export const WithCloseButton = Template.bind({}) +WithCloseButton.args = { + onClose: () => alert('onClose()'), + children: + 'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Soluta sequi ipsam eligendi a cumque corrupti eum obcaecati perspiciatis. Nobis in ab illo et sequi explicabo quisquam dolor corrupti totam architecto.', +} + +export const WithCustomIcon = Template.bind({}) +WithCustomIcon.args = { + children: 'I have a custom icon', + icon: CustomIcon, +} diff --git a/libs/design-system/src/lib/Alert/Alert.tsx b/libs/design-system/src/lib/Alert/Alert.tsx new file mode 100644 index 00000000000..2112d2d09cd --- /dev/null +++ b/libs/design-system/src/lib/Alert/Alert.tsx @@ -0,0 +1,74 @@ +import type { ReactNode } from 'react' +import type { IconType } from 'react-icons' +import classNames from 'classnames' +import { + RiCheckboxCircleLine as SuccessIcon, + RiCloseFill as CloseIcon, + RiErrorWarningLine as ErrorIcon, + RiInformationLine as InfoIcon, +} from 'react-icons/ri' + +export type AlertVariant = 'info' | 'error' | 'success' + +const backgroundVariants: { [key in AlertVariant]: string } = Object.freeze({ + info: 'bg-gray-400 text-white', + error: 'bg-red text-red bg-opacity-10', + success: 'bg-teal text-teal bg-opacity-10', +}) + +const iconVariants: { [key in AlertVariant]: IconType } = Object.freeze({ + info: InfoIcon, + error: ErrorIcon, + success: SuccessIcon, +}) + +export interface AlertProps { + // Indicates if Alert is visible or not. + isVisible: boolean + // Renders a close button a callback is passed. + onClose?: () => void + // Text/Content displayed in the Alert. + children: ReactNode + // Alert variants. + variant?: AlertVariant + // Custom icon + icon?: IconType + className?: string +} + +function Alert({ + isVisible, + onClose, + children, + className, + icon, + variant = 'info', +}: AlertProps): JSX.Element | null { + // For now we can close it abruptly to avoid spending time on any overoptimization, but this can + // be later be improved to use a transition instead. + if (!isVisible) { + return null + } + + const Icon = icon || iconVariants[variant] + + return ( +
    +
    +
    {}
    + + {children} +
    + + {onClose && ( +
    + +
    + )} +
    + ) +} + +export default Alert diff --git a/libs/design-system/src/lib/Alert/index.ts b/libs/design-system/src/lib/Alert/index.ts new file mode 100644 index 00000000000..4f4dff4d397 --- /dev/null +++ b/libs/design-system/src/lib/Alert/index.ts @@ -0,0 +1,2 @@ +export { default as Alert } from './Alert' +export type { AlertProps, AlertVariant } from './Alert' diff --git a/libs/design-system/src/lib/Badge/Badge.spec.tsx b/libs/design-system/src/lib/Badge/Badge.spec.tsx new file mode 100644 index 00000000000..312a8a3b17b --- /dev/null +++ b/libs/design-system/src/lib/Badge/Badge.spec.tsx @@ -0,0 +1,37 @@ +import { render, screen } from '@testing-library/react' +import { Badge } from './' + +describe('Badge', () => { + describe('when rendered with text', () => { + it('should display the text', () => { + const component = render(Hello, World!) + + expect(screen.getByText('Hello, World!')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed a true `highlighted` prop', () => { + it('should render as a highlighted Badge', () => { + const component = render( + + Hello, World! + + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed an `as` prop', () => { + it('should render as the specified element', () => { + const component = render( + + Hello, World! + + ) + // Ideally we'd actually assert the tag name here, but I can't see a good way to do that + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Badge/Badge.stories.tsx b/libs/design-system/src/lib/Badge/Badge.stories.tsx new file mode 100644 index 00000000000..aabf98db7fb --- /dev/null +++ b/libs/design-system/src/lib/Badge/Badge.stories.tsx @@ -0,0 +1,27 @@ +import type { Story, Meta } from '@storybook/react' +import type { BadgeProps } from './Badge' + +import Badge from './Badge' + +export default { + title: 'Components/Badge', + component: Badge, + parameters: { controls: { exclude: ['as', 'className'] } }, + argTypes: { + children: { + control: 'text', + description: 'Badge content', + }, + }, + args: { + children: '+4.5M', + variant: 'teal', + }, +} as Meta + +const Template: Story = (args) => + +export const Base = Template.bind({}) + +export const Highlighted = Template.bind({}) +Highlighted.args = { highlighted: true } diff --git a/libs/design-system/src/lib/Badge/Badge.tsx b/libs/design-system/src/lib/Badge/Badge.tsx new file mode 100644 index 00000000000..46898687582 --- /dev/null +++ b/libs/design-system/src/lib/Badge/Badge.tsx @@ -0,0 +1,84 @@ +import type { ReactNode, HTMLAttributes } from 'react' +import classNames from 'classnames' + +const BadgeVariants = Object.freeze({ + teal: { + normal: 'text-teal bg-teal bg-opacity-10', + highlighted: 'text-gray-800 bg-teal', + }, + red: { + normal: 'text-red bg-red bg-opacity-10', + highlighted: 'text-gray-800 bg-red', + }, + gray: { + normal: 'text-white bg-gray-700', + highlighted: 'text-gray-800 bg-gray-100', + }, + cyan: { + normal: 'text-cyan bg-cyan bg-opacity-10', + highlighted: 'text-gray-800 bg-cyan', + }, + plain: { + normal: '', + highlighted: '', + }, + warn: { + normal: 'text-yellow bg-yellow bg-opacity-10', + highlighted: 'text-gray-800 bg-yellow', + }, +}) + +export type BadgeVariant = keyof typeof BadgeVariants + +const SizeVariant = Object.freeze({ + sm: 'px-1.5 py-1 text-sm', + md: 'px-2 py-1 text-base', +}) + +export interface BadgeProps extends HTMLAttributes { + variant?: BadgeVariant + + size?: 'sm' | 'md' + + /** Whether the badge is in a brighter highlighted state */ + highlighted?: boolean + + /** Whether the badge will be used to display numerical data */ + numeric?: boolean + + /** Element to use (defaults to
    ) */ + as?: React.ElementType + + className?: string + + children: ReactNode +} + +function Badge({ + variant = 'teal', + size = 'md', + highlighted = false, + numeric = true, + as = 'div', + className, + children, + ...rest +}: BadgeProps): JSX.Element { + const classes = classNames( + className, + BadgeVariants[variant][highlighted ? 'highlighted' : 'normal'], + SizeVariant[size], + numeric && 'tabular-nums', + 'inline-block font-medium rounded' + ) + + const Tag = as + + return ( + + {children} + + ) +} + +export default Badge diff --git a/libs/design-system/src/lib/Badge/__snapshots__/Badge.spec.tsx.snap b/libs/design-system/src/lib/Badge/__snapshots__/Badge.spec.tsx.snap new file mode 100644 index 00000000000..7bcf8c59509 --- /dev/null +++ b/libs/design-system/src/lib/Badge/__snapshots__/Badge.spec.tsx.snap @@ -0,0 +1,220 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Badge when passed a true \`highlighted\` prop should render as a highlighted Badge 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + Hello, World! +
    +
    + , + "container":
    +
    + Hello, World! +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Badge when passed an \`as\` prop should render as the specified element 1`] = ` +Object { + "asFragment": [Function], + "baseElement": + + , + "container": , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Badge when rendered with text should display the text 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + Hello, World! +
    +
    + , + "container":
    +
    + Hello, World! +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Badge/index.ts b/libs/design-system/src/lib/Badge/index.ts new file mode 100644 index 00000000000..838936544d0 --- /dev/null +++ b/libs/design-system/src/lib/Badge/index.ts @@ -0,0 +1,2 @@ +export { default as Badge } from './Badge' +export type { BadgeVariant, BadgeProps } from './Badge' diff --git a/libs/design-system/src/lib/Breadcrumb/Breadcrumb.spec.tsx b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.spec.tsx new file mode 100644 index 00000000000..bd2318c7d02 --- /dev/null +++ b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.spec.tsx @@ -0,0 +1,19 @@ +import { render, screen } from '@testing-library/react' +import Breadcrumb from './Breadcrumb' + +describe('Breadcrumbs', () => { + describe('when rendered with text and hrefs', () => { + it('should display the linked text', () => { + const component = render( + + Example + Example 2 + + ) + + expect(screen.getByText('Example')).toBeInTheDocument() + expect(screen.getByText('Example 2')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Breadcrumb/Breadcrumb.stories.tsx b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.stories.tsx new file mode 100644 index 00000000000..c0c3d1dc8d5 --- /dev/null +++ b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.stories.tsx @@ -0,0 +1,21 @@ +import type { Story, Meta } from '@storybook/react' +import type { BreadcrumbProps } from './Breadcrumb' + +import Breadcrumb from './Breadcrumb' + +export default { + title: 'Components/Breadcrumbs', + component: Breadcrumb, + parameters: { controls: { exclude: ['as', 'className'] } }, + argTypes: {}, + args: {}, +} as Meta + +const Template: Story = (args) => ( + + asdf + asdf + +) + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Breadcrumb/Breadcrumb.tsx b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.tsx new file mode 100644 index 00000000000..bca72d73bb1 --- /dev/null +++ b/libs/design-system/src/lib/Breadcrumb/Breadcrumb.tsx @@ -0,0 +1,49 @@ +import type { ReactNode, HTMLAttributes } from 'react' +import { Fragment } from 'react' +import classNames from 'classnames' +import { RiArrowRightSLine } from 'react-icons/ri' +import Link from 'next/link' + +export interface BreadcrumbProps { + href?: string + className?: string + children: ReactNode +} + +function Breadcrumb({ href, className, children }: BreadcrumbProps): JSX.Element { + const Tag = href ? 'a' : 'span' + const inner = {children} + + return href ? ( + + {inner} + + ) : ( + inner + ) +} + +export interface BreadcrumbGroupProps extends HTMLAttributes { + className?: string + children: ReactNode[] +} + +function Group({ className, children, ...rest }: BreadcrumbGroupProps): JSX.Element { + return ( +
    + {children.map((child, idx) => ( + + + {child} + + {idx < children.length - 1 && } + + ))} +
    + ) +} + +export default Object.assign(Breadcrumb, { Group }) diff --git a/libs/design-system/src/lib/Breadcrumb/__snapshots__/Breadcrumb.spec.tsx.snap b/libs/design-system/src/lib/Breadcrumb/__snapshots__/Breadcrumb.spec.tsx.snap new file mode 100644 index 00000000000..52f0857f75e --- /dev/null +++ b/libs/design-system/src/lib/Breadcrumb/__snapshots__/Breadcrumb.spec.tsx.snap @@ -0,0 +1,152 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Breadcrumbs when rendered with text and hrefs should display the linked text 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + Example + + + + + + + + + + + Example 2 + + +
    +
    + , + "container":
    +
    + + + Example + + + + + + + + + + + Example 2 + + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Breadcrumb/index.ts b/libs/design-system/src/lib/Breadcrumb/index.ts new file mode 100644 index 00000000000..d169b8adcc5 --- /dev/null +++ b/libs/design-system/src/lib/Breadcrumb/index.ts @@ -0,0 +1,2 @@ +export { default as Breadcrumb } from './Breadcrumb' +export type { BreadcrumbProps, BreadcrumbGroupProps } from './Breadcrumb' diff --git a/libs/design-system/src/lib/Button/Button.spec.tsx b/libs/design-system/src/lib/Button/Button.spec.tsx new file mode 100644 index 00000000000..09cd1e32c16 --- /dev/null +++ b/libs/design-system/src/lib/Button/Button.spec.tsx @@ -0,0 +1,39 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import Button from './Button' + +describe('Button', () => { + describe('when rendered with text', () => { + it('should display the text', () => { + const component = render() + + expect(screen.getByText('Hello, World!')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when pressed', () => { + it('should call the `onClick` callback', () => { + const onClickMock = jest.fn() + render() + + fireEvent.click(screen.getByRole('button')) + expect(onClickMock).toHaveBeenCalledTimes(1) + }) + }) + + describe('when passed an `href` value', () => { + it('should render as an element', () => { + const component = render() + // Ideally we'd actually assert the tag name here, but I can't see a good way to do that + expect(component).toMatchSnapshot() + }) + }) + + describe('when passed an `as` prop', () => { + it('should render as the specified element', () => { + const component = render() + // Ideally we'd actually assert the tag name here, but I can't see a good way to do that + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Button/Button.stories.tsx b/libs/design-system/src/lib/Button/Button.stories.tsx new file mode 100644 index 00000000000..6a359ef3a8b --- /dev/null +++ b/libs/design-system/src/lib/Button/Button.stories.tsx @@ -0,0 +1,24 @@ +import type { Story, Meta } from '@storybook/react' +import type { ButtonProps } from './Button' + +import Button from './Button' + +export default { + title: 'Components/Button', + component: Button, + parameters: { controls: { exclude: ['as', 'className', 'onClick'] } }, + argTypes: { + children: { + control: 'text', + description: 'Button content', + }, + }, + args: { + children: 'Click Me!', + variant: 'primary', + }, +} as Meta + +const Template: Story = (args) => +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Button/index.ts b/libs/design-system/src/lib/Button/index.ts new file mode 100644 index 00000000000..dff27aa9c84 --- /dev/null +++ b/libs/design-system/src/lib/Button/index.ts @@ -0,0 +1,2 @@ +export { default as Button } from './Button' +export type { ButtonVariant, ButtonProps } from './Button' diff --git a/libs/design-system/src/lib/Checkbox/Checkbox.spec.tsx b/libs/design-system/src/lib/Checkbox/Checkbox.spec.tsx new file mode 100644 index 00000000000..06cfe61aa2f --- /dev/null +++ b/libs/design-system/src/lib/Checkbox/Checkbox.spec.tsx @@ -0,0 +1,32 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { fireEvent, render, screen } from '@testing-library/react' +import { Checkbox } from '.' + +describe('Checkbox', () => { + describe('when rendered with a label', () => { + it('should display the label', () => { + const component = render( {}} label="Checkbox label" />) + + expect(screen.getByText('Checkbox label')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when pressed', () => { + it('should call the `onChange` callback', () => { + const onChangeMock = jest.fn() + render() + + fireEvent.click(screen.getByRole('switch')) + expect(onChangeMock).toHaveBeenCalledTimes(1) + }) + }) + + describe('when toggle is checked', () => { + it('should display a checkmark', () => { + const component = render( {}} checked />) + + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Checkbox/Checkbox.stories.tsx b/libs/design-system/src/lib/Checkbox/Checkbox.stories.tsx new file mode 100644 index 00000000000..c9b434549be --- /dev/null +++ b/libs/design-system/src/lib/Checkbox/Checkbox.stories.tsx @@ -0,0 +1,22 @@ +import type { Story, Meta } from '@storybook/react' +import type { CheckboxProps } from './Checkbox' +import { useState } from 'react' + +import Checkbox from './Checkbox' + +export default { + title: 'Components/Checkbox', + component: Checkbox, + parameters: { controls: { exclude: ['className', 'onClick'] } }, + args: { + label: 'Check something', + }, +} as Meta + +const Template: Story = (args) => { + const [enabled, setEnabled] = useState(args.checked) + + return setEnabled(checked)} /> +} + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Checkbox/Checkbox.tsx b/libs/design-system/src/lib/Checkbox/Checkbox.tsx new file mode 100644 index 00000000000..276a3bc6846 --- /dev/null +++ b/libs/design-system/src/lib/Checkbox/Checkbox.tsx @@ -0,0 +1,63 @@ +import { Switch } from '@headlessui/react' +import classNames from 'classnames' +import type { ReactNode } from 'react' + +export interface CheckboxProps { + label?: ReactNode + checked?: boolean + onChange?: (checked: boolean) => void + className?: string + wrapperClassName?: string + disabled?: boolean + dark?: boolean +} + +export default function Checkbox({ + label, + checked = false, + className, + wrapperClassName, + disabled = false, + onChange, + dark = false, + ...rest +}: CheckboxProps): JSX.Element { + return ( + +
    + onChange && onChange(checked)} + {...rest} + > + {checked && ( + + + + )} + + {label && {label}} +
    +
    + ) +} diff --git a/libs/design-system/src/lib/Checkbox/__snapshots__/Checkbox.spec.tsx.snap b/libs/design-system/src/lib/Checkbox/__snapshots__/Checkbox.spec.tsx.snap new file mode 100644 index 00000000000..42fe3b0981c --- /dev/null +++ b/libs/design-system/src/lib/Checkbox/__snapshots__/Checkbox.spec.tsx.snap @@ -0,0 +1,213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Checkbox when rendered with a label should display the label 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    +
    + , + "container":
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Checkbox when toggle is checked should display a checkmark 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    +
    + , + "container":
    +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Checkbox/index.ts b/libs/design-system/src/lib/Checkbox/index.ts new file mode 100644 index 00000000000..97301f35da4 --- /dev/null +++ b/libs/design-system/src/lib/Checkbox/index.ts @@ -0,0 +1,2 @@ +export { default as Checkbox } from './Checkbox' +export type { CheckboxProps } from './Checkbox' diff --git a/libs/design-system/src/lib/DatePicker/DatePicker.spec.tsx b/libs/design-system/src/lib/DatePicker/DatePicker.spec.tsx new file mode 100644 index 00000000000..a490e8bca03 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePicker.spec.tsx @@ -0,0 +1,107 @@ +import user from '@testing-library/user-event' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import { DatePicker } from './' +import { DateTime } from 'luxon' + +// Set date to Oct 29, 2021 to keep snapshots consistent +beforeAll(() => jest.useFakeTimers().setSystemTime(new Date('2021-10-29 12:00:00'))) + +// DatePicker configuration +const minDate = DateTime.now().minus({ years: 2 }) +const maxDate = DateTime.now() + +describe('', () => { + describe('When datepicker is closed', () => { + it('should have placeholder when empty', () => { + const onChangeMock = jest.fn() + + const component = render( + + ) + + expect(screen.getByPlaceholderText('MM / DD / YYYY')).toBeInTheDocument() + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + + it('should have date when valid', () => { + const onChangeMock = jest.fn() + + const component = render( + + ) + + const input = component.getByPlaceholderText('MM / DD / YYYY') as HTMLInputElement + + user.type(input, '02012021') + expect(onChangeMock).toHaveBeenCalledWith('2021-02-01') + expect(screen.queryByText('Date must be')).not.toBeInTheDocument() + + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + }) + }) + + describe('When datepicker is expanded', () => { + it('should be able to open modal and select a date', async () => { + const onChangeMock = jest.fn() + + render( +
    + +
    + ) + + const currentMonth = DateTime.now() + const priorMonth = DateTime.now().minus({ months: 1 }) + + // Open the modal + fireEvent.click(screen.getByTestId('datepicker-toggle-icon')) + + await waitFor(() => expect(screen.getByTestId('datepicker-panel')).toBeInTheDocument()) + + // Go back a month + fireEvent.click(screen.getByTestId('datepicker-range-back-arrow')) + expect(screen.queryByText(priorMonth.monthShort)).toBeInTheDocument() + expect(screen.queryByText(priorMonth.year)).toBeInTheDocument() + + // Go forward a month + fireEvent.click(screen.getByTestId('datepicker-range-next-arrow')) + expect(screen.queryByText(currentMonth.monthShort)).toBeInTheDocument() + expect(screen.queryByText(currentMonth.year)).toBeInTheDocument() + + // Select a date + fireEvent.click(screen.getByText('17')) + fireEvent.click(screen.getByText('Apply')) + expect(onChangeMock).toHaveBeenCalledWith( + DateTime.fromObject( + { + day: 17, + month: currentMonth.month, + year: currentMonth.year, + }, + { zone: 'utc' } + ).toISODate() + ) + }) + }) +}) diff --git a/libs/design-system/src/lib/DatePicker/DatePicker.stories.tsx b/libs/design-system/src/lib/DatePicker/DatePicker.stories.tsx new file mode 100644 index 00000000000..7d1348e01f5 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePicker.stories.tsx @@ -0,0 +1,27 @@ +import type { Story, Meta } from '@storybook/react' +import type { DatePickerProps } from './DatePicker' +import { useState } from 'react' + +import DatePicker from './DatePicker' + +export default { + title: 'Components/DatePicker', + component: DatePicker, + argTypes: { + placeholder: { description: 'The Input placeholder value' }, + value: { description: 'Valid ISO 8601 string' }, + minDate: { control: 'text', description: 'Valid ISO 8601 string' }, + maxDate: { control: 'text', description: 'Valid ISO 8601 string' }, + error: { control: 'text', description: 'Error message' }, + }, +} as Meta + +export const Base: Story = (props: DatePickerProps) => { + const [date, setDate] = useState(null) + + return ( +
    + +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePicker.tsx b/libs/design-system/src/lib/DatePicker/DatePicker.tsx new file mode 100644 index 00000000000..aff42a5f398 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePicker.tsx @@ -0,0 +1,172 @@ +import type * as PopperJs from '@popperjs/core' +import { PatternFormat, type NumberFormatValues } from 'react-number-format' +import type { Ref } from 'react' +import { useState, useCallback, forwardRef } from 'react' +import { Popover, Portal } from '@headlessui/react' +import { RiCalendarEventFill as CalendarIcon } from 'react-icons/ri' +import { Button, Input } from '../..' +import { DateTime } from 'luxon' +import classNames from 'classnames' +import { usePopper } from 'react-popper' +import { DatePickerCalendar } from './DatePickerCalendar' +import { MAX_SUPPORTED_DATE, MIN_SUPPORTED_DATE } from './utils' + +const INPUT_DATE_FORMAT = 'MM / dd / yyyy' + +export interface DatePickerProps { + name: string + value: string | null + onChange: (date: string | null) => void + error?: string + label?: string + className?: String + placeholder?: string + minCalendarDate?: string + maxCalendarDate?: string + popperPlacement?: PopperJs.Placement + popperStrategy?: PopperJs.PositioningStrategy +} + +function toFormattedStr(date: string | null) { + if (!date) return '' + return DateTime.fromISO(date).toFormat(INPUT_DATE_FORMAT) +} + +function DatePicker( + { + name, + value, + onChange, + error, + label, + className, + placeholder = 'MM / DD / YYYY', + minCalendarDate = MIN_SUPPORTED_DATE.toISODate(), + maxCalendarDate = MAX_SUPPORTED_DATE.toISODate(), + popperPlacement = 'auto', + popperStrategy = 'fixed', + }: DatePickerProps, + ref: Ref +): JSX.Element { + // Positions the datepicker panel appropriately based on screen size and parent elements + const [referenceElement, setReferenceElement] = useState() + const [popperElement, setPopperElement] = useState() + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: popperPlacement, + strategy: popperStrategy, + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 8], + }, + }, + { + name: 'preventOverflow', + options: { + altAxis: true, + }, + }, + ], + }) + + const [calendarValue, setCalendarValue] = useState(value ?? '') + + // Only change input value when it is cleared or is a date value + const handleInputValueChange = useCallback( + (date: NumberFormatValues) => { + if (!date.formattedValue) { + setCalendarValue('') + onChange(null) + } else { + const inputDate = DateTime.fromFormat(date.formattedValue, INPUT_DATE_FORMAT) + + if (inputDate.isValid) { + setCalendarValue(inputDate.toISODate()) + onChange(inputDate.toISODate()) + } + } + }, + [onChange] + ) + + return ( + +
    + - https://github.com/s-yadav/react-number-format#custom-inputs + getInputRef={ref} + format="## / ## / ####" + placeholder={placeholder} + mask={['M', 'M', 'D', 'D', 'Y', 'Y', 'Y', 'Y']} + value={toFormattedStr(value)} + error={error} + label={label} + onValueChange={handleInputValueChange} + fixedRightOverride={ + + + + } + /> +
    + + + + {({ close }) => ( +
    +
    + {/* Calendar */} +
    + + + +
    + } + /> +
    +
    +
    + )} + + + + ) +} + +export default forwardRef(DatePicker) diff --git a/libs/design-system/src/lib/DatePicker/DatePickerCalendar.tsx b/libs/design-system/src/lib/DatePicker/DatePickerCalendar.tsx new file mode 100644 index 00000000000..34f307bc0a9 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerCalendar.tsx @@ -0,0 +1,186 @@ +import type { DateObj } from 'dayzed' +import classNames from 'classnames' +import { useDayzed } from 'dayzed' +import { DateTime, Info } from 'luxon' +import { useMemo, useState } from 'react' +import { RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri' +import { DatePickerMonth } from './DatePickerMonth' +import { DatePickerYear } from './DatePickerYear' + +export interface DatePickerCalendarProps { + date?: string + onChange: (date: string) => void + minDate: string + maxDate: string + controlButtons: React.ReactNode +} + +export function DatePickerCalendar({ + date, + onChange, + minDate, + maxDate, + controlButtons, +}: DatePickerCalendarProps) { + const { currentCalendarDate, currentCalendarSelection } = useMemo(() => { + if (!date) + return { currentCalendarDate: DateTime.now().toJSDate(), currentCalendarSelection: [] } + + return { + currentCalendarDate: DateTime.fromISO(date).toJSDate(), + currentCalendarSelection: DateTime.fromISO(date).toJSDate(), + } + }, [date]) + + const [offset, setOffset] = useState(0) + + const { calendars, getBackProps, getForwardProps, getDateProps } = useDayzed({ + date: currentCalendarDate, // determines what month to show + selected: currentCalendarSelection, + onDateSelected: (dateObj: DateObj) => { + setOffset(0) + onChange(DateTime.fromJSDate(dateObj.date).toISODate()) + }, + minDate: DateTime.fromISO(minDate).toJSDate(), + maxDate: DateTime.fromISO(maxDate).toJSDate(), + offset, + onOffsetChanged: setOffset, + }) + + const [view, setView] = useState<'calendar' | 'month' | 'year'>('calendar') + + return ( +
    + {view === 'calendar' && ( +
    + {/* Back button (- 1 month) */} + + + {/* Displays the calendar's current month and year */} +
    + + +
    + + {/* Forward button (+ 1 month) */} + +
    + )} +
    + {view === 'month' && ( + setView('calendar')} + /> + )} + {view === 'year' && ( + setView('calendar')} + /> + )} + {view === 'calendar' && + calendars.map((calendar) => ( +
    + {/* Day names row */} +
    + {/* Rotate Info.weekdays +6 to start with Sunday */} + {[...Array(7)].map((_, day) => ( +
    + {Info.weekdays('short')[(day + 6) % 7]} +
    + ))} +
    + + {/* Date cells */} +
    + {calendar.weeks.map((week, weekIndex) => ( +
    + {week.map((dateObj, dateIndex) => { + if (dateObj) { + return ( +
    + {/* Date button */} + +
    + ) + } + + // Empty cell + return ( +
    + ) + })} +
    + ))} +
    +
    + ))} +
    + + {view === 'calendar' && controlButtons} +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerInput.tsx b/libs/design-system/src/lib/DatePicker/DatePickerInput.tsx new file mode 100644 index 00000000000..81dc5fd1efa --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerInput.tsx @@ -0,0 +1,82 @@ +import { type NumberFormatValues, PatternFormat } from 'react-number-format' +import { DateTime } from 'luxon' +import { useCallback } from 'react' +import { Input } from '../inputs' + +export interface DatePickerInput { + onChange: (value: string) => void + value?: string + error?: string + hasError?: boolean + onError?: (error: string) => void + minDate?: string + maxDate?: string + className?: string +} + +export function DatePickerInput({ + value, + onChange, + error, + hasError, + onError, + minDate, + maxDate, + className, +}: DatePickerInput) { + const handleInputValueChange = useCallback( + (date: NumberFormatValues) => { + if (date.value.length === 8) { + let errorMessage = '' + + /** + * react-number-format guarantees that we will always have valid user inputs, so + * we can rely on the length of the user input string to determine when the date + * is valid and when we should update it in the UI + */ + const month = +date.value.substring(0, 2) + const day = +date.value.substring(2, 4) + const year = +date.value.substring(4, 8) + + const inputDate = DateTime.fromObject({ month, day, year }) + + // Make sure date is valid + if (!inputDate.isValid) errorMessage = 'Invalid date provided' + + const min = DateTime.fromISO( + minDate || DateTime.now().minus({ years: 50 }).toISODate() + ) + + const max = DateTime.fromISO(maxDate || DateTime.now().toISODate()) + + if (inputDate < min) errorMessage = `Date must be greater than ${min.toISODate()}` + if (inputDate > max) errorMessage = `Date must be less than ${max.toISODate()}` + + if (errorMessage) { + onError && onError(errorMessage) + } else { + onError && onError('') + } + + // Pass value back in YYYY-MM-DD format + onChange(inputDate.toFormat('yyyy-MM-dd')) + } + }, + [minDate, maxDate, onChange, onError] + ) + + return ( + - https://github.com/s-yadav/react-number-format#custom-inputs + format="## / ## / ####" + mask={['M', 'M', 'D', 'D', 'Y', 'Y', 'Y', 'Y']} + value={value ? DateTime.fromISO(value).toFormat('MM / dd / yyyy') : ''} // must pass an empty string for the "undefined" condition to trigger a re-render + error={error} + hasError={!!error || hasError} + onValueChange={handleInputValueChange} + className={className} + placeholder="MM / DD / YYYY" + inputClassName="text-center" + /> + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerMonth.tsx b/libs/design-system/src/lib/DatePicker/DatePickerMonth.tsx new file mode 100644 index 00000000000..9a62b3f25b8 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerMonth.tsx @@ -0,0 +1,84 @@ +import type { Calendar, GetBackForwardPropsOptions } from 'dayzed' +import classNames from 'classnames' +import { Info } from 'luxon' +import { useMemo } from 'react' +import { RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri' + +export interface DatePickerMonthProps { + calendars: Calendar[] + getBackProps: (data: GetBackForwardPropsOptions) => Record + getForwardProps: (data: GetBackForwardPropsOptions) => Record + onMonthSelected: () => void +} + +export function DatePickerMonth({ + calendars, + getBackProps, + getForwardProps, + onMonthSelected, +}: DatePickerMonthProps) { + const calendar = calendars[0] + + const selectedMonth = useMemo(() => `${calendar.year}/${calendar.month}`, []) + + const getMonthProps = (month: number) => { + if (month > calendar.month) { + return getForwardProps({ + calendars, + offset: month - calendar.month, + onClick: onMonthSelected, + }) + } + + return getBackProps({ calendars, offset: calendar.month - month, onClick: onMonthSelected }) + } + + const months = Info.months('short') + + return ( +
    +
    + + + {calendar.year} + + +
    +
    + {months.map((month, index) => ( + + ))} +
    +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerQuickSelect.tsx b/libs/design-system/src/lib/DatePicker/DatePickerQuickSelect.tsx new file mode 100644 index 00000000000..8e8e6b82b65 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerQuickSelect.tsx @@ -0,0 +1,91 @@ +import type { DateRange, SelectableDateRange } from './selectableRanges' +import { RadioGroup } from '@headlessui/react' +import classNames from 'classnames' +import { DateTime } from 'luxon' +import { useMemo } from 'react' + +interface DatePickerQuickSelectProps { + ranges: SelectableDateRange[] + value?: Partial + onChange: (range: DateRange) => void +} + +export function DatePickerQuickSelect({ ranges, value, onChange }: DatePickerQuickSelectProps) { + const optionStyle = 'pl-2 pr-8 py-1.5 rounded' + + const daysSelected = useMemo(() => { + if (!value || !value.start || !value.end) return '' + + const start = DateTime.fromISO(value.start) + const end = DateTime.fromISO(value.end) + + const diffDays = end.diff(start, 'days').days + const diffMonths = end.diff(start, 'months').months + const diffYears = end.diff(start, 'years').years + + if (diffDays <= 90) { + return `Selected: ${Math.ceil(diffDays + 1)} days` + } else if (diffDays < 365) { + return `Selected: ${Math.round(diffMonths)} months` + } else { + return `Selected: ${Math.round(diffYears)} years` + } + }, [value]) + + // Determines whether the current value is custom, or one of the pre-defined options + const { isCustom, radioValue } = useMemo(() => { + if (!value) return { isCustom: true } + + const index = ranges.findIndex( + (val: SelectableDateRange) => val.start === value.start && val.end === value.end + ) + + if (index === -1) { + return { + radioValue: { label: 'Custom', start: value.start, end: value.end }, + isCustom: true, + } + } else { + return { radioValue: ranges[index], isCustom: false } + } + }, [value, ranges]) + + return ( +
    + + onChange({ start: radioValue.start, end: radioValue.end }) + } + > + {ranges.map((range) => ( + + {({ active, checked }) => ( +
    + {range.label} +
    + )} +
    + ))} +
    +
    +
    + Custom range +
    +
    +
    {daysSelected}
    +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.spec.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.spec.tsx new file mode 100644 index 00000000000..bee94e72fdc --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.spec.tsx @@ -0,0 +1,214 @@ +import user from '@testing-library/user-event' +import { fireEvent, getQueriesForElement, render, screen, waitFor } from '@testing-library/react' +import { DatePickerRange } from './DatePickerRange' +import { DateTime } from 'luxon' + +// Set date to Oct 29, 2021 to keep snapshots consistent +beforeAll(() => jest.useFakeTimers().setSystemTime(new Date('2021-10-29 12:00:00'))) + +// DatePicker configuration +const minDate = DateTime.now().minus({ years: 2 }) +const maxDate = DateTime.now() + +describe('', () => { + describe('When datepicker is closed', () => { + it('should have placeholder when empty', () => { + const onChangeMock = jest.fn() + + const component = render( + + ) + + expect(screen.queryByText('Select a date range')).toBeInTheDocument() + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + + it('should have date when valid', () => { + const onChangeMock = jest.fn() + + const component = render( + + ) + + expect(screen.queryByText('Jan 01, 2021')).toBeInTheDocument() + expect(screen.queryByText('to')).toBeInTheDocument() + expect(screen.queryByText('Jan 01, 2022')).toBeInTheDocument() + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('When datepicker is expanded', () => { + it('should be able to select a date from the dropdown while clicking', async () => { + const onChangeMock = jest.fn() + + const start = DateTime.fromISO('2021-12-01') + const end = DateTime.fromISO('2022-01-31') + + render( + + ) + + // Open the modal + fireEvent.click(screen.getByTestId('datepicker-range-toggle-icon')) + + await waitFor(() => + expect(screen.getByTestId('datepicker-range-panel')).toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).toBeInTheDocument() + expect(screen.queryByText('Apply')).toBeInTheDocument() + + expect(screen.queryByText(end.monthShort)).toBeInTheDocument() + expect(screen.queryByText(end.year)).toBeInTheDocument() + + // Go back a month + fireEvent.click(screen.getByTestId('datepicker-range-back-arrow')) + expect(screen.queryByText(start.monthShort)).toBeInTheDocument() + expect(screen.queryByText(start.year)).toBeInTheDocument() + + // Select start date + fireEvent.click( + await getQueriesForElement(screen.getByTestId('day-cells')).findByText('1') + ) + + // Go forward a month + fireEvent.click(screen.getByTestId('datepicker-range-next-arrow')) + expect(screen.queryByText(end.monthShort)).toBeInTheDocument() + expect(screen.queryByText(end.year)).toBeInTheDocument() + + // Select end date + fireEvent.click( + await getQueriesForElement(screen.getByTestId('day-cells')).findByText('31') + ) + + // Submit + fireEvent.click(screen.getByText('Apply')) + + expect(onChangeMock).toHaveBeenCalledWith({ + start: start.toISODate(), + end: end.toISODate(), + }) + + await waitFor(() => + expect(screen.queryByTestId('datepicker-range-panel')).not.toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + }) + it('should be able to select a date from the dropdown using inputs', async () => { + const onChangeMock = jest.fn() + + const start = DateTime.fromISO('2022-01-01') + const end = DateTime.fromISO('2022-01-20') + + render( + + ) + + // Open the modal + fireEvent.click(screen.getByTestId('datepicker-range-toggle-icon')) + + await waitFor(() => + expect(screen.getByTestId('datepicker-range-panel')).toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).toBeInTheDocument() + expect(screen.queryByText('Apply')).toBeInTheDocument() + + const dateInputs = screen.getAllByRole('textbox') + + user.type(dateInputs[0], start.toFormat('MMddyyyy')) + user.type(dateInputs[1], end.toFormat('MMddyyyy')) + + expect(screen.queryByText(end.monthShort)).toBeInTheDocument() + expect(screen.queryByText(end.year)).toBeInTheDocument() + + // Submit + fireEvent.click(screen.getByText('Apply')) + + expect(onChangeMock).toHaveBeenCalledWith({ + start: start.toISODate(), + end: end.toISODate(), + }) + + await waitFor(() => + expect(screen.queryByTestId('datepicker-range-panel')).not.toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + }) + + it('should be able to select a date using the quick select sidebar', async () => { + const onChangeMock = jest.fn() + + render( + + ) + + // Open the modal + fireEvent.click(screen.getByTestId('datepicker-range-toggle-icon')) + + await waitFor(() => + expect(screen.getByTestId('datepicker-range-panel')).toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).toBeInTheDocument() + expect(screen.queryByText('Apply')).toBeInTheDocument() + + fireEvent.click(screen.getByText('Last month')) + + const lastMonth = DateTime.now().minus({ months: 1 }) + + expect(screen.queryByText(lastMonth.monthShort)).toBeInTheDocument() + expect(screen.queryByText(lastMonth.year)).toBeInTheDocument() + + // Submit + fireEvent.click(screen.getByText('Apply')) + + expect(onChangeMock).toHaveBeenCalledWith({ + start: lastMonth.startOf('month').toISODate(), + end: lastMonth.endOf('month').toISODate(), + }) + + await waitFor(() => + expect(screen.queryByTestId('datepicker-range-panel')).not.toBeInTheDocument() + ) + + expect(screen.queryByText('Cancel')).not.toBeInTheDocument() + expect(screen.queryByText('Apply')).not.toBeInTheDocument() + }) + }) +}) diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.stories.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.stories.tsx new file mode 100644 index 00000000000..d4c650845fe --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.stories.tsx @@ -0,0 +1,108 @@ +import type { Story, Meta } from '@storybook/react' +import type { DateRange } from '../selectableRanges' +import { useState } from 'react' +import { DateTime } from 'luxon' +import { DatePickerRange } from './DatePickerRange' + +export default { + title: 'Components/DatePicker/DatePickerRange', + component: DatePickerRange, + argTypes: { + placeholder: { description: 'The Input placeholder value' }, + value: { description: 'Valid ISO 8601 string' }, + minDate: { control: 'text', description: 'Valid ISO 8601 string' }, + maxDate: { control: 'text', description: 'Valid ISO 8601 string' }, + }, +} as Meta + +export const Base: Story = (args) => { + const [range, setRange] = useState | undefined>(undefined) + + return ( +
    + +
    + ) +} + +export const Tabs: Story = (args) => { + // const [range, setRange] = useState | undefined>(undefined) + + const [range, setRange] = useState | undefined>({ + start: '2022-02-01', + end: '2022-02-28', + }) + + return ( +
    + +
    + ) +} + +export const TabsWithCustomRange: Story = (args) => { + // const [range, setRange] = useState | undefined>(undefined) + + const [range, setRange] = useState | undefined>({ + start: '2022-02-01', + end: '2022-02-28', + }) + + return ( +
    + +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.tsx new file mode 100644 index 00000000000..e23b06c0a9f --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRange.tsx @@ -0,0 +1,214 @@ +import type * as PopperJs from '@popperjs/core' +import type { DateRange, SelectableDateRange, SelectableRangeKeys } from '../selectableRanges' + +import { Popover, Portal } from '@headlessui/react' +import classNames from 'classnames' +import { DateTime } from 'luxon' +import { RiArrowRightLine } from 'react-icons/ri' +import { useEffect, useMemo, useState } from 'react' +import { usePopper } from 'react-popper' +import { Button } from '../../Button' +import { DatePickerRangeCalendar } from './DatePickerRangeCalendar' +import { DatePickerInput } from '../DatePickerInput' +import { getNormalizedRanges } from '../selectableRanges' +import { DatePickerQuickSelect } from '../DatePickerQuickSelect' +import { DatePickerRangeButton } from './DatePickerRangeButton' +import { DatePickerRangeTabs } from './DatePickerRangeTabs' + +export interface DatePickerRangeProps { + variant?: 'default' | 'tabs' | 'tabs-custom' + value?: Partial + selectableRanges: Array + onChange: (range: DateRange) => void + className?: string + minDate?: string + maxDate?: string + popperPlacement?: PopperJs.Placement + popperStrategy?: PopperJs.PositioningStrategy +} + +export function DatePickerRange({ + variant = 'default', + value, + selectableRanges, + onChange, + className, + minDate = DateTime.now().minus({ years: 50 }).toISODate(), + maxDate = DateTime.now().toISODate(), + popperPlacement = 'bottom-end', + popperStrategy = 'fixed', +}: DatePickerRangeProps) { + // PopperJS configuration: positions the datepicker panel appropriately based on screen size and parent elements + const [referenceElement, setReferenceElement] = useState() + const [popperElement, setPopperElement] = useState() + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: popperPlacement, + strategy: popperStrategy, + modifiers: [ + { + name: 'offset', + options: { + offset: [0, variant === 'default' ? 8 : 16], + }, + }, + { + name: 'preventOverflow', + options: { + altAxis: true, + }, + }, + ], + }) + + const [range, setRange] = useState(value) + const [rangeError, setRangeError] = useState(false) + const [startError, setStartError] = useState(false) + const [endError, setEndError] = useState(false) + + useEffect(() => { + setRangeError( + range && !!range.start && !!range.end + ? DateTime.fromISO(range.start) > DateTime.fromISO(range.end) + : false + ) + }, [range]) + + const tabs = useMemo(() => { + return getNormalizedRanges(selectableRanges) + }, [selectableRanges]) + + return ( + + {variant === 'tabs' ? ( + { + onChange(range) + setRange(range) + }} + /> + ) : variant === 'tabs-custom' ? ( + { + onChange(range) + setRange(range) + }} + setReferenceElement={setReferenceElement} + /> + ) : ( + + )} + + + + {({ close }) => ( +
    + {variant === 'default' && ( +
    + +
    + )} +
    +
    + + + setRange((previous) => ({ + ...previous, + start, + })) + } + hasError={startError || rangeError} + onError={(err) => setStartError(!!err)} + minDate={minDate} + maxDate={maxDate} + className="w-full xs:w-36" + /> + + + + + setRange((previous) => ({ + ...previous, + end, + })) + } + hasError={endError || rangeError} + onError={(err) => setEndError(!!err)} + minDate={minDate} + maxDate={maxDate} + className="w-full mb-4 xs:mb-0 xs:w-36" // On mobile, calendar sits under the inputs + /> +
    + } + controlButtons={ +
    + + +
    + } + /> +
    +
    + + )} +
    +
    +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeButton.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeButton.tsx new file mode 100644 index 00000000000..76b98bc3144 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeButton.tsx @@ -0,0 +1,47 @@ +import type { DateRange } from '../selectableRanges' +import { Popover } from '@headlessui/react' +import { DateTime } from 'luxon' +import { RiCalendarEventFill } from 'react-icons/ri' + +export interface DatePickerRangeButtonProps { + value?: Partial + setPopperReferenceElement: (el: HTMLButtonElement) => void +} + +// The datepicker button that toggles the panel to open/close, and displays the currently selected date range +export function DatePickerRangeButton({ + value, + setPopperReferenceElement, +}: DatePickerRangeButtonProps) { + return ( + + {({ open }) => { + return ( +
    + {open || !value ? ( + Select a date range + ) : ( + <> + {value.start && ( + + {DateTime.fromISO(value.start).toFormat('MMM dd, yyyy')} + + )} + to + {value.end && ( + + {DateTime.fromISO(value.end).toFormat('MMM dd, yyyy')} + + )} + + )} + +
    + ) + }} +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeCalendar.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeCalendar.tsx new file mode 100644 index 00000000000..2e933e510c0 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeCalendar.tsx @@ -0,0 +1,282 @@ +import type { DateRange } from '../selectableRanges' +import type { DateObj } from 'dayzed' +import { useDayzed } from 'dayzed' +import classNames from 'classnames' +import { DateTime, Info } from 'luxon' +import { useMemo, useState } from 'react' +import { RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri' + +import { DatePickerMonth } from '../DatePickerMonth' +import { DatePickerYear } from '../DatePickerYear' + +export interface DatePickerRangeCalendarProps { + range?: Partial + onChange: (range: Partial) => void + minDate: string + maxDate: string + rangeInputs: React.ReactNode + controlButtons: React.ReactNode +} + +export function DatePickerRangeCalendar({ + range, + onChange, + minDate, + maxDate, + rangeInputs, + controlButtons, +}: DatePickerRangeCalendarProps) { + const { currentCalendarDate, currentCalendarSelection } = useMemo(() => { + if (!range) + return { currentCalendarDate: DateTime.now().toJSDate(), currentCalendarSelection: [] } + + const dates: Date[] = [] + + if (range.start) { + dates.push(DateTime.fromISO(range.start).toJSDate()) + } + + if (range.end) { + dates.push(DateTime.fromISO(range.end).toJSDate()) + } + + return { + currentCalendarDate: DateTime.fromISO( + range.start && range.end ? range.end! : range.start! + ).toJSDate(), + currentCalendarSelection: dates, + } + }, [range]) + + const [offset, setOffset] = useState(0) + + const { calendars, getBackProps, getForwardProps, getDateProps } = useDayzed({ + date: currentCalendarDate, // determines what month to show + selected: currentCalendarSelection, + onDateSelected: (dateObj: DateObj) => { + const date = DateTime.fromJSDate(dateObj.date) + const start = range && range.start ? DateTime.fromISO(range.start) : undefined + const end = range && range.end ? DateTime.fromISO(range.end) : undefined + + // We always want the calendar reflecting the most recently selected date (regardless of whether that date is + // the beginning or end of the range selection). An offset should only exist *between* user clicks. + setOffset(0) + + // Start the range process over + if ((start && end) || !start || (start && date < start)) { + onChange({ start: date.toISODate(), end: undefined }) + + return + } + + onChange({ start: range!.start, end: date.toISODate() }) + }, + minDate: DateTime.fromISO(minDate).toJSDate(), + maxDate: DateTime.fromISO(maxDate).toJSDate(), + offset, + onOffsetChanged: setOffset, + }) + + const [view, setView] = useState<'calendar' | 'month' | 'year'>('calendar') + + return ( +
    + {view === 'calendar' && ( +
    + {/* Back button (- 1 month) */} + + + {/* Displays the calendar's current month and year */} +
    + + +
    + + {/* Forward button (+ 1 month) */} + +
    + )} + {view === 'calendar' && rangeInputs} +
    + {view === 'month' && ( + setView('calendar')} + /> + )} + {view === 'year' && ( + setView('calendar')} + /> + )} + {view === 'calendar' && + calendars.map((calendar) => ( +
    + {/* Day names row */} +
    + {/* Rotate Info.weekdays +6 to start with Sunday */} + {[...Array(7)].map((_, day) => ( +
    + {Info.weekdays('short')[(day + 6) % 7]} +
    + ))} +
    + + {/* Date cells */} +
    + {calendar.weeks.map((week, weekIndex) => ( +
    + {week.map((dateObj, dateIndex) => { + if (dateObj) { + const d = DateTime.fromJSDate(dateObj.date) + + const start = + range && range.start + ? DateTime.fromISO(range.start) + : undefined + + const end = + range && range.end + ? DateTime.fromISO(range.end) + : undefined + + const hasBothDates = !!start && !!end + const inRange = start && end && d > start && d < end + const isEnd = + end && d.toISODate() === end.toISODate() + const isStart = + start && d.toISODate() === start.toISODate() + + const isFirstWeekday = dateObj.date.getDay() === 0 + const isLastWeekday = dateObj.date.getDay() === 6 + const isFirstDayOfMonth = + dateObj.date.getDate() === 1 + const isLastDayOfMonth = + dateObj.date.getDate() === + calendar.lastDayOfMonth.getDate() + + return ( +
    + {hasBothDates && + ((isStart && + !isLastWeekday && + !isLastDayOfMonth) || + (isEnd && + !isFirstWeekday && + !isFirstDayOfMonth)) && + start.toISODate() !== + end.toISODate() && ( + <> + {/* Fills background color gap for cells at ends of range */} +
    + + )} + {/* Actual date button */} + +
    + ) + } + + // Empty cell + return ( +
    + ) + })} +
    + ))} +
    +
    + ))} +
    + + {view === 'calendar' && controlButtons} +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeTabs.tsx b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeTabs.tsx new file mode 100644 index 00000000000..e6b50a40249 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/DatePickerRangeTabs.tsx @@ -0,0 +1,141 @@ +import type { Dispatch, SetStateAction } from 'react' +import type { DateRange, SelectableDateRange } from '..' +import { useMemo } from 'react' +import { Popover, Tab } from '@headlessui/react' +import classNames from 'classnames' +import { RiCalendarEventFill } from 'react-icons/ri' + +export type DatePickerRangeTabsProps = + | { + variant: 'custom' + value?: Partial + onChange: (range: DateRange) => void + tabs: Array + setReferenceElement?: Dispatch> + } + | { + variant: 'default' + value?: Partial + onChange: (range: DateRange) => void + tabs: SelectableDateRange[] + setReferenceElement?: never + } + +function DefaultTab({ value, selected }: { value: string; selected: boolean }) { + return ( + + {value} + + ) +} + +const NOT_SELECTED_TAB_INDEX = 999 + +// The tabs that persistently show and can toggle the panel to open/close +export function DatePickerRangeTabs(props: DatePickerRangeTabsProps) { + const selectedIndex = useMemo(() => { + // No range, no tab selected + if (!props.value?.start || !props.value?.end) { + return NOT_SELECTED_TAB_INDEX + } + + const tabIndex = props.tabs.findIndex( + (tab) => + typeof tab !== 'string' && // not custom tab + tab.start === props.value?.start && + tab.end === props.value?.end + ) + + // known range, select tab + if (tabIndex !== -1) { + return tabIndex + } + + const customTabIndex = props.tabs.findIndex( + (tab) => typeof tab === 'string' && tab === 'custom' + ) + + // custom range, select custom tab + if (customTabIndex !== -1) { + return customTabIndex + } + + return NOT_SELECTED_TAB_INDEX + }, [props.tabs, props.value?.start, props.value?.end]) + + // Render the custom variant (a datepicker shows up as the very last tab) + if (props.variant === 'custom') { + return ( +
    + { + const tab = props.tabs[index] + + // If the "custom" tab is clicked, we defer state updates until the user interacts with the calendar panel and submits + if (tab !== 'custom') { + props.onChange({ start: tab.start, end: tab.end }) + } + }} + > + + {props.tabs.map((tab) => ( + + {({ selected }) => + tab === 'custom' ? ( + renders as a button by default) + data-testid="datepicker-range-toggle-icon-tabs" + className={classNames( + 'px-4 flex items-center text-gray-100 rounded h-8 hover:bg-gray-300', + selected && 'bg-gray-400 text-white shadow' + )} + > + + + ) : ( + + ) + } + + ))} + + +
    + ) + } + + // Default implementation - no datepicker is available to click here + return ( +
    + { + const { start, end } = props.tabs[index] + props.onChange({ start, end }) + }} + > + + {props.tabs.map((tab) => ( + + {({ selected }) => ( + + )} + + ))} + + +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/__snapshots__/DatePickerRange.spec.tsx.snap b/libs/design-system/src/lib/DatePicker/DatePickerRange/__snapshots__/DatePickerRange.spec.tsx.snap new file mode 100644 index 00000000000..38ad81f93b6 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/__snapshots__/DatePickerRange.spec.tsx.snap @@ -0,0 +1,324 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` When datepicker is closed should have date when valid 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    +
    +
    +
    + +
    +
    + , + "container":
    +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[` When datepicker is closed should have placeholder when empty 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    +
    +
    +
    +
    + , + "container":
    +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/DatePicker/DatePickerRange/index.ts b/libs/design-system/src/lib/DatePicker/DatePickerRange/index.ts new file mode 100644 index 00000000000..9ea76440abe --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerRange/index.ts @@ -0,0 +1 @@ +export * from './DatePickerRange' diff --git a/libs/design-system/src/lib/DatePicker/DatePickerYear.tsx b/libs/design-system/src/lib/DatePicker/DatePickerYear.tsx new file mode 100644 index 00000000000..e05fe9a2bd1 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/DatePickerYear.tsx @@ -0,0 +1,100 @@ +import type { Calendar, GetBackForwardPropsOptions } from 'dayzed' +import classNames from 'classnames' +import { RiArrowLeftSLine, RiArrowRightSLine } from 'react-icons/ri' + +import { generateYearsRange, disabled } from './utils' + +export interface DatePickerYearProps { + calendars: Calendar[] + getBackProps: (data: GetBackForwardPropsOptions) => Record + getForwardProps: (data: GetBackForwardPropsOptions) => Record + onYearSelected: () => void + minDate: string + maxDate: string +} + +export function DatePickerYear({ + calendars, + getBackProps, + getForwardProps, + onYearSelected, + minDate, + maxDate, +}: DatePickerYearProps) { + const calendar = calendars[0] + const selectedYear = calendar.year + const years = generateYearsRange(selectedYear) + + const getYearProps = (year: number) => { + if (year > calendar.year) { + const forwardProps = getForwardProps({ + calendars, + offset: (year - calendar.year) * 12, + onClick: onYearSelected, + }) + + return { + ...forwardProps, + disabled: disabled({ year, maxDate }), + } + } + + const backProps = getBackProps({ + calendars, + offset: (calendar.year - year) * 12, + onClick: onYearSelected, + }) + + return { + ...backProps, + disabled: disabled({ year, minDate }), + } + } + + return ( +
    +
    + + + {`${calendar.year - 5} - ${calendar.year + 6}`} + + +
    +
    + {years.map((year) => ( + + ))} +
    +
    + ) +} diff --git a/libs/design-system/src/lib/DatePicker/__snapshots__/DatePicker.spec.tsx.snap b/libs/design-system/src/lib/DatePicker/__snapshots__/DatePicker.spec.tsx.snap new file mode 100644 index 00000000000..5b19a0e412c --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/__snapshots__/DatePicker.spec.tsx.snap @@ -0,0 +1,181 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` When datepicker is closed should have placeholder when empty 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/DatePicker/index.ts b/libs/design-system/src/lib/DatePicker/index.ts new file mode 100644 index 00000000000..2acc01ff931 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/index.ts @@ -0,0 +1,7 @@ +export type { DatePickerProps } from './DatePicker' +export type { DatePickerRangeProps } from './DatePickerRange' +export type { DateRange, SelectableRangeKeys, SelectableDateRange } from './selectableRanges' + +export { default as DatePicker } from './DatePicker' +export { DatePickerRange } from './DatePickerRange' +export { getNormalizedRanges, getRangeDescription } from './selectableRanges' diff --git a/libs/design-system/src/lib/DatePicker/selectableRanges.ts b/libs/design-system/src/lib/DatePicker/selectableRanges.ts new file mode 100644 index 00000000000..0da3cf2b922 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/selectableRanges.ts @@ -0,0 +1,154 @@ +import { DateTime } from 'luxon' + +export type DateRange = { + start: string + end: string +} + +export type SelectableRangeKeys = + | 'day' + | 'last-7-days' + | 'this-month' + | 'prior-month' + | 'last-30-days' + | 'last-90-days' + | 'last-6-months' + | 'this-year' + | 'prior-year' + | 'last-365-days' + | 'last-3-years' + | 'last-5-years' + +export type SelectableDateRange = DateRange & { + label: string + labelShort: string + alternateLabel?: string +} + +export const getNormalizedRanges: ( + selections: Array | 'all' +) => SelectableDateRange[] = (selections) => { + const now = DateTime.now() + const nowISO = now.toISODate() + + const ranges: { [key in SelectableRangeKeys]: SelectableDateRange } = { + day: { + label: 'Today', + labelShort: '1D', + alternateLabel: 'today', + start: now.minus({ days: 1 }).toISODate(), + end: nowISO, + }, + 'last-7-days': { + label: 'Last 7 days', + labelShort: '7D', + alternateLabel: 'past week', + start: now.minus({ days: 7 }).toISODate(), + end: nowISO, + }, + 'this-month': { + label: 'This month', + labelShort: 'This month', + alternateLabel: 'this month', + start: now.startOf('month').toISODate(), + end: nowISO, + }, + 'prior-month': { + label: 'Last month', + labelShort: 'Last month', + alternateLabel: 'last month', + start: now.minus({ months: 1 }).startOf('month').toISODate(), + end: now.minus({ months: 1 }).endOf('month').toISODate(), + }, + 'last-30-days': { + label: 'Last month', + labelShort: '1M', + alternateLabel: 'past month', + start: now.minus({ days: 30 }).toISODate(), + end: nowISO, + }, + 'last-90-days': { + label: 'Last 3 months', + labelShort: '3M', + alternateLabel: 'past 3 months', + start: now.minus({ days: 90 }).toISODate(), + end: nowISO, + }, + 'last-6-months': { + label: 'Last 6 months', + labelShort: '6M', + alternateLabel: 'past 6 months', + start: now.minus({ months: 6 }).toISODate(), + end: nowISO, + }, + 'prior-year': { + label: 'Last year', + labelShort: 'Last year', + alternateLabel: 'last year', + start: now.minus({ years: 1 }).startOf('year').toISODate(), + end: now.minus({ years: 1 }).endOf('year').toISODate(), + }, + 'last-365-days': { + label: 'Last 365 days', + labelShort: '1Y', + alternateLabel: 'past year', + start: now.minus({ days: 365 }).toISODate(), + end: nowISO, + }, + 'this-year': { + label: 'This year', + labelShort: 'YTD', + alternateLabel: 'this year', + start: now.startOf('year').toISODate(), + end: nowISO, + }, + 'last-3-years': { + label: 'Last 3 years', + labelShort: '3Y', + alternateLabel: 'past 3 years', + start: now.minus({ years: 3 }).toISODate(), + end: nowISO, + }, + 'last-5-years': { + label: 'Last 5 years', + labelShort: '5Y', + alternateLabel: 'past 5 years', + start: now.minus({ years: 5 }).toISODate(), + end: nowISO, + }, + } + + if (typeof selections === 'string' && selections === 'all') { + return Object.values(ranges) + } + + return selections.map((selection) => { + if (typeof selection === 'string') { + return ranges[selection] + } else { + return selection + } + }) +} + +export const getRangeDescription = (range?: DateRange, minDate?: string) => { + if (!range) return 'in this period' + + const fromStartRange = { + start: minDate, + end: DateTime.now().toISODate(), + alternateLabel: 'from the start', + } + + const knownRanges = [...getNormalizedRanges('all'), fromStartRange] + + const knownRange = knownRanges.find( + ({ start, end }) => range.start === start && range.end === end + ) + + if (knownRange) { + return knownRange.alternateLabel || 'in this period' + } + + return 'in this period' +} diff --git a/libs/design-system/src/lib/DatePicker/utils.spec.tsx b/libs/design-system/src/lib/DatePicker/utils.spec.tsx new file mode 100644 index 00000000000..7094f6cd911 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/utils.spec.tsx @@ -0,0 +1,56 @@ +import { generateYearsRange, disabled } from './utils' + +describe('disabled', () => { + it('should return true if year is less than minDate', () => { + const year = 2021 + const minDate = '2022-01-01' + const result = disabled({ year, minDate }) + expect(result).toBe(true) + }) + + it('should return false if year is more or equal than minDate', () => { + const year = 2022 + const minDate = '2022-01-01' + const result = disabled({ year, minDate }) + expect(result).toBe(false) + }) + + it('should return false if year is less or equal than maxDate', () => { + const year = 2022 + const maxDate = '2022-01-01' + const result = disabled({ year, maxDate }) + expect(result).toBe(false) + }) + + it('should return true if year is more than maxDate', () => { + const year = 2023 + const maxDate = '2022-01-01' + const result = disabled({ year, maxDate }) + expect(result).toBe(true) + }) +}) + +describe('generateYearsRange', () => { + const currentYear = 2022 + + it('should return a range of years from a previous year', () => { + const years = generateYearsRange(2006, currentYear) + expect(years).toEqual([ + 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + ]) + }) + + it('should return a range of years from current year', () => { + const years = generateYearsRange(2018, currentYear) + expect(years).toEqual([ + 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, + ]) + }) + + it('should return a range of years from next year', () => { + const years = generateYearsRange(2030, currentYear) + expect(years).toEqual([ + 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, + ]) + }) +}) diff --git a/libs/design-system/src/lib/DatePicker/utils.tsx b/libs/design-system/src/lib/DatePicker/utils.tsx new file mode 100644 index 00000000000..4e4c286f170 --- /dev/null +++ b/libs/design-system/src/lib/DatePicker/utils.tsx @@ -0,0 +1,45 @@ +import range from 'lodash/range' +import { DateTime } from 'luxon' + +type DisabledProps = { + year: number + minDate?: string + maxDate?: string +} + +// This custom disabled function because dayzed +// ignores the offset when calculating the disabled dates +// https://github.com/maybe-finance/maybe-app/pull/417#issuecomment-1208475083 +export const disabled = ({ year, minDate, maxDate }: DisabledProps) => { + if (minDate) { + const minDateYear = DateTime.fromISO(minDate).year + + if (year < minDateYear) { + return true + } + } + + if (maxDate) { + const maxDateYear = DateTime.fromISO(maxDate).year + + if (year > maxDateYear) { + return true + } + } + + return false +} + +// Return a year grid of 12 years +// with the current year in the center position (index 4, position 2,2) +export const generateYearsRange = (year: number, currentYear = DateTime.now().year) => { + const naturalIndex = currentYear % 12 // Which position the current year would be in the grid if the grid started with 0 + const desiredIndex = 4 // Which position we want the current year be in the grid + const offset = (year + naturalIndex + desiredIndex) % 12 + + return range(year - offset, year - offset + 12) +} + +// We allow a maximum of 30 years of history for performance reasons (hypertable chunking) +export const MIN_SUPPORTED_DATE = DateTime.utc().minus({ years: 30 }).startOf('day') +export const MAX_SUPPORTED_DATE = DateTime.utc().startOf('day') diff --git a/libs/design-system/src/lib/Dialog/Dialog.spec.tsx b/libs/design-system/src/lib/Dialog/Dialog.spec.tsx new file mode 100644 index 00000000000..0c55e49fe0a --- /dev/null +++ b/libs/design-system/src/lib/Dialog/Dialog.spec.tsx @@ -0,0 +1,24 @@ +import { render, screen } from '@testing-library/react' +import { Dialog } from './' + +describe('Dialog', () => { + describe('when rendered with `open` prop true', () => { + it('should display the modals contents', () => { + const onToggleMock = jest.fn() + const component = render( + + Dialog Title + Dialog Content + Dialog Description + Dialog Actions + + ) + + expect(screen.getByText('Dialog Title')).toBeInTheDocument() + expect(screen.getByText('Dialog Content')).toBeInTheDocument() + expect(screen.getByText('Dialog Description')).toBeInTheDocument() + expect(screen.getByText('Dialog Actions')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Dialog/Dialog.stories.tsx b/libs/design-system/src/lib/Dialog/Dialog.stories.tsx new file mode 100644 index 00000000000..69ac0715f15 --- /dev/null +++ b/libs/design-system/src/lib/Dialog/Dialog.stories.tsx @@ -0,0 +1,149 @@ +import type { Story, Meta } from '@storybook/react' +import type { DialogProps } from './Dialog' +import * as React from 'react' + +import Dialog from './Dialog' + +// Misc UI elements for stories +import { Button, Input, FormGroup } from '../../' + +export default { + title: 'Components/Dialog', + component: Dialog, + parameters: { + controls: { exclude: ['as', 'className'] }, + }, +} as Meta + +export const Base: Story = (args) => { + const [isOpen, setIsOpen] = React.useState(false) + return ( +
    + + setIsOpen(false)}> + Add Home + +
    + + + +
    +
    + + + +
    +
    + ) +} + +export const WithDescription: Story = (args) => { + const [isOpen, setIsOpen] = React.useState(false) + return ( +
    + + setIsOpen(false)}> + Add Home + + + + + + + + + Removing the Business Account will remove all related transactions and + historical data. This will likely impact other views such as your net worth + dashboard. + + + + + +
    + ) +} + +export const WithMultipleActions: Story = (args) => { + const [isOpen, setIsOpen] = React.useState(false) + return ( +
    + + setIsOpen(false)}> + Add Home + +
    + + + +
    +
    + + + + + +
    +
    + ) +} + +export const KitchenSink: Story = (args) => { + const [isOpen, setIsOpen] = React.useState(false) + return ( +
    + + setIsOpen(false)}> + Add Home + + + + + + + + + Removing the Business Account will remove all related transactions and + historical data. This will likely impact other views such as your net worth + dashboard. + + + + + + +
    + ) +} diff --git a/libs/design-system/src/lib/Dialog/Dialog.tsx b/libs/design-system/src/lib/Dialog/Dialog.tsx new file mode 100644 index 00000000000..6433e0b9e00 --- /dev/null +++ b/libs/design-system/src/lib/Dialog/Dialog.tsx @@ -0,0 +1,198 @@ +import type { PropsWithChildren } from 'react' +import React, { Children, Fragment } from 'react' +import { Dialog as HeadlessDialog, Transition } from '@headlessui/react' +import { RiCloseFill as IconClose } from 'react-icons/ri' +import classNames from 'classnames' + +const DialogSizeClassName = Object.freeze({ + xs: 'sm:max-w-xs', + sm: 'sm:max-w-sm', + md: 'sm:max-w-md', + lg: 'sm:max-w-lg', + xl: 'sm:max-w-xl', + '2xl': 'sm:max-w-2xl', +}) + +export interface DialogProps { + /** Boolean value that controls open/close states */ + isOpen?: boolean + + /** Function to run on dialog close—also used to connect to close icon, and should be function used to toggle `isOpen` prop */ + onClose: () => void + + /** Whether or not to show the close icon */ + showCloseButton?: boolean + + initialFocus?: React.MutableRefObject + + // defaults to 'md' + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' +} + +export function DialogRoot({ + isOpen = false, + onClose, + showCloseButton = true, + initialFocus, + size = 'md', + children, + ...rest +}: PropsWithChildren): JSX.Element { + let actions: React.ReactNode = null + let title: React.ReactNode = null + let content: React.ReactNode = null + let description: React.ReactNode = null + let unhandledChildren: React.ReactNode = null + + Children.forEach(children, (child) => { + const name = (child as any).type?.name + switch (name) { + case 'Actions': + actions = child + break + case 'Content': + content = child + break + case 'Title': + title = child + break + case 'Description': + description = child + break + default: + unhandledChildren = child + console.warn( + 'Unhanded child type. Wrap your child element in , , , or to ensure proper placement.' + ) + break + } + }) + + return ( + + + + {/* The backdrop */} + +
    +
    +
    +
    + , + "container":
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Dialog/index.ts b/libs/design-system/src/lib/Dialog/index.ts new file mode 100644 index 00000000000..20f0e3aa7c2 --- /dev/null +++ b/libs/design-system/src/lib/Dialog/index.ts @@ -0,0 +1,3 @@ +export { default as DialogV2 } from './DialogV2' +export { default as Dialog } from './Dialog' +export type { DialogProps } from './Dialog' diff --git a/libs/design-system/src/lib/FormGroup/FormGroup.spec.tsx b/libs/design-system/src/lib/FormGroup/FormGroup.spec.tsx new file mode 100644 index 00000000000..5f4651c8ec2 --- /dev/null +++ b/libs/design-system/src/lib/FormGroup/FormGroup.spec.tsx @@ -0,0 +1,19 @@ +import { render, screen } from '@testing-library/react' +import FormGroup from './FormGroup' + +describe('FormGroup', () => { + describe('when rendered with children', () => { + it('should wrap and display the children', () => { + const component = render( + + + + + ) + + expect(screen.getByText('Hello, World!')).toBeInTheDocument() + expect(screen.getByLabelText('Hello, World!')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/FormGroup/FormGroup.tsx b/libs/design-system/src/lib/FormGroup/FormGroup.tsx new file mode 100644 index 00000000000..57da7daa166 --- /dev/null +++ b/libs/design-system/src/lib/FormGroup/FormGroup.tsx @@ -0,0 +1,19 @@ +import type { ReactNode } from 'react' +import classNames from 'classnames' + +export interface FormGroupProps { + className?: string + children?: ReactNode +} + +function FormGroup({ className, children, ...rest }: FormGroupProps): JSX.Element { + const combinedClassName = classNames(className, 'mb-4') + + return ( +
    + {children} +
    + ) +} + +export default FormGroup diff --git a/libs/design-system/src/lib/FormGroup/__snapshots__/FormGroup.spec.tsx.snap b/libs/design-system/src/lib/FormGroup/__snapshots__/FormGroup.spec.tsx.snap new file mode 100644 index 00000000000..ea90e9af0b3 --- /dev/null +++ b/libs/design-system/src/lib/FormGroup/__snapshots__/FormGroup.spec.tsx.snap @@ -0,0 +1,90 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FormGroup when rendered with children should wrap and display the children 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + +
    +
    + , + "container":
    +
    + + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/FormGroup/index.ts b/libs/design-system/src/lib/FormGroup/index.ts new file mode 100644 index 00000000000..5f1a1df3261 --- /dev/null +++ b/libs/design-system/src/lib/FormGroup/index.ts @@ -0,0 +1,2 @@ +export { default as FormGroup } from './FormGroup' +export type { FormGroupProps } from './FormGroup' diff --git a/libs/design-system/src/lib/FractionalCircle/FractionalCircle.stories.tsx b/libs/design-system/src/lib/FractionalCircle/FractionalCircle.stories.tsx new file mode 100644 index 00000000000..e4ed0ea5b4b --- /dev/null +++ b/libs/design-system/src/lib/FractionalCircle/FractionalCircle.stories.tsx @@ -0,0 +1,26 @@ +import type { Story, Meta } from '@storybook/react' + +import FractionalCircle from './FractionalCircle' + +export default { + title: 'Components/FractionalCircle', + component: FractionalCircle, + argTypes: { + variant: { + options: ['default'], + control: { type: 'radio' }, + defaultValue: 'default', + }, + }, + args: { + radius: 50, + stroke: 10, + percent: 15, + }, +} as Meta + +export const Base: Story = (args) => ( +
    + +
    +) diff --git a/libs/design-system/src/lib/FractionalCircle/FractionalCircle.tsx b/libs/design-system/src/lib/FractionalCircle/FractionalCircle.tsx new file mode 100644 index 00000000000..8ba5577caff --- /dev/null +++ b/libs/design-system/src/lib/FractionalCircle/FractionalCircle.tsx @@ -0,0 +1,75 @@ +import classNames from 'classnames' + +type CircleProps = { + radius: number + stroke: number + className?: string + fill?: boolean + percent?: number +} + +function Circle({ radius, stroke, className, percent = 100 }: CircleProps) { + // validate + const _percent = percent < 0 ? 0 : percent > 100 ? 100 : percent + const circumference = Math.PI * 2 * radius + const strokePercent = ((100 - _percent) * circumference) / 100 + + return ( + + ) +} + +export type FractionalCircleProps = { + percent: number + variant?: 'default' | 'yellow' | 'green' | 'red' + radius?: number + stroke?: number +} + +const CircleVariant = Object.freeze({ + default: { + ring: 'text-white', + background: 'text-gray-300', + }, + yellow: { + ring: 'text-yellow', + background: 'text-gray-300', + }, + green: { + ring: 'text-teal', + background: 'text-teal-300', + }, + red: { + ring: 'text-red', + background: 'text-red-300', + }, +}) + +export default function FractionalCircle({ + percent, + variant = 'default', + radius = 7, + stroke = 2, +}: FractionalCircleProps) { + return ( + + + + + ) +} diff --git a/libs/design-system/src/lib/FractionalCircle/index.ts b/libs/design-system/src/lib/FractionalCircle/index.ts new file mode 100644 index 00000000000..fd3fcac694a --- /dev/null +++ b/libs/design-system/src/lib/FractionalCircle/index.ts @@ -0,0 +1,2 @@ +export { default as FractionalCircle } from './FractionalCircle' +export type { FractionalCircleProps } from './FractionalCircle' diff --git a/libs/design-system/src/lib/IndexTabs/IndexTabs.spec.tsx b/libs/design-system/src/lib/IndexTabs/IndexTabs.spec.tsx new file mode 100644 index 00000000000..f497f492f8f --- /dev/null +++ b/libs/design-system/src/lib/IndexTabs/IndexTabs.spec.tsx @@ -0,0 +1,50 @@ +import { render, screen } from '@testing-library/react' +import { useRef } from 'react' +import { IndexTabs } from './' + +const Example = () => { + const scrollContainer = useRef(null) + const section1 = useRef(null) + const section2 = useRef(null) + + return ( + <> + +
    +
    + Content 1 +
    +
    + Content 2 +
    +
    + + ) +} + +// Very light testing - it's challenging to mock/test all of the scroll interactions +describe('IndexTabs', () => { + it('should render correctly', () => { + const component = render() + + expect(screen.getByText('Section 1')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).toBeInTheDocument() + + expect(screen.getByText('Section 2')).toBeInTheDocument() + expect(screen.queryByText('Content 2')).toBeInTheDocument() + + expect(component).toMatchSnapshot() + }) +}) diff --git a/libs/design-system/src/lib/IndexTabs/IndexTabs.stories.tsx b/libs/design-system/src/lib/IndexTabs/IndexTabs.stories.tsx new file mode 100644 index 00000000000..f33f37a079b --- /dev/null +++ b/libs/design-system/src/lib/IndexTabs/IndexTabs.stories.tsx @@ -0,0 +1,68 @@ +import type { Story, Meta } from '@storybook/react' +import { useRef } from 'react' + +import { IndexTabs } from '.' + +export default { + title: 'Components/IndexTabs', + argTypes: {}, + args: {}, +} as Meta + +export const Base: Story = (_args) => { + const scrollContainer = useRef(null) + const section1 = useRef(null) + const section2 = useRef(null) + const section3 = useRef(null) + + return ( + <> + +
    +
    +

    Section 1

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. +

    +
    +
    +

    Section 2

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. +

    +
    +
    +

    Section 3

    +

    + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod + tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, + quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. +

    +
    +
    + + ) +} diff --git a/libs/design-system/src/lib/IndexTabs/IndexTabs.tsx b/libs/design-system/src/lib/IndexTabs/IndexTabs.tsx new file mode 100644 index 00000000000..9b038f4af48 --- /dev/null +++ b/libs/design-system/src/lib/IndexTabs/IndexTabs.tsx @@ -0,0 +1,142 @@ +import { useCallback, useEffect, useRef, useState } from 'react' +import type { RefObject, HTMLAttributes } from 'react' +import scrollIntoView from 'smooth-scroll-into-view-if-needed' +import classNames from 'classnames' +import { animate } from 'framer-motion' + +export interface IndexTabsProps extends HTMLAttributes { + sections: { + name: string + elementRef: RefObject + }[] + scrollContainer: RefObject + initialIndex?: number +} + +export default function IndexTabs({ + className, + sections, + scrollContainer, + initialIndex, + ...rest +}: IndexTabsProps): JSX.Element { + // The active tab's index + const [activeIndex, setActiveIndex] = useState(0) + + // The index being automatically scrolled to + const [scrollingTo, setScrollingTo] = useState(null) + + const tabElements = useRef<(HTMLElement | null)[]>([]) + + useEffect(() => { + if (!scrollContainer.current) return + const scrollElement = scrollContainer.current + let scrollTimeout: NodeJS.Timeout | undefined = undefined + + const onScroll = () => { + // Find the first visible section + let index: number | null = sections.findIndex(({ elementRef }) => { + const elementRect = elementRef.current?.getBoundingClientRect() + const parentRect = elementRef.current?.parentElement?.getBoundingClientRect() + if (!elementRect || !parentRect) return false + + return elementRect.top <= parentRect.top + ? parentRect.top - elementRect.top < elementRect.height + : elementRect.bottom - parentRect.bottom < elementRect.height + }) + index = index !== -1 ? index : null + + // Only update active tab if we're not automatically scrolling or we're finished scrolling + if (scrollingTo === null || scrollingTo === index) { + setActiveIndex(index) + setScrollingTo(null) + } + + // Reset scrollingTo after 100 ms of not scrolling + clearTimeout(scrollTimeout) + scrollTimeout = setTimeout(function () { + setScrollingTo(null) + }, 100) + } + + scrollElement.addEventListener('scroll', onScroll) + + return () => scrollElement.removeEventListener('scroll', onScroll) + }, [scrollContainer, scrollingTo, sections]) + + const scrollTo = useCallback( + (index: number) => { + const elementRef = sections[index].elementRef + if (!elementRef.current) return + + scrollIntoView(elementRef.current, { + behavior: 'smooth', + block: 'start', + inline: 'nearest', + }) + + // Blink heading if the container is not scrollable + if ( + scrollContainer.current && + scrollContainer.current.clientHeight === scrollContainer.current.scrollHeight + ) { + const el = elementRef.current + + animate(1, [1, 0.6, 1, 0.6, 1], { + duration: 0.5, + onUpdate: (value) => { + const heading = el.querySelector('h1, h2, h3, h4, h5, h6') + if (heading) heading.style.opacity = value.toString() + }, + }) + } + + setScrollingTo(index) + setActiveIndex(index) + }, + [sections, scrollContainer] + ) + + useEffect(() => { + if (initialIndex) { + scrollTo(initialIndex) + } + }, [scrollTo, initialIndex]) + + useEffect(() => { + if (activeIndex !== null) { + const tabElement = tabElements.current[activeIndex] + tabElement && + scrollIntoView(tabElement, { + behavior: 'smooth', + block: 'nearest', + inline: 'nearest', + }) + } + }, [activeIndex, tabElements]) + + return ( + + ) +} diff --git a/libs/design-system/src/lib/IndexTabs/__snapshots__/IndexTabs.spec.tsx.snap b/libs/design-system/src/lib/IndexTabs/__snapshots__/IndexTabs.spec.tsx.snap new file mode 100644 index 00000000000..50554896182 --- /dev/null +++ b/libs/design-system/src/lib/IndexTabs/__snapshots__/IndexTabs.spec.tsx.snap @@ -0,0 +1,120 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`IndexTabs should render correctly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    +
    + Content 1 +
    +
    + Content 2 +
    +
    +
    + , + "container":
    + +
    +
    + Content 1 +
    +
    + Content 2 +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/IndexTabs/index.ts b/libs/design-system/src/lib/IndexTabs/index.ts new file mode 100644 index 00000000000..1535f9ef230 --- /dev/null +++ b/libs/design-system/src/lib/IndexTabs/index.ts @@ -0,0 +1 @@ +export { default as IndexTabs } from './IndexTabs' diff --git a/libs/design-system/src/lib/Listbox/Listbox.spec.tsx b/libs/design-system/src/lib/Listbox/Listbox.spec.tsx new file mode 100644 index 00000000000..de74afc0ea7 --- /dev/null +++ b/libs/design-system/src/lib/Listbox/Listbox.spec.tsx @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { fireEvent, render, screen } from '@testing-library/react' +import { Listbox } from './' + +// Not tested too thoroughly, most logic is covered by Headless UI base +describe('Listbox', () => { + describe('when closed', () => { + it('should render the button', () => { + const component = render( + {}}> + Select... + + Option 1 + + + ) + + expect(screen.getByText('Select...')).toBeVisible() + expect(screen.queryByText('Option 1')).not.toBeVisible() + expect(component).toMatchSnapshot() + }) + }) + + describe('when opened', () => { + it('should render the button and the items', () => { + const component = render( + {}}> + Select... + + Option 1 + + Option 2 + + + + ) + + fireEvent.click(screen.getByText('Select...')) + ;['Select...', 'Option 1', 'Option 2'].forEach((text) => + expect(screen.getByText(text)).toBeVisible() + ) + expect(component).toMatchSnapshot() + }) + }) + + describe('when an option is pressed', () => { + it('should call the `onChange` callback', () => { + const onChangeMock = jest.fn() + render( + + Select... + + Option 1 + + + ) + + fireEvent.click(screen.getByText('Select...')) + fireEvent.click(screen.getByText('Option 1')) + expect(onChangeMock).toHaveBeenCalledWith('Option 1') + }) + }) +}) diff --git a/libs/design-system/src/lib/Listbox/Listbox.stories.tsx b/libs/design-system/src/lib/Listbox/Listbox.stories.tsx new file mode 100644 index 00000000000..41b6c761dbe --- /dev/null +++ b/libs/design-system/src/lib/Listbox/Listbox.stories.tsx @@ -0,0 +1,75 @@ +import type { Story, Meta } from '@storybook/react' +import { useState } from 'react' +import Listbox from './Listbox' +import { RiLineChartLine } from 'react-icons/ri' + +export default { + title: 'Components/Listbox', + component: Listbox, + parameters: { controls: { exclude: ['className', 'as', 'refName'] } }, + argTypes: { + icon: { + control: 'boolean', + }, + iconPosition: { + control: 'select', + options: ['left', 'right'], + description: 'Side of the icon', + label: 'Icon Position', + }, + checkIconPosition: { + control: 'select', + options: ['left', 'right'], + description: 'Side of the selected check', + }, + }, + args: { + icon: true, + iconPosition: 'left', + checkIconPosition: 'right', + label: 'Listbox', + }, +} as Meta + +const Template: Story = ({ label, icon, iconPosition, checkIconPosition }) => { + const [value, setValue] = useState('Option 1') + + return ( +
    + + {value} + + + Option 1 + + + Option 2 + + + Option 3 + + + Disabled + + + +
    + ) +} + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Listbox/Listbox.tsx b/libs/design-system/src/lib/Listbox/Listbox.tsx new file mode 100644 index 00000000000..32cffe8531f --- /dev/null +++ b/libs/design-system/src/lib/Listbox/Listbox.tsx @@ -0,0 +1,284 @@ +import type { Dispatch, MouseEventHandler, PropsWithChildren, SetStateAction } from 'react' +import type { IconType } from 'react-icons' +import type { PopperProps } from 'react-popper' +import React, { createContext, useContext, useState, useEffect } from 'react' +import { Listbox as HeadlessListbox } from '@headlessui/react' +import classNames from 'classnames' +import { RiArrowDownSFill, RiCheckFill } from 'react-icons/ri' +import { usePopper } from 'react-popper' +import { Checkbox } from '../Checkbox' + +type ExtractProps = T extends React.ComponentType ? P : T + +const ListboxButtonVariants = Object.freeze({ + default: + 'text-white bg-gray-500 border-gray-500 focus:border-cyan focus:ring-opacity-10 focus:ring-cyan', + teal: 'text-teal bg-teal bg-opacity-10 border-transparent focus:border-teal focus:ring-opacity-10 focus:ring-teal', + red: 'text-red bg-red bg-opacity-10 border-transparent focus:border-red focus:ring-opacity-10 focus:ring-red', + yellow: 'text-yellow bg-yellow bg-opacity-10 border-transparent focus:border-yellow focus:ring-opacity-10 focus:ring-yellow', +}) + +export type ListboxButtonVariant = keyof typeof ListboxButtonVariants + +export interface ListboxButtonProps extends React.ButtonHTMLAttributes { + variant?: ListboxButtonVariant + + /** Label that appears above the button */ + label?: string + + /** Icon that appears on the left side of the button */ + icon?: IconType + + /** Shows or hides down arrow dropdown icon */ + hideRightIcon?: boolean + + /** Placeholder that appears when there is no child value */ + placeholder?: string + + className?: string + + labelClassName?: string + + buttonClassName?: string + + size?: 'small' | 'default' + + onClick?: MouseEventHandler +} + +const ListboxContext = createContext<{ + referenceElement: HTMLButtonElement | null + setReferenceElement?: Dispatch> + multiple: boolean +}>({ referenceElement: null, multiple: false }) + +function Listbox({ + className, + onClick, + multiple = false, + ...rest +}: { onClick?: MouseEventHandler } & ExtractProps) { + const [referenceElement, setReferenceElement] = useState(null) + + return ( + + + + ) +} + +function Button({ + className, + variant = 'default', + label, + icon: Icon, + hideRightIcon = false, + placeholder, + labelClassName, + buttonClassName, + size = 'default', + children, + onClick, + ...rest +}: ListboxButtonProps & ExtractProps) { + const { setReferenceElement } = useContext(ListboxContext) + + return ( + + {({ disabled }) => ( + <> + {label && ( + + {label} + + )} + + + + )} + + ) +} + +type ListboxOptionsProps = ExtractProps + +function Options({ + className, + placement = 'bottom-start', + children, + ...rest +}: (Omit | Omit) & { + placement?: PopperProps['placement'] +}) { + const { referenceElement, multiple } = useContext(ListboxContext) + const [popperElement, setPopperElement] = useState(null) + const [isOpen, setIsOpen] = useState(false) + + const { styles, attributes, update } = usePopper(referenceElement, popperElement, { + placement, + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 8], + }, + }, + ], + }) + + useEffect(() => { + if (isOpen && update) update() + }, [isOpen, update]) + + return ( +
    + + {({ open }) => { + setIsOpen(open) + return children + }} + +
    + ) +} + +type Position = 'left' | 'right' + +type ListboxOptionProps = { + icon?: IconType + iconPosition?: Position + checkIconPosition?: Position +} + +function Option({ + className, + children, + icon: Icon, + iconPosition = 'left', + checkIconPosition = 'right', + ...rest +}: PropsWithChildren>) { + const { multiple } = useContext(ListboxContext) + + const renderIcon = (selected: boolean, position: Position) => { + const iconClassName = selected ? 'text-white' : 'text-gray-100 group-hover:text-gray-50' + + if (selected && checkIconPosition === position) { + return ( + + + + ) + } + + if (Icon && iconPosition === position) { + return ( + + + + ) + } + + return null + } + + return ( + + {({ selected, disabled }) => + multiple ? ( +
    + +
    + ) : ( + + ) + } +
    + ) +} + +export default Object.assign(Listbox, { Button, Options, Option }) diff --git a/libs/design-system/src/lib/Listbox/__snapshots__/Listbox.spec.tsx.snap b/libs/design-system/src/lib/Listbox/__snapshots__/Listbox.spec.tsx.snap new file mode 100644 index 00000000000..c63fbff54f1 --- /dev/null +++ b/libs/design-system/src/lib/Listbox/__snapshots__/Listbox.spec.tsx.snap @@ -0,0 +1,589 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Listbox when closed should render the button 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    + +
    +
    +
    + , + "container":
    +
    + +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Listbox when opened should render the button and the items 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    + , + "container":
    +
    + +
    +
      +
    • + +
    • +
    • + +
    • +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Listbox/index.ts b/libs/design-system/src/lib/Listbox/index.ts new file mode 100644 index 00000000000..dcedd216948 --- /dev/null +++ b/libs/design-system/src/lib/Listbox/index.ts @@ -0,0 +1 @@ +export { default as Listbox } from './Listbox' diff --git a/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.spec.tsx b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.spec.tsx new file mode 100644 index 00000000000..60c6c940f89 --- /dev/null +++ b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.spec.tsx @@ -0,0 +1,10 @@ +import { render } from '@testing-library/react' +import { LoadingPlaceholder } from './' + +describe('LoadingPlaceholder', () => { + it('should render properly', () => { + const component = render() + + expect(component).toMatchSnapshot() + }) +}) diff --git a/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.stories.tsx b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.stories.tsx new file mode 100644 index 00000000000..9a3f2f40e03 --- /dev/null +++ b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.stories.tsx @@ -0,0 +1,11 @@ +import type { Story, Meta } from '@storybook/react' + +import LoadingPlaceholder from './LoadingPlaceholder' + +export default { + title: 'Components/LoadingPlaceholder', + component: LoadingPlaceholder, + parameters: { controls: { exclude: ['className', 'overlayClassName'] } }, +} as Meta + +export const Base: Story = (args) => Content diff --git a/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.tsx b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.tsx new file mode 100644 index 00000000000..b294ee49f50 --- /dev/null +++ b/libs/design-system/src/lib/LoadingPlaceholder/LoadingPlaceholder.tsx @@ -0,0 +1,36 @@ +import type { ReactNode } from 'react' +import classNames from 'classnames' + +export default function LoadingPlaceholder({ + isLoading = true, + children, + className, + overlayClassName, + maxContent = false, + placeholderContent, +}: { + isLoading?: boolean + children?: ReactNode + className?: string + overlayClassName?: string + maxContent?: boolean + placeholderContent?: ReactNode +}): JSX.Element { + return ( +
    + {isLoading && placeholderContent ? placeholderContent : children} + {isLoading && ( + <> +
    +
    + + )} +
    + ) +} diff --git a/libs/design-system/src/lib/LoadingPlaceholder/__snapshots__/LoadingPlaceholder.spec.tsx.snap b/libs/design-system/src/lib/LoadingPlaceholder/__snapshots__/LoadingPlaceholder.spec.tsx.snap new file mode 100644 index 00000000000..42c02ff1f2f --- /dev/null +++ b/libs/design-system/src/lib/LoadingPlaceholder/__snapshots__/LoadingPlaceholder.spec.tsx.snap @@ -0,0 +1,84 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LoadingPlaceholder should render properly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/LoadingPlaceholder/index.ts b/libs/design-system/src/lib/LoadingPlaceholder/index.ts new file mode 100644 index 00000000000..8df19ce0b19 --- /dev/null +++ b/libs/design-system/src/lib/LoadingPlaceholder/index.ts @@ -0,0 +1 @@ +export { default as LoadingPlaceholder } from './LoadingPlaceholder' diff --git a/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.spec.tsx b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.spec.tsx new file mode 100644 index 00000000000..635fbc55967 --- /dev/null +++ b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.spec.tsx @@ -0,0 +1,11 @@ +import { render, screen } from '@testing-library/react' +import { LoadingSpinner } from './' + +describe('LoadingSpinner', () => { + it('should render properly', () => { + const component = render() + + expect(screen.getByRole('img')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) +}) diff --git a/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.stories.tsx b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.stories.tsx new file mode 100644 index 00000000000..b8163b11e89 --- /dev/null +++ b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.stories.tsx @@ -0,0 +1,10 @@ +import type { Story, Meta } from '@storybook/react' + +import LoadingSpinner from './LoadingSpinner' + +export default { + title: 'Components/LoadingSpinner', + component: LoadingSpinner, +} as Meta + +export const Base: Story = () => diff --git a/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.tsx b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.tsx new file mode 100644 index 00000000000..32d7ed4cbc0 --- /dev/null +++ b/libs/design-system/src/lib/LoadingSpinner/LoadingSpinner.tsx @@ -0,0 +1,142 @@ +/* eslint-disable react-hooks/exhaustive-deps */ +import React, { useRef, useState } from 'react' +import classNames from 'classnames' + +export interface LoadingSpinnerProps extends React.SVGAttributes { + variant?: 'primary' | 'secondary' +} + +export default function LoadingSpinner({ + variant = 'primary', + ...rest +}: LoadingSpinnerProps): JSX.Element { + const cyan = variant === 'primary' ? '#4CC9F0' : '#48494B' + const blue = variant === 'primary' ? '#4361EE' : '#48494B' + const purple = variant === 'primary' ? '#7209B7' : '#48494B' + const pink = variant === 'primary' ? '#F72585' : '#48494B' + + const [frame, setFrame] = useState(0) + + const frameRequest = useRef() + const currentFrameStartTime = useRef() + + const animate = (time: number) => { + if (currentFrameStartTime.current != null) { + if (time - currentFrameStartTime.current > 250) { + setFrame((prevFrame) => (prevFrame + 1) % 4) + currentFrameStartTime.current = time + } + } else { + currentFrameStartTime.current = time + } + frameRequest.current = requestAnimationFrame(animate) + } + + React.useEffect(() => { + frameRequest.current = requestAnimationFrame(animate) + return () => { + if (frameRequest.current != null) cancelAnimationFrame(frameRequest.current) + } + }, []) + + return ( + + {/* Gray background cells */} + + + + + + + + + + + + + + {/* Frame 1: left side colored */} + + + + + + + + {/* Frame 2: middle colored */} + + + + + + + + + + {/* Frame 3: right side colored */} + + + + + + + + ) +} diff --git a/libs/design-system/src/lib/LoadingSpinner/__snapshots__/LoadingSpinner.spec.tsx.snap b/libs/design-system/src/lib/LoadingSpinner/__snapshots__/LoadingSpinner.spec.tsx.snap new file mode 100644 index 00000000000..ae91a21daa0 --- /dev/null +++ b/libs/design-system/src/lib/LoadingSpinner/__snapshots__/LoadingSpinner.spec.tsx.snap @@ -0,0 +1,284 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LoadingSpinner should render properly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + , + "container":
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/LoadingSpinner/index.ts b/libs/design-system/src/lib/LoadingSpinner/index.ts new file mode 100644 index 00000000000..bfb1856afa7 --- /dev/null +++ b/libs/design-system/src/lib/LoadingSpinner/index.ts @@ -0,0 +1 @@ +export { default as LoadingSpinner } from './LoadingSpinner' diff --git a/libs/design-system/src/lib/Menu/Menu.spec.tsx b/libs/design-system/src/lib/Menu/Menu.spec.tsx new file mode 100644 index 00000000000..ddfcb7b7e8d --- /dev/null +++ b/libs/design-system/src/lib/Menu/Menu.spec.tsx @@ -0,0 +1,60 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { Menu } from './' + +// Not tested too thoroughly, most logic is covered by Headless UI base +describe('Menu', () => { + describe('when closed', () => { + it('should render the button', () => { + const component = render( + + Click Me + + Option 1 + + + ) + + expect(screen.getByText('Click Me')).toBeVisible() + expect(screen.queryByText('Option 1')).not.toBeVisible() + expect(component).toMatchSnapshot() + }) + }) + + describe('when opened', () => { + it('should render the button and the items', () => { + const component = render( + + Click Me + + Option 1 + Option 2 + + + ) + + fireEvent.click(screen.getByText('Click Me')) + ;['Click Me', 'Option 1', 'Option 2'].forEach((text) => + expect(screen.getByText(text)).toBeVisible() + ) + expect(component).toMatchSnapshot() + }) + }) + + describe('when an option is pressed', () => { + it('should call the `onClick` callback', () => { + const onClickMock = jest.fn() + render( + + Click Me + + Option 1 + + + ) + + fireEvent.click(screen.getByText('Click Me')) + fireEvent.click(screen.getByText('Option 1')) + expect(onClickMock).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/libs/design-system/src/lib/Menu/Menu.stories.tsx b/libs/design-system/src/lib/Menu/Menu.stories.tsx new file mode 100644 index 00000000000..2f3d9eb587b --- /dev/null +++ b/libs/design-system/src/lib/Menu/Menu.stories.tsx @@ -0,0 +1,46 @@ +import type { Story, Meta } from '@storybook/react' +import { + RiArrowDownSLine, + RiCloseCircleLine, + RiDeleteBinLine, + RiInformationLine, +} from 'react-icons/ri' + +import Menu from './Menu' + +export default { + title: 'Components/Menu', + component: Menu, + parameters: { controls: { exclude: ['className', 'as', 'refName'] } }, + argTypes: { + variant: { + control: 'select', + options: ['primary', 'secondary'], + description: 'Variant for Button component', + }, + }, + args: { + variant: 'primary', + }, +} as Meta + +const Template: Story = (args) => ( +
    + + + Click Me + + + }>Option 1 + } destructive={true}> + Option 2 (Destructive) + + } disabled={true}> + Option 3 (Disabled) + + + +
    +) + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Menu/Menu.tsx b/libs/design-system/src/lib/Menu/Menu.tsx new file mode 100644 index 00000000000..eac820248c4 --- /dev/null +++ b/libs/design-system/src/lib/Menu/Menu.tsx @@ -0,0 +1,157 @@ +import React, { createContext, forwardRef, useContext, useEffect, useRef, useState } from 'react' +import type { ComponentProps, PropsWithChildren, Ref, RefObject } from 'react' +import { Menu as HeadlessMenu } from '@headlessui/react' +import type { PopperProps } from 'react-popper' +import { usePopper } from 'react-popper' +import classNames from 'classnames' +import Link from 'next/link' +import { Button as BaseButton } from '../../' + +type ExtractProps = T extends React.ComponentType ? P : T + +const PopoverContext = createContext<{ + referenceElement?: RefObject +}>({}) + +function Menu({ className, ...rest }: ExtractProps) { + const referenceElement = useRef(null) + + return ( + + + + ) +} + +function Button({ + ...rest +}: ExtractProps & ComponentProps) { + const { referenceElement } = useContext(PopoverContext) + + return +} + +type ItemsProps = ExtractProps + +function Items({ + className, + placement = 'bottom-start', + children, + ...rest +}: { placement?: PopperProps['placement'] } & ( + | Omit + | Omit +)) { + const { referenceElement } = useContext(PopoverContext) + const [popperElement, setPopperElement] = useState(null) + const [isOpen, setIsOpen] = useState(false) + + const { styles, attributes, update } = usePopper(referenceElement?.current, popperElement, { + placement, + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 8], + }, + }, + ], + }) + + useEffect(() => { + if (isOpen && update) update() + }, [isOpen, update]) + + return ( +
    setPopperElement(el)} + className={classNames('z-10 absolute min-w-full', className)} + style={styles.popper} + {...attributes.popper} + > + + {(renderProps) => { + setIsOpen(renderProps.open) + return typeof children === 'function' ? children(renderProps) : children + }} + +
    + ) +} + +type ItemProps = PropsWithChildren<{ + as?: T + icon?: React.ReactNode + destructive?: boolean + disabled?: boolean +}> & + React.ComponentPropsWithoutRef + +function Item({ + as, + className, + children, + icon, + destructive = false, + disabled = false, + ...rest +}: ItemProps) { + const InnerComponent = as || 'button' + + return ( + + {({ active, disabled }) => ( + + {icon && ( + + {icon} + + )} + {children} + + )} + + ) +} + +const NextLink = forwardRef( + ( + { + href, + children, + ...rest + }: { href: string } & PropsWithChildren>, + ref: Ref + ) => { + return ( + + {children} + + ) + } +) + +function ItemNextLink(props: Omit, 'as'>) { + return +} + +export default Object.assign(Menu, { Button, Items, Item, ItemNextLink }) diff --git a/libs/design-system/src/lib/Menu/__snapshots__/Menu.spec.tsx.snap b/libs/design-system/src/lib/Menu/__snapshots__/Menu.spec.tsx.snap new file mode 100644 index 00000000000..608c018b2b5 --- /dev/null +++ b/libs/design-system/src/lib/Menu/__snapshots__/Menu.spec.tsx.snap @@ -0,0 +1,311 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Menu when closed should render the button 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    + +
    +
    +
    + , + "container":
    +
    + +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Menu when opened should render the button and the items 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    + +
    +
    +
    + , + "container":
    +
    + +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Menu/index.ts b/libs/design-system/src/lib/Menu/index.ts new file mode 100644 index 00000000000..03d4d6a4092 --- /dev/null +++ b/libs/design-system/src/lib/Menu/index.ts @@ -0,0 +1 @@ +export { default as Menu } from './Menu' diff --git a/libs/design-system/src/lib/Popover/Popover.spec.tsx b/libs/design-system/src/lib/Popover/Popover.spec.tsx new file mode 100644 index 00000000000..10659d215fe --- /dev/null +++ b/libs/design-system/src/lib/Popover/Popover.spec.tsx @@ -0,0 +1,36 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { Popover } from './' + +// Not tested too thoroughly, most logic is covered by Headless UI base +describe('Popover', () => { + describe('when closed', () => { + it('should render the button', () => { + const component = render( + + Click Me + Content + + ) + + expect(screen.getByText('Click Me')).toBeVisible() + expect(screen.queryByText('Content')).not.toBeVisible() + expect(component).toMatchSnapshot() + }) + }) + + describe('when opened', () => { + it('should render the button and the panel', () => { + const component = render( + + Click Me + Content + + ) + + fireEvent.click(screen.getByText('Click Me')) + expect(screen.getByText('Click Me')).toBeVisible() + expect(screen.getByText('Content')).toBeVisible() + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Popover/Popover.stories.tsx b/libs/design-system/src/lib/Popover/Popover.stories.tsx new file mode 100644 index 00000000000..5de9ef5fe4c --- /dev/null +++ b/libs/design-system/src/lib/Popover/Popover.stories.tsx @@ -0,0 +1,20 @@ +import type { Story, Meta } from '@storybook/react' + +import Popover from './Popover' + +export default { + title: 'Components/Popover', + component: Popover, + parameters: { controls: { exclude: ['className'] } }, +} as Meta + +const Template: Story = (args) => ( +
    + + Click Me + Panel Content + +
    +) + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Popover/Popover.tsx b/libs/design-system/src/lib/Popover/Popover.tsx new file mode 100644 index 00000000000..7817a50c79e --- /dev/null +++ b/libs/design-system/src/lib/Popover/Popover.tsx @@ -0,0 +1,114 @@ +import React, { createContext, useContext, useEffect, useRef, useState } from 'react' +import type { ComponentProps, RefObject } from 'react' +import type { PopperProps } from 'react-popper' +import { Popover as HeadlessPopover } from '@headlessui/react' +import { usePopper } from 'react-popper' +import classNames from 'classnames' +import { Button as BaseButton } from '../../' +import { RiArrowDownSFill } from 'react-icons/ri' + +type ExtractProps = T extends React.ComponentType ? P : T + +const PopoverContext = createContext<{ + referenceElement?: RefObject +}>({}) + +function Popover({ className, ...rest }: ExtractProps) { + const referenceElement = useRef(null) + + return ( + + + + ) +} + +function Button({ + variant, + hideRightIcon = false, + children, + ...rest +}: { hideRightIcon?: boolean } & ExtractProps & + ComponentProps) { + const { referenceElement } = useContext(PopoverContext) + + return ( + + {children} + {!hideRightIcon && ( + + )} + + ) +} + +function PanelButton( + props: ExtractProps & ComponentProps +) { + return +} + +type PopoverPanelProps = ExtractProps + +function Panel({ + children, + className, + placement = 'bottom-start', + ...rest +}: (Omit | Omit) & { + placement?: PopperProps['placement'] +}) { + const { referenceElement } = useContext(PopoverContext) + const [popperElement, setPopperElement] = useState(null) + const [isOpen, setIsOpen] = useState(false) + + const { styles, attributes, update } = usePopper(referenceElement?.current, popperElement, { + placement, + modifiers: [ + { + name: 'offset', + options: { + offset: [0, 8], + }, + }, + ], + }) + + useEffect(() => { + if (isOpen && update) update() + }, [isOpen, update]) + + return ( +
    setPopperElement(el)} + className={classNames('z-20 absolute min-w-full', className)} + style={styles.popper} + {...attributes.popper} + > + + {(renderProps) => { + setIsOpen(renderProps.open) + return typeof children === 'function' ? children(renderProps) : children + }} + +
    + ) +} + +export default Object.assign(Popover, { Button, Panel, PanelButton }) diff --git a/libs/design-system/src/lib/Popover/__snapshots__/Popover.spec.tsx.snap b/libs/design-system/src/lib/Popover/__snapshots__/Popover.spec.tsx.snap new file mode 100644 index 00000000000..07bd9f0c79f --- /dev/null +++ b/libs/design-system/src/lib/Popover/__snapshots__/Popover.spec.tsx.snap @@ -0,0 +1,345 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Popover when closed should render the button 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    + +
    +
    +
    + , + "container":
    +
    + +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Popover when opened should render the button and the panel 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + +
    +
    + Content +
    +
    +
    +
    + , + "container":
    +
    + +
    +
    + Content +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Popover/index.ts b/libs/design-system/src/lib/Popover/index.ts new file mode 100644 index 00000000000..c6e06a314a2 --- /dev/null +++ b/libs/design-system/src/lib/Popover/index.ts @@ -0,0 +1 @@ +export { default as Popover } from './Popover' diff --git a/libs/design-system/src/lib/RTEditor/RTEditor.tsx b/libs/design-system/src/lib/RTEditor/RTEditor.tsx new file mode 100644 index 00000000000..73cd907d344 --- /dev/null +++ b/libs/design-system/src/lib/RTEditor/RTEditor.tsx @@ -0,0 +1,98 @@ +import type { PropsWithChildren } from 'react' +import cn from 'classnames' +import { EditorContent, type Editor } from '@tiptap/react' +import { + RiItalic, + RiBold, + RiStrikethrough, + RiListOrdered, + RiListUnordered, + RiDoubleQuotesR, + RiHeading, +} from 'react-icons/ri' + +export type RTEditorProps = { + editor: Editor | null + className?: string + hideControls?: boolean +} + +export function RTEditor({ hideControls = false, editor, className }: RTEditorProps) { + return ( + <> + {!hideControls && } + + + ) +} + +function RichTextEditorMenuBar({ editor }: { editor: Editor | null }) { + if (!editor) return null + + return ( +
    +
    + + + + + + + + + +
    +
    + + + + + + +
    +
    + + + + + + +
    +
    + ) +} + +function MenuButton({ + editor, + mark, + attributes, + tooltip, + children, +}: PropsWithChildren<{ editor: Editor; mark: string; attributes?: any; tooltip?: string }>) { + const method = `toggle${mark[0].toUpperCase()}${mark.slice(1)}` + return ( + + ) +} diff --git a/libs/design-system/src/lib/RTEditor/index.ts b/libs/design-system/src/lib/RTEditor/index.ts new file mode 100644 index 00000000000..6e68fb22782 --- /dev/null +++ b/libs/design-system/src/lib/RTEditor/index.ts @@ -0,0 +1 @@ +export * from './RTEditor' diff --git a/libs/design-system/src/lib/RadioGroup/RadioGroup.stories.tsx b/libs/design-system/src/lib/RadioGroup/RadioGroup.stories.tsx new file mode 100644 index 00000000000..2e1dfa5e226 --- /dev/null +++ b/libs/design-system/src/lib/RadioGroup/RadioGroup.stories.tsx @@ -0,0 +1,35 @@ +import type { Story, Meta } from '@storybook/react' +import { useState } from 'react' + +import RadioGroup from './RadioGroup' + +export default { + title: 'Components/RadioGroup', + component: RadioGroup, + argTypes: {}, + args: {}, +} as Meta + +export const Base: Story = (args) => { + const [value, setValue] = useState('option-1') + + return ( +
    + + Selections + + + Option 1 + + + + Option 2 + + + + Option 3 + + +
    + ) +} diff --git a/libs/design-system/src/lib/RadioGroup/RadioGroup.tsx b/libs/design-system/src/lib/RadioGroup/RadioGroup.tsx new file mode 100644 index 00000000000..965354fab71 --- /dev/null +++ b/libs/design-system/src/lib/RadioGroup/RadioGroup.tsx @@ -0,0 +1,40 @@ +import { RadioGroup as HeadlessRadio } from '@headlessui/react' +import classNames from 'classnames' +import type { PropsWithChildren } from 'react' + +type ExtractProps = T extends React.ComponentType ? P : T + +function RadioGroup(props: ExtractProps) { + return +} + +function RadioOption({ + children, + ...rest +}: PropsWithChildren>) { + return ( + + {({ checked }) => ( +
    + {/* Circle centered inside a circle */} + + + + {children} +
    + )} +
    + ) +} + +export default Object.assign(RadioGroup, { Label: HeadlessRadio.Label, Option: RadioOption }) diff --git a/libs/design-system/src/lib/RadioGroup/index.ts b/libs/design-system/src/lib/RadioGroup/index.ts new file mode 100644 index 00000000000..46aab333972 --- /dev/null +++ b/libs/design-system/src/lib/RadioGroup/index.ts @@ -0,0 +1 @@ +export { default as RadioGroup } from './RadioGroup' diff --git a/libs/design-system/src/lib/Slider/Slider.spec.tsx b/libs/design-system/src/lib/Slider/Slider.spec.tsx new file mode 100644 index 00000000000..33c2d018812 --- /dev/null +++ b/libs/design-system/src/lib/Slider/Slider.spec.tsx @@ -0,0 +1,19 @@ +import { render, screen } from '@testing-library/react' +import { Slider } from '.' + +describe('Slider', () => { + // Limited test cases we can do here without implementing a rather complex drag-n-drop test case + it('should render correctly', () => { + const onChangeMock = jest.fn() + const component = render() + + const handle = screen.getByTestId('handle-0') + expect(handle).toBeInTheDocument() + + // We only provided 1 handle in the `initialValue` array, so this shouldn't be there + expect(screen.queryByTestId('handle-1')).not.toBeInTheDocument() + + expect(screen.getByTestId('slider-track')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) +}) diff --git a/libs/design-system/src/lib/Slider/Slider.stories.tsx b/libs/design-system/src/lib/Slider/Slider.stories.tsx new file mode 100644 index 00000000000..58f46fd504d --- /dev/null +++ b/libs/design-system/src/lib/Slider/Slider.stories.tsx @@ -0,0 +1,17 @@ +import type { Story, Meta } from '@storybook/react' + +import Slider from './Slider' + +export default { + title: 'Components/Slider', + argTypes: {}, + args: {}, +} as Meta + +export const Base: Story = (_args) => ( +
    +
    + console.log(values)} /> +
    +
    +) diff --git a/libs/design-system/src/lib/Slider/Slider.tsx b/libs/design-system/src/lib/Slider/Slider.tsx new file mode 100644 index 00000000000..a10df4e4e53 --- /dev/null +++ b/libs/design-system/src/lib/Slider/Slider.tsx @@ -0,0 +1,100 @@ +import type { RangerOptions } from 'react-ranger' +import { useRanger } from 'react-ranger' +import classNames from 'classnames' +import { useState } from 'react' +import isEqual from 'lodash/isEqual' + +const SliderHandleClassNames = Object.freeze({ + small: 'w-2 h-2 rounded-lg', + default: 'w-3 h-3 rounded-lg', + large: 'w-4 h-4 rounded-lg', +}) + +const SliderTrackClassNames = Object.freeze({ + small: 'h-1 rounded-lg', + default: 'h-1.5 rounded-lg', + large: 'h-2 rounded-lg', +}) + +const SliderVariantColor = Object.freeze({ + default: 'bg-cyan', +}) + +export type SliderProps = { + variant?: 'default' + size?: 'small' | 'default' | 'large' + className?: string + initialValue: number[] + updateOnDrag?: boolean + onChange: (values: number[]) => void + rangerOptions?: Partial> +} + +export default function Slider({ + variant = 'default', + size = 'default', + initialValue = [0], + updateOnDrag = false, + onChange, + className, + rangerOptions, +}: SliderProps) { + const [internalValues, setInternalValues] = useState(initialValue) + const [isDragging, setIsDragging] = useState(false) + + const { getTrackProps, segments, handles } = useRanger({ + values: internalValues, + onChange: (values: number[]) => { + onChange(values) + setIsDragging(false) + }, + min: rangerOptions?.min ?? 0, + max: rangerOptions?.max ?? 100, + stepSize: rangerOptions?.stepSize ?? 1, + onDrag: (values: number[]) => { + setInternalValues(values) + + // Possible react-ranger bug: seems to fire even after release, so use this workaround + if (!isEqual(values, internalValues)) { + setIsDragging(true) + } + + if (updateOnDrag) { + onChange(values) + } + }, + }) + + return ( +
    +
    +
    + + {handles.map(({ getHandleProps }) => { + const handleProps = getHandleProps({ + className: classNames( + SliderVariantColor[variant], + SliderHandleClassNames[size], + isDragging ? 'cursor-grabbing' : 'cursor-grab' + ), + }) + + return
    + })} +
    +
    + ) +} diff --git a/libs/design-system/src/lib/Slider/__snapshots__/Slider.spec.tsx.snap b/libs/design-system/src/lib/Slider/__snapshots__/Slider.spec.tsx.snap new file mode 100644 index 00000000000..3d5c2e7fb5e --- /dev/null +++ b/libs/design-system/src/lib/Slider/__snapshots__/Slider.spec.tsx.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Slider should render correctly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Slider/index.ts b/libs/design-system/src/lib/Slider/index.ts new file mode 100644 index 00000000000..a3d87b36589 --- /dev/null +++ b/libs/design-system/src/lib/Slider/index.ts @@ -0,0 +1,2 @@ +export { default as Slider } from './Slider' +export type { SliderProps } from './Slider' diff --git a/libs/design-system/src/lib/Step/Step.spec.tsx b/libs/design-system/src/lib/Step/Step.spec.tsx new file mode 100644 index 00000000000..fa7c7ececbf --- /dev/null +++ b/libs/design-system/src/lib/Step/Step.spec.tsx @@ -0,0 +1,102 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { useState } from 'react' +import { Step } from '.' + +describe('Steps', () => { + describe('when linear', () => { + it('should render correctly', () => { + const component = render( + + + Step 1 + Step 2 + + + Content 1 + Content 2 + + + ) + + expect(screen.getByText('Step 1')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + + it('should not allow navigation', () => { + render( + + + Step 1 + Step 2 + + + Content 1 + Content 2 + + + ) + + // Content 1 visible, Content 2 hidden + expect(screen.getByText('Content 1')).toBeInTheDocument() + expect(screen.queryByText('Content 2')).not.toBeInTheDocument() + + fireEvent.click(screen.getByText('Step 2')) + + // Content 1 visible, content 2 hidden + expect(screen.getByText('Content 1')).toBeInTheDocument() + expect(screen.queryByText('Content 2')).not.toBeInTheDocument() + }) + }) + + describe('when non-linear', () => { + it('should render correctly when non-linear', () => { + const component = render( + + + Step 1 + Step 2 + + + Content 1 + Content 2 + + + ) + + expect(screen.getByText('Step 1')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + + it('should allow navigation', () => { + const Component = () => { + const [currentStep, setCurrentStep] = useState(0) + return ( + + + Step 1 + Step 2 + + + Content 1 + Content 2 + + + ) + } + + render() + + // Content 1 visible, Content 2 hidden + expect(screen.getByText('Content 1')).toBeInTheDocument() + expect(screen.queryByText('Content 2')).not.toBeInTheDocument() + + fireEvent.click(screen.getByText('Step 2')) + + // Content 2 visible, content 1 hidden + expect(screen.getByText('Content 2')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).not.toBeInTheDocument() + }) + }) +}) diff --git a/libs/design-system/src/lib/Step/Step.stories.tsx b/libs/design-system/src/lib/Step/Step.stories.tsx new file mode 100644 index 00000000000..2dbebd9f219 --- /dev/null +++ b/libs/design-system/src/lib/Step/Step.stories.tsx @@ -0,0 +1,82 @@ +import type { Story, Meta } from '@storybook/react' +import { useState } from 'react' +import { Button } from '../Button' + +import Step from './Step' + +export default { + title: 'Components/Steps', +} as Meta + +export const Base: Story = () => { + const [currentStep, setCurrentStep] = useState(0) + + return ( + <> + + + Step 1 + Step 2 + Step 3 + + + Step 1 Content + Step 2 Content + Step 3 Content + + +
    + + +
    + + ) +} + +export const NonLinear: Story = () => { + const [currentStep, setCurrentStep] = useState(0) + + return ( + + + Step 1 + Step 2 + Step 3 + + + Step 1 Content + Step 2 Content + Step 3 Content + + + ) +} + +export const StepStatuses: Story = () => { + return ( + + + Step 1 + Step 2 + Step 3 + Step 4 + + + Step 1 Content + Step 2 Content + Step 3 Content + + + ) +} diff --git a/libs/design-system/src/lib/Step/Step.tsx b/libs/design-system/src/lib/Step/Step.tsx new file mode 100644 index 00000000000..08a3b2b9dcc --- /dev/null +++ b/libs/design-system/src/lib/Step/Step.tsx @@ -0,0 +1,160 @@ +import { Tab as HeadlessTab } from '@headlessui/react' +import classNames from 'classnames' +import type { PropsWithChildren } from 'react' +import { useContext, useReducer, useRef, useEffect } from 'react' +import React, { createContext } from 'react' +import { RiCheckFill, RiCloseFill } from 'react-icons/ri' + +type ExtractProps = T extends React.ComponentType ? P : T + +const StepGroupContext = createContext({ + linear: true, + groupComplete: false, + selectedIndex: 0, + steps: [] as HTMLElement[], + registerStep: (element: HTMLElement) => { + console.warn('Step registration failed', element) + }, +}) + +function Step({ + status: statusProp, + className, + children, + ...rest +}: { status?: 'complete' | 'incomplete' | 'error'; disabled?: boolean } & ExtractProps< + typeof HeadlessTab +>): JSX.Element { + const { linear, selectedIndex, steps, registerStep, groupComplete } = + useContext(StepGroupContext) + const ref = useRef(null) + + useEffect(() => { + if (ref.current) { + registerStep(ref.current) + } + }, [registerStep]) + + const index = ref.current ? steps.indexOf(ref.current) : -1 + + const status = statusProp ?? (linear && index < selectedIndex ? 'complete' : 'incomplete') + + return ( + + classNames( + className, + 'flex items-center rounded-md font-medium text-base focus:outline-none', + !linear && 'focus:ring-2 focus:ring-gray-400 focus:ring-opacity-60', + groupComplete + ? 'text-teal' + : selected + ? 'text-cyan' + : { + complete: 'text-teal', + incomplete: 'text-white', + error: 'text-gray-100', + }[status] + ) + } + {...(rest as any)} + > + {({ selected }) => ( + <> + {index > 0 &&
    } + + {groupComplete ? ( + + ) : ( + { + complete: , + incomplete: index + 1, + error: , + }[status] + )} + + {children} + + )} + + ) +} + +function registerStepReducer(state: HTMLElement[], stepElement: HTMLElement) { + if (!state.includes(stepElement)) { + return [...state, stepElement] + } + + return state +} + +function Group({ + linear = true, + complete = false, + currentStep, + children, + onChange, + ...rest +}: { currentStep: number; linear?: boolean; complete?: boolean } & PropsWithChildren< + Omit, 'selectedIndex' | 'children'> +>): JSX.Element { + const [steps, registerStep] = useReducer(registerStepReducer, []) + + return ( + { + if (!linear) onChange(...args) + }} + {...rest} + > + {({ selectedIndex }) => ( + + {children} + + )} + + ) +} + +function List({ className, ...rest }: ExtractProps): JSX.Element { + return ( + + ) +} + +function Panels({ className, ...rest }: ExtractProps): JSX.Element { + return +} + +function Panel({ className, ...rest }: ExtractProps): JSX.Element { + return +} + +export default Object.assign(Step, { Group, List, Panels, Panel }) diff --git a/libs/design-system/src/lib/Step/__snapshots__/Step.spec.tsx.snap b/libs/design-system/src/lib/Step/__snapshots__/Step.spec.tsx.snap new file mode 100644 index 00000000000..1d9614af42f --- /dev/null +++ b/libs/design-system/src/lib/Step/__snapshots__/Step.spec.tsx.snap @@ -0,0 +1,379 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Steps when linear should render correctly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + +
    +
    + Content 1 +
    + +
    +
    + , + "container":
    +
    + + +
    +
    + Content 1 +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Steps when non-linear should render correctly when non-linear 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + +
    +
    +
    + Content 1 +
    + +
    +
    + , + "container":
    +
    + + +
    +
    +
    + Content 1 +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Step/index.ts b/libs/design-system/src/lib/Step/index.ts new file mode 100644 index 00000000000..3de34752b5b --- /dev/null +++ b/libs/design-system/src/lib/Step/index.ts @@ -0,0 +1 @@ +export { default as Step } from './Step' diff --git a/libs/design-system/src/lib/Tab/Tab.spec.tsx b/libs/design-system/src/lib/Tab/Tab.spec.tsx new file mode 100644 index 00000000000..7ae3055e257 --- /dev/null +++ b/libs/design-system/src/lib/Tab/Tab.spec.tsx @@ -0,0 +1,51 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { Tab } from './' + +// Not tested too thoroughly, most logic is covered by Headless UI base +describe('Tab', () => { + it('should render correctly', () => { + const component = render( + + + Tab 1 + Tab 2 + + + Content 1 + Content 2 + + + ) + + expect(screen.getByText('Tab 1')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + + describe('when toggled', () => { + it('should render the correct panel', () => { + render( + + + Tab 1 + Tab 2 + + + Content 1 + Content 2 + + + ) + + // Content 1 visible, Content 2 hidden + expect(screen.getByText('Content 1')).toBeInTheDocument() + expect(screen.queryByText('Content 2')).not.toBeInTheDocument() + + fireEvent.click(screen.getByText('Tab 2')) + + // Content 2 visible, content 1 hidden + expect(screen.getByText('Content 2')).toBeInTheDocument() + expect(screen.queryByText('Content 1')).not.toBeInTheDocument() + }) + }) +}) diff --git a/libs/design-system/src/lib/Tab/Tab.stories.tsx b/libs/design-system/src/lib/Tab/Tab.stories.tsx new file mode 100644 index 00000000000..e11154f1b68 --- /dev/null +++ b/libs/design-system/src/lib/Tab/Tab.stories.tsx @@ -0,0 +1,45 @@ +import type { Story, Meta } from '@storybook/react' +import { RiLineChartLine, RiBarChartFill, RiPieChartFill } from 'react-icons/ri' + +import Tab from './Tab' + +export default { + title: 'Components/Tabs', + argTypes: {}, + args: {}, +} as Meta + +export const Base: Story = (_args) => ( + + + Tab 1 + Tab 2 + Tab 3 + + +) + +export const WithIcons: Story = (_args) => ( + + + }>Line + }>Bar + }>Pie + + +) + +export const WithPanels: Story = (_args) => ( + + + Tab 1 + Tab 2 + Tab 3 + + + Content 1 + Content 2 + Content 3 + + +) diff --git a/libs/design-system/src/lib/Tab/Tab.tsx b/libs/design-system/src/lib/Tab/Tab.tsx new file mode 100644 index 00000000000..d06c0333a20 --- /dev/null +++ b/libs/design-system/src/lib/Tab/Tab.tsx @@ -0,0 +1,57 @@ +import type { PropsWithChildren } from 'react' +import { Tab as HeadlessTab } from '@headlessui/react' +import classNames from 'classnames' + +type ExtractProps = T extends React.ComponentType ? P : T + +function Tab({ + className, + children, + icon, + ...rest +}: { icon?: React.ReactNode } & PropsWithChildren>): JSX.Element { + return ( + + classNames( + className, + 'grow flex items-center justify-center text-base py-1 px-4 rounded focus:outline-none focus:ring-2 focus:ring-opacity-60', + 'focus:ring-gray-400', + selected + ? 'bg-gray-400 hover:bg-gray-300 text-white shadow' + : 'hover:bg-gray-600 text-gray-100' + ) + } + {...(rest as any)} + > + {icon && {icon}} + {children} + + ) +} + +function Group(props: ExtractProps): JSX.Element { + return +} + +function List({ className, ...rest }: ExtractProps): JSX.Element { + return ( + + ) +} + +function Panels({ className, ...rest }: ExtractProps): JSX.Element { + return +} + +function Panel({ className, ...rest }: ExtractProps): JSX.Element { + return +} + +export default Object.assign(Tab, { Group, List, Panels, Panel }) diff --git a/libs/design-system/src/lib/Tab/__snapshots__/Tab.spec.tsx.snap b/libs/design-system/src/lib/Tab/__snapshots__/Tab.spec.tsx.snap new file mode 100644 index 00000000000..fa2d5401b96 --- /dev/null +++ b/libs/design-system/src/lib/Tab/__snapshots__/Tab.spec.tsx.snap @@ -0,0 +1,166 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tab should render correctly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + +
    +
    +
    + Content 1 +
    + +
    +
    + , + "container":
    +
    + + +
    +
    +
    + Content 1 +
    + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Tab/index.ts b/libs/design-system/src/lib/Tab/index.ts new file mode 100644 index 00000000000..6f1134b7214 --- /dev/null +++ b/libs/design-system/src/lib/Tab/index.ts @@ -0,0 +1 @@ +export { default as Tab } from './Tab' diff --git a/libs/design-system/src/lib/Takeover/Takeover.spec.tsx b/libs/design-system/src/lib/Takeover/Takeover.spec.tsx new file mode 100644 index 00000000000..8e59086aef8 --- /dev/null +++ b/libs/design-system/src/lib/Takeover/Takeover.spec.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react' +import { Takeover } from './' + +describe('Takeover', () => { + describe('when rendered with `open` prop true', () => { + it('should display the contents', () => { + const onToggleMock = jest.fn() + const component = render( + + Takeover content + + ) + + expect(screen.getByText('Takeover content')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Takeover/Takeover.stories.tsx b/libs/design-system/src/lib/Takeover/Takeover.stories.tsx new file mode 100644 index 00000000000..915bd4f17c8 --- /dev/null +++ b/libs/design-system/src/lib/Takeover/Takeover.stories.tsx @@ -0,0 +1,27 @@ +import type { Story, Meta } from '@storybook/react' +import type { TakeoverProps } from './Takeover' +import * as React from 'react' + +import Takeover from './Takeover' +import { Button } from '../../' + +export default { + title: 'Components/Takeover', + component: Takeover, + parameters: { + controls: { include: ['as'] }, + }, +} as Meta + +export const Base: Story = (args) => { + const [isOpen, setIsOpen] = React.useState(false) + + return ( +
    + + setIsOpen(false)}> +
    Press ESC to close
    +
    +
    + ) +} diff --git a/libs/design-system/src/lib/Takeover/Takeover.tsx b/libs/design-system/src/lib/Takeover/Takeover.tsx new file mode 100644 index 00000000000..1bb34d20be8 --- /dev/null +++ b/libs/design-system/src/lib/Takeover/Takeover.tsx @@ -0,0 +1,49 @@ +import type { RefObject } from 'react' +import { Fragment } from 'react' +import { Dialog, Transition } from '@headlessui/react' + +type ExtractProps = T extends React.ComponentType ? P : T + +export type TakeoverProps = ExtractProps & { + scrollContainerRef?: RefObject +} + +export default function Takeover({ + open, + onClose, + children, + scrollContainerRef, + ...rest +}: TakeoverProps) { + return ( + + +
    + + {children} + +
    +
    +
    + ) +} diff --git a/libs/design-system/src/lib/Takeover/__snapshots__/Takeover.spec.tsx.snap b/libs/design-system/src/lib/Takeover/__snapshots__/Takeover.spec.tsx.snap new file mode 100644 index 00000000000..437cf85b2e0 --- /dev/null +++ b/libs/design-system/src/lib/Takeover/__snapshots__/Takeover.spec.tsx.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Takeover when rendered with \`open\` prop true should display the contents 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Takeover/index.ts b/libs/design-system/src/lib/Takeover/index.ts new file mode 100644 index 00000000000..d97403203c9 --- /dev/null +++ b/libs/design-system/src/lib/Takeover/index.ts @@ -0,0 +1 @@ +export { default as Takeover } from './Takeover' diff --git a/libs/design-system/src/lib/Toast/Toast.spec.tsx b/libs/design-system/src/lib/Toast/Toast.spec.tsx new file mode 100644 index 00000000000..a0dfafe8303 --- /dev/null +++ b/libs/design-system/src/lib/Toast/Toast.spec.tsx @@ -0,0 +1,20 @@ +import type { ToastVariant } from '.' +import { render, screen } from '@testing-library/react' +import { Toast } from '.' + +describe('Toast', () => { + describe('when rendered with text', () => { + it('should display the text', () => { + render(Hello, World!) + + expect(screen.getByText('Hello, World!')).toBeInTheDocument() + }) + }) + + const variants: ToastVariant[] = ['info', 'success', 'error'] + + test.each(variants)('should render properly as the %s variant', (variant) => { + const component = render(Hello, World!) + expect(component).toMatchSnapshot() + }) +}) diff --git a/libs/design-system/src/lib/Toast/Toast.stories.tsx b/libs/design-system/src/lib/Toast/Toast.stories.tsx new file mode 100644 index 00000000000..bd83b2ece1f --- /dev/null +++ b/libs/design-system/src/lib/Toast/Toast.stories.tsx @@ -0,0 +1,36 @@ +import type { Story, Meta } from '@storybook/react' +import type { ToastProps } from './Toast' + +import Toast from './Toast' + +export default { + title: 'Components/Toast', + component: Toast, + parameters: { controls: { exclude: ['className', 'onClick'] } }, + argTypes: { + children: { + control: 'text', + description: 'Toast content', + }, + variant: { + control: { type: 'select' }, + description: 'Toast variant', + }, + }, + args: { + children: 'Toast message', + }, +} as Meta + +const Template: Story = (args) => + +export const Base = Template.bind({}) + +export const Info = Template.bind({}) +Info.args = { variant: 'info' } + +export const Success = Template.bind({}) +Success.args = { variant: 'success' } + +export const Error = Template.bind({}) +Error.args = { variant: 'error' } diff --git a/libs/design-system/src/lib/Toast/Toast.tsx b/libs/design-system/src/lib/Toast/Toast.tsx new file mode 100644 index 00000000000..849a12623c0 --- /dev/null +++ b/libs/design-system/src/lib/Toast/Toast.tsx @@ -0,0 +1,58 @@ +import type { ReactNode, HTMLAttributes } from 'react' +import classNames from 'classnames' +import { + RiInformationLine as IconInfo, + RiCheckboxCircleLine as IconSuccess, + RiErrorWarningLine as IconError, +} from 'react-icons/ri' + +const ToastVariants = Object.freeze({ + info: { + className: 'text-white bg-gray-500', + icon: IconInfo, + role: 'status', + }, + success: { + className: 'text-black bg-teal', + icon: IconSuccess, + role: 'status', + }, + error: { + className: 'text-black bg-red', + icon: IconError, + role: 'alert', + }, +}) + +export type ToastVariant = keyof typeof ToastVariants + +export interface ToastProps extends HTMLAttributes { + variant?: ToastVariant + + onClick?: () => void + + className?: string + + children?: ReactNode +} + +function Toast({ variant = 'info', children, className, ...rest }: ToastProps): JSX.Element { + const combinedClassName = classNames( + className, + ToastVariants[variant].className, + 'flex items-center py-2 px-4 rounded shadow-md' + ) + + const Icon = ToastVariants[variant].icon + + return ( +
    + + + {children} + +
    + ) +} + +export default Toast diff --git a/libs/design-system/src/lib/Toast/__snapshots__/Toast.spec.tsx.snap b/libs/design-system/src/lib/Toast/__snapshots__/Toast.spec.tsx.snap new file mode 100644 index 00000000000..5cf1c8e3bd8 --- /dev/null +++ b/libs/design-system/src/lib/Toast/__snapshots__/Toast.spec.tsx.snap @@ -0,0 +1,370 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Toast should render properly as the error variant 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + + + Hello, World! + +
    +
    + , + "container":
    +
    + + + + + + + + Hello, World! + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Toast should render properly as the info variant 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + + + Hello, World! + +
    +
    + , + "container":
    +
    + + + + + + + + Hello, World! + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Toast should render properly as the success variant 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + + + Hello, World! + +
    +
    + , + "container":
    +
    + + + + + + + + Hello, World! + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Toast/index.ts b/libs/design-system/src/lib/Toast/index.ts new file mode 100644 index 00000000000..433fbaeb264 --- /dev/null +++ b/libs/design-system/src/lib/Toast/index.ts @@ -0,0 +1,2 @@ +export { default as Toast } from './Toast' +export type { ToastProps, ToastVariant } from './Toast' diff --git a/libs/design-system/src/lib/Toggle/Toggle.spec.tsx b/libs/design-system/src/lib/Toggle/Toggle.spec.tsx new file mode 100644 index 00000000000..b5c63584e01 --- /dev/null +++ b/libs/design-system/src/lib/Toggle/Toggle.spec.tsx @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { fireEvent, render, screen } from '@testing-library/react' +import { Toggle } from '.' + +describe('Toggle', () => { + describe('when rendered with a screenreader label', () => { + it('should display the label to screenreaders', () => { + const component = render( {}} screenReaderLabel="SRLabel" />) + + expect(screen.getByText('SRLabel')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when pressed', () => { + it('should call the `onClick` callback', () => { + const onClickMock = jest.fn() + render() + + fireEvent.click(screen.getByRole('switch')) + expect(onClickMock).toHaveBeenCalledTimes(1) + }) + }) + + describe('when toggle is checked', () => { + it('should have the toggle disc moved to the right', () => { + const component = render( + {}} checked /> + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when passing a className', () => { + it('should merge them and show all classNames', () => { + render( + {}} + className="test-classname" + /> + ) + + expect(screen.getByRole('switch')).toHaveClass('test-classname') + // className cursor-pointer exists on the toggle by default + expect(screen.getByRole('switch')).toHaveClass('cursor-pointer') + }) + }) +}) diff --git a/libs/design-system/src/lib/Toggle/Toggle.stories.tsx b/libs/design-system/src/lib/Toggle/Toggle.stories.tsx new file mode 100644 index 00000000000..f3c71d86325 --- /dev/null +++ b/libs/design-system/src/lib/Toggle/Toggle.stories.tsx @@ -0,0 +1,22 @@ +import type { Story, Meta } from '@storybook/react' +import type { ToggleProps } from './Toggle' +import { useState } from 'react' + +import Toggle from './Toggle' + +export default { + title: 'Components/Toggle', + component: Toggle, + parameters: { controls: { exclude: ['className', 'onClick'] } }, + args: { + screenReaderLabel: 'Toggle something', + }, +} as Meta + +const Template: Story = (args) => { + const [enabled, setEnabled] = useState(args.checked) + + return setEnabled(checked)} /> +} + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/Toggle/Toggle.tsx b/libs/design-system/src/lib/Toggle/Toggle.tsx new file mode 100644 index 00000000000..2cfa9da037b --- /dev/null +++ b/libs/design-system/src/lib/Toggle/Toggle.tsx @@ -0,0 +1,62 @@ +import classNames from 'classnames' +import { Switch } from '@headlessui/react' + +const ToggleSizes = Object.freeze({ + small: { + outer: 'h-6 w-11', + inner: 'h-4 w-4', + innerChecked: 'translate-x-5', + }, + medium: { + outer: 'h-8 w-14', + inner: 'h-6 w-6', + innerChecked: 'translate-x-6', + }, +}) + +export type ToggleSize = keyof typeof ToggleSizes + +export interface ToggleProps { + onChange(checked: boolean): void + checked?: boolean + size?: ToggleSize + screenReaderLabel?: string + className?: string + disabled?: boolean +} + +export default function Toggle({ + checked = false, + disabled = false, + size = 'medium', + className, + screenReaderLabel, + ...rest +}: ToggleProps) { + return ( + + {screenReaderLabel && {screenReaderLabel}} + + + ) +} diff --git a/libs/design-system/src/lib/Toggle/__snapshots__/Toggle.spec.tsx.snap b/libs/design-system/src/lib/Toggle/__snapshots__/Toggle.spec.tsx.snap new file mode 100644 index 00000000000..c9086f655ff --- /dev/null +++ b/libs/design-system/src/lib/Toggle/__snapshots__/Toggle.spec.tsx.snap @@ -0,0 +1,199 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Toggle when rendered with a screenreader label should display the label to screenreaders 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Toggle when toggle is checked should have the toggle disc moved to the right 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Toggle/index.ts b/libs/design-system/src/lib/Toggle/index.ts new file mode 100644 index 00000000000..03c9f130f91 --- /dev/null +++ b/libs/design-system/src/lib/Toggle/index.ts @@ -0,0 +1,2 @@ +export { default as Toggle } from './Toggle' +export type { ToggleProps } from './Toggle' diff --git a/libs/design-system/src/lib/Tooltip/Tooltip.spec.tsx b/libs/design-system/src/lib/Tooltip/Tooltip.spec.tsx new file mode 100644 index 00000000000..73de88914c1 --- /dev/null +++ b/libs/design-system/src/lib/Tooltip/Tooltip.spec.tsx @@ -0,0 +1,47 @@ +import { render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { act } from 'react-dom/test-utils' +import { Tooltip } from '.' + +describe('Tooltip', () => { + describe('when rendered', () => { + it('should show tooltip content on tooltip trigger `focus`', async () => { + let component + await act(async () => { + component = render( + + + + ) + }) + + const tooltipTrrigger = screen.getByTestId('tooltip-trigger') + + tooltipTrrigger.focus() + await waitFor(() => { + expect(screen.getByText(/^tooltip content$/i)).toBeVisible() + }) + + expect(component).toMatchSnapshot() + }) + it('should show tooltip on tooltip trigger `hover`', async () => { + let component + await act(async () => { + component = render( + + + + ) + }) + + const tooltipTrrigger = screen.getByTestId('tooltip-trigger') + + userEvent.hover(tooltipTrrigger) + await waitFor(() => { + expect(screen.getByText(/^tooltip content$/i)).toBeVisible() + }) + + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/Tooltip/Tooltip.stories.tsx b/libs/design-system/src/lib/Tooltip/Tooltip.stories.tsx new file mode 100644 index 00000000000..237e38f450a --- /dev/null +++ b/libs/design-system/src/lib/Tooltip/Tooltip.stories.tsx @@ -0,0 +1,68 @@ +import type { Meta, Story } from '@storybook/react' +import type { TooltipProps } from './Tooltip' +import { RiInformationLine as IconInfo, RiLinksLine as IconLink } from 'react-icons/ri' +import Tooltip from './Tooltip' + +export default { + title: 'Components/Tooltip', + component: Tooltip, +} as Meta + +const Template: Story = (args) => { + return ( +
    + + + +
    + ) +} + +export const Base = Template.bind({}) +Base.args = { content: 'Short content' } + +export const Multiline = Template.bind({}) +Multiline.args = { + content: + 'This is the cumulative investments (or fiat) that you have invested in your cryptocurrency portfolio.', +} + +export const WithMarkup = Template.bind({}) +WithMarkup.args = { + content: ( +
    +

    More information

    +
    + +

    hello world

    +
    +
    + ), +} + +export const Interactive = Template.bind({}) +Interactive.args = { + interactive: true, + content: ( +
    +

    Link to resource

    + + +

    hello world

    +
    +
    + ), +} + +export const Delayed = Template.bind({}) +Delayed.args = { + delay: 500, + content: 'Delayed information ⏰', +} diff --git a/libs/design-system/src/lib/Tooltip/Tooltip.tsx b/libs/design-system/src/lib/Tooltip/Tooltip.tsx new file mode 100644 index 00000000000..4f2bcb69106 --- /dev/null +++ b/libs/design-system/src/lib/Tooltip/Tooltip.tsx @@ -0,0 +1,28 @@ +import type { TippyProps } from '@tippyjs/react/headless' +import Tippy from '@tippyjs/react/headless' +import cn from 'classnames' + +export type TooltipProps = TippyProps + +export default function Tooltip({ content, children, className, ...rest }: TooltipProps) { + return ( + ( +
    + {content} +
    + )} + {...rest} + > + {children} +
    + ) +} diff --git a/libs/design-system/src/lib/Tooltip/__snapshots__/Tooltip.spec.tsx.snap b/libs/design-system/src/lib/Tooltip/__snapshots__/Tooltip.spec.tsx.snap new file mode 100644 index 00000000000..730a7b2adc2 --- /dev/null +++ b/libs/design-system/src/lib/Tooltip/__snapshots__/Tooltip.spec.tsx.snap @@ -0,0 +1,173 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Tooltip when rendered should show tooltip content on tooltip trigger \`focus\` 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Tooltip when rendered should show tooltip on tooltip trigger \`hover\` 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/Tooltip/index.ts b/libs/design-system/src/lib/Tooltip/index.ts new file mode 100644 index 00000000000..aafb844df23 --- /dev/null +++ b/libs/design-system/src/lib/Tooltip/index.ts @@ -0,0 +1,2 @@ +export { default as Tooltip } from './Tooltip' +export type { TooltipProps } from './Tooltip' diff --git a/libs/design-system/src/lib/TrendLine/TrendLine.stories.tsx b/libs/design-system/src/lib/TrendLine/TrendLine.stories.tsx new file mode 100644 index 00000000000..23f9ff2dc8d --- /dev/null +++ b/libs/design-system/src/lib/TrendLine/TrendLine.stories.tsx @@ -0,0 +1,25 @@ +import type { Story, Meta } from '@storybook/react' +import type { TrendLineProps } from '.' + +import TrendLine from './TrendLine' + +const data = Array.from({ length: 30 }, () => Math.random() * 10000).map((value, index) => ({ + key: index, + value, +})) + +export default { + title: 'Components/TrendLine', + component: TrendLine, + parameters: { controls: { exclude: ['className'] } }, + args: { + inverted: false, + data, + }, +} as Meta + +export const Base: Story = (props) => ( +
    + +
    +) diff --git a/libs/design-system/src/lib/TrendLine/TrendLine.tsx b/libs/design-system/src/lib/TrendLine/TrendLine.tsx new file mode 100644 index 00000000000..565fe153081 --- /dev/null +++ b/libs/design-system/src/lib/TrendLine/TrendLine.tsx @@ -0,0 +1,92 @@ +import { useMemo } from 'react' +import { ParentSize } from '@visx/responsive' +import { scaleBand, scaleLinear } from '@visx/scale' +import { Group } from '@visx/group' +import { Area, Circle } from '@visx/shape' +import classNames from 'classnames' + +const CIRCLE_RADIUS = 1 + +export interface TrendLineProps extends React.HTMLAttributes { + data: { + key: { toString(): string } + value: number + }[] + /** Whether a negative trendline should be treated positively (teal vs. red) */ + inverted?: boolean +} + +export default function TrendLine({ + data, + className, + inverted = false, + ...rest +}: TrendLineProps): JSX.Element | null { + const xScale = useMemo( + () => + scaleBand({ + domain: data.map(({ key }) => key.toString()), + round: false, + }), + [data] + ) + const yScale = useMemo(() => { + const yDomain = data.map(({ value }) => value) + + return scaleLinear({ + domain: [Math.min(...yDomain), Math.max(...yDomain)], + round: true, + }) + }, [data]) + + const change = data[data.length - 1].value - data[0].value + + let isPositive = change > 0 + if (inverted) isPositive = !isPositive + + return data.length > 0 ? ( + + {({ width, height }) => { + xScale.range([0, width - CIRCLE_RADIUS * 2]) + yScale.range([height - CIRCLE_RADIUS * 2, 0]) + + return ( + + + <> + xScale(key.toString()) || 0} + y={({ value }) => yScale(value)} + className={ + change === 0 + ? 'text-gray-200' + : isPositive + ? 'text-teal' + : 'text-red' + } + stroke="currentColor" + strokeWidth={1} + strokeLinejoin="round" + /> + + + + + ) + }} + + ) : null +} diff --git a/libs/design-system/src/lib/TrendLine/Trendline.spec.tsx b/libs/design-system/src/lib/TrendLine/Trendline.spec.tsx new file mode 100644 index 00000000000..b61874d22c8 --- /dev/null +++ b/libs/design-system/src/lib/TrendLine/Trendline.spec.tsx @@ -0,0 +1,52 @@ +import { render } from '@testing-library/react' +import TrendLine from './TrendLine' + +describe('TrendLine', () => { + describe('when rendered with a positive trendline', () => { + it('should render correcly', () => { + const component = render( + + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when rendered with a negative trendline', () => { + it('should render correcly', () => { + const component = render( + + ) + + expect(component).toMatchSnapshot() + }) + }) + + describe('when rendered with a neutral trendline', () => { + it('should render correcly', () => { + const component = render( + + ) + + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/TrendLine/__snapshots__/Trendline.spec.tsx.snap b/libs/design-system/src/lib/TrendLine/__snapshots__/Trendline.spec.tsx.snap new file mode 100644 index 00000000000..3b8b271aa68 --- /dev/null +++ b/libs/design-system/src/lib/TrendLine/__snapshots__/Trendline.spec.tsx.snap @@ -0,0 +1,364 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TrendLine when rendered with a negative trendline should render correcly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + +
    +
    + , + "container":
    +
    + + + + + + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`TrendLine when rendered with a neutral trendline should render correcly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + +
    +
    + , + "container":
    +
    + + + + + + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`TrendLine when rendered with a positive trendline should render correcly 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    + + + + + + +
    +
    + , + "container":
    +
    + + + + + + +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/TrendLine/index.ts b/libs/design-system/src/lib/TrendLine/index.ts new file mode 100644 index 00000000000..7e3118ed22b --- /dev/null +++ b/libs/design-system/src/lib/TrendLine/index.ts @@ -0,0 +1,2 @@ +export { default as TrendLine } from './TrendLine' +export type { TrendLineProps } from './TrendLine' diff --git a/libs/design-system/src/lib/inputs/Input/Input.spec.tsx b/libs/design-system/src/lib/inputs/Input/Input.spec.tsx new file mode 100644 index 00000000000..eaa8679dc5f --- /dev/null +++ b/libs/design-system/src/lib/inputs/Input/Input.spec.tsx @@ -0,0 +1,57 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { Input } from '..' + +describe('Input', () => { + describe('when rendered', () => { + it('should display a placeholder when passed', () => { + const component = render() + + expect(screen.getByPlaceholderText('Placeholder')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should display a label when passed', () => { + const component = render() + + expect(screen.getByLabelText('Label')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should display a color hint when passed', () => { + const component = render() + + expect(component).toMatchSnapshot() + }) + it('should render plainly when no extra props are passed', () => { + const component = render() + + expect(component).toMatchSnapshot() + }) + }) + + describe('when interacted with', () => { + it('should call `onFocus` and `onBlur` callbacks', () => { + const onFocusMock = jest.fn(), + onBlurMock = jest.fn() + render( + + ) + const input = screen.getByTestId('input') + + fireEvent.focus(input) + expect(onFocusMock).toHaveBeenCalledTimes(1) + fireEvent.blur(input) + expect(onBlurMock).toHaveBeenCalledTimes(1) + }) + + it('should call the `onChange` callback on change', () => { + const onChangeMock = jest.fn() + render() + const input = screen.getByTestId('input') + + fireEvent.focus(input) + fireEvent.change(input, { + target: { value: 'abc' }, + }) + expect(onChangeMock).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/libs/design-system/src/lib/inputs/Input/Input.stories.tsx b/libs/design-system/src/lib/inputs/Input/Input.stories.tsx new file mode 100644 index 00000000000..3daef8d00fe --- /dev/null +++ b/libs/design-system/src/lib/inputs/Input/Input.stories.tsx @@ -0,0 +1,82 @@ +import type { Story, Meta } from '@storybook/react' +import type { InputProps } from '..' + +import { Input } from '..' +import { FormGroup } from '../../FormGroup' +import { RiTerminalBoxFill, RiWifiFill } from 'react-icons/ri' + +export default { + title: 'Components/Inputs/Input', + component: Input, + parameters: { + controls: { exclude: ['variant', 'className', 'labelClassName'] }, + }, + argTypes: { + type: { + control: { + type: 'select', + options: ['text', 'number', 'password', 'email'], + }, + }, + colorHint: { + control: { + type: 'text', + description: 'Color to display on the right side of the input', + }, + }, + fixedLeftOverride: { + control: { + type: 'text', + description: 'Override content placed on the left side of the input', + }, + }, + fixedRightOverride: { + control: { + type: 'text', + description: 'Override content placed on the right side of the input', + }, + }, + disabled: { + control: { + type: 'boolean', + }, + }, + readOnly: { + control: { + type: 'boolean', + }, + }, + hint: { + control: { + type: 'text', + }, + }, + error: { + control: { + type: 'text', + }, + }, + }, + args: { + label: 'Label', + placeholder: 'Placeholder', + type: 'text', + }, +} as Meta + +const Template: Story = (args) => ( + + + +) + +export const Base = Template.bind({}) + +export const WithColorHint = Template.bind({}) +WithColorHint.args = { colorHint: 'cyan' } + +export const WithIcons = Template.bind({}) +WithIcons.args = { + fixedLeftOverride: , + fixedRightOverride: , +} diff --git a/libs/design-system/src/lib/inputs/Input/Input.tsx b/libs/design-system/src/lib/inputs/Input/Input.tsx new file mode 100644 index 00000000000..9e7ee62b0b3 --- /dev/null +++ b/libs/design-system/src/lib/inputs/Input/Input.tsx @@ -0,0 +1,144 @@ +import type { InputColorHintColor } from '../' +import type { InputHTMLAttributes, ReactNode } from 'react' +import classNames from 'classnames' +import { InputColorHint, InputHint } from '../' +import React, { forwardRef } from 'react' + +const InputVariants = Object.freeze({ + default: 'focus-within:border-cyan focus-within:ring-cyan', + positive: 'focus-within:border-teal focus-within:ring-teal', + negative: 'focus-within:border-red focus-within:ring-red', +}) + +export type InputVariant = keyof typeof InputVariants + +export interface InputProps extends InputHTMLAttributes { + variant?: InputVariant + + /** Label that shows up over input */ + label?: string + + /** Color to appear on the right side of the input */ + colorHint?: InputColorHintColor + + labelClassName?: string + + inputClassName?: string + + /** Hint message that appears below the input */ + hint?: string + + /** Error message that appears below the input */ + error?: string + + /** If there's no error message, but you want to color the border as red, pass `true` */ + hasError?: boolean + + /** Override content appearing on the left side of the input */ + fixedLeftOverride?: ReactNode + + /** Override content appearing on the right side of the input */ + fixedRightOverride?: ReactNode +} + +/** + * Simple input component + */ +function Input( + { + variant = 'default', + disabled, + readOnly, + colorHint, + className, + label, + labelClassName, + inputClassName, + hint, + error, + hasError, + fixedLeftOverride, + fixedRightOverride, + ...rest + }: InputProps, + ref: React.Ref +): JSX.Element { + const fixedLeft = fixedLeftOverride + const fixedRight = + fixedRightOverride || (colorHint ? : null) + + const bgClass = readOnly ? 'bg-gray-600' : 'bg-gray-500' + + return ( + + ) +} + +export default forwardRef(Input) diff --git a/libs/design-system/src/lib/inputs/Input/__snapshots__/Input.spec.tsx.snap b/libs/design-system/src/lib/inputs/Input/__snapshots__/Input.spec.tsx.snap new file mode 100644 index 00000000000..76b99aef740 --- /dev/null +++ b/libs/design-system/src/lib/inputs/Input/__snapshots__/Input.spec.tsx.snap @@ -0,0 +1,379 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Input when rendered should display a color hint when passed 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Input when rendered should display a label when passed 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Input when rendered should display a placeholder when passed 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`Input when rendered should render plainly when no extra props are passed 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/inputs/Input/index.ts b/libs/design-system/src/lib/inputs/Input/index.ts new file mode 100644 index 00000000000..c2703cf9bed --- /dev/null +++ b/libs/design-system/src/lib/inputs/Input/index.ts @@ -0,0 +1,2 @@ +export { default as Input } from './Input' +export type { InputProps } from './Input' diff --git a/libs/design-system/src/lib/inputs/InputColorHint/InputColorHint.tsx b/libs/design-system/src/lib/inputs/InputColorHint/InputColorHint.tsx new file mode 100644 index 00000000000..c6496fdb237 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputColorHint/InputColorHint.tsx @@ -0,0 +1,37 @@ +import type { HTMLAttributes } from 'react' +import classNames from 'classnames' + +const InputColorHintColors = Object.freeze({ + cyan: 'bg-cyan', + blue: 'bg-blue', + pink: 'bg-pink', + teal: 'bg-teal', + green: 'bg-green', + red: 'bg-red', + yellow: 'bg-yellow', +}) + +export type InputColorHintColor = keyof typeof InputColorHintColors + +export interface InputColorHintProps extends HTMLAttributes { + color: InputColorHintColor + className?: string +} + +export default function InputColorHint({ + color, + className, + ...rest +}: InputColorHintProps): JSX.Element { + const combinedClassName = classNames( + className, + InputColorHintColors[color], + 'block w-1.5 rounded-lg leading-none' + ) + + return ( + +   {/* Used to force a height matching the font size */} + + ) +} diff --git a/libs/design-system/src/lib/inputs/InputColorHint/index.ts b/libs/design-system/src/lib/inputs/InputColorHint/index.ts new file mode 100644 index 00000000000..1b074141c67 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputColorHint/index.ts @@ -0,0 +1,2 @@ +export { default as InputColorHint } from './InputColorHint' +export type { InputColorHintColor, InputColorHintProps } from './InputColorHint' diff --git a/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.spec.tsx b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.spec.tsx new file mode 100644 index 00000000000..85b930de82b --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.spec.tsx @@ -0,0 +1,40 @@ +import { fireEvent, render, screen } from '@testing-library/react' +import { useState } from 'react' +import { InputCurrency } from '..' + +function ControlledInputCurrency(props: { [key: string]: unknown }): JSX.Element { + const [value, setValue] = useState(20) + return +} + +describe('InputCurrency', () => { + describe('when rendered', () => { + it('should display a dollar sign by default', () => { + const component = render( null} />) + + expect(screen.getByText('$')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should display a specified currency symbol', () => { + const component = render( + null} /> + ) + + expect(screen.getByText('symbol')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + }) + + describe('when interacted with', () => { + it('should call `onChange` when the entered number changes', () => { + const onChangeMock = jest.fn() + + render() + const input = screen.getByTestId('input') + + fireEvent.focus(input) + fireEvent.change(input, { target: { value: -123 } }) + expect(onChangeMock).toHaveBeenCalledWith(123) // Negative inputs not accepted, will be converted to positive + }) + }) +}) diff --git a/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.stories.tsx b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.stories.tsx new file mode 100644 index 00000000000..5611e5252f2 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.stories.tsx @@ -0,0 +1,42 @@ +import type { Story, Meta } from '@storybook/react' +import type { InputCurrencyProps } from '..' +import InputStories from '../Input/Input.stories' + +import { FormGroup } from '../../FormGroup' +import { InputCurrency } from '..' +import React, { useState } from 'react' + +const inheritedArgTypes = InputStories.argTypes || {} +delete inheritedArgTypes.type +delete inheritedArgTypes.fixedLeftOverride +delete inheritedArgTypes.fixedRightOverride + +export default { + title: 'Components/Inputs/InputCurrency', + component: InputCurrency, + parameters: { + controls: { exclude: ['className', 'labelClassName', 'onValueChange'] }, + }, + argTypes: { + ...inheritedArgTypes, + }, + args: { + label: 'Currency', + placeholder: 'Amount', + }, +} as Meta + +const Template: Story = (args) => { + const [value, setValue] = useState(null) + + return ( + + + + ) +} + +export const Base = Template.bind({}) + +export const WithColorHint = Template.bind({}) +WithColorHint.args = { colorHint: 'cyan' } diff --git a/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.tsx b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.tsx new file mode 100644 index 00000000000..010c4677d89 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputCurrency/InputCurrency.tsx @@ -0,0 +1,40 @@ +import React, { forwardRef } from 'react' +import { NumericFormat, type NumericFormatProps } from 'react-number-format' +import { Input, type InputProps } from '..' + +export type InputCurrencyProps = Omit & + Omit & { + value: number | null + onChange(value: number | null): void + + /** Currency symbol to appear on the left side of the input */ + symbol?: string + } + +/** + * Controlled input for numerical currency values + */ +function InputCurrency( + { value, onChange, type, symbol = '$', allowNegative = false, ...rest }: InputCurrencyProps, + ref: React.Ref +) { + // https://github.com/s-yadav/react-number-format#custom-inputs + return ( + { + onChange(value.floatValue ?? null) + }} + decimalScale={2} + thousandSeparator + allowNegative={allowNegative} + fixedLeftOverride={symbol} + {...rest} + type={type as any} // conflicting types between React and Number format + /> + ) +} + +export default forwardRef(InputCurrency) diff --git a/libs/design-system/src/lib/inputs/InputCurrency/__snapshots__/InputCurrency.spec.tsx.snap b/libs/design-system/src/lib/inputs/InputCurrency/__snapshots__/InputCurrency.spec.tsx.snap new file mode 100644 index 00000000000..7a667a133ed --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputCurrency/__snapshots__/InputCurrency.spec.tsx.snap @@ -0,0 +1,203 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InputCurrency when rendered should display a dollar sign by default 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputCurrency when rendered should display a specified currency symbol 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    + +
    + , + "container":
    + +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/inputs/InputCurrency/index.ts b/libs/design-system/src/lib/inputs/InputCurrency/index.ts new file mode 100644 index 00000000000..29028b01a82 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputCurrency/index.ts @@ -0,0 +1,2 @@ +export { default as InputCurrency } from './InputCurrency' +export type { InputCurrencyProps } from './InputCurrency' diff --git a/libs/design-system/src/lib/inputs/InputHint/InputHint.tsx b/libs/design-system/src/lib/inputs/InputHint/InputHint.tsx new file mode 100644 index 00000000000..35c3ad1993c --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputHint/InputHint.tsx @@ -0,0 +1,25 @@ +import type { ReactNode } from 'react' +import classNames from 'classnames' + +export interface InputHintProps { + error?: boolean + disabled?: boolean + children: ReactNode +} + +export default function InputHint({ + error = false, + disabled = false, + children, +}: InputHintProps): JSX.Element { + return ( + + {children} + + ) +} diff --git a/libs/design-system/src/lib/inputs/InputHint/index.ts b/libs/design-system/src/lib/inputs/InputHint/index.ts new file mode 100644 index 00000000000..0d8ba6b2aa5 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputHint/index.ts @@ -0,0 +1,2 @@ +export { default as InputHint } from './InputHint' +export type { InputHintProps } from './InputHint' diff --git a/libs/design-system/src/lib/inputs/InputPassword/InputPassword.spec.tsx b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.spec.tsx new file mode 100644 index 00000000000..b255bed7efe --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.spec.tsx @@ -0,0 +1,82 @@ +import { render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { act } from 'react-dom/test-utils' +import { InputPassword } from '..' + +describe('InputPassword', () => { + describe('when rendered', () => { + it('should render plainly when reveal button and complexity bar are disabled', () => { + const component = render( + + ) + + expect(component.queryByTestId('reveal-password-button')).not.toBeInTheDocument() + expect(component.queryByTitle('Password is very weak')).not.toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should render the reveal button when enabled', async () => { + let component + await act(async () => { + component = render() + }) + + expect(screen.getByTestId('reveal-password-button')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should render the complexity bar when enabled', async () => { + let component + await act(async () => { + component = render() + }) + + expect(screen.getByTitle('Password is very weak')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should render a complexity based on the calculated score', async () => { + let component + await act(async () => { + component = render( + null} + passwordComplexity={() => 3} + /> + ) + }) + + expect(screen.getByTitle('Password is strong')).toBeInTheDocument() + expect(component).toMatchSnapshot() + }) + it('should show password requirements on `onFocus`', async () => { + let component + await act(async () => { + component = render( + + ) + }) + + const inputElement = screen.getByPlaceholderText('Password') + + inputElement.focus() + await waitFor(() => { + expect(screen.getByText('Password requirements')).toBeVisible() + }) + + expect(component).toMatchSnapshot() + }) + it('should show tooltip on reveal button hover', async () => { + let component + await act(async () => { + component = render() + }) + + userEvent.hover(screen.getByTestId('reveal-password-button')) + await waitFor(() => { + expect(screen.getByText(/^show password$/i)).toBeVisible() + }) + + expect(component).toMatchSnapshot() + }) + }) +}) diff --git a/libs/design-system/src/lib/inputs/InputPassword/InputPassword.stories.tsx b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.stories.tsx new file mode 100644 index 00000000000..ab3a67aefab --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.stories.tsx @@ -0,0 +1,45 @@ +import type { Story, Meta } from '@storybook/react' +import type { InputPasswordProps } from '..' +import InputStories from '../Input/Input.stories' + +import { FormGroup } from '../../FormGroup' +import { InputPassword } from '..' +import React, { useState } from 'react' + +const inheritedArgTypes = InputStories.argTypes || {} +delete inheritedArgTypes.type +delete inheritedArgTypes.fixedLeftOverride +delete inheritedArgTypes.fixedRightOverride +delete inheritedArgTypes.colorHint + +export default { + title: 'Components/Inputs/InputPassword', + component: InputPassword, + parameters: { + controls: { + exclude: ['className', 'labelClassName', 'onValueChange', 'passwordComplexity'], + }, + }, + argTypes: { + ...inheritedArgTypes, + }, + args: { + label: 'Password', + }, +} as Meta + +const Template: Story = (args) => { + const [value, setValue] = useState('') + + return ( + + ) => setValue(e.target.value)} + /> + + ) +} + +export const Base = Template.bind({}) diff --git a/libs/design-system/src/lib/inputs/InputPassword/InputPassword.tsx b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.tsx new file mode 100644 index 00000000000..fd02d9940d3 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputPassword/InputPassword.tsx @@ -0,0 +1,214 @@ +import type { InputProps } from '..' +import { useEffect, useMemo, useState } from 'react' +import classNames from 'classnames' +import type zxcvbnType from 'zxcvbn' +import { + RiEyeLine as IconReveal, + RiEyeOffLine as IconHide, + RiCloseFill as IconClose, + RiCheckFill as IconCheck, +} from 'react-icons/ri' +import { Input } from '..' +import { InputHint } from '../InputHint' +import { Tooltip } from '../../Tooltip/' + +interface PasswordValidation { + isValid: boolean + message: string +} + +interface PasswordValidator { + (password: string): PasswordValidation +} + +export interface InputPasswordProps extends Omit { + /** Whether to show and enable the button to reveal/hide the password */ + showRevealButton?: boolean + + /** Whether to show the complexity measurement bar */ + showComplexityBar?: boolean + + /** Custom password complexity function, returning a score from 0 to 4 */ + passwordComplexity?: (password: string) => number + + /** Whether or not to show a password requirements popover */ + showPasswordRequirements?: boolean + + /** Fires when password validity changes */ + onValidityChange?: (validations: { isValid: boolean; message: string }[]) => void +} + +const COMPLEXITY_SCORE_NAMES = ['very weak', 'weak', 'fair', 'strong', 'very strong'] + +declare const zxcvbn: typeof zxcvbnType + +/** + * Input for password values + */ +function InputPassword({ + value, + showRevealButton = true, + showComplexityBar = true, + showPasswordRequirements = false, + passwordComplexity, + disabled, + hint, + error, + onValidityChange, + ...rest +}: InputPasswordProps): JSX.Element { + const [revealPassword, setRevealPassword] = useState(false) + const [complexityScore, setComplexityScore] = useState(0) + const [validations, setValidations] = useState([]) + + const _passwordComplexity = useMemo( + () => passwordComplexity ?? ((password: string) => zxcvbn(password).score), + [passwordComplexity] + ) + + const passwordRequirements = useMemo(() => { + const validators: PasswordValidator[] = [ + (p) => ({ + isValid: p.length >= 8 && p.length <= 64, + message: 'Between 8 - 64 characters', + }), + (p) => ({ isValid: /[a-z]+/.test(p), message: 'Contains 1+ lowercase characters' }), + (p) => ({ isValid: /[A-Z]+/.test(p), message: 'Contains 1+ uppercase characters' }), + (p) => ({ + isValid: /[*!@#$%^&(){}:;<>,.?/~_+-=|]+/.test(p), + message: 'Contains 1+ special characters', + }), + ] + + return validators + }, []) + + useEffect(() => { + setComplexityScore(value ? _passwordComplexity(value as string) : 0) + }, [value, _passwordComplexity]) + + // Using Auth0 "Good" password requirements - https://auth0.com/docs/connections/database/password-strength#password-policies + useEffect(() => { + if (onValidityChange) { + const checks = passwordRequirements.map((validatorFn) => + validatorFn((value as string) || '') + ) + onValidityChange(checks) + setValidations(checks) + } + }, [value, passwordRequirements, onValidityChange]) + + return ( +
    + { + if (placement.includes('bottom')) { + return showComplexityBar ? [0, 20] : [0, 8] + } else { + return [0, 4] + } + }} + disabled={!showPasswordRequirements} + content={ +
    +

    Password requirements

    + +
    + {validations + .sort((a, b) => { + const aVal = a.isValid ? 1 : 0 + const bVal = b.isValid ? 1 : 0 + return bVal - aVal + }) + .map((validation) => { + return ( +
    + + {validation.isValid ? ( + + ) : ( + + )} + + + {validation.message} + +
    + ) + })} +
    +
    + } + > +
    + + + + ) : null + } + hasError={!!error} + disabled={disabled} + hint={hint} + error={error} + {...rest} + /> +
    +
    + {showComplexityBar && ( +
    + {['bg-red', 'bg-orange', 'bg-yellow', 'bg-green'].map((bg, index) => ( +
    index ? bg : 'bg-gray-300', + index < 3 && 'mr-1.5' + )} + >
    + ))} +
    + )} + + {hint && !error && {hint}} + {error && ( + + {error} + + )} +
    + ) +} + +export default InputPassword diff --git a/libs/design-system/src/lib/inputs/InputPassword/__snapshots__/InputPassword.spec.tsx.snap b/libs/design-system/src/lib/inputs/InputPassword/__snapshots__/InputPassword.spec.tsx.snap new file mode 100644 index 00000000000..2be0d9fc01d --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputPassword/__snapshots__/InputPassword.spec.tsx.snap @@ -0,0 +1,1092 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InputPassword when rendered should render a complexity based on the calculated score 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputPassword when rendered should render plainly when reveal button and complexity bar are disabled 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    + , + "container":
    +
    +
    + +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputPassword when rendered should render the complexity bar when enabled 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputPassword when rendered should render the reveal button when enabled 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    + , + "container":
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputPassword when rendered should show password requirements on \`onFocus\` 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + + , + "container":
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; + +exports[`InputPassword when rendered should show tooltip on reveal button hover 1`] = ` +Object { + "asFragment": [Function], + "baseElement": +
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    + , + "container":
    +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    +
    , + "debug": [Function], + "findAllByAltText": [Function], + "findAllByDisplayValue": [Function], + "findAllByLabelText": [Function], + "findAllByPlaceholderText": [Function], + "findAllByRole": [Function], + "findAllByTestId": [Function], + "findAllByText": [Function], + "findAllByTitle": [Function], + "findByAltText": [Function], + "findByDisplayValue": [Function], + "findByLabelText": [Function], + "findByPlaceholderText": [Function], + "findByRole": [Function], + "findByTestId": [Function], + "findByText": [Function], + "findByTitle": [Function], + "getAllByAltText": [Function], + "getAllByDisplayValue": [Function], + "getAllByLabelText": [Function], + "getAllByPlaceholderText": [Function], + "getAllByRole": [Function], + "getAllByTestId": [Function], + "getAllByText": [Function], + "getAllByTitle": [Function], + "getByAltText": [Function], + "getByDisplayValue": [Function], + "getByLabelText": [Function], + "getByPlaceholderText": [Function], + "getByRole": [Function], + "getByTestId": [Function], + "getByText": [Function], + "getByTitle": [Function], + "queryAllByAltText": [Function], + "queryAllByDisplayValue": [Function], + "queryAllByLabelText": [Function], + "queryAllByPlaceholderText": [Function], + "queryAllByRole": [Function], + "queryAllByTestId": [Function], + "queryAllByText": [Function], + "queryAllByTitle": [Function], + "queryByAltText": [Function], + "queryByDisplayValue": [Function], + "queryByLabelText": [Function], + "queryByPlaceholderText": [Function], + "queryByRole": [Function], + "queryByTestId": [Function], + "queryByText": [Function], + "queryByTitle": [Function], + "rerender": [Function], + "unmount": [Function], +} +`; diff --git a/libs/design-system/src/lib/inputs/InputPassword/index.ts b/libs/design-system/src/lib/inputs/InputPassword/index.ts new file mode 100644 index 00000000000..ee4cd109971 --- /dev/null +++ b/libs/design-system/src/lib/inputs/InputPassword/index.ts @@ -0,0 +1,2 @@ +export { default as InputPassword } from './InputPassword' +export type { InputPasswordProps } from './InputPassword' diff --git a/libs/design-system/src/lib/inputs/index.ts b/libs/design-system/src/lib/inputs/index.ts new file mode 100644 index 00000000000..d1ae5eb9cbe --- /dev/null +++ b/libs/design-system/src/lib/inputs/index.ts @@ -0,0 +1,14 @@ +export * from './Input' +export type { InputProps } from './Input' + +export * from './InputColorHint' +export type { InputColorHintColor, InputColorHintProps } from './InputColorHint' + +export * from './InputCurrency' +export type { InputCurrencyProps } from './InputCurrency' + +export * from './InputHint' +export type { InputHintProps } from './InputHint' + +export * from './InputPassword' +export type { InputPasswordProps } from './InputPassword' diff --git a/libs/design-system/tailwind.config.js b/libs/design-system/tailwind.config.js new file mode 100644 index 00000000000..36dc2698b96 --- /dev/null +++ b/libs/design-system/tailwind.config.js @@ -0,0 +1,156 @@ +const { join } = require('path') +const defaultTheme = require('tailwindcss/defaultTheme') + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [join(__dirname, 'src/**/*.tsx'), join(__dirname, 'docs/**/*.{tsx,mdx}')], + theme: { + screens: { + xs: '375px', + sm: '640px', + md: '744px', + lg: '1024px', + xl: '1440px', + }, + colors: { + transparent: 'transparent', + current: 'currentColor', + black: '#16161A', + white: '#F8F9FA', + gray: { + DEFAULT: '#34363C', + 25: '#DEE2E6', + 50: '#ADB5BD', + 100: '#868E96', + 200: '#4B4F55', + 300: '#44474C', + 400: '#3D4045', + 500: '#34363C', + 600: '#2C2D32', + 700: '#232428', + 800: '#1C1C20', + }, + cyan: { + DEFAULT: '#3BC9DB', + 50: '#D7F6FA', + 300: '#99E9F2', + 400: '#66D9E8', + 500: '#3BC9DB', + }, + red: { + DEFAULT: '#FF8787', + 50: '#FFE8E8', + 300: '#FFC9C9', + 400: '#FFA8A8', + 500: '#FF8787', + }, + teal: { + DEFAULT: '#38D9A9', + 50: '#D6FAEE', + 300: '#96F2D7', + 400: '#63E6BE', + 500: '#38D9A9', + }, + yellow: { + DEFAULT: '#FFCA28', + 50: '#FFF2CB', + 300: '#FFE082', + 400: '#FFD54F', + 500: '#FFCA28', + }, + blue: { + DEFAULT: '#4DABF7', + 50: '#DBEEFF', + 300: '#A5D8FF', + 400: '#74C0FC', + 500: '#4DABF7', + }, + orange: { + DEFAULT: '#FFA94D', + 50: '#FFEEDA', + 300: '#FFD8A8', + 400: '#FFC078', + 500: '#FFA94D', + }, + pink: { + DEFAULT: '#F783AC', + 50: '#FFE5EE', + 300: '#FCC2D7', + 400: '#FAA2C1', + 500: '#F783AC', + }, + grape: { + DEFAULT: '#DA77F2', + 50: '#F9E4FD', + 300: '#EEBEFA', + 400: '#E599F7', + 500: '#DA77F2', + }, + indigo: { + DEFAULT: '#748FFC', + 50: '#E3E8FF', + 300: '#BAC8FF', + 400: '#91A7FF', + 500: '#748FFC', + }, + green: { + DEFAULT: '#66BB6A', + 50: '#E3F2E3', + 300: '#A5D6A7', + 400: '#81C784', + 500: '#66BB6A', + }, + }, + fontFamily: { + sans: ['Inter', ...defaultTheme.fontFamily.sans], + display: ['Monument Extended', ...defaultTheme.fontFamily.sans], + mono: defaultTheme.fontFamily.mono, + }, + fontSize: { + sm: ['0.75rem', '1rem'], + base: ['0.875rem', '1.5rem'], + lg: ['1rem', '1.5rem'], + xl: ['1.125rem', '1.5rem'], + '2xl': ['1.25rem', '2rem'], + '3xl': ['1.5rem', '2rem'], + '4xl': ['1.875rem', '2.5rem'], + '5xl': ['2.5rem', '3.5rem'], + }, + extend: { + boxShadow: { + DEFAULT: '0px 1px 2px 0px rgba(0, 0, 0, 0.1)', + md: '0px 1px 4px 0px rgba(0, 0, 0, 0.25)', + lg: '0px 2px 4px 1px rgba(0, 0, 0, 0.3)', + }, + backgroundImage: { + shine: 'linear-gradient(to right, transparent, transparent, #FFF1, transparent, transparent)', + }, + keyframes: { + shine: { + '0%': { + transform: 'translateX(-100%)', + }, + '100%': { + transform: 'translateX(100%)', + }, + }, + appearUp: { + '0%': { + transformOrigin: 'center', + transform: 'translateY(50%) scale(0.8)', + opacity: 0, + }, + '100%': { + transformOrigin: 'center', + transform: 'translateY(0) scale(1)', + opacity: 1, + }, + }, + }, + animation: { + shine: 'shine 1.8s infinite', + appearUp: 'appearUp 0.3s', + }, + }, + }, +} diff --git a/libs/design-system/tsconfig.json b/libs/design-system/tsconfig.json new file mode 100644 index 00000000000..c45a45f4203 --- /dev/null +++ b/libs/design-system/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react-jsx", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/libs/design-system/tsconfig.lib.json b/libs/design-system/tsconfig.lib.json new file mode 100644 index 00000000000..b0c0d2e6fae --- /dev/null +++ b/libs/design-system/tsconfig.lib.json @@ -0,0 +1,24 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": [ + "docs/**/*", + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.stories.ts", + "**/*.stories.js", + "**/*.stories.jsx", + "**/*.stories.tsx", + "jest.config.ts" + ], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/design-system/tsconfig.spec.json b/libs/design-system/tsconfig.spec.json new file mode 100644 index 00000000000..41adcb281f6 --- /dev/null +++ b/libs/design-system/tsconfig.spec.json @@ -0,0 +1,21 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.setup.js", + "jest.config.ts" + ] +} diff --git a/libs/finicity-api/.babelrc b/libs/finicity-api/.babelrc new file mode 100644 index 00000000000..4496e8f26c8 --- /dev/null +++ b/libs/finicity-api/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/finicity-api/.eslintrc.json b/libs/finicity-api/.eslintrc.json new file mode 100644 index 00000000000..5626944bd17 --- /dev/null +++ b/libs/finicity-api/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/finicity-api/README.md b/libs/finicity-api/README.md new file mode 100644 index 00000000000..009c632025d --- /dev/null +++ b/libs/finicity-api/README.md @@ -0,0 +1,7 @@ +# finicity-api + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test finicity-api` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/finicity-api/jest.config.ts b/libs/finicity-api/jest.config.ts new file mode 100644 index 00000000000..e6d33b2fa5f --- /dev/null +++ b/libs/finicity-api/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'finicity-api', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/finicity-api', +} diff --git a/libs/finicity-api/src/finicity-api.ts b/libs/finicity-api/src/finicity-api.ts new file mode 100644 index 00000000000..cb21fc25b59 --- /dev/null +++ b/libs/finicity-api/src/finicity-api.ts @@ -0,0 +1,294 @@ +import type { AxiosInstance, AxiosRequestConfig } from 'axios' +import type { + AddCustomerRequest, + AddCustomerResponse, + AuthenticationResponse, + DeleteCustomerAccountsByInstitutionLoginRequest, + DeleteCustomerAccountsByInstitutionLoginResponse, + GenerateConnectUrlResponse, + GenerateFixConnectUrlRequest, + GenerateLiteConnectUrlRequest, + GetAccountTransactionsRequest, + GetAccountTransactionsResponse, + GetCustomerAccountRequest, + GetCustomerAccountResponse, + GetCustomerAccountsRequest, + GetCustomerAccountsResponse, + GetInstitutionsRequest, + GetInstitutionsResponse, + LoadHistoricTransactionsRequest, + RefreshCustomerAccountRequest, + TxPushDisableRequest, + TxPushSubscriptionRequest, + TxPushSubscriptions, +} from './types' +import { DateTime } from 'luxon' +import axios from 'axios' + +const is2xx = (status: number): boolean => status >= 200 && status < 300 + +/** + * Basic typed mapping for Finicity API + */ +export class FinicityApi { + private api: AxiosInstance | null = null + private tokenTimestamp: DateTime | null = null + + constructor( + private readonly appKey: string, + private readonly partnerId: string, + private readonly partnerSecret: string + ) {} + + /** + * Search for supported financial institutions + * + * https://api-reference.finicity.com/#/rest/api-endpoints/institutions/get-institutions + */ + async getInstitutions(options: GetInstitutionsRequest): Promise { + return this.get(`/institution/v2/institutions`, options) + } + + /** + * Enroll an active or testing customer + * + * https://api-reference.finicity.com/#/rest/api-endpoints/customer/add-customer + */ + async addCustomer(options: AddCustomerRequest): Promise { + return this.post(`/aggregation/v2/customers/active`, options) + } + + /** + * Enroll a testing customer + * + * https://api-reference.finicity.com/#/rest/api-endpoints/customer/add-testing-customer + */ + async addTestingCustomer(options: AddCustomerRequest): Promise { + return this.post(`/aggregation/v2/customers/testing`, options) + } + + /** + * Generate a Connect Lite URL + * + * https://api-reference.finicity.com/#/rest/api-endpoints/connect/generate-v2-lite-connect-url + */ + async generateLiteConnectUrl( + options: Omit + ): Promise { + return this.post<{ link: string }>(`/connect/v2/generate/lite`, { + partnerId: this.partnerId, + ...options, + }) + } + + /** + * Generate a Fix Connect URL + * + * https://api-reference.finicity.com/#/rest/api-endpoints/connect/generate-v2-fix-connect-url + */ + async generateFixConnectUrl( + options: Omit + ): Promise { + return this.post(`/connect/v2/generate/fix`, { + partnerId: this.partnerId, + ...options, + }) + } + + /** + * Get details for all accounts owned by a customer, optionally for a specific institution + * + * https://api-reference.finicity.com/#/rest/api-endpoints/accounts/get-customer-accounts + */ + async getCustomerAccounts( + options: GetCustomerAccountsRequest + ): Promise { + const { customerId, ...rest } = options + + return this.get( + `/aggregation/v2/customers/${customerId}/accounts`, + rest, + { + validateStatus: (status) => is2xx(status) && status !== 203, + } + ) + } + + /** + * Get details for an account + * + * https://api-reference.finicity.com/#/rest/api-endpoints/accounts/get-customer-account + */ + async getCustomerAccount( + options: GetCustomerAccountRequest + ): Promise { + const { customerId, accountId, ...rest } = options + + return this.get( + `/aggregation/v2/customers/${customerId}/accounts/${accountId}`, + rest, + { + validateStatus: (status) => is2xx(status) && status !== 203, + } + ) + } + + /** + * Refresh accounts + * + * https://api-reference.finicity.com/#/rest/api-endpoints/accounts/refresh-customer-accounts + */ + async refreshCustomerAccounts({ + customerId, + }: RefreshCustomerAccountRequest): Promise { + return this.post( + `/aggregation/v1/customers/${customerId}/accounts`, + undefined, + { + timeout: 120_000, + validateStatus: (status) => is2xx(status) && status !== 203, + } + ) + } + + async deleteCustomerAccountsByInstitutionLogin( + options: DeleteCustomerAccountsByInstitutionLoginRequest + ): Promise { + const { customerId, institutionLoginId, ...rest } = options + + return this.delete( + `/aggregation/v1/customers/${customerId}/institutionLogins/${institutionLoginId}`, + rest + ) + } + + /** + * Get transactions for an account + * + * https://api-reference.finicity.com/#/rest/api-endpoints/transactions/get-customer-account-transactions + */ + async getAccountTransactions( + options: GetAccountTransactionsRequest + ): Promise { + const { customerId, accountId, ...rest } = options + + return this.get( + `/aggregation/v4/customers/${customerId}/accounts/${accountId}/transactions`, + rest, + { + validateStatus: (status) => is2xx(status) && status !== 203, + } + ) + } + + /** + * Load historic transactions for an account + * + * https://api-reference.finicity.com/#/rest/api-endpoints/accounts/load-historic-transactions-for-customer-account + */ + async loadHistoricTransactions({ + customerId, + accountId, + }: LoadHistoricTransactionsRequest): Promise { + await this.post( + `/aggregation/v1/customers/${customerId}/accounts/${accountId}/transactions/historic`, + {}, + { + timeout: 180_000, + validateStatus: (status) => is2xx(status) && status !== 203, + } // 180 second timeout recommended by Finicity + ) + } + + /** + * Subscribe to TxPUSH notifications + * + * https://api-reference.finicity.com/#/rest/api-endpoints/txpush/subscribe-to-txpush-notifications + */ + async subscribeTxPush({ + customerId, + accountId, + callbackUrl, + }: TxPushSubscriptionRequest): Promise { + return this.post(`/aggregation/v1/customers/${customerId}/accounts/${accountId}/txpush`, { + callbackUrl, + }) + } + + /** + * Disable TxPUSH notifications + * + * https://api-reference.finicity.com/#/rest/api-endpoints/txpush/disable-txpush-notifications + */ + async disableTxPush({ customerId, accountId }: TxPushDisableRequest): Promise { + await this.delete(`/aggregation/v1/customers/${customerId}/accounts/${accountId}/txpush`) + } + + private async getApi(): Promise { + const tokenAge = + this.tokenTimestamp && Math.abs(this.tokenTimestamp.diffNow('minutes').minutes) + + // Refresh token if over 90 minutes old (https://api-reference.finicity.com/#/rest/api-endpoints/authentication/partner-authentication) + if (!this.api || !tokenAge || (tokenAge && tokenAge > 90)) { + const token = ( + await axios.post( + 'https://api.finicity.com/aggregation/v2/partners/authentication', + { + partnerId: this.partnerId, + partnerSecret: this.partnerSecret, + }, + { + headers: { + 'Finicity-App-Key': this.appKey, + Accept: 'application/json', + }, + } + ) + ).data.token + + this.tokenTimestamp = DateTime.now() + + this.api = axios.create({ + baseURL: `https://api.finicity.com`, + timeout: 30_000, + headers: { + 'Finicity-App-Token': token, + 'Finicity-App-Key': this.appKey, + Accept: 'application/json', + }, + }) + } + + return this.api + } + + /** Generic API GET request method */ + private async get( + path: string, + params?: any, + config?: AxiosRequestConfig + ): Promise { + const api = await this.getApi() + return api.get(path, { params, ...config }).then(({ data }) => data) + } + + /** Generic API POST request method */ + private async post( + path: string, + body?: any, + config?: AxiosRequestConfig + ): Promise { + const api = await this.getApi() + return api.post(path, body, config).then(({ data }) => data) + } + + /** Generic API DELETE request method */ + private async delete( + path: string, + params?: any, + config?: AxiosRequestConfig + ): Promise { + const api = await this.getApi() + return api.delete(path, { params, ...config }).then(({ data }) => data) + } +} diff --git a/libs/finicity-api/src/index.ts b/libs/finicity-api/src/index.ts new file mode 100644 index 00000000000..05517f1630e --- /dev/null +++ b/libs/finicity-api/src/index.ts @@ -0,0 +1,2 @@ +export * from './finicity-api' +export * as FinicityTypes from './types' diff --git a/libs/finicity-api/src/types/accounts.ts b/libs/finicity-api/src/types/accounts.ts new file mode 100644 index 00000000000..0ca2dbe9e0e --- /dev/null +++ b/libs/finicity-api/src/types/accounts.ts @@ -0,0 +1,163 @@ +/** https://api-reference.finicity.com/#/rest/models/enumerations/account-type */ +export type AccountType = + | 'checking' + | 'savings' + | 'cd' + | 'moneyMarket' + | 'creditCard' + | 'lineOfCredit' + | 'investment' + | 'brokerageAccount' + | 'pension' + | 'profitSharingPlan' + | 'investmentTaxDeferred' + | 'employeeStockPurchasePlan' + | 'ira' + | 'simpleIRA' + | 'sepIRA' + | '401k' + | 'roth' + | 'roth401k' + | '403b' + | '529' + | '529plan' + | 'rollover' + | 'ugma' + | 'utma' + | 'keogh' + | '457' + | '457plan' + | '401a' + | 'cryptocurrency' + | 'mortgage' + | 'loan' + | 'studentLoan' + | 'studentLoanGroup' + | 'studentLoanAccount' + +/** https://api-reference.finicity.com/#/rest/models/structures/customer-account-position */ +export type CustomerAccountPosition = { + [key: string]: any + id?: number + description?: string + securityId?: string + securityIdType?: string + symbol?: string + /** @deprecated finicity still uses this field in lieu of `units` for some accounts (eg. Citibank) as of 2023-01-30 */ + quantity?: number + units?: number + currentPrice?: number + securityName?: string + /** @deprecated undocumented field */ + fundName?: string + transactionType?: string + marketValue?: number | string + costBasis?: number + status?: string + currentPriceDate?: number + invSecurityType?: string + mfType?: string + posType?: string + totalGLDollar?: number + totalGLPercent?: number + securityType?: string + securityCurrency?: string + fiAssetClass?: string + assetClass?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/customer-account-detail */ +export type CustomerAccountDetail = { + [key: string]: any + availableBalanceAmount?: number + availableCashBalance?: number + interestRate?: string + creditAvailableAmount?: number + paymentMinAmount?: number + statementCloseBalance?: number + locPrincipalBalance?: number + paymentDueDate?: number + statementEndDate?: number + vestedBalance?: number + currentLoanBalance?: number + payoffAmount?: number + principalBalance?: number + autoPayEnrolled?: 'Y' | 'N' + firstMortgage?: 'Y' | 'N' + recurringPaymentAmount?: number + lender?: string + endingBalanceAmount?: number + loanTermType?: string + paymentsMade?: number + balloonAmount?: number + paymentsRemaining?: number + loanMinAmtDue?: number + loanPaymentFreq?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/customer-account */ +export type CustomerAccount = { + [key: string]: any + id: string + accountNumberDisplay: string + realAccountNumberLast4?: string + name: string + balance?: number + type: AccountType + aggregationStatusCode?: number + status: string + customerId: string + institutionId: string + balanceDate: number + aggregationSuccessDate?: number + aggregationAttemptDate?: number + createdDate: number + currency: string + lastTransactionDate?: number + /** Incorrectly shown as "Required" in Finicity docs */ + oldestTransactionDate?: number + institutionLoginId: number + detail?: CustomerAccountDetail + position?: CustomerAccountPosition[] + displayPosition: number + parentAccount?: number + + /** Not in Finicity docs */ + accountNickname?: string + /** Not in Finicity docs */ + marketSegment?: string + + /** @deprecated */ + number?: string +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/accounts/get-customer-accounts */ +export type GetCustomerAccountsRequest = { + customerId: string + status?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/customer-accounts */ +export type GetCustomerAccountsResponse = { + accounts: CustomerAccount[] +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/accounts/get-customer-account */ +export type GetCustomerAccountRequest = { + customerId: string + accountId: number +} + +export type GetCustomerAccountResponse = CustomerAccount + +export type RefreshCustomerAccountRequest = { + customerId: string | number +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/accounts/delete-customer-accounts-by-institution-login */ +export type DeleteCustomerAccountsByInstitutionLoginRequest = { + customerId: string + institutionLoginId: number +} + +export type DeleteCustomerAccountsByInstitutionLoginResponse = void diff --git a/libs/finicity-api/src/types/authentication.ts b/libs/finicity-api/src/types/authentication.ts new file mode 100644 index 00000000000..c9a97c83d69 --- /dev/null +++ b/libs/finicity-api/src/types/authentication.ts @@ -0,0 +1,4 @@ +/** https://api-reference.finicity.com/#/rest/models/structures/authentication-response */ +export type AuthenticationResponse = { + token: string +} diff --git a/libs/finicity-api/src/types/connect.ts b/libs/finicity-api/src/types/connect.ts new file mode 100644 index 00000000000..50edc1cd92b --- /dev/null +++ b/libs/finicity-api/src/types/connect.ts @@ -0,0 +1,32 @@ +/** https://api-reference.finicity.com/#/rest/models/structures/generate-connect-url-request-lite-v2 */ +export type GenerateLiteConnectUrlRequest = { + partnerId: string + customerId: string + institutionId: string + redirectUri?: string + webhook?: string + webhookContentType?: string + webhookData?: object + webhookHeaders?: object + experience?: string + singleUseUrl?: boolean +} + +/** https://api-reference.finicity.com/#/rest/models/structures/generate-connect-url-request-fix-v2 */ +export type GenerateFixConnectUrlRequest = { + partnerId: string + customerId: string + institutionLoginId: string | number + redirectUri?: string + webhook?: string + webhookContentType?: string + webhookData?: object + webhookHeaders?: object + experience?: string + singleUseUrl?: boolean +} + +/** https://api-reference.finicity.com/#/rest/models/structures/generate-connect-url-response */ +export type GenerateConnectUrlResponse = { + link: string +} diff --git a/libs/finicity-api/src/types/customers.ts b/libs/finicity-api/src/types/customers.ts new file mode 100644 index 00000000000..d56b0b17cc9 --- /dev/null +++ b/libs/finicity-api/src/types/customers.ts @@ -0,0 +1,14 @@ +/** https://api-reference.finicity.com/#/rest/models/structures/add-customer-request */ +export type AddCustomerRequest = { + username: string + firstName?: string + lastName?: string + applicationId?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/add-customer-response */ +export type AddCustomerResponse = { + id: string + username: string + createdDate: string +} diff --git a/libs/finicity-api/src/types/index.ts b/libs/finicity-api/src/types/index.ts new file mode 100644 index 00000000000..e4fd5f22d0c --- /dev/null +++ b/libs/finicity-api/src/types/index.ts @@ -0,0 +1,8 @@ +export * from './accounts' +export * from './authentication' +export * from './connect' +export * from './customers' +export * from './institutions' +export * from './transactions' +export * from './webhooks' +export * from './txpush' diff --git a/libs/finicity-api/src/types/institutions.ts b/libs/finicity-api/src/types/institutions.ts new file mode 100644 index 00000000000..dd58fcdbe5a --- /dev/null +++ b/libs/finicity-api/src/types/institutions.ts @@ -0,0 +1,65 @@ +/** https://api-reference.finicity.com/#/rest/models/structures/institution-address */ +export type InstitutionAddress = { + city?: string + state?: string + country?: string + postalCode?: string + addressLine1?: string + addressLine2?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/get-institutions-institution-branding */ +export type InstitutionBranding = { + logo?: string + alternateLogo?: string + icon?: string + primaryColor?: string + tile?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/institution */ +export type Institution = { + id: number + name?: string + transAgg: boolean + ach: boolean + stateAgg: boolean + voi: boolean + voa: boolean + aha: boolean + availBalance: boolean + accountOwner: boolean + accountTypeDescription?: string + phone?: string + urlHomeApp?: string + urlLogonApp?: string + oauthEnabled: boolean + urlForgotPassword?: string + urlOnlineRegistration?: string + class?: string + specialText?: string + specialInstructions?: string[] + address?: InstitutionAddress + currency: string + email?: string + status: string + newInstitutionId?: number + branding?: InstitutionBranding + oauthInstitutionId?: number +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/institutions/get-institutions */ +export type GetInstitutionsRequest = { + search?: string + start?: number + limit?: number +} + +/** https://api-reference.finicity.com/#/rest/models/structures/get-institutions-response */ +export type GetInstitutionsResponse = { + found: number + displaying: number + moreAvailable: boolean + createdDate: string + institutions: Institution[] +} diff --git a/libs/finicity-api/src/types/transactions.ts b/libs/finicity-api/src/types/transactions.ts new file mode 100644 index 00000000000..77cb5ef190d --- /dev/null +++ b/libs/finicity-api/src/types/transactions.ts @@ -0,0 +1,98 @@ +/** https://api-reference.finicity.com/#/rest/models/enumerations/transaction-type */ +export type TransactionType = + | 'atm' + | 'cash' + | 'check' + | 'credit' + | 'debit' + | 'deposit' + | 'directDebit' + | 'directDeposit' + | 'dividend' + | 'fee' + | 'interest' + | 'other' + | 'payment' + | 'pointOfSale' + | 'repeatPayment' + | 'serviceCharge' + | 'transfer' + | 'DIV' // undocumented + | 'SRVCHG' // undocumented + +/** https://api-reference.finicity.com/#/rest/models/structures/categorization */ +export type TransactionCategorization = { + [key: string]: any + normalizedPayeeName: string + /** https://api-reference.finicity.com/#/rest/models/enumerations/categories */ + category: string + city?: string + state?: string + postalCode?: string + country: string + bestRepresentation?: string +} + +/** https://api-reference.finicity.com/#/rest/models/structures/transaction */ +export type Transaction = { + [key: string]: any + id: number + amount: number + accountId: number + customerId: number + status: 'active' | 'pending' | 'shadow' + description: string + memo?: string + postedDate: number + transactionDate?: number + effectiveDate?: number + firstEffectiveDate?: number + createdDate: number + type?: TransactionType | string + checkNum?: number + escrowAmount?: number + feeAmount?: number + interestAmount?: number + principalAmount?: number + unitQuantity?: number + unitPrice?: number + categorization?: TransactionCategorization + subaccountSecurityType?: string + commissionAmount?: number + symbol?: string + ticker?: string + investmentTransactionType?: string + taxesAmount?: number + currencySymbol?: string + securityId?: string + securityIdType?: string +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/transactions/get-customer-account-transactions */ +export type GetAccountTransactionsRequest = { + customerId: string + accountId: string + fromDate: number + toDate: number + start?: number + limit?: number + sort?: 'asc' | 'desc' + includePending?: boolean +} + +/** https://api-reference.finicity.com/#/rest/models/structures/get-transactions-response */ +export type GetAccountTransactionsResponse = { + found: number + displaying: number + moreAvailable: string + fromDate: string + toDate: string + sort: string + transactions: Transaction[] +} + +/** https://api-reference.finicity.com/#/rest/api-endpoints/accounts/load-historic-transactions-for-customer-account */ +export type LoadHistoricTransactionsRequest = { + customerId: string + accountId: string +} diff --git a/libs/finicity-api/src/types/txpush.ts b/libs/finicity-api/src/types/txpush.ts new file mode 100644 index 00000000000..ca6519f92f7 --- /dev/null +++ b/libs/finicity-api/src/types/txpush.ts @@ -0,0 +1,41 @@ +import type { CustomerAccount } from './accounts' +import type { Transaction } from './transactions' + +export type TxPushSubscriptionRequest = { + customerId: string | number + accountId: string | number + callbackUrl: string +} + +type SubscriptionRecord = { + id: number + accountId: number + type: 'account' | 'transaction' + callbackUrl: string + signingKey: string +} + +export type TxPushSubscriptions = { + subscriptions: SubscriptionRecord[] +} + +export type TxPushEvent = + | { + class: 'transaction' + type: 'created' | 'modified' | 'deleted' + records: Transaction[] + } + | { + class: 'account' + type: 'modified' | 'deleted' + records: CustomerAccount[] + } + +export type TxPushEventMessage = { + event: TxPushEvent +} + +export type TxPushDisableRequest = { + customerId: string | number + accountId: string | number +} diff --git a/libs/finicity-api/src/types/webhooks.ts b/libs/finicity-api/src/types/webhooks.ts new file mode 100644 index 00000000000..bcc01b322e4 --- /dev/null +++ b/libs/finicity-api/src/types/webhooks.ts @@ -0,0 +1,37 @@ +import type { CustomerAccount } from './accounts' + +/** https://docs.finicity.com/webhook-events-list/#webhooks-2-3 */ +export type WebhookData = + | { + eventType: 'ping' + } + | { + eventType: 'added' | 'discovered' + payload: { + accounts: CustomerAccount[] + institutionId: string + } + } + | { + eventType: 'done' + customerId: string + } + | { + eventType: 'institutionNotFound' + payload: { + query: string + } + } + | { + eventType: 'institutionNotSupported' + payload: { + institutionId: string + } + } + | { + eventType: 'unableToConnect' + payload: { + institutionId: string + code: number + } + } diff --git a/libs/finicity-api/tsconfig.json b/libs/finicity-api/tsconfig.json new file mode 100644 index 00000000000..1e5701a2202 --- /dev/null +++ b/libs/finicity-api/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/finicity-api/tsconfig.lib.json b/libs/finicity-api/tsconfig.lib.json new file mode 100644 index 00000000000..e3cabfb5d5d --- /dev/null +++ b/libs/finicity-api/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/finicity-api/tsconfig.spec.json b/libs/finicity-api/tsconfig.spec.json new file mode 100644 index 00000000000..146143f1b3e --- /dev/null +++ b/libs/finicity-api/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.test.ts", + "**/*.spec.ts", + "**/*.test.tsx", + "**/*.spec.tsx", + "**/*.test.js", + "**/*.spec.js", + "**/*.test.jsx", + "**/*.spec.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/libs/server/features/.babelrc b/libs/server/features/.babelrc new file mode 100644 index 00000000000..4496e8f26c8 --- /dev/null +++ b/libs/server/features/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/server/features/.eslintrc.json b/libs/server/features/.eslintrc.json new file mode 100644 index 00000000000..9761c563892 --- /dev/null +++ b/libs/server/features/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/server/features/README.md b/libs/server/features/README.md new file mode 100644 index 00000000000..00b14d52a81 --- /dev/null +++ b/libs/server/features/README.md @@ -0,0 +1,7 @@ +# server-features + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test server-features` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/server/features/jest.config.ts b/libs/server/features/jest.config.ts new file mode 100644 index 00000000000..32a96dcd5f3 --- /dev/null +++ b/libs/server/features/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'server-features', + preset: '../../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../coverage/libs/server/features', +} diff --git a/libs/server/features/src/account-balance/balance-sync.strategy.ts b/libs/server/features/src/account-balance/balance-sync.strategy.ts new file mode 100644 index 00000000000..e5fae685b77 --- /dev/null +++ b/libs/server/features/src/account-balance/balance-sync.strategy.ts @@ -0,0 +1,40 @@ +import { DateUtil } from '@maybe-finance/shared' +import type { Account, PrismaClient } from '@prisma/client' +import { DateTime } from 'luxon' + +export interface IBalanceSyncStrategy { + syncAccountBalances(account: Account): Promise +} + +export abstract class BalanceSyncStrategyBase implements IBalanceSyncStrategy { + constructor(protected readonly prisma: PrismaClient) {} + + async syncAccountBalances(account: Account) { + const [{ date }] = await this.prisma.$queryRaw< + [{ date: Date }] + >`SELECT account_value_start_date(${account.id}::int) AS date` + + const startDate = DateTime.max( + DateUtil.MIN_SUPPORTED_DATE, + DateTime.fromJSDate(date, { zone: 'utc' }) + ) + + await this.syncBalances(account, startDate) + } + + protected abstract syncBalances(account: Account, startDate: DateTime): Promise +} + +export interface IBalanceSyncStrategyFactory { + for(account: Account): IBalanceSyncStrategy +} + +export class BalanceSyncStrategyFactory implements IBalanceSyncStrategyFactory { + constructor(private readonly strategies: Record) {} + + for(account: Account): IBalanceSyncStrategy { + const strategy = this.strategies[account.type] + if (!strategy) throw new Error(`cannot find strategy for account: ${account.id}`) + return strategy + } +} diff --git a/libs/server/features/src/account-balance/index.ts b/libs/server/features/src/account-balance/index.ts new file mode 100644 index 00000000000..503a503abe7 --- /dev/null +++ b/libs/server/features/src/account-balance/index.ts @@ -0,0 +1,5 @@ +export * from './balance-sync.strategy' +export * from './investment-transaction-balance-sync.strategy' +export * from './transaction-balance-sync.strategy' +export * from './valuation-balance-sync.strategy' +export * from './loan-balance-sync.strategy' diff --git a/libs/server/features/src/account-balance/investment-transaction-balance-sync.strategy.ts b/libs/server/features/src/account-balance/investment-transaction-balance-sync.strategy.ts new file mode 100644 index 00000000000..21f0d914084 --- /dev/null +++ b/libs/server/features/src/account-balance/investment-transaction-balance-sync.strategy.ts @@ -0,0 +1,144 @@ +import type { Account, PrismaClient } from '@prisma/client' +import { Prisma } from '@prisma/client' +import type { DateTime } from 'luxon' +import type { Logger } from 'winston' +import { BalanceSyncStrategyBase } from './balance-sync.strategy' + +export class InvestmentTransactionBalanceSyncStrategy extends BalanceSyncStrategyBase { + constructor(private readonly logger: Logger, prisma: PrismaClient) { + super(prisma) + } + + async syncBalances(account: Account, startDate: DateTime) { + const pAccountId = Prisma.raw(account.id.toString()) + const pStart = Prisma.raw(`'${startDate.toISODate()}'`) + + await this.prisma.$executeRaw` + WITH holdings AS ( + -- historical (artificial) holdings (eg. user bought 100 shares, then sold 100 shares, and now no longer has a holding record) + ( + SELECT DISTINCT ON (it.account_id, it.security_id) + CONCAT(it.account_id, '|', it.security_id) AS id, + it.security_id, + 0 AS quantity, + 0 AS value + FROM + investment_transaction it + LEFT JOIN holding h ON h.account_id = it.account_id AND h.security_id = it.security_id + WHERE + it.account_id = ${pAccountId} + AND it.security_id IS NOT NULL + AND h.id IS NULL + ) + UNION + -- current holdings + ( + SELECT + h.id::text, + h.security_id, + h.quantity, + h.value + FROM + holding h + INNER JOIN security s ON s.id = h.security_id + WHERE + h.account_id = ${pAccountId} + AND NOT h.excluded + AND NOT s.is_brokerage_cash + ) + ), holdings_daily AS ( + SELECT + d.date, + sp.price, + s.shares_per_contract, + h.quantity - SUM(COALESCE(it.quantity, 0)) OVER (PARTITION BY h.id ORDER BY it.date DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) as quantity, + h.value as current_value + FROM + holdings h + CROSS JOIN ( + SELECT generate_series(${pStart}, now(), '1d')::date + ) d(date) + INNER JOIN security s ON s.id = h.security_id + LEFT JOIN ( + SELECT + time_bucket_gapfill('1d', it.date) AS date, + it.security_id, + COALESCE(SUM(CASE it.flow WHEN 'INFLOW' THEN -ABS(it.quantity) ELSE ABS(it.quantity) END), 0) AS quantity + FROM + investment_transaction it + WHERE + it.account_id = ${pAccountId} + AND it.date BETWEEN ${pStart} AND now() + AND ( -- filter for transactions that modify a position + it.plaid_type IN ('buy', 'sell', 'transfer') + OR it.finicity_transaction_id IS NOT NULL + ) + GROUP BY + 1, 2 + ) it ON it.security_id = s.id AND it.date = d.date + LEFT JOIN ( + SELECT + time_bucket_gapfill('1d', sp.date) AS date, + sp.security_id, + locf(avg(sp.price_close)) AS price + FROM + security_pricing sp + WHERE + sp.date BETWEEN ${pStart} AND now() + AND sp.security_id IN (SELECT DISTINCT security_id FROM holdings) + GROUP BY + 1, 2 + ) sp ON sp.security_id = s.id AND sp.date = d.date + ), holding_balances AS ( + SELECT + hd.date, + SUM(COALESCE(hd.price * hd.quantity * COALESCE(hd.shares_per_contract, 1), hd.current_value)) AS balance + FROM + holdings_daily hd + GROUP BY + hd.date + ), cash_balances AS ( + SELECT + it.date, + -- IF available_balance is null/0 AND account has 0 holdings THEN we use current_balance as a constant historical balance + COALESCE(NULLIF(a.available_balance, 0), (SELECT CASE WHEN EXISTS (SELECT 1 FROM holdings) THEN 0 ELSE a.current_balance END)) + + COALESCE(SUM(it.amount) OVER (ORDER BY it.date DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) AS balance, + it.inflows, + it.outflows + FROM + account a + LEFT JOIN LATERAL ( + SELECT + time_bucket_gapfill('1d', it.date) AS date, + COALESCE(SUM(it.amount), 0) AS amount, + COALESCE(SUM(ABS(it.amount)) FILTER (WHERE it.flow = 'INFLOW'), 0) AS inflows, + COALESCE(SUM(ABS(it.amount)) FILTER (WHERE it.flow = 'OUTFLOW'), 0) AS outflows + FROM + investment_transaction it + WHERE + it.account_id = a.id + AND it.date BETWEEN ${pStart} AND now() + GROUP BY + 1 + ) it ON TRUE + WHERE + a.id = ${pAccountId} + ) + INSERT INTO account_balance (account_id, date, balance, inflows, outflows) + SELECT + ${pAccountId}, + COALESCE(hb.date, cb.date) AS date, + COALESCE(hb.balance + cb.balance, cb.balance, 0) AS balance, + cb.inflows, + cb.outflows + FROM + holding_balances hb + FULL OUTER JOIN cash_balances cb ON cb.date = hb.date + ON CONFLICT (account_id, date) DO UPDATE + SET + balance = EXCLUDED.balance, + inflows = EXCLUDED.inflows, + outflows = EXCLUDED.outflows; + ` + } +} diff --git a/libs/server/features/src/account-balance/loan-balance-sync.strategy.ts b/libs/server/features/src/account-balance/loan-balance-sync.strategy.ts new file mode 100644 index 00000000000..8fbe6721327 --- /dev/null +++ b/libs/server/features/src/account-balance/loan-balance-sync.strategy.ts @@ -0,0 +1,132 @@ +import { DateTime } from 'luxon' +import type { Account, PrismaClient } from '@prisma/client' +import { Prisma } from '@prisma/client' +import type { Logger } from 'winston' +import { BalanceSyncStrategyBase } from './balance-sync.strategy' + +export class LoanBalanceSyncStrategy extends BalanceSyncStrategyBase { + constructor(private readonly logger: Logger, prisma: PrismaClient) { + super(prisma) + } + + async syncBalances(account: Account, startDate: DateTime) { + if (!account.loan) { + this.logger.warn(`account ${account.id} is missing loan data, skipping balance sync`) + return + } + + const pAccountId = account.id + const pStart = Prisma.raw(`'${startDate.toISODate()}'`) + + const { + _min: { date: minDate }, + } = await this.prisma.transaction.aggregate({ + where: { accountId: account.id }, + _min: { date: true }, + }) + + // the cutoff date is one day prior to the first date we have transaction data for + // this serves as our stopping point for interpolation from the origination date + const pCutoffDate = minDate + ? Prisma.raw( + `'${DateTime.fromJSDate(minDate, { zone: 'utc' }) + .minus({ days: 1 }) + .toISODate()}'` + ) + : Prisma.raw('now()') + + await this.prisma.$executeRaw` + WITH interpolated_balances AS ( + -- interpolate balances from origination -> earliest transaction + SELECT + time_bucket_gapfill('1d', b.date) AS "date", + interpolate(avg(b.balance)) AS "balance" + FROM + ( + SELECT + (a.loan->>'originationDate')::date AS "date", + (a.loan->'originationPrincipal')::numeric AS "balance" + FROM + account a + WHERE + a.id = ${pAccountId} AND a.loan IS NOT NULL + UNION + SELECT + ${pCutoffDate}::date AS "date", + COALESCE(a.current_balance - COALESCE((SELECT SUM(amount) FROM "transaction" WHERE account_id = a.id AND date > ${pCutoffDate}), 0), 0) AS "balance" + FROM + account a + WHERE + a.id = ${pAccountId} + ) b + WHERE + b.date >= ${pStart} + AND b.date <= ${pCutoffDate} + GROUP BY + 1 + ), txn_balances AS ( + -- compute balances from now -> earliest transaction (using standard transaction calculation approach) + SELECT + t.date, + ( + COALESCE(a.current_balance, a.available_balance) - + COALESCE(SUM(t.net_flows) OVER (ORDER BY t.date DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) + ) as balance, + SUM(t.inflows) OVER (PARTITION BY t.date) as inflows, + SUM(t.outflows) OVER (PARTITION BY t.date) as outflows + FROM + account a, + ( + SELECT + time_bucket_gapfill('1d', t.date) AS date, + COALESCE(SUM(t.amount), 0) as net_flows, + COALESCE(SUM(ABS(amount)) FILTER (WHERE flow = 'INFLOW'), 0) as inflows, + COALESCE(SUM(amount) FILTER (WHERE flow = 'OUTFLOW'), 0) as outflows + FROM + transaction t + WHERE + t.account_id = ${pAccountId} + AND t.date > ${pCutoffDate} + AND t.date <= now() + GROUP BY + 1 + ) t + WHERE + a.id = ${pAccountId} + ), combined_balances AS ( + -- combine results + SELECT + date, + balance, + NULL AS "inflows", + NULL AS "outflows" + FROM + interpolated_balances ib + UNION + SELECT + date, + balance, + inflows, + outflows + FROM + txn_balances tb + ) + INSERT INTO account_balance (account_id, date, balance, inflows, outflows) + SELECT + ${pAccountId}, + date, + balance, + inflows, + outflows + FROM + combined_balances + WHERE + balance IS NOT NULL -- balance can be NULL for accounts w/o valid loan info + ON CONFLICT (account_id, date) DO UPDATE + SET + balance = EXCLUDED.balance, + inflows = EXCLUDED.inflows, + outflows = EXCLUDED.outflows + ` + } +} diff --git a/libs/server/features/src/account-balance/transaction-balance-sync.strategy.ts b/libs/server/features/src/account-balance/transaction-balance-sync.strategy.ts new file mode 100644 index 00000000000..c78e4789914 --- /dev/null +++ b/libs/server/features/src/account-balance/transaction-balance-sync.strategy.ts @@ -0,0 +1,53 @@ +import type { DateTime } from 'luxon' +import type { Account, PrismaClient } from '@prisma/client' +import type { Logger } from 'winston' +import { BalanceSyncStrategyBase } from './balance-sync.strategy' + +export class TransactionBalanceSyncStrategy extends BalanceSyncStrategyBase { + constructor(private readonly logger: Logger, prisma: PrismaClient) { + super(prisma) + } + + async syncBalances(account: Account, startDate: DateTime) { + const pAccountId = account.id + const pStart = startDate.toJSDate() + + await this.prisma.$executeRaw` + INSERT INTO account_balance (account_id, date, balance, inflows, outflows) + SELECT + t.account_id, + t.date, + ( + COALESCE(a.current_balance, a.available_balance) + + (CASE WHEN a.classification = 'liability' THEN -1 ELSE 1 END) * + COALESCE(SUM(t.net_flows) OVER (PARTITION BY t.account_id ORDER BY t.date DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0) + ) as balance, + SUM(t.inflows) OVER (PARTITION BY t.account_id, t.date) as inflows, + SUM(t.outflows) OVER (PARTITION BY t.account_id, t.date) as outflows + FROM + ( + SELECT + time_bucket_gapfill('1d', t.date) AS date, + t.account_id, + COALESCE(SUM(t.amount), 0) as net_flows, + COALESCE(SUM(ABS(amount)) FILTER (WHERE flow = 'INFLOW'), 0) as inflows, + COALESCE(SUM(amount) FILTER (WHERE flow = 'OUTFLOW'), 0) as outflows + FROM + transaction t + WHERE + t.account_id = ${pAccountId} + AND t.date BETWEEN ${pStart} AND now() + GROUP BY + 1, 2 + ) t + INNER JOIN account a ON a.id = t.account_id + WHERE + COALESCE(a.current_balance, a.available_balance) IS NOT NULL + ON CONFLICT (account_id, date) DO UPDATE + SET + inflows = EXCLUDED.inflows, + outflows = EXCLUDED.outflows, + balance = EXCLUDED.balance + ` + } +} diff --git a/libs/server/features/src/account-balance/valuation-balance-sync.strategy.ts b/libs/server/features/src/account-balance/valuation-balance-sync.strategy.ts new file mode 100644 index 00000000000..65bf1d4ff28 --- /dev/null +++ b/libs/server/features/src/account-balance/valuation-balance-sync.strategy.ts @@ -0,0 +1,40 @@ +import type { DateTime } from 'luxon' +import type { Account, PrismaClient } from '@prisma/client' +import type { Logger } from 'winston' +import { BalanceSyncStrategyBase } from './balance-sync.strategy' + +export class ValuationBalanceSyncStrategy extends BalanceSyncStrategyBase { + constructor(private readonly logger: Logger, prisma: PrismaClient) { + super(prisma) + } + + async syncBalances(account: Account, startDate: DateTime) { + const pAccountId = account.id + const pStart = startDate.toJSDate() + + await this.prisma.$executeRaw` + INSERT INTO account_balance (account_id, date, balance) + SELECT + v.account_id, + v.date, + COALESCE(v.interpolate, v.locf) AS balance + FROM + ( + SELECT + time_bucket_gapfill('1d', v.date) AS date, + v.account_id, + interpolate(avg(v.amount)), + locf(avg(v.amount)) + FROM + valuation v + WHERE + v.account_id = ${pAccountId} + AND v.date BETWEEN ${pStart} AND now() + GROUP BY + 1, 2 + ) v + ON CONFLICT (account_id, date) DO UPDATE + SET inflows = EXCLUDED.inflows, outflows = EXCLUDED.outflows, balance = EXCLUDED.balance + ` + } +} diff --git a/libs/server/features/src/account-connection/account-connection.processor.ts b/libs/server/features/src/account-connection/account-connection.processor.ts new file mode 100644 index 00000000000..085445d7332 --- /dev/null +++ b/libs/server/features/src/account-connection/account-connection.processor.ts @@ -0,0 +1,106 @@ +import type { AccountConnection } from '@prisma/client' +import type { SyncConnectionQueueJobData } from '@maybe-finance/server/shared' +import type { SharedType } from '@maybe-finance/shared' +import type { Logger } from 'winston' +import type { IAccountConnectionProviderFactory } from './account-connection.provider' +import type { IAccountConnectionService } from './account-connection.service' +import { ErrorUtil, ServerUtil } from '@maybe-finance/server/shared' +import * as Sentry from '@sentry/node' +import type { ITransactionService } from '../transaction' + +export interface IAccountConnectionProcessor { + sync( + jobData: SyncConnectionQueueJobData, + setProgress: (progress: SharedType.AccountSyncProgress) => Promise + ): Promise +} + +export class AccountConnectionProcessor implements IAccountConnectionProcessor { + constructor( + private readonly logger: Logger, + private readonly connectionService: IAccountConnectionService, + private readonly transactionService: ITransactionService, + private readonly providers: IAccountConnectionProviderFactory + ) {} + + async sync( + jobData: SyncConnectionQueueJobData, + setProgress: (progress: SharedType.AccountSyncProgress) => Promise + ) { + const connection = await this.connectionService.get(jobData.accountConnectionId) + const provider = this.providers.for(connection) + + await ServerUtil.useSync({ + onStart: async (connection) => { + this.logger.info(`[sync.onStart] connection=${connection.id}`, { + connection: connection.id, + }) + await this.connectionService.update(connection.id, { + syncStatus: 'SYNCING', + }) + }, + sync: async (connection) => { + await Promise.all([ + setProgress({ progress: 0.2, description: 'Syncing data' }), + provider.sync(connection, jobData.options), + ]) + }, + onSyncError: async (connection, error) => { + this.logger.error(`[sync.onSyncError] connection=${connection.id}`, { + connection: connection.id, + error: ErrorUtil.parseError(error), + }) + + const err = ErrorUtil.parseError(error) + Sentry.captureException(err, { + level: 'error', + tags: err.sentryTags, + contexts: err.sentryContexts, + }) + + await Promise.all([ + setProgress({ progress: 0.75, description: 'Syncing data' }), + provider.onSyncEvent(connection, { type: 'error', error }), + ]) + }, + onSyncSuccess: async (connection) => { + this.logger.info(`[sync.onSyncSuccess] connection=${connection.id}`, { + connection: connection.id, + }) + + await Promise.all([ + setProgress({ progress: 0.4, description: 'Syncing data' }), + provider.onSyncEvent(connection, { type: 'success' }), + ]) + + await Promise.all([ + setProgress({ progress: 0.6, description: 'Cleaning data' }), + this.transactionService.markTransfers(connection.userId), + ]) + + // Temporarily disable + // await Promise.all([ + // setProgress({ progress: 0.5, description: 'Syncing data' }), + // this.connectionService.syncSecurities(connection.id), + // ]) + + await Promise.all([ + setProgress({ progress: 0.75, description: 'Updating balances' }), + this.connectionService.syncBalances(connection.id), + ]) + }, + onEnd: async (connection) => { + this.logger.info(`[sync.onEnd] connection=${connection.id}`, { + connection: connection.id, + }) + + await Promise.all([ + setProgress({ progress: 0.9, description: 'Finishing up' }), + this.connectionService.update(connection.id, { + syncStatus: 'IDLE', + }), + ]) + }, + })(connection) + } +} diff --git a/libs/server/features/src/account-connection/account-connection.provider.ts b/libs/server/features/src/account-connection/account-connection.provider.ts new file mode 100644 index 00000000000..8f11d9bfb09 --- /dev/null +++ b/libs/server/features/src/account-connection/account-connection.provider.ts @@ -0,0 +1,26 @@ +import type { SyncConnectionOptions } from '@maybe-finance/server/shared' +import type { AccountConnection } from '@prisma/client' + +export type AccountConnectionSyncEvent = { type: 'error'; error: unknown } | { type: 'success' } + +export interface IAccountConnectionProvider { + sync(connection: AccountConnection, options?: SyncConnectionOptions): Promise + onSyncEvent(connection: AccountConnection, event: AccountConnectionSyncEvent): Promise + delete(connection: AccountConnection): Promise +} + +export interface IAccountConnectionProviderFactory { + for(connection: AccountConnection): IAccountConnectionProvider +} + +export class AccountConnectionProviderFactory implements IAccountConnectionProviderFactory { + constructor( + private readonly providers: Record + ) {} + + for(connection: AccountConnection): IAccountConnectionProvider { + const provider = this.providers[connection.type] + if (!provider) throw new Error(`Unsupported connection type: ${connection.type}`) + return provider + } +} diff --git a/libs/server/features/src/account-connection/account-connection.service.ts b/libs/server/features/src/account-connection/account-connection.service.ts new file mode 100644 index 00000000000..b4e0940dcad --- /dev/null +++ b/libs/server/features/src/account-connection/account-connection.service.ts @@ -0,0 +1,227 @@ +import type { SharedType } from '@maybe-finance/shared' +import type { SyncConnectionOptions, SyncConnectionQueue } from '@maybe-finance/server/shared' +import type { AccountConnection, User, PrismaClient, Prisma } from '@prisma/client' +import type { Logger } from 'winston' +import type { IAccountConnectionProviderFactory } from './account-connection.provider' +import type { IBalanceSyncStrategyFactory } from '../account-balance' +import type { ISecurityPricingService } from '../security-pricing' +import { DateTime } from 'luxon' + +export interface IAccountConnectionService { + get(id: AccountConnection['id']): Promise + getAll(userId: User['id']): Promise + sync(id: AccountConnection['id'], options?: SyncConnectionOptions): Promise + syncBalances(id: AccountConnection['id']): Promise + syncSecurities(id: AccountConnection['id']): Promise + disconnect(id: AccountConnection['id']): Promise + reconnect(id: AccountConnection['id']): Promise + update( + id: AccountConnection['id'], + data: Prisma.AccountConnectionUncheckedUpdateInput + ): Promise + delete(id: AccountConnection['id']): Promise +} + +export class AccountConnectionService implements IAccountConnectionService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly providers: IAccountConnectionProviderFactory, + private readonly balanceSyncStrategyFactory: IBalanceSyncStrategyFactory, + private readonly securityPricingService: ISecurityPricingService, + private readonly queue: SyncConnectionQueue + ) {} + + async get(id: AccountConnection['id']) { + return this.prisma.accountConnection.findUniqueOrThrow({ + where: { id }, + include: { accounts: { orderBy: { id: 'asc' } } }, + }) + } + + async getAll(userId: User['id']) { + return this.prisma.accountConnection.findMany({ + where: { userId }, + include: { accounts: { orderBy: { id: 'asc' } } }, + orderBy: { id: 'asc' }, + }) + } + + async sync(id: AccountConnection['id'], options?: SyncConnectionOptions) { + const connection = await this.prisma.accountConnection.findUniqueOrThrow({ + where: { id }, + include: { user: true }, + }) + + await this.queue.add('sync-connection', { + accountConnectionId: connection.id, + options, + }) + + return this.prisma.accountConnection.update({ + where: { id }, + data: { syncStatus: 'PENDING' }, + }) + } + + async syncBalances(id: AccountConnection['id']) { + const connection = await this.get(id) + + const profiler = this.logger.startTimer() + + await Promise.all( + connection.accounts.map((account) => + this.balanceSyncStrategyFactory.for(account).syncAccountBalances(account) + ) + ) + + profiler.done({ message: `synced connection ${id} balances` }) + + return connection + } + + async syncSecurities(id: AccountConnection['id']) { + const securities = await this.prisma.security.findMany({ + where: { + AND: [ + { + OR: [ + { + holdings: { + some: { + account: { + accountConnectionId: id, + isActive: true, + }, + }, + }, + }, + { + investmentTransactions: { + some: { + account: { + accountConnectionId: id, + isActive: true, + }, + }, + }, + }, + ], + }, + { + OR: [ + { pricingLastSyncedAt: null }, + { + pricingLastSyncedAt: { + lt: DateTime.now().minus({ days: 1 }).toJSDate(), + }, + }, + ], + }, + ], + }, + select: { + id: true, + symbol: true, + plaidType: true, + }, + }) + + const profiler = this.logger.startTimer() + + await Promise.allSettled( + securities.map((security) => this.securityPricingService.sync(security)) + ) + + profiler.done({ message: `synced connection ${id} securities (${securities.length})` }) + } + + async disconnect(id: AccountConnection['id']) { + const [connection] = await this.prisma.$transaction([ + this.prisma.accountConnection.update({ + where: { id }, + data: { + status: 'DISCONNECTED', + }, + }), + this.prisma.account.updateMany({ + where: { accountConnectionId: id }, + data: { + isActive: false, + }, + }), + ]) + + this.logger.info( + `Disconnected connection id=${connection.id} type=${ + connection.type + } provider_connection_id=${ + connection.type === 'plaid' + ? connection.plaidItemId + : connection.finicityInstitutionId + }` + ) + + return connection + } + + async reconnect(id: AccountConnection['id']) { + const [connection] = await this.prisma.$transaction([ + this.prisma.accountConnection.update({ + where: { id }, + data: { + status: 'OK', + }, + }), + this.prisma.account.updateMany({ + where: { accountConnectionId: id }, + data: { + isActive: true, + }, + }), + ]) + + this.logger.info( + `Reconnected connection id=${connection.id} type=${ + connection.type + } provider_connection_id=${ + connection.type === 'plaid' + ? connection.plaidItemId + : connection.finicityInstitutionId + }` + ) + + return connection + } + + async update(id: AccountConnection['id'], data: Prisma.AccountConnectionUncheckedUpdateInput) { + return this.prisma.accountConnection.update({ + where: { id }, + data, + }) + } + + async delete(id: AccountConnection['id']) { + const connection = await this.prisma.accountConnection.findUniqueOrThrow({ + where: { id }, + }) + + await this.providers.for(connection).delete(connection) + + const deletedConnection = await this.prisma.accountConnection.delete({ + where: { id: connection.id }, + }) + + this.logger.info( + `Deleted connection id=${deletedConnection.id} type=${ + connection.type + } provider_connection_id=${ + connection.type === 'plaid' + ? connection.plaidItemId + : connection.finicityInstitutionId + }` + ) + + return deletedConnection + } +} diff --git a/libs/server/features/src/account-connection/index.ts b/libs/server/features/src/account-connection/index.ts new file mode 100644 index 00000000000..afcae9ff088 --- /dev/null +++ b/libs/server/features/src/account-connection/index.ts @@ -0,0 +1,3 @@ +export * from './account-connection.service' +export * from './account-connection.processor' +export * from './account-connection.provider' diff --git a/libs/server/features/src/account/account-query.service.ts b/libs/server/features/src/account/account-query.service.ts new file mode 100644 index 00000000000..fa2069044d6 --- /dev/null +++ b/libs/server/features/src/account/account-query.service.ts @@ -0,0 +1,529 @@ +import type { + User, + Valuation, + AccountCategory, + AccountConnection, + Account, + AccountClassification, +} from '@prisma/client' +import { Prisma } from '@prisma/client' +import type { Logger } from 'winston' +import type { DateTime } from 'luxon' +import _ from 'lodash' +import type { SharedType } from '@maybe-finance/shared' +import type { PgService } from '@maybe-finance/server/shared' +import { raw, sql, DbUtil } from '@maybe-finance/server/shared' + +type PaginationOptions = { page: number; pageSize: number } + +type ValuationTrend = { + date: string + amount: Prisma.Decimal + valuation_id: Valuation['id'] | null + period_change: Prisma.Decimal | null + period_change_pct: Prisma.Decimal | null + total_change: Prisma.Decimal + total_change_pct: Prisma.Decimal +} + +type BalanceSeries = { + account_id: Account['id'] + date: string + balance: Prisma.Decimal +} + +type ReturnSeries = { + account_id: Account['id'] + date: string + rate_of_return: Prisma.Decimal + contributions: Prisma.Decimal + contributions_period: Prisma.Decimal +} + +type NetWorthSeries = { + date: string + netWorth: Prisma.Decimal + assets: Prisma.Decimal + liabilities: Prisma.Decimal + categories: Partial> +} + +type AccountRollup = { + date: string + classification: Account['classification'] + category: Account['category'] | null + id: Account['id'] | null + balance: Prisma.Decimal + rollup_pct: Prisma.Decimal + total_pct: Prisma.Decimal + grouping: 'classification' | 'category' | 'account' + account: + | (Pick & { + connection: Pick | null + }) + | null +} + +export interface IAccountQueryService { + getHoldingsEnriched( + accountId: Account['id'], + options: PaginationOptions + ): Promise< + Array< + SharedType.HoldingEnriched & { + cost_basis_user: Prisma.Decimal | null + cost_basis_provider: Prisma.Decimal | null + } + > + > + getValuationTrends( + accountId: Account['id'], + start?: DateTime, + end?: DateTime + ): Promise + getReturnSeries( + accountId: Account['id'] | Account['id'][], + start: string, + end: string + ): Promise + getBalanceSeries( + accountId: Account['id'] | Account['id'][], + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ): Promise + getNetWorthSeries( + id: { userId: User['id'] } | { accountIds: Account['id'][] }, + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ): Promise + getRollup( + id: { accountId: Account['id'] } | { userId: User['id'] }, + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ): Promise +} + +export class AccountQueryService implements IAccountQueryService { + constructor(private readonly logger: Logger, private readonly pg: PgService) {} + + async getHoldingsEnriched(accountId: Account['id'], { page, pageSize }: PaginationOptions) { + const { rows } = await this.pg.pool.query< + SharedType.HoldingEnriched & { + cost_basis_user: Prisma.Decimal | null + cost_basis_provider: Prisma.Decimal | null + } + >( + sql` + SELECT + h.id, + h.security_id, + s.name, + s.symbol, + s.shares_per_contract, + he.quantity, + he.value, + he.cost_basis, + h.cost_basis_user, + h.cost_basis_provider, + he.cost_basis_per_share, + he.price, + he.price_prev, + he.excluded + FROM + holdings_enriched he + INNER JOIN security s ON s.id = he.security_id + INNER JOIN holding h ON h.id = he.id + WHERE + he.account_id = ${accountId} + ORDER BY + he.excluded ASC, + he.value DESC + OFFSET ${page * pageSize} + LIMIT ${pageSize}; + ` + ) + + return rows + } + + async getValuationTrends(accountId: Account['id'], start?: DateTime, end?: DateTime) { + // start/end date SQL query params + const pStart = start + ? raw(`'${start.toISODate()}'`) + : sql`account_value_start_date(${accountId}::int)` + const pEnd = end ? raw(`'${end.toISODate()}'`) : sql`now()` + + const { rows } = await this.pg.pool.query( + sql` + WITH valuation_trends AS ( + SELECT + date, + COALESCE(interpolated::numeric, filled) AS amount + FROM ( + SELECT + time_bucket_gapfill('1d', v.date) AS date, + interpolate(avg(v.amount)) AS interpolated, + locf(avg(v.amount)) AS filled + FROM + valuation v + WHERE + v.account_id = ${accountId} + AND v.date BETWEEN ${pStart} AND ${pEnd} + GROUP BY + 1 + ) valuations_gapfilled + WHERE + to_char(date, 'MM-DD') = '01-01' + ), valuations_combined AS ( + SELECT + COALESCE(v.date, vt.date) AS date, + COALESCE(v.amount, vt.amount) AS amount, + v.id AS valuation_id + FROM + (SELECT * FROM valuation WHERE account_id = ${accountId}) v + FULL OUTER JOIN valuation_trends vt ON vt.date = v.date + ) + SELECT + v.date, + v.amount, + v.valuation_id, + v.amount - v.prev_amount AS period_change, + ROUND((v.amount - v.prev_amount)::numeric / NULLIF(v.prev_amount, 0), 4) AS period_change_pct, + v.amount - v.first_amount AS total_change, + ROUND((v.amount - v.first_amount)::numeric / NULLIF(v.first_amount, 0), 4) AS total_change_pct + FROM ( + SELECT + *, + LAG(amount, 1) OVER (ORDER BY date ASC) AS prev_amount, + (SELECT amount FROM valuations_combined ORDER BY date ASC LIMIT 1) AS first_amount + FROM + valuations_combined + ) v + ORDER BY + v.date ASC + ` + ) + + return rows + } + + /** + * Return formula is the "Basic Return with Cashflows at period end" outlined here - https://www.kitces.com/blog/twr-dwr-irr-calculations-performance-reporting-software-methodology-gips-compliance/ + */ + async getReturnSeries(accountId: Account['id'], start: string, end: string) { + const pAccountIds = Array.isArray(accountId) ? accountId : [accountId] + const pStart = raw(`'${start}'`) + const pEnd = raw(`'${end}'`) + + const { rows } = await this.pg.pool.query( + sql` + WITH start_date AS ( + SELECT + a.id AS "account_id", + GREATEST(account_value_start_date(a.id), a.start_date) AS "start_date" + FROM + account a + WHERE + a.id = ANY(${pAccountIds}) + GROUP BY + 1 + ), external_flows AS ( + SELECT + it.account_id, + it.date, + SUM(it.amount) AS "amount" + FROM + investment_transaction it + LEFT JOIN start_date sd ON sd.account_id = it.account_id + WHERE + it.account_id = ANY(${pAccountIds}) + AND it.date BETWEEN sd.start_date AND ${pEnd} + -- filter for investment_transactions that represent external flows + AND ( + (it.plaid_type = 'cash' AND it.plaid_subtype IN ('contribution', 'deposit', 'withdrawal')) + OR (it.plaid_type = 'transfer' AND it.plaid_subtype IN ('transfer')) + OR (it.plaid_type = 'buy' AND it.plaid_subtype IN ('contribution')) + OR (it.finicity_transaction_id IS NOT NULL AND it.finicity_investment_transaction_type IN ('contribution', 'deposit', 'transfer')) + ) + GROUP BY + 1, 2 + ), external_flow_totals AS ( + SELECT + account_id, + SUM(amount) as "amount" + FROM + external_flows + GROUP BY + 1 + ), balances AS ( + SELECT + abg.account_id, + abg.date, + abg.balance, + 0 - SUM(COALESCE(ef.amount, 0)) OVER (PARTITION BY abg.account_id ORDER BY abg.date ASC) AS "contributions_period", + COALESCE(-1 * (eft.amount - coalesce(SUM(ef.amount) OVER (PARTITION BY abg.account_id ORDER BY abg.date DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING), 0)), 0) AS "contributions" + FROM + account_balances_gapfilled( + ${pStart}, + ${pEnd}, + '1d', + ${pAccountIds} + ) abg + LEFT JOIN external_flows ef ON ef.account_id = abg.account_id AND ef.date = abg.date + LEFT JOIN external_flow_totals eft ON eft.account_id = abg.account_id + ) + SELECT + b.account_id, + b.date, + b.balance, + b.contributions, + b.contributions_period, + COALESCE(ROUND((b.balance - b0.balance - b.contributions_period) / COALESCE(NULLIF(b0.balance, 0), NULLIF(b.contributions_period, 0)), 4), 0) AS "rate_of_return" + FROM + balances b + LEFT JOIN ( + SELECT DISTINCT ON (account_id) + account_id, + balance + FROM + balances + ORDER BY + account_id, date ASC + ) b0 ON b0.account_id = b.account_id + ` + ) + + return rows + } + + async getBalanceSeries( + accountId: Account['id'] | Account['id'][], + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ) { + // by defining the query params upfront like this, we can easily copy-paste the query to debug in TablePlus w/ query param substitution + const pAccountIds = Array.isArray(accountId) ? accountId : [accountId] + const pStart = raw(`'${start}'`) + const pEnd = raw(`'${end}'`) + const pInterval = raw(`'${DbUtil.toPgInterval(interval)}'`) + + const { rows } = await this.pg.pool.query( + sql` + SELECT + abg.account_id, + abg.date, + abg.balance + FROM + account_balances_gapfilled( + ${pStart}, + ${pEnd}, + ${pInterval}, + ${pAccountIds} + ) abg + ` + ) + + return rows + } + + /** + * returns net worth time series for an account + */ + async getNetWorthSeries( + id: { userId: User['id'] } | { accountIds: Account['id'][] }, + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ) { + // by defining the query params upfront like this, we can easily copy-paste the query to debug in TablePlus w/ query param substitution + const pAccountIds = + 'accountIds' in id + ? id.accountIds + : sql`( + SELECT + array_agg(a.id) + FROM + account a + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE + (a.user_id = ${id.userId} OR ac.user_id = ${id.userId}) + AND a.is_active + )` + + const pStart = raw(`'${start}'`) + const pEnd = raw(`'${end}'`) + const pInterval = raw(`'${DbUtil.toPgInterval(interval)}'`) + + const { rows } = await this.pg.pool.query< + | { + date: string + classification: null + category: null + balance: Prisma.Decimal + } + | { + date: string + classification: AccountClassification + category: null + balance: Prisma.Decimal + } + | { + date: string + classification: AccountClassification + category: AccountCategory + balance: Prisma.Decimal + } + >( + sql` + SELECT + abg.date, + a.category, + a.classification, + SUM(CASE WHEN a.classification = 'asset' THEN abg.balance ELSE -abg.balance END) AS balance + FROM + account_balances_gapfilled( + ${pStart}, + ${pEnd}, + ${pInterval}, + ${pAccountIds} + ) abg + INNER JOIN account a ON a.id = abg.account_id + GROUP BY + GROUPING SETS ( + (abg.date, a.classification, a.category), + (abg.date, a.classification), + (abg.date) + ) + ORDER BY date ASC; + ` + ) + + // Group independent rows into NetWorthSeries objects + return _(rows) + .groupBy((r) => r.date) + .mapValues((data, date) => ({ + ...data.reduce( + (acc, d) => { + if (d.classification == null) { + return { + ...acc, + netWorth: d.balance, + } + } + + if (d.category == null) { + return d.classification === 'asset' + ? { ...acc, assets: d.balance } + : { ...acc, liabilities: d.balance } + } + + return { + ...acc, + categories: { + ...acc.categories, + [d.category]: d.balance, + }, + } + }, + { + date, + netWorth: new Prisma.Decimal(0), + assets: new Prisma.Decimal(0), + liabilities: new Prisma.Decimal(0), + categories: {}, + } + ), + })) + .values() + .value() + } + + async getRollup( + id: { accountId: Account['id'] } | { userId: User['id'] }, + start: string, + end: string, + interval: SharedType.TimeSeriesInterval + ) { + // by defining the query params upfront like this, we can easily copy-paste the query to debug in TablePlus w/ query param substitution + const pAccountIds = + 'accountId' in id + ? [id.accountId] + : sql`( + SELECT + array_agg(a.id) + FROM + account a + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE + (a.user_id = ${id.userId} OR ac.user_id = ${id.userId}) + AND a.is_active + )` + + const pStart = raw(`'${start}'`) + const pEnd = raw(`'${end}'`) + const pInterval = raw(`'${DbUtil.toPgInterval(interval)}'`) + + const { rows } = await this.pg.pool.query( + sql` + WITH account_rollup AS ( + SELECT + abg.date, + a.classification, + a.category, + a.id, + SUM(abg.balance) AS balance, + CASE GROUPING(abg.date, a.classification, a.category, a.id) + WHEN 3 THEN 'classification' + WHEN 1 THEN 'category' + WHEN 0 THEN 'account' + ELSE NULL + END AS grouping + FROM + account_balances_gapfilled( + ${pStart}, + ${pEnd}, + ${pInterval}, + ${pAccountIds} + ) abg + INNER JOIN account a ON a.id = abg.account_id + GROUP BY + GROUPING SETS ( + (abg.date, a.classification, a.category, a.id), + (abg.date, a.classification, a.category), + (abg.date, a.classification) + ) + ) + SELECT + ar.date, + ar.classification, + ar.category, + ar.id, + ar.balance, + ar.grouping, + CASE + WHEN a.id IS NULL THEN NULL + ELSE json_build_object('id', a.id, 'name', a.name, 'mask', a.mask, 'syncStatus', a.sync_status, 'connection', CASE WHEN ac.id IS NULL THEN NULL ELSE json_build_object('name', ac.name, 'syncStatus', ac.sync_status) END) + END AS account, + ROUND( + CASE ar.grouping + WHEN 'account' THEN COALESCE(ar.balance / SUM(NULLIF(ar.balance, 0)) OVER (PARTITION BY ar.grouping, ar.date, ar.classification, ar.category), 0) + WHEN 'category' THEN COALESCE(ar.balance / SUM(NULLIF(ar.balance, 0)) OVER (PARTITION BY ar.grouping, ar.date, ar.classification), 0) + WHEN 'classification' THEN COALESCE(ar.balance / SUM(NULLIF(ar.balance, 0)) OVER (PARTITION BY ar.grouping, ar.date), 0) + END, 4) AS rollup_pct, + ROUND(ar.balance / SUM(NULLIF(ar.balance, 0)) OVER (PARTITION BY ar.grouping, ar.date), 4) AS total_pct + FROM + account_rollup ar + LEFT JOIN account a ON a.id = ar.id + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + ORDER BY + ar.classification, ar.category, ar.id, ar.date; + ` + ) + + return rows + } +} diff --git a/libs/server/features/src/account/account.processor.ts b/libs/server/features/src/account/account.processor.ts new file mode 100644 index 00000000000..c05ca7f9034 --- /dev/null +++ b/libs/server/features/src/account/account.processor.ts @@ -0,0 +1,33 @@ +import type { SyncAccountQueueJobData } from '@maybe-finance/server/shared' +import type { Account } from '@prisma/client' +import type { Logger } from 'winston' +import { ServerUtil } from '@maybe-finance/server/shared' +import type { IAccountProviderFactory } from './account.provider' +import type { IAccountService } from './account.service' + +export interface IAccountProcessor { + sync(jobData: SyncAccountQueueJobData): Promise +} + +export class AccountProcessor implements IAccountProcessor { + constructor( + private readonly logger: Logger, + private readonly accountService: IAccountService, + private readonly providers: IAccountProviderFactory + ) {} + + async sync(jobData: SyncAccountQueueJobData) { + const account = await this.accountService.get(jobData.accountId) + const provider = this.providers.for(account) + + await ServerUtil.useSync({ + onStart: (account) => this.accountService.update(account.id, { syncStatus: 'SYNCING' }), + sync: (account) => provider.sync(account, jobData.options), + onSyncSuccess: (account) => this.accountService.syncBalances(account.id), + onSyncError: async (account, error) => { + this.logger.error(`error syncing account ${account.id}`, { error }) + }, + onEnd: (account) => this.accountService.update(account.id, { syncStatus: 'IDLE' }), + })(account) + } +} diff --git a/libs/server/features/src/account/account.provider.ts b/libs/server/features/src/account/account.provider.ts new file mode 100644 index 00000000000..3bd5836f6ec --- /dev/null +++ b/libs/server/features/src/account/account.provider.ts @@ -0,0 +1,31 @@ +import type { SyncAccountOptions } from '@maybe-finance/server/shared' +import type { Account } from '@prisma/client' + +export interface IAccountProvider { + sync(account: Account, options?: SyncAccountOptions): Promise + delete(account: Account): Promise +} + +export class NoOpAccountProvider implements IAccountProvider { + sync(_account: Account) { + return Promise.resolve() + } + + delete(_account: Account) { + return Promise.resolve() + } +} + +export interface IAccountProviderFactory { + for(account: Account): IAccountProvider +} + +export class AccountProviderFactory implements IAccountProviderFactory { + constructor( + private readonly providers: Partial> + ) {} + + for(account: Account): IAccountProvider { + return this.providers[account.provider] ?? new NoOpAccountProvider() + } +} diff --git a/libs/server/features/src/account/account.schema.ts b/libs/server/features/src/account/account.schema.ts new file mode 100644 index 00000000000..ea70a30a50c --- /dev/null +++ b/libs/server/features/src/account/account.schema.ts @@ -0,0 +1,211 @@ +import { DateUtil } from '@maybe-finance/shared' +import { AccountCategory } from '@prisma/client' +import { z } from 'zod' + +const CommonAccountFields = z.object({ + name: z.string(), + currencyCode: z.string().default('USD'), + isActive: z.boolean().default(true), + startDate: z + .string() + .nullable() + .transform((d) => (d ? DateUtil.datetimeTransform(d).toJSDate() : null)), +}) + +const ValuationAccountFields = z.object({ + provider: z.literal('user').default('user'), + valuations: z.object({ + originalBalance: z.number(), + currentBalance: z.number().nullish(), + currentDate: z.string().transform((d) => DateUtil.datetimeTransform(d)), + }), +}) + +// Property + +const PropertyBaseSchema = z.object({ + type: z.literal('PROPERTY'), + categoryUser: z.enum(['property']), + propertyMeta: z + .object({ + track: z.boolean().default(false), + address: z.object({ + line1: z.string(), + line2: z.string().optional(), + city: z.string(), + state: z.string(), + zip: z.string(), + country: z.string().default('United States'), + }), + }) + .optional(), +}) + +const PropertyCreateSchema = + PropertyBaseSchema.merge(CommonAccountFields).merge(ValuationAccountFields) + +const PropertyUpdateSchema = PropertyBaseSchema.merge(CommonAccountFields.partial()) + +// Vehicle + +const VehicleBaseSchema = z.object({ + type: z.literal('VEHICLE'), + categoryUser: z.enum(['vehicle']), + vehicleMeta: z + .object({ + track: z.boolean().default(false), + make: z.string(), + model: z.string(), + year: z.number(), + }) + .optional(), +}) + +const VehicleCreateSchema = + VehicleBaseSchema.merge(CommonAccountFields).merge(ValuationAccountFields) + +const VehicleUpdateSchema = VehicleBaseSchema.merge(CommonAccountFields.partial()) + +// Loan +const LoanBaseSchema = z.object({ + type: z.literal('LOAN'), + categoryUser: z.enum(['loan']), + currentBalance: z.number(), + loanUser: z + .object({ + originationDate: z.string(), + maturityDate: z.string(), + originationPrincipal: z.number(), + interestRate: z.discriminatedUnion('type', [ + z.object({ + type: z.literal('fixed'), + rate: z.number(), + }), + z.object({ + type: z.literal('arm'), + }), + z.object({ + type: z.literal('variable'), + }), + ]), + loanDetail: z.discriminatedUnion('type', [ + z.object({ + type: z.literal('student'), + }), + z.object({ + type: z.literal('mortgage'), + }), + z.object({ + type: z.literal('other'), + }), + ]), + }) + .optional(), +}) + +const LoanCreateSchema = LoanBaseSchema.merge(CommonAccountFields).merge( + z.object({ provider: z.literal('user').default('user') }) +) +const LoanUpdateSchema = LoanBaseSchema.merge(CommonAccountFields.partial()) + +// Credit + +const CreditBaseSchema = z.object({ + type: z.literal('CREDIT'), + categoryUser: z.enum(['credit']), + creditUser: z + .object({ + isOverdue: z.boolean(), + lastPaymentAmount: z.number(), + lastPaymentDate: z.string(), + lastStatementAmount: z.number(), + lastStatementDate: z.string(), + minimumPayment: z.number(), + }) + .optional(), +}) + +const CreditCreateSchema = CreditBaseSchema.merge(CommonAccountFields).merge(ValuationAccountFields) +const CreditUpdateSchema = CreditBaseSchema.merge(CommonAccountFields.partial()) + +// Other Asset + +const OtherAssetBaseSchema = z.object({ + type: z.literal('OTHER_ASSET'), + categoryUser: z.enum(['cash', 'investment', 'crypto', 'valuable', 'other']), +}) + +const OtherAssetCreateSchema = + OtherAssetBaseSchema.merge(CommonAccountFields).merge(ValuationAccountFields) +const OtherAssetUpdateSchema = OtherAssetBaseSchema.merge(CommonAccountFields.partial()) + +// Other Liability + +const OtherLiabilityBaseSchema = z.object({ + type: z.literal('OTHER_LIABILITY'), + categoryUser: z.enum(['other']), +}) + +const OtherLiabilityCreateSchema = + OtherLiabilityBaseSchema.merge(CommonAccountFields).merge(ValuationAccountFields) +const OtherLiabilityUpdateSchema = OtherLiabilityBaseSchema.merge(CommonAccountFields.partial()) + +export const AccountCreateSchema = z.discriminatedUnion('type', [ + PropertyCreateSchema, + VehicleCreateSchema, + LoanCreateSchema, + CreditCreateSchema, + OtherAssetCreateSchema, + OtherLiabilityCreateSchema, +]) + +const ProviderAccountUpdateSchema = z.discriminatedUnion('type', [ + PropertyUpdateSchema, + VehicleUpdateSchema, + LoanUpdateSchema, + CreditUpdateSchema, + OtherAssetUpdateSchema, + OtherLiabilityUpdateSchema, + z + .object({ + type: z.literal('DEPOSITORY'), + categoryUser: z.enum(['cash', 'other']), + }) + .merge(CommonAccountFields), + z + .object({ + type: z.literal('INVESTMENT'), + categoryUser: z.enum(['investment', 'cash', 'other']), + }) + .merge(CommonAccountFields), +]) + +const UserAccountUpdateSchema = z.discriminatedUnion('type', [ + PropertyUpdateSchema, + VehicleUpdateSchema, + LoanUpdateSchema, + CreditUpdateSchema, + OtherAssetUpdateSchema, + OtherLiabilityUpdateSchema, +]) + +export const AccountUpdateSchema = z.discriminatedUnion('provider', [ + z.object({ + provider: z.literal('plaid'), + data: ProviderAccountUpdateSchema, + }), + z.object({ + provider: z.literal('finicity'), + data: ProviderAccountUpdateSchema, + }), + z.object({ + provider: z.literal('user'), + data: UserAccountUpdateSchema, + }), + z.object({ + provider: z.literal(undefined), + data: CommonAccountFields.partial().and( + z.object({ categoryUser: z.nativeEnum(AccountCategory).optional() }) + ), + }), +]) diff --git a/libs/server/features/src/account/account.service.ts b/libs/server/features/src/account/account.service.ts new file mode 100644 index 00000000000..6bc8c52e10c --- /dev/null +++ b/libs/server/features/src/account/account.service.ts @@ -0,0 +1,444 @@ +import type { Logger } from 'winston' +import type { SyncAccountQueue, SyncConnectionQueue } from '@maybe-finance/server/shared' +import type { IBalanceSyncStrategyFactory } from '../account-balance' +import type { IAccountQueryService } from './account-query.service' +import type { + Account, + AccountCategory, + AccountClassification, + User, + PrismaClient, + Prisma, + InvestmentTransactionCategory, +} from '@prisma/client' +import _ from 'lodash' +import { DateTime } from 'luxon' +import { SharedType, AccountUtil, DateUtil } from '@maybe-finance/shared' +import { DbUtil } from '@maybe-finance/server/shared' + +export interface IAccountService { + get(id: Account['id']): Promise + getAll(userId: User['id']): Promise + getAccountRollup( + userId: User['id'], + start?: string, + end?: string, + interval?: SharedType.TimeSeriesInterval + ): Promise + sync(id: Account['id']): Promise + syncBalances(id: Account['id']): Promise + create( + data: Prisma.AccountUncheckedCreateInput, + initialValuations?: Prisma.ValuationCreateNestedManyWithoutAccountInput + ): Promise + update(id: Account['id'], data: Prisma.AccountUncheckedUpdateInput): Promise + delete(id: Account['id']): Promise +} + +export class AccountService implements IAccountService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly queryService: IAccountQueryService, + private readonly syncAccountQueue: SyncAccountQueue, + private readonly syncConnectionQueue: SyncConnectionQueue, + private readonly balanceSyncStrategyFactory: IBalanceSyncStrategyFactory + ) {} + + async get(id: Account['id']) { + return this.prisma.account.findUniqueOrThrow({ + where: { id }, + include: { accountConnection: true }, + }) + } + + /** + * A user can have account associated with an `AccountConnection` or directly tied to their profile + * + * To retrieve all accounts, check both `Account` and `AccountConnection` tables for a non-null `userId` + */ + async getAll(userId: User['id']): Promise { + const [accounts, connections] = await Promise.all([ + this.prisma.account.findMany({ + where: { userId }, + orderBy: { id: 'asc' }, + }), + this.prisma.accountConnection.findMany({ + where: { userId }, + include: { + accounts: { + orderBy: { id: 'asc' }, + }, + }, + orderBy: { id: 'asc' }, + }), + ]) + + const activeConnectionSyncJobs = await this.syncConnectionQueue.getActiveJobs() + + return { + accounts, + connections: connections.map((connection) => { + const job = activeConnectionSyncJobs.find( + (job) => job.data.accountConnectionId === connection.id + ) + + const progress = job ? job.progress() : null + + return { + ...connection, + syncProgress: + progress != null && + typeof progress === 'object' && + typeof progress.description === 'string' + ? progress + : undefined, + } + }), + } + } + + async getAccountDetails(id: Account['id']) { + return this.prisma.account.findUniqueOrThrow({ + where: { id }, + include: { + accountConnection: true, + transactions: { + take: SharedType.PageSize.Transaction, + }, + investmentTransactions: { + take: SharedType.PageSize.InvestmentTransaction, + }, + valuations: { + take: SharedType.PageSize.Valuation, + }, + holdings: { + include: { + security: true, + }, + take: SharedType.PageSize.Holding, + }, + }, + }) + } + + async create( + data: Omit, + initialValuations?: Prisma.ValuationCreateNestedManyWithoutAccountInput + ) { + return this.prisma.account.create({ + data: { + ...data, + valuations: initialValuations, + }, + }) + } + + async update(id: Account['id'], data: Prisma.AccountUncheckedUpdateInput) { + const account = await this.prisma.account.update({ + where: { id }, + data, + }) + return account + } + + async sync(id: Account['id']) { + const account = await this.get(id) + await this.syncAccountQueue.add('sync-account', { accountId: account.id }) + return account + } + + async syncBalances(id: Account['id']) { + const account = await this.get(id) + const strategy = this.balanceSyncStrategyFactory.for(account) + + const profiler = this.logger.startTimer() + await strategy.syncAccountBalances(account) + profiler.done({ message: `synced account ${account.id} balances` }) + + return account + } + + async delete(id: Account['id']) { + return this.prisma.account.delete({ where: { id } }) + } + + async getTransactions(accountId: Account['id'], page = 0, start?: DateTime, end?: DateTime) { + const [transactions, totalTransactions] = await this.prisma.$transaction([ + this.prisma.$queryRaw` + SELECT + * + FROM transactions_enriched t + WHERE + t."accountId" = ${accountId} + AND (${start?.toISODate()}::date IS NULL OR t.date >= ${start?.toISODate()}::date) + AND (${end?.toISODate()}::date IS NULL OR t.date <= ${end?.toISODate()}::date) + ORDER BY t.date desc + LIMIT ${SharedType.PageSize.Transaction} + OFFSET ${page * SharedType.PageSize.Transaction} + `, + this.prisma.transaction.count({ + where: { + accountId, + date: { + gte: start?.toJSDate(), + lte: end?.toJSDate(), + }, + }, + }), + ]) + + return { + transactions, + totalTransactions, + } + } + + async getHoldings( + accountId: Account['id'], + page = 0, + pageSize = SharedType.PageSize.Holding + ): Promise { + const [holdings, totalHoldings] = await Promise.all([ + this.queryService.getHoldingsEnriched(accountId, { page, pageSize }), + this.prisma.holding.count({ where: { accountId } }), + ]) + + return { + holdings: holdings.map((h) => { + return { + id: h.id, + securityId: h.security_id, + name: h.name, + symbol: h.symbol, + quantity: h.quantity, + sharesPerContract: h.shares_per_contract, + costBasis: h.cost_basis_per_share, + costBasisUser: h.cost_basis_user, + costBasisProvider: h.cost_basis_provider, + price: h.price, + value: h.value, + trend: { + total: h.cost_basis ? DbUtil.calculateTrend(h.cost_basis, h.value) : null, + today: h.price_prev + ? DbUtil.calculateTrend(h.price_prev.times(h.quantity), h.value) + : null, + }, + excluded: h.excluded, + } + }), + totalHoldings, + } + } + + async getInvestmentTransactions( + accountId: Account['id'], + page = 0, + start?: DateTime, + end?: DateTime, + category?: InvestmentTransactionCategory, + pageSize = SharedType.PageSize.InvestmentTransaction + ) { + const where = { + accountId, + date: { + gte: start?.toJSDate(), + lte: end?.toJSDate(), + }, + category, + } + + const [investmentTransactions, totalInvestmentTransactions] = + await this.prisma.$transaction([ + this.prisma.investmentTransaction.findMany({ + where, + include: { + security: true, + }, + orderBy: { + date: 'desc', + }, + skip: page * pageSize, + take: pageSize, + }), + this.prisma.investmentTransaction.count({ where }), + ]) + + return { + investmentTransactions, + totalInvestmentTransactions, + } + } + + async getBalance( + accountId: Account['id'], + date: string = DateTime.utc().plus({ days: 1 }).toISODate() // default to one day here to ensure we're grabbing the most recent date's balance + ): Promise { + const [balance] = await this.queryService.getBalanceSeries(accountId, date, date, 'days') + + return { + date: balance.date, + balance: balance.balance, + } + } + + async getBalances( + accountId: Account['id'], + start = DateTime.utc().minus({ years: 2 }).toISODate(), + end = DateTime.utc().toISODate(), + interval?: SharedType.TimeSeriesInterval + ): Promise { + interval = interval ?? DateUtil.calculateTimeSeriesInterval(start, end) + + const [balances, today, minDate] = await Promise.all([ + this.queryService.getBalanceSeries(accountId, start, end, interval), + this.getBalance(accountId), + this.getOldestBalanceDate(accountId), + ]) + + return { + series: { + interval, + start, + end, + data: balances, + }, + today, + minDate, + trend: DbUtil.calculateTrend( + balances[0].balance, + balances[balances.length - 1].balance + ), + } + } + + async getReturns( + accountId: Account['id'], + start = DateTime.utc().minus({ years: 2 }).toISODate(), + end = DateTime.utc().toISODate() + ): Promise { + const returnSeries = await this.queryService.getReturnSeries(accountId, start, end) + + return returnSeries.map( + ({ + date, + rate_of_return: rateOfReturn, + contributions, + contributions_period: contributionsPeriod, + }) => ({ + date, + account: { + contributions, + contributionsPeriod, + rateOfReturn, + }, + }) + ) + } + + async getAccountRollup( + userId: User['id'], + start = DateTime.utc().minus({ years: 2 }).toISODate(), + end = DateTime.utc().toISODate(), + interval?: SharedType.TimeSeriesInterval + ): Promise { + interval = interval ?? DateUtil.calculateTimeSeriesInterval(start, end) + + const rollup = await this.queryService.getRollup({ userId }, start, end, interval) + + const toTimeSeries = (rows: typeof rollup): SharedType.AccountRollupTimeSeries => { + return { + interval: interval!, + start, + end, + data: rows.map(({ date, balance, rollup_pct, total_pct }) => ({ + date, + balance, + rollupPct: rollup_pct, + totalPct: total_pct, + })), + } + } + + // Arranges the flattened SQL query into a hierarchical tree for the UI + return _(rollup) + .groupBy((r) => r.classification) + .omit('null') + .mapValues((classificationRows, classification) => { + return { + key: classification as AccountClassification, + title: classification === 'asset' ? 'Assets' : 'Debts', + balances: toTimeSeries( + classificationRows.filter((r) => r.grouping === 'classification') + ), + items: _(classificationRows) + .groupBy((r) => r.category) + .omit('null') + .mapValues((categoryRows, category) => { + return { + key: category as AccountCategory, + title: AccountUtil.CATEGORIES[category as AccountCategory].plural, + balances: toTimeSeries( + categoryRows.filter((r) => r.grouping === 'category') + ), + items: _(categoryRows) + .groupBy((r) => r.id) + .omit('null') + .mapValues((accountRows) => { + const [{ account }] = accountRows + + if (!account) { + this.logger.warn('accountRow', accountRows[0]) + throw new Error( + `accountRow is missing account data (rows: ${accountRows.length})` + ) + } + + return { + ...account, + syncing: + account.syncStatus !== 'IDLE' || + (!!account.connection && + account.connection.syncStatus !== 'IDLE'), + balances: toTimeSeries( + accountRows.filter((r) => r.grouping === 'account') + ), + } + }) + .values() + .value(), + } + }) + .values() + .value(), + } + }) + .values() + .value() + } + + /** + * If the user has defined a `start_date`, use that. Otherwise, find the oldest balance record. + */ + private async getOldestBalanceDate(accountId: Account['id']): Promise { + const account = await this.prisma.account.findUnique({ + where: { id: accountId }, + select: { startDate: true }, + }) + + if (account?.startDate) { + return DateTime.fromJSDate(account.startDate, { zone: 'utc' }).toISODate() + } + + const { + _min: { date: minDate }, + } = await this.prisma.accountBalance.aggregate({ + where: { accountId }, + _min: { date: true }, + }) + + return minDate + ? DateTime.fromJSDate(minDate, { zone: 'utc' }).toISODate() + : DateTime.utc().minus({ years: 2 }).toISODate() + } +} diff --git a/libs/server/features/src/account/index.ts b/libs/server/features/src/account/index.ts new file mode 100644 index 00000000000..22f71614a81 --- /dev/null +++ b/libs/server/features/src/account/index.ts @@ -0,0 +1,6 @@ +export * from './account.service' +export * from './account.processor' +export * from './account.provider' +export * from './account-query.service' +export * from './account.schema' +export * from './insight.service' diff --git a/libs/server/features/src/account/insight.service.ts b/libs/server/features/src/account/insight.service.ts new file mode 100644 index 00000000000..4c736ab7243 --- /dev/null +++ b/libs/server/features/src/account/insight.service.ts @@ -0,0 +1,912 @@ +import type { Logger } from 'winston' +import type { + Security, + Account, + Holding, + PrismaClient, + User, + TransactionType, +} from '@prisma/client' +import { Prisma } from '@prisma/client' +import _ from 'lodash' +import { SharedUtil, type SharedType } from '@maybe-finance/shared' +import { DbUtil } from '@maybe-finance/server/shared' +import { DateTime } from 'luxon' + +type UserInsightOptions = { + userId: User['id'] + accountIds?: Account['id'] | Account['id'][] + now?: DateTime +} + +type AccountInsightOptions = { + accountId: Account['id'] + now?: DateTime +} + +type HoldingInsightOptions = { + holding: Holding + now?: DateTime +} + +type PlanInsightOptions = { + userId: User['id'] + now?: DateTime +} + +export interface IInsightService { + getUserInsights(options: UserInsightOptions): Promise + getAccountInsights(options: AccountInsightOptions): Promise + getHoldingInsights(options: HoldingInsightOptions): Promise + getPlanInsights(options: PlanInsightOptions): Promise +} + +export class InsightService implements IInsightService { + constructor(private readonly logger: Logger, private readonly prisma: PrismaClient) {} + + async getUserInsights({ + userId, + accountIds, + now = DateTime.utc(), + }: UserInsightOptions): Promise { + const pAccountIds = + accountIds != null + ? Array.isArray(accountIds) + ? accountIds + : [accountIds] + : Prisma.sql`( + SELECT + a.id + FROM + account a + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE + (a.user_id = ${userId} OR ac.user_id = ${userId}) + AND a.is_active + )` + + const [user, accountSummary, holdingBreakdown, assetSummary, transactionSummary] = + await this.prisma.$transaction([ + this.prisma.user.findUniqueOrThrow({ where: { id: userId } }), + this._accountSummary(pAccountIds, now), + this._holdingBreakdown(pAccountIds), + this._assetSummary(pAccountIds, now), + this._transactionSummary(pAccountIds, now), + ]) + + const assets = accountSummary.find( + (row) => row.grouping === 'classification' && row.classification === 'asset' + ) ?? { + balance_now: new Prisma.Decimal(0), + balance_yearly: new Prisma.Decimal(0), + balance_monthly: new Prisma.Decimal(0), + balance_weekly: new Prisma.Decimal(0), + } + const assetBuckets = assetSummary.filter( + (row): row is Extract => + row.classification === 'asset' + ) + + const liabilities = accountSummary.find( + (row) => row.grouping === 'classification' && row.classification === 'liability' + ) ?? { + balance_now: new Prisma.Decimal(0), + balance_yearly: new Prisma.Decimal(0), + balance_monthly: new Prisma.Decimal(0), + balance_weekly: new Prisma.Decimal(0), + } + const liabilityBuckets = assetSummary.filter( + (row): row is Extract => + row.classification === 'liability' + ) + + const netWorthNow = assets.balance_now.minus(liabilities.balance_now) + const netWorthYearly = assets.balance_yearly.minus(liabilities.balance_yearly) + const netWorthMonthly = assets.balance_monthly.minus(liabilities.balance_monthly) + const netWorthWeekly = assets.balance_weekly.minus(liabilities.balance_weekly) + + const liquidAssetsNow = new Prisma.Decimal( + assetBuckets.find((row) => row.bucket === 'assets-liquid')?.balance_now ?? 0 + ) + + const incomeMonthlyCalculated = new Prisma.Decimal( + transactionSummary.find((row) => row.type === 'INCOME')?.avg_6mo ?? 0 + ) + const incomeMonthly = user.monthlyIncomeUser ?? incomeMonthlyCalculated + + const expensesMonthlyCalculated = new Prisma.Decimal( + transactionSummary.find((row) => row.type === 'EXPENSE')?.avg_6mo ?? 0 + ) + const expensesMonthly = user.monthlyExpensesUser ?? expensesMonthlyCalculated + + const debtMonthlyCalculated = new Prisma.Decimal( + transactionSummary.find((row) => row.type === 'PAYMENT')?.avg_6mo ?? 0 + ) + const debtMonthly = user.monthlyDebtUser ?? debtMonthlyCalculated + + const toAmountAndPct = ( + value: Prisma.Decimal | null | undefined, + total: Prisma.Decimal + ) => { + const amount = value ?? new Prisma.Decimal(0) + return { amount, percentage: amount.dividedBy(total) } + } + + return { + netWorthToday: netWorthNow, + netWorth: { + yearly: DbUtil.calculateTrend(netWorthYearly, netWorthNow), + monthly: DbUtil.calculateTrend(netWorthMonthly, netWorthNow), + weekly: DbUtil.calculateTrend(netWorthWeekly, netWorthNow), + }, + safetyNet: { + months: liquidAssetsNow.dividedBy(expensesMonthly).clampedTo(0, Infinity), + spending: expensesMonthly, + }, + debtIncome: { + ratio: debtMonthly.dividedBy(incomeMonthly), + debt: debtMonthly, + income: incomeMonthly, + user: { + debt: user.monthlyDebtUser, + income: user.monthlyIncomeUser, + }, + calculated: { + debt: debtMonthlyCalculated, + income: incomeMonthlyCalculated, + }, + }, + debtAsset: { + ratio: liabilities.balance_now.dividedBy(assets.balance_now), + debt: liabilities.balance_now, + asset: assets.balance_now, + }, + assetSummary: { + liquid: toAmountAndPct(liquidAssetsNow, assets.balance_now), + illiquid: toAmountAndPct( + assetBuckets.find((row) => row.bucket === 'assets-illiquid')?.balance_now, + assets.balance_now + ), + yielding: toAmountAndPct( + assetBuckets.find((row) => row.bucket === 'assets-yielding')?.balance_now, + assets.balance_now + ), + }, + debtSummary: { + good: toAmountAndPct( + liabilityBuckets.find((row) => row.bucket === 'debt-good')?.balance_now, + liabilities.balance_now + ), + bad: toAmountAndPct( + liabilityBuckets.find((row) => row.bucket === 'debt-bad')?.balance_now, + liabilities.balance_now + ), + total: toAmountAndPct( + liabilities.balance_now, + assets.balance_now.plus(liabilities.balance_now) + ), + }, + transactionSummary: { + income: incomeMonthly, + expenses: expensesMonthly, + payments: debtMonthly, + }, + transactionBreakdown: transactionSummary.map((row) => ({ + category: row.type, + amount: row.amount, + avg_6mo: row.avg_6mo, + })), + accountSummary: accountSummary + .filter( + (row): row is Extract => + row.grouping === 'category' + ) + .map((row) => ({ + classification: row.classification, + category: row.category, + balance: row.balance_now, + allocation: row.allocation_now, + })), + holdingBreakdown: _(holdingBreakdown) + .filter((h) => h.grouping === 'category') + .map((c) => ({ + ...c, + holdings: _(holdingBreakdown) + .filter( + (h): h is Extract => + h.grouping === 'security' && h.category === c.category + ) + .orderBy((h) => +h.value, 'desc') + .value(), + })) + .orderBy((h) => +h.value, 'desc') + .value(), + } + } + + async getAccountInsights({ + accountId, + now = DateTime.utc(), + }: AccountInsightOptions): Promise { + const [ + holdingSummary, + [returns], + [contributions], + { + _sum: { fees }, + }, + ] = await Promise.all([ + this._holdingSummary(accountId), + this._portfolioReturn(accountId, now), + this._investmentTransactionSummary(accountId, now), + this.prisma.investmentTransaction.aggregate({ + where: { accountId }, + _sum: { + fees: true, + }, + }), + ]) + + const { value, cost_basis, pnl_amt, pnl_pct } = holdingSummary.find( + (s): s is Extract => SharedUtil.isNull(s.asset_class) + )! + + return { + portfolio: + value != null + ? { + return: { + '1m': DbUtil.toTrend(returns.amt_1m, returns.pct_1m), + '1y': DbUtil.toTrend(returns.amt_1y, returns.pct_1y), + ytd: DbUtil.toTrend(returns.amt_ytd, returns.pct_ytd), + }, + pnl: DbUtil.toTrend(pnl_amt, pnl_pct), + costBasis: DbUtil.toDecimal(cost_basis), + contributions: { + ytd: { + amount: DbUtil.toDecimal(contributions.ytd_total).negated(), + monthlyAvg: DbUtil.toDecimal(contributions.ytd_total) + .dividedBy(DateTime.utc().month) + .negated(), + }, + lastYear: { + amount: DbUtil.toDecimal(contributions.last_year_total).negated(), + monthlyAvg: DbUtil.toDecimal(contributions.last_year_total) + .dividedBy(12) + .negated(), + }, + }, + fees: fees ?? new Prisma.Decimal(0), + holdingBreakdown: holdingSummary + .filter( + (s): s is Extract => + !SharedUtil.isNull(s.asset_class) + ) + .map((s) => ({ + asset_class: s.asset_class, + amount: DbUtil.toDecimal(s.value), + percentage: DbUtil.toDecimal(s.percentage), + })), + } + : undefined, + } + } + + async getHoldingInsights({ + holding, + }: HoldingInsightOptions): Promise { + const [ + { + _sum: { amount: dividends }, + }, + [{ allocation }], + ] = await Promise.all([ + this.prisma.investmentTransaction.aggregate({ + _sum: { + amount: true, + }, + where: { + security: { id: holding.securityId }, + accountId: holding.accountId, + OR: [ + { + plaidSubtype: 'dividend', + }, + { + finicityInvestmentTransactionType: 'dividend', + }, + ], + }, + }), + this.prisma.$queryRaw<[{ allocation: Prisma.Decimal | null }]>` + WITH security_allocations as ( + SELECT + security_id, + value / SUM(value) OVER () as "allocation" + FROM + holdings_enriched + WHERE + account_id = ${holding.accountId} + ) + SELECT allocation + FROM security_allocations + WHERE security_id = ${holding.securityId} + `, + ]) + + return { + holding, + dividends: DbUtil.toDecimal(dividends), + allocation: DbUtil.toDecimal(allocation), + } + } + + async getPlanInsights({ userId, now = DateTime.utc() }: PlanInsightOptions) { + const accountIds = Prisma.sql`( + SELECT + a.id + FROM + account a + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE + (a.user_id = ${userId} OR ac.user_id = ${userId}) + AND a.is_active + )` + + const [user, projectionAssetBreakdown, projectionLiabilityBreakdown, transactionSummary] = + await Promise.all([ + this.prisma.user.findUniqueOrThrow({ where: { id: userId } }), + this._projectionAssetBreakdown(accountIds, now), + this._projectionLiabilityBreakdown(accountIds, now), + this._transactionSummary(accountIds, now), + ]) + + const incomeMonthly = + user.monthlyIncomeUser ?? + new Prisma.Decimal( + transactionSummary.find((row) => row.type === 'INCOME')?.avg_6mo ?? 0 + ) + + const expensesMonthly = + user.monthlyExpensesUser ?? + new Prisma.Decimal( + transactionSummary.find((row) => row.type === 'EXPENSE')?.amount ?? 0 + ) + + return { + projectionAssetBreakdown, + projectionLiabilityBreakdown, + income: incomeMonthly.times(12), + expenses: expensesMonthly.times(12), + } + } + + private _accountSummary(accountIds: Prisma.Sql | number[], now: DateTime) { + const timepoints = { + now, + yearly: now.minus({ years: 1 }), + monthly: now.minus({ months: 1 }), + weekly: now.minus({ weeks: 1 }), + } + + // discriminated union represents valid columns for each grouping + type AccountGrouping = + | { + grouping: 'classification' + classification: Account['classification'] + } + | { + grouping: 'category' + classification: Account['classification'] + category: Account['category'] + } + + return this.prisma.$queryRaw< + (AccountGrouping & { + [key in keyof typeof timepoints as `balance_${key}`]: Prisma.Decimal + } & { + [key in keyof typeof timepoints as `allocation_${key}`]: Prisma.Decimal + })[] + >` + WITH category_rollup AS ( + SELECT + CASE GROUPING(a.classification, a.category) + WHEN 0 THEN 'category' + WHEN 1 THEN 'classification' + END AS grouping, + a.classification, + a.category, + ${Prisma.join( + Object.entries(timepoints).map(([key, date]) => { + const pCol = Prisma.raw(`"balance_${key}"`) + const pDate = Prisma.raw(`'${date.toISODate()}'`) + return Prisma.sql` + SUM( + CASE + WHEN a.start_date IS NOT NULL AND ${pDate} < a.start_date THEN 0 + ELSE COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date <= ${pDate} ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > ${pDate} ORDER BY date ASC LIMIT 1), + a.current_balance, + 0 + ) + END + ) AS ${pCol} + ` + }) + )} + FROM + account a + WHERE + a.id IN ${accountIds} + GROUP BY + GROUPING SETS ( + (a.classification, a.category), + (a.classification) + ) + ) + SELECT + *, + ${Prisma.join( + Object.entries(timepoints).map(([key]) => { + const pBalanceCol = Prisma.raw(`"balance_${key}"`) + const pAllocationCol = Prisma.raw(`"allocation_${key}"`) + return Prisma.sql` + CASE grouping + WHEN 'category' THEN COALESCE(${pBalanceCol} / SUM(NULLIF(${pBalanceCol}, 0)) OVER (PARTITION BY grouping, classification), 0) + WHEN 'classification' THEN COALESCE(${pBalanceCol} / SUM(NULLIF(${pBalanceCol}, 0)) OVER (PARTITION BY grouping), 0) + END AS ${pAllocationCol} + ` + }) + )} + FROM + category_rollup + ` + } + + private _assetSummary(accountIds: Prisma.Sql | number[], now: DateTime) { + const timepoints = { + now, + yearly: now.minus({ years: 1 }), + monthly: now.minus({ months: 1 }), + weekly: now.minus({ weeks: 1 }), + } + + // discriminated union represents valid columns for each grouping + type AssetGrouping = + | { + classification: 'asset' + bucket: 'assets-liquid' | 'assets-illiquid' | 'assets-yielding' | 'assets-other' + } + | { + classification: 'liability' + bucket: 'debt-good' | 'debt-bad' | 'debt-other' + } + + return this.prisma.$queryRaw< + (AssetGrouping & { + [key in keyof typeof timepoints as `balance_${key}`]: Prisma.Decimal + })[] + >` + SELECT + a.classification, + x.bucket, + ${Prisma.join( + Object.entries(timepoints).map(([key, date]) => { + const pCol = Prisma.raw(`"balance_${key}"`) + const pDate = Prisma.raw(`'${date.toISODate()}'`) + return Prisma.sql` + SUM( + CASE + WHEN a.start_date IS NOT NULL AND ${pDate} < a.start_date THEN 0 + ELSE COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date <= ${pDate} ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > ${pDate} ORDER BY date ASC LIMIT 1), + a.current_balance, + 0 + ) + END + ) AS ${pCol} + ` + }) + )} + FROM + account a + LEFT JOIN LATERAL ( + SELECT + UNNEST( + CASE a.classification + WHEN 'asset' THEN ( + CASE + WHEN a.category = 'cash' THEN ARRAY['assets-liquid'] + WHEN a.category = 'property' THEN ARRAY['assets-illiquid', 'assets-yielding'] + WHEN a.category = 'investment' THEN ARRAY['assets-illiquid', 'assets-yielding'] + ELSE ARRAY['assets-other'] + END + ) + WHEN 'liability' THEN ( + CASE + WHEN a.category = 'loan' AND a.subcategory + IN ('business', 'commercial', 'construction', 'mortgage') + THEN ARRAY['debt-good'] + WHEN a.category = 'loan' AND a.subcategory + IN ('consumer', 'home equity', 'overdraft', 'line of credit') + THEN ARRAY['debt-bad'] + ELSE ARRAY['debt-other'] + END + ) + END + ) AS bucket + ) x ON true + WHERE + a.id IN ${accountIds} + GROUP BY + a.classification, x.bucket + ` + } + + private _transactionSummary(accountIds: Prisma.Sql | number[], now: DateTime) { + const pNow = now.toISODate() + + return this.prisma.$queryRaw< + { + type: TransactionType + amount: Prisma.Decimal + avg_6mo: Prisma.Decimal + }[] + >` + WITH txns AS ( + SELECT + t.id, + t.date, + t.amount, + t.flow, + t.type, + t."accountType" + FROM + transactions_enriched t + WHERE + t."accountId" IN ${accountIds} + AND NOT t.excluded + AND t.date >= date_trunc('month', ${pNow}::date - interval '6 months') + AND t.date < date_trunc('month', ${pNow}::date) + ), txn_type_months AS ( + SELECT DISTINCT + t.type, + date_trunc('month', d.date) AS "month" + FROM + unnest(enum_range(NULL::"TransactionType")) t(type) + CROSS JOIN generate_series(date_trunc('month', (SELECT MIN(date) FROM txns)), now(), '1 month') d(date) + ), txn_monthly_agg AS ( + SELECT + t.type, + date_trunc('month', t.date) AS "month", + ABS(SUM(t.amount)) AS amount + FROM + txns t + WHERE + -- filter out credit account payments until we have a way to distinguish interest vs balance payments + NOT (t.type = 'PAYMENT' AND t."accountType" = 'CREDIT') + GROUP BY + 1, 2 + ), monthly_summary AS ( + SELECT + ttm.type, + ttm.month, + COALESCE(tma.amount, 0) AS amount, + AVG(COALESCE(tma.amount, 0)) OVER (PARTITION BY ttm.type ORDER BY ttm.month ASC ROWS BETWEEN 5 PRECEDING AND CURRENT ROW) AS "avg_6mo" + FROM + txn_type_months ttm + LEFT JOIN txn_monthly_agg tma ON tma.type IS NOT DISTINCT FROM ttm.type AND tma.month = ttm.month + ) + SELECT + type, + amount, + avg_6mo + FROM + monthly_summary + WHERE + month = date_trunc('month', ${pNow}::date - interval '1 month'); + ` + } + + private _holdingSummary(accountId: Account['id']) { + return this.prisma.$queryRaw< + ( + | { + asset_class: string + value: Prisma.Decimal + percentage: Prisma.Decimal + cost_basis: Prisma.Decimal | null + pnl_amt: Prisma.Decimal | null + pnl_pct: Prisma.Decimal | null + } + | { + asset_class: null + value: Prisma.Decimal | null + percentage: Prisma.Decimal | null + cost_basis: Prisma.Decimal | null + pnl_amt: Prisma.Decimal | null + pnl_pct: Prisma.Decimal | null + } + )[] + >` + SELECT + s.asset_class, + SUM(h.value) AS "value", + ROUND(SUM(h.value) / SUM(SUM(h.value)) OVER (PARTITION BY GROUPING(s.asset_class)), 4) AS "percentage", + SUM(h.cost_basis) AS "cost_basis", + SUM(h.value - h.cost_basis) AS "pnl_amt", + ROUND(SUM(h.value - h.cost_basis) / NULLIF(SUM(h.cost_basis), 0), 4) AS "pnl_pct" + FROM + holdings_enriched h + INNER JOIN ( + SELECT + id, + CASE + -- plaid + WHEN plaid_type IN ('equity', 'etf', 'mutual fund', 'derivative') THEN 'stocks' + WHEN plaid_type IN ('fixed income') THEN 'fixed_income' + WHEN plaid_type IN ('cash', 'loan') THEN 'cash' + WHEN plaid_type IN ('cryptocurrency') THEN 'crypto' + -- finicity + WHEN finicity_type IN ('EQUITY', 'ETF', 'MUTUALFUND', 'STOCKINFO', 'MFINFO') THEN 'stocks' + WHEN finicity_type IN ('BOND') THEN 'fixed_income' + ELSE 'other' + END AS "asset_class" + FROM + "security" + ) s ON s.id = h.security_id + WHERE + h.account_id = ${accountId} + GROUP BY + ROLLUP (s.asset_class) + ` + } + + private _holdingBreakdown(accountIds: Prisma.Sql | number[]) { + type HoldingCategory = 'stocks' | 'fixed_income' | 'cash' | 'crypto' | 'other' + + type HoldingBreakdown = + | { + grouping: 'category' + category: HoldingCategory + security: null + value: Holding['value'] + allocation: Prisma.Decimal + } + | { + grouping: 'security' + category: HoldingCategory + security: Pick + value: Holding['value'] + allocation: Prisma.Decimal + } + + return this.prisma.$queryRaw` + WITH holding_rollup AS ( + SELECT + CASE GROUPING(x.category, h.security_id) + WHEN 0 THEN 'security' + WHEN 1 THEN 'category' + END AS "grouping", + x.category, + h.security_id, + SUM(h.value) AS "value", + SUM(h.value) / NULLIF(SUM(SUM(h.value)) OVER (PARTITION BY GROUPING(x.category, h.security_id)), 0) AS "allocation" + FROM + holdings_enriched h + INNER JOIN security s ON s.id = h.security_id + LEFT JOIN LATERAL ( + SELECT + CASE + -- plaid + WHEN s.plaid_type IN ('equity', 'etf', 'mutual fund', 'derivative') THEN 'stocks' + WHEN s.plaid_type IN ('fixed income') THEN 'fixed_income' + WHEN s.plaid_type IN ('cash', 'loan') THEN 'cash' + WHEN s.plaid_type IN ('cryptocurrency') THEN 'crypto' + -- finicity + WHEN s.finicity_type IN ('EQUITY', 'ETF', 'MUTUALFUND', 'STOCKINFO', 'MFINFO') THEN 'stocks' + WHEN s.finicity_type IN ('BOND') THEN 'fixed_income' + ELSE 'other' + END AS "category" + ) x ON TRUE + WHERE + h.account_id IN ${accountIds} + GROUP BY + x.category, ROLLUP (h.security_id) + ) + SELECT + r.grouping, + r.category, + CASE r.grouping WHEN 'security' THEN json_build_object('id', s.id, 'symbol', s.symbol, 'name', s.name) ELSE NULL END AS "security", + r.value, + r.allocation + FROM + holding_rollup r + LEFT JOIN security s ON s.id = r.security_id + ` + } + + private _investmentTransactionSummary(accountId: Account['id'], now: DateTime) { + return this.prisma.$queryRaw< + [ + { + last_year_total: Prisma.Decimal + ytd_total: Prisma.Decimal + } + ] + >` + WITH cashflow_txns AS ( + SELECT + (date_trunc('month', it.date) + interval '1 month' - interval '1 day')::date AS "month", + SUM(it.amount) as "amount" + FROM + investment_transaction it + LEFT JOIN account a ON a.id = it.account_id + WHERE + it.account_id = ${accountId} + AND ( + (it.plaid_type = 'cash' AND it.plaid_subtype IN ('contribution', 'deposit', 'withdrawal')) + OR (it.plaid_type = 'transfer' AND it.plaid_subtype IN ('transfer', 'send', 'request')) + OR (it.plaid_type = 'buy' AND it.plaid_subtype IN ('contribution')) + OR (it.finicity_transaction_id IS NOT NULL AND it.finicity_investment_transaction_type IN ('contribution', 'deposit', 'transfer')) + ) + -- Exclude any contributions made prior to the start date since balances will be 0 + AND (a.start_date is NULL OR it.date >= a.start_date) + GROUP BY 1 + ) + SELECT + COALESCE(sum(amount) FILTER (WHERE month >= date_trunc('year', now())), 0) AS ytd_total, + COALESCE(sum(amount) FILTER (WHERE month BETWEEN (date_trunc('year', now()) - interval '1 year') AND date_trunc('year', now())), 0) AS last_year_total + FROM + cashflow_txns + ` + } + + private _portfolioReturn(accountId: Account['id'], now: DateTime) { + const timepoints = { + '1m': [now.minus({ months: 1 }), now], + '1y': [now.minus({ years: 1 }), now], + ytd: [now.startOf('year'), now], + } + + return this.prisma.$queryRaw< + [ + { + [key in keyof typeof timepoints as `pct_${key}`]: Prisma.Decimal + } & { + [key in keyof typeof timepoints as `amt_${key}`]: Prisma.Decimal + } + ] + >` + SELECT + * + FROM + ${Prisma.join( + Object.entries(timepoints).map(([key, [start, end]]) => { + const pAccountId = Prisma.raw(accountId.toString()) + const pStart = Prisma.raw(`'${start.toISODate()}'`) + const pEnd = Prisma.raw(`'${end.toISODate()}'`) + const pKey = Prisma.raw(key) + return Prisma.sql` + calculate_return_dietz(${pAccountId}, ${pStart}, ${pEnd}) ror_${pKey}(pct_${pKey}, amt_${pKey}) + ` + }) + )} + ` + } + + private _projectionAssetBreakdown(accountIds: Prisma.Sql, now: DateTime) { + const pAccountIds = accountIds + const pNow = now.toISODate() + + return this.prisma.$queryRaw< + { type: SharedType.ProjectionAssetType; amount: Prisma.Decimal }[] + >` + SELECT + asset_type AS "type", + SUM(amount) AS "amount" + FROM ( + -- non-investment accounts / investment accounts with 0 holdings + SELECT + x.asset_type, + SUM( + CASE + WHEN a.start_date IS NOT NULL AND ${pNow}::date < a.start_date THEN 0 + ELSE COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date <= ${pNow}::date ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > ${pNow}::date ORDER BY date ASC LIMIT 1), + a.current_balance, + 0 + ) + END + ) AS "amount" + FROM + account a + LEFT JOIN LATERAL ( + SELECT + CASE + WHEN a.type IN ('DEPOSITORY', 'INVESTMENT') THEN 'cash' + WHEN a.type IN ('PROPERTY') THEN 'property' + ELSE 'other' + END AS "asset_type" + ) x ON true + WHERE + a.id IN ${pAccountIds} + AND a.classification = 'asset' + AND (a.type <> 'INVESTMENT' OR NOT EXISTS (SELECT 1 FROM holding h WHERE h.account_id = a.id)) + GROUP BY + x.asset_type + UNION ALL + -- investment accounts + SELECT + s.asset_type, + SUM(h.value) AS "amount" + FROM + holdings_enriched h + INNER JOIN ( + SELECT + id, + CASE + -- plaid + WHEN plaid_type IN ('equity', 'etf', 'mutual fund', 'derivative') THEN 'stocks' + WHEN plaid_type IN ('fixed income') THEN 'bonds' + WHEN plaid_type IN ('cash', 'loan') THEN 'cash' + WHEN plaid_type IN ('cryptocurrency') THEN 'crypto' + -- finicity + WHEN finicity_type IN ('EQUITY', 'ETF', 'MUTUALFUND', 'STOCKINFO', 'MFINFO') THEN 'stocks' + WHEN finicity_type IN ('BOND') THEN 'bonds' + ELSE 'other' + END AS "asset_type" + FROM + "security" + ) s ON s.id = h.security_id + WHERE + h.account_id IN ${pAccountIds} + GROUP BY + s.asset_type + ) x + GROUP BY + 1 + ` + } + + private _projectionLiabilityBreakdown(accountIds: Prisma.Sql, now: DateTime) { + const pAccountIds = accountIds + const pNow = now.toISODate() + + return this.prisma.$queryRaw< + { type: SharedType.ProjectionLiabilityType; amount: Prisma.Decimal }[] + >` + SELECT + x.asset_type AS "type", + SUM( + CASE + WHEN a.start_date IS NOT NULL AND ${pNow}::date < a.start_date THEN 0 + ELSE COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date <= ${pNow}::date ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > ${pNow}::date ORDER BY date ASC LIMIT 1), + a.current_balance, + 0 + ) + END + ) AS "amount" + FROM + account a + LEFT JOIN LATERAL ( + SELECT + CASE + WHEN a.type IN ('CREDIT') THEN 'credit' + WHEN a.type IN ('LOAN') THEN 'loan' + ELSE 'other' + END AS "asset_type" + ) x ON true + WHERE + a.id IN ${pAccountIds} + AND a.classification = 'liability' + GROUP BY + x.asset_type + ` + } +} diff --git a/libs/server/features/src/conversation/conversation.schema.ts b/libs/server/features/src/conversation/conversation.schema.ts new file mode 100644 index 00000000000..db12a21d967 --- /dev/null +++ b/libs/server/features/src/conversation/conversation.schema.ts @@ -0,0 +1,12 @@ +import { z } from 'zod' +import { MessageCreateSchema } from './message.schema' + +export const ConversationCreateSchema = z.object({ + title: z.string().min(1), + status: z.enum(['open', 'closed']).optional(), + initialMessage: MessageCreateSchema.optional(), + accountId: z.number().optional(), + planId: z.number().optional(), +}) + +export const ConversationUpdateSchema = ConversationCreateSchema.partial() diff --git a/libs/server/features/src/conversation/conversation.service.ts b/libs/server/features/src/conversation/conversation.service.ts new file mode 100644 index 00000000000..0d10086802b --- /dev/null +++ b/libs/server/features/src/conversation/conversation.service.ts @@ -0,0 +1,166 @@ +import { type QueueService, ServerUtil } from '@maybe-finance/server/shared' +import type { SharedType } from '@maybe-finance/shared' +import type { Conversation, PrismaClient, Prisma, User } from '@prisma/client' +import type { Logger } from 'winston' + +type SignerOpts = { + cdnUrl: string + pubKeyId: string + privKey: string +} + +export interface IConversationService { + getAll(userId: User['id']): Promise + get(id: Conversation['id'], signerOpts?: SignerOpts): Promise + create(data: Prisma.ConversationUncheckedCreateInput): Promise + update( + id: Conversation['id'], + data: Prisma.ConversationUncheckedUpdateInput + ): Promise + delete(id: Conversation['id']): Promise +} + +export class ConversationService implements IConversationService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly queueService: QueueService + ) {} + + async getAll(userId: User['id']) { + const conversations = await this.prisma.conversation.findMany({ + where: { userId }, + include: { + advisors: { + include: { + advisor: { + select: { + fullName: true, + title: true, + bio: true, + avatarSrc: true, + }, + }, + }, + }, + messages: { take: 1, orderBy: { createdAt: 'desc' } }, + _count: { select: { messages: true } }, + }, + }) + + return conversations.map(({ messages, _count, ...conversation }) => ({ + ...conversation, + messageCount: _count.messages, + firstMessage: messages.at(-1) ?? null, + lastMessage: messages[0] ?? null, + })) + } + + async get(id: Conversation['id'], signerConfig?: ServerUtil.SignerConfig) { + const conversation = await this.prisma.conversation.findUniqueOrThrow({ + where: { id }, + include: { + advisors: { + include: { + advisor: { + select: { + fullName: true, + title: true, + bio: true, + avatarSrc: true, + }, + }, + }, + orderBy: { + createdAt: 'asc', + }, + }, + messages: { + include: { + user: { + select: { + advisor: { + select: { + fullName: true, + title: true, + bio: true, + avatarSrc: true, + }, + }, + }, + }, + }, + }, + }, + }) + + return { + ...conversation, + messages: conversation.messages.map((msg) => ServerUtil.mapMessage(msg, signerConfig)), + } + } + + async create(data: Prisma.ConversationUncheckedCreateInput) { + const conversation = await this.prisma.conversation.create({ + data, + include: { + user: { + select: { + ataAll: true, + ataSubmitted: true, + }, + }, + }, + }) + + if (conversation.user?.ataAll || conversation.user?.ataSubmitted) { + await this.queueService.getQueue('send-email').add('send-email', { + type: 'conversation-notification', + notification: { + type: 'submitted', + conversationId: conversation.id, + }, + }) + } + + return conversation + } + + async update(id: Conversation['id'], data: Prisma.ConversationUncheckedUpdateInput) { + const updatedConversation = await this.prisma.conversation.update({ + where: { id }, + data, + include: { + user: { + select: { + ataAll: true, + ataClosed: true, + }, + }, + }, + }) + + const isCloseStatus = data.status === 'closed' + + if ( + isCloseStatus && + (updatedConversation.user.ataAll || updatedConversation.user.ataClosed) + ) { + await this.queueService.getQueue('send-email').add('send-email', { + type: 'conversation-notification', + notification: { + type: 'closed', + conversationId: updatedConversation.id, + }, + }) + } + + return updatedConversation + } + + delete(id: Conversation['id']) { + return this.prisma.conversation.delete({ + where: { id }, + }) + } +} diff --git a/libs/server/features/src/conversation/index.ts b/libs/server/features/src/conversation/index.ts new file mode 100644 index 00000000000..5d51e9609af --- /dev/null +++ b/libs/server/features/src/conversation/index.ts @@ -0,0 +1,5 @@ +export * from './conversation.schema' +export * from './conversation.service' +export * from './message.schema' +export * from './message.service' +export * as Sandbox from './sandbox' diff --git a/libs/server/features/src/conversation/message.schema.ts b/libs/server/features/src/conversation/message.schema.ts new file mode 100644 index 00000000000..a9a4da00aea --- /dev/null +++ b/libs/server/features/src/conversation/message.schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod' + +export const MessageCreateSchema = z.object({ + type: z.enum(['text', 'audio', 'video']), + body: z.string().nullish(), +}) + +export const MessageUpdateSchema = MessageCreateSchema.partial() diff --git a/libs/server/features/src/conversation/message.service.ts b/libs/server/features/src/conversation/message.service.ts new file mode 100644 index 00000000000..f98e9b35986 --- /dev/null +++ b/libs/server/features/src/conversation/message.service.ts @@ -0,0 +1,79 @@ +import type { QueueService } from '@maybe-finance/server/shared' +import type { Prisma, Message, PrismaClient } from '@prisma/client' +import type { Logger } from 'winston' + +export interface IMessageService { + get(id: Message['id']): Promise + create(data: Prisma.MessageUncheckedCreateInput, isAdvisorMessage: boolean): Promise + update(id: Message['id'], data: Prisma.MessageUncheckedUpdateInput): Promise + delete(id: Message['id']): Promise +} + +export class MessageService implements IMessageService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly queueService: QueueService + ) {} + + get(id: Message['id']): Promise { + return this.prisma.message.findUniqueOrThrow({ + where: { id }, + }) + } + + async create( + data: Prisma.MessageUncheckedCreateInput, + isAdvisorMessage: boolean + ): Promise { + const newMessage = await this.prisma.message.create({ + data, + include: { + conversation: { + include: { + user: { + select: { + id: true, + ataAll: true, + ataUpdate: true, + }, + }, + }, + }, + user: { + select: { + id: true, + ataAll: true, + ataUpdate: true, + }, + }, + }, + }) + + if (isAdvisorMessage && (newMessage.user?.ataAll || newMessage.user?.ataUpdate)) { + // Notify user that the advisor has seen their message + await this.queueService.getQueue('send-email').add('send-email', { + type: 'conversation-notification', + notification: { + type: 'update', + conversationId: newMessage.conversationId, + }, + }) + } + + return newMessage + } + + update(id: Message['id'], data: Prisma.MessageUncheckedUpdateInput): Promise { + return this.prisma.message.update({ + where: { id }, + data, + }) + } + + delete(id: Message['id']): Promise { + return this.prisma.message.delete({ + where: { id }, + }) + } +} diff --git a/libs/server/features/src/conversation/sandbox.ts b/libs/server/features/src/conversation/sandbox.ts new file mode 100644 index 00000000000..cf3c719f5fe --- /dev/null +++ b/libs/server/features/src/conversation/sandbox.ts @@ -0,0 +1,217 @@ +import type { PrismaClient, User } from '@prisma/client' +import { DateTime } from 'luxon' +import { z } from 'zod' + +export const SandboxSchema = z.discriminatedUnion('action', [ + z.object({ action: z.literal('reset') }), + z.object({ action: z.literal('assign-advisor'), conversationId: z.number() }), +]) + +// pre-defined Auth0 user with the Advisor role +const mockAdvisorAuth0Id = 'REPLACE_THIS' + +export async function assignAdvisor(conversationId: number, prisma: PrismaClient) { + await prisma.$transaction(async (tx) => { + const advisorUser = await tx.user.findUnique({ + where: { auth0Id: mockAdvisorAuth0Id }, + include: { advisor: { select: { id: true } } }, + }) + + const advisorId = advisorUser?.advisor?.id + + if (!advisorId) { + throw new Error('No advisor found') + } + + await prisma.conversation.update({ + where: { + id: conversationId, + }, + data: { + advisors: { + create: { + advisorId, + }, + }, + }, + }) + }) +} + +export async function resetConversations( + input: z.infer & { userId: User['id'] }, + prisma: PrismaClient +) { + const { userId, action } = input + + await prisma.$transaction(async (tx) => { + const mockAdvisor = await tx.user.upsert({ + where: { + auth0Id: mockAdvisorAuth0Id, + }, + create: { + auth0Id: mockAdvisorAuth0Id, + email: 'mock+advisor@maybe.co', + advisor: { + create: { + approvalStatus: 'approved', + fullName: 'Travis Woods', + title: 'Maybe Co-Founder & CFO, CFP, CFA', + bio: "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.", + avatarSrc: '/assets/images/advisor-avatar.png', + }, + }, + }, + update: {}, + include: { + advisor: true, + }, + }) + + if (action === 'reset') { + await tx.conversation.deleteMany({ + where: { + userId, + }, + }) + + // Test conversation 1 - an example conversation that was completed in the past and archived. + await tx.conversation.create({ + data: { + title: 'What can I do to minimize my taxes annually?', + userId, + status: 'closed', + advisors: { + create: { + advisorId: mockAdvisor.advisor!.id, + createdAt: DateTime.now().minus({ days: 4, hours: 4 }).toJSDate(), + updatedAt: DateTime.now().minus({ days: 4, hours: 4 }).toJSDate(), + }, + }, + messages: { + createMany: { + data: [ + { + type: 'text', + userId, + createdAt: DateTime.now() + .minus({ days: 4, hours: 5 }) + .toJSDate(), + updatedAt: DateTime.now() + .minus({ days: 4, hours: 5 }) + .toJSDate(), + body: 'For some additional context, I have switched jobs 3 times in the past year and was wondering if there were any things I need to look out for given that unique situation.', + }, + { + type: 'video', + userId: mockAdvisor.id, + createdAt: DateTime.now() + .minus({ days: 4, hours: 3 }) + .toJSDate(), + updatedAt: DateTime.now() + .minus({ days: 4, hours: 3 }) + .toJSDate(), + body: 'Please see my video response below where I explain more about personal income tax and what you can do based on your finances.', + mediaSrc: '/assets/short-sample-video-file.mp4', + }, + { + type: 'text', + userId, + createdAt: DateTime.now() + .minus({ days: 4, hours: 2 }) + .toJSDate(), + updatedAt: DateTime.now() + .minus({ days: 4, hours: 2 }) + .toJSDate(), + body: 'I just have one follow up. Would I have any benefit to the married filing separate status? Or is my current filing status the best for me?', + }, + { + type: 'audio', + userId: mockAdvisor.id, + createdAt: DateTime.now() + .minus({ days: 4, hours: 1 }) + .toJSDate(), + updatedAt: DateTime.now() + .minus({ days: 4, hours: 1 }) + .toJSDate(), + body: 'No problem, here is a quick clip I recorded explaining those filing statuses.', + mediaSrc: '/assets/short-sample-audio-file.mp3', + }, + { + type: 'text', + userId, + createdAt: DateTime.now().minus({ days: 3 }).toJSDate(), + updatedAt: DateTime.now().minus({ days: 3 }).toJSDate(), + body: 'Great, thank you for your helpful response! I think I am all good now!', + }, + ], + }, + }, + }, + }) + + // Test conversation 2 - an example conversation that is in progress, no advisor assigned yet + await tx.conversation.create({ + data: { + title: 'Is my current investing strategy on track?', + userId, + messages: { + createMany: { + data: [ + { + type: 'text', + userId, + createdAt: DateTime.now().minus({ hours: 3 }).toJSDate(), + updatedAt: DateTime.now().minus({ hours: 3 }).toJSDate(), + body: 'I lost big with crypto last year and have decided to re-evaluate my strategy. Does this strategy seem appropriate for my age and goals?', + }, + ], + }, + }, + }, + }) + + // Test conversation 3 - an example conversation that is in progress, but will expire soon due to inactivity + await tx.conversation.create({ + data: { + title: 'Can I make good money in the stock market?', + userId, + messages: { + createMany: { + data: [ + { + type: 'text', + userId, + createdAt: DateTime.now().minus({ days: 15 }).toJSDate(), + updatedAt: DateTime.now().minus({ hours: 15 }).toJSDate(), + body: 'sample context', + }, + ], + }, + }, + }, + }) + + // Test conversation 4 - an example conversation that is in progress, but will be closed by cron job due to inactivity + await tx.conversation.create({ + data: { + title: 'What is the best software for filing taxes?', + userId, + messages: { + createMany: { + data: [ + { + type: 'text', + userId, + createdAt: DateTime.now().minus({ days: 20 }).toJSDate(), + updatedAt: DateTime.now().minus({ hours: 20 }).toJSDate(), + body: 'Sample context', + }, + ], + }, + }, + }, + }) + } + }) +} diff --git a/libs/server/features/src/email/email.processor.ts b/libs/server/features/src/email/email.processor.ts new file mode 100644 index 00000000000..34e8a7a8fc1 --- /dev/null +++ b/libs/server/features/src/email/email.processor.ts @@ -0,0 +1,268 @@ +import type { Logger } from 'winston' +import type { PrismaClient } from '@prisma/client' +import type { SendEmailQueueJobData } from '@maybe-finance/server/shared' +import type { IEmailService } from './email.service' +import type { ManagementClient } from 'auth0' +import type { SharedType } from '@maybe-finance/shared' +import { ATAUtil } from '@maybe-finance/shared' +import { DateTime } from 'luxon' +import { zip } from 'lodash' + +export interface IEmailProcessor { + send(jobData: SendEmailQueueJobData): Promise + sendTrialEndReminders(): Promise + sendConversationNotification(notification: SharedType.ConversationNotification): Promise +} + +export class EmailProcessor implements IEmailProcessor { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly auth0: ManagementClient, + private readonly emailService: IEmailService + ) {} + + async send(jobData: SendEmailQueueJobData) { + if ('type' in jobData) { + switch (jobData.type) { + case 'plain': + this.emailService.send(jobData.messages) + break + case 'template': + this.emailService.sendTemplate(jobData.messages) + break + case 'conversation-notification': + this.sendConversationNotification(jobData.notification) + break + case 'trial-reminders': + this.sendTrialEndReminders() + break + case 'conversation-expirations': + this.sendConversationExpiryNotifications() + break + } + } else { + this.logger.warn('Failed to process email send job', jobData) + } + } + + async sendConversationNotification({ + type, + conversationId, + }: SharedType.ConversationNotification) { + const conversation = await this.prisma.conversation.findUniqueOrThrow({ + where: { id: conversationId }, + select: { + id: true, + title: true, + status: true, + user: { + select: { + id: true, + auth0Id: true, + }, + }, + }, + }) + + if (conversation.status === 'closed' && type !== 'closed') { + this.logger.warn(`conversation is closed - skipping send of notification type=${type}`) + return + } + + const auth0User = await this.auth0.getUser({ id: conversation.user.auth0Id }) + + if (!auth0User.email) { + throw new Error( + `User is missing email user=${conversation.user.id} auth0Id=${conversation.user.auth0Id}` + ) + } + + const urls = ATAUtil.getUrls(conversation.id) + + switch (type) { + case 'submitted': { + await this.emailService.sendTemplate({ + to: auth0User.email, + template: { + alias: 'ata-conversation-created', + model: { + name: auth0User.user_metadata?.firstName, + question: conversation.title, + urls, + }, + }, + }) + break + } + default: { + const { subject, message } = ATAUtil.getEmailNotificationContent(type) + await this.emailService.sendTemplate({ + to: auth0User.email, + template: { + alias: 'ata-conversation-updated', + model: { + name: auth0User.user_metadata?.firstName, + subject, + message, + urls, + }, + }, + }) + break + } + } + } + + async sendConversationExpiryNotifications() { + const activeConversations = await this.prisma.conversation.findMany({ + where: { + status: 'open', + }, + include: { + user: { + select: { + auth0Id: true, + }, + }, + messages: { + select: { + createdAt: true, + }, + }, + }, + }) + + const expiringConversations = activeConversations.filter( + (c) => ATAUtil.getExpiryStatus(c) != null + ) + + if (!expiringConversations.length) { + this.logger.info('No expiring conversations found, skipping email notifications.') + return + } + + const emails = await Promise.all( + expiringConversations.map(async (c) => { + const auth0User = await this.auth0.getUser({ id: c.user.auth0Id }) + const emailType = ATAUtil.getExpiryStatus(c) + + return { + conversation: c, + type: emailType, + content: { + to: auth0User.email!, + template: { + alias: 'ata-conversation-updated', + model: { + name: auth0User.user_metadata!.firstName, + subject: + emailType === 'expired' + ? 'Your conversation has expired' + : 'Your conversation will expire in 3 days', + message: + emailType === 'expired' + ? 'Your Ask the Advisor conversation has been open for over 2 weeks without new messages, so we have archived it due to inactivity. To start a new conversation, please go to your dashboard and ask another question.' + : 'We just wanted to reach out and let you know that your conversation with your advisor will be expiring in 3 days due to inactivity. If you would like to keep this conversation open, please send a message from your dashboard.', + urls: { + conversation: `https://app.maybe.co/ask-the-advisor/${c.id}`, + settings: 'https://app.maybe.co/settings?tab=notifications', + }, + }, + }, + }, + } + }) + ) + + const emailBatchResults = await this.emailService.sendTemplate(emails.map((e) => e.content)) + + await Promise.all( + zip(emailBatchResults, emails) + .map(([result, email]) => { + if (result?.ErrorCode !== 0) { + this.logger.error( + `Could not send expiry email for conversation id=${email?.conversation.id}` + ) + return null + } + + this.logger.info( + `${ + email?.type === 'expired' ? 'Closing' : 'Sending expiry email for' + } conversation id=${email?.conversation.id}` + ) + + return this.prisma.conversation.update({ + where: { id: email?.conversation.id }, + data: { + status: email?.type === 'expired' ? 'closed' : undefined, + expiryEmailSent: + email?.type === 'expiring-soon' ? new Date() : undefined, + }, + }) + }) + .filter((v) => v != null) + ) + } + + async sendTrialEndReminders() { + // Find all users with trials expiring in under 3 days which haven't been notified in at least 7 days + const users = await this.prisma.user.findMany({ + where: { + trialEnd: { + gt: DateTime.now().toJSDate(), + lt: DateTime.now().plus({ days: 3 }).toJSDate(), + }, + stripeCancelAt: null, + OR: [ + { trialReminderSent: null }, + { + trialReminderSent: { + lt: DateTime.now().minus({ days: 7 }).toJSDate(), + }, + }, + ], + }, + }) + + if (!users.length) return + + const results = await this.emailService.sendTemplate( + users.map((user) => ({ + to: user.email, + template: { + alias: 'trial-ending', + model: { + endDate: DateTime.fromJSDate(user.trialEnd!).toFormat('MMM dd, yyyy'), + }, + }, + })) + ) + + const successful = results + .map((response, idx) => ({ + userId: users[idx]?.id, + response, + })) + .filter(({ userId, response }) => { + if (response.ErrorCode !== 0) + this.logger.error(`Failed to send trial end notification to user ${userId}`) + + return response.ErrorCode === 0 + }) + + if (successful.length) { + await this.prisma.user.updateMany({ + where: { + id: { + in: successful.map(({ userId }) => userId), + }, + }, + data: { + trialReminderSent: DateTime.now().toJSDate(), + }, + }) + } + } +} diff --git a/libs/server/features/src/email/email.schema.ts b/libs/server/features/src/email/email.schema.ts new file mode 100644 index 00000000000..d7cab53c88e --- /dev/null +++ b/libs/server/features/src/email/email.schema.ts @@ -0,0 +1,49 @@ +import { z } from 'zod' + +const ATABaseSchema = z.object({ + name: z.string(), + urls: z.object({ + conversation: z.string(), + settings: z.string(), + }), +}) + +// This schema should be kept in sync with templates maintained in the Postmark dashboard +export const EmailTemplateSchema = z.discriminatedUnion('alias', [ + z.object({ + alias: z.literal('ata-conversation-created'), + model: ATABaseSchema.and( + z.object({ + question: z.string(), + }) + ), + }), + z.object({ + alias: z.literal('ata-conversation-updated'), + model: ATABaseSchema.and( + z.object({ + subject: z.string(), + message: z.string(), + }) + ), + }), + z.object({ + alias: z.literal('trial-ending'), + model: z.object({ + endDate: z.string(), + }), + }), + z.object({ + alias: z.literal('agreements-update'), + model: z.object({ + name: z.string(), + urls: z.object({ + fee: z.string(), + form_adv_2a: z.string(), + form_adv_2b: z.string(), + form_crs: z.string(), + privacy_policy: z.string(), + }), + }), + }), +]) diff --git a/libs/server/features/src/email/email.service.ts b/libs/server/features/src/email/email.service.ts new file mode 100644 index 00000000000..6c908985733 --- /dev/null +++ b/libs/server/features/src/email/email.service.ts @@ -0,0 +1,131 @@ +import type { Logger } from 'winston' +import type { Message, ServerClient as PostmarkServerClient, TemplatedMessage } from 'postmark' +import type { MessageSendingResponse } from 'postmark/dist/client/models' +import type { SharedType } from '@maybe-finance/shared' +import { chunk, uniq } from 'lodash' +import { EmailTemplateSchema } from './email.schema' + +export interface IEmailService { + send(messages: SharedType.PlainEmailMessage): Promise + send(messages: SharedType.PlainEmailMessage[]): Promise + send( + messages: SharedType.PlainEmailMessage | SharedType.PlainEmailMessage[] + ): Promise + + sendTemplate(messages: SharedType.TemplateEmailMessage): Promise + sendTemplate(messages: SharedType.TemplateEmailMessage[]): Promise + sendTemplate( + messages: SharedType.TemplateEmailMessage | SharedType.TemplateEmailMessage[] + ): Promise +} + +export class EmailService implements IEmailService { + constructor( + private readonly logger: Logger, + private readonly postmark: PostmarkServerClient, + private readonly defaultAddresses: { from: string; replyTo?: string } + ) {} + + /** + * Sends plain email(s) + * + * @returns success boolean(s) + */ + async send(messages: SharedType.PlainEmailMessage): Promise + async send(messages: SharedType.PlainEmailMessage[]): Promise + async send( + messages: SharedType.PlainEmailMessage | SharedType.PlainEmailMessage[] + ): Promise { + const mapToPostmark = (message: SharedType.PlainEmailMessage): Message => ({ + From: message.from ?? this.defaultAddresses.from, + ReplyTo: message.replyTo ?? this.defaultAddresses.replyTo, + To: message.to, + Subject: message.subject, + TextBody: message.textBody, + HtmlBody: message.htmlBody, + }) + + return Array.isArray(messages) + ? this.sendEmailBatch(messages.map(mapToPostmark)) + : this.sendEmail(mapToPostmark(messages)) + } + + /** + * Sends template email(s) + */ + async sendTemplate(messages: SharedType.TemplateEmailMessage): Promise + async sendTemplate( + messages: SharedType.TemplateEmailMessage[] + ): Promise + async sendTemplate( + messages: SharedType.TemplateEmailMessage | SharedType.TemplateEmailMessage[] + ): Promise { + const mapToPostmark = (message: SharedType.TemplateEmailMessage): TemplatedMessage => { + const { alias, model } = EmailTemplateSchema.parse(message.template) + + return { + From: message.from ?? this.defaultAddresses.from, + ReplyTo: message.replyTo ?? this.defaultAddresses.replyTo, + To: message.to, + TemplateAlias: alias, + TemplateModel: model, + } + } + + return Array.isArray(messages) + ? this.sendEmailBatchWithTemplate(messages.map(mapToPostmark)) + : this.sendEmailWithTemplate(mapToPostmark(messages)) + } + + private async sendEmailWithTemplate( + message: TemplatedMessage + ): Promise { + this.logger.info( + `Sending templated email template=${message.TemplateAlias} from=${message.From} to=${message.To}`, + message.TemplateModel + ) + + return await this.postmark.sendEmailWithTemplate(message) + } + + private async sendEmail(message: Message): Promise { + this.logger.info( + `Sending plain email subject=${message.Subject} from=${message.From} to=${message.To}`, + { text: message.TextBody, html: message.HtmlBody } + ) + + return await this.postmark.sendEmail(message) + } + + private async sendEmailBatchWithTemplate( + messages: TemplatedMessage[] + ): Promise { + this.logger.info( + `Sending templated email batch templates=[${uniq( + messages.map(({ TemplateAlias }) => TemplateAlias) + ).join(',')}] count=${messages.length}` + ) + + return ( + await Promise.all( + chunk(messages, 500).map((chunk) => + this.postmark.sendEmailBatchWithTemplates(chunk) + ) + ) + ).flat() + } + + private async sendEmailBatch(messages: Message[]): Promise { + this.logger.info( + `Sending templated email batch subjects=[${uniq( + messages.map(({ Subject }) => Subject) + ).join(',')}] count=${messages.length}` + ) + + return ( + await Promise.all( + chunk(messages, 500).map((chunk) => this.postmark.sendEmailBatch(chunk)) + ) + ).flat() + } +} diff --git a/libs/server/features/src/email/index.ts b/libs/server/features/src/email/index.ts new file mode 100644 index 00000000000..602ab868a63 --- /dev/null +++ b/libs/server/features/src/email/index.ts @@ -0,0 +1,2 @@ +export * from './email.processor' +export * from './email.service' diff --git a/libs/server/features/src/holding/holding.schema.ts b/libs/server/features/src/holding/holding.schema.ts new file mode 100644 index 00000000000..adf8db04642 --- /dev/null +++ b/libs/server/features/src/holding/holding.schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod' + +export const HoldingUpdateInputSchema = z + .object({ + excluded: z.boolean(), + costBasisUser: z.number().nullable(), + }) + .partial() diff --git a/libs/server/features/src/holding/holding.service.ts b/libs/server/features/src/holding/holding.service.ts new file mode 100644 index 00000000000..c64faf5be01 --- /dev/null +++ b/libs/server/features/src/holding/holding.service.ts @@ -0,0 +1,73 @@ +import type { Logger } from 'winston' +import type { PrismaClient, Holding } from '@prisma/client' +import type { Prisma } from '@prisma/client' +import type { SharedType } from '@maybe-finance/shared' +import { DbUtil } from '@maybe-finance/server/shared' + +export class HoldingService { + constructor(private readonly logger: Logger, private readonly prisma: PrismaClient) {} + + async get(id: Holding['id']) { + return this.prisma.holding.findUniqueOrThrow({ + where: { id }, + include: { account: { include: { accountConnection: true } } }, + }) + } + + async getHoldingDetails(id: Holding['id']): Promise { + const [he] = await this.prisma.$queryRaw< + Array< + SharedType.HoldingEnriched & { + cost_basis_user: Prisma.Decimal | null + cost_basis_provider: Prisma.Decimal | null + } + > + >` + SELECT + he.*, + h.security_id, + h.cost_basis_user, + h.cost_basis_provider, + s.name, + s.symbol + FROM + holdings_enriched he + INNER JOIN security s ON s.id = he.security_id + INNER JOIN holding h ON h.id = he.id + WHERE + h.id = ${id}; + ` + + return { + id, + securityId: he.security_id, + name: he.name, + symbol: he.symbol, + quantity: DbUtil.toDecimal(he.quantity), + sharesPerContract: DbUtil.toDecimal(he.shares_per_contract), + costBasis: DbUtil.toDecimal(he.cost_basis), + costBasisUser: DbUtil.toDecimal(he.cost_basis_user), + costBasisProvider: DbUtil.toDecimal(he.cost_basis_provider), + price: DbUtil.toDecimal(he.price), + value: DbUtil.toDecimal(he.value), + trend: { + total: he.cost_basis ? DbUtil.calculateTrend(he.cost_basis, he.value) : null, + today: he.price_prev + ? DbUtil.calculateTrend(he.price_prev.times(he.quantity), he.value) + : null, + }, + excluded: he.excluded, + } + } + + async update(id: Holding['id'], data: Prisma.HoldingUncheckedUpdateInput) { + const holding = await this.prisma.holding.update({ + where: { id }, + data, + }) + + this.logger.info(`Updated holding id=${id} account=${holding.accountId}`) + + return holding + } +} diff --git a/libs/server/features/src/holding/index.ts b/libs/server/features/src/holding/index.ts new file mode 100644 index 00000000000..731ddcac0ca --- /dev/null +++ b/libs/server/features/src/holding/index.ts @@ -0,0 +1,2 @@ +export * from './holding.service' +export * from './holding.schema' diff --git a/libs/server/features/src/index.ts b/libs/server/features/src/index.ts new file mode 100644 index 00000000000..fb880007e15 --- /dev/null +++ b/libs/server/features/src/index.ts @@ -0,0 +1,15 @@ +export * from './account' +export * from './account-connection' +export * from './account-balance' +export * from './email' +export * from './institution' +export * from './security-pricing' +export * from './user' +export * from './valuation' +export * from './providers' +export * from './transaction' +export * from './holding' +export * from './investment-transaction' +export * from './plan' +export * from './conversation' +export * from './stripe' diff --git a/libs/server/features/src/institution/index.ts b/libs/server/features/src/institution/index.ts new file mode 100644 index 00000000000..0148d3ebb70 --- /dev/null +++ b/libs/server/features/src/institution/index.ts @@ -0,0 +1,2 @@ +export * from './institution.service' +export * from './institution.provider' diff --git a/libs/server/features/src/institution/institution.provider.ts b/libs/server/features/src/institution/institution.provider.ts new file mode 100644 index 00000000000..e731d8cee0e --- /dev/null +++ b/libs/server/features/src/institution/institution.provider.ts @@ -0,0 +1,19 @@ +import type { Prisma, Provider } from '@prisma/client' + +export interface IInstitutionProvider { + getInstitutions(): Promise[]> +} + +export interface IInstitutionProviderFactory { + for(provider: Provider): IInstitutionProvider +} + +export class InstitutionProviderFactory implements IInstitutionProviderFactory { + constructor(private readonly providers: Record) {} + + for(p: Provider): IInstitutionProvider { + const provider = this.providers[p] + if (!provider) throw new Error(`Unsupported provider: ${p}`) + return provider + } +} diff --git a/libs/server/features/src/institution/institution.service.ts b/libs/server/features/src/institution/institution.service.ts new file mode 100644 index 00000000000..32cfeebd6b7 --- /dev/null +++ b/libs/server/features/src/institution/institution.service.ts @@ -0,0 +1,283 @@ +import type { Institution, PrismaClient, Provider, ProviderInstitution } from '@prisma/client' +import type { Logger } from 'winston' +import type { PgService } from '@maybe-finance/server/shared' +import type { IInstitutionProviderFactory } from './institution.provider' +import { Prisma } from '@prisma/client' +import _ from 'lodash' +import { SharedType } from '@maybe-finance/shared' +import { join, sql } from '@maybe-finance/server/shared' + +export interface IInstitutionService { + getAll(options: { query?: string; page?: number }): Promise + sync(provider: Provider): Promise + deduplicateInstitutions(): Promise +} + +export class InstitutionService implements IInstitutionService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly pg: PgService, + private readonly providers: IInstitutionProviderFactory + ) {} + + async getAll({ + query, + page = 0, + }: { + query?: string + page?: number + } = {}): Promise { + if (!query) { + // filter for institutions with at least one provider + const institutionWhere: Prisma.InstitutionWhereInput = { + providers: { some: {} }, + } + + // filter for provider institutions not attached to an institution + const providerInstitutionWhere: Prisma.ProviderInstitutionWhereInput = { + institution: null, + } + + const [institutions, institutionCount, providerInstitutions, providerInstitutionCount] = + await this.prisma.$transaction([ + this.prisma.institution.findMany({ + where: institutionWhere, + include: { + providers: { + select: { + id: true, + provider: true, + providerId: true, + rank: true, + }, + orderBy: { rank: 'desc', oauth: 'desc' }, + }, + }, + orderBy: { name: 'asc' }, + skip: page * SharedType.PageSize.Institution, + take: SharedType.PageSize.Institution, + }), + this.prisma.institution.count({ where: institutionWhere }), + this.prisma.providerInstitution.findMany({ + where: providerInstitutionWhere, + orderBy: { name: 'asc' }, + skip: page * SharedType.PageSize.Institution, + take: SharedType.PageSize.Institution, + }), + this.prisma.providerInstitution.count({ where: providerInstitutionWhere }), + ]) + + return { + institutions: [ + ...institutions, + ...providerInstitutions.map((pi) => ({ + id: `provider[${pi.id}]`, + name: pi.name, + url: pi.url, + logo: pi.logo, + logoUrl: pi.logoUrl, + primaryColor: pi.primaryColor, + providers: [ + { + id: pi.id, + provider: pi.provider, + providerId: pi.providerId, + rank: pi.rank, + }, + ], + })), + ], + totalInstitutions: institutionCount + providerInstitutionCount, + } + } + + // autocomplete/search using postgres full-text search + const tokens = query + .trim() + .split(/\s+/gim) // remove whitespace + .map((x) => x.replace(/\W/gim, '')) // remove non-word characters + .filter((x) => x.length > 0) + .map((x) => `${x}:*`) // convert to prefix search query + this.logger.debug(`converted query: "${query}" to tokens: ${JSON.stringify(tokens)}`) + + const q = Prisma.sql`(${Prisma.join( + tokens.map((token) => Prisma.sql`to_tsquery('simple', ${token})`), + ' && ' + )})` + + type InstitutionQueryResult = (Pick< + Institution, + 'id' | 'name' | 'url' | 'logo' | 'logoUrl' | 'primaryColor' + > & { + providers: Pick[] + })[] + + const institutionSearchQuery = Prisma.sql` + SELECT + i.id, + i.name, + i.url, + i.logo, + i.logo_url AS "logoUrl", + i.primary_color AS "primaryColor", + jsonb_agg(jsonb_build_object('id', pi.id, 'provider', pi.provider, 'providerId', pi.provider_id, 'rank', pi.rank) ORDER BY pi.rank, pi.oauth DESC) AS providers + FROM + institution i + INNER JOIN provider_institution pi ON pi.institution_id = i.id + WHERE + edge_ngram_tsvector(i.name) @@ ${q} + AND pi.rank >= 0 + GROUP BY + i.id + ORDER BY + ts_rank(edge_ngram_tsvector(i.name), ${q}, 1|16) DESC, length(i.name) ASC + ` + + const providerInstitutionSearchQuery = Prisma.sql` + SELECT + 'provider.' || pi.id AS id, + pi.name, + pi.url, + pi.logo, + pi.logo_url AS "logoUrl", + pi.primary_color AS "primaryColor", + jsonb_build_array(jsonb_build_object('id', pi.id, 'provider', pi.provider, 'providerId', pi.provider_id, 'rank', pi.rank)) AS providers + FROM + provider_institution pi + WHERE + pi.institution_id IS NULL + AND edge_ngram_tsvector(pi.name) @@ ${q} + AND pi.rank >= 0 + ORDER BY + ts_rank(edge_ngram_tsvector(pi.name), ${q}, 1|16) DESC, length(pi.name) ASC + ` + + const [institutions, [{ count: institutionCount }], [{ count: providerInstitutionCount }]] = + await this.prisma.$transaction([ + this.prisma.$queryRaw` + ${institutionSearchQuery} + LIMIT ${SharedType.PageSize.Institution} + OFFSET ${page * SharedType.PageSize.Institution}; + `, + this.prisma.$queryRaw< + [{ count: bigint }] + >`SELECT COUNT(*) FROM (${institutionSearchQuery}) q`, + this.prisma.$queryRaw< + [{ count: bigint }] + >`SELECT COUNT(*) FROM (${providerInstitutionSearchQuery}) q`, + ]) + + const providerInstitutions = + institutions.length < SharedType.PageSize.Institution + ? await this.prisma.$queryRaw` + ${providerInstitutionSearchQuery} + LIMIT ${SharedType.PageSize.Institution - institutions.length} + OFFSET ${Math.max( + 0, + page * SharedType.PageSize.Institution - Number(institutionCount) + )}; + ` + : [] + + return { + institutions: [...institutions, ...providerInstitutions], + totalInstitutions: Number(institutionCount + providerInstitutionCount), + } + } + + async sync(provider: Provider) { + const institutions = await this.providers.for(provider).getInstitutions() + this.logger.info(`fetched ${institutions.length} institutions for provider ${provider}`) + + for (const chunk of _.chunk(institutions, 2000)) { + await this.pg.pool.query( + sql` + INSERT INTO provider_institution (provider, provider_id, name, url, logo, logo_url, primary_color, oauth, data) + VALUES + ${join( + chunk.map( + (institution) => sql`( + ${provider}, + ${institution.providerId}, + ${institution.name}, + ${institution.url}, + ${institution.logo}, + ${institution.logoUrl}, + ${institution.primaryColor}, + ${institution.oauth}, + ${institution.data as any} + )` + ) + )} + ON CONFLICT (provider, provider_id) DO UPDATE + SET + name = EXCLUDED.name, + url = EXCLUDED.url, + logo = EXCLUDED.logo, + logo_url = EXCLUDED.logo_url, + primary_color = EXCLUDED.primary_color, + oauth = EXCLUDED.oauth, + data = EXCLUDED.data; + ` + ) + } + + await this.pg.pool.query(sql` + DELETE FROM provider_institution + WHERE + provider = ${provider} + AND provider_id NOT IN (${join(institutions.map((i) => i.providerId))}) + `) + } + + async deduplicateInstitutions() { + await this.prisma.$executeRaw` + WITH duplicates AS ( + SELECT + MAX(TRIM(pi.name)) AS name, + x.url, + array_agg(pi.id) AS provider_ids + FROM + provider_institution pi + LEFT JOIN LATERAL ( + SELECT 'https://' || LOWER(TRIM(SPLIT_PART(pi.url, '://', 2))) AS url + ) x ON true + GROUP BY + UPPER(TRIM(pi.name)), + x.url + HAVING + COUNT(pi.id) > 1 AND COUNT(pi.institution_id) < COUNT(pi.id) + ), institutions AS ( + INSERT INTO institution (name, url, logo, logo_url, primary_color) + SELECT + name, + url, + (SELECT logo FROM provider_institution pi WHERE pi.id = ANY(provider_ids) AND logo IS NOT NULL LIMIT 1), + (SELECT logo_url FROM provider_institution pi WHERE pi.id = ANY(provider_ids) AND logo_url IS NOT NULL LIMIT 1), + (SELECT primary_color FROM provider_institution pi WHERE pi.id = ANY(provider_ids) AND primary_color IS NOT NULL LIMIT 1) + FROM + duplicates + ON CONFLICT (name, url) DO UPDATE + SET + name = EXCLUDED.name, + url = EXCLUDED.url, + logo = EXCLUDED.logo, + logo_url = EXCLUDED.logo_url, + primary_color = EXCLUDED.primary_color + RETURNING id, name, url + ) + UPDATE + provider_institution pi + SET + institution_id = i.id, + rank = (CASE WHEN pi.provider = 'PLAID' THEN 1 ELSE 0 END) + FROM + duplicates d + INNER JOIN institutions i ON i.name = d.name AND i.url = d.url + WHERE + pi.id = ANY(d.provider_ids) + AND pi.institution_id IS NULL; + ` + } +} diff --git a/libs/server/features/src/investment-transaction/index.ts b/libs/server/features/src/investment-transaction/index.ts new file mode 100644 index 00000000000..8ad8a72c8ea --- /dev/null +++ b/libs/server/features/src/investment-transaction/index.ts @@ -0,0 +1 @@ +export * from './investment-transaction.schema' diff --git a/libs/server/features/src/investment-transaction/investment-transaction.schema.ts b/libs/server/features/src/investment-transaction/investment-transaction.schema.ts new file mode 100644 index 00000000000..cb8c7b5ec1c --- /dev/null +++ b/libs/server/features/src/investment-transaction/investment-transaction.schema.ts @@ -0,0 +1,12 @@ +import { z } from 'zod' + +export const InvestmentTransactionCategorySchema = z.enum([ + 'buy', + 'sell', + 'dividend', + 'transfer', + 'tax', + 'fee', + 'cancel', + 'other', +]) diff --git a/libs/server/features/src/plan/index.ts b/libs/server/features/src/plan/index.ts new file mode 100644 index 00000000000..b568ecd309e --- /dev/null +++ b/libs/server/features/src/plan/index.ts @@ -0,0 +1,3 @@ +export * from './plan.service' +export * from './plan.schema' +export * from './projection' diff --git a/libs/server/features/src/plan/plan.schema.ts b/libs/server/features/src/plan/plan.schema.ts new file mode 100644 index 00000000000..a8d79f2be59 --- /dev/null +++ b/libs/server/features/src/plan/plan.schema.ts @@ -0,0 +1,91 @@ +import Decimal from 'decimal.js' +import { z } from 'zod' + +const DecimalSchema = z.union([z.number(), z.string(), z.instanceof(Decimal)]) +const IdSchema = z.number().int() +const YearSchema = z.number().int().positive() + +const RetirementTemplateSchema = z.object({ + retirementYear: z.number(), + annualIncome: DecimalSchema.nullish(), + annualRetirementIncome: DecimalSchema.nullish(), + annualExpenses: DecimalSchema.nullish(), + annualRetirementExpenses: DecimalSchema.nullish(), +}) + +export const PlanTemplateSchema = z.discriminatedUnion('type', [ + z.object({ + type: z.literal('retirement'), + data: RetirementTemplateSchema, + }), + z.object({ + type: z.literal('placeholder'), + data: z.object({}).default({}), + }), +]) + +export type RetirementTemplate = z.infer +export type PlanTemplate = z.infer + +const PlanEventCreateSchema = z.object({ + name: z.string(), + category: z.string().nullish(), + frequency: z.enum(['monthly', 'yearly']).optional(), + initialValue: DecimalSchema.nullish(), + initialValueRef: z.enum(['income', 'expenses']).nullish(), + rate: DecimalSchema.optional(), + startYear: YearSchema.nullish(), + startMilestoneId: IdSchema.nullish(), + endYear: YearSchema.nullish(), + endMilestoneId: IdSchema.nullish(), +}) + +const PlanEventUpdateSchema = PlanEventCreateSchema.partial() + +const PlanMilestoneBaseCreateSchema = z.object({ + name: z.string(), + category: z.string().optional(), +}) + +const PlanMilestoneTypeCreateSchema = z.discriminatedUnion('type', [ + z.object({ + type: z.literal('year'), + year: YearSchema, + }), + z.object({ + type: z.literal('net_worth'), + expenseMultiple: z.number().nonnegative(), + expenseYears: z.number().int().nonnegative(), + }), +]) + +const PlanMilestoneCreateSchema = PlanMilestoneBaseCreateSchema.and(PlanMilestoneTypeCreateSchema) +const PlanMilestoneUpdateSchema = PlanMilestoneBaseCreateSchema.partial().and( + PlanMilestoneTypeCreateSchema +) + +export const PlanCreateSchema = z.object({ + name: z.string(), + lifeExpectancy: z.number(), + events: z.array(PlanEventCreateSchema).default([]), + milestones: z.array(PlanMilestoneCreateSchema).default([]), +}) + +export const PlanUpdateSchema = PlanCreateSchema.omit({ events: true, milestones: true }) + .extend({ + events: z + .object({ + create: z.array(PlanEventCreateSchema), + update: z.array(z.object({ id: IdSchema, data: PlanEventUpdateSchema })), + delete: z.array(IdSchema), + }) + .partial(), + milestones: z + .object({ + create: z.array(PlanMilestoneCreateSchema), + update: z.array(z.object({ id: IdSchema, data: PlanMilestoneUpdateSchema })), + delete: z.array(IdSchema), + }) + .partial(), + }) + .partial() diff --git a/libs/server/features/src/plan/plan.service.ts b/libs/server/features/src/plan/plan.service.ts new file mode 100644 index 00000000000..b8fa4aca8b4 --- /dev/null +++ b/libs/server/features/src/plan/plan.service.ts @@ -0,0 +1,467 @@ +import type { Prisma, Plan, User, PrismaClient, PlanEvent, PlanMilestone } from '@prisma/client' +import { PlanEventFrequency } from '@prisma/client' +import type { SharedType } from '@maybe-finance/shared' +import type { IInsightService } from '../account' +import type { IProjectionCalculator, ProjectionInput, ProjectionSeriesData } from './projection' +import type { PlanTemplate, RetirementTemplate } from './plan.schema' +import Decimal from 'decimal.js' +import { DateTime } from 'luxon' +import _ from 'lodash' +import { DateUtil, PlanUtil, SharedUtil, StatsUtil } from '@maybe-finance/shared' +import { AssetValue, monteCarlo } from './projection' + +const PERCENTILES: Decimal[] = ['0.1', '0.9'].map((p) => new Decimal(p)) +const MONTE_CARLO_N = 1_000 + +type PlanWithEventsMilestones = Plan & { events: PlanEvent[]; milestones: PlanMilestone[] } +type PlanWithEventsMilestonesUser = PlanWithEventsMilestones & { user: User } + +type ValueRefMap = Record + +function resolveValueRef(valueRef: string, valueRefMap: ValueRefMap): Prisma.Decimal { + return valueRefMap[valueRef] ?? 0 +} + +function yearToDate(year: number) { + return DateTime.fromObject({ year }, { zone: 'utc' }).toISODate() +} + +/** + * Mapping of asset types -> [avg annual return, annual return standard deviation] + */ +const PROJECTION_ASSET_PARAMS: { + [type in SharedType.ProjectionAssetType]: [mean: Decimal.Value, stddev: Decimal.Value] +} = { + stocks: ['0.05', '0.186'], + bonds: ['0.02', '0.052'], + cash: ['-0.02', '0.05'], + crypto: ['1.0', '1.0'], + property: ['0.1', '0.2'], + other: ['-0.02', '0'], +} + +export interface IPlanService { + get(id: Plan['id']): Promise + getAll(userId: User['id']): Promise + create(data: Prisma.PlanUncheckedCreateInput): Promise + createWithTemplate(user: User, template: PlanTemplate): Promise + update(id: Plan['id'], data: Prisma.PlanUncheckedUpdateInput): Promise + updateWithTemplate( + planId: Plan['id'], + template: PlanTemplate, + shouldReset?: boolean + ): Promise + delete(id: Plan['id']): Promise + projections(id: Plan['id']): Promise +} + +export class PlanService implements IPlanService { + constructor( + private readonly prisma: PrismaClient, + private readonly projectionCalculator: IProjectionCalculator, + private readonly insightService: IInsightService + ) {} + + async get(id: Plan['id']): Promise { + const plan = await this.prisma.plan.findUniqueOrThrow({ + where: { id }, + include: { + user: true, + events: true, + milestones: true, + }, + }) + + return this._mapToSharedPlan(plan, await this._getValueRefMap(plan.user.id)) + } + + async getAll(userId: User['id']): Promise { + const [user, plans] = await Promise.all([ + this.prisma.user.findUniqueOrThrow({ where: { id: userId } }), + this.prisma.plan.findMany({ + where: { userId }, + include: { + events: true, + milestones: true, + }, + orderBy: { createdAt: 'desc' }, + }), + ]) + + const insights = await this.insightService.getPlanInsights({ userId: user.id }) + const valueRefMap = this._toValueRefMap(insights) + + return { + plans: plans.map((plan) => this._mapToSharedPlan(plan, valueRefMap)), + } + } + + async create(data: Prisma.PlanUncheckedCreateInput) { + return this.prisma.plan.create({ + data, + }) + } + + async createWithTemplate(user: User, template: PlanTemplate) { + const plan = await this._connectTemplate(template, async (tx, data) => { + const _plan = await tx.plan.create({ data: { ...data, userId: user.id } }) + return _plan.id + }) + + const insights = await this.insightService.getPlanInsights({ userId: user.id }) + const valueRefMap = this._toValueRefMap(insights) + + return this._mapToSharedPlan(plan, valueRefMap) + } + + async update(id: Plan['id'], data: Prisma.PlanUncheckedUpdateInput) { + const plan = await this.prisma.plan.update({ + where: { id }, + include: { + user: true, + events: true, + milestones: true, + }, + data, + }) + + return this._mapToSharedPlan(plan, await this._getValueRefMap(plan.user.id)) + } + + async updateWithTemplate(planId: Plan['id'], template: PlanTemplate, reset = false) { + // Clear out previous templates's milestones/events + if (reset) { + await this.prisma.plan.update({ + where: { id: planId }, + data: { events: { deleteMany: {} }, milestones: { deleteMany: {} } }, + }) + } + + const plan = await this._connectTemplate(template, planId) + const insights = await this.insightService.getPlanInsights({ userId: plan.user.id }) + const valueRefMap = this._toValueRefMap(insights) + + return this._mapToSharedPlan(plan, valueRefMap) + } + + async delete(id: Plan['id']) { + return this.prisma.plan.delete({ where: { id } }) + } + + async projections(id: Plan['id']) { + const plan = await this.prisma.plan.findUniqueOrThrow({ + where: { id }, + include: { + user: true, + events: true, + milestones: true, + }, + }) + + const insights = await this.insightService.getPlanInsights({ userId: plan.user.id }) + + const age = DateUtil.dobToAge(plan.user.dob) ?? PlanUtil.DEFAULT_AGE + + const inputTheo = this._toProjectionInput(plan, age, insights, false) + const theo = this.projectionCalculator.calculate(inputTheo) + + const inputRandomized = this._toProjectionInput(plan, age, insights, true) + const simulations = monteCarlo(() => this.projectionCalculator.calculate(inputRandomized), { + n: MONTE_CARLO_N, + }) + + const simulationStats = _.zipWith(...simulations, (...series) => { + const year = series[0].year + const netWorths = series.map((d) => d.netWorth) + const successRate = StatsUtil.rateOf(netWorths, (netWorth) => netWorth.gt(0)) + + return { + year, + percentiles: StatsUtil.quantiles(netWorths, PERCENTILES), + successRate, + } + }) + + const simulationsByPercentile = PERCENTILES.map((percentile, idx) => ({ + percentile, + simulation: simulationStats.map(({ year, percentiles }) => ({ + year, + netWorth: percentiles[idx], + })), + })) + + const planMapped = this._mapToSharedPlan(plan, this._toValueRefMap(insights)) + + return { + input: inputRandomized, + projection: this._mapToProjectionTimeSeries(theo, simulationStats, planMapped, age), + simulations: simulationsByPercentile.map(({ percentile, simulation }) => ({ + percentile, + simulation: this._mapToSimulationTimeSeries(simulation, age), + })), + } + } + + private async _connectTemplate( + template: PlanTemplate, + planIdAccessor: + | (( + tx: Prisma.TransactionClient, + data: Omit + ) => Promise) + | Plan['id'] + ) { + return this.prisma.$transaction(async (tx) => { + let updatedPlan: PlanWithEventsMilestonesUser + + switch (template.type) { + case 'retirement': { + const _planId = + typeof planIdAccessor === 'function' + ? await planIdAccessor(tx, { name: 'Retirement Plan' }) + : planIdAccessor + + updatedPlan = await this._connectRetirementTemplate(tx, _planId, template.data) + + break + } + default: { + throw new Error('Template not implemented') + } + } + + return updatedPlan + }) + } + + private async _connectRetirementTemplate( + tx: Prisma.TransactionClient, + planId: Plan['id'], + data: RetirementTemplate & { userAge?: number } + ) { + const milestone = await tx.planMilestone.create({ + data: { + planId, + name: 'Retirement', + category: PlanUtil.PlanMilestoneCategory.Retirement, + type: 'year', + year: data.retirementYear, + }, + }) + + return await tx.plan.update({ + where: { id: planId }, + data: { + events: { + createMany: { + data: [ + // User's current income, stops at retirement + { + name: 'Income (current)', + endMilestoneId: milestone.id, + frequency: PlanEventFrequency.yearly, + initialValue: data.annualIncome ? data.annualIncome : undefined, + initialValueRef: data.annualIncome ? undefined : 'income', + }, + + // User's retirement income - if not specified, we assume no income + ...(data.annualRetirementIncome + ? [ + { + name: 'Income (retirement)', + startMilestoneId: milestone.id, + frequency: PlanEventFrequency.yearly, + initialValue: data.annualRetirementIncome, + }, + ] + : []), + + // User's current expenses, stops at retirement + { + name: 'Expenses (current)', + endMilestoneId: milestone.id, + frequency: PlanEventFrequency.yearly, + initialValue: data.annualExpenses ? data.annualExpenses : undefined, + initialValueRef: data.annualExpenses ? undefined : 'expenses', + }, + + // User's post-retirement expenses - if not specified, defaults to current expenses + { + name: 'Expenses (retirement)', + startMilestoneId: milestone.id, + frequency: PlanEventFrequency.yearly, + initialValue: data.annualRetirementExpenses + ? data.annualRetirementExpenses + : undefined, + initialValueRef: data.annualRetirementExpenses + ? undefined + : 'expenses', + }, + ], + }, + }, + }, + include: { + user: true, + events: true, + milestones: true, + }, + }) + } + + private _toProjectionInput( + plan: PlanWithEventsMilestones, + age: number, + insights: SharedType.PlanInsights, + randomized: boolean + ): ProjectionInput { + const valueRefMap = this._toValueRefMap(insights) + + return { + years: plan.lifeExpectancy - age + 1, + assets: insights.projectionAssetBreakdown.map(({ type, amount }) => { + const [mean, std] = PROJECTION_ASSET_PARAMS[type] + return { + id: type, + value: new AssetValue(amount.toString(), mean, randomized ? std : 0), + } + }), + liabilities: insights.projectionLiabilityBreakdown.map(({ type, amount }) => { + return { + id: type, + value: new AssetValue(amount.toString()), + } + }), + events: plan.events.map( + ({ + id, + startYear, + startMilestoneId, + endYear, + endMilestoneId, + frequency, + initialValue, + initialValueRef, + rate, + }) => { + const value = initialValue ?? resolveValueRef(initialValueRef!, valueRefMap) + + const valueYearly = Decimal.mul( + value.toString(), + frequency === 'monthly' ? 12 : 1 + ) + + return { + id: id.toString(), + value: new AssetValue(valueYearly, rate.toString()), + start: startYear ?? startMilestoneId?.toString(), + end: endYear ?? endMilestoneId?.toString(), + } + } + ), + milestones: plan.milestones.map((milestone) => { + switch (milestone.type) { + case 'year': + return { + id: milestone.id.toString(), + type: 'year', + year: milestone.year!, + } + case 'net_worth': + return { + id: milestone.id.toString(), + type: 'net-worth', + expenseMultiple: milestone.expenseMultiple!, + expenseYears: milestone.expenseYears!, + } + } + }), + } + } + + private _toValueRefMap(insights: SharedType.PlanInsights): ValueRefMap { + return { + income: insights.income.toDP(2), + expenses: insights.expenses.negated().toDP(2), + } + } + + private async _getValueRefMap(userId: User['id']): Promise { + const insights = await this.insightService.getPlanInsights({ userId }) + return this._toValueRefMap(insights) + } + + /** + * Converts a plan to its DTO representation. + */ + private _mapToSharedPlan( + plan: Plan & { + events: PlanEvent[] + milestones: PlanMilestone[] + }, + valueRefMap: ValueRefMap + ): SharedType.Plan { + return { + ...plan, + events: plan.events.map((event) => ({ + ...event, + initialValue: event.initialValueRef + ? resolveValueRef(event.initialValueRef, valueRefMap) + : event.initialValue!, + })), + } + } + + private _mapToProjectionTimeSeries( + theo: ProjectionSeriesData[], + simulationStats: { year: number; successRate: Decimal }[], + plan: SharedType.Plan, + currentAge: number + ): SharedType.TimeSeries { + return { + interval: 'years', + start: yearToDate(theo[0].year), + end: yearToDate(theo[theo.length - 1].year), + data: theo.map((data, idx) => { + const { successRate } = simulationStats.find((x) => x.year === data.year)! + return { + date: yearToDate(data.year), + values: { + age: currentAge + idx, + year: data.year, + netWorth: data.netWorth, + events: data.events + .map((e) => { + const event = plan.events.find((event) => event.id === +e.id) + return event ? { event, calculatedValue: e.balance } : undefined + }) + .filter(SharedUtil.nonNull), + milestones: data.milestones + .map((m) => plan.milestones.find((milestone) => milestone.id === +m.id)) + .filter(SharedUtil.nonNull), + successRate, + }, + } + }), + } + } + + private _mapToSimulationTimeSeries( + simulation: { year: number; netWorth: Decimal }[], + currentAge: number + ): SharedType.TimeSeries { + return { + interval: 'years', + start: yearToDate(simulation[0].year), + end: yearToDate(simulation[simulation.length - 1].year), + data: simulation.map((data, idx) => ({ + date: yearToDate(data.year), + values: { + age: currentAge + idx, + year: data.year, + netWorth: data.netWorth, + }, + })), + } + } +} diff --git a/libs/server/features/src/plan/projection/index.ts b/libs/server/features/src/plan/projection/index.ts new file mode 100644 index 00000000000..0bc59f3ad82 --- /dev/null +++ b/libs/server/features/src/plan/projection/index.ts @@ -0,0 +1,3 @@ +export * from './projection-calculator' +export * from './projection-value' +export * from './monte-carlo' diff --git a/libs/server/features/src/plan/projection/monte-carlo.spec.ts b/libs/server/features/src/plan/projection/monte-carlo.spec.ts new file mode 100644 index 00000000000..d945fd61703 --- /dev/null +++ b/libs/server/features/src/plan/projection/monte-carlo.spec.ts @@ -0,0 +1,14 @@ +import Decimal from 'decimal.js' +import { monteCarlo } from './monte-carlo' + +const d = (x: Decimal.Value) => new Decimal(x) + +describe('monte carlo', () => { + it('simulates', () => { + const results = monteCarlo(() => Math.random(), { + n: 1_000, + }) + + expect(results).toHaveLength(1_000) + }) +}) diff --git a/libs/server/features/src/plan/projection/monte-carlo.ts b/libs/server/features/src/plan/projection/monte-carlo.ts new file mode 100644 index 00000000000..8739f8a0a9e --- /dev/null +++ b/libs/server/features/src/plan/projection/monte-carlo.ts @@ -0,0 +1,12 @@ +import range from 'lodash/range' + +type MonteCarloOptions = { + n: number +} + +export function monteCarlo( + fn: (i: number) => T, + { n = 1_000 }: Partial = {} +) { + return range(n).map(fn) +} diff --git a/libs/server/features/src/plan/projection/projection-calculator.spec.ts b/libs/server/features/src/plan/projection/projection-calculator.spec.ts new file mode 100644 index 00000000000..eb6d0754a47 --- /dev/null +++ b/libs/server/features/src/plan/projection/projection-calculator.spec.ts @@ -0,0 +1,198 @@ +import Decimal from 'decimal.js' +import { DateTime } from 'luxon' +import { AssetValue } from './projection-value' +import { ProjectionCalculator } from './projection-calculator' + +expect.extend({ + toEqualDecimal( + received: Decimal.Value, + expected: Decimal.Value, + threshold: Decimal.Value = '0.01' + ) { + const pass = Decimal.sub(received, expected).abs().lt(threshold) + return { + pass, + message: () => + `expected ${this.utils.printReceived(received)} ${ + pass ? `not to be` : 'to be' + } within ${threshold} of ${this.utils.printExpected(expected)}`, + } + }, +}) + +interface CustomMatchers { + toEqualDecimal(expected: Decimal.Value, threshold?: Decimal.Value): R +} + +/* eslint-disable */ +declare global { + namespace jest { + interface Expect extends CustomMatchers {} + interface Matchers extends CustomMatchers {} + interface InverseAsymmetricMatchers extends CustomMatchers {} + } +} +/* eslint-enable */ + +const calculator = new ProjectionCalculator() + +describe('projection service', () => { + it('simulates assets', () => { + const series = calculator.calculate( + { + years: 30, + assets: [ + { id: 'stock', value: new AssetValue(800, 0.05) }, + { id: 'bonds', value: new AssetValue(150, 0.02) }, + { id: 'cash', value: new AssetValue(50, -0.2) }, + ], + liabilities: [], + events: [], + milestones: [], + }, + DateTime.fromISO('2022-01-01') + ) + + expect(series).toHaveLength(30) + + series.forEach((data) => { + expect(data.assets).toHaveLength(3) + }) + + expect(series[0]).toMatchObject({ year: 2022, netWorth: expect.toEqualDecimal(1000) }) + expect(series[1]).toMatchObject({ year: 2023, netWorth: expect.toEqualDecimal(1033) }) + expect(series[2]).toMatchObject({ year: 2024, netWorth: expect.toEqualDecimal(1067.09) }) + expect(series[29]).toMatchObject({ year: 2051, netWorth: expect.toEqualDecimal(2563.95) }) + }) + + it('simulates liabilities', () => { + const series = calculator.calculate( + { + years: 10, + assets: [{ id: 'asset', value: new AssetValue(100) }], + liabilities: [{ id: 'liability', value: new AssetValue(50) }], + events: [], + milestones: [], + }, + DateTime.fromISO('2022-01-01') + ) + + expect(series).toHaveLength(10) + + series.forEach((data) => { + expect(data.liabilities).toHaveLength(1) + }) + + expect(series[0]).toMatchObject({ year: 2022, netWorth: expect.toEqualDecimal(50) }) + expect(series[1]).toMatchObject({ year: 2023, netWorth: expect.toEqualDecimal(50) }) + expect(series[2]).toMatchObject({ year: 2024, netWorth: expect.toEqualDecimal(50) }) + expect(series[9]).toMatchObject({ year: 2031, netWorth: expect.toEqualDecimal(50) }) + }) + + it('simulates events', () => { + const series = calculator.calculate( + { + years: 30, + assets: [ + { id: 'stock', value: new AssetValue(800, 0.05) }, + { id: 'bonds', value: new AssetValue(150, 0.02) }, + { id: 'cash', value: new AssetValue(50, -0.03) }, + ], + liabilities: [], + events: [ + { id: 'salary', value: new AssetValue(2000) }, + { id: 'rent', value: new AssetValue(-1000) }, + { id: 'windfall', value: new AssetValue(100), start: 2025, end: 2025 }, + ], + milestones: [], + }, + DateTime.fromISO('2022-01-01') + ) + + expect(series).toHaveLength(30) + expect(series[0]).toMatchObject({ year: 2022, netWorth: expect.toEqualDecimal(2000) }) + expect(series[1]).toMatchObject({ year: 2023, netWorth: expect.toEqualDecimal(3083) }) + expect(series[2]).toMatchObject({ year: 2024, netWorth: expect.toEqualDecimal(4210.94) }) + expect(series[2].events).toHaveLength(2) + expect(series[3]).toMatchObject({ year: 2025, netWorth: expect.toEqualDecimal(5485.7) }) + expect(series[3].events).toHaveLength(3) + expect(series[4]).toMatchObject({ year: 2026, netWorth: expect.toEqualDecimal(6713.36) }) + expect(series[4].events).toHaveLength(2) + }) + + it('simulates milestones', () => { + const series = calculator.calculate( + { + years: 30, + assets: [ + { id: 'stock', value: new AssetValue(800, 0.05) }, + { id: 'bonds', value: new AssetValue(150, 0.02) }, + { id: 'cash', value: new AssetValue(50, -0.03) }, + ], + liabilities: [], + events: [ + { id: 'salary', value: new AssetValue(2000), end: 'fi' }, + { id: 'rent', value: new AssetValue(-1000) }, + { + id: 'fi-spend', + value: new AssetValue(-100), + start: 'fi', + end: 'retirement', + }, + { id: 'retirement-spend', value: new AssetValue(-100), start: 'retirement' }, + ], + milestones: [ + { id: 'fi', type: 'net-worth', expenseMultiple: 25, expenseYears: 3 }, + { id: 'retirement', type: 'year', year: 2050 }, + ], + }, + DateTime.fromISO('2022-01-01') + ) + + expect(series).toHaveLength(30) + expect(series[0]).toMatchObject({ year: 2022, netWorth: expect.toEqualDecimal(2000) }) + expect(series[1]).toMatchObject({ year: 2023, netWorth: expect.toEqualDecimal(3083) }) + expect(series[2]).toMatchObject({ year: 2024, netWorth: expect.toEqualDecimal(4210.94) }) + + // year before `fi` milestone + expect(series[15].events.map((e) => e.id)).toEqual(['salary', 'rent']) + // year of `fi` milestone + expect(series[16].events.map((e) => e.id)).toEqual(['salary', 'rent']) + expect(series[16].milestones.map((m) => m.id)).toEqual(['fi']) + // year after `fi` milestone + expect(series[17].events.map((e) => e.id)).toEqual(['rent', 'fi-spend']) + + // year before `retirement` milestone + expect(series[27].events.map((e) => e.id)).toEqual(['rent', 'fi-spend']) + // year of `retirement` milestone + expect(series[28].events.map((e) => e.id)).toEqual(['rent', 'fi-spend', 'retirement-spend']) + expect(series[28].milestones.map((m) => m.id)).toEqual(['retirement']) + // year after `retirement` milestone + expect(series[29].events.map((e) => e.id)).toEqual(['rent', 'retirement-spend']) + }) + + it('events end in same year as referenced milestone', () => { + const series = calculator.calculate( + { + years: 10, + assets: [], + liabilities: [], + events: [{ id: 'income', value: new AssetValue(1_000), end: 'retirement' }], + milestones: [{ id: 'retirement', type: 'year', year: 2025 }], + }, + DateTime.fromISO('2022-01-01') + ) + + // 2024 + expect(series[2].events).toHaveLength(1) + expect(series[2].milestones).toHaveLength(0) + + // 2025 + expect(series[3].events).toHaveLength(1) + expect(series[3].milestones).toHaveLength(1) + + // 2026 + expect(series[4].events).toHaveLength(0) + expect(series[4].milestones).toHaveLength(0) + }) +}) diff --git a/libs/server/features/src/plan/projection/projection-calculator.ts b/libs/server/features/src/plan/projection/projection-calculator.ts new file mode 100644 index 00000000000..7bac5ef6576 --- /dev/null +++ b/libs/server/features/src/plan/projection/projection-calculator.ts @@ -0,0 +1,197 @@ +import type { ProjectionValue } from './projection-value' +import type Decimal from 'decimal.js' +import { DateTime } from 'luxon' +import { NumberUtil } from '@maybe-finance/shared' +import range from 'lodash/range' + +export type ProjectionAsset = { + id: string + value: ProjectionValue +} + +export type ProjectionLiability = { + id: string + value: ProjectionValue +} + +export type ProjectionMilestone = { + id: string +} & ( + | { + type: 'year' + year: number + } + | { + type: 'net-worth' + expenseMultiple: number + expenseYears: number + } +) + +export type ProjectionEvent = { + id: string + value: ProjectionValue + start?: number | ProjectionMilestone['id'] | null + end?: number | ProjectionMilestone['id'] | null +} + +export type ProjectionInput = { + years: number + assets: ProjectionAsset[] + liabilities: ProjectionLiability[] + events: ProjectionEvent[] + milestones: ProjectionMilestone[] +} + +export type ProjectionSeriesData = { + year: number + netWorth: Decimal + assets: { id: ProjectionAsset['id']; balance: Decimal }[] + liabilities: { id: ProjectionLiability['id']; balance: Decimal }[] + events: { id: ProjectionEvent['id']; balance: Decimal }[] + milestones: { id: ProjectionMilestone['id'] }[] +} + +export interface IProjectionCalculator { + calculate(input: ProjectionInput, now?: DateTime): ProjectionSeriesData[] +} + +export class ProjectionCalculator implements IProjectionCalculator { + calculate(input: ProjectionInput, now = DateTime.now()): ProjectionSeriesData[] { + const initialAssets = NumberUtil.sumBy(input.assets, (a) => a.value.initialValue) + const assetsWithAllocation = input.assets.map((asset) => ({ + ...asset, + allocation: asset.value.initialValue.dividedBy(initialAssets), + })) + + const milestones: { id: ProjectionMilestone['id']; year: number }[] = input.milestones + .filter((m): m is Extract => m.type === 'year') + .map((m) => ({ id: m.id, year: m.year })) + + return range(input.years).reduce((acc, t) => { + const year = now.year + t + + // events + const events = input.events + .filter((e) => this.isActive(e, year, milestones)) + .map((event) => { + if (t === 0) { + return { id: event.id, balance: event.value.initialValue } + } + + const balancePrev = acc[t - 1]!.events.find((e) => e.id === event.id)?.balance + + return { + id: event.id, + balance: event.value.next(balancePrev), + } + }) + const netEvents = NumberUtil.sumBy(events, (e) => e.balance) + + // assets + const assets = assetsWithAllocation.map((asset) => { + if (t === 0) { + return { + id: asset.id, + balance: asset.value.initialValue, + } + } + + // in order to determine this asset's last balance we assume a constant allocation + // of contributions (ie. netEventsPrev) based on the initial (t0) allocation + // that way we don't have to do any manual "rebalancing" of the asset portfolio + const netAssetsPrev = NumberUtil.sumBy(acc[t - 1]!.assets, (a) => a.balance) + const netEventsPrev = NumberUtil.sumBy(acc[t - 1]!.events, (e) => e.balance) + const balancePrev = netAssetsPrev.plus(netEventsPrev).times(asset.allocation) + + return { + id: asset.id, + balance: asset.value.next(balancePrev), + } + }) + const assetsTotal = NumberUtil.sumBy(assets, (a) => a.balance) + + // liabilities + const liabilities = input.liabilities.map((liability, idx) => { + if (t === 0) { + return { + id: liability.id, + balance: liability.value.initialValue, + } + } + + // ToDo: update this logic to apply "payments" made each year towards this liability (via `netEventsPrev`) so that the balance goes down over time. + // - we'll need to figure out priority for how excess money gets distributed between assets vs liabilities + // - ProjectionLab handles this by allowing user to allocate $X/yr to each liability, or specify a payment plan (eg. "Pay over 10 years"), or specify % of excess cash that gets put towards paying down debt + const balancePrev = acc[t - 1]!.liabilities[idx]!.balance + + return { + id: liability.id, + balance: liability.value.next(balancePrev), + } + }) + const liabilitiesTotal = NumberUtil.sumBy(liabilities, (l) => l.balance) + + const netWorth = assetsTotal.minus(liabilitiesTotal).plus(netEvents) + + // milestones + milestones.push( + ...input.milestones + .filter(({ id }) => !milestones.some((m) => m.id === id)) + .map((milestone) => { + switch (milestone.type) { + case 'net-worth': { + const data = acc.slice(-milestone.expenseYears) + const target = NumberUtil.sumBy(data, ({ events }) => + NumberUtil.sumBy( + events.filter((e) => e.balance.lt(0)), + (e) => e.balance.abs() + ) + ) + .dividedBy(data.length) + .times(milestone.expenseMultiple) + + return { + id: milestone.id, + year: netWorth.gte(target) ? year : null, + } + } + default: + return { id: milestone.id, year: milestone.year } + } + }) + .filter((m): m is typeof milestones[0] => m.year != null) + ) + + acc.push({ + year, + netWorth, + assets, + liabilities, + events, + milestones: milestones.filter((m) => m.year === year), + }) + + return acc + }, [] as ProjectionSeriesData[]) + } + + private isActive( + event: ProjectionEvent, + year: number, + milestones: { id: ProjectionMilestone['id']; year: number }[] + ): boolean { + const hasStarted = + event.start == null || + (typeof event.start === 'number' && year >= event.start) || + (typeof event.start === 'string' && + milestones.some((m) => m.id === event.start && year >= m.year)) + + const hasEnded = + (typeof event.end === 'number' && year > event.end) || + (typeof event.end === 'string' && + milestones.some((m) => m.id === event.end && year > m.year)) + + return hasStarted && !hasEnded + } +} diff --git a/libs/server/features/src/plan/projection/projection-value.spec.ts b/libs/server/features/src/plan/projection/projection-value.spec.ts new file mode 100644 index 00000000000..846f3644d59 --- /dev/null +++ b/libs/server/features/src/plan/projection/projection-value.spec.ts @@ -0,0 +1,15 @@ +import { Decimal } from 'decimal.js' +import { AssetValue } from './projection-value' + +const d = (n: Decimal.Value) => new Decimal(n) + +describe('asset value', () => { + it('never generates negative values', () => { + const value = new AssetValue(1, -2) + expect(value.initialValue).toEqual(d(1)) + + const next = value.next() + expect(next).toEqual(d(0)) + expect(value.next(next)).toEqual(d(0)) + }) +}) diff --git a/libs/server/features/src/plan/projection/projection-value.ts b/libs/server/features/src/plan/projection/projection-value.ts new file mode 100644 index 00000000000..67b63f0ef85 --- /dev/null +++ b/libs/server/features/src/plan/projection/projection-value.ts @@ -0,0 +1,41 @@ +import { StatsUtil } from '@maybe-finance/shared' +import Decimal from 'decimal.js' + +export type ProjectionValue = { + initialValue: Decimal + + /** + * @param previousValue the value to compute next from (defaults to `initialValue`) + */ + next(previousValue?: Decimal.Value): Decimal +} + +/** + * Models an asset's value. + */ +export class AssetValue implements ProjectionValue { + readonly initialValue: Decimal + readonly rate: Decimal + readonly stddev: Decimal + + /** + * @param initialValue initial asset value + * @param rate average rate of return + * @param stddev stddev used to simulate rate of return + */ + constructor(initialValue: Decimal.Value, rate: Decimal.Value = 0, stddev: Decimal.Value = 0) { + this.initialValue = new Decimal(initialValue) + this.rate = new Decimal(rate) + this.stddev = new Decimal(stddev) + } + + next(previousValue: Decimal.Value = this.initialValue): Decimal { + // avoid calculating `randomNormal` if stddev=0 for performance + const rate = this.stddev.isZero() + ? this.rate + : StatsUtil.randomNormal(this.rate, this.stddev) + + // need to clamp the minimum rate at -100% since an asset can never lose more than 100% of its value. + return rate.clamp(-1, Infinity).times(previousValue).plus(previousValue) + } +} diff --git a/libs/server/features/src/providers/finicity/finicity.etl.ts b/libs/server/features/src/providers/finicity/finicity.etl.ts new file mode 100644 index 00000000000..253368a748d --- /dev/null +++ b/libs/server/features/src/providers/finicity/finicity.etl.ts @@ -0,0 +1,662 @@ +import type { AccountConnection, PrismaClient } from '@prisma/client' +import type { Logger } from 'winston' +import { SharedUtil, AccountUtil, type SharedType } from '@maybe-finance/shared' +import type { FinicityApi, FinicityTypes } from '@maybe-finance/finicity-api' +import { DbUtil, FinicityUtil, type IETL } from '@maybe-finance/server/shared' +import { Prisma } from '@prisma/client' +import _ from 'lodash' +import { DateTime } from 'luxon' + +type FinicitySecurity = { + securityName: string | undefined + symbol: string | undefined + currentPrice: number | undefined + currentPriceDate: number | undefined + securityId: string + securityIdType: string + type: string | undefined + assetClass: string | undefined + fiAssetClass: string | undefined +} + +export type FinicityRawData = { + accounts: FinicityTypes.CustomerAccount[] + transactions: FinicityTypes.Transaction[] + transactionsDateRange: SharedType.DateRange +} + +export type FinicityData = { + accounts: FinicityTypes.CustomerAccount[] + positions: (FinicityTypes.CustomerAccountPosition & { + accountId: FinicityTypes.CustomerAccount['id'] + security: FinicitySecurity + })[] + transactions: FinicityTypes.Transaction[] + transactionsDateRange: SharedType.DateRange + investmentTransactions: (FinicityTypes.Transaction & { + security: Pick | null + })[] + investmentTransactionsDateRange: SharedType.DateRange +} + +type Connection = Pick< + AccountConnection, + 'id' | 'userId' | 'finicityInstitutionId' | 'finicityInstitutionLoginId' +> + +/** + * Determines if a Finicity Transaction should be treated as an investment_transaction + */ +function isInvestmentTransaction( + t: Pick< + FinicityTypes.Transaction, + 'securityId' | 'symbol' | 'ticker' | 'investmentTransactionType' + > +) { + return ( + t.securityId != null || + t.symbol != null || + t.ticker != null || + t.investmentTransactionType != null + ) +} + +/** + * Normalizes Finicity identifiers to handle cases where transactions/positions don't contain a valid + * securityId/securityIdType pair + */ +function getSecurityIdAndType( + txnOrPos: Pick< + FinicityTypes.Transaction | FinicityTypes.CustomerAccountPosition, + 'securityId' | 'securityIdType' | 'symbol' | 'ticker' + > +): { securityId: string; securityIdType: string } | null { + const securityId: string | null | undefined = + txnOrPos.securityId || txnOrPos.symbol || txnOrPos.ticker + + if (!securityId) return null + + const securityIdType = + txnOrPos.securityIdType || + (txnOrPos.securityId ? '__SECURITY_ID__' : txnOrPos.symbol ? '__SYMBOL__' : '__TICKER__') + + return { + securityId, + securityIdType, + } +} + +/** returns unique identifier for a given security (used for de-duping) */ +function getSecurityId(s: Pick): string { + return `${s.securityIdType}|${s.securityId}` +} + +export class FinicityETL implements IETL { + public constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly finicity: Pick< + FinicityApi, + 'getCustomerAccounts' | 'getAccountTransactions' + > + ) {} + + async extract(connection: Connection): Promise { + if (!connection.finicityInstitutionId || !connection.finicityInstitutionLoginId) { + throw new Error( + `connection ${connection.id} is missing finicityInstitutionId or finicityInstitutionLoginId` + ) + } + + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: connection.userId }, + select: { + id: true, + finicityCustomerId: true, + }, + }) + + if (!user.finicityCustomerId) { + throw new Error(`user ${user.id} is missing finicityCustomerId`) + } + + const transactionsDateRange = { + start: DateTime.now().minus(FinicityUtil.FINICITY_WINDOW_MAX), + end: DateTime.now(), + } + + const accounts = await this._extractAccounts( + user.finicityCustomerId, + connection.finicityInstitutionLoginId + ) + + const transactions = await this._extractTransactions( + user.finicityCustomerId, + accounts.map((a) => a.id), + transactionsDateRange + ) + + this.logger.info( + `Extracted Finicity data for customer ${user.finicityCustomerId} accounts=${accounts.length} transactions=${transactions.length}`, + { connection: connection.id, transactionsDateRange } + ) + + return { + accounts, + transactions, + transactionsDateRange, + } + } + + transform( + connection: Connection, + { accounts, transactions, transactionsDateRange }: FinicityRawData + ): Promise { + const positions = accounts.flatMap( + (a) => + a.position + ?.filter((p) => p.securityId != null || p.symbol != null) + .map((p) => ({ + ...p, + accountId: a.id, + marketValue: p.marketValue ? +p.marketValue || 0 : 0, + security: { + ...getSecurityIdAndType(p)!, + securityName: p.securityName ?? p.fundName, + symbol: p.symbol, + currentPrice: p.currentPrice, + currentPriceDate: p.currentPriceDate, + type: p.securityType, + assetClass: p.assetClass, + fiAssetClass: p.fiAssetClass, + }, + })) ?? [] + ) + + const [_investmentTransactions, _transactions] = _(transactions) + .uniqBy((t) => t.id) + .partition((t) => isInvestmentTransaction(t)) + .value() + + this.logger.info( + `Transformed Finicity transactions positions=${positions.length} transactions=${_transactions.length} investment_transactions=${_investmentTransactions.length}`, + { connection: connection.id } + ) + + return Promise.resolve({ + accounts, + positions, + transactions: _transactions, + transactionsDateRange, + investmentTransactions: _investmentTransactions.map((it) => { + const security = getSecurityIdAndType(it) + return { + ...it, + security: security + ? { + ...security, + symbol: it.symbol || it.ticker, + } + : null, + } + }), + investmentTransactionsDateRange: transactionsDateRange, + }) + } + + async load(connection: Connection, data: FinicityData): Promise { + await this.prisma.$transaction([ + ...this._loadAccounts(connection, data), + ...this._loadPositions(connection, data), + ...this._loadTransactions(connection, data), + ...this._loadInvestmentTransactions(connection, data), + ]) + + this.logger.info(`Loaded Finicity data for connection ${connection.id}`, { + connection: connection.id, + }) + } + + private async _extractAccounts(customerId: string, institutionLoginId: string) { + const { accounts } = await this.finicity.getCustomerAccounts({ customerId }) + + return accounts.filter( + (a) => a.institutionLoginId.toString() === institutionLoginId && a.currency === 'USD' + ) + } + + private _loadAccounts(connection: Connection, { accounts }: Pick) { + return [ + // upsert accounts + ...accounts.map((finicityAccount) => { + const type = FinicityUtil.getType(finicityAccount) + const classification = AccountUtil.getClassification(type) + + return this.prisma.account.upsert({ + where: { + accountConnectionId_finicityAccountId: { + accountConnectionId: connection.id, + finicityAccountId: finicityAccount.id, + }, + }, + create: { + accountConnectionId: connection.id, + finicityAccountId: finicityAccount.id, + type: FinicityUtil.getType(finicityAccount), + provider: 'finicity', + categoryProvider: FinicityUtil.getAccountCategory(finicityAccount), + subcategoryProvider: finicityAccount.type, + name: finicityAccount.name, + mask: finicityAccount.accountNumberDisplay, + finicityType: finicityAccount.type, + finicityDetail: finicityAccount.detail, + ...FinicityUtil.getAccountBalanceData(finicityAccount, classification), + }, + update: { + type: FinicityUtil.getType(finicityAccount), + categoryProvider: FinicityUtil.getAccountCategory(finicityAccount), + subcategoryProvider: finicityAccount.type, + finicityType: finicityAccount.type, + finicityDetail: finicityAccount.detail, + ..._.omit( + FinicityUtil.getAccountBalanceData(finicityAccount, classification), + ['currentBalanceStrategy', 'availableBalanceStrategy'] + ), + }, + }) + }), + // any accounts that are no longer in Finicity should be marked inactive + this.prisma.account.updateMany({ + where: { + accountConnectionId: connection.id, + AND: [ + { finicityAccountId: { not: null } }, + { finicityAccountId: { notIn: accounts.map((a) => a.id) } }, + ], + }, + data: { + isActive: false, + }, + }), + ] + } + + private _loadPositions(connection: Connection, { positions }: Pick) { + const securities = _(positions) + .map((p) => p.security) + .uniqBy((s) => getSecurityId(s)) + .value() + + const securitiesWithPrices = securities.filter((s) => s.currentPrice != null) + + return [ + ...(securities.length > 0 + ? [ + // upsert securities + this.prisma.$executeRaw` + INSERT INTO security (finicity_security_id, finicity_security_id_type, name, symbol, finicity_type, finicity_asset_class, finicity_fi_asset_class) + VALUES + ${Prisma.join( + securities.map( + ({ + securityId, + securityIdType, + securityName, + symbol, + type, + assetClass, + fiAssetClass, + }) => + Prisma.sql`( + ${securityId}, + ${securityIdType}, + ${securityName}, + ${symbol}, + ${type}, + ${assetClass}, + ${fiAssetClass} + )` + ) + )} + ON CONFLICT (finicity_security_id, finicity_security_id_type) DO UPDATE + SET + name = EXCLUDED.name, + symbol = EXCLUDED.symbol, + finicity_type = EXCLUDED.finicity_type, + finicity_asset_class = EXCLUDED.finicity_asset_class, + finicity_fi_asset_class = EXCLUDED.finicity_fi_asset_class + `, + ] + : []), + ...(securitiesWithPrices.length > 0 + ? [ + // upsert security prices + this.prisma.$executeRaw` + INSERT INTO security_pricing (security_id, date, price_close, source) + VALUES + ${Prisma.join( + securitiesWithPrices.map( + ({ + securityId, + securityIdType, + currentPrice, + currentPriceDate, + }) => + Prisma.sql`( + (SELECT id FROM security WHERE finicity_security_id = ${securityId} AND finicity_security_id_type = ${securityIdType}), + ${ + currentPriceDate + ? DateTime.fromSeconds(currentPriceDate, { + zone: 'utc', + }).toISODate() + : DateTime.now().toISODate() + }::date, + ${currentPrice}, + 'finicity' + )` + ) + )} + ON CONFLICT DO NOTHING + `, + ] + : []), + ...(positions.length > 0 + ? [ + // upsert holdings + this.prisma.$executeRaw` + INSERT INTO holding (finicity_position_id, account_id, security_id, value, quantity, cost_basis_provider, currency_code) + VALUES + ${Prisma.join( + // de-dupe positions in case Finicity returns duplicate account/security pairs (they do for test accounts) + _.uniqBy( + positions, + (p) => `${p.accountId}.${getSecurityId(p.security)}` + ).map( + ({ + id, + accountId, + security: { securityId, securityIdType }, + units, + quantity, + marketValue, + costBasis, + }) => + Prisma.sql`( + ${id}, + (SELECT id FROM account WHERE account_connection_id = ${ + connection.id + } AND finicity_account_id = ${accountId}), + (SELECT id FROM security WHERE finicity_security_id = ${securityId} AND finicity_security_id_type = ${securityIdType}), + ${marketValue || 0}, + ${units ?? quantity ?? 0}, + ${costBasis}, + ${'USD'} + )` + ) + )} + ON CONFLICT (finicity_position_id) DO UPDATE + SET + account_id = EXCLUDED.account_id, + security_id = EXCLUDED.security_id, + value = EXCLUDED.value, + quantity = EXCLUDED.quantity, + cost_basis_provider = EXCLUDED.cost_basis_provider, + currency_code = EXCLUDED.currency_code; + `, + ] + : []), + // Any holdings that are no longer in Finicity should be deleted + this.prisma.holding.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { finicityPositionId: { not: null } }, + { + finicityPositionId: { + notIn: positions + .map((p) => p.id?.toString()) + .filter((id): id is string => id != null), + }, + }, + ], + }, + }), + ] + } + + private async _extractTransactions( + customerId: string, + accountIds: string[], + dateRange: SharedType.DateRange + ) { + const accountTransactions = await Promise.all( + accountIds.map((accountId) => + SharedUtil.paginate({ + pageSize: 1000, // https://api-reference.finicity.com/#/rest/api-endpoints/transactions/get-customer-account-transactions + fetchData: async (offset, count) => { + const { transactions } = await SharedUtil.withRetry( + () => + this.finicity.getAccountTransactions({ + customerId, + accountId, + fromDate: dateRange.start.toUnixInteger(), + toDate: dateRange.end.toUnixInteger(), + start: offset + 1, // finicity uses 1-based indexing + limit: count, + }), + { + maxRetries: 3, + } + ) + + return transactions + }, + }) + ) + ) + + return accountTransactions.flat() + } + + private _loadTransactions( + connection: Connection, + { + transactions, + transactionsDateRange, + }: Pick + ) { + if (!transactions.length) return [] + + const txnUpsertQueries = _.chunk(transactions, 1_000).map((chunk) => { + return this.prisma.$executeRaw` + INSERT INTO transaction (account_id, finicity_transaction_id, date, name, amount, pending, currency_code, merchant_name, finicity_type, finicity_categorization) + VALUES + ${Prisma.join( + chunk.map((finicityTransaction) => { + const { + id, + accountId, + description, + memo, + amount, + status, + type, + categorization, + transactionDate, + postedDate, + currencySymbol, + } = finicityTransaction + + return Prisma.sql`( + (SELECT id FROM account WHERE account_connection_id = ${ + connection.id + } AND finicity_account_id = ${accountId.toString()}), + ${id}, + ${DateTime.fromSeconds(transactionDate ?? postedDate, { + zone: 'utc', + }).toISODate()}::date, + ${[description, memo].filter(Boolean).join(' ')}, + ${DbUtil.toDecimal(-amount)}, + ${status === 'pending'}, + ${currencySymbol || 'USD'}, + ${categorization?.normalizedPayeeName}, + ${type}, + ${categorization} + )` + }) + )} + ON CONFLICT (finicity_transaction_id) DO UPDATE + SET + name = EXCLUDED.name, + amount = EXCLUDED.amount, + pending = EXCLUDED.pending, + merchant_name = EXCLUDED.merchant_name, + finicity_type = EXCLUDED.finicity_type, + finicity_categorization = EXCLUDED.finicity_categorization; + ` + }) + + return [ + // upsert transactions + ...txnUpsertQueries, + // delete finicity-specific transactions that are no longer in finicity + this.prisma.transaction.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { finicityTransactionId: { not: null } }, + { finicityTransactionId: { notIn: transactions.map((t) => `${t.id}`) } }, + ], + date: { + gte: transactionsDateRange.start.startOf('day').toJSDate(), + lte: transactionsDateRange.end.endOf('day').toJSDate(), + }, + }, + }), + ] + } + + private _loadInvestmentTransactions( + connection: Connection, + { + investmentTransactions, + investmentTransactionsDateRange, + }: Pick + ) { + if (!investmentTransactions.length) return [] + + const securities = _(investmentTransactions) + .map((p) => p.security) + .filter(SharedUtil.nonNull) + .uniqBy((s) => getSecurityId(s)) + .value() + + return [ + // upsert securities + ...(securities.length > 0 + ? [ + this.prisma.$executeRaw` + INSERT INTO security (finicity_security_id, finicity_security_id_type, symbol) + VALUES + ${Prisma.join( + securities.map((s) => { + return Prisma.sql`( + ${s.securityId}, + ${s.securityIdType}, + ${s.symbol} + )` + }) + )} + ON CONFLICT DO NOTHING; + `, + ] + : []), + + // upsert investment transactions + ..._.chunk(investmentTransactions, 1_000).map((chunk) => { + return this.prisma.$executeRaw` + INSERT INTO investment_transaction (account_id, security_id, finicity_transaction_id, date, name, amount, fees, quantity, price, currency_code, finicity_investment_transaction_type) + VALUES + ${Prisma.join( + chunk.map((t) => { + const { + id, + accountId, + amount, + feeAmount, + description, + memo, + unitQuantity, + unitPrice, + transactionDate, + postedDate, + currencySymbol, + investmentTransactionType, + security, + } = t + + return Prisma.sql`( + (SELECT id FROM account WHERE account_connection_id = ${ + connection.id + } AND finicity_account_id = ${accountId.toString()}), + ${ + security + ? Prisma.sql`(SELECT id FROM security WHERE finicity_security_id = ${security.securityId} AND finicity_security_id_type = ${security.securityIdType})` + : null + }, + ${id}, + ${DateTime.fromSeconds(transactionDate ?? postedDate, { + zone: 'utc', + }).toISODate()}::date, + ${[description, memo].filter(Boolean).join(' ')}, + ${DbUtil.toDecimal(-amount)}, + ${DbUtil.toDecimal(feeAmount)}, + ${DbUtil.toDecimal(unitQuantity ?? 0)}, + ${DbUtil.toDecimal(unitPrice ?? 0)}, + ${currencySymbol || 'USD'}, + ${investmentTransactionType} + )` + }) + )} + ON CONFLICT (finicity_transaction_id) DO UPDATE + SET + account_id = EXCLUDED.account_id, + security_id = EXCLUDED.security_id, + date = EXCLUDED.date, + name = EXCLUDED.name, + amount = EXCLUDED.amount, + fees = EXCLUDED.fees, + quantity = EXCLUDED.quantity, + price = EXCLUDED.price, + currency_code = EXCLUDED.currency_code, + finicity_investment_transaction_type = EXCLUDED.finicity_investment_transaction_type; + ` + }), + + // delete finicity-specific investment transactions that are no longer in finicity + this.prisma.investmentTransaction.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { finicityTransactionId: { not: null } }, + { + finicityTransactionId: { + notIn: investmentTransactions.map((t) => `${t.id}`), + }, + }, + ], + date: { + gte: investmentTransactionsDateRange.start.startOf('day').toJSDate(), + lte: investmentTransactionsDateRange.end.endOf('day').toJSDate(), + }, + }, + }), + ] + } +} diff --git a/libs/server/features/src/providers/finicity/finicity.service.ts b/libs/server/features/src/providers/finicity/finicity.service.ts new file mode 100644 index 00000000000..ba0bdf1833c --- /dev/null +++ b/libs/server/features/src/providers/finicity/finicity.service.ts @@ -0,0 +1,281 @@ +import type { Logger } from 'winston' +import type { AccountConnection, PrismaClient, User } from '@prisma/client' +import type { IETL, SyncConnectionOptions } from '@maybe-finance/server/shared' +import type { FinicityApi, FinicityTypes } from '@maybe-finance/finicity-api' +import type { IInstitutionProvider } from '../../institution' +import type { + AccountConnectionSyncEvent, + IAccountConnectionProvider, +} from '../../account-connection' +import _ from 'lodash' +import axios from 'axios' +import { v4 as uuid } from 'uuid' +import { SharedUtil } from '@maybe-finance/shared' +import { etl } from '@maybe-finance/server/shared' + +export interface IFinicityConnect { + generateConnectUrl(userId: User['id'], institutionId: string): Promise<{ link: string }> + + generateFixConnectUrl( + userId: User['id'], + accountConnectionId: AccountConnection['id'] + ): Promise<{ link: string }> +} + +export class FinicityService + implements IFinicityConnect, IAccountConnectionProvider, IInstitutionProvider +{ + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly finicity: FinicityApi, + private readonly etl: IETL, + private readonly webhookUrl: string | Promise, + private readonly testMode: boolean + ) {} + + async generateConnectUrl(userId: User['id'], institutionId: string) { + const customerId = await this.getOrCreateCustomerId(userId) + + this.logger.debug( + `Generating Finicity connect URL with user=${userId} institution=${institutionId} customerId=${customerId}` + ) + + const res = await this.finicity.generateLiteConnectUrl({ + customerId, + institutionId, + webhook: await this.webhookUrl, + webhookContentType: 'application/json', + }) + + return res + } + + async generateFixConnectUrl(userId: User['id'], accountConnectionId: AccountConnection['id']) { + const accountConnection = await this.prisma.accountConnection.findUniqueOrThrow({ + where: { id: accountConnectionId }, + }) + + if (!accountConnection.finicityInstitutionLoginId) { + throw new Error( + `connection ${accountConnection.id} is missing finicityInstitutionLoginId` + ) + } + + const res = await this.finicity.generateFixConnectUrl({ + customerId: await this.getOrCreateCustomerId(userId), + institutionLoginId: accountConnection.finicityInstitutionLoginId, + webhook: await this.webhookUrl, + webhookContentType: 'application/json', + }) + + return res + } + + async sync(connection: AccountConnection, options?: SyncConnectionOptions): Promise { + if (options && options.type !== 'finicity') throw new Error('invalid sync options') + + if (options?.initialSync) { + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: connection.userId }, + }) + + if (!user.finicityCustomerId) { + throw new Error(`user ${user.id} missing finicityCustomerId`) + } + + // refresh customer accounts + try { + this.logger.info( + `refreshing customer accounts for customer: ${user.finicityCustomerId}` + ) + const { accounts } = await this.finicity.refreshCustomerAccounts({ + customerId: user.finicityCustomerId, + }) + + // no need to await this - this is fire-and-forget and shouldn't delay the sync process + this.logger.info( + `triggering load historic transactions for customer: ${ + user.finicityCustomerId + } accounts: ${accounts.map((a) => a.id)}` + ) + Promise.allSettled( + accounts + .filter( + (a) => + a.institutionLoginId.toString() === + connection.finicityInstitutionLoginId + ) + .map((account) => + this.finicity + .loadHistoricTransactions({ + accountId: account.id, + customerId: account.customerId, + }) + .catch((err) => { + this.logger.warn( + `error loading historic transactions for finicity account: ${account.id} customer: ${account.customerId}`, + err + ) + }) + ) + ) + } catch (err) { + // gracefully handle error, this shouldn't prevent the sync process from continuing + this.logger.error(`error refreshing customer accounts for initial sync`, err) + } + } + + await etl(this.etl, connection) + } + + async onSyncEvent(connection: AccountConnection, event: AccountConnectionSyncEvent) { + switch (event.type) { + case 'success': { + await this.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + status: 'OK', + }, + }) + break + } + case 'error': { + const { error } = event + + await this.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + status: 'ERROR', + finicityError: + axios.isAxiosError(error) && error.response + ? _.pick(error.response, ['status', 'data']) + : undefined, + }, + }) + break + } + } + } + + async delete(connection: AccountConnection): Promise { + if (connection.finicityInstitutionLoginId) { + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: connection.userId }, + select: { + finicityCustomerId: true, + accountConnections: { + where: { + id: { not: connection.id }, + finicityInstitutionLoginId: connection.finicityInstitutionLoginId, + }, + select: { id: true }, + }, + }, + }) + + // ensure there are no other connections with the same `finicityInstitutionLoginId` before deleting the accounts from Finicity + if (user.finicityCustomerId && !user.accountConnections.length) { + try { + await this.finicity.deleteCustomerAccountsByInstitutionLogin({ + customerId: user.finicityCustomerId, + institutionLoginId: +connection.finicityInstitutionLoginId, + }) + this.logger.info( + `deleted finicity customer ${user.finicityCustomerId} accounts for institutionLoginId ${connection.finicityInstitutionLoginId}` + ) + } catch (err) { + this.logger.error( + `error deleting finicity customer ${user.finicityCustomerId} accounts for institutionLoginId ${connection.finicityInstitutionLoginId}`, + err + ) + } + } else { + this.logger.warn( + `skipping delete for finicity customer ${user.finicityCustomerId} accounts for institutionLoginId ${connection.finicityInstitutionLoginId} (duplicate_connections: ${user.accountConnections.length})` + ) + } + } + } + + async getInstitutions() { + const finicityInstitutions = await SharedUtil.paginate({ + pageSize: 1000, + fetchData: (offset, count) => + SharedUtil.withRetry( + () => + this.finicity + .getInstitutions({ + start: offset / count + 1, + limit: count, + }) + .then(({ institutions, found, displaying }) => { + this.logger.debug( + `paginated finicity fetch inst=${displaying} (total=${found} offset=${offset} count=${count})` + ) + return institutions + }), + { + maxRetries: 3, + onError: (error, attempt) => { + this.logger.error( + `Finicity fetch institutions request failed attempt=${attempt} offset=${offset} count=${count}`, + { error } + ) + return ( + !axios.isAxiosError(error) || + (error.response && error.response.status >= 500) + ) + }, + } + ), + }) + + return _.uniqBy(finicityInstitutions, (i) => i.id).map((finicityInstitution) => ({ + providerId: `${finicityInstitution.id}`, + name: finicityInstitution.name || '', + url: finicityInstitution.urlHomeApp + ? SharedUtil.normalizeUrl(finicityInstitution.urlHomeApp) + : null, + logoUrl: finicityInstitution.branding?.icon, + primaryColor: finicityInstitution.branding?.primaryColor, + oauth: finicityInstitution.oauthEnabled, + data: finicityInstitution, + })) + } + + private async getOrCreateCustomerId( + userId: User['id'] + ): Promise { + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: userId }, + select: { id: true, finicityCustomerId: true }, + }) + + if (user.finicityCustomerId) { + return user.finicityCustomerId + } + + // See https://api-reference.finicity.com/#/rest/api-endpoints/customer/add-customer + const finicityUsername = uuid() + + const { id: finicityCustomerId } = this.testMode + ? await this.finicity.addTestingCustomer({ username: finicityUsername }) + : await this.finicity.addCustomer({ username: finicityUsername }) + + await this.prisma.user.update({ + where: { + id: userId, + }, + data: { + finicityUsername, + finicityCustomerId, + }, + }) + + this.logger.info( + `created finicity customer ${finicityCustomerId} for user ${userId} (testMode=${this.testMode})` + ) + return finicityCustomerId + } +} diff --git a/libs/server/features/src/providers/finicity/finicity.webhook.ts b/libs/server/features/src/providers/finicity/finicity.webhook.ts new file mode 100644 index 00000000000..833f971744c --- /dev/null +++ b/libs/server/features/src/providers/finicity/finicity.webhook.ts @@ -0,0 +1,144 @@ +import type { Logger } from 'winston' +import _ from 'lodash' +import type { PrismaClient } from '@prisma/client' +import type { FinicityApi, FinicityTypes } from '@maybe-finance/finicity-api' +import type { IAccountConnectionService } from '../../account-connection' + +export interface IFinicityWebhookHandler { + handleWebhook(data: FinicityTypes.WebhookData): Promise + handleTxPushEvent(event: FinicityTypes.TxPushEvent): Promise +} + +export class FinicityWebhookHandler implements IFinicityWebhookHandler { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly finicity: FinicityApi, + private readonly accountConnectionService: IAccountConnectionService, + private readonly txPushUrl: string | Promise + ) {} + + /** + * Process Finicity Connect webhooks. These handlers should execute as quick as possible and + * long-running operations should be performed in the background. + */ + async handleWebhook(data: FinicityTypes.WebhookData) { + switch (data.eventType) { + case 'added': { + const { accounts, institutionId } = data.payload + + const { customerId, institutionLoginId } = accounts[0] + + const [user, providerInstitution] = await Promise.all([ + this.prisma.user.findUniqueOrThrow({ + where: { + finicityCustomerId: customerId, + }, + }), + this.prisma.providerInstitution.findUnique({ + where: { + provider_providerId: { + provider: 'FINICITY', + providerId: institutionId, + }, + }, + include: { + institution: true, + }, + }), + ]) + + const connection = await this.prisma.accountConnection.create({ + data: { + userId: user.id, + name: + providerInstitution?.institution?.name || + providerInstitution?.name || + 'Institution', + type: 'finicity', + finicityInstitutionId: institutionId, + finicityInstitutionLoginId: String(institutionLoginId), + }, + }) + + await Promise.allSettled([ + // subscribe to TxPUSH + ...accounts.map(async (account) => + this.finicity.subscribeTxPush({ + accountId: account.id, + customerId: account.customerId, + callbackUrl: await this.txPushUrl, + }) + ), + ]) + + // sync + await this.accountConnectionService.sync(connection.id, { + type: 'finicity', + initialSync: true, + }) + + break + } + default: { + this.logger.warn('Unhandled Finicity webhook', { data }) + break + } + } + } + + async handleTxPushEvent(event: FinicityTypes.TxPushEvent) { + switch (event.class) { + case 'account': { + const connections = await this.prisma.accountConnection.findMany({ + where: { + accounts: { + some: { + finicityAccountId: { + in: _.uniq(event.records.map((a) => String(a.id))), + }, + }, + }, + }, + select: { + id: true, + }, + }) + + await Promise.allSettled( + connections.map((connection) => + this.accountConnectionService.sync(connection.id) + ) + ) + break + } + case 'transaction': { + const connections = await this.prisma.accountConnection.findMany({ + where: { + accounts: { + some: { + finicityAccountId: { + in: _.uniq(event.records.map((t) => String(t.accountId))), + }, + }, + }, + }, + select: { + id: true, + }, + }) + + await Promise.allSettled( + connections.map((connection) => + this.accountConnectionService.sync(connection.id) + ) + ) + break + } + default: { + this.logger.warn(`unhandled Finicity TxPush event`, { event }) + break + } + } + } +} diff --git a/libs/server/features/src/providers/finicity/index.ts b/libs/server/features/src/providers/finicity/index.ts new file mode 100644 index 00000000000..7a99288f768 --- /dev/null +++ b/libs/server/features/src/providers/finicity/index.ts @@ -0,0 +1,3 @@ +export * from './finicity.service' +export * from './finicity.etl' +export * from './finicity.webhook' diff --git a/libs/server/features/src/providers/index.ts b/libs/server/features/src/providers/index.ts new file mode 100644 index 00000000000..7167b51c374 --- /dev/null +++ b/libs/server/features/src/providers/index.ts @@ -0,0 +1,4 @@ +export * from './plaid' +export * from './finicity' +export * from './vehicle' +export * from './property' diff --git a/libs/server/features/src/providers/plaid/index.ts b/libs/server/features/src/providers/plaid/index.ts new file mode 100644 index 00000000000..9b810e27499 --- /dev/null +++ b/libs/server/features/src/providers/plaid/index.ts @@ -0,0 +1,3 @@ +export * from './plaid.service' +export * from './plaid.etl' +export * from './plaid.webhook' diff --git a/libs/server/features/src/providers/plaid/plaid.etl.spec.ts b/libs/server/features/src/providers/plaid/plaid.etl.spec.ts new file mode 100644 index 00000000000..fb1b1a9c41a --- /dev/null +++ b/libs/server/features/src/providers/plaid/plaid.etl.spec.ts @@ -0,0 +1,275 @@ +import { PlaidApi, PlaidErrorType } from 'plaid' +import { PrismaClient } from '@prisma/client' +import { createLogger, transports } from 'winston' +import { CryptoService } from '@maybe-finance/server/shared' +import { TestUtil } from '@maybe-finance/shared' +import { PlaidTestData } from '../../../../../../tools/test-data' +import { PlaidETL } from './plaid.etl' +jest.mock('@prisma/client') +jest.mock('plaid') + +const prisma = jest.mocked(new PrismaClient()) +const plaid = jest.mocked(new PlaidApi()) +const logger = createLogger({ transports: [new transports.Console()] }) +const crypto = jest.mocked(new CryptoService('TEST_SECRET')) +const marketData = jest.mocked({ + source: 'TEST_MOCK', + getDailyPricing: jest.fn(), + getLivePricing: jest.fn(), + getOptionDetails: jest.fn(), +}) + +describe('Plaid ETL', () => { + it.each` + errorType | errorCode | endpoints + ${PlaidErrorType.ItemError} | ${'PRODUCTS_NOT_SUPPORTED'} | ${['investmentsTransactionsGet', 'investmentsHoldingsGet', 'liabilitiesGet']} + ${PlaidErrorType.ItemError} | ${'PRODUCT_NOT_READY'} | ${['investmentsTransactionsGet', 'investmentsHoldingsGet', 'liabilitiesGet']} + ${PlaidErrorType.ItemError} | ${'NO_INVESTMENT_ACCOUNTS'} | ${['investmentsTransactionsGet', 'investmentsHoldingsGet']} + ${PlaidErrorType.ItemError} | ${'NO_INVESTMENT_AUTH_ACCOUNTS'} | ${['investmentsTransactionsGet', 'investmentsHoldingsGet']} + ${PlaidErrorType.ItemError} | ${'NO_LIABILITY_ACCOUNTS'} | ${['liabilitiesGet']} + `( + `extracts ignoring errors ($errorType | $errorCode | $endpoints)`, + async ({ errorType, errorCode, endpoints }) => { + const item = PlaidTestData.item + + plaid.accountsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.checkingAccount], + request_id: '1', + }) + ) + + if (endpoints.includes('transactionsGet')) { + plaid.transactionsGet.mockRejectedValue( + TestUtil.mockPlaidError({ + error_type: errorType, + error_code: errorCode, + error_message: '', + display_message: null, + }) + ) + } else { + plaid.transactionsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.checkingAccount], + transactions: PlaidTestData.checkingTransactions, + total_transactions: PlaidTestData.checkingTransactions.length, + request_id: '2', + }) + ) + } + + if (endpoints.includes('investmentsTransactionsGet')) { + plaid.investmentsTransactionsGet.mockRejectedValue( + TestUtil.mockPlaidError({ + error_type: errorType, + error_code: errorCode, + error_message: '', + display_message: null, + }) + ) + } else { + plaid.investmentsTransactionsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.brokerageAccount], + investment_transactions: PlaidTestData.investmentTransactions, + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + total_investment_transactions: PlaidTestData.investmentTransactions.length, + request_id: '3', + }) + ) + } + + if (endpoints.includes('investmentsHoldingsGet')) { + plaid.investmentsHoldingsGet.mockRejectedValue( + TestUtil.mockPlaidError({ + error_type: errorType, + error_code: errorCode, + error_message: '', + display_message: null, + }) + ) + } else { + plaid.investmentsHoldingsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.brokerageAccount], + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + request_id: '4', + }) + ) + } + + if (endpoints.includes('liabilitiesGet')) { + plaid.liabilitiesGet.mockRejectedValue( + TestUtil.mockPlaidError({ + error_type: errorType, + error_code: errorCode, + error_message: '', + display_message: null, + }) + ) + } else { + plaid.liabilitiesGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.creditAccount], + liabilities: { + credit: [PlaidTestData.creditCardLiability], + mortgage: null, + student: null, + }, + request_id: '5', + }) + ) + } + + const etl = new PlaidETL(logger, prisma, plaid, crypto, marketData) + + const data = await etl.extract({ + id: 123, + plaidAccessToken: crypto.encrypt('DUMMY_PLAID_ACCESS_TOKEN'), + }) + + expect(data.item).toMatchObject(item) + expect(data.accounts).toHaveLength(1) + expect(data.holdings).toHaveLength(endpoints.includes('investmentsHoldingsGet') ? 0 : 3) + expect(data.liabilities).toMatchObject({ + credit: endpoints.includes('liabilitiesGet') ? null : {}, + mortgage: null, + student: null, + }) + expect(data.transactions).toHaveLength(endpoints.includes('transactionsGet') ? 0 : 5) + expect(data.investmentTransactions).toHaveLength( + endpoints.includes('investmentsTransactionsGet') ? 0 : 3 + ) + } + ) + + it('extracts throwing unhandled errors', async () => { + const item = PlaidTestData.item + const error = TestUtil.mockPlaidError({ + error_type: PlaidErrorType.ItemError, + error_code: 'ITEM_LOGIN_REQUIRED', + error_message: '', + display_message: null, + }) + + plaid.accountsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [ + PlaidTestData.checkingAccount, + PlaidTestData.brokerageAccount, + PlaidTestData.creditAccount, + ], + request_id: '1', + }) + ) + + plaid.transactionsGet.mockRejectedValue(error) + + plaid.investmentsTransactionsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.brokerageAccount], + investment_transactions: PlaidTestData.investmentTransactions, + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + total_investment_transactions: PlaidTestData.investmentTransactions.length, + request_id: '3', + }) + ) + + plaid.investmentsHoldingsGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.brokerageAccount], + holdings: PlaidTestData.holdings, + securities: PlaidTestData.securities, + request_id: '4', + }) + ) + + plaid.liabilitiesGet.mockResolvedValue( + TestUtil.axiosSuccess({ + item, + accounts: [PlaidTestData.creditAccount], + liabilities: { + credit: [PlaidTestData.creditCardLiability], + mortgage: null, + student: null, + }, + request_id: '5', + }) + ) + + const etl = new PlaidETL(logger, prisma, plaid, crypto, marketData) + + expect( + etl.extract({ + id: 123, + plaidAccessToken: crypto.encrypt('DUMMY_PLAID_ACCESS_TOKEN'), + }) + ).rejects.toThrowError(error) + }) + + it.each` + symbol | type | expected + ${'AAPL'} | ${'equity'} | ${'AAPL'} + ${'AAPL240315P00115000'} | ${'equity'} | ${'AAPL'} + ${'AAPL240315P00115000'} | ${'derivative'} | ${'AAPL240315P00115000'} + `(`maps security $symbol ($type) -> $expected`, async ({ symbol, type, expected }) => { + const etl = new PlaidETL(logger, prisma, plaid, crypto, marketData) + + const data = await etl.transform( + { + id: 123, + plaidAccessToken: crypto.encrypt('DUMMY_PLAID_ACCESS_TOKEN'), + }, + { + item: { error: null, consent_expiration_time: null }, + accounts: [], + transactions: [], + transactionsDateRange: { start: '', end: '' }, + investmentTransactions: [], + investmentTransactionsDateRange: { start: '', end: '' }, + liabilities: { credit: null, mortgage: null, student: null }, + holdings: [ + { + account_id: 'a1', + account: null, + cost_basis: 168.15, + institution_price_as_of: '2022-12-08', + institution_value: 1894.65, + iso_currency_code: 'USD', + quantity: 13.443, + unofficial_currency_code: null, + security_id: 's1', + security: { + close_price: null, + close_price_as_of: null, + cusip: '037833100', + is_cash_equivalent: false, + isin: 'US0378331005', + iso_currency_code: 'USD', + name: 'Apple Inc', + security_id: 's1', + ticker_symbol: symbol, + type, + unofficial_currency_code: null, + }, + }, + ], + } + ) + + expect(data.holdings).toHaveLength(1) + expect(data.holdings[0].security!.ticker_symbol).toBe(expected) + }) +}) diff --git a/libs/server/features/src/providers/plaid/plaid.etl.ts b/libs/server/features/src/providers/plaid/plaid.etl.ts new file mode 100644 index 00000000000..ad16b62e27e --- /dev/null +++ b/libs/server/features/src/providers/plaid/plaid.etl.ts @@ -0,0 +1,954 @@ +import type { AccountConnection, PrismaClient } from '@prisma/client' +import type { Logger } from 'winston' +import type { IMarketDataService, IETL, ICryptoService } from '@maybe-finance/server/shared' +import { DbUtil } from '@maybe-finance/server/shared' +import type { SharedType } from '@maybe-finance/shared' +import { MarketUtil } from '@maybe-finance/shared' +import type { + AccountBase as PlaidAccount, + Transaction as PlaidTransaction, + InvestmentTransaction as PlaidInvestmentTransaction, + Security as PlaidSecurity, + Holding as PlaidHolding, + Item as PlaidItem, + LiabilitiesObject as PlaidLiabilities, + PlaidApi, +} from 'plaid' +import { Prisma } from '@prisma/client' +import { DateTime } from 'luxon' +import _, { chunk } from 'lodash' +import { ErrorUtil, PlaidUtil } from '@maybe-finance/server/shared' +import { SharedUtil } from '@maybe-finance/shared' + +export type PlaidRawData = { + item: Pick + accounts: Pick[] + transactions: Pick< + PlaidTransaction, + | 'transaction_id' + | 'account_id' + | 'name' + | 'amount' + | 'pending' + | 'date' + | 'merchant_name' + | 'category' + | 'category_id' + | 'personal_finance_category' + | 'iso_currency_code' + | 'unofficial_currency_code' + >[] + transactionsDateRange: SharedType.DateRange + investmentTransactions: (Pick< + PlaidInvestmentTransaction, + | 'investment_transaction_id' + | 'account_id' + | 'security_id' + | 'name' + | 'amount' + | 'fees' + | 'quantity' + | 'date' + | 'price' + | 'type' + | 'subtype' + | 'iso_currency_code' + | 'unofficial_currency_code' + > & { + security: Pick< + PlaidSecurity, + | 'security_id' + | 'name' + | 'ticker_symbol' + | 'cusip' + | 'isin' + | 'type' + | 'iso_currency_code' + | 'unofficial_currency_code' + | 'is_cash_equivalent' + > | null + account: PlaidRawData['accounts'][0] | null + })[] + investmentTransactionsDateRange: SharedType.DateRange + holdings: (Pick< + PlaidHolding, + | 'account_id' + | 'security_id' + | 'quantity' + | 'cost_basis' + | 'institution_value' + | 'institution_price_as_of' + | 'iso_currency_code' + | 'unofficial_currency_code' + > & { + security: Pick< + PlaidSecurity, + | 'security_id' + | 'name' + | 'ticker_symbol' + | 'cusip' + | 'isin' + | 'type' + | 'close_price' + | 'close_price_as_of' + | 'iso_currency_code' + | 'unofficial_currency_code' + | 'is_cash_equivalent' + > | null + account: PlaidRawData['accounts'][0] | null + })[] + liabilities: PlaidLiabilities +} + +type PlaidData = PlaidRawData + +const isPlaidErrorIgnorable = (err: any) => + ErrorUtil.isPlaidError(err) && + err.response.data.error_type === 'ITEM_ERROR' && + [ + 'PRODUCTS_NOT_SUPPORTED', + 'PRODUCT_NOT_READY', + 'NO_ACCOUNTS', + 'NO_AUTH_ACCOUNTS', + 'NO_INVESTMENT_ACCOUNTS', + 'NO_INVESTMENT_AUTH_ACCOUNTS', + 'NO_LIABILITY_ACCOUNTS', + ].includes(err.response.data.error_code) + +type Connection = Pick + +function getSecurityTickerData({ + type, + ticker_symbol, + cusip, + isin, +}: Pick): Pick< + PlaidSecurity, + 'ticker_symbol' | 'cusip' | 'isin' +> { + if (ticker_symbol != null && type != null && ['equity', 'etf'].includes(type)) { + const underlyingTicker = MarketUtil.getUnderlyingTicker(ticker_symbol) + if (underlyingTicker) { + return { ticker_symbol: underlyingTicker, cusip: null, isin: null } + } + } + + return { ticker_symbol, cusip, isin } +} + +export class PlaidETL implements IETL { + public constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly plaid: Pick< + PlaidApi, + | 'accountsGet' + | 'transactionsGet' + | 'investmentsTransactionsGet' + | 'investmentsHoldingsGet' + | 'liabilitiesGet' + >, + private readonly crypto: ICryptoService, + private readonly marketData: Pick + ) {} + + async extract(connection: Connection): Promise { + if (!connection.plaidAccessToken) { + throw new Error(`no plaid access token for connection ${connection.id}`) + } + + const accessToken = this.crypto.decrypt(connection.plaidAccessToken) + + const transactionsDateRange = { + start: DateTime.now().minus(PlaidUtil.PLAID_WINDOW_MAX).toISODate(), + end: DateTime.now().toISODate(), + } + + const investmentTransactionsDateRange = { + start: DateTime.now().minus(PlaidUtil.PLAID_WINDOW_MAX).toISODate(), + end: DateTime.now().toISODate(), + } + + const [{ item, accounts }, transactions, investmentTransactions, holdings, liabilities] = + await Promise.all([ + this._extractAccounts(accessToken), + this._extractTransactions(accessToken, transactionsDateRange), + this._extractInvestmentTransactions(accessToken, investmentTransactionsDateRange), + this._extractHoldings(accessToken), + this._extractLiabilities(accessToken), + ]) + + this.logger.info( + `Extracted Plaid data for item ${item.item_id} accounts=${ + accounts.length + } transactions=${transactions.length} investmentTransactions=${ + investmentTransactions.length + } holdings=${holdings.length} liabilities=${ + (liabilities.credit?.length ?? 0) + + (liabilities.mortgage?.length ?? 0) + + (liabilities.student?.length ?? 0) + }`, + { connection: connection.id, transactionsDateRange, investmentTransactionsDateRange } + ) + + return { + item, + accounts: accounts.filter((a) => a.balances.iso_currency_code === 'USD'), + transactions, + transactionsDateRange, + investmentTransactions, + investmentTransactionsDateRange, + holdings, + liabilities, + } + } + + async transform(_connection: Connection, data: PlaidData): Promise { + return { + ...data, + investmentTransactions: data.investmentTransactions.map((it) => ({ + ...it, + security: it.security + ? { + ...it.security, + ...getSecurityTickerData(it.security), + } + : null, + })), + holdings: data.holdings.map((h) => ({ + ...h, + security: h.security + ? { + ...h.security, + ...getSecurityTickerData(h.security), + } + : null, + })), + } + } + + async load(connection: Connection, data: PlaidData): Promise { + await this.prisma.$transaction([ + ...this._loadAccounts(connection, data), + ...this._loadTransactions(connection, data), + ...this._loadInvestmentTransactions(connection, data), + ...(await this._loadHoldings(connection, data)), + ...this._loadLiabilities(connection, data), + // update connection status + this.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + plaidConsentExpiration: data.item.consent_expiration_time, + ...(data.item.error && { + status: 'ERROR', + plaidError: data.item.error as any, + }), + }, + }), + ]) + + this.logger.info(`Loaded plaid data for connection ${connection.id}`, { + connection: connection.id, + }) + } + + private async _extractAccounts(accessToken: string) { + const { data } = await this.plaid.accountsGet({ + access_token: accessToken, + }) + + return { item: data.item, accounts: data.accounts } + } + + private _loadAccounts(connection: Connection, { accounts }: Pick) { + return [ + // upsert accounts + ...accounts.map((plaidAccount) => { + return this.prisma.account.upsert({ + where: { + accountConnectionId_plaidAccountId: { + accountConnectionId: connection.id, + plaidAccountId: plaidAccount.account_id, + }, + }, + create: { + type: PlaidUtil.getType(plaidAccount.type), + provider: 'plaid', + categoryProvider: PlaidUtil.plaidTypesToCategory(plaidAccount.type), + subcategoryProvider: plaidAccount.subtype ?? 'other', + accountConnectionId: connection.id, + plaidAccountId: plaidAccount.account_id, + name: plaidAccount.name, + plaidType: plaidAccount.type, + plaidSubtype: plaidAccount.subtype, + mask: plaidAccount.mask, + ...PlaidUtil.getAccountBalanceData( + plaidAccount.balances, + plaidAccount.type + ), + }, + update: { + type: PlaidUtil.getType(plaidAccount.type), + categoryProvider: PlaidUtil.plaidTypesToCategory(plaidAccount.type), + subcategoryProvider: plaidAccount.subtype ?? 'other', + plaidType: plaidAccount.type, + plaidSubtype: plaidAccount.subtype, + ..._.omit( + PlaidUtil.getAccountBalanceData( + plaidAccount.balances, + plaidAccount.type + ), + ['currentBalanceStrategy', 'availableBalanceStrategy'] + ), + }, + }) + }), + // any accounts that are no longer in Plaid should be marked inactive + this.prisma.account.updateMany({ + where: { + accountConnectionId: connection.id, + AND: [ + { plaidAccountId: { not: null } }, + { plaidAccountId: { notIn: accounts.map((a) => a.account_id) } }, + ], + }, + data: { + isActive: false, + }, + }), + ] + } + + private _extractTransactions(accessToken: string, dateRange: SharedType.DateRange) { + return SharedUtil.paginate({ + pageSize: 500, // https://plaid.com/docs/api/products/transactions/#transactions-get-request-options-count + fetchData: async (offset, count) => { + try { + const { data } = await SharedUtil.withRetry( + () => + this.plaid.transactionsGet({ + access_token: accessToken, + start_date: dateRange.start, + end_date: dateRange.end, + options: { + offset, + count, + include_personal_finance_category: true, + }, + }), + { + maxRetries: 3, + onError: (err) => + !ErrorUtil.isPlaidError(err) || err.response.status >= 500, + } + ) + + return data.transactions + } catch (err) { + if (isPlaidErrorIgnorable(err)) { + return [] + } + + throw err + } + }, + }) + } + + private _loadTransactions( + connection: Connection, + { + transactions, + transactionsDateRange, + }: Pick + ) { + if (!transactions.length) return [] + + const txnUpsertQueries = chunk(transactions, 1_000).map((chunk) => { + return this.prisma.$executeRaw` + INSERT INTO transaction (account_id, plaid_transaction_id, date, name, amount, pending, currency_code, merchant_name, plaid_category, plaid_category_id, plaid_personal_finance_category) + VALUES + ${Prisma.join( + chunk.map((plaidTransaction) => { + const { + account_id, + transaction_id, + name, + amount, + pending, + date, + merchant_name, + category, + category_id, + personal_finance_category, + iso_currency_code, + unofficial_currency_code, + } = plaidTransaction + + const currencyCode = + iso_currency_code || unofficial_currency_code || 'USD' + + return Prisma.sql`( + (SELECT id FROM account WHERE account_connection_id = ${ + connection.id + } AND plaid_account_id = ${account_id}), + ${transaction_id}, + ${date}::date, + ${name}, + ${DbUtil.toDecimal(amount)}, + ${pending}, + ${currencyCode}, + ${merchant_name}, + ${category ?? []}, + ${category_id}, + ${personal_finance_category} + )` + }) + )} + ON CONFLICT (plaid_transaction_id) DO UPDATE + SET + name = EXCLUDED.name, + amount = EXCLUDED.amount, + pending = EXCLUDED.pending, + merchant_name = EXCLUDED.merchant_name, + plaid_category = EXCLUDED.plaid_category, + plaid_category_id = EXCLUDED.plaid_category_id, + plaid_personal_finance_category = EXCLUDED.plaid_personal_finance_category; + ` + }) + + return [ + ...txnUpsertQueries, + // delete plaid-specific transactions that are no longer in plaid + this.prisma.transaction.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { plaidTransactionId: { not: null } }, + { + plaidTransactionId: { + notIn: transactions.map((t) => t.transaction_id), + }, + }, + ], + date: { + gte: DateTime.fromISO(transactionsDateRange.start) + .startOf('day') + .toJSDate(), + lte: DateTime.fromISO(transactionsDateRange.end).endOf('day').toJSDate(), + }, + }, + }), + ] + } + + private _extractInvestmentTransactions(accessToken: string, dateRange: SharedType.DateRange) { + return SharedUtil.paginate({ + pageSize: 500, // https://plaid.com/docs/api/products/investments/#investments-transactions-get-request-options-count + fetchData: async (offset, count) => { + try { + const { data } = await SharedUtil.withRetry( + () => + this.plaid.investmentsTransactionsGet({ + access_token: accessToken, + start_date: dateRange.start, + end_date: dateRange.end, + options: { + offset, + count, + }, + }), + { + maxRetries: 3, + onError: (err) => + !ErrorUtil.isPlaidError(err) || err.response.status >= 500, + } + ) + + return data.investment_transactions.map((it) => ({ + ...it, + security: + data.securities.find((s) => s.security_id === it.security_id) ?? null, + account: data.accounts.find((a) => a.account_id === it.account_id)!, + })) + } catch (err) { + if (isPlaidErrorIgnorable(err)) { + return [] + } + + throw err + } + }, + }) + } + + private _loadInvestmentTransactions( + connection: Connection, + { + investmentTransactions, + investmentTransactionsDateRange, + }: Pick + ) { + if (!investmentTransactions.length) return [] + + const securities = _(investmentTransactions) + .map((it) => it.security) + .filter(SharedUtil.nonNull) + .uniqBy((s) => s.security_id) + .value() + const accounts = investmentTransactions.map((it) => it.account!) + + return [ + ...(securities.length > 0 + ? [ + // upsert securities + this.prisma.$executeRaw` + INSERT INTO security (plaid_security_id, name, symbol, cusip, isin, currency_code, plaid_type, plaid_is_cash_equivalent) + VALUES + ${Prisma.join( + securities.map( + ({ + security_id, + name, + ticker_symbol, + cusip, + isin, + iso_currency_code, + unofficial_currency_code, + type, + is_cash_equivalent, + }) => + Prisma.sql`( + ${security_id}, + ${name}, + ${ticker_symbol}, + ${cusip}, + ${isin}, + ${iso_currency_code || unofficial_currency_code || 'USD'}, + ${type}, + ${is_cash_equivalent} + )` + ) + )} + ON CONFLICT (plaid_security_id) DO UPDATE + SET + name = EXCLUDED.name, + symbol = EXCLUDED.symbol, + cusip = EXCLUDED.cusip, + isin = EXCLUDED.isin, + currency_code = EXCLUDED.currency_code, + plaid_type = EXCLUDED.plaid_type; + `, + ] + : []), + + // Insert inv transactions + ...chunk(investmentTransactions, 1_000).map( + (chunk) => + this.prisma.$executeRaw` + INSERT INTO investment_transaction (account_id, security_id, plaid_investment_transaction_id, date, name, amount, fees, quantity, price, currency_code, plaid_type, plaid_subtype) + VALUES + ${Prisma.join( + chunk.map( + ({ + account_id, + security_id, + investment_transaction_id, + name, + amount, + fees, + quantity, + date, + price, + iso_currency_code, + unofficial_currency_code, + type, + subtype, + }) => { + const currencyCode = + iso_currency_code || unofficial_currency_code || 'USD' + + return Prisma.sql`( + (SELECT id FROM account WHERE account_connection_id = ${ + connection.id + } AND plaid_account_id = ${account_id}), + (SELECT id FROM security WHERE plaid_security_id = ${security_id}), + ${investment_transaction_id}, + ${date}::date, + ${name}, + ${DbUtil.toDecimal(amount)}, + ${DbUtil.toDecimal(fees)}, + ${DbUtil.toDecimal(quantity)}, + ${DbUtil.toDecimal(price)}, + ${currencyCode}, + ${type}, + ${subtype} + )` + } + ) + )} + ON CONFLICT (plaid_investment_transaction_id) DO UPDATE + SET + account_id = EXCLUDED.account_id, + security_id = EXCLUDED.security_id, + date = EXCLUDED.date, + name = EXCLUDED.name, + amount = EXCLUDED.amount, + fees = EXCLUDED.fees, + quantity = EXCLUDED.quantity, + price = EXCLUDED.price, + currency_code = EXCLUDED.currency_code, + plaid_type = EXCLUDED.plaid_type, + plaid_subtype = EXCLUDED.plaid_subtype; + ` + ), + + // delete plaid-specific investment transactions that are no longer in plaid + this.prisma.investmentTransaction.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { + plaidInvestmentTransactionId: { not: null }, + }, + { + plaidInvestmentTransactionId: { + notIn: investmentTransactions.map( + (it) => it.investment_transaction_id + ), + }, + }, + ], + date: { + gte: DateTime.fromISO(investmentTransactionsDateRange.start) + .startOf('day') + .toJSDate(), + lte: DateTime.fromISO(investmentTransactionsDateRange.end) + .endOf('day') + .toJSDate(), + }, + }, + }), + // update account current/available balances + ...(accounts.length > 0 + ? [ + this.prisma.$executeRaw` + UPDATE account AS a + SET + current_balance_provider = u.current_balance_provider, + available_balance_provider = u.available_balance_provider, + currency_code = u.currency_code + FROM ( + VALUES + ${Prisma.join( + accounts.map(({ account_id, balances, type }) => { + const { + currentBalanceProvider, + availableBalanceProvider, + currencyCode, + } = PlaidUtil.getAccountBalanceData(balances, type) + + return Prisma.sql`( + (SELECT id FROM account WHERE account_connection_id = ${connection.id} AND plaid_account_id = ${account_id}), + ${currentBalanceProvider}::numeric, + ${availableBalanceProvider}::numeric, + ${currencyCode} + )` + }) + )} + ) AS u(id, current_balance_provider, available_balance_provider, currency_code) + WHERE + a.id = u.id; + `, + ] + : []), + ] + } + + private async _extractHoldings(accessToken: string) { + try { + const { data } = await this.plaid.investmentsHoldingsGet({ access_token: accessToken }) + + return data.holdings.map((h) => ({ + ...h, + security: data.securities.find((s) => s.security_id === h.security_id)!, + account: data.accounts.find((a) => a.account_id === h.account_id)!, + })) + } catch (err) { + if (isPlaidErrorIgnorable(err)) { + return [] + } + + throw err + } + } + + private async _loadHoldings(connection: Connection, { holdings }: Pick) { + const securities = _(holdings) + .filter((h) => !!h.security) + .map((h) => h.security!) + .uniqBy((s) => s.security_id) + .value() + + const securitiesWithPrices = securities.filter((s) => s.close_price != null) + + // Fill security prices from holdings when Plaid doesn't provide them explicitly + const holdingsWithSecurityPrices = holdings.filter( + (h) => + !securitiesWithPrices.some((s) => s.security_id === h.security_id) && h.quantity > 0 + ) + + // Gather options contract details from market data to determine shares per contract + const optionsDetails = await Promise.all( + securities + .filter( + (security) => security.ticker_symbol != null && security.type === 'derivative' + ) + .map(async ({ ticker_symbol: symbol }) => ({ + symbol, + details: await this.marketData.getOptionDetails(symbol).catch(() => null), + })) + ) + + const holdingsWithDerivedIds = PlaidUtil.getHoldingsWithDerivedIds(holdings) + + return [ + ...(securities.length > 0 + ? [ + // upsert securities + this.prisma.$executeRaw` + INSERT INTO security (plaid_security_id, name, symbol, cusip, isin, shares_per_contract, currency_code, plaid_type, plaid_is_cash_equivalent) + VALUES + ${Prisma.join( + securities.map( + ({ + security_id, + name, + ticker_symbol, + cusip, + isin, + iso_currency_code, + unofficial_currency_code, + type, + is_cash_equivalent, + }) => + Prisma.sql`( + ${security_id}, + ${name}, + ${ticker_symbol}, + ${cusip}, + ${isin}, + ${ + type === 'derivative' + ? optionsDetails.find( + (od) => od.symbol === ticker_symbol + )?.details?.sharesPerContract ?? null + : null + }, + ${iso_currency_code || unofficial_currency_code || 'USD'}, + ${type}, + ${is_cash_equivalent} + )` + ) + )} + ON CONFLICT (plaid_security_id) DO UPDATE + SET + name = EXCLUDED.name, + symbol = EXCLUDED.symbol, + cusip = COALESCE(EXCLUDED.cusip, security.cusip), + isin = COALESCE(EXCLUDED.isin, security.isin), + shares_per_contract = COALESCE(EXCLUDED.shares_per_contract, security.shares_per_contract), + currency_code = EXCLUDED.currency_code, + plaid_type = EXCLUDED.plaid_type, + plaid_is_cash_equivalent = EXCLUDED.plaid_is_cash_equivalent + `, + ] + : []), + ...(securitiesWithPrices.length + ? [ + this.prisma.$executeRaw` + INSERT INTO security_pricing (security_id, date, price_close, source) + VALUES + ${Prisma.join( + securitiesWithPrices.map( + ({ security_id, close_price, close_price_as_of }) => + Prisma.sql`( + (SELECT id FROM security WHERE plaid_security_id = ${security_id}), + ${close_price_as_of ?? DateTime.now().toISODate()}::date, + ${close_price}, + 'plaid' + )` + ) + )} + ON CONFLICT DO NOTHING + `, + ] + : []), + ...(holdingsWithSecurityPrices.length + ? [ + this.prisma.$executeRaw` + INSERT INTO security_pricing (security_id, date, price_close, source) + VALUES + ${Prisma.join( + holdingsWithSecurityPrices.map( + ({ + security_id, + institution_value, + quantity, + institution_price_as_of, + }) => + Prisma.sql`( + (SELECT id FROM security WHERE plaid_security_id = ${security_id}), + ${ + institution_price_as_of ?? DateTime.now().toISODate() + }::date, + ${institution_value} / ${quantity}::numeric / COALESCE((SELECT shares_per_contract FROM security WHERE plaid_security_id = ${security_id}), 1), + 'plaid' + )` + ) + )} + ON CONFLICT DO NOTHING + `, + ] + : []), + ...(holdings.length > 0 + ? [ + // upsert holdings + this.prisma.$executeRaw` + INSERT INTO holding (plaid_holding_id, account_id, security_id, value, quantity, cost_basis_provider, currency_code) + VALUES + ${Prisma.join( + holdingsWithDerivedIds.map( + ({ + account_id, + security_id, + institution_value, + quantity, + cost_basis, + iso_currency_code, + unofficial_currency_code, + derivedId, + }) => { + const currencyCode = + iso_currency_code || unofficial_currency_code || 'USD' + + return Prisma.sql`( + ${derivedId}, + (SELECT id FROM account WHERE account_connection_id = ${connection.id} AND plaid_account_id = ${account_id}), + (SELECT id FROM security WHERE plaid_security_id = ${security_id}), + ${institution_value}, + ${quantity}, + ${cost_basis}, + ${currencyCode} + )` + } + ) + )} + ON CONFLICT (plaid_holding_id) DO UPDATE + SET + value = EXCLUDED.value, + quantity = EXCLUDED.quantity, + cost_basis_provider = EXCLUDED.cost_basis_provider, + currency_code = EXCLUDED.currency_code; + `, + ] + : []), + // any holdings that are no longer in Plaid should be deleted + this.prisma.holding.deleteMany({ + where: { + account: { + accountConnectionId: connection.id, + }, + AND: [ + { plaidHoldingId: { not: null } }, + { + plaidHoldingId: { + notIn: holdingsWithDerivedIds.map((h) => h.derivedId), + }, + }, + ], + }, + }), + ] + } + + private async _extractLiabilities(accessToken: string) { + try { + const { data } = await this.plaid.liabilitiesGet({ access_token: accessToken }) + + return data.liabilities + } catch (err) { + if (isPlaidErrorIgnorable(err)) { + return { credit: null, mortgage: null, student: null } as PlaidLiabilities + } + + throw err + } + } + + private _loadLiabilities( + connection: Connection, + { liabilities }: Pick + ) { + return [ + ...(liabilities.credit ?? []) + .filter((ccl) => !!ccl.account_id) + .map((ccl) => + this.prisma.account.update({ + where: { + accountConnectionId_plaidAccountId: { + accountConnectionId: connection.id, + plaidAccountId: ccl.account_id!, + }, + }, + data: { + plaidLiability: { credit: ccl }, + creditProvider: PlaidUtil.normalizeCreditData(ccl), + }, + }) + ), + ...(liabilities.mortgage ?? []).map((ml) => { + const loan = PlaidUtil.normalizeMortgageData(ml) + return this.prisma.account.update({ + where: { + accountConnectionId_plaidAccountId: { + accountConnectionId: connection.id, + plaidAccountId: ml.account_id, + }, + }, + data: { + startDate: loan.originationDate + ? DateTime.fromISO(loan.originationDate, { zone: 'utc' }).toJSDate() + : undefined, + plaidLiability: { mortgage: ml }, + loanProvider: loan, + }, + }) + }), + ...(liabilities.student ?? []) + .filter((sl) => !!sl.account_id) + .map((sl) => { + const loan = PlaidUtil.normalizeStudentLoanData(sl) + return this.prisma.account.update({ + where: { + accountConnectionId_plaidAccountId: { + accountConnectionId: connection.id, + plaidAccountId: sl.account_id!, + }, + }, + data: { + startDate: loan.originationDate + ? DateTime.fromISO(loan.originationDate, { zone: 'utc' }).toJSDate() + : undefined, + plaidLiability: { student: sl }, + loanProvider: loan, + }, + }) + }), + ] + } +} diff --git a/libs/server/features/src/providers/plaid/plaid.service.ts b/libs/server/features/src/providers/plaid/plaid.service.ts new file mode 100644 index 00000000000..91d14a0dd99 --- /dev/null +++ b/libs/server/features/src/providers/plaid/plaid.service.ts @@ -0,0 +1,330 @@ +import type { AccountConnection, PrismaClient, User } from '@prisma/client' +import type { Logger } from 'winston' +import type { LinkTokenCreateRequest, PlaidApi, Institution } from 'plaid' +import type { SharedType } from '@maybe-finance/shared' +import type { SyncConnectionOptions, CryptoService, IETL } from '@maybe-finance/server/shared' +import type { IInstitutionProvider } from '../../institution' +import type { + AccountConnectionSyncEvent, + IAccountConnectionProvider, +} from '../../account-connection' + +import _ from 'lodash' +import { CountryCode, Products } from 'plaid' +import { SharedUtil } from '@maybe-finance/shared' +import { ErrorUtil, etl } from '@maybe-finance/server/shared' + +export interface IPlaidConnect { + createLinkToken(userId: User['id'], options?: Partial): Promise + + createLinkTokenForUpdateMode( + userId: User['id'], + accountConnectionId: AccountConnection['id'], + mode: SharedType.PlaidLinkUpdateMode + ): Promise + + exchangePublicToken( + userId: User['id'], + token: string, + institution: Pick + ): Promise +} + +export class PlaidService + implements IPlaidConnect, IAccountConnectionProvider, IInstitutionProvider +{ + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly plaid: PlaidApi, + private readonly etl: IETL, + private readonly crypto: CryptoService, + private readonly webhookUrl: string | Promise, + private readonly clientUrl: string + ) {} + + async sync(connection: AccountConnection, options?: SyncConnectionOptions) { + if (options && options.type !== 'plaid') throw new Error('invalid sync options') + + await etl(this.etl, connection) + } + + async onSyncEvent(connection: AccountConnection, event: AccountConnectionSyncEvent) { + switch (event.type) { + case 'success': { + await this.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + status: 'OK', + }, + }) + break + } + case 'error': { + const { error } = event + + await this.prisma.accountConnection.update({ + where: { id: connection.id }, + data: { + status: 'ERROR', + plaidError: ErrorUtil.isPlaidError(error) + ? (error.response.data as any) + : undefined, + }, + }) + break + } + } + } + + async delete(connection: AccountConnection) { + // purge plaid data + if (connection.plaidAccessToken) { + const res = await this.plaid.itemRemove({ + access_token: this.crypto.decrypt(connection.plaidAccessToken), + }) + + this.logger.info( + `Item ${connection.plaidItemId} removed with request ID ${res.data.request_id}` + ) + } + } + + async getInstitutions() { + // Retrieve paginated institutions from Plaid - https://plaid.com/docs/api/institutions/#institutionsget + const plaidInstitutions = await SharedUtil.paginate({ + pageSize: 500, + delay: + process.env.NODE_ENV !== 'production' + ? { + onDelay: (message: string) => this.logger.debug(message), + milliseconds: 7_000, // Sandbox rate limited at 10 calls / minute + } + : undefined, + fetchData: (offset, count) => + SharedUtil.withRetry( + () => + this.plaid + .institutionsGet({ + country_codes: [CountryCode.Us], + count, + offset, + options: { + include_optional_metadata: true, + }, + }) + .then(({ data }) => { + this.logger.debug( + `paginated plaid fetch inst=${data.institutions.length} (total=${data.total} offset=${offset} count=${count})`, + { request_id: data.request_id } + ) + return data.institutions + }), + { + maxRetries: 3, + onError: (error, attempt) => { + this.logger.error( + `Plaid fetch institutions request failed attempt=${attempt} offset=${offset} count=${count}`, + { error: ErrorUtil.parseError(error) } + ) + + return !ErrorUtil.isPlaidError(error) || error.response.status >= 500 + }, + } + ), + }) + + return _.uniqBy(plaidInstitutions, (i) => i.institution_id).map((plaidInstitution) => { + const { institution_id, name, url, logo, primary_color } = plaidInstitution + return { + providerId: institution_id, + name, + url: url ? SharedUtil.normalizeUrl(url) : null, + logo, + primaryColor: primary_color, + oauth: plaidInstitution.oauth, + data: plaidInstitution, + } + }) + } + + /** + * Returns existing link token for OAuth re-initialization + */ + async getLinkToken(userId: User['id']) { + const user = await this.prisma.user.findFirstOrThrow({ + where: { id: userId }, + select: { plaidLinkToken: true }, + }) + + if (!user.plaidLinkToken) + throw new Error('Could not re-initialize flow for OAuth, no link token found') + + return user.plaidLinkToken + } + + // Save link token for later retrieval (needed for OAuth institutions) - this will be cleared upon connection + async cacheLinkToken(userId: User['id'], token: string) { + return await this.prisma.user.update({ + where: { id: userId }, + data: { plaidLinkToken: token }, + }) + } + + async createLinkToken(userId: User['id'], options?: Partial) { + const config = await this.getLinkConfig(userId, { + products: [Products.Transactions], + ...options, + }) + + const { + data: { link_token, request_id }, + } = await this.plaid.linkTokenCreate(config) + + this.logger.info(`Plaid link token created for user ${userId}`, { + request_id, + }) + + return link_token + } + + /** + * @see https://plaid.com/docs/link/update-mode/#using-update-mode + */ + async createLinkTokenForUpdateMode( + userId: User['id'], + accountConnectionId: AccountConnection['id'], + mode: SharedType.PlaidLinkUpdateMode + ) { + const accountConnection = await this.prisma.accountConnection.findUniqueOrThrow({ + where: { id: accountConnectionId }, + }) + + if (!accountConnection.plaidAccessToken) { + throw new Error(`connection ${accountConnection.id} does not have a plaid access token`) + } + + const config = await this.getLinkConfig( + userId, + mode === 'new-accounts' + ? { + access_token: this.crypto.decrypt(accountConnection.plaidAccessToken), + update: { account_selection_enabled: true }, + } + : { + access_token: this.crypto.decrypt(accountConnection.plaidAccessToken), + } + ) + + const { + data: { link_token, request_id }, + } = await this.plaid.linkTokenCreate(config) + + this.logger.info(`Plaid link token in update mode created for user ${userId}`, { + request_id, + }) + + return link_token + } + + async exchangePublicToken( + userId: User['id'], + token: string, + institution: Pick + ) { + const connections = await this.prisma.accountConnection.findMany({ + where: { userId }, + }) + + if (connections.length > 40) { + throw new Error('MAX_ACCOUNT_CONNECTIONS') + } + + const { + data: { access_token, item_id, request_id }, + } = await this.plaid.itemPublicTokenExchange({ public_token: token }) + + this.logger.info(`Plaid token exchanged for item ${item_id}`, { request_id }) + + const { + data: { accounts: plaidAccounts, request_id: accountsRequest, item }, + } = await this.plaid.accountsGet({ access_token }) + + this.logger.info(`Plaid accounts retrieved for item ${item.item_id}`, { + request_id: accountsRequest, + }) + + // If all the accounts are Non-USD, throw an error + if (plaidAccounts.every((a) => a.balances.iso_currency_code !== 'USD')) { + throw new Error('USD_ONLY') + } + + // Create account connection on exchange; accounts + txns will sync later with webhook + const [accountConnection] = await this.prisma.$transaction([ + this.prisma.accountConnection.create({ + data: { + name: institution.name, + type: 'plaid' as SharedType.AccountConnectionType, + plaidItemId: item_id, + plaidInstitutionId: institution.institution_id, + plaidAccessToken: this.crypto.encrypt(access_token), + userId, + syncStatus: 'PENDING', + }, + }), + // Once connection created, no longer allow the user's cached link token to be used + this.prisma.user.update({ + where: { id: userId }, + data: { plaidLinkToken: null }, + }), + ]) + + return accountConnection + } + + async createSandboxAccount(userId: User['id'], username?: string) { + // https://plaid.com/docs/sandbox/institutions/ + const randomInstitution = _.sample([ + { name: 'First Platypus Bank', id: 'ins_109508' }, + { name: 'First Gingham Credit Union', id: 'ins_109509' }, + { name: 'Tattersall Federal Credit Union', id: 'ins_109510' }, + { name: 'Tartan Bank', id: 'ins_109511' }, + { name: 'Houndstooth Bank', id: 'ins_109512' }, + ])! + + // Does the same thing as Plaid Link flow, but all with code (great for dev testing) + const { + data: { public_token }, + } = await this.plaid.sandboxPublicTokenCreate({ + institution_id: randomInstitution.id, + initial_products: [Products.Transactions], + options: { + webhook: await this.webhookUrl, + override_username: username, + }, + }) + + return this.exchangePublicToken(userId, public_token, { + name: randomInstitution.name, + institution_id: randomInstitution.id, + }) + } + + private async getLinkConfig( + userId: User['id'], + options: Partial + ): Promise { + return { + user: { + client_user_id: userId.toString(), + }, + client_name: 'Maybe', + country_codes: [CountryCode.Us], + language: 'en', + redirect_uri: `${this.clientUrl}/oauth`, + webhook: await this.webhookUrl, + link_customization_name: 'account_selection', + ...options, + } + } +} diff --git a/libs/server/features/src/providers/plaid/plaid.webhook.ts b/libs/server/features/src/providers/plaid/plaid.webhook.ts new file mode 100644 index 00000000000..3b331546ee1 --- /dev/null +++ b/libs/server/features/src/providers/plaid/plaid.webhook.ts @@ -0,0 +1,174 @@ +import type { Logger } from 'winston' +import type { PrismaClient } from '@prisma/client' +import type { QueueService } from '@maybe-finance/server/shared' +import type { IAccountConnectionService } from '../../account-connection' +import type { + PlaidApi, + ItemErrorWebhook, + NewAccountsAvailableWebhook, + PendingExpirationWebhook, + TransactionsRemovedWebhook, + UserPermissionRevokedWebhook, +} from 'plaid' +import { Prisma } from '@prisma/client' + +type PlaidWebhook = { + [key: string]: any + webhook_type: string + webhook_code: string + item_id: string +} + +export interface IPlaidWebhookHandler { + handleWebhook(webhook: PlaidWebhook): Promise +} + +export class PlaidWebhookHandler implements IPlaidWebhookHandler { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly plaid: PlaidApi, + private readonly accountConnectionService: IAccountConnectionService, + private readonly queueService: QueueService + ) {} + + async handleWebhook(webhook: PlaidWebhook) { + const { webhook_type, webhook_code, item_id } = webhook + + const accountConnection = await this.prisma.accountConnection.findUnique({ + where: { plaidItemId: item_id }, + }) + + if (!accountConnection) { + this.logger.warn( + `Could not find Plaid item ${item_id} in DB, aborting webhook handler code=${webhook_code} type=${webhook_type}` + ) + return + } + + switch (webhook_type) { + case 'ITEM': + switch (webhook_code) { + case 'ERROR': { + const data = webhook as ItemErrorWebhook + await this.prisma.accountConnection.update({ + where: { plaidItemId: data.item_id }, + data: { + status: 'ERROR', + plaidError: (data.error as any) ?? Prisma.DbNull, + }, + }) + break + } + case 'PENDING_EXPIRATION': { + const data = webhook as PendingExpirationWebhook + await this.prisma.accountConnection.update({ + where: { plaidItemId: data.item_id }, + data: { + status: 'ERROR', + plaidConsentExpiration: data.consent_expiration_time, + }, + }) + break + } + case 'USER_PERMISSION_REVOKED': { + const data = webhook as UserPermissionRevokedWebhook + await this.prisma.accountConnection.update({ + where: { plaidItemId: data.item_id }, + data: { + status: 'ERROR', + plaidError: (data.error as any) ?? Prisma.DbNull, + }, + }) + break + } + case 'NEW_ACCOUNTS_AVAILABLE': { + const data = webhook as NewAccountsAvailableWebhook + await this.prisma.accountConnection.update({ + where: { plaidItemId: data.item_id }, + data: { + // set flag indicating new accounts available -> trigger plaid update mode + // https://plaid.com/docs/link/account-select-v2-migration-guide/#requesting-data-for-new-accounts + plaidNewAccountsAvailable: true, + }, + }) + break + } + } + break + case 'TRANSACTIONS': { + switch (webhook_code) { + case 'INITIAL_UPDATE': + break // skip because everything will be handled in historical / default updates + case 'HISTORICAL_UPDATE': { + await this.accountConnectionService.sync(accountConnection.id) + break + } + case 'DEFAULT_UPDATE': { + await this.accountConnectionService.sync(accountConnection.id, { + type: 'plaid', + products: ['transactions'], + }) + break + } + case 'TRANSACTIONS_REMOVED': { + const data = webhook as TransactionsRemovedWebhook + + this.logger.info( + `${data.removed_transactions.length} TRANSACTIONS_REMOVED, ids=${data.removed_transactions}` + ) + + await this.prisma.$executeRaw` + DELETE FROM "transaction" WHERE "plaid_transaction_id" IN (${Prisma.join( + data.removed_transactions + )}) + ` + break + } + } + break + } + case 'HOLDINGS': { + switch (webhook_code) { + case 'DEFAULT_UPDATE': { + await this.accountConnectionService.sync(accountConnection.id, { + type: 'plaid', + products: ['holdings'], + }) + + break + } + } + break + } + case 'INVESTMENTS_TRANSACTIONS': { + switch (webhook_code) { + case 'DEFAULT_UPDATE': { + await this.accountConnectionService.sync(accountConnection.id, { + type: 'plaid', + products: ['investment-transactions'], + }) + + break + } + } + break + } + case 'LIABILITIES': { + switch (webhook_code) { + case 'DEFAULT_UPDATE': { + await this.accountConnectionService.sync(accountConnection.id, { + type: 'plaid', + products: ['liabilities'], + }) + + break + } + } + break + } + default: + break + } + } +} diff --git a/libs/server/features/src/providers/property/index.ts b/libs/server/features/src/providers/property/index.ts new file mode 100644 index 00000000000..e96a5bdcda9 --- /dev/null +++ b/libs/server/features/src/providers/property/index.ts @@ -0,0 +1 @@ +export * from './property.service' diff --git a/libs/server/features/src/providers/property/property.service.ts b/libs/server/features/src/providers/property/property.service.ts new file mode 100644 index 00000000000..7f029080ac6 --- /dev/null +++ b/libs/server/features/src/providers/property/property.service.ts @@ -0,0 +1,37 @@ +import type { Account } from '@prisma/client' +import type { Logger } from 'winston' +import type { IETL, SyncAccountOptions } from '@maybe-finance/server/shared' +import type { IAccountProvider } from '../../account' +import { etl } from '@maybe-finance/server/shared' + +export type PropertyData = { + pricing: {} +} + +export class PropertyService implements IAccountProvider, IETL { + public constructor(private readonly logger: Logger) {} + + async sync(account: Account, _options?: SyncAccountOptions) { + await etl(this, account) + } + + async delete(_account: Account) { + // ToDo: implement if needed + } + + async extract(_account: Account) { + // ToDo: fetch pricing from Zillow|Redfin + return { + pricing: {}, + } + } + + transform(_account: Account, data: PropertyData) { + return Promise.resolve(data) + } + + async load(_account: Account, _data: PropertyData) { + // ToDo: save pricing valuation + throw new Error('Method not implemented.') + } +} diff --git a/libs/server/features/src/providers/vehicle/index.ts b/libs/server/features/src/providers/vehicle/index.ts new file mode 100644 index 00000000000..9a32f844e79 --- /dev/null +++ b/libs/server/features/src/providers/vehicle/index.ts @@ -0,0 +1 @@ +export * from './vehicle.service' diff --git a/libs/server/features/src/providers/vehicle/vehicle.service.ts b/libs/server/features/src/providers/vehicle/vehicle.service.ts new file mode 100644 index 00000000000..31f8356599e --- /dev/null +++ b/libs/server/features/src/providers/vehicle/vehicle.service.ts @@ -0,0 +1,39 @@ +import type { Account } from '@prisma/client' +import type { Logger } from 'winston' +import type { IETL, SyncAccountOptions } from '@maybe-finance/server/shared' +import type { IAccountProvider } from '../../account' +import { etl } from '@maybe-finance/server/shared' + +export type VehicleData = { + pricing: {} +} + +export class VehicleService implements IAccountProvider, IETL { + public constructor(private readonly logger: Logger) {} + + async sync(account: Account, _options?: SyncAccountOptions) { + await etl(this, account) + } + + async delete(_account: Account) { + // ToDo: implement if needed + } + + /** + * @todo fetch pricing from KBB / Edmunds + */ + async extract(_account: Account) { + return { + pricing: {}, + } + } + + transform(_account: Account, data: VehicleData) { + return Promise.resolve(data) + } + + async load(_account: Account, _data: VehicleData) { + // ToDo: save pricing valuation + throw new Error('Method not implemented.') + } +} diff --git a/libs/server/features/src/security-pricing/index.ts b/libs/server/features/src/security-pricing/index.ts new file mode 100644 index 00000000000..0b16a43fbef --- /dev/null +++ b/libs/server/features/src/security-pricing/index.ts @@ -0,0 +1,2 @@ +export * from './security-pricing.service' +export * from './security-pricing.processor' diff --git a/libs/server/features/src/security-pricing/security-pricing.processor.ts b/libs/server/features/src/security-pricing/security-pricing.processor.ts new file mode 100644 index 00000000000..9188265dff3 --- /dev/null +++ b/libs/server/features/src/security-pricing/security-pricing.processor.ts @@ -0,0 +1,18 @@ +import type { Logger } from 'winston' +import type { SyncSecurityQueueJobData } from '@maybe-finance/server/shared' +import type { ISecurityPricingService } from './security-pricing.service' + +export interface ISecurityPricingProcessor { + syncAll(jobData?: SyncSecurityQueueJobData): Promise +} + +export class SecurityPricingProcessor implements ISecurityPricingProcessor { + constructor( + private readonly logger: Logger, + private readonly securityPricingService: ISecurityPricingService + ) {} + + async syncAll(_jobData?: SyncSecurityQueueJobData) { + await this.securityPricingService.syncAll() + } +} diff --git a/libs/server/features/src/security-pricing/security-pricing.service.ts b/libs/server/features/src/security-pricing/security-pricing.service.ts new file mode 100644 index 00000000000..02918f02459 --- /dev/null +++ b/libs/server/features/src/security-pricing/security-pricing.service.ts @@ -0,0 +1,157 @@ +import type { PrismaClient, Security } from '@prisma/client' +import type { IMarketDataService } from '@maybe-finance/server/shared' +import type { Logger } from 'winston' +import { Prisma } from '@prisma/client' +import { DateTime } from 'luxon' +import { SharedUtil } from '@maybe-finance/shared' + +export interface ISecurityPricingService { + sync(security: Pick, syncStart?: string): Promise + syncAll(): Promise +} + +export class SecurityPricingService implements ISecurityPricingService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly marketDataService: IMarketDataService + ) {} + + async sync( + security: Pick, + syncStart?: string + ) { + const dailyPricing = await this.marketDataService.getDailyPricing( + security, + syncStart + ? DateTime.fromISO(syncStart, { zone: 'utc' }) + : DateTime.utc().minus({ years: 2 }), + DateTime.now() + ) + + if (!dailyPricing.length) return + + this.logger.debug( + `fetched ${dailyPricing.length} daily prices for Security{id=${security.id} symbol=${security.symbol})` + ) + + await this.prisma.$transaction([ + this.prisma.$executeRaw` + INSERT INTO security_pricing (security_id, date, price_close, price_as_of, source) + VALUES + ${Prisma.join( + dailyPricing.map( + ({ date, priceClose }) => + Prisma.sql`( + ${security.id}, + ${date.toISODate()}::date, + ${priceClose}, + NOW(), + ${this.marketDataService.source} + )` + ) + )} + ON CONFLICT (security_id, date) DO UPDATE + SET + price_close = EXCLUDED.price_close, + price_as_of = EXCLUDED.price_as_of, + source = EXCLUDED.source; + `, + this.prisma.security.update({ + where: { id: security.id }, + data: { + pricingLastSyncedAt: new Date(), + }, + }), + ]) + } + + async syncAll() { + const profiler = this.logger.startTimer() + + for await (const securities of SharedUtil.paginateIt({ + pageSize: 100, + fetchData: (offset, count) => + this.prisma.security.findMany({ + select: { + id: true, + symbol: true, + plaidType: true, + currencyCode: true, + }, + skip: offset, + take: count, + }), + })) { + const livePricing = await this.marketDataService + .getLivePricing(securities) + .then((lp) => lp.filter((p) => !!p.pricing)) + + this.logger.debug( + `Fetched live pricing for ${livePricing.length} / ${securities.length} securities` + ) + + if (livePricing.length === 0) break + + await this.prisma.$transaction([ + this.prisma.$executeRaw` + INSERT INTO security_pricing (security_id, date, price_close, price_as_of, source) + VALUES + ${Prisma.join( + livePricing.map( + ({ security, pricing }) => + Prisma.sql`( + ${security.id}, + ${pricing!.updatedAt.toISODate()}::date, + ${pricing!.price}, + ${pricing!.updatedAt.toJSDate()}, + ${this.marketDataService.source} + )` + ) + )} + ON CONFLICT (security_id, date) DO UPDATE + SET + price_close = EXCLUDED.price_close, + price_as_of = EXCLUDED.price_as_of, + source = EXCLUDED.source; + `, + // Update today's balance record for any accounts with holdings containing synced securities + this.prisma.$executeRaw` + INSERT INTO account_balance (account_id, date, balance) + SELECT + h.account_id, + NOW() AS date, + SUM(COALESCE(h.quantity * sp.price_close * COALESCE(s.shares_per_contract, 1), h.value)) AS balance + FROM + holding h + INNER JOIN security s ON s.id = h.security_id + LEFT JOIN ( + SELECT DISTINCT ON (security_id) + * + FROM + security_pricing + ORDER BY + security_id, date DESC + ) sp ON sp.security_id = s.id + WHERE + h.account_id IN ( + SELECT + a.id + FROM + account a + INNER JOIN holding h ON h.account_id = a.id + WHERE + h.security_id IN (${Prisma.join(livePricing.map((p) => p.security.id))}) + ) + GROUP BY + h.account_id + ON CONFLICT (account_id, date) DO UPDATE + SET + balance = EXCLUDED.balance; + `, + ]) + } + + profiler.done({ message: 'Synced all securities' }) + } +} diff --git a/libs/server/features/src/stripe/index.ts b/libs/server/features/src/stripe/index.ts new file mode 100644 index 00000000000..9f50cbf7dd2 --- /dev/null +++ b/libs/server/features/src/stripe/index.ts @@ -0,0 +1 @@ +export * from './stripe.webhook' diff --git a/libs/server/features/src/stripe/stripe.webhook.ts b/libs/server/features/src/stripe/stripe.webhook.ts new file mode 100644 index 00000000000..d139a06b892 --- /dev/null +++ b/libs/server/features/src/stripe/stripe.webhook.ts @@ -0,0 +1,107 @@ +import type { Logger } from 'winston' +import type Stripe from 'stripe' +import type { PrismaClient } from '@prisma/client' +import { DateTime } from 'luxon' + +export interface IStripeWebhookHandler { + handleWebhook(event: Stripe.Event): Promise +} + +export class StripeWebhookHandler implements IStripeWebhookHandler { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly stripe: Stripe + ) {} + + async handleWebhook(event: Stripe.Event) { + switch (event.type) { + case 'checkout.session.completed': { + const session = event.data.object as Stripe.Checkout.Session + + if (!session.subscription || !session.client_reference_id) return + + const subscription = await this.stripe.subscriptions.retrieve( + session.subscription as string + ) + + await this.prisma.user.updateMany({ + where: { + auth0Id: session.client_reference_id, + }, + data: { + trialEnd: null, + stripeCustomerId: subscription.customer as string, + stripeSubscriptionId: subscription.id, + stripePriceId: subscription.items.data[0]?.price.id, + stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000), + stripeCancelAt: subscription.cancel_at + ? new Date(subscription.cancel_at * 1000) + : null, + }, + }) + + break + } + case 'customer.subscription.created': + case 'customer.subscription.updated': { + const subscription = event.data.object as Stripe.Subscription + + await this.prisma.user.updateMany({ + where: { + stripeCustomerId: subscription.customer as string, + }, + data: { + trialEnd: null, + stripeSubscriptionId: subscription.id, + stripePriceId: subscription.items.data[0]?.price.id, + stripeCurrentPeriodEnd: new Date(subscription.current_period_end * 1000), + stripeCancelAt: subscription.cancel_at + ? new Date(subscription.cancel_at * 1000) + : null, + }, + }) + + break + } + case 'customer.subscription.deleted': { + const subscription = event.data.object as Stripe.Subscription + + await this.prisma.user.updateMany({ + where: { + stripeSubscriptionId: subscription.id, + }, + data: { + stripeSubscriptionId: null, + stripePriceId: null, + stripeCurrentPeriodEnd: null, + stripeCancelAt: DateTime.now().toJSDate(), + }, + }) + + break + } + case 'customer.deleted': { + const customer = event.data.object as Stripe.Customer + + await this.prisma.user.updateMany({ + where: { + stripeCustomerId: customer.id, + }, + data: { + stripeCustomerId: null, + stripeSubscriptionId: null, + stripePriceId: null, + stripeCurrentPeriodEnd: null, + stripeCancelAt: null, + }, + }) + break + } + default: { + this.logger.warn('Unhandled Stripe event', { event }) + break + } + } + } +} diff --git a/libs/server/features/src/transaction/index.ts b/libs/server/features/src/transaction/index.ts new file mode 100644 index 00000000000..f17e792e4bd --- /dev/null +++ b/libs/server/features/src/transaction/index.ts @@ -0,0 +1,2 @@ +export * from './transaction.service' +export * from './transaction.schema' diff --git a/libs/server/features/src/transaction/transaction.schema.ts b/libs/server/features/src/transaction/transaction.schema.ts new file mode 100644 index 00000000000..4e3fbf8febe --- /dev/null +++ b/libs/server/features/src/transaction/transaction.schema.ts @@ -0,0 +1,27 @@ +import { DateTime } from 'luxon' +import { z } from 'zod' +import { DateUtil } from '@maybe-finance/shared' + +export const TransactionPaginateParams = z.object({ + pageIndex: z.string().transform((val) => parseInt(val)), + pageSize: z + .string() + .default('50') + .transform((val) => parseInt(val)), +}) + +export const ISODateSchema = z + .string() + .default(DateTime.utc().toISODate()) + .transform((s) => DateUtil.datetimeTransform(s).toJSDate()) + +export const TransactionUpdateInputSchema = z + .object({ + date: ISODateSchema, + name: z.string(), + amount: z.number(), + categoryUser: z.string(), + excluded: z.boolean(), + typeUser: z.enum(['INCOME', 'EXPENSE', 'PAYMENT', 'TRANSFER']), + }) + .partial() diff --git a/libs/server/features/src/transaction/transaction.service.ts b/libs/server/features/src/transaction/transaction.service.ts new file mode 100644 index 00000000000..5da9cb0d668 --- /dev/null +++ b/libs/server/features/src/transaction/transaction.service.ts @@ -0,0 +1,127 @@ +import type { Logger } from 'winston' +import type { AccountConnection, PrismaClient, Transaction, User } from '@prisma/client' +import type { Prisma } from '@prisma/client' +import { SharedType } from '@maybe-finance/shared' +import { DateTime } from 'luxon' + +type TransactionWithConnection = Transaction & { + account: SharedType.Account & { + accountConnection: AccountConnection | null + } +} + +export interface ITransactionService { + get( + id: User['id'], + pageIndex?: number, + pageSize?: SharedType.PageSize + ): Promise + getAll(userId: User['id']): Promise<{ transactions: Transaction[]; pageCount: number }> + update( + id: Transaction['id'], + data: Prisma.TransactionUncheckedUpdateInput + ): Promise + markTransfers(userId: User['id'], startDate?: string): Promise +} + +export class TransactionService implements ITransactionService { + constructor(private readonly logger: Logger, private readonly prisma: PrismaClient) {} + + async getAll( + userId: User['id'], + pageIndex = 0, + pageSize = SharedType.PageSize.Transaction + ): Promise { + const where = { + OR: [{ account: { userId } }, { account: { accountConnection: { userId } } }], + } + + const [transactions, count] = await this.prisma.$transaction([ + this.prisma.transaction.findMany({ + where, + include: { account: { include: { accountConnection: true } } }, + skip: pageIndex * pageSize, + take: pageSize, + orderBy: [{ date: 'desc' }, { amount: 'desc' }, { name: 'desc' }], + }), + this.prisma.transaction.count({ where }), + ]) + + return { + transactions, + pageCount: Math.ceil(count / pageSize), + } + } + + async get(id: Transaction['id']) { + return await this.prisma.transaction.findUniqueOrThrow({ + where: { id }, + include: { account: { include: { accountConnection: true } } }, + }) + } + + async update(id: Transaction['id'], data: Prisma.TransactionUncheckedUpdateInput) { + const transaction = await this.prisma.transaction.update({ + where: { id }, + data, + }) + + this.logger.info(`Updated transaction id=${id} account=${transaction.accountId}`) + + return transaction + } + + async markTransfers( + userId: User['id'], + startDate = DateTime.utc().minus({ years: 2 }).toISODate() + ) { + this.logger.debug(`Analyzing and enhancing transactions for user=${userId}`) + + const transferPairs = await this.prisma.$queryRaw<{ id: number; match_id: number }[]>` + WITH txn_set AS ( + SELECT + t.id, + t.date, + t.amount, + t.flow, + a.id AS account_id, + a.type AS account_type, + a.classification AS account_classification + FROM transaction t + INNER JOIN account a ON a.id = t.account_id + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE (a.user_id = ${userId} OR ac.user_id = ${userId}) + AND t.date >= ${startDate}::date + AND t.date <= ${DateTime.utc().toISODate()}::date + ), txn_matches as ( + SELECT DISTINCT + ON (t.id) + t.id, + tc.id AS match_id + FROM txn_set t + LEFT JOIN txn_set tc ON tc.id <> t.id + AND tc.account_id <> t.account_id + AND ABS(tc.date - t.date) <= 1 + AND tc.amount = - t.amount + AND ( + (t.account_classification = 'asset' AND tc.account_classification = 'asset') -- asset transfer + OR (t.account_classification = 'asset' AND t.flow = 'OUTFLOW' AND tc.account_classification = 'liability') -- transfer from asset to liability + OR (t.account_classification = 'liability' AND t.flow = 'INFLOW' AND tc.account_classification = 'asset') -- payment received from asset to liability + ) + WHERE tc IS NOT NULL + ) + UPDATE transaction t + SET match_id = tm.match_id + FROM txn_matches tm + WHERE t.id = tm.id + RETURNING t.id, tm.match_id + ` + + if (transferPairs.length) { + this.logger.info( + `Marked ${transferPairs.length} transactions as transfer matches for user=${userId}`, + transferPairs + ) + } + } +} diff --git a/libs/server/features/src/user/index.ts b/libs/server/features/src/user/index.ts new file mode 100644 index 00000000000..bc9ce66233d --- /dev/null +++ b/libs/server/features/src/user/index.ts @@ -0,0 +1,5 @@ +export * from './user.processor' +export * from './user.service' +export * from './onboarding.schema' + +export type { OnboardingState, RegisteredStep } from './onboarding.service' diff --git a/libs/server/features/src/user/onboarding.schema.ts b/libs/server/features/src/user/onboarding.schema.ts new file mode 100644 index 00000000000..9057a46795a --- /dev/null +++ b/libs/server/features/src/user/onboarding.schema.ts @@ -0,0 +1,43 @@ +import { z } from 'zod' + +export const UpdateOnboardingSchema = z.discriminatedUnion('flow', [ + z.object({ + flow: z.literal('main'), + updates: z + .object({ + key: z.enum([ + 'intro', + 'profile', + 'verifyEmail', + 'firstAccount', + 'accountSelection', + 'terms', + 'maybe', + 'welcome', + ]), + markedComplete: z.boolean(), + }) + .array(), + }), + z.object({ + flow: z.literal('sidebar'), + markedComplete: z.boolean().optional(), + updates: z + .object({ + key: z.enum([ + 'connect-depository', + 'connect-investment', + 'connect-liability', + 'add-crypto', + 'add-property', + 'add-vehicle', + 'add-other', + 'upgrade-account', + 'create-plan', + 'ask-advisor', + ]), + markedComplete: z.boolean(), + }) + .array(), + }), +]) diff --git a/libs/server/features/src/user/onboarding.service.ts b/libs/server/features/src/user/onboarding.service.ts new file mode 100644 index 00000000000..6febd889fcf --- /dev/null +++ b/libs/server/features/src/user/onboarding.service.ts @@ -0,0 +1,130 @@ +import type { SharedType } from '@maybe-finance/shared' + +export type RegisteredStep = { + key: string + markedComplete: boolean +} + +// DB Json field where each key represents a separate onboarding flow +export type OnboardingState = { + [key in SharedType.OnboardingFlow]: { + markedComplete: boolean // An override to indicate the flow is complete + steps: RegisteredStep[] + } +} + +type CallbackFn = (user: TData, step: Step) => T + +export class Step { + group: string | undefined + ctaPath: string | undefined + title!: CallbackFn + isComplete!: CallbackFn + isMarkedComplete: CallbackFn = () => false + isExcluded: CallbackFn = () => false + isOptional = false + + /** + * Constructs an onboarding step + * @param key a stable key that the UI relies to show relevant view components + * @param group a grouping that UI uses to visually group steps as "substeps" + */ + constructor(readonly key: string) {} + + addToGroup(group: string) { + this.group = group + return this + } + + /** When clicked, the router path that app will redirect to */ + setCTAPath(path: string) { + this.ctaPath = path + return this + } + + setTitle(fn: CallbackFn) { + this.title = fn + return this + } + + completeIf(fn: CallbackFn) { + this.isComplete = fn + return this + } + + markedCompleteIf(fn: CallbackFn) { + this.isMarkedComplete = fn + return this + } + + excludeIf(fn: CallbackFn) { + this.isExcluded = fn + return this + } + + optional() { + this.isOptional = true + return this + } +} + +export class Onboarding { + private _steps: Step[] = [] + + constructor( + private readonly data: TData, + private readonly onboardingCompleteOverride: boolean + ) { + if (!this._steps.every((step) => step.completeIf != null && step.title != null)) { + throw new Error('Every step must define completeIf callback fn and title') + } + } + + get isComplete() { + return this.steps.every( + (step) => step.isComplete || step.isOptional || step.isMarkedComplete + ) + } + + get isMarkedComplete() { + return this.steps.every((step) => step.isMarkedComplete) || this.onboardingCompleteOverride + } + + /** Progress, expressed as percentage. */ + get progress() { + const completed = this.steps.filter( + (step) => step.isComplete || step.isMarkedComplete + ).length + + return { + completed, + total: this.steps.length, + percent: +(completed / this.steps.length).toFixed(2), + } + } + + get steps() { + return this._steps + .filter((step) => !step.isExcluded(this.data, step)) + .map((step) => ({ + key: step.key, + title: step.title(this.data, step), + group: step.group, + ctaPath: step.ctaPath, + isOptional: step.isOptional, + isComplete: step.isComplete(this.data, step), + isMarkedComplete: step.isMarkedComplete(this.data, step), + })) + } + + get currentStep() { + const incompleteSteps = this.steps.filter((step) => !step.isComplete) + return incompleteSteps[0] + } + + addStep(key: string) { + const step = new Step(key) + this._steps.push(step) + return step + } +} diff --git a/libs/server/features/src/user/user.processor.ts b/libs/server/features/src/user/user.processor.ts new file mode 100644 index 00000000000..818158350d3 --- /dev/null +++ b/libs/server/features/src/user/user.processor.ts @@ -0,0 +1,65 @@ +import type { Logger } from 'winston' +import type { PrismaClient, User } from '@prisma/client' +import type { SyncUserQueueJobData } from '@maybe-finance/server/shared' +import type { IAccountService } from '../account' +import type { IUserService } from './user.service' +import type { + IAccountConnectionProviderFactory, + IAccountConnectionService, +} from '../account-connection' +import { ServerUtil } from '@maybe-finance/server/shared' + +export interface IUserProcessor { + sync(jobData: SyncUserQueueJobData): Promise + delete(jobData: SyncUserQueueJobData): Promise +} + +export class UserProcessor implements IUserProcessor { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly userService: IUserService, + private readonly accountService: IAccountService, + private readonly connectionService: IAccountConnectionService, + private readonly connectionProviders: IAccountConnectionProviderFactory + ) {} + + async sync(jobData: SyncUserQueueJobData) { + const user = await this.userService.get(jobData.userId) + + await ServerUtil.useSync({ + sync: async (user) => { + const { connections, accounts } = await this.accountService.getAll(user.id) + + await Promise.allSettled([ + ...connections.map((connection) => this.connectionService.sync(connection.id)), + ...accounts.map((account) => this.accountService.sync(account.id)), + ]) + }, + onSyncSuccess: (user) => this.userService.syncBalances(user.id), + onSyncError: async (user, error) => { + this.logger.error(`error syncing user ${user.id}`, { error }) + }, + })(user) + } + + async delete(jobData: SyncUserQueueJobData) { + const { userId } = jobData + + this.logger.info(`deleting user ${userId}...`) + + const connections = await this.prisma.accountConnection.findMany({ + where: { userId }, + }) + + // delete connection data + this.logger.info(`deleting user ${userId} data for ${connections.length} connections...`) + await Promise.allSettled(connections.map((c) => this.connectionProviders.for(c).delete(c))) + + // delete from database (will cascade to relations) + this.logger.info(`deleting user ${userId} from database...`) + await this.prisma.user.delete({ where: { id: userId } }) + + this.logger.info(`user ${userId} deleted successfully`) + } +} diff --git a/libs/server/features/src/user/user.service.ts b/libs/server/features/src/user/user.service.ts new file mode 100644 index 00000000000..4dec7454f20 --- /dev/null +++ b/libs/server/features/src/user/user.service.ts @@ -0,0 +1,713 @@ +import type { + AccountCategory, + AccountType, + Agreement, + PrismaClient, + SignedAgreement, + User, +} from '@prisma/client' +import type { Logger } from 'winston' +import { + AuthUtil, + type PurgeUserQueue, + type S3Service, + type SyncUserQueue, +} from '@maybe-finance/server/shared' +import type { ManagementClient, UnlinkAccountsParamsProvider } from 'auth0' +import type Stripe from 'stripe' +import type { IBalanceSyncStrategyFactory } from '../account-balance' +import type { IAccountQueryService } from '../account' +import type { SharedType } from '@maybe-finance/shared' +import { CopyObjectCommand, MetadataDirective } from '@aws-sdk/client-s3' +import { DateTime } from 'luxon' +import crypto from 'crypto' +import { DbUtil } from '@maybe-finance/server/shared' +import { DateUtil } from '@maybe-finance/shared' +import { flatten } from 'lodash' +import { Onboarding, type OnboardingState, type Step } from './onboarding.service' + +export type MainOnboardingUser = Pick< + User, + 'dob' | 'household' | 'maybeGoals' | 'firstName' | 'lastName' | 'name' +> & { + emailVerified: boolean + isAppleIdentity: boolean + onboarding: OnboardingState['main'] + accountConnections: { accounts: { id: number }[] }[] + accounts: { id: number }[] + signedAgreements: (SignedAgreement & { agreement: Agreement })[] +} + +export type SidebarOnboardingUser = { + onboarding: OnboardingState['sidebar'] + accountConnections: { + accounts: { + type: AccountType + category: AccountCategory + }[] + }[] + accounts: { + type: AccountType + category: AccountCategory + }[] + _count: { + plans: number + conversations: number + } +} + +export interface IUserService { + get(id: User['id']): Promise + sync(id: User['id']): Promise + syncBalances(id: User['id']): Promise + delete(id: User['id']): Promise +} + +export class UserService implements IUserService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly queryService: IAccountQueryService, + private readonly balanceSyncStrategyFactory: IBalanceSyncStrategyFactory, + private readonly syncQueue: SyncUserQueue, + private readonly purgeQueue: PurgeUserQueue, + private readonly auth0: ManagementClient, + private readonly stripe: Stripe + ) {} + + async get(id: User['id']) { + return this.prisma.user.findUniqueOrThrow({ + where: { id }, + }) + } + + async getAuth0Profile(user: User): Promise { + if (!user.email) throw new Error('No email found for user') + + const usersWithMatchingEmail = await this.auth0.getUsersByEmail(user.email) + const autoPromptEnabled = user.linkAccountDismissedAt == null + const currentUser = usersWithMatchingEmail.find((u) => u.user_id === user.auth0Id) + const primaryIdentity = currentUser?.identities?.find( + (identity) => !('profileData' in identity) + ) + const secondaryIdentities = + currentUser?.identities?.filter((identity) => 'profileData' in identity) ?? [] + if (!currentUser || !primaryIdentity) throw new Error('Failed to get Auth0 user') + + const socialOnlyUser = + primaryIdentity.isSocial && secondaryIdentities.every((i) => i.isSocial) + + const suggestedIdentities = usersWithMatchingEmail + .filter( + (match) => + match.email_verified && + match.user_id !== user.auth0Id && + match.identities?.at(0) != null + ) + .map((user) => user.identities!.at(0)!) + + // Auth0 returns 'true' (mis-typing) or true, so normalize the type here + const email_verified = + (currentUser.email_verified as unknown as string) === 'true' || + currentUser.email_verified === true + + return { + ...currentUser, + email_verified, + primaryIdentity, + secondaryIdentities, + suggestedIdentities, + socialOnlyUser, + autoPromptEnabled, + mfaEnabled: currentUser.user_metadata?.enrolled_mfa === true, + } + } + + async sync(id: User['id']) { + const user = await this.get(id) + await this.syncQueue.add('sync-user', { userId: user.id }) + return user + } + + async syncBalances(id: User['id']) { + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id }, + include: { + accounts: true, + accountConnections: { + select: { + accounts: true, + }, + }, + }, + }) + + const profiler = this.logger.startTimer() + + await Promise.all([ + ...user.accounts.map((account) => + this.balanceSyncStrategyFactory.for(account).syncAccountBalances(account) + ), + ...user.accountConnections.flatMap((connection) => + connection.accounts.map((account) => + this.balanceSyncStrategyFactory.for(account).syncAccountBalances(account) + ) + ), + ]) + + profiler.done({ message: `Synced user ${id} balances` }) + + return user + } + + async update(id: User['id'], data: SharedType.UpdateUser) { + return this.prisma.user.update({ + where: { id }, + data, + }) + } + + async delete(id: User['id']) { + const user = await this.get(id) + + // Delete Stripe customer, ending any active subscriptions + if (user.stripeCustomerId) await this.stripe.customers.del(user.stripeCustomerId) + + // Delete user from Auth0 so that it cannot be accessed in a partially-purged state + this.logger.info(`Removing user ${user.id} from Auth0 (${user.auth0Id})`) + await this.auth0.deleteUser({ id: user.auth0Id }) + + await this.purgeQueue.add('purge-user', { userId: user.id }) + + return user + } + + async getNetWorth( + userId: User['id'], + date: string = DateTime.utc().plus({ days: 1 }).toISODate() // default to one day here to ensure we're grabbing the most recent date's net worth + ): Promise { + const [netWorth] = await this.queryService.getNetWorthSeries({ userId }, date, date, 'days') + + return netWorth + } + + async getNetWorthSeries( + userId: User['id'], + start = DateTime.utc().minus({ years: 2 }).toISODate(), + end = DateTime.utc().toISODate(), + interval?: SharedType.TimeSeriesInterval + ): Promise { + interval = interval ?? DateUtil.calculateTimeSeriesInterval(start, end) + + const [series, today, minDate] = await Promise.all([ + this.queryService.getNetWorthSeries({ userId }, start, end, interval), + this.getNetWorth(userId), + this.getOldestBalanceDate(userId), + ]) + + return { + series: { + interval, + start, + end, + data: series, + }, + today, + minDate, + trend: + series.length > 0 + ? DbUtil.calculateTrend(series[0].netWorth, series[series.length - 1].netWorth) + : { amount: null, percentage: null, direction: 'flat' }, + } + } + + private async getOldestBalanceDate(userId: User['id']): Promise { + const [{ min_start_date }] = await this.prisma.$queryRaw<[{ min_start_date: Date | null }]>` + SELECT + LEAST( + MIN(a.start_date), + MIN(account_value_start_date(a.id)) + ) AS min_start_date + FROM + account a + LEFT JOIN account_connection ac ON ac.id = a.account_connection_id + WHERE + (a.user_id = ${userId} OR ac.user_id = ${userId}) + AND a.is_active; + ` + + const minDate = DateTime.min( + DateTime.utc().minus({ years: 2 }), + ...(min_start_date ? [DateTime.fromJSDate(min_start_date, { zone: 'utc' })] : []) + ) + + return minDate.toISODate() + } + + async getSubscription(userId: User['id']): Promise { + const { + trialEnd: trialEndRaw, + stripePriceId, + stripeCurrentPeriodEnd, + stripeCancelAt, + } = await this.prisma.user.findUniqueOrThrow({ + select: { + trialEnd: true, + stripePriceId: true, + stripeCurrentPeriodEnd: true, + stripeCancelAt: true, + }, + where: { id: userId }, + }) + + const trialEnd = trialEndRaw ? DateTime.fromJSDate(trialEndRaw) : null + + const trialing = trialEnd != null && trialEnd.diffNow().milliseconds > 0 + + const subscribed = !!stripePriceId || trialing + const cancelAt = stripeCancelAt ? DateTime.fromJSDate(stripeCancelAt) : null + + return { + subscribed, + trialing, + canceled: stripeCancelAt != null, + + currentPeriodEnd: stripeCurrentPeriodEnd + ? DateTime.fromJSDate(stripeCurrentPeriodEnd) + : null, + trialEnd, + cancelAt, + } + } + + async getIntercomMetadata( + userId: User['id'], + secret?: string + ): Promise { + const { auth0Id } = await this.prisma.user.findUniqueOrThrow({ + select: { auth0Id: true }, + where: { id: userId }, + }) + + return { + hash: secret + ? crypto.createHmac('sha256', secret).update(auth0Id).digest('hex') + : undefined, + } + } + + async getSignedAgreements(userId: User['id']) { + return this.prisma.agreement.findMany({ + distinct: 'type', + where: { signers: { some: { userId } } }, + orderBy: { revision: 'desc' }, + }) + } + + async getNewestAgreements() { + const agreements = await this.prisma.agreement.findMany({ + distinct: 'type', + where: { active: true }, + orderBy: { revision: 'desc' }, + }) + + if (agreements.length < 5) throw new Error('Failed to fetch all required agreements') + + return agreements + } + + // Should run serially to ensure audit record is saved prior to updating DB + async signAgreements(userId: User['id'], agreementIds: Agreement['id'][], s3: S3Service) { + // Agreement versions are stored over public CDN and copied (for audit) to an object-locked private bucket + const fromBucket = s3.buckets['public'] + const toBucket = s3.buckets['private'] + + const agreements = await this.prisma.agreement.findMany({ + where: { id: { in: agreementIds } }, + }) + + const feeAgreement = agreements.find((a) => a.type === 'fee') + if (!feeAgreement) throw new Error('Fee agreement must be present for signing') + + const Key = `private/agreements/signed/uid-${userId}/${feeAgreement.src.split('/').at(-1)}` + + // Store the fee agreement in S3 for audit + await s3.cli.send( + new CopyObjectCommand({ + Bucket: toBucket, + Key, + CopySource: `/${fromBucket}/${feeAgreement.src}`, + Metadata: { + // Correlated database record id + 'agreement-id': feeAgreement.id.toString(), + 'agreement-revision': DateUtil.dateToStr(feeAgreement.revision), + 'user-id': userId.toString(), + }, + MetadataDirective: MetadataDirective.REPLACE, + }) + ) + + this.logger.info( + `Successfully uploaded S3 audit record for fee agreement user=${userId} revision=${DateUtil.dateToStr( + feeAgreement.revision + )}` + ) + + // Make idempotent for easier testing + const signatures = await Promise.all( + Object.entries(agreements).map(([agreementType, agreement]) => + this.prisma.signedAgreement.upsert({ + where: { userId_agreementId: { userId, agreementId: agreement.id } }, + create: { + src: agreementType === 'fee' ? Key : undefined, + userId, + agreementId: agreement.id, + }, + update: {}, + }) + ) + ) + + this.logger.info(`Successfully signed agreements user=${userId}`) + + return signatures + } + + async getMemberCard(memberId: string, clientUrl: string) { + const { + name, + memberNumber, + title, + createdAt: joinDate, + maybe, + } = await this.prisma.user.findUniqueOrThrow({ + where: { memberId }, + select: { + name: true, + memberNumber: true, + title: true, + createdAt: true, + maybe: true, + }, + }) + + const cardUrl = new URL(`/card/${memberId}`, clientUrl) + + const imageUrl = new URL('api/card', clientUrl) + imageUrl.searchParams.append('name', name || 'Maybe User') + imageUrl.searchParams.append('number', memberNumber.toString()) + imageUrl.searchParams.append('title', title ?? '') + imageUrl.searchParams.append('date', joinDate.toISOString()) + + return { + memberNumber, + name, + title, + joinDate, + maybe, + cardUrl: cardUrl.href, + imageUrl: imageUrl.href, + } + } + + async buildMainOnboarding(userId: User['id']): Promise { + function markedComplete(user: MainOnboardingUser, step: Step) { + return ( + user.onboarding.steps.find((dbStep) => dbStep.key === step.key)?.markedComplete ?? + false + ) + } + + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: userId }, + select: { + auth0Id: true, + onboarding: true, + dob: true, + household: true, + maybeGoals: true, + firstName: true, + lastName: true, + name: true, + accounts: { select: { id: true } }, + accountConnections: { + select: { accounts: { select: { id: true } } }, + }, + signedAgreements: { include: { agreement: true } }, + }, + }) + + const auth0User = await this.auth0.getUser({ id: user.auth0Id }) + + // Auth0 has this mis-typed and it comes in as a 'true' string + const email_verified = auth0User.email_verified as unknown as string | boolean + + const typedOnboarding = user.onboarding as OnboardingState | null + const onboardingState = typedOnboarding + ? typedOnboarding.main + : { markedComplete: false, steps: [] } + + const onboarding = new Onboarding( + { + ...user, + onboarding: onboardingState, + emailVerified: email_verified === true || email_verified === 'true', + isAppleIdentity: auth0User.identities?.[0].provider === 'apple', + }, + onboardingState.markedComplete + ) + + onboarding + .addStep('intro') + .setTitle((user) => `Hey ${user.firstName ?? 'there'}, meet Maybe`) + .addToGroup('account') + .completeIf(markedComplete) + + onboarding + .addStep('profile') + .setTitle((_) => "Let's complete your profile") + .addToGroup('profile') + .completeIf((user) => { + return user.dob != null && user.household != null + }) + + onboarding + .addStep('verifyEmail') + .setTitle((_) => "Before we start, let's verify your email") + .addToGroup('setup') + .completeIf((user) => user.emailVerified) + .excludeIf((user) => user.isAppleIdentity) // Auth0 auto-verifies Apple identities. + + onboarding + .addStep('firstAccount') + .setTitle((_) => "Let's add your first account") + .addToGroup('setup') + .markedCompleteIf((user, step) => markedComplete(user, step)) + .completeIf((user, step) => { + return ( + user.accountConnections.length > 0 || + user.accounts.length > 0 || + markedComplete(user, step) + ) + }) + + onboarding + .addStep('accountSelection') + .setTitle((_) => 'What other accounts do you have?') + .addToGroup('setup') + .completeIf(markedComplete) + + onboarding + .addStep('terms') + .setTitle((_) => 'Finally, some agreements') + .addToGroup('setup') + .completeIf((user) => { + // All 5 agreements should be signed and Fee agreement should have S3 audit record + const feeAgreement = user.signedAgreements.find((sa) => sa.agreement.type === 'fee') + return user.signedAgreements.length >= 5 && feeAgreement?.agreement?.src != null + }) + + onboarding + .addStep('maybe') + .setTitle((_) => "One more thing, what's your maybe?") + .completeIf(markedComplete) + + onboarding + .addStep('welcome') + .setTitle((user) => { + return `Welcome to Maybe${user.name ? `, ${user.name}` : '!'}` + }) + .completeIf(markedComplete) + + return onboarding + } + + async buildSidebarOnboarding(userId: User['id']): Promise { + function markedComplete(user: SidebarOnboardingUser, step: Step) { + return ( + user.onboarding.steps.find((dbStep) => dbStep.key === step.key)?.markedComplete ?? + false + ) + } + + function stepUnregistered(user: SidebarOnboardingUser, step: Step) { + return user.onboarding.steps.findIndex((dbStep) => dbStep.key === step.key) < 0 + } + + function hasAccountType( + user: SidebarOnboardingUser, + types: AccountType | AccountType[], + category?: { + match?: AccountCategory + exclude?: AccountCategory + } + ) { + const accounts = [ + ...user.accounts, + ...flatten(user.accountConnections.map((ac) => ac.accounts)), + ] + + return ( + accounts.filter((a) => { + let match = Array.isArray(types) ? types.includes(a.type) : types === a.type + + if (!match) return false + + if (category) { + if (category.match) { + match = a.category === category.match + } + + if (category.exclude) { + match = a.category !== category.exclude + } + } + + return match + }).length > 0 + ) + } + + const user = await this.prisma.user.findUniqueOrThrow({ + where: { id: userId }, + select: { + onboarding: true, + accounts: { select: { type: true, category: true } }, + accountConnections: { + select: { accounts: { select: { type: true, category: true } } }, + }, + _count: { select: { plans: true, conversations: true } }, + }, + }) + + const typedOnboarding = user.onboarding as OnboardingState | null + const onboardingState = typedOnboarding + ? typedOnboarding.sidebar + : { markedComplete: false, steps: [] } + + const onboarding = new Onboarding( + { + ...user, + onboarding: onboardingState, + }, + onboardingState.markedComplete + ) + + onboarding + .addStep('connect-depository') + .setTitle((_) => 'Connect bank accounts') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, 'DEPOSITORY')) + .excludeIf(stepUnregistered) + + onboarding + .addStep('connect-investment') + .setTitle((_) => 'Connect investment accounts') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, 'INVESTMENT')) + .excludeIf(stepUnregistered) + + onboarding + .addStep('connect-liability') + .setTitle((_) => 'Connect credit card and loan accounts') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, ['CREDIT', 'LOAN'])) + .excludeIf(stepUnregistered) + + onboarding + .addStep('add-crypto') + .setTitle((_) => 'Manually add crypto accounts') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, 'OTHER_ASSET', { match: 'crypto' })) + .excludeIf(stepUnregistered) + + onboarding + .addStep('add-property') + .setTitle((_) => 'Manually add real estate') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, 'PROPERTY')) + .excludeIf(stepUnregistered) + + onboarding + .addStep('add-vehicle') + .setTitle((_) => 'Manually add vehicle') + .addToGroup('accounts') + .markedCompleteIf(markedComplete) + .completeIf((user) => hasAccountType(user, 'VEHICLE')) + .excludeIf(stepUnregistered) + + onboarding + .addStep('add-other') + .addToGroup('accounts') + .setTitle((_) => 'Manually add other assets and debts') + .markedCompleteIf(markedComplete) + .completeIf((user) => + hasAccountType(user, ['OTHER_ASSET', 'OTHER_LIABILITY'], { + exclude: 'crypto', + }) + ) + .excludeIf(stepUnregistered) + + onboarding + .addStep('create-plan') + .addToGroup('bonus') + .setTitle((_) => 'Setup a financial plan') + .completeIf((user) => user._count.plans > 0) + .setCTAPath('/plans') + + onboarding + .addStep('ask-advisor') + .addToGroup('bonus') + .setTitle((_) => 'Ask an advisor a question') + .completeIf((user) => user._count.conversations > 0) + .setCTAPath('/ask-the-advisor') + + return onboarding + } + + async linkAccounts( + primaryAuth0Id: User['auth0Id'], + provider: string, + secondaryJWT: { token: string; domain: string; audience: string } + ) { + const validatedJWT = await AuthUtil.validateRS256JWT( + `Bearer ${secondaryJWT.token}`, + secondaryJWT.domain, + secondaryJWT.audience + ) + + const user = await this.prisma.user.findFirst({ where: { auth0Id: validatedJWT.auth0Id } }) + + if (user?.stripePriceId) { + throw new Error( + 'The account you are trying to link has an active Stripe trial or subscription. We cannot link this identity at this time.' + ) + } + + return this.auth0.linkUsers(primaryAuth0Id, { + user_id: validatedJWT.auth0Id, + provider, + }) + } + + async unlinkAccounts( + primaryAuth0Id: User['auth0Id'], + secondaryAuth0Id: User['auth0Id'], + secondaryProvider: UnlinkAccountsParamsProvider + ) { + const response = await this.auth0.unlinkUsers({ + id: primaryAuth0Id, + provider: secondaryProvider, + user_id: secondaryAuth0Id, + }) + + this.logger.info(`Unlinked ${secondaryAuth0Id} from ${primaryAuth0Id}`) + + return response + } +} diff --git a/libs/server/features/src/valuation/index.ts b/libs/server/features/src/valuation/index.ts new file mode 100644 index 00000000000..4286fd714a9 --- /dev/null +++ b/libs/server/features/src/valuation/index.ts @@ -0,0 +1 @@ +export * from './valuation.service' diff --git a/libs/server/features/src/valuation/valuation.service.ts b/libs/server/features/src/valuation/valuation.service.ts new file mode 100644 index 00000000000..1f4c93fae5f --- /dev/null +++ b/libs/server/features/src/valuation/valuation.service.ts @@ -0,0 +1,84 @@ +import type { Logger } from 'winston' +import type { Account, PrismaClient, Valuation } from '@prisma/client' +import type { Prisma } from '@prisma/client' +import type { DateTime } from 'luxon' +import type { SharedType } from '@maybe-finance/shared' +import type { IAccountQueryService } from '../account' +import { SharedUtil } from '@maybe-finance/shared' +import { DbUtil } from '@maybe-finance/server/shared' + +export class ValuationService { + constructor( + private readonly logger: Logger, + private readonly prisma: PrismaClient, + private readonly queryService: IAccountQueryService + ) {} + + async getValuations( + accountId: Account['id'], + start?: DateTime, + end?: DateTime + ): Promise { + const [valuations, trends] = await Promise.all([ + this.prisma.valuation.findMany({ + where: { + accountId, + date: { + gte: start?.toJSDate(), + lte: end?.toJSDate(), + }, + }, + orderBy: { date: 'asc' }, + }), + this.queryService.getValuationTrends(accountId, start, end), + ]) + + return { + valuations: valuations.map((valuation) => { + const trend = trends.find((t) => t.valuation_id === valuation.id) + return { + ...valuation, + trend: trend + ? { + period: DbUtil.toTrend(trend.period_change, trend.period_change_pct), + total: DbUtil.toTrend(trend.total_change, trend.total_change_pct), + } + : null, + } + }), + trends: trends + .filter((trend) => !SharedUtil.nonNull(trend.valuation_id)) + .map((trend) => ({ + date: trend.date, + amount: trend.amount, + period: DbUtil.toTrend(trend.period_change, trend.period_change_pct), + total: DbUtil.toTrend(trend.total_change, trend.total_change_pct), + })), + } + } + + async getValuation(id: Valuation['id']) { + return await this.prisma.valuation.findUniqueOrThrow({ + where: { id }, + include: { account: true }, + }) + } + + async createValuation(data: Prisma.ValuationUncheckedCreateInput) { + return await this.prisma.valuation.create({ data }) + } + + async updateValuation( + id: Valuation['id'], + data: { date?: Date; amount?: Prisma.Decimal | number } + ) { + return await this.prisma.valuation.update({ + where: { id }, + data, + }) + } + + async deleteValuation(id: Valuation['id']) { + return await this.prisma.valuation.delete({ where: { id } }) + } +} diff --git a/libs/server/features/tsconfig.json b/libs/server/features/tsconfig.json new file mode 100644 index 00000000000..bab70e20f1b --- /dev/null +++ b/libs/server/features/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + "noImplicitAny": false, + "strict": true, + "strictNullChecks": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/server/features/tsconfig.lib.json b/libs/server/features/tsconfig.lib.json new file mode 100644 index 00000000000..e0d2517ec20 --- /dev/null +++ b/libs/server/features/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/server/features/tsconfig.spec.json b/libs/server/features/tsconfig.spec.json new file mode 100644 index 00000000000..b6ee8c9ce47 --- /dev/null +++ b/libs/server/features/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/libs/server/shared/.babelrc b/libs/server/shared/.babelrc new file mode 100644 index 00000000000..4496e8f26c8 --- /dev/null +++ b/libs/server/shared/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/server/shared/.eslintrc.json b/libs/server/shared/.eslintrc.json new file mode 100644 index 00000000000..9761c563892 --- /dev/null +++ b/libs/server/shared/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/libs/server/shared/README.md b/libs/server/shared/README.md new file mode 100644 index 00000000000..07e01a14ef4 --- /dev/null +++ b/libs/server/shared/README.md @@ -0,0 +1,7 @@ +# server-shared + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test server-shared` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/server/shared/jest.config.ts b/libs/server/shared/jest.config.ts new file mode 100644 index 00000000000..e1b4ced779f --- /dev/null +++ b/libs/server/shared/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'server-shared', + preset: '../../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../../coverage/libs/server/shared', +} diff --git a/libs/server/shared/src/endpoint.ts b/libs/server/shared/src/endpoint.ts new file mode 100644 index 00000000000..4b6542eb884 --- /dev/null +++ b/libs/server/shared/src/endpoint.ts @@ -0,0 +1,72 @@ +import type { RequestHandler, Request, Response } from 'express' +import { z } from 'zod' + +type EndpointSchema = { + parse: (input: any) => TInput +} + +type EndpointResolverArgs = { + input: TInput + ctx: TContext + req: Request +} + +type EndpointOnSuccess = (req: Request, res: Response, output: TOutput) => any + +type EndpointWithInput = { + input: EndpointSchema + authorize?(args: EndpointResolverArgs): boolean | Promise + resolve(args: EndpointResolverArgs): Promise + onSuccess?: EndpointOnSuccess +} + +type EndpointWithoutInput = Omit< + EndpointWithInput, + 'input' +> + +export class EndpointFactory { + constructor( + private readonly options: { + createContext: (req: Request, res: Response) => TContext | Promise + onSuccess?: EndpointOnSuccess + } + ) {} + + create(opts: EndpointWithInput): RequestHandler + create(opts: EndpointWithoutInput): RequestHandler + create( + opts: EndpointWithInput | EndpointWithoutInput + ): RequestHandler { + const { authorize, resolve } = opts + const input = 'input' in opts ? opts.input : undefined + + return async (req, res, next) => { + let inputData: TInput | undefined + try { + inputData = input?.parse({ ...req.query, ...req.body }) + } catch (err) { + console.error('input parse error', err) + if (err instanceof z.ZodError) { + return res.status(400).json({ errors: err.format() }) + } + } + + const ctx = await this.options.createContext(req, res) + + if (authorize && !(await authorize({ input: inputData as any, ctx, req }))) { + return res.status(401).send('Unauthorized') + } + + resolve({ input: inputData as any, ctx, req }) + .then((output) => + opts.onSuccess + ? opts.onSuccess(req, res, output) + : this.options.onSuccess + ? this.options.onSuccess(req, res, output) + : res.status(200).json(output) + ) + .catch(next) + } + } +} diff --git a/libs/server/shared/src/etl.ts b/libs/server/shared/src/etl.ts new file mode 100644 index 00000000000..f9e788bfca7 --- /dev/null +++ b/libs/server/shared/src/etl.ts @@ -0,0 +1,14 @@ +export interface IETL { + extract(input: TInput): Promise + transform(input: TInput, extracted: TExtracted): Promise + load(input: TInput, transformed: TTransformed): Promise +} + +export async function etl( + service: IETL, + input: TInput +): Promise { + const extracted = await service.extract(input) + const transformed = await service.transform(input, extracted) + await service.load(input, transformed) +} diff --git a/libs/server/shared/src/index.ts b/libs/server/shared/src/index.ts new file mode 100644 index 00000000000..b6c88406467 --- /dev/null +++ b/libs/server/shared/src/index.ts @@ -0,0 +1,6 @@ +export * from './services' +export * from './endpoint' +export * from './etl' +export * from './logger' +export * from './utils' +export { default as sql, raw, join } from './sql-template-tag' diff --git a/libs/server/shared/src/logger.ts b/libs/server/shared/src/logger.ts new file mode 100644 index 00000000000..2a856083232 --- /dev/null +++ b/libs/server/shared/src/logger.ts @@ -0,0 +1,25 @@ +import type { LoggerOptions, Logger } from 'winston' +import type Transport from 'winston-transport' + +import { createLogger as _createLogger, transports as _transports, format } from 'winston' + +export const createLogger = (opts?: LoggerOptions): Logger => { + const defaultTransport: Transport = new _transports.Console({ + format: + process.env.NODE_ENV === 'production' + ? format.json() + : format.combine(format.colorize(), format.simple()), + handleExceptions: true, + }) + + const transports = !opts?.transports + ? defaultTransport + : Array.isArray(opts.transports) + ? [defaultTransport, ...opts.transports] + : [defaultTransport, opts.transports] + + return _createLogger({ + ...opts, + transports, + }) +} diff --git a/libs/server/shared/src/services/cache.service.ts b/libs/server/shared/src/services/cache.service.ts new file mode 100644 index 00000000000..94bb3d25164 --- /dev/null +++ b/libs/server/shared/src/services/cache.service.ts @@ -0,0 +1,70 @@ +import type { Logger } from 'winston' +import { DateTime, Duration } from 'luxon' +import type Redis from 'ioredis' +import { superjson } from '@maybe-finance/shared' + +interface ICacheBackend { + getItem(key: string): Promise + setItem(key: string, value: TValue, exp: Duration): Promise +} + +export class MemoryCacheBackend implements ICacheBackend { + constructor(private readonly cache: Record = {}) {} + + async getItem(key: string): Promise { + const item = this.cache[key] + if (item == null) return null + return item.exp.diffNow() >= Duration.fromMillis(0) ? item.value : null + } + + async setItem(key: string, value: TValue, exp: Duration): Promise { + this.cache[key] = { value, exp: DateTime.now().plus(exp) } + } +} + +export class RedisCacheBackend implements ICacheBackend { + constructor(private readonly redis: Redis) {} + + async getItem(key: string): Promise { + const rawValue = await this.redis.get(this.key(key)) + return rawValue == null ? null : superjson.parse(rawValue) + } + + async setItem(key: string, value: TValue, exp: Duration): Promise { + await this.redis.setex(this.key(key), exp.as('seconds'), superjson.stringify(value)) + } + + private key(key: string) { + return `cache:${key}` + } +} + +export class CacheService { + constructor( + private readonly logger: Logger, + private readonly cache: ICacheBackend, + private readonly defaultExpiration = Duration.fromObject({ minutes: 15 }) + ) {} + + async getOrAdd( + key: K, + valueFn: V | ((_key: K) => Promise), + exp?: Duration + ): Promise { + // first check for non-expired cached value + const existingValue = await this.cache.getItem(key) + if (existingValue) { + this.logger.debug(`HIT k="${key}"`) + return existingValue + } + + this.logger.debug(`MISS k="${key}"`) + + // compute value to cache + const newValue = typeof valueFn === 'function' ? await (valueFn as any)(key) : valueFn + await this.cache.setItem(key, newValue, exp || this.defaultExpiration) + this.logger.debug(`SET k="${key}"`) + + return newValue + } +} diff --git a/libs/server/shared/src/services/crypto.service.ts b/libs/server/shared/src/services/crypto.service.ts new file mode 100644 index 00000000000..9a865325220 --- /dev/null +++ b/libs/server/shared/src/services/crypto.service.ts @@ -0,0 +1,18 @@ +import CryptoJS from 'crypto-js' + +export interface ICryptoService { + encrypt(plainText: string): string + decrypt(encrypted: string): string +} + +export class CryptoService implements ICryptoService { + constructor(private readonly secret: string) {} + + encrypt(plainText: string) { + return CryptoJS.AES.encrypt(plainText, this.secret).toString() + } + + decrypt(encrypted: string) { + return CryptoJS.AES.decrypt(encrypted, this.secret).toString(CryptoJS.enc.Utf8) + } +} diff --git a/libs/server/shared/src/services/feature-flag.service.ts b/libs/server/shared/src/services/feature-flag.service.ts new file mode 100644 index 00000000000..1d5b976a3fd --- /dev/null +++ b/libs/server/shared/src/services/feature-flag.service.ts @@ -0,0 +1,25 @@ +import type { LDClient, LDUser } from 'launchdarkly-node-server-sdk' + +export interface IFeatureFlagService { + getFlag(flagKey: string, defaultValue: TValue, user?: LDUser): Promise +} + +export class LaunchDarklyFeatureFlagService implements IFeatureFlagService { + constructor(private readonly ldClient: LDClient) {} + + async getFlag( + flagKey: string, + defaultValue: TValue, + user?: LDUser + ): Promise { + if (!this.ldClient) return defaultValue + + await this.ldClient.waitForInitialization() + + return await this.ldClient.variation( + flagKey, + user ?? { key: 'anonymous-server', anonymous: true }, + defaultValue + ) + } +} diff --git a/libs/server/shared/src/services/index.ts b/libs/server/shared/src/services/index.ts new file mode 100644 index 00000000000..dda9ffa3042 --- /dev/null +++ b/libs/server/shared/src/services/index.ts @@ -0,0 +1,8 @@ +export * from './crypto.service' +export * from './feature-flag.service' +export * from './queue.service' +export * from './queue' +export * from './cache.service' +export * from './market-data.service' +export * from './pg.service' +export * from './s3.service' diff --git a/libs/server/shared/src/services/market-data.service.spec.ts b/libs/server/shared/src/services/market-data.service.spec.ts new file mode 100644 index 00000000000..764b8b66fa1 --- /dev/null +++ b/libs/server/shared/src/services/market-data.service.spec.ts @@ -0,0 +1,21 @@ +import { getPolygonTicker } from './market-data.service' + +describe('PolygonMarketDataService', () => { + it.each` + symbol | plaidType | ticker + ${'AAPL'} | ${'equity'} | ${{ market: 'stocks', ticker: 'AAPL' }} + ${'AAPL'} | ${null} | ${{ market: 'stocks', ticker: 'AAPL' }} + ${'AAPL220909C00070000'} | ${'derivative'} | ${{ market: 'options', ticker: 'O:AAPL220909C00070000' }} + ${'AAPL220909C00070000'} | ${null} | ${{ market: 'options', ticker: 'O:AAPL220909C00070000' }} + ${'BTC'} | ${'cryptocurrency'} | ${{ market: 'crypto', ticker: 'X:BTCUSD' }} + ${'CUR:BTC'} | ${'cash'} | ${{ market: 'crypto', ticker: 'X:BTCUSD' }} + ${'CUR:USD'} | ${'cash'} | ${null} + ${'USD'} | ${'cash'} | ${null} + ${'EUR'} | ${'cash'} | ${{ market: 'fx', ticker: 'C:EURUSD' }} + `( + 'properly parses security symbol: $symbol plaidType: $plaidType', + ({ symbol, plaidType, ticker }) => { + expect(getPolygonTicker({ symbol, plaidType, currencyCode: 'USD' })).toEqual(ticker) + } + ) +}) diff --git a/libs/server/shared/src/services/market-data.service.ts b/libs/server/shared/src/services/market-data.service.ts new file mode 100644 index 00000000000..41a095dc431 --- /dev/null +++ b/libs/server/shared/src/services/market-data.service.ts @@ -0,0 +1,491 @@ +import { Prisma } from '@prisma/client' +import type { Security } from '@prisma/client' +import _ from 'lodash' +import { DateTime, Duration } from 'luxon' +import type { Logger } from 'winston' +import type { IRestClient } from '@polygon.io/client-js' +import { restClient } from '@polygon.io/client-js' +import type { SharedType } from '@maybe-finance/shared' +import { MarketUtil, SharedUtil } from '@maybe-finance/shared' +import type { CacheService } from '.' +import { toDecimal } from '../utils/db-utils' + +type DailyPricing = { + date: DateTime + priceClose: Prisma.Decimal +} + +type LivePricing = { + security: TSecurity + pricing: { + ticker: string + price: Prisma.Decimal + change: Prisma.Decimal + changePct: Prisma.Decimal + updatedAt: DateTime + } | null +} + +type OptionDetails = { + sharesPerContract: number | undefined +} + +export interface IMarketDataService { + /** + * internal identifier for this market data source + */ + get source(): string + + /** + * fetches pricing info for inclusive date range + */ + getDailyPricing>( + security: TSecurity, + start: DateTime, + end: DateTime + ): Promise + + /** + * fetches up-to-date pricing info for a batch of securities + */ + getLivePricing< + TSecurity extends Pick + >( + securities: TSecurity[] + ): Promise[]> + + /** + * fetches options contract details + */ + getOptionDetails(symbol: Security['symbol']): Promise + + getSecurityDetails( + security: Pick + ): Promise +} + +export class PolygonMarketDataService implements IMarketDataService { + private readonly api: IRestClient + + readonly source = 'polygon' + + constructor( + private readonly logger: Logger, + apiKey: string, + private readonly cache: CacheService + ) { + this.api = restClient(apiKey) + } + + async getDailyPricing< + TSecurity extends Pick + >(security: TSecurity, start: DateTime, end: DateTime): Promise { + const ticker = getPolygonTicker(security) + if (!ticker) return [] + + /** + * https://polygon.io/docs/stocks/get_v2_aggs_ticker__stocksticker__range__multiplier___timespan___from___to + */ + const res = await this.api.stocks.aggregates( + ticker.ticker, + 1, + 'day', + start.toISODate(), + end.toISODate() + ) + + return ( + res.results + ?.filter(({ t, c }) => t != null && c != null) + .map(({ t, c }) => ({ + date: DateTime.fromMillis(t!, { zone: 'America/New_York' }), + priceClose: new Prisma.Decimal(c!), + })) ?? [] + ) + } + + async getLivePricing< + TSecurity extends Pick + >(securities: TSecurity[]): Promise[]> { + const securitiesWithTicker = securities.map((security) => ({ + security, + ticker: getPolygonTicker(security), + })) + + const tickers = _(securitiesWithTicker) + .map((s) => s.ticker) + .filter(SharedUtil.nonNull) + .uniqBy((t) => t.ticker) + .sortBy((t) => [t.market, t.ticker]) + .value() + + const stockTickers = tickers.filter((t) => t.market === 'stocks').map((s) => s.ticker) + const optionTickers = tickers.filter((t) => t.market === 'options').map((o) => o.ticker) + const cryptoTickers = tickers.filter((t) => t.market === 'crypto').map((c) => c.ticker) + + const [stocksSnapshot, optionsSnapshot, cryptoSnapshot] = await Promise.all([ + stockTickers.length > 0 + ? this.cache.getOrAdd( + `live-pricing[${stockTickers.join(',')}]`, + () => this._snapshotStocks(stockTickers), + Duration.fromObject({ minutes: 2 }) + ) + : null, + optionTickers.length > 0 + ? Promise.allSettled( + optionTickers.map((optionTicker) => + this.cache + .getOrAdd( + `live-pricing[${optionTicker}]`, + () => this._snapshotOption(optionTicker), + Duration.fromObject({ minutes: 2 }) + ) + .catch((err) => { + this.logger.warn( + `failed to get option snapshot for ${optionTicker}: ${err}` + ) + return null + }) + ) + ).then((results) => + results + .filter(SharedUtil.isFullfilled) + .map((r) => r.value) + .filter(SharedUtil.nonNull) + ) + : null, + cryptoTickers.length > 0 + ? this.cache.getOrAdd( + `live-pricing[${cryptoTickers.join(',')}]`, + () => this._snapshotCrypto(cryptoTickers), + Duration.fromObject({ minutes: 2 }) + ) + : null, + ]) + + return securitiesWithTicker.map(({ security, ticker }) => { + if (!ticker) { + return { security, pricing: null } + } + + const snapshot = + ticker.market === 'stocks' + ? stocksSnapshot?.find((s) => s.ticker === ticker.ticker) + : ticker.market === 'options' + ? optionsSnapshot?.find((o) => o.ticker === ticker.ticker) + : ticker.market === 'crypto' + ? cryptoSnapshot?.find((c) => c.ticker === ticker.ticker) + : null + + return { security, pricing: snapshot?.pricing ?? null } + }) + } + + async getOptionDetails(symbol: Security['symbol']): Promise { + const ticker = getPolygonTicker({ + symbol, + plaidType: 'derivative', + currencyCode: 'USD', + }) + + if (!ticker) { + return { sharesPerContract: 100 } + } + + const contractResponse = await this.api.reference.optionsContract(ticker.ticker) + + return { + // https://github.com/polygon-io/client-js/issues/95 + sharesPerContract: (contractResponse.results as any)?.shares_per_contract, + } + } + + async getSecurityDetails(security: Pick) { + const ticker = getPolygonTicker(security) + if (!ticker || ticker.market === 'options') { + return {} + } + + const now = DateTime.now() + const oneYearAgo = DateTime.now().minus({ weeks: 52 }) + + try { + const [snapshot, yearAggregate, details, financials, dividends] = await Promise.all([ + this.cache.getOrAdd( + `ticker-snapshot[${ticker}]`, + () => this.api.stocks.snapshotTicker(ticker.ticker), + Duration.fromObject({ minutes: 2 }) + ), + + this.cache.getOrAdd( + `ticker-year-aggregate[${ticker}]`, + () => + this.api.stocks.aggregates( + ticker.ticker, + 1, + 'year', + oneYearAgo.toFormat('yyyy-MM-dd'), + now.toFormat('yyyy-MM-dd') + ), + Duration.fromObject({ minutes: 2 }) + ), + + this.cache.getOrAdd( + `ticker-details[${ticker}]`, + () => this.api.reference.tickerDetails(ticker.ticker), + Duration.fromObject({ hours: 12 }) + ), + + this.cache.getOrAdd( + `ticker-financials[${ticker}]`, + () => this.api.reference.stockFinancials({ ticker: ticker.ticker }), + Duration.fromObject({ minutes: 1 }) + ), + + this.cache.getOrAdd( + `ticker-dividends[${ticker}]`, + () => + this.api.reference.dividends({ + ticker: ticker.ticker, + 'pay_date.gt': oneYearAgo.toFormat('yyyy-MM-dd'), + 'pay_date.lte': now.toFormat('yyyy-MM-dd'), + }), + Duration.fromObject({ minutes: 1 }) + ), + ]) + + return { + day: { + open: toDecimal(snapshot.ticker?.day?.o) ?? undefined, + prevClose: toDecimal(snapshot.ticker?.prevDay?.c) ?? undefined, + high: toDecimal(snapshot.ticker?.day?.h) ?? undefined, + low: toDecimal(snapshot.ticker?.day?.l) ?? undefined, + }, + year: { + high: toDecimal(yearAggregate.results?.[0].h) ?? undefined, + low: toDecimal(yearAggregate.results?.[0].l) ?? undefined, + volume: toDecimal(yearAggregate.results?.[0].v) ?? undefined, + dividends: + toDecimal( + dividends.results?.reduce((sum, d) => sum + (d.cash_amount ?? 0), 0) + ) ?? undefined, + }, + marketCap: toDecimal(details.results?.market_cap) ?? undefined, + eps: + toDecimal( + financials.results?.[0]?.financials?.income_statement + ?.basic_earnings_per_share.value + ) ?? undefined, + } + } catch (e) { + this.logger.warn(`Failed to get security details for ${ticker}: ${e}`) + } + + return {} + } + + private async _snapshotStocks(tickers: string[]) { + /** + * https://polygon.io/docs/stocks/get_v2_snapshot_locale_us_markets_stocks_tickers + */ + const res = await this.api.stocks.snapshotAllTickers({ + tickers: tickers.join(','), + }) + + const snapshots = res.tickers ?? [] + + // ENG-601: alert us if polygon returns any prices as 0 + const emptySnapshots = + snapshots.filter((t) => !t.updated && !t.lastTrade?.t && !t.lastQuote?.t) ?? [] + + if (emptySnapshots.length > 0) { + this.logger.error( + `polygon snapshot empty tickers: ${emptySnapshots.map((t) => t.ticker).join(',')}` + ) + } + + return snapshots.map((snapshot) => { + // extract the (p)rice + (t)ime to use for the live price + // if the (t)ime is 0, we're dealing with an empty/zero snapshot from Polygon + // + // the order of priority for pricing that we use is: + // + // 1. The `day` snapshot object from Polygon (OHLCV) + // 2. The `lastTrade` + // 3. The `lastQuote` - we calculate the midpoint + const [p, t] = + // it's possible for a snapshot to have a valid `updated` time but + // a zeroed out `snapshot.day` object which is why we check for both here + snapshot.updated && snapshot.day && Object.values(snapshot.day).some((v) => v > 0) + ? [snapshot.day.c, snapshot.updated] + : snapshot.lastTrade && snapshot.lastTrade.t + ? [snapshot.lastTrade.p, snapshot.lastTrade.t] + : snapshot.lastQuote && snapshot.lastQuote.t + ? [_.mean([snapshot.lastQuote.P, snapshot.lastQuote.p]), snapshot.lastQuote.t] + : [null, null] + + return { + ticker: snapshot.ticker, + pricing: + t && + snapshot.ticker && + snapshot.todaysChange != null && + snapshot.todaysChangePerc != null + ? { + ticker: snapshot.ticker, + price: new Prisma.Decimal(p!), + change: new Prisma.Decimal(snapshot.todaysChange), + changePct: new Prisma.Decimal(snapshot.todaysChangePerc), + updatedAt: DateTime.fromMillis(t / 1e6, { + zone: 'America/New_York', + }), + } + : null, + } + }) + } + + private async _snapshotOption(ticker: string) { + // https://polygon.io/docs/options/get_v3_reference_options_contracts__options_ticker + const underlyingTicker = + MarketUtil.getUnderlyingTicker(ticker) ?? + (await this.api.reference + .optionsContract(ticker) + .then(({ results: oc }) => oc?.underlying_ticker) + .catch(() => null)) + + if (!underlyingTicker) return null + + const { results: snapshot } = await this.api.options.snapshotOptionContract( + underlyingTicker, + ticker + ) + + return { + ticker, + pricing: + snapshot?.day?.close != null && + snapshot.day.change != null && + snapshot.day.change_percent != null && + snapshot.day.last_updated != null + ? { + ticker: ticker, + price: new Prisma.Decimal(snapshot.day.close), + change: new Prisma.Decimal(snapshot.day.change), + changePct: new Prisma.Decimal(snapshot.day.change_percent), + updatedAt: DateTime.fromMillis(snapshot.day.last_updated / 1e6, { + zone: 'America/New_York', + }), + } + : null, + } + } + + private async _snapshotCrypto(tickers: string[]) { + /** + * https://polygon.io/docs/crypto/get_v2_snapshot_locale_global_markets_crypto_tickers + */ + const res = await this.api.crypto.snapshotAllTickers({ + tickers: tickers.join(','), + }) + + const snapshots = res.tickers ?? [] + + // ENG-601: alert us if polygon returns any prices as 0 + const emptySnapshots = snapshots.filter((t) => !t.updated && !t.lastTrade?.t) + + if (emptySnapshots.length > 0) { + this.logger.error( + `polygon snapshot empty tickers: ${emptySnapshots.map((t) => t.ticker).join(',')}` + ) + } + + return snapshots.map((snapshot) => { + // extract the (p)rice + (t)ime to use for the live price + // if the (t)ime is 0, we're dealing with an empty/zero snapshot from Polygon + // + // the order of priority for pricing that we use is: + // + // 1. The `day` snapshot object from Polygon (OHLCV) + // 2. The `lastTrade` + // 3. The `lastQuote` - we calculate the midpoint + const [p, t] = + // it's possible for a snapshot to have a valid `updated` time but + // a zeroed out `snapshot.day` object which is why we check for both here + snapshot.updated && snapshot.day && Object.values(snapshot.day).some((v) => v > 0) + ? [snapshot.day.c, snapshot.updated] + : snapshot.lastTrade && snapshot.lastTrade.t + ? [snapshot.lastTrade.p, snapshot.lastTrade.t] + : [null, null] + + return { + ticker: snapshot.ticker, + pricing: + t && + snapshot.ticker && + snapshot.todaysChange != null && + snapshot.todaysChangePerc != null + ? { + ticker: snapshot.ticker, + price: new Prisma.Decimal(p!), + change: new Prisma.Decimal(snapshot.todaysChange), + changePct: new Prisma.Decimal(snapshot.todaysChangePerc), + updatedAt: DateTime.fromMillis(t / 1e6, { + zone: 'America/New_York', + }), + } + : null, + } + }) + } +} + +class PolygonTicker { + constructor(readonly market: 'stocks' | 'options' | 'fx' | 'crypto', readonly ticker: string) {} + + get key() { + return `${this.market}|${this.ticker}` + } + + /** override so this object can be used directly in string interpolation for cache keys */ + toString() { + return this.key + } +} + +export function getPolygonTicker({ + symbol, + plaidType, + currencyCode, +}: Pick): PolygonTicker | null { + if (!symbol) return null + + // https://plaid.com/docs/api/products/investments/#investments-holdings-get-response-securities-type + switch (plaidType) { + case 'derivative': { + return new PolygonTicker('options', `O:${symbol}`) + } + case 'cryptocurrency': { + return new PolygonTicker('crypto', `X:${symbol}${currencyCode}`) + } + case 'cash': { + // Plaid used to prefix `ticker_symbol` with "CUR:" for crypto securities so this check is for handling that legacy scenario + if (symbol.startsWith('CUR:')) { + return symbol.slice(4) === currencyCode + ? null // if the symbol matches the currencyCode then we're just dealing with a basic cash holding + : new PolygonTicker('crypto', `X:${symbol.slice(4)}${currencyCode}`) + } + + return symbol === currencyCode + ? null // if the symbol matches the currencyCode then we're just dealing with a basic cash holding + : new PolygonTicker('fx', `C:${symbol}${currencyCode}`) + } + } + + // Finicity's `type` field isn't really helpful here, so we'll just use isOptionTicker + if (MarketUtil.isOptionTicker(symbol)) { + return new PolygonTicker('options', `O:${symbol}`) + } + + return new PolygonTicker('stocks', symbol) +} diff --git a/libs/server/shared/src/services/pg.service.ts b/libs/server/shared/src/services/pg.service.ts new file mode 100644 index 00000000000..a8e5cd26e7f --- /dev/null +++ b/libs/server/shared/src/services/pg.service.ts @@ -0,0 +1,34 @@ +import { Prisma } from '@prisma/client' +import { Pool, types } from 'pg' +import type { Logger } from 'winston' + +// convert date to string +types.setTypeParser(types.builtins.DATE, (val) => val) + +// convert bigint +types.setTypeParser(types.builtins.INT8, (val) => { + return val == null ? null : BigInt(val) +}) + +// convert numeric to Decimal.js +types.setTypeParser(types.builtins.NUMERIC, (val) => { + return val == null ? null : new Prisma.Decimal(val) +}) + +export class PgService { + private readonly _pool: Pool + + constructor(private readonly logger: Logger, databaseUrl = process.env.NX_DATABASE_URL) { + this._pool = new Pool({ + connectionString: databaseUrl, + }) + + this._pool.on('error', (err, _client) => { + console.error('[pg.error]', err) + }) + } + + get pool() { + return this._pool + } +} diff --git a/libs/server/shared/src/services/queue.service.ts b/libs/server/shared/src/services/queue.service.ts new file mode 100644 index 00000000000..a28da82d320 --- /dev/null +++ b/libs/server/shared/src/services/queue.service.ts @@ -0,0 +1,138 @@ +import type { AccountConnection, User, Account } from '@prisma/client' +import type { Logger } from 'winston' +import type { Job, JobOptions } from 'bull' +import type { SharedType } from '@maybe-finance/shared' + +export type IJob = Pick, 'id' | 'name' | 'data' | 'progress'> + +export type { JobOptions } + +export type IQueue = {}, TJobName extends string = string> = { + name: string + isHealthy(): Promise + add(name: TJobName, data: TData, options?: JobOptions): Promise> + addBulk( + jobs: { name: TJobName; data: TData; options?: JobOptions | undefined }[] + ): Promise[]> + process( + name: TJobName, + callback: (job: IJob) => Promise, + options?: { concurrency: number } + ): Promise + getActiveJobs(): Promise[]> + cancelJobs(): Promise +} + +export type SyncUserOptions = {} +export type SyncUserQueueJobData = { + userId: User['id'] + options?: SyncUserOptions +} + +export type SyncAccountOptions = {} +export type SyncAccountQueueJobData = { + accountId: Account['id'] + options?: SyncAccountOptions +} + +export type SyncConnectionOptions = + | { + type: 'plaid' + products?: Array<'transactions' | 'investment-transactions' | 'holdings' | 'liabilities'> + } + | { type: 'finicity'; initialSync?: boolean } + +export type SyncConnectionQueueJobData = { + accountConnectionId: AccountConnection['id'] + options?: SyncConnectionOptions +} + +export type SyncSecurityQueueJobData = {} + +export type SendEmailQueueJobData = + | { + type: 'trial-reminders' + } + | { + type: 'plain' + messages: SharedType.PlainEmailMessage | SharedType.PlainEmailMessage[] + } + | { + type: 'template' + messages: SharedType.TemplateEmailMessage | SharedType.TemplateEmailMessage[] + } + | { + type: 'conversation-notification' + notification: SharedType.ConversationNotification + } + | { + type: 'conversation-expirations' + } + +export type SyncUserQueue = IQueue +export type SyncAccountQueue = IQueue +export type SyncConnectionQueue = IQueue +export type SyncSecurityQueue = IQueue +export type PurgeUserQueue = IQueue<{ userId: User['id'] }, 'purge-user'> +export type SyncInstitutionQueue = IQueue< + {}, + 'sync-finicity-institutions' | 'sync-plaid-institutions' +> +export type SendEmailQueue = IQueue + +export type QueueName = + | 'sync-user' + | 'sync-account-connection' + | 'sync-account' + | 'purge-user' + | 'sync-security' + | 'sync-institution' + | 'send-email' + +export interface IQueueFactory { + createQueue(name: 'sync-user'): SyncUserQueue + createQueue(name: 'sync-account'): SyncAccountQueue + createQueue(name: 'sync-account-connection'): SyncConnectionQueue + createQueue(name: 'sync-security'): SyncSecurityQueue + createQueue(name: 'purge-user'): PurgeUserQueue + createQueue(name: 'sync-institution'): SyncInstitutionQueue + createQueue(name: 'send-email'): SendEmailQueue + createQueue(name: QueueName): IQueue +} + +export class QueueService { + private readonly queues: Record> = {} + + constructor(private readonly logger: Logger, private readonly queueFactory: IQueueFactory) { + this.createQueue('sync-user') + this.createQueue('sync-account') + this.createQueue('sync-account-connection') + this.createQueue('sync-security') + this.createQueue('purge-user') + this.createQueue('sync-institution') + this.createQueue('send-email') + } + + get allQueues() { + return Object.values(this.queues) + } + + getQueue(name: 'sync-user'): SyncUserQueue + getQueue(name: 'sync-account'): SyncAccountQueue + getQueue(name: 'sync-account-connection'): SyncConnectionQueue + getQueue(name: 'sync-security'): SyncSecurityQueue + getQueue(name: 'purge-user'): PurgeUserQueue + getQueue(name: 'sync-institution'): SyncInstitutionQueue + getQueue(name: 'send-email'): SendEmailQueue + getQueue = any>(name: QueueName): IQueue { + return this.queues[name] ?? this.createQueue(name) + } + + async cancelAllJobs() { + await Promise.allSettled(this.allQueues.map((q) => q.cancelJobs())) + } + + private createQueue(name: QueueName) { + return (this.queues[name] = this.queueFactory.createQueue(name)) + } +} diff --git a/libs/server/shared/src/services/queue/bull-queue.ts b/libs/server/shared/src/services/queue/bull-queue.ts new file mode 100644 index 00000000000..3fcd51949c9 --- /dev/null +++ b/libs/server/shared/src/services/queue/bull-queue.ts @@ -0,0 +1,165 @@ +import Queue from 'bull' +import * as Sentry from '@sentry/node' +import type { Transaction } from '@sentry/types' +import type { Logger } from 'winston' +import type { IJob, IQueue, IQueueFactory, QueueName } from '../queue.service' + +const TRACE_ID_KEY = '__SENTRY_TRACE_ID__' +const PARENT_SPAN_ID_KEY = '__SENTRY_PARENT_SPAN_ID__' + +export class BullQueue = any, TJobName extends string = string> + implements IQueue +{ + constructor(readonly logger: Logger, readonly queue: Queue.Queue) {} + + get name() { + return this.queue.name + } + + async isHealthy() { + const isReady = await this.queue.isReady() + return isReady && this.queue.clients.every((cli) => cli.status === 'ready') + } + + async add(name: TJobName, data: TData, options?: Queue.JobOptions | undefined) { + const parentSpan = Sentry.getCurrentHub().getScope()?.getSpan() + const span = parentSpan?.startChild({ + op: `queue.send`, + description: `${this.name} send`, + tags: { + 'messaging.system': 'bull', + 'messaging.destination': this.name, + 'messaging.destination_kind': 'queue', + 'messaging.bull.job_name': name, + }, + }) + + try { + const job = await this.queue.add( + name, + { + ...data, + [TRACE_ID_KEY]: span?.traceId, + [PARENT_SPAN_ID_KEY]: span?.parentSpanId, + }, + options + ) + span?.setTag('messaging.message_id', job.id) + return job + } finally { + span?.finish() + } + } + + async addBulk(jobs: { name: TJobName; data: TData; options?: Queue.JobOptions | undefined }[]) { + const parentSpan = Sentry.getCurrentHub().getScope()?.getSpan() + + const spans = jobs.map((job) => + parentSpan?.startChild({ + op: `queue.send`, + description: `${this.name} send`, + tags: { + 'messaging.system': 'bull', + 'messaging.destination': this.name, + 'messaging.destination_kind': 'queue', + 'messaging.bull.job_name': job.name, + }, + }) + ) + + try { + const added = await this.queue.addBulk(jobs) + spans.forEach((span, idx) => span?.setTag('messaging.message_id', added[idx]?.id)) + return added + } finally { + spans.forEach((span) => span?.finish()) + } + } + + async process( + name: TJobName, + callback: (job: Queue.Job) => Promise, + options: { concurrency?: number } = {} + ) { + const { concurrency = 1 } = options + + return this.queue.process(name, concurrency, async (job) => { + let transaction: Transaction | null = null + + try { + // https://docs.sentry.io/platforms/javascript/performance/instrumentation/custom-instrumentation/ + transaction = Sentry.startTransaction({ + op: 'queue.process', + name: `${this.name} process`, + tags: { + 'messaging.system': 'bull', + 'messaging.operation': 'process', + 'messaging.destination': this.name, + 'messaging.destination_kind': 'queue', + 'messaging.message_id': job.id, + 'messaging.bull.job_name': job.name, + }, + traceId: job.data?.[TRACE_ID_KEY], + parentSpanId: job.data?.[PARENT_SPAN_ID_KEY], + }) + + Sentry.getCurrentHub().configureScope((scope) => scope.setSpan(transaction!)) + } catch (err) { + this.logger.error(`error starting sentry transaction`, err) + } + + try { + await callback(job) + } finally { + transaction?.finish() + } + }) + } + + async getActiveJobs() { + return this.queue.getActive() + } + + async cancelJobs() { + await this.queue.pause(true, true) + await this.queue.removeJobs('*') + const activeJobs = await this.queue.getActive() + await Promise.all( + activeJobs.map((job) => job.moveToFailed({ message: 'Force Remove' }, true)) + ) + await this.queue.removeJobs('*') + await this.queue.resume(true) + } + + on(event: 'active', callback: (job: IJob) => void): void + on(event: 'completed', callback: (job: IJob) => void): void + on(event: 'failed', callback: (job: IJob, error: Error) => void): void + on(event: 'error', callback: (error: Error) => void): void + on(event: string, callback: (...args: any[]) => void) { + return this.queue.on(event, callback) + } +} + +export interface IBullQueueEventHandler { + onQueueCreated(queue: BullQueue): void +} + +/** + * This service uses shared Bull queue connection, to avoid connection limit issues and easily share between apps and libs + * + * @see https://github.com/OptimalBits/bull/blob/HEAD/PATTERNS.md#reusing-redis-connections + */ +export class BullQueueFactory implements IQueueFactory { + constructor( + private readonly logger: Logger, + private readonly redisUrl: string, + private readonly eventHandler?: IBullQueueEventHandler + ) {} + + createQueue(name: QueueName) { + const logger = this.logger.child({ service: `BullQueue[${name}]` }) + const queue = new BullQueue(logger, new Queue(name, this.redisUrl)) + this.eventHandler?.onQueueCreated(queue) + return queue + } +} diff --git a/libs/server/shared/src/services/queue/in-memory-queue.ts b/libs/server/shared/src/services/queue/in-memory-queue.ts new file mode 100644 index 00000000000..4695611859b --- /dev/null +++ b/libs/server/shared/src/services/queue/in-memory-queue.ts @@ -0,0 +1,76 @@ +import type { F } from 'ts-toolbelt' +import type { IQueue, IJob, JobOptions, IQueueFactory, QueueName } from '../queue.service' + +/** + * This is a mock implementation of a queue used for testing only. + * @see BullQueue for a production implementation. + */ +export class InMemoryQueue< + TData extends Record = any, + TJobName extends string = string +> implements IQueue +{ + private readonly processFns: Record< + string, + F.Parameters['process']>[1] + > = {} + + constructor(readonly name: string, private readonly ignoreJobNames: string[] = []) {} + + async isHealthy() { + return true + } + + async add(name: TJobName, data: TData, _options?: JobOptions | undefined) { + const job: IJob = { + id: `${name}.${new Date().getTime()}.${Math.random()}`, + name, + data, + progress: () => Promise.resolve(), + } + + if (!this.ignoreJobNames.includes(name)) { + try { + // immediately run job + await this.processFns[name](job) + } catch (err) { + // ignore + } + } + + return job + } + + async addBulk(jobs: { name: TJobName; data: TData; options?: JobOptions | undefined }[]) { + return Promise.all(jobs.map((job) => this.add(job.name, job.data))) + } + + async process(name: TJobName, fn: (job: IJob) => Promise) { + this.processFns[name] = fn + } + + async getActiveJobs() { + return [] + } + + async cancelJobs() { + // no-op + } +} + +export class InMemoryQueueFactory implements IQueueFactory { + constructor( + private readonly ignoreJobNames: string[] = [ + 'sync-all-securities', + 'sync-plaid-institutions', + 'sync-finicity-institutions', + 'trial-reminders', + 'conversation-expirations', + 'send-email', + ] + ) {} + + createQueue(name: QueueName) { + return new InMemoryQueue(name, this.ignoreJobNames) + } +} diff --git a/libs/server/shared/src/services/queue/index.ts b/libs/server/shared/src/services/queue/index.ts new file mode 100644 index 00000000000..732cd35ca8f --- /dev/null +++ b/libs/server/shared/src/services/queue/index.ts @@ -0,0 +1,2 @@ +export * from './bull-queue' +export * from './in-memory-queue' diff --git a/libs/server/shared/src/services/s3.service.ts b/libs/server/shared/src/services/s3.service.ts new file mode 100644 index 00000000000..a6df8246282 --- /dev/null +++ b/libs/server/shared/src/services/s3.service.ts @@ -0,0 +1,62 @@ +import { + type S3Client, + type PutObjectCommandOutput, + type PutObjectCommandInput, + type DeleteObjectCommandInput, + type DeleteObjectCommandOutput, + PutObjectCommand, + DeleteObjectCommand, +} from '@aws-sdk/client-s3' + +import { v4 as uuid } from 'uuid' + +type UploadOptions = { + bucketKey: BucketKey + Key?: string +} & Omit + +type DeleteOptions = { + bucketKey: BucketKey + Key: string +} & Omit + +export interface IS3Service { + upload(opts?: UploadOptions): Promise + delete(opts: DeleteOptions): Promise +} + +export type BucketKey = 'public' | 'private' + +export class S3Service implements IS3Service { + constructor(readonly cli: S3Client, readonly buckets: Record) {} + + async upload({ bucketKey, Key = uuid(), ...rest }: UploadOptions) { + const uploadRes = await this.cli.send( + new PutObjectCommand({ + Bucket: this.buckets[bucketKey], + Key, + ...rest, + }) + ) + + return { + ...uploadRes, + Key, + } + } + + async delete({ bucketKey, Key, ...rest }: DeleteOptions) { + const deleteRes = await this.cli.send( + new DeleteObjectCommand({ + Bucket: this.buckets[bucketKey], + Key, + ...rest, + }) + ) + + return { + ...deleteRes, + Key, + } + } +} diff --git a/libs/server/shared/src/sql-template-tag.ts b/libs/server/shared/src/sql-template-tag.ts new file mode 100644 index 00000000000..afbe61ace2b --- /dev/null +++ b/libs/server/shared/src/sql-template-tag.ts @@ -0,0 +1,125 @@ +/** + * This is a copy of the sql-template-tag package, I wasn't able to use the npm package due to errors with nx/webpack and ESM modules + * https://github.com/blakeembrey/sql-template-tag + */ +export type Value = + | string + | number + | boolean + | Date + | null + | undefined + | Value[] + | { [key: string | number]: Value } + +export type RawValue = Value | Sql + +/** + * A SQL instance can be nested within each other to build SQL strings. + */ +export class Sql { + values: Value[] + strings: string[] + + constructor(rawStrings: ReadonlyArray, rawValues: ReadonlyArray) { + let valuesLength = rawValues.length + let stringsLength = rawStrings.length + + if (stringsLength === 0) { + throw new TypeError('Expected at least 1 string') + } + + if (stringsLength - 1 !== valuesLength) { + throw new TypeError( + `Expected ${stringsLength} strings to have ${stringsLength - 1} values` + ) + } + + for (const child of rawValues) { + if (child instanceof Sql) { + valuesLength += child.values.length - 1 + stringsLength += child.strings.length - 2 + } + } + + this.values = new Array(valuesLength) + this.strings = new Array(stringsLength) + + this.strings[0] = rawStrings[0] + + // Iterate over raw values, strings, and children. The value is always + // positioned between two strings, e.g. `index + 1`. + let index = 1 + let position = 0 + while (index < rawStrings.length) { + const child = rawValues[index - 1] + const rawString = rawStrings[index++] + + // Check for nested `sql` queries. + if (child instanceof Sql) { + // Append child prefix text to current string. + this.strings[position] += child.strings[0] + + let childIndex = 0 + while (childIndex < child.values.length) { + this.values[position++] = child.values[childIndex++] + this.strings[position] = child.strings[childIndex] + } + + // Append raw string to current string. + this.strings[position] += rawString + } else { + this.values[position++] = child + this.strings[position] = rawString + } + } + } + + get text() { + return this.strings.reduce((text, part, index) => `${text}$${index}${part}`) + } + + get sql() { + return this.strings.join('?') + } + + inspect() { + return { + text: this.text, + sql: this.sql, + values: this.values, + } + } +} + +/** + * Create a SQL query for a list of values. + */ +export function join(values: RawValue[], separator = ',') { + if (values.length === 0) { + throw new TypeError( + 'Expected `join([])` to be called with an array of multiple elements, but got an empty array' + ) + } + + return new Sql(['', ...Array(values.length - 1).fill(separator), ''], values) +} + +/** + * Create raw SQL statement. + */ +export function raw(value: string) { + return new Sql([value], []) +} + +/** + * Placeholder value for "no text". + */ +export const empty = raw('') + +/** + * Create a SQL object from a template string. + */ +export default function sql(strings: ReadonlyArray, ...values: RawValue[]) { + return new Sql(strings, values) +} diff --git a/libs/server/shared/src/utils/auth-utils.ts b/libs/server/shared/src/utils/auth-utils.ts new file mode 100644 index 00000000000..08d7492b416 --- /dev/null +++ b/libs/server/shared/src/utils/auth-utils.ts @@ -0,0 +1,60 @@ +import jwks from 'jwks-rsa' +import jwt from 'jsonwebtoken' +import { SharedType } from '@maybe-finance/shared' + +export const verifyRoleClaims = (claims, role: SharedType.UserRole) => { + const customRoleClaim = claims[SharedType.Auth0CustomNamespace.Roles] + + return customRoleClaim && Array.isArray(customRoleClaim) && customRoleClaim.includes(role) +} + +export async function validateRS256JWT( + token: string, + domain: string, + audience: string +): Promise<{ + auth0Id: string + userMetadata: SharedType.MaybeUserMetadata + appMetadata: SharedType.MaybeAppMetadata +}> { + const jwksClient = jwks({ + rateLimit: true, + jwksUri: `https://${domain}/.well-known/jwks.json`, + }) + + return new Promise((resolve, reject) => { + if (!token) reject('No token provided') + + const parts = token.split(' ') + + if (!parts || parts.length !== 2) reject('JWT must be in format: Bearer ') + if (parts[0] !== 'Bearer') reject('JWT must be in format: Bearer ') + + const rawToken = parts[1] + + jwt.verify( + rawToken, + (header, cb) => { + jwksClient + .getSigningKey(header.kid) + .then((key) => cb(null, key.getPublicKey())) + .catch((err) => cb(err)) + }, + { + audience, + issuer: `https://${domain}/`, + algorithms: ['RS256'], + }, + (err, payload) => { + if (err) return reject(err) + if (typeof payload !== 'object') return reject('payload not an object') + + resolve({ + auth0Id: payload.sub!, + appMetadata: payload[SharedType.Auth0CustomNamespace.AppMetadata] ?? {}, + userMetadata: payload[SharedType.Auth0CustomNamespace.UserMetadata] ?? {}, + }) + } + ) + }) +} diff --git a/libs/server/shared/src/utils/db-utils.ts b/libs/server/shared/src/utils/db-utils.ts new file mode 100644 index 00000000000..112c3b9adc4 --- /dev/null +++ b/libs/server/shared/src/utils/db-utils.ts @@ -0,0 +1,89 @@ +import type { SharedType } from '@maybe-finance/shared' +import { Prisma } from '@prisma/client' +import type { Logger } from 'winston' +import { NumberUtil, SharedUtil } from '@maybe-finance/shared' + +// prisma middleware that reports slow queries +export function slowQueryMiddleware(logger: Logger, cutoffDuration = 1_000): Prisma.Middleware { + return async (params, next) => { + const start = Date.now() + const res = await next(params) + const duration = Date.now() - start + + // log slow queries + if (duration > cutoffDuration) { + logger.warn( + `[SLOW_QUERY] ${params.model ? `${params.model}.` : ''}${ + params.action + } took ${duration}ms`, + { duration } + ) + + logger.debug(`[SLOW_QUERY] query`, params) + } + + return res + } +} + +/** + * converts a `TimeSeriesInterval` to a postgres interval literal + */ +export function toPgInterval(interval: SharedType.TimeSeriesInterval): string { + switch (interval) { + case 'days': + return '1 day' + case 'weeks': + return '1 week' + case 'months': + return '30 days' + case 'quarters': + return '91 days' + case 'years': + return '365 days' + default: + throw new Error(`invalid interval: ${interval}`) + } +} + +type NumberOrDecimal = Prisma.Decimal | number + +function getTrendDirection(_amount: NumberOrDecimal | null): SharedType.Trend['direction'] { + const amount = toDecimal(_amount) + if (!SharedUtil.nonNull(amount)) return 'flat' + return amount.lt(-0.01) ? 'down' : amount.gt(0.01) ? 'up' : 'flat' +} + +export function calculateTrend(_from: NumberOrDecimal, _to: NumberOrDecimal): SharedType.Trend { + const from = toDecimal(_from) + const to = toDecimal(_to) + + const amount = to.minus(from) + const percentage = NumberUtil.calculatePercentChange(from, to) + + return { + direction: getTrendDirection(amount), + amount, + percentage, + } +} + +export function toTrend( + _amount: NumberOrDecimal | null, + _percentage: NumberOrDecimal | null +): SharedType.Trend { + const amount = toDecimal(_amount) + const percentage = toDecimal(_percentage) + + return { + direction: getTrendDirection(amount), + amount, + percentage, + } +} + +export function toDecimal(x: NumberOrDecimal): Prisma.Decimal +export function toDecimal(x?: NumberOrDecimal | null): Prisma.Decimal | null +export function toDecimal(x?: NumberOrDecimal | null): Prisma.Decimal | null { + return x == null ? null : typeof x === 'number' ? new Prisma.Decimal(x).toDP(16) : x +} diff --git a/libs/server/shared/src/utils/error-utils.ts b/libs/server/shared/src/utils/error-utils.ts new file mode 100644 index 00000000000..3366b3fbee6 --- /dev/null +++ b/libs/server/shared/src/utils/error-utils.ts @@ -0,0 +1,118 @@ +import type { SharedType } from '@maybe-finance/shared' +import type { AxiosError } from 'axios' +import { Prisma } from '@prisma/client' +import axios from 'axios' + +type PrismaError = + | Prisma.PrismaClientKnownRequestError + | Prisma.PrismaClientUnknownRequestError + | Prisma.PrismaClientRustPanicError + | Prisma.PrismaClientInitializationError + | Prisma.PrismaClientValidationError + +// Current no simple `isPrismaError()` method provided, so we must check all Class interfaces +function isPrismaError(error: unknown): error is PrismaError { + return ( + error instanceof Prisma.PrismaClientKnownRequestError || + error instanceof Prisma.PrismaClientUnknownRequestError || + error instanceof Prisma.PrismaClientRustPanicError || + error instanceof Prisma.PrismaClientInitializationError || + error instanceof Prisma.PrismaClientValidationError + ) +} + +// https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates +// Checks for all the *required* attributes of a plaid error +export function isPlaidError(err: unknown): err is SharedType.AxiosPlaidError { + if (!err) return false + if (!axios.isAxiosError(err)) return false + if (typeof err.response?.data !== 'object') return false + + const { data } = err.response + return 'error_type' in data && 'error_code' in data && 'error_message' in data +} + +export function parseError(error: unknown): SharedType.ParsedError { + if (isPlaidError(error)) { + return parsePlaidError(error) + } + + if (axios.isAxiosError(error)) { + return parseAxiosError(error) + } + + if (isPrismaError(error)) { + return parsePrismaError(error) + } + + if (error instanceof Error) { + return parseJSError(error) + } + + if (typeof error === 'string') { + return { + message: error, + } + } + + if (typeof error === 'number') { + return { + message: error.toString(), + } + } + + return { + message: '[unknown-error] Unable to parse', + metadata: error, + } +} + +function parseAxiosError(error: AxiosError): SharedType.ParsedError { + return { + message: error.message, + statusCode: error.response ? error.response.status.toString() : '500', + metadata: error.response ? error.response.data : undefined, + stackTrace: error.stack, + sentryContexts: { + 'axios error': error.response?.data, + }, + } +} + +function parsePlaidError(error: SharedType.AxiosPlaidError): SharedType.ParsedError { + const { error_code, error_type, error_message, display_message, documentation_url } = + error.response.data + + return { + message: `[plaid-error] code=${error_code} type=${error_type} message=${error_message} display_message=${display_message}`, + statusCode: error.response.status.toString(), + metadata: error.response.data, + sentryTags: { + error_type, + error_code, + }, + sentryContexts: { + 'plaid error': { + error_type, + error_code, + error_message, + display_message, + documentation_url, + }, + }, + } +} + +function parseJSError(error: Error): SharedType.ParsedError { + return { + message: error.message, + stackTrace: error.stack, + } +} + +function parsePrismaError(error: PrismaError): SharedType.ParsedError { + return { + message: `[prisma-error] name=${error.name} message=${error.message}`, + stackTrace: error.stack, + } +} diff --git a/libs/server/shared/src/utils/finicity-utils.ts b/libs/server/shared/src/utils/finicity-utils.ts new file mode 100644 index 00000000000..cc25bdaeed2 --- /dev/null +++ b/libs/server/shared/src/utils/finicity-utils.ts @@ -0,0 +1,121 @@ +import type { Account, AccountCategory, AccountClassification, AccountType } from '@prisma/client' +import { Prisma } from '@prisma/client' +import { Duration } from 'luxon' +import type { FinicityTypes } from '@maybe-finance/finicity-api' + +type FinicityAccount = FinicityTypes.CustomerAccount + +/** + * Finicity delivers up to 180 days prior to account addition but doesn't provide a cutoff window + */ +export const FINICITY_WINDOW_MAX = Duration.fromObject({ years: 2 }) + +export function getType({ type }: Pick): AccountType { + switch (type) { + case 'investment': + case 'investmentTaxDeferred': + case 'brokerageAccount': + case '401k': + case '401a': + case '403b': + case '457': + case '457plan': + case '529': + case '529plan': + case 'ira': + case 'simpleIRA': + case 'sepIRA': + case 'roth': + case 'roth401k': + case 'rollover': + case 'ugma': + case 'utma': + case 'keogh': + case 'employeeStockPurchasePlan': + return 'INVESTMENT' + case 'creditCard': + return 'CREDIT' + case 'lineOfCredit': + case 'loan': + case 'studentLoan': + case 'studentLoanAccount': + case 'studentLoanGroup': + case 'mortgage': + return 'LOAN' + default: + return 'DEPOSITORY' + } +} + +export function getAccountCategory({ type }: Pick): AccountCategory { + switch (type) { + case 'checking': + case 'savings': + case 'cd': + case 'moneyMarket': + return 'cash' + case 'investment': + case 'investmentTaxDeferred': + case 'brokerageAccount': + case '401k': + case '401a': + case '403b': + case '457': + case '457plan': + case '529': + case '529plan': + case 'ira': + case 'simpleIRA': + case 'sepIRA': + case 'roth': + case 'roth401k': + case 'rollover': + case 'ugma': + case 'utma': + case 'keogh': + case 'employeeStockPurchasePlan': + return 'investment' + case 'mortgage': + case 'loan': + case 'lineOfCredit': + case 'studentLoan': + case 'studentLoanAccount': + case 'studentLoanGroup': + return 'loan' + case 'creditCard': + return 'credit' + case 'cryptocurrency': + return 'crypto' + default: + return 'other' + } +} + +export function getAccountBalanceData( + { balance, currency, detail }: Pick, + classification: AccountClassification +): Pick< + Account, + | 'currentBalanceProvider' + | 'currentBalanceStrategy' + | 'availableBalanceProvider' + | 'availableBalanceStrategy' + | 'currencyCode' +> { + // Flip balance values to positive for liabilities + const sign = classification === 'liability' ? -1 : 1 + + return { + currentBalanceProvider: new Prisma.Decimal(balance ? sign * balance : 0), + currentBalanceStrategy: 'current', + availableBalanceProvider: !detail + ? null + : detail.availableBalanceAmount != null + ? new Prisma.Decimal(sign * detail.availableBalanceAmount) + : detail.availableCashBalance != null + ? new Prisma.Decimal(sign * detail.availableCashBalance) + : null, + availableBalanceStrategy: 'available', + currencyCode: currency, + } +} diff --git a/libs/server/shared/src/utils/index.ts b/libs/server/shared/src/utils/index.ts new file mode 100644 index 00000000000..bad4dc185c8 --- /dev/null +++ b/libs/server/shared/src/utils/index.ts @@ -0,0 +1,8 @@ +export * as AuthUtil from './auth-utils' +export * as DbUtil from './db-utils' +export * as FinicityUtil from './finicity-utils' +export * as PlaidUtil from './plaid-utils' +export * as ErrorUtil from './error-utils' + +// All "generic" server utils grouped here +export * as ServerUtil from './server-utils' diff --git a/libs/server/shared/src/utils/plaid-utils.spec.ts b/libs/server/shared/src/utils/plaid-utils.spec.ts new file mode 100644 index 00000000000..a5d6b6fdbbb --- /dev/null +++ b/libs/server/shared/src/utils/plaid-utils.spec.ts @@ -0,0 +1,67 @@ +import { Prisma, type AccountBalanceStrategy } from '@prisma/client' +import { AccountType, AccountSubtype } from 'plaid' +import { isPlaidLiability, getAccountBalanceData } from './plaid-utils' + +const { Credit, Depository, Loan, Investment } = AccountType +const { CreditCard, Paypal, Student, Mortgage, _401k } = AccountSubtype + +describe('Plaid utils', () => { + it.each` + type | subtype | isLiability + ${Credit} | ${CreditCard} | ${true} + ${Credit} | ${Paypal} | ${true} + ${Loan} | ${Student} | ${true} + ${Loan} | ${Mortgage} | ${true} + ${Investment} | ${_401k} | ${false} + ${null} | ${null} | ${false} + `( + `should identify Plaid Liability: (type: $type, subtype: $subtype)`, + ({ type, subtype, isLiability }) => { + expect(isPlaidLiability(type, subtype)).toBe(isLiability) + } + ) + + it.each` + type | plaidCurrent | plaidAvailable | currentBalanceProvider | currentBalanceStrategy | availableBalanceProvider | availableBalanceStrategy + ${Depository} | ${10} | ${20} | ${10} | ${`current`} | ${20} | ${`available`} + ${Investment} | ${10} | ${10} | ${10} | ${`current`} | ${10} | ${`available`} + ${Investment} | ${10} | ${20} | ${10} | ${`sum`} | ${20} | ${`available`} + `( + `should derive account balances: (type: $type, plaidCurrent: $plaidCurrent, plaidAvailable: $plaidAvailable)`, + ({ + type, + plaidCurrent, + plaidAvailable, + currentBalanceProvider, + currentBalanceStrategy, + availableBalanceProvider, + availableBalanceStrategy, + }: { + type: AccountType + plaidCurrent: number + plaidAvailable: number + currentBalanceProvider: number + currentBalanceStrategy: AccountBalanceStrategy + availableBalanceProvider: number + availableBalanceStrategy: AccountBalanceStrategy + }) => { + expect( + getAccountBalanceData( + { + current: plaidCurrent, + available: plaidAvailable, + iso_currency_code: 'USD', + unofficial_currency_code: null, + }, + type + ) + ).toEqual({ + currentBalanceProvider: new Prisma.Decimal(currentBalanceProvider), + currentBalanceStrategy, + availableBalanceProvider: new Prisma.Decimal(availableBalanceProvider), + availableBalanceStrategy, + currencyCode: 'USD', + }) + } + ) +}) diff --git a/libs/server/shared/src/utils/plaid-utils.ts b/libs/server/shared/src/utils/plaid-utils.ts new file mode 100644 index 00000000000..58fd3cac141 --- /dev/null +++ b/libs/server/shared/src/utils/plaid-utils.ts @@ -0,0 +1,180 @@ +import type { Account, AccountType } from '@prisma/client' +import { Prisma, AccountCategory } from '@prisma/client' +import type { + Transaction as PlaidTransaction, + AccountBalance as PlaidAccountBalance, + Holding, + MortgageLiability, + StudentLoan, + CreditCardLiability, +} from 'plaid' +import { AccountType as PlaidAccountType, AccountSubtype as PlaidAccountSubtype } from 'plaid' +import { Duration } from 'luxon' +import { countBy } from 'lodash' +import type { SharedType } from '@maybe-finance/shared' + +// From the "taxonomy csv file" on the transactions docs page - https://plaid.com/docs/api/products/transactions/#transactionsget +type PersonalFinanceCategoryPrimary = + | 'INCOME' + | 'TRANSFER_IN' + | 'TRANSFER_OUT' + | 'LOAN_PAYMENTS' + | 'BANK_FEES' + | 'ENTERTAINMENT' + | 'FOOD_AND_DRINK' + | 'GENERAL_MERCHANDISE' + | 'HOME_IMPROVEMENT' + | 'MEDICAL' + | 'PERSONAL_CARE' + | 'GENERAL_SERVICES' + | 'GOVERNMENT_AND_NON_PROFIT' + | 'TRANSPORTATION' + | 'TRAVEL' + | 'RENT_AND_UTILITIES' + +/** + * Plaid only delivers 2 years worth of data at maximum + */ +export const PLAID_WINDOW_MAX = Duration.fromObject({ years: 2 }) + +export function getAccountBalanceData( + { + current, + available, + iso_currency_code, + unofficial_currency_code, + }: Pick< + PlaidAccountBalance, + 'current' | 'available' | 'iso_currency_code' | 'unofficial_currency_code' + >, + plaidType?: PlaidAccountType +): Pick< + Account, + | 'currentBalanceProvider' + | 'currentBalanceStrategy' + | 'availableBalanceProvider' + | 'availableBalanceStrategy' + | 'currencyCode' +> { + const currencyCode = iso_currency_code || unofficial_currency_code || 'USD' + + return { + currentBalanceProvider: current != null ? new Prisma.Decimal(current) : null, + currentBalanceStrategy: + // For investment accounts with different current/available balances, + // We assume that one is a portfolio value and the other is a cash value and combine them + plaidType === PlaidAccountType.Investment && + current != null && + available != null && + current !== available + ? 'sum' + : 'current', + availableBalanceProvider: available != null ? new Prisma.Decimal(available) : null, + availableBalanceStrategy: 'available', + currencyCode, + } +} + +export function getType(plaidType: PlaidAccountType): AccountType { + switch (plaidType) { + case PlaidAccountType.Depository: + return 'DEPOSITORY' + case PlaidAccountType.Investment: + case PlaidAccountType.Brokerage: + return 'INVESTMENT' + case PlaidAccountType.Credit: + return 'CREDIT' + case PlaidAccountType.Loan: + return 'LOAN' + default: + return 'OTHER_ASSET' + } +} + +export function isPlaidLiability(plaidType: string | null, plaidSubtype: string | null) { + const { Loan, Credit } = PlaidAccountType + const { Student, Mortgage, CreditCard, Paypal } = PlaidAccountSubtype + + if (plaidType === Loan && (plaidSubtype === Student || plaidSubtype === Mortgage)) { + return true + } + + if (plaidType === Credit && (plaidSubtype === CreditCard || plaidSubtype === Paypal)) { + return true + } + + return false +} + +export function plaidTypesToCategory(plaidType: PlaidAccountType): AccountCategory { + switch (plaidType) { + case PlaidAccountType.Depository: + return AccountCategory.cash + case PlaidAccountType.Investment: + case PlaidAccountType.Brokerage: + return AccountCategory.investment + case PlaidAccountType.Loan: + return AccountCategory.loan + case PlaidAccountType.Credit: + return AccountCategory.credit + default: + return AccountCategory.other + } +} + +export function getHoldingsWithDerivedIds< + THolding extends Pick +>(holdings: THolding[]) { + const counts = countBy(holdings, getPlaidHoldingId) + + return holdings.map((h, idx) => { + const id = getPlaidHoldingId(h) + const derivedId = counts[id] > 1 ? `${id}.index[${idx}]` : id + + return { + ...h, + derivedId, + } + }) +} + +export function getPlaidHoldingId(holding: Pick) { + return `account[${holding.account_id}].security[${holding.security_id}]` +} + +export function normalizeMortgageData(mortgage: MortgageLiability): SharedType.Loan { + return { + originationDate: mortgage.origination_date ?? undefined, + originationPrincipal: mortgage.origination_principal_amount ?? undefined, + maturityDate: mortgage.maturity_date ?? undefined, + interestRate: + mortgage.interest_rate.type === 'fixed' + ? { type: 'fixed', rate: mortgage.interest_rate.percentage ?? undefined } + : { type: 'variable' }, + loanDetail: { type: 'mortgage' }, + } +} + +export function normalizeStudentLoanData(studentLoan: StudentLoan): SharedType.Loan { + return { + originationDate: studentLoan.origination_date ?? undefined, + originationPrincipal: studentLoan.origination_principal_amount ?? undefined, + maturityDate: studentLoan.expected_payoff_date ?? undefined, + interestRate: { + type: 'fixed', // assume all Plaid student loans are fixed rate (user can always override later) + rate: studentLoan.interest_rate_percentage ?? undefined, + }, + loanDetail: { type: 'student' }, + } +} + +export function normalizeCreditData(credit: CreditCardLiability): SharedType.Credit { + return { + isOverdue: credit.is_overdue ?? undefined, + lastPaymentAmount: credit.last_payment_amount ?? undefined, + lastPaymentDate: credit.last_payment_date ?? undefined, + lastStatementAmount: credit.last_statement_balance ?? undefined, + lastStatementDate: credit.last_statement_issue_date ?? undefined, + minimumPayment: credit.minimum_payment_amount ?? undefined, + } +} diff --git a/libs/server/shared/src/utils/server-utils.ts b/libs/server/shared/src/utils/server-utils.ts new file mode 100644 index 00000000000..a482dba7e4a --- /dev/null +++ b/libs/server/shared/src/utils/server-utils.ts @@ -0,0 +1,99 @@ +import type { Message } from '@prisma/client' +import { getSignedUrl } from '@aws-sdk/cloudfront-signer' +import { DateTime } from 'luxon' + +/** + * @returns redis retry strategy + */ +export function redisRetryStrategy({ + maxAttempts = Infinity, + delayMs = 2_000, + backoff = 'linear', +}: { + maxAttempts?: number + delayMs?: number + backoff?: 'linear' | 'exponential' +} = {}) { + return (attempt: number) => { + const delay = backoff === 'linear' ? delayMs : delayMs * attempt + return attempt <= maxAttempts ? delay : null + } +} + +/** + * wrapper for executing sync pattern, basically a try-catch-else-finally pattern + */ +export function useSync({ + onStart, + sync, + onSyncError, + onSyncSuccess, + onEnd, +}: { + onStart?: (entity: TEntity) => Promise + sync: (entity: TEntity) => Promise + onSyncError: (entity: TEntity, error: unknown) => Promise + onSyncSuccess: (entity: TEntity) => Promise + onEnd?: (entity: TEntity) => Promise +}): (entity: TEntity) => Promise { + return async (entity: TEntity) => { + await onStart?.(entity) + + await tryCatchElseFinally( + () => sync(entity), + (error) => onSyncError(entity, error), + () => onSyncSuccess(entity), + () => onEnd?.(entity) ?? Promise.resolve() + ) + } +} + +async function tryCatchElseFinally( + _try: () => Promise, + _catch: (error: unknown) => Promise, + _else: () => Promise, + _finally: () => Promise +): Promise { + try { + let success = true + + try { + await _try() + } catch (error) { + success = false + await _catch(error) + } + + if (success) { + await _else() + } + } finally { + await _finally() + } +} + +// Temporary until Prisma Client Extensions work better +export type SignerConfig = { + cdnUrl: string + pubKeyId: string + privKey: string +} + +// Returns signed url if mediaSrc is present, otherwise returns message as-is +export function mapMessage(msg: T, config?: SignerConfig): T { + let mediaSrc = msg.mediaSrc + + if (mediaSrc && config != null) { + mediaSrc = getSignedUrl({ + url: `${config.cdnUrl}/${msg.mediaSrc}`, + keyPairId: config.pubKeyId, + privateKey: config.privKey, + dateLessThan: DateTime.now().plus({ days: 30 }).toString(), + }) + } + + return { + ...msg, + mediaSrc, + } +} diff --git a/libs/server/shared/tsconfig.json b/libs/server/shared/tsconfig.json new file mode 100644 index 00000000000..bab70e20f1b --- /dev/null +++ b/libs/server/shared/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + "noImplicitAny": false, + "strict": true, + "strictNullChecks": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/server/shared/tsconfig.lib.json b/libs/server/shared/tsconfig.lib.json new file mode 100644 index 00000000000..e0d2517ec20 --- /dev/null +++ b/libs/server/shared/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/server/shared/tsconfig.spec.json b/libs/server/shared/tsconfig.spec.json new file mode 100644 index 00000000000..b6ee8c9ce47 --- /dev/null +++ b/libs/server/shared/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/libs/shared/.babelrc b/libs/shared/.babelrc new file mode 100644 index 00000000000..4496e8f26c8 --- /dev/null +++ b/libs/shared/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] +} diff --git a/libs/shared/.eslintrc.json b/libs/shared/.eslintrc.json new file mode 100644 index 00000000000..e15cd15d6d2 --- /dev/null +++ b/libs/shared/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts"], + "rules": { + "@typescript-eslint/no-unused-vars": ["warn", { "argsIgnorePattern": "^_" }] + } + } + ] +} diff --git a/libs/shared/README.md b/libs/shared/README.md new file mode 100644 index 00000000000..043b8fd55e5 --- /dev/null +++ b/libs/shared/README.md @@ -0,0 +1,7 @@ +# shared + +This library was generated with [Nx](https://nx.dev). + +## Running unit tests + +Run `nx test shared` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/libs/shared/jest.config.ts b/libs/shared/jest.config.ts new file mode 100644 index 00000000000..cb3e91751b5 --- /dev/null +++ b/libs/shared/jest.config.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +export default { + displayName: 'shared', + preset: '../../jest.preset.js', + globals: { + 'ts-jest': { + tsconfig: '/tsconfig.spec.json', + }, + }, + testEnvironment: 'node', + transform: { + '^.+\\.[tj]sx?$': 'ts-jest', + }, + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], + coverageDirectory: '../../coverage/libs/shared', +} diff --git a/libs/shared/src/index.ts b/libs/shared/src/index.ts new file mode 100644 index 00000000000..36b882482c8 --- /dev/null +++ b/libs/shared/src/index.ts @@ -0,0 +1,6 @@ +export * as SharedType from './types' + +// namespaces for these are exported from ./utils/index.ts +export * from './utils' + +export { default as superjson } from './superjson' diff --git a/libs/shared/src/superjson.spec.ts b/libs/shared/src/superjson.spec.ts new file mode 100644 index 00000000000..a791fb3ec8f --- /dev/null +++ b/libs/shared/src/superjson.spec.ts @@ -0,0 +1,22 @@ +import { Prisma } from '@prisma/client' +import { Decimal } from 'decimal.js' +import { DateTime } from 'luxon' +import superjson from './superjson' + +describe('superjson', () => { + it.each` + type | value + ${`BigInt`} | ${BigInt(123)} + ${`Decimal`} | ${new Decimal(1.23)} + ${`Prisma.Decimal`} | ${new Prisma.Decimal(1.23)} + ${`Date`} | ${new Date()} + ${`DateTime`} | ${DateTime.now()} + `('can serialize $type', ({ type, value }) => { + const s = superjson.stringify(value) + const v = superjson.parse(s) + + // Prisma.Decimal always gets converted to a decimal.js Decimal + // so we need to special case that equality check + expect(v).toEqual(type === 'Prisma.Decimal' ? new Decimal(value) : value) + }) +}) diff --git a/libs/shared/src/superjson.ts b/libs/shared/src/superjson.ts new file mode 100644 index 00000000000..62d36ca1a8e --- /dev/null +++ b/libs/shared/src/superjson.ts @@ -0,0 +1,24 @@ +import superjson from 'superjson' +import type { Prisma } from '@prisma/client' +import { Decimal } from 'decimal.js' +import { DateTime } from 'luxon' + +superjson.registerCustom( + { + isApplicable: (v): v is Decimal | Prisma.Decimal => Decimal.isDecimal(v), + serialize: (d) => d.toJSON(), + deserialize: (s) => new Decimal(s), + }, + 'Decimal' +) + +superjson.registerCustom( + { + isApplicable: (v): v is DateTime => DateTime.isDateTime(v), + serialize: (dt) => dt.toISO(), + deserialize: (s) => DateTime.fromISO(s), + }, + 'DateTime' +) + +export default superjson diff --git a/libs/shared/src/types/account-types.ts b/libs/shared/src/types/account-types.ts new file mode 100644 index 00000000000..63c92ac76be --- /dev/null +++ b/libs/shared/src/types/account-types.ts @@ -0,0 +1,234 @@ +import type { + Account, + AccountType, + AccountClassification, + AccountConnection, + AccountConnectionType, + AccountConnectionStatus, + Holding, + InvestmentTransaction, + Security, + Transaction, + Valuation, + Prisma, + AccountCategory, + TransactionType, +} from '@prisma/client' +import type { TimeSeries, TimeSeriesResponseWithDetail, Trend } from './general-types' +import type { TransactionEnriched } from './transaction-types' + +/** + * ================================================================ + * ====== Account Detail ====== + * ================================================================ + */ +export type { + Account, + AccountType, + AccountClassification, + AccountConnection, + AccountConnectionType, + AccountConnectionStatus, + Valuation, +} + +export type Loan = { + originationDate?: string + maturityDate?: string + originationPrincipal?: number + interestRate: { type: 'fixed'; rate?: number } | { type: 'arm' } | { type: 'variable' } + loanDetail: { type: 'student' } | { type: 'mortgage' } | { type: 'other' } +} + +export type Credit = { + isOverdue?: boolean + lastPaymentAmount?: number + lastPaymentDate?: string + lastStatementAmount?: number + lastStatementDate?: string + minimumPayment?: number +} + +export type AccountSyncProgress = { + description: string + progress?: number // 0-1 +} + +export type AccountDetail = Omit & { + accountConnection: AccountConnection + transactions: Transaction[] + investmentTransactions: InvestmentTransaction[] + valuations: Array + holdings: Holding[] + loan: Loan | null + credit: Credit | null +} + +export type AccountWithConnection = Account & { accountConnection?: AccountConnection } + +export type AccountsResponse = { + accounts: Account[] + connections: (ConnectionWithAccounts & ConnectionWithSyncProgress)[] +} + +export enum PageSize { + Transaction = 50, + InvestmentTransaction = 25, + Valuation = 50, + Holding = 50, + Institution = 50, +} + +export type NormalizedCategory = { + value: AccountCategory + singular: string + plural: string +} + +/** + * ================================================================ + * ====== Connections ====== + * ================================================================ + */ + +export type ConnectionWithAccounts = AccountConnection & { + accounts: Account[] +} + +export type ConnectionWithSyncProgress = AccountConnection & { + syncProgress?: AccountSyncProgress +} + +/** + * ================================================================ + * ====== Account Timeseries ====== + * ================================================================ + */ + +export type AccountBalanceTimeSeriesData = { + date: string // yyyy-mm-dd + balance: Prisma.Decimal +} + +export type AccountBalanceResponse = TimeSeriesResponseWithDetail< + TimeSeries +> + +export type AccountReturnTimeSeriesData = { + date: string + account: { + rateOfReturn: Prisma.Decimal + contributions?: Prisma.Decimal + contributionsPeriod?: Prisma.Decimal + } + comparisons?: { + [ticker in string]?: Prisma.Decimal + } +} + +export type AccountReturnResponse = TimeSeries + +export type AccountTransactionResponse = { + transactions: TransactionEnriched[] + totalTransactions: number +} + +export type AccountHolding = Pick< + Holding, + | 'id' + | 'securityId' + | 'costBasis' + | 'costBasisUser' + | 'costBasisProvider' + | 'quantity' + | 'value' + | 'excluded' +> & + Pick & { + price: Prisma.Decimal + trend: { + total: Trend | null + today: Trend | null + } + } + +export type AccountHoldingResponse = { + holdings: AccountHolding[] + totalHoldings: number +} + +export type AccountInvestmentTransaction = InvestmentTransaction & { security?: Security } + +export type AccountInvestmentTransactionResponse = { + investmentTransactions: AccountInvestmentTransaction[] + totalInvestmentTransactions: number +} + +export type AccountRollupTimeSeries = TimeSeries< + Pick & { + rollupPct: Prisma.Decimal + totalPct: Prisma.Decimal + } +> + +type AccountRollupGroup = { + key: TKey + title: string + balances: AccountRollupTimeSeries + items: TItem[] +} + +export type AccountRollup = AccountRollupGroup< + AccountClassification, + AccountRollupGroup< + AccountCategory, + Pick & { + connection: Pick | null + syncing: boolean + balances: AccountRollupTimeSeries + } + > +>[] + +export type AccountValuationsResponse = { + valuations: (Valuation & { + trend: { + period: Trend + total: Trend + } | null + })[] + trends: { + date: string + amount: Prisma.Decimal + period: Trend + total: Trend + }[] +} + +export type AccountInsights = { + portfolio?: { + return: { + '1m': Trend | null + '1y': Trend | null + ytd: Trend | null + } + pnl: Trend | null + costBasis: Prisma.Decimal | null + contributions: { + ytd: { + amount: Prisma.Decimal | null + monthlyAvg: Prisma.Decimal | null + } + lastYear: { + amount: Prisma.Decimal | null + monthlyAvg: Prisma.Decimal | null + } + } + fees: Prisma.Decimal + holdingBreakdown: { + asset_class: string // 'stocks' | 'fixed_income' | 'cash' | 'crypto' | 'other' + amount: Prisma.Decimal + percentage: Prisma.Decimal + }[] + } +} diff --git a/libs/shared/src/types/api-types.ts b/libs/shared/src/types/api-types.ts new file mode 100644 index 00000000000..9141d7e9bb0 --- /dev/null +++ b/libs/shared/src/types/api-types.ts @@ -0,0 +1,22 @@ +// A simplified version of: https://jsonapi.org/examples/#error-objects-multiple-errors +export interface ErrorResponse { + errors: Array<{ status: string; title: string; detail?: string }> +} + +export interface SuccessResponse { + data: { + json: any // eslint-disable-line + meta?: any // eslint-disable-line + } + [metadata: string]: any // eslint-disable-line +} + +// Can be used in Axios typings in components +// eslint-disable-next-line +export interface ApiResponse { + data?: T + errors?: ErrorResponse['errors'] +} + +// eslint-disable-next-line +export type BaseResponse = SuccessResponse | ErrorResponse diff --git a/libs/shared/src/types/conversation-types.ts b/libs/shared/src/types/conversation-types.ts new file mode 100644 index 00000000000..05fd06247bb --- /dev/null +++ b/libs/shared/src/types/conversation-types.ts @@ -0,0 +1,28 @@ +import type { Advisor, Conversation, ConversationAdvisor, Message } from '@prisma/client' + +export type AdvisorProfile = Pick + +export type ConversationWithDetail = Conversation & { + advisors: (ConversationAdvisor & { + advisor: AdvisorProfile + })[] + messages: (Message & { + user: { + advisor: AdvisorProfile | null + } | null + })[] +} + +export type ConversationWithMessageSummary = Conversation & { + advisors: (ConversationAdvisor & { + advisor: AdvisorProfile + })[] + messageCount: number + firstMessage: Message | null + lastMessage: Message | null +} + +export type ConversationNotification = { + type: 'submitted' | 'review' | 'update' | 'closed' | 'expired' + conversationId: Conversation['id'] +} diff --git a/libs/shared/src/types/email-types.ts b/libs/shared/src/types/email-types.ts new file mode 100644 index 00000000000..14a1d1d2df1 --- /dev/null +++ b/libs/shared/src/types/email-types.ts @@ -0,0 +1,12 @@ +type EmailCommon = { + from?: string + to: string + replyTo?: string +} + +type EmailTemplate = { alias: string; model: Record } + +type PlainMessageContent = { subject: string; textBody?: string; htmlBody?: string } + +export type PlainEmailMessage = EmailCommon & PlainMessageContent +export type TemplateEmailMessage = EmailCommon & { template: EmailTemplate } diff --git a/libs/shared/src/types/general-types.ts b/libs/shared/src/types/general-types.ts new file mode 100644 index 00000000000..97117f0181d --- /dev/null +++ b/libs/shared/src/types/general-types.ts @@ -0,0 +1,93 @@ +import type { Prisma } from '@prisma/client' +import type { PlaidError } from 'plaid' +import type { AxiosError } from 'axios' +import type { Contexts, Primitive } from '@sentry/types' +import type DecimalJS from 'decimal.js' +import type { O } from 'ts-toolbelt' + +export type Decimal = DecimalJS | Prisma.Decimal + +export type DateRange = { + start: TDate + end: TDate +} + +/** + * ================================================================ + * ====== TimeSeries Data ====== + * ================================================================ + */ +export type TimeSeriesInterval = 'days' | 'weeks' | 'months' | 'quarters' | 'years' + +export type TimeSeries< + TData extends { date: string }, + TInterval extends string = TimeSeriesInterval +> = { + interval: TInterval + start: string // yyyy-mm-dd + end: string // yyyy-mm-dd + data: TData[] +} + +export type TimeSeriesResponseWithDetail = TSeries extends TimeSeries< + infer TData, + infer _TInterval // eslint-disable-line +> + ? { + series: TSeries + today?: TData + minDate: string + trend: Trend + } + : never + +/** + * ================================================================ + * ====== Calculations / Metrics ====== + * ================================================================ + */ + +export type Trend = { + direction: 'up' | 'down' | 'flat' + amount: Decimal | null + percentage: Decimal | null +} + +export type FormatString = 'currency' | 'short-currency' | 'percent' | 'decimal' | 'short-decimal' + +/** + * ================================================================ + * ====== Error types ====== + * ================================================================ + */ +export type ParsedError = { + // Parser will attempt to produce a descriptive message from the error + message: string + + // Any extra error data to include in logs + metadata?: any + + // Not safe for production, but provided for local logs + stackTrace?: any + + // This parser covers more than just HTTP errors, so this is optional + statusCode?: string + + sentryContexts?: Contexts + sentryTags?: { [key: string]: Primitive } +} + +export type AxiosPlaidError = O.Required, 'response' | 'config'> + +export type StatusPageResponse = { + page?: { + id?: string + name?: string + url?: string + updated_at?: string + } + status?: { + description?: string + indicator?: 'none' | 'minor' | 'major' | 'critical' + } +} diff --git a/libs/shared/src/types/holding-types.ts b/libs/shared/src/types/holding-types.ts new file mode 100644 index 00000000000..ae8203c0be4 --- /dev/null +++ b/libs/shared/src/types/holding-types.ts @@ -0,0 +1,18 @@ +import type { Holding, Prisma, Security } from '@prisma/client' + +export type HoldingInsights = { + holding: Holding + dividends: Prisma.Decimal | null + allocation: Prisma.Decimal | null +} + +export type HoldingEnriched = Pick & { + name: Security['name'] + security_id: Security['id'] + symbol: Security['symbol'] + shares_per_contract: Security['sharesPerContract'] + cost_basis: Prisma.Decimal | null + cost_basis_per_share: Prisma.Decimal | null + price: Prisma.Decimal + price_prev: Prisma.Decimal | null +} diff --git a/libs/shared/src/types/index.ts b/libs/shared/src/types/index.ts new file mode 100644 index 00000000000..547a108d91d --- /dev/null +++ b/libs/shared/src/types/index.ts @@ -0,0 +1,12 @@ +export * from './api-types' +export * from './account-types' +export * from './email-types' +export * from './institution-types' +export * from './security-types' +export * from './transaction-types' +export * from './investment-transaction-types' +export * from './user-types' +export * from './general-types' +export * from './holding-types' +export * from './plan-types' +export * from './conversation-types' diff --git a/libs/shared/src/types/institution-types.ts b/libs/shared/src/types/institution-types.ts new file mode 100644 index 00000000000..77eb3fb636a --- /dev/null +++ b/libs/shared/src/types/institution-types.ts @@ -0,0 +1,22 @@ +import type { + Institution as PrismaInstitution, + ProviderInstitution as PrismaProviderInstitution, +} from '@prisma/client' + +export type ProviderInstitution = Pick< + PrismaProviderInstitution, + 'id' | 'provider' | 'providerId' | 'rank' +> + +export type Institution = Pick< + PrismaInstitution | PrismaProviderInstitution, + 'name' | 'url' | 'logo' | 'logoUrl' | 'primaryColor' +> & { + id: string | number // we allow a `string` ID so we can construct artifical IDs for Institutions backed by a ProviderInstitution + providers: ProviderInstitution[] +} + +export type InstitutionsResponse = { + institutions: Institution[] + totalInstitutions: number +} diff --git a/libs/shared/src/types/investment-transaction-types.ts b/libs/shared/src/types/investment-transaction-types.ts new file mode 100644 index 00000000000..85f81bcf11d --- /dev/null +++ b/libs/shared/src/types/investment-transaction-types.ts @@ -0,0 +1,3 @@ +import type { InvestmentTransaction, InvestmentTransactionCategory } from '@prisma/client' + +export type { InvestmentTransaction, InvestmentTransactionCategory } diff --git a/libs/shared/src/types/plan-types.ts b/libs/shared/src/types/plan-types.ts new file mode 100644 index 00000000000..9b0f8d9c254 --- /dev/null +++ b/libs/shared/src/types/plan-types.ts @@ -0,0 +1,73 @@ +import type { + Prisma, + Plan as PrismaPlan, + PlanEvent as PrismaPlanEvent, + PlanMilestone as PrismaPlanMilestone, +} from '@prisma/client' +import type { O } from 'ts-toolbelt' +import type { Decimal, TimeSeries } from './general-types' + +export type PlanEvent = O.NonNullable +export type PlanMilestone = PrismaPlanMilestone + +export type Plan = PrismaPlan & { + events: PlanEvent[] + milestones: PlanMilestone[] +} + +export type PlansResponse = { + plans: Plan[] +} + +export type PlanProjectionEvent = { + event: PlanEvent + calculatedValue: Decimal +} + +export type PlanProjectionMilestone = PlanMilestone + +export type PlanProjectionData = { + date: string + values: { + year: number + age: number + netWorth: Decimal + events: PlanProjectionEvent[] + milestones: PlanProjectionMilestone[] + successRate: Decimal + } +} + +export type PlanSimulationData = { + date: string + values: { + year: number + age: number + netWorth: Decimal + } +} + +// API response +export type PlanProjectionResponse = { + projection: TimeSeries + simulations: { + percentile: Decimal + simulation: TimeSeries + }[] +} + +export type ProjectionAssetType = 'stocks' | 'bonds' | 'cash' | 'crypto' | 'property' | 'other' +export type ProjectionLiabilityType = 'credit' | 'loan' | 'other' + +export type PlanInsights = { + projectionAssetBreakdown: { + type: ProjectionAssetType + amount: Prisma.Decimal + }[] + projectionLiabilityBreakdown: { + type: ProjectionLiabilityType + amount: Prisma.Decimal + }[] + income: Prisma.Decimal + expenses: Prisma.Decimal +} diff --git a/libs/shared/src/types/security-types.ts b/libs/shared/src/types/security-types.ts new file mode 100644 index 00000000000..193799a4bbe --- /dev/null +++ b/libs/shared/src/types/security-types.ts @@ -0,0 +1,32 @@ +import type { Prisma, Security, SecurityPricing } from '@prisma/client' +import type { DateTime } from 'luxon' + +export type { Security, SecurityPricing } + +export type SecurityWithPricing = Security & { + pricing: SecurityPricing[] +} + +export type SecurityDetails = { + day?: { + open?: Prisma.Decimal + prevClose?: Prisma.Decimal + high?: Prisma.Decimal + low?: Prisma.Decimal + } + year?: { + high?: Prisma.Decimal + low?: Prisma.Decimal + volume?: Prisma.Decimal + dividends?: Prisma.Decimal + } + marketCap?: Prisma.Decimal + peRatio?: Prisma.Decimal + expenseRatio?: Prisma.Decimal + eps?: Prisma.Decimal +} + +export type DailyPricing = { + date: DateTime + priceClose: Prisma.Decimal +} diff --git a/libs/shared/src/types/transaction-types.ts b/libs/shared/src/types/transaction-types.ts new file mode 100644 index 00000000000..b72b77cdbf5 --- /dev/null +++ b/libs/shared/src/types/transaction-types.ts @@ -0,0 +1,38 @@ +import type { + Account, + AccountClassification, + AccountConnection, + AccountType, + Transaction, + TransactionType, + User, +} from '@prisma/client' + +export type { Transaction } + +export type TransactionEnriched = Omit< + Transaction, + | 'plaidTransactionId' + | 'plaidCategory' + | 'plaidCategoryId' + | 'plaidPersonalFinanceCategory' + | 'finicityTransactionId' + | 'finicityType' + | 'finicityCategorization' +> & { + type: TransactionType + userId: User['id'] + accountClassification: AccountClassification + accountType: AccountType +} + +export type TransactionWithAccountDetail = Transaction & { + account: Account & { + accountConnection: AccountConnection | null + } +} + +export type TransactionsResponse = { + transactions: TransactionWithAccountDetail[] + pageCount: number +} diff --git a/libs/shared/src/types/user-types.ts b/libs/shared/src/types/user-types.ts new file mode 100644 index 00000000000..03642a2f216 --- /dev/null +++ b/libs/shared/src/types/user-types.ts @@ -0,0 +1,324 @@ +import type { User as Auth0UserClient } from '@auth0/auth0-react' +import type { Identity, User as Auth0UserServer } from 'auth0' +import type { + AccountCategory, + AccountClassification, + Agreement, + Holding, + Prisma, + Security, + User as PrismaUser, +} from '@prisma/client' +import type { Institution } from 'plaid' +import type { TimeSeries, TimeSeriesResponseWithDetail, Trend } from './general-types' +import type { DateTime } from 'luxon' + +/** + * ================================================================ + * ====== User ====== + * ================================================================ + */ + +export type User = Omit & { riskAnswers: RiskAnswer[] } +export type UpdateUser = Partial< + Prisma.UserUncheckedUpdateInput & { + monthlyDebtUser: number | null + monthlyIncomeUser: number | null + monthlyExpensesUser: number | null + } +> + +/** + * ================================================================ + * ====== Net Worth ====== + * ================================================================ + */ +export type NetWorthTimeSeriesData = { + date: string // yyyy-mm-dd + netWorth: Prisma.Decimal + assets: Prisma.Decimal + liabilities: Prisma.Decimal + categories: Partial> +} + +export type NetWorthTimeSeriesResponse = TimeSeriesResponseWithDetail< + TimeSeries +> + +/** + * ================================================================ + * ====== Insights ====== + * ================================================================ + */ + +export type UserInsights = { + netWorthToday: Prisma.Decimal + netWorth: { + yearly: Trend + monthly: Trend + weekly: Trend + } + safetyNet: { + months: Prisma.Decimal + spending: Prisma.Decimal + } + debtIncome: { + ratio: Prisma.Decimal + debt: Prisma.Decimal + income: Prisma.Decimal + user: { + debt: Prisma.Decimal | null + income: Prisma.Decimal | null + } + calculated: { + debt: Prisma.Decimal + income: Prisma.Decimal + } + } + debtAsset: { + ratio: Prisma.Decimal + debt: Prisma.Decimal + asset: Prisma.Decimal + } + accountSummary: { + classification: AccountClassification + category: AccountCategory + balance: Prisma.Decimal + allocation: Prisma.Decimal + }[] + assetSummary: { + liquid: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + illiquid: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + yielding: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + } + debtSummary: { + good: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + bad: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + total: { + amount: Prisma.Decimal + percentage: Prisma.Decimal + } + } + holdingBreakdown: { + category: 'stocks' | 'fixed_income' | 'cash' | 'crypto' | 'other' + value: Holding['value'] + allocation: Prisma.Decimal + holdings: { + security: Pick + value: Holding['value'] + allocation: Prisma.Decimal + }[] + }[] + transactionSummary: { + income: Prisma.Decimal + expenses: Prisma.Decimal + payments: Prisma.Decimal + } + transactionBreakdown: { + category: string | null + amount: Prisma.Decimal + avg_6mo: Prisma.Decimal + }[] +} + +/** + * ================================================================ + * ====== User Profile/Account ====== + * ================================================================ + */ + +// Arbitrary custom namespaces to avoid collision with Auth0 properties +export enum Auth0CustomNamespace { + Email = 'https://maybe.co/email', + Picture = 'https://maybe.co/picture', + Roles = 'https://maybe.co/roles', + UserMetadata = 'https://maybe.co/user-metadata', + AppMetadata = 'https://maybe.co/app-metadata', + + // A convenience property (so we dont have to parse the Auth0 `identities` array every time) + PrimaryIdentity = 'https://maybe.co/primary-identity', +} + +// Maybe's "normalized" Auth0 `user.user_metadata` object +export type MaybeUserMetadata = Partial<{ + enrolled_mfa: boolean + hasDuplicateAccounts: boolean + shouldPromptUserAccountLink: boolean +}> + +// Maybe's "normalized" Auth0 `user.app_metadata` object +export type MaybeAppMetadata = {} + +// The custom roles we have defined in Auth0 +export type UserRole = 'Admin' | 'CIUser' | 'Advisor' + +export type PrimaryAuth0Identity = Partial<{ + connection: string + provider: string + isSocial: boolean +}> + +// Added to access and ID tokens via Auth0 rules +export type MaybeCustomClaims = { + [Auth0CustomNamespace.Email]?: string | null + [Auth0CustomNamespace.Picture]?: string | null + [Auth0CustomNamespace.Roles]?: UserRole[] + [Auth0CustomNamespace.UserMetadata]?: MaybeUserMetadata + [Auth0CustomNamespace.AppMetadata]?: MaybeAppMetadata + [Auth0CustomNamespace.PrimaryIdentity]?: PrimaryAuth0Identity +} + +export type Auth0ReactUser = Auth0UserClient & MaybeCustomClaims +export type Auth0User = Auth0UserServer +export type Auth0Profile = Auth0User & { + primaryIdentity: Identity // actual + secondaryIdentities: Identity[] // linked + suggestedIdentities: Identity[] // potential links + autoPromptEnabled: boolean + socialOnlyUser: boolean + mfaEnabled: boolean +} + +export type UpdateAuth0User = { enrolled_mfa: boolean } + +export interface PasswordReset { + currentPassword: string + newPassword: string +} + +export type LinkAccountStatus = { + autoPromptEnabled: boolean + suggestedUsers: Auth0User[] +} + +export interface LinkAccounts { + secondaryJWT: string + secondaryProvider: string +} + +export interface UnlinkAccount { + secondaryAuth0Id: string + secondaryProvider: string +} + +export type UserSubscription = { + subscribed: boolean + trialing: boolean + canceled: boolean + + trialEnd: DateTime | null + cancelAt: DateTime | null + + currentPeriodEnd: DateTime | null +} + +export type UserIntercomMetadata = { + hash?: string +} + +export type UserMemberCardDetails = { + memberNumber: number + name: string + title: string + joinDate: Date + maybe: string + cardUrl: string + imageUrl: string +} + +export type RiskQuestionChoice = { + key: string + text: string + riskScore: number +} + +export type RiskQuestion = { + key: string + text: string + choices: RiskQuestionChoice[] +} + +export type RiskAnswer = { + questionKey: string + choiceKey: string +} + +export type AgreementWithUrl = Agreement & { url: string } + +/** + * main - the fullscreen "takeover" every user must go through + * sidebar - the post-onboarding sidebar for connecting accounts + */ +export type OnboardingFlow = 'main' | 'sidebar' + +export type OnboardingStep = { + key: string + title: string + isComplete: boolean + isMarkedComplete: boolean + group?: string + ctaPath?: string +} + +export type OnboardingResponse = { + steps: OnboardingStep[] + currentStep: OnboardingStep | null + progress: { + completed: number + total: number + percent: number + } + isComplete: boolean + isMarkedComplete: boolean +} + +/** + * ================================================================ + * ====== Plaid Connections ====== + * ================================================================ + */ +export type PlaidLinkUpdateMode = 'reconnect' | 'new-accounts' + +// Used for /create/link/token when user is connecting a bank account +export interface LinkConfig { + token: string +} + +export type PublicTokenExchange = LinkConfig & { + institution: Institution +} + +/** + * ================================================================ + * ====== ConvertKitApi ====== + * ================================================================ + */ + +export type ConvertKitSubscriber = { + id: number + first_name: string + email_address: string + state: 'cancelled' | 'active' + created_at: string +} + +export type ConvertKitSubscription = { + id: number + state: 'cancelled' | 'active' + subscriber: Pick +} diff --git a/libs/shared/src/utils/account-utils.ts b/libs/shared/src/utils/account-utils.ts new file mode 100644 index 00000000000..b0d6e48b6e1 --- /dev/null +++ b/libs/shared/src/utils/account-utils.ts @@ -0,0 +1,194 @@ +import type { SharedType } from '..' +import type { AccountCategory, AccountClassification, AccountType } from '@prisma/client' +import groupBy from 'lodash/groupBy' +import keyBy from 'lodash/keyBy' +import mapValues from 'lodash/mapValues' + +export const ACCOUNT_TYPES: AccountType[] = [ + 'CREDIT', + 'DEPOSITORY', + 'INVESTMENT', + 'LOAN', + 'OTHER_ASSET', + 'OTHER_LIABILITY', + 'PROPERTY', + 'VEHICLE', +] + +export const CATEGORIES: Record = { + cash: { + value: 'cash', + singular: 'Cash', + plural: 'Cash', + }, + credit: { + value: 'credit', + singular: 'Credit Card', + plural: 'Credit Cards', + }, + crypto: { + value: 'crypto', + singular: 'Crypto', + plural: 'Crypto', + }, + investment: { + value: 'investment', + singular: 'Investment', + plural: 'Investments', + }, + loan: { + value: 'loan', + singular: 'Loan', + plural: 'Loans', + }, + property: { + value: 'property', + singular: 'Real Estate', + plural: 'Real Estate', + }, + valuable: { + value: 'valuable', + singular: 'Valuable', + plural: 'Valuables', + }, + vehicle: { + value: 'vehicle', + singular: 'Vehicle', + plural: 'Vehicles', + }, + other: { + value: 'other', + singular: 'Other', + plural: 'Other', + }, +} + +export const LIABILITY_CATEGORIES = [CATEGORIES.loan, CATEGORIES.credit, CATEGORIES.other] +export const ASSET_CATEGORIES = [ + CATEGORIES.cash, + CATEGORIES.investment, + CATEGORIES.property, + CATEGORIES.vehicle, + CATEGORIES.crypto, + CATEGORIES.valuable, + CATEGORIES.other, +] + +export const CATEGORY_MAP_SIMPLE: Record = { + INVESTMENT: ['investment', 'cash', 'other'], + DEPOSITORY: ['cash', 'other'], + CREDIT: ['credit'], + LOAN: ['loan'], + PROPERTY: ['property'], + VEHICLE: ['vehicle'], + OTHER_ASSET: ['cash', 'investment', 'crypto', 'valuable', 'other'], + OTHER_LIABILITY: ['other'], +} + +export const CATEGORY_MAP = mapValues(keyBy(ACCOUNT_TYPES), (accountType) => + CATEGORY_MAP_SIMPLE[accountType].map((category) => CATEGORIES[category]) +) as Record + +/** + * Same logic used in the dbgenerated() classification column, used for cases + * where the Account context is not available + */ +export function getClassification(type: AccountType): AccountClassification { + switch (type) { + case 'CREDIT': + case 'LOAN': + case 'OTHER_LIABILITY': + return 'liability' + default: + return 'asset' + } +} + +export function groupAccountsByCategory>( + accounts: TAccount[] +) { + return Object.entries(groupBy(accounts, (a) => a.category)).map(([category, accounts]) => ({ + category: CATEGORIES[category as AccountCategory].plural, + subtitle: + accounts.length === 1 + ? `1 ${CATEGORIES[category as AccountCategory].singular}` + : `${accounts.length} ${CATEGORIES[category as AccountCategory].plural}`, + accounts, + })) +} + +/** + * Determines a user-friendly account type name based on the account's category + * and subcategory + */ +export function getAccountTypeName(category: string, subcategory: string): string | null { + switch (category) { + case 'cash': + switch (subcategory) { + case 'cd': + return 'CD account' + case 'ebt': + return 'EBT account' + case 'hsa': + return 'HSA' + case 'prepaid': + return 'prepaid debit card' + } + + return `${subcategory} account` + + case 'investment': + switch (subcategory) { + case 'hsa': + case 'ira': + case 'isa': + return subcategory.toUpperCase() + case '401k': + return '401(k) account' + case 'roth': + return 'Roth IRA' + case 'roth 401k': + return 'Roth 401(k)' + case 'brokerage': + case 'pension': + case 'retirement': + return `${subcategory} account` + } + + return 'investment account' + + case 'loan': + switch (subcategory) { + case 'line of credit': + return 'line of credit' + case 'home equity': + return 'home equity line of credit' + case 'other': + return 'loan' + } + return subcategory ?? 'loan' + + case 'credit': + return 'credit card' + } + + return null +} + +export const flattenAccounts = ( + data?: SharedType.AccountsResponse +): Array => { + if (!data) return [] + + const { accounts, connections } = data + + const manual = accounts ?? [] + const connected = (connections ?? []) + .flatMap((c) => c.accounts) + .map((account) => { + const cnx = connections.find((c) => c.accounts.some((a) => a.id === account.id)) + return { ...account, accountConnection: cnx } + }) + + return [...manual, ...connected] +} diff --git a/libs/shared/src/utils/ata-utils.ts b/libs/shared/src/utils/ata-utils.ts new file mode 100644 index 00000000000..d452cfaabd7 --- /dev/null +++ b/libs/shared/src/utils/ata-utils.ts @@ -0,0 +1,291 @@ +import type { ConversationNotification, RiskAnswer, RiskQuestion } from '../types' +import type { Conversation, Message, MessageType } from '@prisma/client' +import { DateTime } from 'luxon' +import { v4 as uuid } from 'uuid' +import sum from 'lodash/sum' +import { scaleQuantile } from '@visx/scale' + +export function getEmailNotificationContent(type: ConversationNotification['type']) { + switch (type) { + case 'review': + return { + subject: 'An advisor has been assigned to your question', + message: + 'We have assigned an advisor to your question and they are gathering the information required to answer. You should be receiving a response shortly.', + } + case 'update': + return { + subject: 'You have a new message from your advisor', + message: + 'A new message has been posted to your conversation. Please go to your dashboard to view it.', + } + case 'closed': + return { + subject: 'Your conversation has been closed', + message: + 'Your conversation has been marked as closed and has been archived. You can view all your past conversations in your dashboard.', + } + case 'expired': + return { + subject: 'Your conversation has expired', + message: + 'Your conversation has not received any new messages recently and has been closed due to inactivity. If you still have unresolved questions feel free to open another one in your dashboard!', + } + default: + throw new Error(`Invalid notification type provided type=${type}`) + } +} + +export function getUrls(conversationId: Conversation['id']) { + return { + conversation: `https://app.maybe.co/ask-the-advisor/${conversationId}`, + settings: 'https://app.maybe.co/settings?tab=notifications', + } +} + +export function getExpiryStatus( + conversation: Conversation & { messages: Pick[] } +) { + const daysAgo = (d: Date) => { + return Math.abs(DateTime.fromJSDate(d).diffNow('days').days) + } + + const DAYS_UNTIL_EXPIRY_NOTIFICATION = 14 + const DAYS_UNTIL_EXPIRY = 17 + let refDate: Date | undefined + + if (!conversation.messages.length) { + refDate = conversation.createdAt + } else { + refDate = conversation.messages.sort( + (a, b) => b.createdAt.valueOf() - a.createdAt.valueOf() + )[0].createdAt + } + + if (daysAgo(refDate) >= DAYS_UNTIL_EXPIRY) { + return 'expired' + } else if ( + daysAgo(refDate) >= DAYS_UNTIL_EXPIRY_NOTIFICATION && + !conversation.expiryEmailSent + ) { + return 'expiring-soon' + } else { + return null + } +} + +// Based on common mime types - https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types +export function mimeToMessageType(mime?: string): MessageType { + if (!mime) return 'text' + + // Need to support multiple since user might upload their own audio/video files rather than recording + const supportedAudioMimeTypes = [ + 'audio/aac', + 'audio/wav', + 'audio/ogg', + 'audio/webm', + 'audio/mpeg', // .mp3 + ] + const supportedVideoMimeTypes = [ + 'video/mpeg', + 'video/mp4', + 'video/ogg', + 'video/webm', + 'video/quicktime', // .mov + 'video/x-msvideo', // .avi + ] + + return supportedAudioMimeTypes.includes(mime) + ? 'audio' + : supportedVideoMimeTypes.includes(mime) + ? 'video' + : 'text' // anything not supported will show up as regular file upload +} + +export function generateS3Filename(conversationId: Conversation['id']) { + return `private/ask-my-advisor/cid${conversationId}-${uuid()}` +} + +/** + * Questions for AMA risk tolerance onboarding + * + * - OKAY to add/remove/re-order questions + * - OKAY to re-order answers within a question + * - NEVER change key values (they are used to dynamically construct the quiz) + */ +export const riskQuestions: RiskQuestion[] = [ + { + key: '37cae883-ca70-4059-b185-85431c86b122', + text: 'You are on a TV game show and can choose one of the following. Which would you take?', + choices: [ + { + key: '604b03e4-b4de-40ba-ac53-3331e5d3ad76', + text: '$1,000 in cash', + riskScore: 1, + }, + { + key: '6dfed058-a76a-460a-a197-23242705028d', + text: 'A 50% chance at winning $5,000', + riskScore: 2, + }, + { + key: '80016d31-9c7b-444f-ba38-72f278f0b836', + text: 'A 25% chance at winning $10,000', + riskScore: 3, + }, + { + key: '67203867-b5a0-4a48-94e2-39e0dc6b05c2', + text: 'A 5% chance at winning $100,000', + riskScore: 4, + }, + ], + }, + { + key: '369400eb-c571-406b-9182-15c52be7e934', + text: 'You have just finished saving for a “once-in-a-lifetime” vacation. Three weeks before you plan to leave, you lose your job. You would:', + choices: [ + { + key: '01f14a87-b024-43fc-b00b-148779be8b97', + text: 'Cancel the vacation', + riskScore: 1, + }, + { + key: '86111b01-becf-4a1d-a0bc-892621feefd9', + text: 'Take a much more modest vacation', + riskScore: 2, + }, + { + key: '66fbb270-6adf-4ecc-928a-ae087281d27d', + text: 'Go as scheduled, reasoning that you need the time to prepare for a job search', + riskScore: 3, + }, + { + key: '5549b8a6-4f8b-4c5b-abc8-37a418ed9065', + text: 'Extend your vacation, because this might be your last chance to go first-class', + riskScore: 4, + }, + ], + }, + { + key: '3d0ad4fc-a2ea-49e8-add6-4830023cb93c', + text: 'When you think of the word “risk” which of the following words comes to mind first?', + choices: [ + { + key: '807af2cb-97c6-42ad-bdf4-d3557a4f1839', + text: 'Loss', + riskScore: 1, + }, + { + key: '28ece46e-c243-48cf-b8ca-8ef7583174d0', + text: 'Uncertainty', + riskScore: 2, + }, + { + key: '167a4ae3-63dd-48a6-be98-ad4307d049ee', + text: 'Opportunity', + riskScore: 3, + }, + { + key: '77142643-58e8-4d7c-84e7-d677602dc03c', + text: 'Thrill', + riskScore: 4, + }, + ], + }, + { + key: '4124bcd7-9481-432e-ba4b-aa4534c902c8', + text: 'Given the best and worst case returns of the four investment choices below, which would you prefer?', + choices: [ + { + key: '6ede11a4-683c-4234-a455-4828562c2a7b', + text: '$200 gain best case; $0 gain/loss worst case', + riskScore: 1, + }, + { + key: 'a0879e66-ad69-4c06-93cc-2c24cac44447', + text: '$800 gain best case; $200 loss worst case', + riskScore: 2, + }, + { + key: '591bb1e9-4b33-40fc-a660-11241b9a5ca7', + text: '$2,600 gain best case; $800 loss worst case', + riskScore: 3, + }, + { + key: 'e8180b64-ee56-42ae-9fb8-ef68b98df6d1', + text: '$4,800 gain best case; $2,400 loss worst case', + riskScore: 4, + }, + ], + }, + { + key: '1498e650-03f0-4a69-aa19-85232e0d8dff', + text: 'Suppose a relative left you an inheritance of $100,000, stipulating in the will that you invest ALL the money in ONE of the following choices. Which one would you select?', + choices: [ + { + key: '81a4ad12-031b-4c25-9afe-7b220f46cdb0', + text: 'A savings account or money market mutual fund', + riskScore: 1, + }, + { + key: 'c02ffdc6-5e18-48a0-9d8e-b8c92a56e42c', + text: 'A mutual fund that owns stocks and bonds', + riskScore: 2, + }, + { + key: '72c15026-2a43-43f1-85f2-5d0e2d6108df', + text: 'A portfolio of 15 common stocks', + riskScore: 3, + }, + { + key: '7e0b2a5f-5b26-4781-a50d-ecf878e21e59', + text: 'Commodities like gold, silver, and oil', + riskScore: 4, + }, + ], + }, +] + +export function calcRiskProfile(questions: RiskQuestion[], answers: RiskAnswer[]) { + function getRiskDescription(qualitativeScore: string) { + switch (qualitativeScore) { + case 'Low': + return 'What this means is you want some predictability in your portfolio, and would prefer to balance out some of the risk with stability.' + case 'Moderate': + return 'This means that you prefer investments that offer a modest rate of return with very little downside risk.' + case 'High': + return 'This just means that you are okay dealing with volatility and are likely trying to earn an aggressive rate of return over your peers.' + default: + throw new Error( + 'Invalid qualitative score provided, must be Low, Moderate, or High' + ) + } + } + + const withAnswers = questions.map((q) => { + const answer = answers.find((a) => a.questionKey === q.key) + return { + ...q, + answer: q.choices.find((c) => c.key === answer?.choiceKey), + } + }) + + const riskScores = withAnswers + .map((wa) => wa.answer?.riskScore) + .filter((s): s is number => s != null) + + if (!riskScores.length) return null + + const avgRiskScore = sum(riskScores) / riskScores.length + + const qualitativeScore = scaleQuantile({ + domain: [1, 2, 3, 4], + range: ['Low', 'Moderate', 'High'], + })(avgRiskScore) + + return { + score: avgRiskScore, + label: qualitativeScore, + description: getRiskDescription(qualitativeScore), + } +} diff --git a/libs/shared/src/utils/date-utils.spec.ts b/libs/shared/src/utils/date-utils.spec.ts new file mode 100644 index 00000000000..bbda444fcbb --- /dev/null +++ b/libs/shared/src/utils/date-utils.spec.ts @@ -0,0 +1,72 @@ +import { DateTime } from 'luxon' +import { ageToYear, calculateTimeSeriesInterval, dobToAge, yearToAge } from './date-utils' + +describe('calculateTimeSeriesInterval', () => { + it.each` + duration | chunks | interval + ${{ weeks: 1 }} | ${150} | ${'days'} + ${{ weeks: 1 }} | ${250} | ${'days'} + ${{ months: 1 }} | ${150} | ${'days'} + ${{ months: 1 }} | ${250} | ${'days'} + ${{ months: 3 }} | ${150} | ${'days'} + ${{ months: 3 }} | ${250} | ${'days'} + ${{ months: 6 }} | ${150} | ${'days'} + ${{ months: 6 }} | ${250} | ${'days'} + ${{ years: 1 }} | ${150} | ${'days'} + ${{ years: 1 }} | ${250} | ${'days'} + ${{ years: 2 }} | ${150} | ${'weeks'} + ${{ years: 2 }} | ${250} | ${'days'} + ${{ years: 3 }} | ${150} | ${'weeks'} + ${{ years: 3 }} | ${250} | ${'weeks'} + ${{ years: 5 }} | ${150} | ${'weeks'} + ${{ years: 5 }} | ${250} | ${'weeks'} + ${{ years: 10 }} | ${150} | ${'months'} + ${{ years: 10 }} | ${250} | ${'weeks'} + ${{ years: 20 }} | ${150} | ${'months'} + ${{ years: 20 }} | ${250} | ${'months'} + `( + `should calculate properly for duration: $duration chunks: $chunks`, + ({ duration, chunks, interval }) => { + const d = DateTime.now() + expect(calculateTimeSeriesInterval(d, d.plus(duration), chunks)).toBe(interval) + } + ) +}) + +describe('converts between years and ages', () => { + it.each` + dateOfBirth | currentAge + ${'1995-02-20'} | ${27} + ${new Date('Feb 20 1995')} | ${27} + ${DateTime.fromISO('1995-02-20')} | ${27} + ${null} | ${null} + ${undefined} | ${null} + ${'2022-10-15'} | ${0} + ${'2021-10-12'} | ${1} + ${'2021-10-18'} | ${0} + `(`dob $dateOfBirth is $currentAge years old today`, ({ dateOfBirth, currentAge }) => { + const now = DateTime.fromISO('2022-10-15') + + expect(dobToAge(dateOfBirth, now)).toBe(currentAge) + }) + + it.each` + age | year + ${30} | ${2027} + ${20} | ${2017} + `(`at age $currentAge the year will be $year`, ({ age, year }) => { + const currentAge = 25 + + expect(ageToYear(age, currentAge, 2022)).toBe(year) + }) + + it.each` + year | age + ${2027} | ${30} + ${2017} | ${20} + `(`at year $year the age will be $age`, ({ age, year }) => { + const currentAge = 25 + + expect(yearToAge(year, currentAge, 2022)).toBe(age) + }) +}) diff --git a/libs/shared/src/utils/date-utils.ts b/libs/shared/src/utils/date-utils.ts new file mode 100644 index 00000000000..ac87503e2c5 --- /dev/null +++ b/libs/shared/src/utils/date-utils.ts @@ -0,0 +1,140 @@ +import type { SharedType } from '..' +import type { NiceTime } from '@visx/scale' +import type { TimeSeriesInterval } from '../types' +import { DateTime } from 'luxon' +import range from 'lodash/range' + +export function generateDailySeries(start: string, end: string, zone = 'utc'): string[] { + const s = DateTime.fromISO(start, { zone }) + const e = DateTime.fromISO(end, { zone }) + const daysBetween = Math.abs(s.diff(e, 'days').days) + + return range(0, daysBetween + 1, 1).map((idx) => s.plus({ days: idx }).toFormat('yyyy-MM-dd')) +} + +export function isToday(date: Date | string | null | undefined, today = DateTime.utc()): boolean { + if (!date) return false + return isSameDate(datetimeTransform(date), today) +} + +export function isSameDate(date: DateTime, as: DateTime): boolean { + return date.toUTC().toISODate() === as.toUTC().toISODate() +} + +export function datetimeTransform(val: Date | string): DateTime +export function datetimeTransform(val: Date | string | null): DateTime | null +export function datetimeTransform(val: Date | string | undefined): DateTime | undefined +export function datetimeTransform( + val: Date | string | null | undefined +): DateTime | null | undefined { + if (val === undefined) return undefined + if (val === null) return null + const dt = + typeof val === 'string' + ? DateTime.fromISO(val, { zone: 'utc' }) + : DateTime.fromJSDate(val, { zone: 'utc' }) + if (!dt.isValid) throw new Error(`invalid datetime: ${val}`) + return dt +} + +// Validates ISO string date and returns ISO string +export function dateTransform(val: Date | string): string +export function dateTransform(val: Date | string | null): string | null +export function dateTransform(val: Date | string | undefined): string | undefined +export function dateTransform(val: Date | string | null | undefined): string | null | undefined { + if (val === undefined) return undefined + if (val === null) return null + const d = + typeof val === 'string' + ? DateTime.fromISO(val, { zone: 'utc' }) + : DateTime.fromJSDate(val, { zone: 'utc' }) + if (!d.isValid) throw new Error(`invalid ISO8601 date: ${val}`) + return d.toISODate() +} + +export function strToDate(val: string, zone = 'utc'): Date { + return DateTime.fromISO(val, { zone }).toJSDate() +} + +export function dateToStr(val: Date, zone = 'utc'): string { + return DateTime.fromJSDate(val, { zone }).toISODate() +} + +export function calculateTimeSeriesInterval( + start: string | DateTime, + end: string | DateTime, + desiredChunks = 150 +): SharedType.TimeSeriesInterval { + const INTERVALS: [SharedType.TimeSeriesInterval, number][] = [ + ['days', 1], + ['weeks', 7], + ['months', 30], + ['quarters', 91], + ['years', 365], + ] + + const startDate = typeof start === 'string' ? DateTime.fromISO(start) : start + const endDate = typeof end === 'string' ? DateTime.fromISO(end) : end + const diff = endDate.diff(startDate, 'days') + + // determine exact optimal interval and then find the closest actual interval + const goal = Math.abs(diff.days) / desiredChunks + const closestInterval = INTERVALS.reduce((best, curr) => { + return Math.abs(curr[1] - goal) < Math.abs(best[1] - goal) ? curr : best + }) + + return closestInterval[0] +} + +/** + * Temporary mapping to avoid full refactor of all time-series values + * @todo - update `TimeSeriesInterval` to have same types as `NiceTime` + */ +export function toD3Interval(interval: TimeSeriesInterval): NiceTime { + switch (interval) { + case 'days': + return 'day' + case 'weeks': + return 'week' + case 'months': + return 'month' + case 'quarters': + return 'month' // no quarterly value available + case 'years': + return 'year' + default: + return 'day' + } +} + +/** + * Converts a calendar year to an age based on the current age + */ +export function yearToAge(year: number, currentAge = 30, currentYear = DateTime.now().year) { + return year - currentYear + currentAge +} + +/** + * Converts an age to a calendar year based on the current age + */ +export function ageToYear(age: number, currentAge = 30, currentYear = DateTime.now().year) { + return age - currentAge + currentYear +} + +/** Calculates an age from a DOB in ISO string format */ +export function dobToAge(dob: string | Date | DateTime | null | undefined, now = DateTime.now()) { + if (!dob) return null + + const normalizedDate = + typeof dob === 'string' + ? DateTime.fromISO(dob, { zone: 'utc' }) + : dob instanceof Date + ? DateTime.fromJSDate(dob, { zone: 'utc' }) + : dob + + return Math.floor(now.diff(normalizedDate, 'years').years) +} + +// We allow a maximum of 30 years of history for performance reasons (hypertable chunking) +export const MIN_SUPPORTED_DATE = DateTime.now().minus({ years: 30 }) +export const MAX_SUPPORTED_DATE = DateTime.now() diff --git a/libs/shared/src/utils/geo-utils.ts b/libs/shared/src/utils/geo-utils.ts new file mode 100644 index 00000000000..a211b0bffe5 --- /dev/null +++ b/libs/shared/src/utils/geo-utils.ts @@ -0,0 +1,305 @@ +export const countries = [ + { name: 'Afghanistan', code: 'AF' }, + { name: 'Åland Islands', code: 'AX' }, + { name: 'Albania', code: 'AL' }, + { name: 'Algeria', code: 'DZ' }, + { name: 'American Samoa', code: 'AS' }, + { name: 'Andorra', code: 'AD' }, + { name: 'Angola', code: 'AO' }, + { name: 'Anguilla', code: 'AI' }, + { name: 'Antarctica', code: 'AQ' }, + { name: 'Antigua and Barbuda', code: 'AG' }, + { name: 'Argentina', code: 'AR' }, + { name: 'Armenia', code: 'AM' }, + { name: 'Aruba', code: 'AW' }, + { name: 'Australia', code: 'AU' }, + { name: 'Austria', code: 'AT' }, + { name: 'Azerbaijan', code: 'AZ' }, + { name: 'Bahamas', code: 'BS' }, + { name: 'Bahrain', code: 'BH' }, + { name: 'Bangladesh', code: 'BD' }, + { name: 'Barbados', code: 'BB' }, + { name: 'Belarus', code: 'BY' }, + { name: 'Belgium', code: 'BE' }, + { name: 'Belize', code: 'BZ' }, + { name: 'Benin', code: 'BJ' }, + { name: 'Bermuda', code: 'BM' }, + { name: 'Bhutan', code: 'BT' }, + { name: 'Bolivia (Plurinational State of)', code: 'BO' }, + { name: 'Bonaire, Sint Eustatius and Saba', code: 'BQ' }, + { name: 'Bosnia and Herzegovina', code: 'BA' }, + { name: 'Botswana', code: 'BW' }, + { name: 'Bouvet Island', code: 'BV' }, + { name: 'Brazil', code: 'BR' }, + { name: 'British Indian Ocean Territory', code: 'IO' }, + { name: 'Brunei Darussalam', code: 'BN' }, + { name: 'Bulgaria', code: 'BG' }, + { name: 'Burkina Faso', code: 'BF' }, + { name: 'Burundi', code: 'BI' }, + { name: 'Cabo Verde', code: 'CV' }, + { name: 'Cambodia', code: 'KH' }, + { name: 'Cameroon', code: 'CM' }, + { name: 'Canada', code: 'CA' }, + { name: 'Cayman Islands', code: 'KY' }, + { name: 'Central African Republic', code: 'CF' }, + { name: 'Chad', code: 'TD' }, + { name: 'Chile', code: 'CL' }, + { name: 'China', code: 'CN' }, + { name: 'Christmas Island', code: 'CX' }, + { name: 'Cocos (Keeling) Islands', code: 'CC' }, + { name: 'Colombia', code: 'CO' }, + { name: 'Comoros', code: 'KM' }, + { name: 'Congo', code: 'CG' }, + { name: 'Congo, Democratic Republic of the', code: 'CD' }, + { name: 'Cook Islands', code: 'CK' }, + { name: 'Costa Rica', code: 'CR' }, + { name: "Côte d'Ivoire", code: 'CI' }, + { name: 'Croatia', code: 'HR' }, + { name: 'Cuba', code: 'CU' }, + { name: 'Curaçao', code: 'CW' }, + { name: 'Cyprus', code: 'CY' }, + { name: 'Czechia', code: 'CZ' }, + { name: 'Denmark', code: 'DK' }, + { name: 'Djibouti', code: 'DJ' }, + { name: 'Dominica', code: 'DM' }, + { name: 'Dominican Republic', code: 'DO' }, + { name: 'Ecuador', code: 'EC' }, + { name: 'Egypt', code: 'EG' }, + { name: 'El Salvador', code: 'SV' }, + { name: 'Equatorial Guinea', code: 'GQ' }, + { name: 'Eritrea', code: 'ER' }, + { name: 'Estonia', code: 'EE' }, + { name: 'Eswatini', code: 'SZ' }, + { name: 'Ethiopia', code: 'ET' }, + { name: 'Falkland Islands (Malvinas)', code: 'FK' }, + { name: 'Faroe Islands', code: 'FO' }, + { name: 'Fiji', code: 'FJ' }, + { name: 'Finland', code: 'FI' }, + { name: 'France', code: 'FR' }, + { name: 'French Guiana', code: 'GF' }, + { name: 'French Polynesia', code: 'PF' }, + { name: 'French Southern Territories', code: 'TF' }, + { name: 'Gabon', code: 'GA' }, + { name: 'Gambia', code: 'GM' }, + { name: 'Georgia', code: 'GE' }, + { name: 'Germany', code: 'DE' }, + { name: 'Ghana', code: 'GH' }, + { name: 'Gibraltar', code: 'GI' }, + { name: 'Greece', code: 'GR' }, + { name: 'Greenland', code: 'GL' }, + { name: 'Grenada', code: 'GD' }, + { name: 'Guadeloupe', code: 'GP' }, + { name: 'Guam', code: 'GU' }, + { name: 'Guatemala', code: 'GT' }, + { name: 'Guernsey', code: 'GG' }, + { name: 'Guinea', code: 'GN' }, + { name: 'Guinea-Bissau', code: 'GW' }, + { name: 'Guyana', code: 'GY' }, + { name: 'Haiti', code: 'HT' }, + { name: 'Heard Island and McDonald Islands', code: 'HM' }, + { name: 'Holy See', code: 'VA' }, + { name: 'Honduras', code: 'HN' }, + { name: 'Hong Kong', code: 'HK' }, + { name: 'Hungary', code: 'HU' }, + { name: 'Iceland', code: 'IS' }, + { name: 'India', code: 'IN' }, + { name: 'Indonesia', code: 'ID' }, + { name: 'Iran (Islamic Republic of)', code: 'IR' }, + { name: 'Iraq', code: 'IQ' }, + { name: 'Ireland', code: 'IE' }, + { name: 'Isle of Man', code: 'IM' }, + { name: 'Israel', code: 'IL' }, + { name: 'Italy', code: 'IT' }, + { name: 'Jamaica', code: 'JM' }, + { name: 'Japan', code: 'JP' }, + { name: 'Jersey', code: 'JE' }, + { name: 'Jordan', code: 'JO' }, + { name: 'Kazakhstan', code: 'KZ' }, + { name: 'Kenya', code: 'KE' }, + { name: 'Kiribati', code: 'KI' }, + { name: "Korea (Democratic People's Republic of)", code: 'KP' }, + { name: 'Korea, Republic of', code: 'KR' }, + { name: 'Kuwait', code: 'KW' }, + { name: 'Kyrgyzstan', code: 'KG' }, + { name: "Lao People's Democratic Republic", code: 'LA' }, + { name: 'Latvia', code: 'LV' }, + { name: 'Lebanon', code: 'LB' }, + { name: 'Lesotho', code: 'LS' }, + { name: 'Liberia', code: 'LR' }, + { name: 'Libya', code: 'LY' }, + { name: 'Liechtenstein', code: 'LI' }, + { name: 'Lithuania', code: 'LT' }, + { name: 'Luxembourg', code: 'LU' }, + { name: 'Macao', code: 'MO' }, + { name: 'Madagascar', code: 'MG' }, + { name: 'Malawi', code: 'MW' }, + { name: 'Malaysia', code: 'MY' }, + { name: 'Maldives', code: 'MV' }, + { name: 'Mali', code: 'ML' }, + { name: 'Malta', code: 'MT' }, + { name: 'Marshall Islands', code: 'MH' }, + { name: 'Martinique', code: 'MQ' }, + { name: 'Mauritania', code: 'MR' }, + { name: 'Mauritius', code: 'MU' }, + { name: 'Mayotte', code: 'YT' }, + { name: 'Mexico', code: 'MX' }, + { name: 'Micronesia (Federated States of)', code: 'FM' }, + { name: 'Moldova, Republic of', code: 'MD' }, + { name: 'Monaco', code: 'MC' }, + { name: 'Mongolia', code: 'MN' }, + { name: 'Montenegro', code: 'ME' }, + { name: 'Montserrat', code: 'MS' }, + { name: 'Morocco', code: 'MA' }, + { name: 'Mozambique', code: 'MZ' }, + { name: 'Myanmar', code: 'MM' }, + { name: 'Namibia', code: 'NA' }, + { name: 'Nauru', code: 'NR' }, + { name: 'Nepal', code: 'NP' }, + { name: 'Netherlands', code: 'NL' }, + { name: 'New Caledonia', code: 'NC' }, + { name: 'New Zealand', code: 'NZ' }, + { name: 'Nicaragua', code: 'NI' }, + { name: 'Niger', code: 'NE' }, + { name: 'Nigeria', code: 'NG' }, + { name: 'Niue', code: 'NU' }, + { name: 'Norfolk Island', code: 'NF' }, + { name: 'North Macedonia', code: 'MK' }, + { name: 'Northern Mariana Islands', code: 'MP' }, + { name: 'Norway', code: 'NO' }, + { name: 'Oman', code: 'OM' }, + { name: 'Pakistan', code: 'PK' }, + { name: 'Palau', code: 'PW' }, + { name: 'Palestine, State of', code: 'PS' }, + { name: 'Panama', code: 'PA' }, + { name: 'Papua New Guinea', code: 'PG' }, + { name: 'Paraguay', code: 'PY' }, + { name: 'Peru', code: 'PE' }, + { name: 'Philippines', code: 'PH' }, + { name: 'Pitcairn', code: 'PN' }, + { name: 'Poland', code: 'PL' }, + { name: 'Portugal', code: 'PT' }, + { name: 'Puerto Rico', code: 'PR' }, + { name: 'Qatar', code: 'QA' }, + { name: 'Réunion', code: 'RE' }, + { name: 'Romania', code: 'RO' }, + { name: 'Russian Federation', code: 'RU' }, + { name: 'Rwanda', code: 'RW' }, + { name: 'Saint Barthélemy', code: 'BL' }, + { name: 'Saint Helena, Ascension and Tristan da Cunha', code: 'SH' }, + { name: 'Saint Kitts and Nevis', code: 'KN' }, + { name: 'Saint Lucia', code: 'LC' }, + { name: 'Saint Martin (French part)', code: 'MF' }, + { name: 'Saint Pierre and Miquelon', code: 'PM' }, + { name: 'Saint Vincent and the Grenadines', code: 'VC' }, + { name: 'Samoa', code: 'WS' }, + { name: 'San Marino', code: 'SM' }, + { name: 'Sao Tome and Principe', code: 'ST' }, + { name: 'Saudi Arabia', code: 'SA' }, + { name: 'Senegal', code: 'SN' }, + { name: 'Serbia', code: 'RS' }, + { name: 'Seychelles', code: 'SC' }, + { name: 'Sierra Leone', code: 'SL' }, + { name: 'Singapore', code: 'SG' }, + { name: 'Sint Maarten (Dutch part)', code: 'SX' }, + { name: 'Slovakia', code: 'SK' }, + { name: 'Slovenia', code: 'SI' }, + { name: 'Solomon Islands', code: 'SB' }, + { name: 'Somalia', code: 'SO' }, + { name: 'South Africa', code: 'ZA' }, + { name: 'South Georgia and the South Sandwich Islands', code: 'GS' }, + { name: 'South Sudan', code: 'SS' }, + { name: 'Spain', code: 'ES' }, + { name: 'Sri Lanka', code: 'LK' }, + { name: 'Sudan', code: 'SD' }, + { name: 'Suriname', code: 'SR' }, + { name: 'Svalbard and Jan Mayen', code: 'SJ' }, + { name: 'Sweden', code: 'SE' }, + { name: 'Switzerland', code: 'CH' }, + { name: 'Syrian Arab Republic', code: 'SY' }, + { name: 'Taiwan, Province of China', code: 'TW' }, + { name: 'Tajikistan', code: 'TJ' }, + { name: 'Tanzania, United Republic of', code: 'TZ' }, + { name: 'Thailand', code: 'TH' }, + { name: 'Timor-Leste', code: 'TL' }, + { name: 'Togo', code: 'TG' }, + { name: 'Tokelau', code: 'TK' }, + { name: 'Tonga', code: 'TO' }, + { name: 'Trinidad and Tobago', code: 'TT' }, + { name: 'Tunisia', code: 'TN' }, + { name: 'Turkey', code: 'TR' }, + { name: 'Turkmenistan', code: 'TM' }, + { name: 'Turks and Caicos Islands', code: 'TC' }, + { name: 'Tuvalu', code: 'TV' }, + { name: 'Uganda', code: 'UG' }, + { name: 'Ukraine', code: 'UA' }, + { name: 'United Arab Emirates', code: 'AE' }, + { name: 'United Kingdom of Great Britain and Northern Ireland', code: 'GB' }, + { name: 'United States', code: 'US' }, + { name: 'United States Minor Outlying Islands', code: 'UM' }, + { name: 'Uruguay', code: 'UY' }, + { name: 'Uzbekistan', code: 'UZ' }, + { name: 'Vanuatu', code: 'VU' }, + { name: 'Venezuela (Bolivarian Republic of)', code: 'VE' }, + { name: 'Viet Nam', code: 'VN' }, + { name: 'Virgin Islands (British)', code: 'VG' }, + { name: 'Virgin Islands (U.S.)', code: 'VI' }, + { name: 'Wallis and Futuna', code: 'WF' }, + { name: 'Western Sahara', code: 'EH' }, + { name: 'Yemen', code: 'YE' }, + { name: 'Zambia', code: 'ZM' }, + { name: 'Zimbabwe', code: 'ZW' }, +] + +export const states = [ + { name: 'Alabama', code: 'AL' }, + { name: 'Alaska', code: 'AK' }, + { name: 'Arizona', code: 'AZ' }, + { name: 'Arkansas', code: 'AR' }, + { name: 'California', code: 'CA' }, + { name: 'Colorado', code: 'CO' }, + { name: 'Connecticut', code: 'CT' }, + { name: 'Delaware', code: 'DE' }, + { name: 'District of Columbia', code: 'DC' }, + { name: 'Florida', code: 'FL' }, + { name: 'Georgia', code: 'GA' }, + { name: 'Hawaii', code: 'HI' }, + { name: 'Idaho', code: 'ID' }, + { name: 'Illinois', code: 'IL' }, + { name: 'Indiana', code: 'IN' }, + { name: 'Iowa', code: 'IA' }, + { name: 'Kansas', code: 'KS' }, + { name: 'Kentucky', code: 'KY' }, + { name: 'Louisiana', code: 'LA' }, + { name: 'Maine', code: 'ME' }, + { name: 'Maryland', code: 'MD' }, + { name: 'Massachusetts', code: 'MA' }, + { name: 'Michigan', code: 'MI' }, + { name: 'Minnesota', code: 'MN' }, + { name: 'Mississippi', code: 'MS' }, + { name: 'Missouri', code: 'MO' }, + { name: 'Montana', code: 'MT' }, + { name: 'Nebraska', code: 'NE' }, + { name: 'Nevada', code: 'NV' }, + { name: 'New Hampshire', code: 'NH' }, + { name: 'New Jersey', code: 'NJ' }, + { name: 'New Mexico', code: 'NM' }, + { name: 'New York', code: 'NY' }, + { name: 'North Carolina', code: 'NC' }, + { name: 'North Dakota', code: 'ND' }, + { name: 'Ohio', code: 'OH' }, + { name: 'Oklahoma', code: 'OK' }, + { name: 'Oregon', code: 'OR' }, + { name: 'Pennsylvania', code: 'PA' }, + { name: 'Rhode Island', code: 'RI' }, + { name: 'South Carolina', code: 'SC' }, + { name: 'South Dakota', code: 'SD' }, + { name: 'Tennessee', code: 'TN' }, + { name: 'Texas', code: 'TX' }, + { name: 'Utah', code: 'UT' }, + { name: 'Vermont', code: 'VT' }, + { name: 'Virginia', code: 'VA' }, + { name: 'Washington', code: 'WA' }, + { name: 'West Virginia', code: 'WV' }, + { name: 'Wisconsin', code: 'WI' }, + { name: 'Wyoming', code: 'WY' }, +] diff --git a/libs/shared/src/utils/index.ts b/libs/shared/src/utils/index.ts new file mode 100644 index 00000000000..e55106f15f2 --- /dev/null +++ b/libs/shared/src/utils/index.ts @@ -0,0 +1,12 @@ +export * as DateUtil from './date-utils' +export * as NumberUtil from './number-utils' +export * as SharedUtil from './shared-utils' +export * as TestUtil from './test-utils' +export * as AccountUtil from './account-utils' +export * as TransactionUtil from './transaction-utils' +export * as MarketUtil from './market-utils' +export * as PlanUtil from './plan-utils' +export * as StatsUtil from './stats-utils' +export * as ATAUtil from './ata-utils' +export * as UserUtil from './user-utils' +export * as Geo from './geo-utils' diff --git a/libs/shared/src/utils/market-utils.ts b/libs/shared/src/utils/market-utils.ts new file mode 100644 index 00000000000..7c381b428db --- /dev/null +++ b/libs/shared/src/utils/market-utils.ts @@ -0,0 +1,9 @@ +// Option ticker reference: https://www.investopedia.com/ask/answers/05/052505.asp + +export function isOptionTicker(ticker: string): boolean { + return ticker.length >= 16 +} + +export function getUnderlyingTicker(ticker: string): string | null { + return ticker.length >= 16 ? ticker.slice(0, -15).trim() : null +} diff --git a/libs/shared/src/utils/number-utils.spec.ts b/libs/shared/src/utils/number-utils.spec.ts new file mode 100644 index 00000000000..708626293dd --- /dev/null +++ b/libs/shared/src/utils/number-utils.spec.ts @@ -0,0 +1,111 @@ +import { calculatePercentChange, format } from './number-utils' + +describe('number-utils', () => { + describe('calculatePercentChange', () => { + test.each([ + [0, 100, Infinity], + [0, -100, -Infinity], + [0, 0, 0], + [100, 100, 0], + [-100, -100, 0], + [100, 200, 1], + [200, 100, -0.5], + [null, 100, NaN], + [100, null, NaN], + [null, null, NaN], + [NaN, 100, NaN], + [100, NaN, NaN], + [NaN, NaN, NaN], + ])('(%i, %i) === %s', (from, to, percentage) => { + expect(calculatePercentChange(from, to)).toStrictEqual(percentage) + }) + }) + + describe('currency formatting edge cases', () => { + test.each([ + [NaN, '--'], + [Infinity, `∞`], + [-Infinity, `-∞`], + [undefined, '--'], + [null, '--'], + ['invalid number string', '--'], + ])('input=%s output=%s', (value, expectedValue) => { + expect(format(value, 'currency')).toStrictEqual(expectedValue) + }) + }) + + describe('percentage formatting edge cases', () => { + test.each([ + [NaN, '--'], + [Infinity, `∞%`], + [-Infinity, `-∞%`], + [undefined, '--'], + [null, '--'], + ['invalid number string', '--'], + ])('input=%s output=%s', (value, expectedValue) => { + expect(format(value, 'percent')).toStrictEqual(expectedValue) + }) + }) + + describe('currency format', () => { + test.each([ + ['20', '$20.00', undefined], + [20, '$20.00', undefined], + [20.45, '$20.45', undefined], + [20, '$20.00', undefined], + [2000, '$2,000.00', undefined], + [2000, '$2,000', { minimumFractionDigits: 0 }], + [2000.2, '$2,000.2', { minimumFractionDigits: 0 }], + [2000.25, '$2,000.25', { minimumFractionDigits: 0 }], + [2000.25, '$2,000', { minimumFractionDigits: 0, maximumFractionDigits: 0 }], + ])('input=%s output=%s', (value, expectedValue, options) => { + expect(format(value, 'currency', options)).toStrictEqual(expectedValue) + }) + }) + + describe('short currency format', () => { + test.each([ + ['20', '$20.00', undefined], + [20, '$20.00', undefined], + [20.2, '$20.20', { minimumFractionDigits: 2, maximumFractionDigits: 2 }], + [20, '$20.0', { minimumFractionDigits: 1, maximumFractionDigits: 2 }], + [20.2, '$20.20', undefined], + [20000, '$20k', undefined], + [-20000, '-$20k', undefined], + [-28000, '-$28k', undefined], + [-28000, '-$28k', { minimumFractionDigits: 2, maximumFractionDigits: 2 }], + [-28500, '-$28.5k', { minimumFractionDigits: 2, maximumFractionDigits: 2 }], + [-28000, '-$28k', { minimumFractionDigits: 1, maximumFractionDigits: 2 }], + [-28500, '-$28.5k', { minimumFractionDigits: 1, maximumFractionDigits: 2 }], + [-28550, '-$28.55k', { minimumFractionDigits: 1, maximumFractionDigits: 2 }], + [-28550, '-$28.6k', { minimumFractionDigits: 1, maximumFractionDigits: 1 }], + [-28550, '-$29k', { minimumFractionDigits: 0, maximumFractionDigits: 0 }], + [2_000_000, '$2m', undefined], + [2_000_000, '$2m', undefined], + [2_000_000_000, '$2b', undefined], + [2_500_000_000, '$2.5b', undefined], + [2_540_000_000, '$2.54b', undefined], + [2_560_000_000, '$2.56b', undefined], + [2_560_000_000, '$2.56b', { minimumFractionDigits: 2, maximumFractionDigits: 2 }], + [2_500_000_000, '$2.5b', { minimumFractionDigits: 2, maximumFractionDigits: 2 }], + [0, '$0.00', undefined], + ])('input=%s output=%s', (value, expectedValue, options) => { + expect(format(value, 'short-currency', options)).toStrictEqual(expectedValue) + }) + }) + + describe('percent format', () => { + test.each([ + ['0.02', '+2%', undefined], + [0.02, '+2%', undefined], + ['-0.02', '-2%', undefined], + [-0.02, '-2%', undefined], + [20, '+2,000%', undefined], + [20000, '+2,000,000%', undefined], + [-20000, '-2,000,000%', undefined], + [0, '0%', undefined], + ])('input=%s output=%s', (value, expectedValue, options) => { + expect(format(value, 'percent', options)).toStrictEqual(expectedValue) + }) + }) +}) diff --git a/libs/shared/src/utils/number-utils.ts b/libs/shared/src/utils/number-utils.ts new file mode 100644 index 00000000000..33bb2743e80 --- /dev/null +++ b/libs/shared/src/utils/number-utils.ts @@ -0,0 +1,118 @@ +import type { Decimal, FormatString } from '../types' +import DecimalJS from 'decimal.js' + +export function calculatePercentChange(_from: Decimal | null, _to: Decimal | null): Decimal +export function calculatePercentChange(_from: number | null, _to: number | null): number +export function calculatePercentChange( + _from: Decimal | number | null, + _to: Decimal | number | null +): Decimal | number { + const isDecimal = DecimalJS.isDecimal(_from) || DecimalJS.isDecimal(_to) + + if (_from == null || _to == null) return isDecimal ? new DecimalJS(NaN) : NaN + + const from = new DecimalJS(_from.toString()) + const to = new DecimalJS(_to.toString()) + + const diff = to.minus(from) + + const pctChange = diff.isZero() ? new DecimalJS(0) : diff.dividedBy(from.abs()) + + return isDecimal ? pctChange : pctChange.toNumber() +} + +export function format( + value: Decimal | number | string | undefined | null, + format: FormatString, + options?: Intl.NumberFormatOptions +): string { + if (value == null) return '--' + const _value = +value + + // Catches anything that's not a valid number + if (!Number.isFinite(_value)) { + switch (_value) { + case Infinity: + return format === 'percent' ? `∞%` : `∞` + case -Infinity: + return format === 'percent' ? `-∞%` : `-∞` + default: + return '--' + } + } + + const defaultCurrencyOptions: Intl.NumberFormatOptions = { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, // defaults to $X.XX + maximumFractionDigits: 2, + } + + const defaultDecimalOptions: Intl.NumberFormatOptions = { + style: 'decimal', + currency: 'USD', + minimumFractionDigits: 0, + maximumFractionDigits: 2, + } + + if (format === 'currency') { + return _value.toLocaleString('en-US', { ...defaultCurrencyOptions, ...options }) + } + + if (format === 'percent') { + const defaultPercentageOptions: Intl.NumberFormatOptions = { + style: 'percent', + signDisplay: 'exceptZero', + minimumFractionDigits: 0, + maximumFractionDigits: 1, + } + + return _value.toLocaleString('en-US', { ...defaultPercentageOptions, ...options }) + } + + if (format === 'decimal') { + return _value.toLocaleString('en-US', { ...defaultDecimalOptions, ...options }) + } + + const shortUnits = [ + { value: 1e12, symbol: 't' }, + { value: 1e9, symbol: 'b' }, + { value: 1e6, symbol: 'm' }, + { value: 1e3, symbol: 'k' }, + { value: 1, symbol: '' }, + ] + + // This should always be the last case because regexp are expensive computations + if (['short-currency', 'short-decimal'].includes(format)) { + const defaultOptions = + format === 'short-currency' ? defaultCurrencyOptions : defaultDecimalOptions + + const item = shortUnits.find(function (item) { + return Math.abs(_value) >= item.value + }) + + if (!item) { + return _value.toLocaleString('en-US', { ...defaultOptions, ...options }) + } + + const initialString = (_value / item.value).toLocaleString('en-US', { + ...defaultOptions, + ...options, + }) + + // For larger numbers like "$20.00k", strip the zeroes at the end ($20.00k => $20k) + const rx = /\.0+$|(\.[0-9]*[1-9])0+$/ + const stripZeroString = + Math.abs(_value) > 999 ? initialString.replace(rx, '$1') : initialString + return stripZeroString + item.symbol + } + + throw new Error('Invalid format type') +} + +/** + * Lodash `sumBy` equivalent that supports Decimal.js + */ +export function sumBy(a: T[] | null | undefined, by: (item: T) => DecimalJS): DecimalJS { + return a != null && a.length > 0 ? DecimalJS.sum(...a.map(by)) : new DecimalJS(0) +} diff --git a/libs/shared/src/utils/plan-utils.ts b/libs/shared/src/utils/plan-utils.ts new file mode 100644 index 00000000000..a4d847ab565 --- /dev/null +++ b/libs/shared/src/utils/plan-utils.ts @@ -0,0 +1,20 @@ +import type { SharedType } from '..' +import type { PlanProjectionResponse } from '../types' + +export const DEFAULT_AGE = 30 +export const DEFAULT_LIFE_EXPECTANCY = 85 +export const CONFIDENCE_INTERVAL = 0.9 +export const RETIREMENT_MILESTONE_AGE = 65 + +export enum PlanEventCategory {} +export enum PlanMilestoneCategory { + Retirement = 'retirement', + FI = 'fi', +} + +export function resolveMilestoneYear( + projection: PlanProjectionResponse['projection'], + id: SharedType.PlanMilestone['id'] +): number | undefined { + return projection.data.find((d) => d.values.milestones.some((m) => m.id === id))?.values.year +} diff --git a/libs/shared/src/utils/shared-utils.spec.ts b/libs/shared/src/utils/shared-utils.spec.ts new file mode 100644 index 00000000000..bc284ff7c60 --- /dev/null +++ b/libs/shared/src/utils/shared-utils.spec.ts @@ -0,0 +1,151 @@ +import _ from 'lodash' +import { paginate, paginateIt, chunkIt, withRetry } from './shared-utils' + +describe('paginate', () => { + it.each` + pageSize | dataSize | fetchCalls + ${10} | ${0} | ${1} + ${10} | ${1} | ${1} + ${10} | ${10} | ${2} + ${10} | ${11} | ${2} + ${10} | ${20} | ${3} + `( + `paginates correctly: (pageSize: $pageSize, dataSize: $dataSize)`, + async ({ pageSize, dataSize, fetchCalls }) => { + const mockFetchData = jest.fn((offset, count) => + Promise.resolve(_.slice(_.range(dataSize), offset, offset + count)) + ) + + const result = await paginate({ + pageSize, + fetchData: mockFetchData, + }) + + expect(result).toHaveLength(dataSize) + expect(result).toEqual(_.range(dataSize)) + + expect(mockFetchData).toHaveBeenCalledTimes(fetchCalls) + _.range(fetchCalls).map((i) => { + expect(mockFetchData).toHaveBeenNthCalledWith(i + 1, i * pageSize, pageSize) + }) + } + ) +}) + +describe('paginateIt', () => { + it.each` + pageSize | dataSize | fetchCalls + ${10} | ${0} | ${1} + ${10} | ${1} | ${1} + ${10} | ${10} | ${2} + ${10} | ${11} | ${2} + ${10} | ${20} | ${3} + `( + `paginates correctly: (pageSize: $pageSize, dataSize: $dataSize)`, + async ({ pageSize, dataSize, fetchCalls }) => { + const mockFetchData = jest.fn((offset, count) => + Promise.resolve(_.slice(_.range(dataSize), offset, offset + count)) + ) + + const it = paginateIt({ + pageSize, + fetchData: mockFetchData, + }) + + const result = [] + for await (const page of it) { + result.push(...page) + } + + expect(result).toHaveLength(dataSize) + expect(result).toEqual(_.range(dataSize)) + + expect(mockFetchData).toHaveBeenCalledTimes(fetchCalls) + _.range(fetchCalls).map((i) => { + expect(mockFetchData).toHaveBeenNthCalledWith(i + 1, i * pageSize, pageSize) + }) + } + ) +}) + +describe('chunkIt', () => { + it('lazily chunks iterable', async () => { + const data = ['a', 'b', 'c'] + + const yieldTracker = jest.fn(() => { + /* noop */ + }) + + const iterable: AsyncIterable = { + async *[Symbol.asyncIterator]() { + for (const x of data) { + yieldTracker() + yield x + } + }, + } + + const it = chunkIt(iterable, 2) + + const chunk1 = await it.next() + expect(chunk1.value).toHaveLength(2) + expect(chunk1.value).toEqual(['a', 'b']) + expect(yieldTracker).toHaveBeenCalledTimes(2) + + const chunk2 = await it.next() + expect(chunk2.value).toHaveLength(1) + expect(chunk2.value).toEqual(['c']) + expect(yieldTracker).toHaveBeenCalledTimes(3) + }) +}) + +describe('withRetry', () => { + it.each` + failAttempts | maxRetries + ${0} | ${5} + ${1} | ${5} + ${2} | ${5} + ${5} | ${5} + `( + `retries correctly: (failAttempts: $failAttempts, maxRetries: $maxRetries)`, + async ({ failAttempts, maxRetries }) => { + const mock = jest.fn((attempt) => { + if (attempt < failAttempts) throw new Error(`keep trying!`) + return 'done' + }) + + await withRetry(mock, { maxRetries }) + + expect(mock).toHaveBeenCalledTimes(failAttempts + 1) + _.range(failAttempts + 1).map((i) => { + expect(mock).toHaveBeenNthCalledWith(i + 1, i) + }) + } + ) + + it(`throws last error`, async () => { + const maxRetries = 5 + + const mock = jest.fn((attempt) => { + throw new Error(`keep trying! attempt: ${attempt}`) + }) + + expect(withRetry(mock, { maxRetries })).rejects.toThrow() + expect(mock).toHaveBeenCalledTimes(maxRetries + 1) + }) + + it(`obeys onError poison pill`, async () => { + const maxRetries = 5 + const exitAfterAttempts = 1 + + const mock = jest.fn((attempt) => { + throw new Error(`keep trying! attempt: ${attempt}`) + }) + + const mockOnError = jest.fn((_err, attempt) => attempt < exitAfterAttempts) + + expect(withRetry(mock, { maxRetries, onError: mockOnError })).rejects.toThrow() + expect(mock).toHaveBeenCalledTimes(exitAfterAttempts + 1) + expect(mockOnError).toHaveBeenCalledTimes(exitAfterAttempts + 1) + }) +}) diff --git a/libs/shared/src/utils/shared-utils.ts b/libs/shared/src/utils/shared-utils.ts new file mode 100644 index 00000000000..85cbfb3a556 --- /dev/null +++ b/libs/shared/src/utils/shared-utils.ts @@ -0,0 +1,151 @@ +export function isNull(value: T | null | undefined): value is null | undefined { + return value == null +} + +export function nonNull(value: T | null | undefined): value is NonNullable { + return !isNull(value) +} + +export function isFullfilled(p: PromiseSettledResult): p is PromiseFulfilledResult { + return p.status === 'fulfilled' +} + +export function stringToArray(s: string): string[] +export function stringToArray(s: string | null | undefined): string[] | undefined +export function stringToArray(s: string | null | undefined, sep = ','): string[] | undefined { + return s?.split(sep).map((x) => x.trim()) +} + +/** + * Helper function for paginating data (typically from an API) + */ +export async function paginate({ + fetchData, + pageSize, + delay, +}: { + fetchData: (offset: number, count: number) => Promise + pageSize: number + delay?: { onDelay: (message: string) => void; milliseconds: number } +}): Promise { + const result: TData[] = [] + let offset = 0 + let data: TData[] = [] + + do { + // fetch one page of data + data = await fetchData(offset, pageSize) + + // yield each item in the page (lets the async iterator move through one page of data) + result.push(...data) + + // increase the offset by the page count so the next iteration fetches fresh data + offset += pageSize + + if (delay && data.length >= pageSize) { + delay.onDelay(`Waiting ${delay.milliseconds / 1000} seconds`) + await new Promise((resolve) => setTimeout(resolve, delay.milliseconds)) + } + } while (data.length >= pageSize) + + return result +} + +/** + * Helper function for paginating data using a generator (typically from an API) + */ +export async function* paginateIt({ + fetchData, + pageSize, +}: { + fetchData: (offset: number, count: number) => Promise + pageSize: number +}): AsyncGenerator { + let offset = 0 + let data: TData[] = [] + + do { + // fetch one page of data + data = await fetchData(offset, pageSize) + + // yield each item in the page (lets the async iterator move through one page of data) + yield data + + // increase the offset by the page count so the next iteration fetches fresh data + offset += pageSize + } while (data.length >= pageSize) +} + +/** + * Helper function for generating chunks from an `AsyncIterable` + */ +export async function* chunkIt(it: AsyncIterable, size: number): AsyncGenerator { + let chunk: T[] = [] + + for await (const item of it) { + chunk.push(item) + + if (chunk.length === size) { + yield chunk + chunk = [] + } + } + + if (chunk.length > 0) yield chunk +} + +/** + * Wraps a function with basic retry logic + */ +export async function withRetry( + fn: (attempt: number) => TResult | Promise, + { + maxRetries = 10, + onError, + delay, + }: { + maxRetries?: number + onError?(error: unknown, attempt: number): boolean | undefined // true = retry, false = stop + delay?: number // milliseconds + } = {} +) { + let retries = 0 + let lastError: unknown + + while (retries <= maxRetries) { + if (delay && retries > 0) { + await new Promise((resolve) => setTimeout(resolve, delay)) + } + + try { + const res = await fn(retries) + return res + } catch (err) { + lastError = err + + if (onError) { + const shouldRetry = onError(err, retries) + if (!shouldRetry) { + break + } + } + + retries++ + } + } + + throw lastError +} + +/** + * Ensures a URL contains an HTTP(s) protocol and does NOT end with a slash + */ +export function normalizeUrl(url: string) { + // Add protocol if missing + if (!/^https?:\/\//i.test(url)) url = `http://${url}` + + // Remove trailing slash + if (url.endsWith('/')) url = url.slice(0, -1) + + return url +} diff --git a/libs/shared/src/utils/stats-utils.spec.ts b/libs/shared/src/utils/stats-utils.spec.ts new file mode 100644 index 00000000000..dc8a12357bb --- /dev/null +++ b/libs/shared/src/utils/stats-utils.spec.ts @@ -0,0 +1,37 @@ +import Decimal from 'decimal.js' +import { confidenceInterval, mean, stddev, variance, quantiles, quantilesBy } from './stats-utils' + +const d = (x: Decimal.Value) => new Decimal(x) + +describe('stats', () => { + it.each` + data | mean | variance | stddev | ci + ${[1, 7, 9, 15]} | ${8} | ${25} | ${5} | ${[3.1, 12.9]} + `('calculates data=$data μ=$mean σ²=$variance σ=$stddev ci=$ci', (x) => { + expect(mean(x.data)).toEqual(d(x.mean)) + expect(variance(x.data)).toEqual(d(x.variance)) + expect(stddev(x.data)).toEqual(d(x.stddev)) + expect(confidenceInterval(x.data)).toEqual(x.ci.map(d)) + }) + + it('calculates quantiles', () => { + const res = quantiles([1, 3, 2, 5, 4], ['0', '0.1', '0.25', '0.5', '0.75', '0.9', '1']) + + expect(res).toHaveLength(7) + expect(res).toEqual([1, 1, 2, 3, 4, 5, 5].map(d)) + }) + + it('calculates median using average of middle 2 elements', () => { + const res = quantiles([1, 2, 3, 4], ['0.5']) + + expect(res[0]).toEqual(d(2.5)) + }) + + it('calcualtes quantiles by property', () => { + const data = [{ a: 1 }, { a: 2 }, { a: 3 }, { a: 4 }] + + const res = quantilesBy(data, (item) => new Decimal(item.a), ['0.5']) + + expect(res[0]).toEqual(data[1]) + }) +}) diff --git a/libs/shared/src/utils/stats-utils.ts b/libs/shared/src/utils/stats-utils.ts new file mode 100644 index 00000000000..765651d4b90 --- /dev/null +++ b/libs/shared/src/utils/stats-utils.ts @@ -0,0 +1,102 @@ +import Decimal from 'decimal.js' +import sortBy from 'lodash/sortBy' + +export function mean(x: Decimal.Value[]): Decimal { + if (!x.length) throw new Error('mean requires at least 1 data point') + return Decimal.sum(...x).div(x.length) +} + +export function variance(x: Decimal.Value[]): Decimal { + const meanValue = mean(x) + return Decimal.sum(...x.map((k) => Decimal.sub(k, meanValue).pow(2))).div(x.length) +} + +export function stddev(x: Decimal.Value[]): Decimal { + return variance(x).sqrt() +} + +export function confidenceInterval( + x: Decimal.Value[], + z: Decimal.Value = 1.96 // 90% = 1.645, 95% = 1.96, 99% = 2.58 +): [Decimal, Decimal] { + const meanValue = mean(x) + const v = Decimal.mul(z, stddev(x).div(Decimal.sqrt(x.length))) + return [meanValue.minus(v), meanValue.plus(v)] +} + +function boxMullerTransform(): [z0: number, z1: number] { + const u1 = Math.random() + const u2 = Math.random() + + return [ + Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math.PI * u2), + Math.sqrt(-2.0 * Math.log(u1)) * Math.sin(2.0 * Math.PI * u2), + ] +} + +/** + * Generates random number using normal distribution around a specified mean / stddev + */ +export function randomNormal(mean: Decimal.Value, std: Decimal.Value): Decimal { + const [z0] = boxMullerTransform() + return Decimal.mul(z0, std).plus(mean) +} + +function countIf(data: T[], fn: (item: T) => boolean): number { + let count = 0 + for (const item of data) { + if (fn(item)) count++ + } + return count +} + +/** + * returns percentage of items meeting a specific criteria + */ +export function rateOf(data: T[], fn: (item: T) => boolean): Decimal { + return Decimal.div(countIf(data, fn), data.length) +} + +export function quantiles(data: Decimal.Value[], p: Decimal.Value[]): Decimal[] { + const tiles = p.map(_toDecimal) + const sorted = data.map(_toDecimal).sort((a, b) => a.cmp(b)) + + return tiles.map((tile) => _quantileSorted(sorted, tile, mean)) +} + +export function quantilesBy(data: T[], by: (item: T) => Decimal, p: Decimal.Value[]): T[] { + const tiles = p.map(_toDecimal) + const sorted = sortBy(data, (item) => by(item).toNumber()) + + return tiles.map((tile) => _quantileSorted(sorted, tile, (data) => data[0])) +} + +function _quantileSorted(x: T[], p: Decimal, avg: (data: [T, T]) => T): T { + const idx = x.length * +p + if (x.length === 0) { + throw new Error('quantile requires at least one data point.') + } else if (p.lt(0) || p.gt(1)) { + throw new Error('quantiles must be between 0 and 1') + } else if (p.eq(1)) { + // If p is 1, directly return the last element + return x[x.length - 1] + } else if (p.isZero()) { + // If p is 0, directly return the first element + return x[0] + } else if (idx % 1 !== 0) { + // If p is not integer, return the next element in array + return x[Math.ceil(idx) - 1] + } else if (x.length % 2 === 0) { + // If the list has even-length, we'll take the average of this number + // and the next value, if there is one + return avg([x[idx - 1], x[idx]]) + } else { + // Finally, in the simple case of an integer value + // with an odd-length list, return the x value at the index. + return x[idx] + } +} + +function _toDecimal(x: Decimal.Value): Decimal { + return x instanceof Decimal ? x : new Decimal(x) +} diff --git a/libs/shared/src/utils/test-utils.ts b/libs/shared/src/utils/test-utils.ts new file mode 100644 index 00000000000..74cd7857cc9 --- /dev/null +++ b/libs/shared/src/utils/test-utils.ts @@ -0,0 +1,51 @@ +import type { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios' +import type { PlaidError } from 'plaid' + +class MockPlaidError extends Error implements AxiosError { + isAxiosError = true + code?: string | undefined + config: AxiosRequestConfig = {} + request?: any + response?: AxiosResponse + toJSON = () => ({}) + + constructor(error: PlaidError, status: number) { + super(error.error_message) + + this.response = { + status, + statusText: 'ERROR', + data: error, + config: {}, + headers: {}, + } + } +} + +export function mockPlaidError(error: PlaidError, status = 400) { + return new MockPlaidError(error, status) +} + +export function axiosSuccess(data: T) { + return { + status: 200, + statusText: '200', + headers: {}, + config: {}, + data, + } +} + +export function axios400Error(data: T) { + return { + config: {}, + response: { + status: 400, + statusText: '400', + headers: {}, + config: {}, + data, + }, + isAxiosError: true, + } +} diff --git a/libs/shared/src/utils/transaction-utils.ts b/libs/shared/src/utils/transaction-utils.ts new file mode 100644 index 00000000000..967e6950a14 --- /dev/null +++ b/libs/shared/src/utils/transaction-utils.ts @@ -0,0 +1,15 @@ +// For now, we'll maintain a fixed list of categories. Eventually, we'll accept any string input +export const CATEGORIES = [ + 'Income', + 'Shopping', + 'Utilities', + 'Food and Drink', + 'Home Improvement', + 'Health', + 'Transportation', + 'Travel', + 'Housing Payments', + 'Vehicle Payments', + 'Other Payments', + 'Other', +] diff --git a/libs/shared/src/utils/user-utils.ts b/libs/shared/src/utils/user-utils.ts new file mode 100644 index 00000000000..d05751ca772 --- /dev/null +++ b/libs/shared/src/utils/user-utils.ts @@ -0,0 +1,38 @@ +export const MAX_MAYBE_LENGTH = 180 + +export const userTitles = [ + 'DIY Investor', + 'Internet Businessman', + 'Soon-to-be Millionaire', + 'Inevitable Billionaire', + 'Passive Income Pro', + 'Curious Self-Starter', + 'Crypto Whale', + 'Complete Degen', + 'Stock Market Maverick', + 'Wall Street Insider', + 'NFT Collector', + 'Real Estate Mogul', + 'Diamond Hands', + 'Savings Savant', + 'Aspiring Retiree', + 'FIRE seeker', + 'FatFIRE hopeful', + 'Credit Card Connoisseur', + 'Debt Destroyer', + 'Investing Novice', + 'Economic Enthusiast', + 'Freedom Seeker', + 'Bootstrapped Founder', + 'Smart Saver', + 'Wealth Builder', + 'Budget Master', + 'Financial Freedom Fighter', + 'Stock Market Enthusiast', + 'Savvy Spender', +] + +export const randomUserTitle = (except?: string) => + userTitles.filter((title) => title !== except)[ + Math.floor(Math.random() * (userTitles.length - (except ? 1 : 0))) + ] diff --git a/libs/shared/tsconfig.json b/libs/shared/tsconfig.json new file mode 100644 index 00000000000..16df0439ff1 --- /dev/null +++ b/libs/shared/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "esModuleInterop": true, + "strict": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/libs/shared/tsconfig.lib.json b/libs/shared/tsconfig.lib.json new file mode 100644 index 00000000000..e3cabfb5d5d --- /dev/null +++ b/libs/shared/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "exclude": ["**/*.spec.ts", "**/*.test.ts", "jest.config.ts"], + "include": ["**/*.ts"] +} diff --git a/libs/shared/tsconfig.spec.json b/libs/shared/tsconfig.spec.json new file mode 100644 index 00000000000..65cee4c47a6 --- /dev/null +++ b/libs/shared/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "types": ["jest", "node"] + }, + "include": [ + "**/*.spec.ts", + "**/*.test.ts", + "**/*.spec.tsx", + "**/*.test.tsx", + "**/*.spec.js", + "**/*.test.js", + "**/*.spec.jsx", + "**/*.test.jsx", + "**/*.d.ts", + "jest.config.ts" + ] +} diff --git a/nx.json b/nx.json new file mode 100644 index 00000000000..c52248b6cea --- /dev/null +++ b/nx.json @@ -0,0 +1,70 @@ +{ + "npmScope": "maybe-finance", + "affected": { + "defaultBase": "main" + }, + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": ["build", "lint", "test", "e2e", "build-storybook"], + "parallel": 1 + } + } + }, + "generators": { + "@nrwl/react": { + "application": { + "style": "css", + "linter": "eslint", + "babel": true + }, + "component": { + "style": "css" + }, + "library": { + "style": "css", + "linter": "eslint" + } + }, + "@nrwl/next": { + "application": { + "style": "none", + "linter": "eslint" + } + } + }, + "defaultProject": "workers", + "$schema": "./node_modules/nx/schemas/nx-schema.json", + "targetDefaults": { + "build": { + "dependsOn": ["^build"], + "inputs": ["production", "^production"] + }, + "e2e": { + "inputs": ["default", "^production"] + }, + "test": { + "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"] + }, + "lint": { + "inputs": ["default", "{workspaceRoot}/.eslintrc.json"] + }, + "build-storybook": { + "inputs": ["default", "^production", "{workspaceRoot}/.storybook/**/*"] + } + }, + "namedInputs": { + "default": ["{projectRoot}/**/*", "sharedGlobals"], + "sharedGlobals": ["{workspaceRoot}/prisma/**", "{workspaceRoot}/babel.config.json"], + "production": [ + "default", + "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", + "!{projectRoot}/tsconfig.spec.json", + "!{projectRoot}/jest.config.[jt]s", + "!{projectRoot}/.eslintrc.json", + "!{projectRoot}/.storybook/**/*", + "!{projectRoot}/**/*.stories.@(js|jsx|ts|tsx|mdx)" + ] + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000000..6a91e4337e3 --- /dev/null +++ b/package.json @@ -0,0 +1,270 @@ +{ + "name": "maybe-finance", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "dev": "nx run-many --target serve --projects=client,advisor,server,workers --parallel --host 0.0.0.0 --nx-bail=true --maxParallel=100", + "dev:services": "COMPOSE_PROFILES=services docker-compose up", + "dev:services:all": "COMPOSE_PROFILES=services,ngrok,stripe docker-compose up", + "dev:workers:test": "nx test workers --skip-nx-cache --runInBand", + "dev:server:test": "nx test server --skip-nx-cache --runInBand", + "dev:test:unit": "yarn dev:ci:test --testPathPattern='^(?!.*integration).*$' --verbose --skip-nx-cache", + "dev:test:integration": "NX_PORT=3335 yarn dev:ci:test --testPathPattern='^.*.integration.spec.ts$' --verbose --skip-nx-cache", + "dev:lint": "nx affected --target=lint", + "dev:ci:e2e": "nx run e2e:e2e", + "dev:ci": "yarn dev:ci:lint:all && yarn dev:ci:test:all && yarn dev:ci:format:all && nx run-many --target build --all", + "dev:ci:test": "nx run-many --target=test --all --runInBand", + "dev:ci:dependency-graph": "nx dep-graph", + "prisma:gen": "prisma generate", + "prisma:db:push": "prisma db push", + "prisma:migrate:dev": "prisma migrate dev", + "prisma:migrate:create": "prisma migrate dev --create-only", + "prisma:migrate:deploy": "prisma migrate deploy", + "prisma:migrate:reset": "prisma migrate reset", + "prisma:seed": "prisma db seed", + "prisma:studio": "prisma studio", + "dev:docker:reset": "docker-compose down -v --rmi all --remove-orphans && docker system prune --all --volumes && docker-compose build", + "dev:circular": "npx madge --circular --extensions ts libs", + "analyze:client": "ANALYZE=true nx build client --skip-nx-cache", + "auth0:sync": "node auth0/sync", + "auth0:deploy": "node auth0/deploy", + "auth0:test": "auth0 test login 7MtD6RWsXKInGPrFyeEseo7Y8PXSBEiV --tenant maybe-finance-development.us.auth0.com --force", + "auth0:edit": "live-server auth0", + "tools:pages": "live-server tools/pages", + "prepare": "husky install" + }, + "prisma": { + "seed": "ts-node --transpile-only prisma/seed.ts" + }, + "private": true, + "dependencies": { + "@auth0/auth0-react": "^2.0.0", + "@auth0/nextjs-auth0": "^2.0.0", + "@aws-sdk/client-s3": "^3.231.0", + "@aws-sdk/client-secrets-manager": "^3.235.0", + "@aws-sdk/cloudfront-signer": "^3.229.0", + "@aws-sdk/s3-presigned-post": "^3.234.0", + "@bull-board/express": "^4.6.4", + "@casl/ability": "^6.3.2", + "@casl/prisma": "^1.3.3", + "@fast-csv/format": "^4.3.5", + "@finicity/connect-web-sdk": "^1.0.0-rc.4", + "@headlessui/react": "^1.7.2", + "@hookform/resolvers": "^2.9.6", + "@polygon.io/client-js": "^6.0.6", + "@popperjs/core": "^2.11.5", + "@prisma/client": "4.8.1", + "@sentry/node": "^7.22.0", + "@sentry/react": "^7.22.0", + "@sentry/tracing": "^7.22.0", + "@storybook/core-server": "6.5.15", + "@stripe/stripe-js": "^1.44.1", + "@tanstack/react-query": "^4.19.1", + "@tanstack/react-query-devtools": "^4.19.1", + "@tanstack/react-table": "^8.3.0", + "@tippyjs/react": "^4.2.6", + "@tiptap/extension-placeholder": "^2.0.0-beta.209", + "@tiptap/react": "^2.0.0-beta.209", + "@tiptap/starter-kit": "^2.0.0-beta.209", + "@trpc/client": "^10.4.3", + "@trpc/next": "^10.4.3", + "@trpc/react-query": "^10.4.3", + "@trpc/server": "^10.4.3", + "@types/sanitize-html": "^2.8.0", + "@uppy/aws-s3": "^3.0.4", + "@uppy/core": "^3.0.4", + "@uppy/dashboard": "^3.2.0", + "@uppy/drag-drop": "^3.0.1", + "@uppy/file-input": "^3.0.1", + "@uppy/progress-bar": "^3.0.1", + "@uppy/react": "^3.0.2", + "@uppy/screen-capture": "^3.0.1", + "@uppy/webcam": "^3.2.1", + "@vercel/analytics": "^0.1.8", + "@vercel/og": "^0.0.25", + "@visx/axis": "^2.11.1", + "@visx/curve": "^2.1.0", + "@visx/event": "^2.6.0", + "@visx/glyph": "^2.10.0", + "@visx/gradient": "^2.10.0", + "@visx/grid": "^2.11.1", + "@visx/group": "^2.10.0", + "@visx/responsive": "^2.10.0", + "@visx/scale": "^2.2.2", + "@visx/shape": "^2.11.1", + "@visx/threshold": "^2.12.2", + "@visx/tooltip": "^2.10.0", + "auth0": "^2.44.0", + "auth0-deploy-cli": "^7.15.1", + "autoprefixer": "10.4.13", + "axios": "^0.26.1", + "bull": "^4.10.2", + "classnames": "^2.3.1", + "core-js": "^3.6.5", + "cors": "^2.8.5", + "crypto-js": "^4.1.1", + "d3-array": "^3.2.0", + "dayzed": "^3.2.3", + "decimal.js": "^10.4.2", + "ejs": "^3.1.8", + "express": "4.18.2", + "express-async-errors": "^3.1.1", + "express-jwt": "^7.7.7", + "express-jwt-authz": "^2.4.1", + "express-openid-connect": "^2.10.0", + "framer-motion": "^6.5.1", + "fuzzysearch": "^1.0.3", + "http-errors": "^2.0.0", + "ioredis": "^5.2.4", + "is-ci": "^3.0.1", + "jsonwebtoken": "^8.5.1", + "jwk-to-pem": "^2.0.5", + "jwks-rsa": "^3.0.0", + "jwt-decode": "^3.1.2", + "launchdarkly-node-server-sdk": "^6.4.3", + "launchdarkly-react-client-sdk": "^2.25.1", + "lodash": "^4.17.21", + "luxon": "^3.1.0", + "mime-types": "^2.1.35", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "next": "13.1.1", + "pg": "^8.8.0", + "plaid": "^12.1.0", + "postcss": "8.4.19", + "postmark": "^3.0.14", + "prisma": "4.8.1", + "prosemirror-commands": "^1.5.0", + "prosemirror-dropcursor": "^1.6.1", + "prosemirror-gapcursor": "^1.3.1", + "prosemirror-history": "^1.3.0", + "prosemirror-keymap": "^1.2.0", + "prosemirror-model": "^1.18.3", + "prosemirror-schema-list": "^1.2.2", + "prosemirror-state": "^1.4.2", + "prosemirror-transform": "^1.7.0", + "prosemirror-view": "^1.29.1", + "react": "18.2.0", + "react-animate-height": "^3.0.4", + "react-dom": "18.2.0", + "react-error-boundary": "^3.1.4", + "react-hook-form": "^7.33.1", + "react-hot-toast": "^2.3.0", + "react-icons": "^4.4.0", + "react-infinite-scroller": "^1.2.6", + "react-media-recorder": "1.6.5", + "react-number-format": "^5.1.3", + "react-plaid-link": "^3.3.2", + "react-popper": "^2.3.0", + "react-ranger": "^2.1.0", + "react-responsive": "^9.0.0-beta.10", + "regenerator-runtime": "0.13.7", + "sanitize-html": "^2.8.1", + "smooth-scroll-into-view-if-needed": "^1.1.33", + "stripe": "^10.17.0", + "superjson": "^1.11.0", + "tailwindcss": "3.2.4", + "tslib": "^2.3.0", + "uuid": "^9.0.0", + "winston": "^3.8.2", + "winston-transport": "^4.5.0", + "zod": "^3.19.1" + }, + "devDependencies": { + "@babel/core": "7.17.5", + "@babel/preset-react": "^7.14.5", + "@babel/preset-typescript": "7.16.7", + "@fast-csv/parse": "^4.3.6", + "@next/bundle-analyzer": "^13.1.1", + "@nrwl/cli": "15.5.2", + "@nrwl/cypress": "15.5.2", + "@nrwl/eslint-plugin-nx": "15.5.2", + "@nrwl/express": "15.5.2", + "@nrwl/jest": "15.5.2", + "@nrwl/linter": "15.5.2", + "@nrwl/next": "15.5.2", + "@nrwl/node": "15.5.2", + "@nrwl/react": "15.5.2", + "@nrwl/storybook": "15.5.2", + "@nrwl/web": "15.5.2", + "@nrwl/workspace": "15.5.2", + "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", + "@storybook/addon-essentials": "6.5.15", + "@storybook/addon-postcss": "3.0.0-alpha.1", + "@storybook/builder-webpack5": "6.5.15", + "@storybook/manager-webpack5": "6.5.15", + "@storybook/react": "6.5.15", + "@svgr/webpack": "^6.1.2", + "@tailwindcss/forms": "^0.5.3", + "@tailwindcss/line-clamp": "^0.4.2", + "@tailwindcss/typography": "^0.5.8", + "@testing-library/jest-dom": "^5.16.2", + "@testing-library/react": "13.4.0", + "@testing-library/user-event": "^13.2.1", + "@types/auth0": "^2.35.7", + "@types/cors": "^2.8.12", + "@types/crypto-js": "^4.1.1", + "@types/d3-array": "^3.0.3", + "@types/express": "4.17.14", + "@types/is-ci": "^3.0.0", + "@types/jest": "28.1.1", + "@types/jsonwebtoken": "^8.5.9", + "@types/lodash": "^4.14.182", + "@types/luxon": "^3.1.0", + "@types/mime-types": "^2.1.1", + "@types/morgan": "^1.9.3", + "@types/multer": "^1.4.7", + "@types/node": "18.11.9", + "@types/pg": "^8.6.5", + "@types/react": "18.0.25", + "@types/react-dom": "18.0.9", + "@types/react-infinite-scroller": "^1.2.3", + "@types/react-ranger": "^2.0.1", + "@types/uuid": "^8.3.4", + "@types/zxcvbn": "^4.4.1", + "@typescript-eslint/eslint-plugin": "5.43.0", + "@typescript-eslint/parser": "5.43.0", + "babel-jest": "28.1.3", + "babel-loader": "8.2.3", + "css-loader": "^6.4.0", + "cypress": "^12.3.0", + "dotenv": "^16.0.0", + "eslint": "8.15.0", + "eslint-config-next": "13.1.1", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-cypress": "^2.10.3", + "eslint-plugin-import": "2.26.0", + "eslint-plugin-json": "^3.1.0", + "eslint-plugin-jsx-a11y": "6.6.1", + "eslint-plugin-react": "7.31.11", + "eslint-plugin-react-hooks": "4.6.0", + "husky": "^7.0.0", + "jest": "28.1.1", + "jest-environment-jsdom": "28.1.1", + "jest-mock-extended": "^2.0.5", + "lint-staged": "^12.3.7", + "live-server": "^1.2.2", + "nock": "^13.2.9", + "nx": "15.5.2", + "prettier": "2.7.1", + "react-refresh": "^0.10.0", + "react-test-renderer": "18.2.0", + "style-loader": "^3.3.0", + "stylus": "^0.55.0", + "stylus-loader": "^7.1.0", + "ts-jest": "28.0.5", + "ts-node": "10.9.1", + "ts-toolbelt": "^9.6.0", + "typescript": "4.8.4", + "url-loader": "^4.1.1", + "wait-on": "^6.0.1", + "webpack": "^5.75.0", + "webpack-merge": "^5.8.0" + }, + "lint-staged": { + "*": [ + "yarn nx format:write --uncommitted", + "yarn eslint --fix" + ] + } +} diff --git a/prisma/migrations/20211005200319_init/migration.sql b/prisma/migrations/20211005200319_init/migration.sql new file mode 100644 index 00000000000..e3625be172d --- /dev/null +++ b/prisma/migrations/20211005200319_init/migration.sql @@ -0,0 +1,108 @@ +-- CreateTable +CREATE TABLE "account_balance" ( + "id" SERIAL NOT NULL, + "snapshot_date" DATE NOT NULL, + "closing_balance" BIGINT NOT NULL, + "debit_amount" BIGINT NOT NULL, + "credit_amount" BIGINT NOT NULL, + "quantity" BIGINT, + "account_id" INTEGER NOT NULL, + + CONSTRAINT "account_balance_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account_connection" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "type" VARCHAR(255) NOT NULL, + "plaid_item_id" VARCHAR(255), + "plaid_access_token" VARCHAR(255), + "plaid_institution_id" VARCHAR(255), + "user_id" INTEGER NOT NULL, + + CONSTRAINT "account_connection_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "plaid_account_id" VARCHAR(255), + "is_active" BOOLEAN NOT NULL, + "plaid_type" VARCHAR(255), + "plaid_subtype" VARCHAR(255), + "type" VARCHAR(255) NOT NULL, + "subtype" VARCHAR(255), + "current_balance" BIGINT, + "available_balance" BIGINT, + "iso_currency_code" VARCHAR(3) NOT NULL, + "unofficial_currency_code" VARCHAR(255), + "account_connection_id" INTEGER NOT NULL, + + CONSTRAINT "account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "transaction" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "name" VARCHAR(255) NOT NULL, + "amount" BIGINT NOT NULL, + "type" VARCHAR(255) NOT NULL, + "pending" BOOLEAN NOT NULL, + "posted_date" TIMESTAMPTZ(6) NOT NULL, + "effective_date" DATE NOT NULL, + "plaid_transaction_id" VARCHAR(255), + "plaid_category_id" VARCHAR(255), + "category" VARCHAR(255) NOT NULL, + "subcategory" VARCHAR(255), + "iso_currency_code" VARCHAR(3) NOT NULL, + "unofficial_currency_code" VARCHAR(255), + "account_id" INTEGER NOT NULL, + + CONSTRAINT "transaction_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "user" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "auth0_id" VARCHAR(255) NOT NULL, + "iso_currency_code" VARCHAR(3) NOT NULL DEFAULT E'USD', + + CONSTRAINT "user_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "account_balances_account_id_index" ON "account_balance"("account_id"); + +-- CreateIndex +CREATE INDEX "account_connections_user_id_index" ON "account_connection"("user_id"); + +-- CreateIndex +CREATE INDEX "accounts_account_connection_id_index" ON "account"("account_connection_id"); + +-- CreateIndex +CREATE INDEX "transactions_account_id_index" ON "transaction"("account_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "users_auth0_id_unique" ON "user"("auth0_id"); + +-- AddForeignKey +ALTER TABLE "account_balance" ADD CONSTRAINT "account_balances_account_id_foreign" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "account_connection" ADD CONSTRAINT "account_connections_user_id_foreign" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "accounts_account_connection_id_foreign" FOREIGN KEY ("account_connection_id") REFERENCES "account_connection"("id") ON DELETE CASCADE ON UPDATE NO ACTION; + +-- AddForeignKey +ALTER TABLE "transaction" ADD CONSTRAINT "transactions_account_id_foreign" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE NO ACTION; diff --git a/prisma/migrations/20211019194924_unique_constraint_on_account_balances/migration.sql b/prisma/migrations/20211019194924_unique_constraint_on_account_balances/migration.sql new file mode 100644 index 00000000000..e79135861ba --- /dev/null +++ b/prisma/migrations/20211019194924_unique_constraint_on_account_balances/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[snapshot_date,account_id]` on the table `account_balance` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "account_balance_snapshot_date_account_id_key" ON "account_balance"("snapshot_date", "account_id"); diff --git a/prisma/migrations/20211019214200_default_values/migration.sql b/prisma/migrations/20211019214200_default_values/migration.sql new file mode 100644 index 00000000000..84b8a033ff0 --- /dev/null +++ b/prisma/migrations/20211019214200_default_values/migration.sql @@ -0,0 +1,10 @@ +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "is_active" SET DEFAULT true; + +-- AlterTable +ALTER TABLE "account_balance" ALTER COLUMN "debit_amount" SET DEFAULT 0, +ALTER COLUMN "credit_amount" SET DEFAULT 0; + +-- AlterTable +ALTER TABLE "transaction" ALTER COLUMN "pending" SET DEFAULT false, +ALTER COLUMN "category" SET DEFAULT E'Default'; diff --git a/prisma/migrations/20211025200206_account_balance_schema_update/migration.sql b/prisma/migrations/20211025200206_account_balance_schema_update/migration.sql new file mode 100644 index 00000000000..46919b98d05 --- /dev/null +++ b/prisma/migrations/20211025200206_account_balance_schema_update/migration.sql @@ -0,0 +1,62 @@ +/* + Warnings: + + - You are about to drop the column `iso_currency_code` on the `account` table. All the data in the column will be lost. + - You are about to drop the column `plaid_subtype` on the `account` table. All the data in the column will be lost. + - You are about to drop the column `plaid_type` on the `account` table. All the data in the column will be lost. + - You are about to drop the column `unofficial_currency_code` on the `account` table. All the data in the column will be lost. + - You are about to drop the column `closing_balance` on the `account_balance` table. All the data in the column will be lost. + - You are about to drop the column `credit_amount` on the `account_balance` table. All the data in the column will be lost. + - You are about to drop the column `debit_amount` on the `account_balance` table. All the data in the column will be lost. + - You are about to drop the column `quantity` on the `account_balance` table. All the data in the column will be lost. + - You are about to drop the column `plaid_institution_id` on the `account_connection` table. All the data in the column will be lost. + - You are about to drop the column `iso_currency_code` on the `transaction` table. All the data in the column will be lost. + - You are about to drop the column `plaid_category_id` on the `transaction` table. All the data in the column will be lost. + - You are about to drop the column `plaid_transaction_id` on the `transaction` table. All the data in the column will be lost. + - You are about to drop the column `unofficial_currency_code` on the `transaction` table. All the data in the column will be lost. + - Added the required column `currency_code` to the `account` table without a default value. This is not possible if the table is not empty. + - Added the required column `balance` to the `account_balance` table without a default value. This is not possible if the table is not empty. + - Added the required column `currency_code` to the `transaction` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "account_balance" DROP CONSTRAINT "account_balances_account_id_foreign"; + +-- DropForeignKey +ALTER TABLE "transaction" DROP CONSTRAINT "transactions_account_id_foreign"; + +-- AlterTable +ALTER TABLE "account" DROP COLUMN "iso_currency_code", +DROP COLUMN "plaid_subtype", +DROP COLUMN "plaid_type", +DROP COLUMN "unofficial_currency_code", +ADD COLUMN "currency_code" VARCHAR(3) NOT NULL, +ADD COLUMN "house_meta" JSONB, +ADD COLUMN "vehicle_meta" JSONB; + +-- AlterTable +ALTER TABLE "account_balance" DROP COLUMN "closing_balance", +DROP COLUMN "credit_amount", +DROP COLUMN "debit_amount", +DROP COLUMN "quantity", +ADD COLUMN "balance" BIGINT NOT NULL, +ADD COLUMN "inflows" BIGINT NOT NULL DEFAULT 0, +ADD COLUMN "outflows" BIGINT NOT NULL DEFAULT 0; + +-- AlterTable +ALTER TABLE "account_connection" DROP COLUMN "plaid_institution_id"; + +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "iso_currency_code", +DROP COLUMN "plaid_category_id", +DROP COLUMN "plaid_transaction_id", +DROP COLUMN "unofficial_currency_code", +ADD COLUMN "currency_code" VARCHAR(3) NOT NULL, +ADD COLUMN "plaidTransactionId" VARCHAR(255), +ADD COLUMN "quantity" BIGINT; + +-- AddForeignKey +ALTER TABLE "account_balance" ADD CONSTRAINT "account_balances_account_id_foreign" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "transaction" ADD CONSTRAINT "transactions_account_id_foreign" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20211026174357_default_text_type/migration.sql b/prisma/migrations/20211026174357_default_text_type/migration.sql new file mode 100644 index 00000000000..8a9082f2664 --- /dev/null +++ b/prisma/migrations/20211026174357_default_text_type/migration.sql @@ -0,0 +1,24 @@ +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "name" SET DATA TYPE TEXT, +ALTER COLUMN "plaid_account_id" SET DATA TYPE TEXT, +ALTER COLUMN "type" SET DATA TYPE TEXT, +ALTER COLUMN "subtype" SET DATA TYPE TEXT, +ALTER COLUMN "currency_code" SET DATA TYPE TEXT; + +-- AlterTable +ALTER TABLE "account_connection" ALTER COLUMN "name" SET DATA TYPE TEXT, +ALTER COLUMN "type" SET DATA TYPE TEXT, +ALTER COLUMN "plaid_item_id" SET DATA TYPE TEXT, +ALTER COLUMN "plaid_access_token" SET DATA TYPE TEXT; + +-- AlterTable +ALTER TABLE "transaction" ALTER COLUMN "name" SET DATA TYPE TEXT, +ALTER COLUMN "type" SET DATA TYPE TEXT, +ALTER COLUMN "category" SET DATA TYPE TEXT, +ALTER COLUMN "subcategory" SET DATA TYPE TEXT, +ALTER COLUMN "currency_code" SET DATA TYPE TEXT, +ALTER COLUMN "plaidTransactionId" SET DATA TYPE TEXT; + +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "auth0_id" SET DATA TYPE TEXT, +ALTER COLUMN "iso_currency_code" SET DATA TYPE TEXT; diff --git a/prisma/migrations/20211026175641_default_values/migration.sql b/prisma/migrations/20211026175641_default_values/migration.sql new file mode 100644 index 00000000000..336d65867ec --- /dev/null +++ b/prisma/migrations/20211026175641_default_values/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "transaction" ALTER COLUMN "category" SET DEFAULT E'Default'; + +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "iso_currency_code" SET DEFAULT E'USD'; diff --git a/prisma/migrations/20211102165759_account_status/migration.sql b/prisma/migrations/20211102165759_account_status/migration.sql new file mode 100644 index 00000000000..86f2f72b748 --- /dev/null +++ b/prisma/migrations/20211102165759_account_status/migration.sql @@ -0,0 +1,24 @@ +/* + Warnings: + + - You are about to drop the column `plaidTransactionId` on the `transaction` table. All the data in the column will be lost. + - A unique constraint covering the columns `[plaid_item_id]` on the table `account_connection` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[plaid_transaction_id]` on the table `transaction` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateEnum +CREATE TYPE "AccountConnectionStatus" AS ENUM ('OK', 'ERROR'); + +-- AlterTable +ALTER TABLE "account_connection" ADD COLUMN "plaid_consent_expiration" TIMESTAMP(3), +ADD COLUMN "plaid_error" JSONB, +ADD COLUMN "status" "AccountConnectionStatus" NOT NULL DEFAULT E'OK'; + +-- AlterTable +ALTER TABLE "transaction" RENAME COLUMN "plaidTransactionId" TO "plaid_transaction_id"; + +-- CreateIndex +CREATE UNIQUE INDEX "account_connection_plaid_item_id_key" ON "account_connection"("plaid_item_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "transaction_plaid_transaction_id_key" ON "transaction"("plaid_transaction_id"); diff --git a/prisma/migrations/20211102183151_add_account_types_and_subtypes/migration.sql b/prisma/migrations/20211102183151_add_account_types_and_subtypes/migration.sql new file mode 100644 index 00000000000..162fbe168d4 --- /dev/null +++ b/prisma/migrations/20211102183151_add_account_types_and_subtypes/migration.sql @@ -0,0 +1,60 @@ +-- CreateEnum +CREATE TYPE "AccountClassification" AS ENUM ('asset', 'liability'); + +-- CreateTable +CREATE TABLE "account_type" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "classification" "AccountClassification" NOT NULL, + "plaid_types" TEXT[], + + CONSTRAINT "account_type_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account_subtype" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "account_type_id" INTEGER NOT NULL, + "plaid_subtypes" TEXT[], + + CONSTRAINT "account_subtype_pkey" PRIMARY KEY ("id") +); + +-- AlterTable +ALTER TABLE "account" RENAME COLUMN "type" TO "plaid_type"; +ALTER TABLE "account" RENAME COLUMN "subtype" TO "plaid_subtype"; +ALTER TABLE "account" + ALTER COLUMN "plaid_type" DROP NOT NULL, + ADD COLUMN "subtype_id" INTEGER, + ADD COLUMN "type_id" INTEGER; + +-- Add default types +INSERT INTO "account_type" ("name", "classification", "plaid_types") VALUES ('Other Asset', 'asset', '{other}'), ('Other Liability', 'liability', '{}'); + +-- Set default `type_id`s +UPDATE "account" SET "type_id" = CASE WHEN "plaid_type" = 'LIABILITY' THEN 2 ELSE 1 END; + +-- Make `account`.`type_id` NOT NULL +ALTER TABLE "account" ALTER COLUMN "type_id" SET NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "account_type_name_key" ON "account_type"("name"); + +-- CreateIndex +CREATE UNIQUE INDEX "account_subtype_name_key" ON "account_subtype"("name"); + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_type_id_fkey" FOREIGN KEY ("type_id") REFERENCES "account_type"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_subtype_id_fkey" FOREIGN KEY ("subtype_id") REFERENCES "account_subtype"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account_subtype" ADD CONSTRAINT "account_subtype_account_type_id_fkey" FOREIGN KEY ("account_type_id") REFERENCES "account_type"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- CreateIndex +CREATE INDEX "accounts_type_id_index" ON "account"("type_id"); + +-- CreateIndex +CREATE INDEX "accounts_subtype_id_index" ON "account"("subtype_id"); \ No newline at end of file diff --git a/prisma/migrations/20211104155259_account_uniqueness/migration.sql b/prisma/migrations/20211104155259_account_uniqueness/migration.sql new file mode 100644 index 00000000000..ef46c9f574a --- /dev/null +++ b/prisma/migrations/20211104155259_account_uniqueness/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[account_connection_id,plaid_account_id]` on the table `account` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "account_account_connection_id_plaid_account_id_key" ON "account"("account_connection_id", "plaid_account_id"); diff --git a/prisma/migrations/20211105234550_posted_date_type/migration.sql b/prisma/migrations/20211105234550_posted_date_type/migration.sql new file mode 100644 index 00000000000..1e8fbf0a74f --- /dev/null +++ b/prisma/migrations/20211105234550_posted_date_type/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "transaction" ALTER COLUMN "posted_date" SET DATA TYPE DATE; diff --git a/prisma/migrations/20211109151750_account_type_seed/migration.sql b/prisma/migrations/20211109151750_account_type_seed/migration.sql new file mode 100644 index 00000000000..51a2e4e45f7 --- /dev/null +++ b/prisma/migrations/20211109151750_account_type_seed/migration.sql @@ -0,0 +1,23 @@ +INSERT INTO account_type (name, classification, plaid_types) +VALUES + ('Cash', 'asset', '{depository}'), + ('Investments', 'asset', '{investment,brokerage}'), + ('Loans', 'liability', '{loan}'), + ('Credit', 'liability', '{credit}') +ON CONFLICT (name) DO NOTHING; + + +INSERT INTO account_subtype (name, account_type_id, plaid_subtypes) +VALUES + ('Checking', (SELECT id from account_type WHERE name = 'Cash'), '{depository}'), + ('Savings', (SELECT id from account_type WHERE name = 'Cash'), '{savings}'), + ('Certificate of Deposit', (SELECT id from account_type WHERE name = 'Cash'), '{cd}'), + ('Money Market', (SELECT id from account_type WHERE name = 'Cash'), '{"money market"}'), + ('Retirement', (SELECT id from account_type WHERE name = 'Investments'), '{401a,401k,403B,457b,ira,keogh,lif,lira,lrif,lrsp,pension,prif,retirement,roth,"roth 401k",rrif,rrsp,sarsep,"sep ira","simple ira",sipp,tfsa}'), + ('Brokerage', (SELECT id from account_type WHERE name = 'Investments'), '{brokerage}'), + ('Auto', (SELECT id from account_type WHERE name = 'Loans'), '{auto}'), + ('Home Equity', (SELECT id from account_type WHERE name = 'Loans'), '{"home equity"}'), + ('Mortgage', (SELECT id from account_type WHERE name = 'Loans'), '{mortgage}'), + ('Student', (SELECT id from account_type WHERE name = 'Loans'), '{student}'), + ('Credit Card', (SELECT id from account_type WHERE name = 'Credit'), '{"credit card",paypal}') +ON CONFLICT (name) DO NOTHING; diff --git a/prisma/migrations/20211110044559_manual_accounts_rename_fk/migration.sql b/prisma/migrations/20211110044559_manual_accounts_rename_fk/migration.sql new file mode 100644 index 00000000000..3ad4012976f --- /dev/null +++ b/prisma/migrations/20211110044559_manual_accounts_rename_fk/migration.sql @@ -0,0 +1,64 @@ +-- DropForeignKey +ALTER TABLE "account" DROP CONSTRAINT "accounts_account_connection_id_foreign"; + +-- DropForeignKey +ALTER TABLE "account_connection" DROP CONSTRAINT "account_connections_user_id_foreign"; + +-- AlterTable +ALTER TABLE "account" ADD COLUMN "user_id" INTEGER, +ALTER COLUMN "account_connection_id" DROP NOT NULL; + +-- CreateTable +CREATE TABLE "valuation" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "account_id" INTEGER NOT NULL, + "source" TEXT NOT NULL, + "amount" BIGINT NOT NULL, + "currency_code" TEXT NOT NULL DEFAULT E'USD', + + CONSTRAINT "valuation_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "valuation_account_id_idx" ON "valuation"("account_id"); + +-- RenameForeignKey +ALTER TABLE "account_balance" RENAME CONSTRAINT "account_balances_account_id_foreign" TO "account_balance_account_id_fkey"; + +-- RenameForeignKey +ALTER TABLE "transaction" RENAME CONSTRAINT "transactions_account_id_foreign" TO "transaction_account_id_fkey"; + +-- AddForeignKey +ALTER TABLE "account_connection" ADD CONSTRAINT "account_connection_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_account_connection_id_fkey" FOREIGN KEY ("account_connection_id") REFERENCES "account_connection"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "valuation" ADD CONSTRAINT "valuation_account_id_fkey" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- RenameIndex +ALTER INDEX "accounts_account_connection_id_index" RENAME TO "account_account_connection_id_idx"; + +-- RenameIndex +ALTER INDEX "accounts_subtype_id_index" RENAME TO "account_subtype_id_idx"; + +-- RenameIndex +ALTER INDEX "accounts_type_id_index" RENAME TO "account_type_id_idx"; + +-- RenameIndex +ALTER INDEX "account_balances_account_id_index" RENAME TO "account_balance_account_id_idx"; + +-- RenameIndex +ALTER INDEX "account_connections_user_id_index" RENAME TO "account_connection_user_id_idx"; + +-- RenameIndex +ALTER INDEX "transactions_account_id_index" RENAME TO "transaction_account_id_idx"; + +-- RenameIndex +ALTER INDEX "users_auth0_id_unique" RENAME TO "user_auth0_id_key"; diff --git a/prisma/migrations/20211116235652_investment_data/migration.sql b/prisma/migrations/20211116235652_investment_data/migration.sql new file mode 100644 index 00000000000..f442c2b4f7a --- /dev/null +++ b/prisma/migrations/20211116235652_investment_data/migration.sql @@ -0,0 +1,101 @@ +/* + Warnings: + + - You are about to drop the column `quantity` on the `transaction` table. All the data in the column will be lost. + - Changed the type of `type` on the `transaction` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + +*/ +-- CreateEnum +CREATE TYPE "TransactionType" AS ENUM ('INFLOW', 'OUTFLOW'); + +-- Convert transaction amount to a signed value +UPDATE "transaction" +SET amount = -1 * amount +WHERE type = 'INFLOW'; + +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "quantity", +DROP COLUMN "type", +ADD COLUMN "type" "TransactionType" GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionType" ELSE 'OUTFLOW'::"TransactionType" END) STORED; + +-- CreateTable +CREATE TABLE "holding" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "account_id" INTEGER NOT NULL, + "security_id" INTEGER NOT NULL, + "value" BIGINT NOT NULL, + "quantity" DECIMAL(36,18) NOT NULL, + "cost_basis" BIGINT, + "price" BIGINT NOT NULL, + "price_as_of" DATE, + "currency_code" TEXT NOT NULL DEFAULT E'USD', + "plaid_holding_id" TEXT, + + CONSTRAINT "holding_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "investment_transaction" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "account_id" INTEGER NOT NULL, + "security_id" INTEGER, + "date" DATE NOT NULL, + "name" TEXT NOT NULL, + "amount" BIGINT NOT NULL, + "type" "TransactionType" GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionType" ELSE 'OUTFLOW'::"TransactionType" END) STORED, + "quantity" DECIMAL(36,18) NOT NULL, + "price" BIGINT NOT NULL, + "currency_code" TEXT NOT NULL DEFAULT E'USD', + "plaid_investment_transaction_id" TEXT, + "plaid_type" TEXT, + "plaid_subtype" TEXT, + + CONSTRAINT "investment_transaction_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "security" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL, + "name" TEXT, + "symbol" TEXT, + "cusip" TEXT, + "isin" TEXT, + "currency_code" TEXT NOT NULL DEFAULT E'USD', + "plaid_security_id" TEXT, + "plaid_type" TEXT, + + CONSTRAINT "security_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "holding_plaid_holding_id_key" ON "holding"("plaid_holding_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "holding_account_id_security_id_key" ON "holding"("account_id", "security_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "investment_transaction_plaid_investment_transaction_id_key" ON "investment_transaction"("plaid_investment_transaction_id"); + +-- CreateIndex +CREATE INDEX "investment_transaction_account_id_idx" ON "investment_transaction"("account_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "security_plaid_security_id_key" ON "security"("plaid_security_id"); + +-- AddForeignKey +ALTER TABLE "holding" ADD CONSTRAINT "holding_account_id_fkey" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "holding" ADD CONSTRAINT "holding_security_id_fkey" FOREIGN KEY ("security_id") REFERENCES "security"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "investment_transaction" ADD CONSTRAINT "investment_transaction_account_id_fkey" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "investment_transaction" ADD CONSTRAINT "investment_transaction_security_id_fkey" FOREIGN KEY ("security_id") REFERENCES "security"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20211117190140_add_manual_account_types/migration.sql b/prisma/migrations/20211117190140_add_manual_account_types/migration.sql new file mode 100644 index 00000000000..78a566b2064 --- /dev/null +++ b/prisma/migrations/20211117190140_add_manual_account_types/migration.sql @@ -0,0 +1,5 @@ +INSERT INTO account_type (name, classification) +VALUES + ('Property', 'asset'), + ('Vehicles', 'asset') +ON CONFLICT (name) DO NOTHING; \ No newline at end of file diff --git a/prisma/migrations/20211117190719_updated_at_default/migration.sql b/prisma/migrations/20211117190719_updated_at_default/migration.sql new file mode 100644 index 00000000000..74c7e58e839 --- /dev/null +++ b/prisma/migrations/20211117190719_updated_at_default/migration.sql @@ -0,0 +1,32 @@ +/* + Warnings: + + - Made the column `type` on table `investment_transaction` required. This step will fail if there are existing NULL values in that column. + - Made the column `type` on table `transaction` required. This step will fail if there are existing NULL values in that column. + +*/ +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "account_connection" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "holding" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "investment_transaction" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP, +ALTER COLUMN "type" SET NOT NULL; + +-- AlterTable +ALTER TABLE "security" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "transaction" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP, +ALTER COLUMN "type" SET NOT NULL; + +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; + +-- AlterTable +ALTER TABLE "valuation" ALTER COLUMN "updated_at" SET DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/migrations/20211117210112_valuation_date/migration.sql b/prisma/migrations/20211117210112_valuation_date/migration.sql new file mode 100644 index 00000000000..d776368577b --- /dev/null +++ b/prisma/migrations/20211117210112_valuation_date/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `date` to the `valuation` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "valuation" ADD COLUMN "date" DATE NOT NULL; diff --git a/prisma/migrations/20211117233026_add_date_indices/migration.sql b/prisma/migrations/20211117233026_add_date_indices/migration.sql new file mode 100644 index 00000000000..013d1a76355 --- /dev/null +++ b/prisma/migrations/20211117233026_add_date_indices/migration.sql @@ -0,0 +1,17 @@ +-- DropIndex +DROP INDEX "investment_transaction_account_id_idx"; + +-- DropIndex +DROP INDEX "transaction_account_id_idx"; + +-- DropIndex +DROP INDEX "valuation_account_id_idx"; + +-- CreateIndex +CREATE INDEX "investment_transaction_account_id_date_idx" ON "investment_transaction"("account_id", "date"); + +-- CreateIndex +CREATE INDEX "transaction_account_id_effective_date_idx" ON "transaction"("account_id", "effective_date"); + +-- CreateIndex +CREATE INDEX "valuation_account_id_date_idx" ON "valuation"("account_id", "date"); diff --git a/prisma/migrations/20211118160716_account_balance_update/migration.sql b/prisma/migrations/20211118160716_account_balance_update/migration.sql new file mode 100644 index 00000000000..b067c71107a --- /dev/null +++ b/prisma/migrations/20211118160716_account_balance_update/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - You are about to drop the column `snapshot_date` on the `account_balance` table. All the data in the column will be lost. + - A unique constraint covering the columns `[account_id,date]` on the table `account_balance` will be added. If there are existing duplicate values, this will fail. + - Added the required column `date` to the `account_balance` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropIndex +DROP INDEX "account_balance_account_id_idx"; + +-- DropIndex +DROP INDEX "account_balance_snapshot_date_account_id_key"; + +-- AlterTable +ALTER TABLE "account_balance" +ALTER COLUMN "inflows" DROP NOT NULL, +ALTER COLUMN "outflows" DROP NOT NULL; + +ALTER TABLE "account_balance" RENAME COLUMN "snapshot_date" TO "date"; + +-- CreateIndex +CREATE UNIQUE INDEX "account_balance_account_id_date_key" ON "account_balance"("account_id", "date"); diff --git a/prisma/migrations/20211118191000_account_balance_timestamps/migration.sql b/prisma/migrations/20211118191000_account_balance_timestamps/migration.sql new file mode 100644 index 00000000000..b6b9732a754 --- /dev/null +++ b/prisma/migrations/20211118191000_account_balance_timestamps/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "account_balance" ADD COLUMN "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/migrations/20211118194940_account_functions/migration.sql b/prisma/migrations/20211118194940_account_functions/migration.sql new file mode 100644 index 00000000000..2408644a125 --- /dev/null +++ b/prisma/migrations/20211118194940_account_functions/migration.sql @@ -0,0 +1,58 @@ +-- determines the bookkeeping type of an account +CREATE OR REPLACE FUNCTION account_value_type(p_account_id int) RETURNS text AS $$ + SELECT + CASE + WHEN EXISTS (SELECT 1 FROM investment_transaction WHERE account_id = p_account_id) THEN 'investment_transaction' + WHEN EXISTS (SELECT 1 FROM valuation WHERE account_id = p_account_id) THEN 'valuation' + ELSE 'transaction' + END +$$ LANGUAGE SQL STABLE; + + +-- returns the start date of the account from a bookkeeping perspective +CREATE OR REPLACE FUNCTION account_value_start_date(p_account_id int) RETURNS date AS $$ + SELECT + CASE + WHEN account_value_type(p_account_id) = 'valuation' THEN (SELECT MIN(date) FROM valuation WHERE account_id = p_account_id) + WHEN account_value_type(p_account_id) = 'transaction' THEN (SELECT MIN(effective_date) FROM transaction WHERE account_id = p_account_id) + WHEN account_value_type(p_account_id) = 'investment_transaction' THEN (SELECT MIN(date) FROM investment_transaction WHERE account_id = p_account_id) + END +$$ LANGUAGE SQL STABLE; + + +-- calculates balance info for every day of the account's lifetime +CREATE OR REPLACE FUNCTION calculate_account_balances(p_account_id int) RETURNS TABLE(account_id int, date date, balance bigint, inflows bigint, outflows bigint) AS $$ + WITH dates AS ( + SELECT generate_series(account_value_start_date(p_account_id), CURRENT_DATE, '1d')::date AS date + ) + SELECT + a.id AS account_id, + d.date, + CASE + WHEN account_value_type(a.id) = 'valuation' THEN (SELECT v.amount FROM valuation v WHERE v.account_id = a.id AND v.date <= d.date ORDER BY v.date DESC LIMIT 1) + WHEN account_value_type(a.id) = 'transaction' THEN a.current_balance + ((CASE WHEN at.classification = 'liability' THEN -1 ELSE 1 END) * SUM(COALESCE(SUM(t.amount), 0)) OVER w) + WHEN account_value_type(a.id) = 'investment_transaction' THEN a.current_balance + ((CASE WHEN at.classification = 'liability' THEN -1 ELSE 1 END) * SUM(COALESCE(SUM(it.amount), 0)) OVER w) + END AS balance, + CASE + WHEN account_value_type(a.id) = 'valuation' THEN NULL + WHEN account_value_type(a.id) = 'transaction' THEN COALESCE(SUM(ABS(t.amount)) FILTER (WHERE t.type = 'INFLOW'), 0) + WHEN account_value_type(a.id) = 'investment_transaction' THEN COALESCE(SUM(ABS(it.amount)) FILTER (WHERE it.type = 'INFLOW'), 0) + END AS inflows, + CASE + WHEN account_value_type(a.id) = 'valuation' THEN NULL + WHEN account_value_type(a.id) = 'transaction' THEN COALESCE(SUM(ABS(t.amount)) FILTER (WHERE t.type = 'OUTFLOW'), 0) + WHEN account_value_type(a.id) = 'investment_transaction' THEN COALESCE(SUM(ABS(it.amount)) FILTER (WHERE it.type = 'OUTFLOW'), 0) + END AS outflows + FROM + account a + LEFT JOIN account_type at ON at.id = a.type_id + CROSS JOIN dates d + LEFT JOIN transaction t ON t.account_id = a.id AND t.effective_date = d.date + LEFT JOIN investment_transaction it ON it.account_id = a.id AND it.date = d.date + WHERE + a.id = p_account_id + GROUP BY + a.id, at.classification, d.date + WINDOW + w AS (ORDER BY d.date DESC) +$$ LANGUAGE SQL STABLE; diff --git a/prisma/migrations/20211118214727_txn_date_naming/migration.sql b/prisma/migrations/20211118214727_txn_date_naming/migration.sql new file mode 100644 index 00000000000..e1c23f7786e --- /dev/null +++ b/prisma/migrations/20211118214727_txn_date_naming/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable + +ALTER TABLE "transaction" RENAME COLUMN "posted_date" TO "date"; + diff --git a/prisma/migrations/20211129155121_connection_status_codes/migration.sql b/prisma/migrations/20211129155121_connection_status_codes/migration.sql new file mode 100644 index 00000000000..6b988ffc8e0 --- /dev/null +++ b/prisma/migrations/20211129155121_connection_status_codes/migration.sql @@ -0,0 +1,10 @@ +-- AlterEnum +-- This migration adds more than one value to an enum. +-- With PostgreSQL versions 11 and earlier, this is not possible +-- in a single migration. This can be worked around by creating +-- multiple migrations, each migration adding only one value to +-- the enum. + + +ALTER TYPE "AccountConnectionStatus" ADD VALUE 'SYNCING'; +ALTER TYPE "AccountConnectionStatus" ADD VALUE 'DISCONNECTED'; diff --git a/prisma/migrations/20211130184227_new_accounts_available_flag/migration.sql b/prisma/migrations/20211130184227_new_accounts_available_flag/migration.sql new file mode 100644 index 00000000000..2eca8be0a5f --- /dev/null +++ b/prisma/migrations/20211130184227_new_accounts_available_flag/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "account_connection" ADD COLUMN "plaid_new_accounts_available" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20211201023540_account_single_table_inheritance/migration.sql b/prisma/migrations/20211201023540_account_single_table_inheritance/migration.sql new file mode 100644 index 00000000000..1d5a7832d86 --- /dev/null +++ b/prisma/migrations/20211201023540_account_single_table_inheritance/migration.sql @@ -0,0 +1,91 @@ +-- DropForeignKey +ALTER TABLE "account" DROP CONSTRAINT "account_subtype_id_fkey"; + +-- DropForeignKey +ALTER TABLE "account" DROP CONSTRAINT "account_type_id_fkey"; + +-- DropForeignKey +ALTER TABLE "account_subtype" DROP CONSTRAINT "account_subtype_account_type_id_fkey"; + +-- DropIndex +DROP INDEX "account_subtype_id_idx"; + +-- DropIndex +DROP INDEX "account_type_id_idx"; + +-- First, add the new columns +ALTER TABLE account +ADD COLUMN classification "AccountClassification", +ADD COLUMN "type" TEXT, +ADD COLUMN "valuation_type" TEXT, +ADD COLUMN "subcategory_override" TEXT; + +ALTER TABLE account +RENAME COLUMN "house_meta" TO "property_meta"; + +-- Update all `Account.type` fields +UPDATE "account" SET "type" = 'plaid' WHERE plaid_account_id IS NOT NULL; +UPDATE "account" SET "type" = 'property' WHERE property_meta IS NOT NULL; + +-- Update all `Account.classification` fields +UPDATE "account" a +SET classification = at.classification +FROM "account_type" at +WHERE a.type_id = at.id; + +-- Update all `Account.valuation_type` fields +UPDATE "account" +SET "valuation_type" = 'plaid-investment-transaction' +WHERE "plaid_type" = 'investment'; + +UPDATE "account" +SET "valuation_type" = 'plaid-transaction' +WHERE "plaid_type" <> 'investment' AND "plaid_type" IS NOT NULL; + +UPDATE "account" +SET "valuation_type" = 'zillow-valuation' +WHERE "plaid_type" IS NULL; + +-- Add computed column for category +ALTER TABLE "account" +ADD COLUMN "category" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "type" = 'plaid' AND "plaid_type" IN ('depository') THEN 'cash' + WHEN "type" = 'plaid' AND "plaid_type" IN ('investment' ,'brokerage') THEN 'investment' + WHEN "type" = 'plaid' AND "plaid_type" IN ('loan') THEN 'loan' + WHEN "type" = 'plaid' AND "plaid_type" IN ('credit') THEN 'credit' + WHEN "type" = 'property' THEN 'property' + WHEN "type" = 'vehicle' THEN 'vehicle' + WHEN "type" = 'other' THEN 'other' + END +) STORED; + +-- Add computed column for subcategory +ALTER TABLE "account" +ADD COLUMN "subcategory" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "subcategory_override" IS NOT NULL THEN "subcategory_override" + WHEN "type" = 'plaid' THEN "plaid_subtype" + WHEN "type" = 'property' THEN 'property' + WHEN "type" = 'vehicle' THEN 'vehicle' + ELSE 'other' + END +) STORED; + + +ALTER TABLE "account" +ALTER COLUMN "type" SET NOT NULL, +ALTER COLUMN "classification" SET NOT NULL, +ALTER COLUMN "valuation_type" SET NOT NULL, +ALTER COLUMN "category" SET NOT NULL, +ALTER COLUMN "subcategory" SET NOT NULL; + +ALTER TABLE "account" +DROP COLUMN "subtype_id", +DROP COLUMN "type_id"; + +-- DropTable +DROP TABLE "account_subtype"; + +-- DropTable +DROP TABLE "account_type"; \ No newline at end of file diff --git a/prisma/migrations/20211203180216_security_pricing/migration.sql b/prisma/migrations/20211203180216_security_pricing/migration.sql new file mode 100644 index 00000000000..bb1f2a3b4a6 --- /dev/null +++ b/prisma/migrations/20211203180216_security_pricing/migration.sql @@ -0,0 +1,22 @@ +-- AlterTable +ALTER TABLE "security" ADD COLUMN "pricing_last_synced_at" TIMESTAMPTZ(6); + +-- Enable timescale +CREATE EXTENSION IF NOT EXISTS timescaledb; + +-- CreateTable +CREATE TABLE "security_pricing" ( + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "security_id" INTEGER NOT NULL, + "date" DATE NOT NULL, + "price_close" DECIMAL(18,10) NOT NULL, + + CONSTRAINT "security_pricing_pkey" PRIMARY KEY ("security_id","date") +); + +-- AddForeignKey +ALTER TABLE "security_pricing" ADD CONSTRAINT "security_pricing_security_id_fkey" FOREIGN KEY ("security_id") REFERENCES "security"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- Convert security_pricing to hypertable +SELECT create_hypertable('security_pricing', 'date', if_not_exists => true, migrate_data => true); \ No newline at end of file diff --git a/prisma/migrations/20211204053810_account_balance_hypertable/migration.sql b/prisma/migrations/20211204053810_account_balance_hypertable/migration.sql new file mode 100644 index 00000000000..569c1cd0572 --- /dev/null +++ b/prisma/migrations/20211204053810_account_balance_hypertable/migration.sql @@ -0,0 +1,17 @@ +/* + Warnings: + + - The primary key for the `account_balance` table will be changed. If it partially fails, the table could be left without primary key constraint. + - You are about to drop the column `id` on the `account_balance` table. All the data in the column will be lost. + +*/ +-- DropIndex +DROP INDEX "account_balance_account_id_date_key"; + +-- AlterTable +ALTER TABLE "account_balance" DROP CONSTRAINT "account_balance_pkey", +DROP COLUMN "id", +ADD CONSTRAINT "account_balance_pkey" PRIMARY KEY ("account_id", "date"); + +-- Convert account_balance to hypertable +SELECT create_hypertable('account_balance', 'date', if_not_exists => true, migrate_data => true); \ No newline at end of file diff --git a/prisma/migrations/20211207192726_add_valuation_generated_cols/migration.sql b/prisma/migrations/20211207192726_add_valuation_generated_cols/migration.sql new file mode 100644 index 00000000000..54a438c54e1 --- /dev/null +++ b/prisma/migrations/20211207192726_add_valuation_generated_cols/migration.sql @@ -0,0 +1,54 @@ + +-- CreateEnum +CREATE TYPE "ValuationType" AS ENUM ('PLAID_TRANSACTION', 'PLAID_INVESTMENT_TRANSACTION', 'KBB_VALUATION', 'ZILLOW_VALUATION', 'USER_VALUATION'); + +-- Integrate new enum +ALTER TABLE "account" +ALTER "valuation_type" DROP NOT NULL; + +UPDATE "account" +SET "valuation_type" = NULL; + +ALTER TABLE "account" +DROP COLUMN "valuation_type", +ADD COLUMN "valuation_type" "ValuationType"; + +-- Populate enums +UPDATE "account" +SET "valuation_type" = 'PLAID_TRANSACTION' +WHERE "type" = 'plaid' AND "plaid_type" <> 'investment'; + +UPDATE "account" +SET "valuation_type" = 'PLAID_INVESTMENT_TRANSACTION' +WHERE "type" = 'plaid' AND "plaid_type" = 'investment'; + +UPDATE "account" +SET "valuation_type" = 'USER_VALUATION' +WHERE "type" <> 'plaid'; + +-- Add generated columns +ALTER TABLE "account" +ADD COLUMN "valuation_source" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "valuation_type" = 'PLAID_TRANSACTION' OR "valuation_type" = 'PLAID_INVESTMENT_TRANSACTION' THEN 'plaid' + WHEN "valuation_type" = 'USER_VALUATION' THEN 'user' + WHEN "valuation_type" = 'KBB_VALUATION' THEN 'kbb' + WHEN "valuation_type" = 'ZILLOW_VALUATION' THEN 'zillow' + ELSE 'other' + END +) STORED; + +ALTER TABLE "account" +ADD COLUMN "valuation_method" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "valuation_type" = 'PLAID_TRANSACTION' THEN 'transaction' + WHEN "valuation_type" = 'PLAID_INVESTMENT_TRANSACTION' THEN 'investment-transaction' + ELSE 'valuation' + END +) STORED; + +-- Add non-null constraints +ALTER TABLE "account" +ALTER COLUMN "valuation_method" SET NOT NULL, +ALTER COLUMN "valuation_source" SET NOT NULL, +ALTER COLUMN "valuation_type" SET NOT NULL; diff --git a/prisma/migrations/20211208162929_transaction_date/migration.sql b/prisma/migrations/20211208162929_transaction_date/migration.sql new file mode 100644 index 00000000000..13a4fd0a604 --- /dev/null +++ b/prisma/migrations/20211208162929_transaction_date/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - You are about to drop the column `effective_date` on the `transaction` table. All the data in the column will be lost. + +*/ +-- DropIndex +DROP INDEX "transaction_account_id_effective_date_idx"; + +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "effective_date"; + +-- CreateIndex +CREATE INDEX "transaction_account_id_date_idx" ON "transaction"("account_id", "date"); diff --git a/prisma/migrations/20211209041710_remove_initial_txn/migration.sql b/prisma/migrations/20211209041710_remove_initial_txn/migration.sql new file mode 100644 index 00000000000..40ba792efb6 --- /dev/null +++ b/prisma/migrations/20211209041710_remove_initial_txn/migration.sql @@ -0,0 +1,3 @@ +-- now that we are using the new balance calculation, remove the old initial transactions +DELETE FROM transaction WHERE name = 'INITIAL_TRANSACTION'; +DELETE FROM investment_transaction WHERE name = 'INITIAL_TRANSACTION'; \ No newline at end of file diff --git a/prisma/migrations/20211209050532_update_fns/migration.sql b/prisma/migrations/20211209050532_update_fns/migration.sql new file mode 100644 index 00000000000..e1a06b0a4a8 --- /dev/null +++ b/prisma/migrations/20211209050532_update_fns/migration.sql @@ -0,0 +1,18 @@ +-- no longer used +DROP FUNCTION IF EXISTS account_value_type; + +-- default to today's date if we don't have any transactions/valuations for the account so it plays nicely with time_bucket_gapfill +CREATE OR REPLACE FUNCTION account_value_start_date(p_account_id integer) RETURNS date STABLE AS $$ + SELECT + COALESCE( + CASE + WHEN a.valuation_method = 'valuation' THEN (SELECT MIN(date) FROM valuation WHERE account_id = p_account_id) + WHEN a.valuation_method = 'transaction' THEN (SELECT MIN(date) FROM transaction WHERE account_id = p_account_id) + WHEN a.valuation_method = 'investment-transaction' THEN (SELECT MIN(date) FROM investment_transaction WHERE account_id = p_account_id) + ELSE NULL + END, now()) + FROM + account a + WHERE + a.id = p_account_id +$$ LANGUAGE SQL; \ No newline at end of file diff --git a/prisma/migrations/20211211140103_add_institution_id_to_connection/migration.sql b/prisma/migrations/20211211140103_add_institution_id_to_connection/migration.sql new file mode 100644 index 00000000000..5314ae7ac58 --- /dev/null +++ b/prisma/migrations/20211211140103_add_institution_id_to_connection/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "account_connection" ADD COLUMN "plaid_institution_id" TEXT; diff --git a/prisma/migrations/20211213211517_account_user_index/migration.sql b/prisma/migrations/20211213211517_account_user_index/migration.sql new file mode 100644 index 00000000000..f42a11f0fb9 --- /dev/null +++ b/prisma/migrations/20211213211517_account_user_index/migration.sql @@ -0,0 +1,2 @@ +-- CreateIndex +CREATE INDEX "account_user_id_idx" ON "account"("user_id"); diff --git a/prisma/migrations/20211214162659_security_pricing_source/migration.sql b/prisma/migrations/20211214162659_security_pricing_source/migration.sql new file mode 100644 index 00000000000..630451503a2 --- /dev/null +++ b/prisma/migrations/20211214162659_security_pricing_source/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "security_pricing" ADD COLUMN "source" TEXT; diff --git a/prisma/migrations/20211215195518_add_account_start_date/migration.sql b/prisma/migrations/20211215195518_add_account_start_date/migration.sql new file mode 100644 index 00000000000..aa5c4e60566 --- /dev/null +++ b/prisma/migrations/20211215195518_add_account_start_date/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "account" ADD COLUMN "start_date" DATE; diff --git a/prisma/migrations/20211230035441_account_sync_status/migration.sql b/prisma/migrations/20211230035441_account_sync_status/migration.sql new file mode 100644 index 00000000000..e860433543b --- /dev/null +++ b/prisma/migrations/20211230035441_account_sync_status/migration.sql @@ -0,0 +1,25 @@ +/* + Warnings: + + - The values [SYNCING] on the enum `AccountConnectionStatus` will be removed. If these variants are still used in the database, this will fail. + +*/ +-- CreateEnum +CREATE TYPE "AccountSyncStatus" AS ENUM ('IDLE', 'PENDING', 'SYNCING'); + +-- AlterEnum +BEGIN; +CREATE TYPE "AccountConnectionStatus_new" AS ENUM ('OK', 'ERROR', 'DISCONNECTED'); +ALTER TABLE "account_connection" ALTER COLUMN "status" DROP DEFAULT; +ALTER TABLE "account_connection" ALTER COLUMN "status" TYPE "AccountConnectionStatus_new" USING ("status"::text::"AccountConnectionStatus_new"); +ALTER TYPE "AccountConnectionStatus" RENAME TO "AccountConnectionStatus_old"; +ALTER TYPE "AccountConnectionStatus_new" RENAME TO "AccountConnectionStatus"; +DROP TYPE "AccountConnectionStatus_old"; +ALTER TABLE "account_connection" ALTER COLUMN "status" SET DEFAULT 'OK'; +COMMIT; + +-- AlterTable +ALTER TABLE "account" ADD COLUMN "sync_status" "AccountSyncStatus" NOT NULL DEFAULT E'IDLE'; + +-- AlterTable +ALTER TABLE "account_connection" ADD COLUMN "sync_status" "AccountSyncStatus" NOT NULL DEFAULT E'IDLE'; diff --git a/prisma/migrations/20220106215040_add_mask_to_account/migration.sql b/prisma/migrations/20220106215040_add_mask_to_account/migration.sql new file mode 100644 index 00000000000..8a1a717d492 --- /dev/null +++ b/prisma/migrations/20220106215040_add_mask_to_account/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "account" ADD COLUMN "mask" TEXT; diff --git a/prisma/migrations/20220107170334_hypertable_chunk_size_tuning/migration.sql b/prisma/migrations/20220107170334_hypertable_chunk_size_tuning/migration.sql new file mode 100644 index 00000000000..2859e011b22 --- /dev/null +++ b/prisma/migrations/20220107170334_hypertable_chunk_size_tuning/migration.sql @@ -0,0 +1,28 @@ +-- Tune timescale chunk sizes + +-- account_balance -> 30d +CREATE TEMP TABLE account_balance_data ON COMMIT DROP AS ( + SELECT * FROM account_balance +); + +SELECT drop_chunks('account_balance', interval '0 days'); +TRUNCATE account_balance; + +SELECT set_chunk_time_interval('account_balance', interval '30 days'); + +INSERT INTO account_balance +SELECT * FROM account_balance_data; + + +-- security_pricing -> 30d +CREATE TEMP TABLE security_pricing_data ON COMMIT DROP AS ( + SELECT * FROM security_pricing +); + +SELECT drop_chunks('security_pricing', interval '0 days'); +TRUNCATE security_pricing; + +SELECT set_chunk_time_interval('security_pricing', interval '30 days'); + +INSERT INTO security_pricing +SELECT * FROM security_pricing_data; \ No newline at end of file diff --git a/prisma/migrations/20220112171128_update_fn/migration.sql b/prisma/migrations/20220112171128_update_fn/migration.sql new file mode 100644 index 00000000000..57bfe389cc8 --- /dev/null +++ b/prisma/migrations/20220112171128_update_fn/migration.sql @@ -0,0 +1,16 @@ +-- update the account_value_start_date to be the date prior to the first transaction/investment_transaction + +CREATE OR REPLACE FUNCTION account_value_start_date(p_account_id integer) RETURNS date STABLE AS $$ + SELECT + COALESCE( + CASE + WHEN a.valuation_method = 'valuation' THEN (SELECT MIN(date) FROM valuation WHERE account_id = p_account_id) + WHEN a.valuation_method = 'transaction' THEN (SELECT MIN(date) - 1 FROM transaction WHERE account_id = p_account_id) + WHEN a.valuation_method = 'investment-transaction' THEN (SELECT MIN(date) - 1 FROM investment_transaction WHERE account_id = p_account_id) + ELSE NULL + END, now()) + FROM + account a + WHERE + a.id = p_account_id +$$ LANGUAGE SQL; \ No newline at end of file diff --git a/prisma/migrations/20220121175453_account_liability_json/migration.sql b/prisma/migrations/20220121175453_account_liability_json/migration.sql new file mode 100644 index 00000000000..c36886f59f0 --- /dev/null +++ b/prisma/migrations/20220121175453_account_liability_json/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "account" ADD COLUMN "plaid_liability" JSONB; diff --git a/prisma/migrations/20220124193549_add_plaid_valuation_valuation_type/migration.sql b/prisma/migrations/20220124193549_add_plaid_valuation_valuation_type/migration.sql new file mode 100644 index 00000000000..90e8c75460f --- /dev/null +++ b/prisma/migrations/20220124193549_add_plaid_valuation_valuation_type/migration.sql @@ -0,0 +1,3 @@ +-- AlterEnum +ALTER TYPE "ValuationType" ADD VALUE 'PLAID_VALUATION'; + diff --git a/prisma/migrations/20220124211317_update_valuation_types_and_sources/migration.sql b/prisma/migrations/20220124211317_update_valuation_types_and_sources/migration.sql new file mode 100644 index 00000000000..d74f6453735 --- /dev/null +++ b/prisma/migrations/20220124211317_update_valuation_types_and_sources/migration.sql @@ -0,0 +1,19 @@ +-- Update generated column + +ALTER TABLE "account" DROP COLUMN "valuation_source"; + +ALTER TABLE "account" +ADD COLUMN "valuation_source" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "valuation_type" = 'PLAID_TRANSACTION' + OR "valuation_type" = 'PLAID_INVESTMENT_TRANSACTION' + OR "valuation_type" = 'PLAID_VALUATION' + THEN 'plaid' + WHEN "valuation_type" = 'USER_VALUATION' THEN 'user' + WHEN "valuation_type" = 'KBB_VALUATION' THEN 'kbb' + WHEN "valuation_type" = 'ZILLOW_VALUATION' THEN 'zillow' + ELSE 'other' + END +) STORED; + +ALTER TABLE "account" ALTER COLUMN "valuation_source" SET NOT NULL; \ No newline at end of file diff --git a/prisma/migrations/20220125211038_add_unique_constraint_to_valuations/migration.sql b/prisma/migrations/20220125211038_add_unique_constraint_to_valuations/migration.sql new file mode 100644 index 00000000000..42e9dbbafe1 --- /dev/null +++ b/prisma/migrations/20220125211038_add_unique_constraint_to_valuations/migration.sql @@ -0,0 +1,15 @@ +-- Remove duplicates +DELETE FROM valuation a +WHERE EXISTS ( + SELECT + 1 + FROM + valuation b + WHERE + b.account_id = a.account_id + AND b.source = a.source + AND b.date = a.date + AND b.id < a.id); + +-- CreateIndex +CREATE UNIQUE INDEX "valuation_account_id_source_date_key" ON "valuation"("account_id", "source", "date"); diff --git a/prisma/migrations/20220202184342_account_balances_gapfilled_fn/migration.sql b/prisma/migrations/20220202184342_account_balances_gapfilled_fn/migration.sql new file mode 100644 index 00000000000..44c695ddeeb --- /dev/null +++ b/prisma/migrations/20220202184342_account_balances_gapfilled_fn/migration.sql @@ -0,0 +1,97 @@ +-- returns a table of balances for each account in the date series +-- this is the foundation for all balance/net-worth calculations + +CREATE OR REPLACE FUNCTION account_balances_gapfilled(p_start date, p_end date, p_interval interval, p_account_ids integer[]) RETURNS TABLE(account_id integer, date date, balance bigint) LANGUAGE SQL STABLE AS $$ + WITH account_balances_gapfilled AS ( + -- fill in balance for start of range + ( + SELECT + ab.account_id, + p_start::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_start + GROUP BY + ab.account_id + ) + UNION + -- fill in balance for end of range + ( + SELECT + ab.account_id, + p_end::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_end + GROUP BY + ab.account_id + ) + UNION + -- this gapfill covers accounts who have at least 1 balance record in the range + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date) AS date, + locf( + first(ab.balance, ab.date), + COALESCE( + (SELECT balance FROM account_balance WHERE date < p_start AND account_id = ab.account_id ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = ab.account_id ORDER BY date ASC LIMIT 1) + ) + ) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date BETWEEN p_start AND p_end + GROUP BY + 1, 2 + ) + UNION + -- this gapfill covers accounts that either (a) have balance records outside range OR (b) don't have any balance records + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date, p_start, (p_end::date + interval '1d')::date) AS date, + locf(first(ab.balance, ab.date)) AS balance + FROM ( + SELECT + fb.account_id, + p_start::date AS date, + fb.balance + FROM ( + SELECT + a.id AS account_id, + COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date < p_start ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > p_end ORDER BY date ASC LIMIT 1), + a.current_balance + ) AS balance + FROM + account a + WHERE + a.id = ANY(p_account_ids) + AND a.id NOT IN (SELECT DISTINCT account_id FROM account_balance WHERE date BETWEEN p_start AND p_end) + ) fb + ) ab + GROUP BY + 1, 2 + ) + ) + SELECT DISTINCT ON (abg.account_id, abg.date) + abg.account_id, + abg.date, + CASE + WHEN a.start_date IS NOT NULL AND abg.date < a.start_date THEN 0 + ELSE COALESCE(abg.balance, 0) + END AS balance + FROM + account_balances_gapfilled abg + INNER JOIN account a ON a.id = abg.account_id +$$; \ No newline at end of file diff --git a/prisma/migrations/20220203234737_update_fn/migration.sql b/prisma/migrations/20220203234737_update_fn/migration.sql new file mode 100644 index 00000000000..cf6e0eb0888 --- /dev/null +++ b/prisma/migrations/20220203234737_update_fn/migration.sql @@ -0,0 +1,96 @@ +CREATE OR REPLACE FUNCTION account_balances_gapfilled(p_start date, p_end date, p_interval interval, p_account_ids integer[]) RETURNS TABLE(account_id integer, date date, balance bigint) LANGUAGE SQL STABLE AS $$ + WITH account_balances_gapfilled AS ( + -- fill in balance for start of range + ( + SELECT + ab.account_id, + p_start::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_start + GROUP BY + ab.account_id + ) + UNION + -- fill in balance for end of range + ( + SELECT + ab.account_id, + p_end::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_end + GROUP BY + ab.account_id + ) + UNION + -- this gapfill covers accounts who have at least 1 balance record in the range + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date) AS date, + locf( + first(ab.balance, ab.date), + COALESCE( + (SELECT balance FROM account_balance WHERE date < p_start AND account_id = ab.account_id ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = ab.account_id ORDER BY date ASC LIMIT 1) + ) + ) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date BETWEEN p_start AND p_end + GROUP BY + 1, 2 + ) + UNION + -- this gapfill covers accounts that either (a) have balance records outside range OR (b) don't have any balance records + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date, p_start, (p_end::date + interval '1d')::date) AS date, + locf(first(ab.balance, ab.date)) AS balance + FROM ( + SELECT + fb.account_id, + p_start::date AS date, + fb.balance + FROM ( + SELECT + a.id AS account_id, + COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date < p_start ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > p_end ORDER BY date ASC LIMIT 1), + a.current_balance + ) AS balance + FROM + account a + WHERE + a.id = ANY(p_account_ids) + AND a.id NOT IN (SELECT DISTINCT account_id FROM account_balance WHERE date BETWEEN p_start AND p_end) + ) fb + ) ab + GROUP BY + 1, 2 + ) + ) + SELECT DISTINCT ON (abg.account_id, abg.date) + abg.account_id, + abg.date, + CASE + WHEN a.start_date IS NOT NULL AND abg.date < a.start_date THEN 0 + ELSE COALESCE(abg.balance, 0) + END AS balance + FROM + account_balances_gapfilled abg + INNER JOIN account a ON a.id = abg.account_id + WHERE + abg.date BETWEEN p_start AND p_end +$$; \ No newline at end of file diff --git a/prisma/migrations/20220214175713_narrow_transaction_category/migration.sql b/prisma/migrations/20220214175713_narrow_transaction_category/migration.sql new file mode 100644 index 00000000000..ade5b6d8e67 --- /dev/null +++ b/prisma/migrations/20220214175713_narrow_transaction_category/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - The `category` column on the `transaction` table would be dropped and recreated. This will lead to data loss if there is data in the column. + +*/ +-- CreateEnum +CREATE TYPE "TransactionCategory" AS ENUM ('INCOME', 'EXPENSE', 'TRANSFER', 'PAYMENT'); + +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "category", +ADD COLUMN "category" "TransactionCategory"; diff --git a/prisma/migrations/20220215201534_transaction_remove_subcategory_add_plaid_category/migration.sql b/prisma/migrations/20220215201534_transaction_remove_subcategory_add_plaid_category/migration.sql new file mode 100644 index 00000000000..1946b376084 --- /dev/null +++ b/prisma/migrations/20220215201534_transaction_remove_subcategory_add_plaid_category/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `subcategory` on the `transaction` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "subcategory", +ADD COLUMN "plaid_category" TEXT[]; diff --git a/prisma/migrations/20220215212216_add_transaction_indexes/migration.sql b/prisma/migrations/20220215212216_add_transaction_indexes/migration.sql new file mode 100644 index 00000000000..f612cc94499 --- /dev/null +++ b/prisma/migrations/20220215212216_add_transaction_indexes/migration.sql @@ -0,0 +1,5 @@ +-- CreateIndex +CREATE INDEX "transaction_amount_idx" ON "transaction"("amount"); + +-- CreateIndex +CREATE INDEX "transaction_category_idx" ON "transaction"("category"); diff --git a/prisma/migrations/20220217040807_add_merchant_name_to_transactions/migration.sql b/prisma/migrations/20220217040807_add_merchant_name_to_transactions/migration.sql new file mode 100644 index 00000000000..1e4aedb6661 --- /dev/null +++ b/prisma/migrations/20220217040807_add_merchant_name_to_transactions/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "transaction" ADD COLUMN "merchant_name" TEXT; diff --git a/prisma/migrations/20220228233043_change_money_type/migration.sql b/prisma/migrations/20220228233043_change_money_type/migration.sql new file mode 100644 index 00000000000..5409fdab861 --- /dev/null +++ b/prisma/migrations/20220228233043_change_money_type/migration.sql @@ -0,0 +1,136 @@ +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "current_balance" SET DATA TYPE DECIMAL(19,4) USING current_balance::numeric / 100; +ALTER TABLE "account" ALTER COLUMN "available_balance" SET DATA TYPE DECIMAL(19,4) USING available_balance::numeric / 100; + +-- AlterTable +ALTER TABLE "account_balance" ALTER COLUMN "balance" SET DATA TYPE DECIMAL(19,4) USING balance::numeric / 100; +ALTER TABLE "account_balance" ALTER COLUMN "inflows" SET DEFAULT 0; +ALTER TABLE "account_balance" ALTER COLUMN "inflows" SET DATA TYPE DECIMAL(19,4) USING inflows::numeric / 100; +ALTER TABLE "account_balance" ALTER COLUMN "outflows" SET DEFAULT 0; +ALTER TABLE "account_balance" ALTER COLUMN "outflows" SET DATA TYPE DECIMAL(19,4) USING outflows::numeric / 100; + +-- AlterTable +ALTER TABLE "holding" ALTER COLUMN "value" SET DATA TYPE DECIMAL(19,4) USING value::numeric / 100; +ALTER TABLE "holding" ALTER COLUMN "cost_basis" SET DATA TYPE DECIMAL(23,8) USING cost_basis::numeric / 100; +ALTER TABLE "holding" ALTER COLUMN "price" SET DATA TYPE DECIMAL(23,8) USING price::numeric / 100; + +-- AlterTable +ALTER TABLE "transaction" DROP COLUMN "type"; +ALTER TABLE "transaction" ALTER COLUMN "amount" SET DATA TYPE DECIMAL(19,4) USING amount::numeric / 100; +ALTER TABLE "transaction" ADD COLUMN "type" "TransactionType" NOT NULL GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionType" ELSE 'OUTFLOW'::"TransactionType" END) STORED; + +-- AlterTable +ALTER TABLE "investment_transaction" DROP COLUMN "type"; +ALTER TABLE "investment_transaction" ALTER COLUMN "amount" SET DATA TYPE DECIMAL(19,4) USING amount::numeric / 100; +ALTER TABLE "investment_transaction" ALTER COLUMN "price" SET DATA TYPE DECIMAL(23,8) USING price::numeric / 100; +ALTER TABLE "investment_transaction" ADD COLUMN "type" "TransactionType" NOT NULL GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionType" ELSE 'OUTFLOW'::"TransactionType" END) STORED; + +-- AlterTable +ALTER TABLE "security_pricing" ALTER COLUMN "price_close" SET DATA TYPE DECIMAL(23,8); + +-- AlterTable +ALTER TABLE "valuation" ALTER COLUMN "amount" SET DATA TYPE DECIMAL(19,4) USING amount::numeric / 100; + +-- Update functions +DROP FUNCTION IF EXISTS calculate_account_balances; +DROP FUNCTION IF EXISTS account_balances_gapfilled(date,date,interval,integer[]); +CREATE OR REPLACE FUNCTION public.account_balances_gapfilled(p_start date, p_end date, p_interval interval, p_account_ids integer[]) + RETURNS TABLE(account_id integer, date date, balance numeric) + LANGUAGE sql + STABLE +AS $$ + WITH account_balances_gapfilled AS ( + -- fill in balance for start of range + ( + SELECT + ab.account_id, + p_start::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_start + GROUP BY + ab.account_id + ) + UNION + -- fill in balance for end of range + ( + SELECT + ab.account_id, + p_end::date AS date, + last(ab.balance, ab.date) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date <= p_end + GROUP BY + ab.account_id + ) + UNION + -- this gapfill covers accounts who have at least 1 balance record in the range + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date) AS date, + locf( + first(ab.balance, ab.date), + COALESCE( + (SELECT balance FROM account_balance WHERE date < p_start AND account_id = ab.account_id ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = ab.account_id ORDER BY date ASC LIMIT 1) + ) + ) AS balance + FROM + account_balance ab + WHERE + ab.account_id = ANY(p_account_ids) + AND ab.date BETWEEN p_start AND p_end + GROUP BY + 1, 2 + ) + UNION + -- this gapfill covers accounts that either (a) have balance records outside range OR (b) don't have any balance records + ( + SELECT + ab.account_id, + time_bucket_gapfill(p_interval, ab.date, p_start, (p_end::date + interval '1d')::date) AS date, + locf(first(ab.balance, ab.date)) AS balance + FROM ( + SELECT + fb.account_id, + p_start::date AS date, + fb.balance + FROM ( + SELECT + a.id AS account_id, + COALESCE( + (SELECT balance FROM account_balance WHERE account_id = a.id AND date < p_start ORDER BY date DESC LIMIT 1), + (SELECT balance FROM account_balance WHERE account_id = a.id AND date > p_end ORDER BY date ASC LIMIT 1), + a.current_balance + ) AS balance + FROM + account a + WHERE + a.id = ANY(p_account_ids) + AND a.id NOT IN (SELECT DISTINCT account_id FROM account_balance WHERE date BETWEEN p_start AND p_end) + ) fb + ) ab + GROUP BY + 1, 2 + ) + ) + SELECT DISTINCT ON (abg.account_id, abg.date) + abg.account_id, + abg.date, + CASE + WHEN a.start_date IS NOT NULL AND abg.date < a.start_date THEN 0 + ELSE COALESCE(abg.balance, 0) + END AS balance + FROM + account_balances_gapfilled abg + INNER JOIN account a ON a.id = abg.account_id + WHERE + abg.date BETWEEN p_start AND p_end +$$; \ No newline at end of file diff --git a/prisma/migrations/20220302181536_add_price_as_of_to_security_pricing/migration.sql b/prisma/migrations/20220302181536_add_price_as_of_to_security_pricing/migration.sql new file mode 100644 index 00000000000..7007211a464 --- /dev/null +++ b/prisma/migrations/20220302181536_add_price_as_of_to_security_pricing/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "security_pricing" ADD COLUMN "price_as_of" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP; diff --git a/prisma/migrations/20220307200633_remove_price_from_holding/migration.sql b/prisma/migrations/20220307200633_remove_price_from_holding/migration.sql new file mode 100644 index 00000000000..9ce791aea7b --- /dev/null +++ b/prisma/migrations/20220307200633_remove_price_from_holding/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `price` on the `holding` table. All the data in the column will be lost. + - You are about to drop the column `price_as_of` on the `holding` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "holding" DROP COLUMN "price", +DROP COLUMN "price_as_of"; diff --git a/prisma/migrations/20220307211701_valuation_trigger/migration.sql b/prisma/migrations/20220307211701_valuation_trigger/migration.sql new file mode 100644 index 00000000000..a8579ad4461 --- /dev/null +++ b/prisma/migrations/20220307211701_valuation_trigger/migration.sql @@ -0,0 +1,19 @@ +-- the valuation_changed trigger is used to keep `account.start_date` and `account.current_balance` in sync with the account's valuations +CREATE OR REPLACE FUNCTION valuation_changed() RETURNS TRIGGER LANGUAGE plpgsql AS $$ + BEGIN + UPDATE account AS a + SET + start_date = account_value_start_date(a.id), + current_balance = (SELECT v.amount FROM valuation v WHERE v.account_id = a.id ORDER BY v.date DESC LIMIT 1) + WHERE + (a.id = NEW.account_id OR a.id = OLD.account_id) + AND a.valuation_method = 'valuation'; + RETURN NULL; + END; +$$; + +CREATE OR REPLACE TRIGGER valuation_changed + AFTER INSERT OR UPDATE OF account_id, amount, date OR DELETE + ON valuation + FOR EACH ROW + EXECUTE FUNCTION valuation_changed(); \ No newline at end of file diff --git a/prisma/migrations/20220311165323_add_shares_per_contract_to_security/migration.sql b/prisma/migrations/20220311165323_add_shares_per_contract_to_security/migration.sql new file mode 100644 index 00000000000..4927573599e --- /dev/null +++ b/prisma/migrations/20220311165323_add_shares_per_contract_to_security/migration.sql @@ -0,0 +1,8 @@ +-- AlterTable +ALTER TABLE "security" ADD COLUMN "shares_per_contract" DECIMAL(36,18); + +-- Remove incorrect derivative pricing from Plaid +DELETE FROM "security_pricing" sp USING "security" s +WHERE s.id = sp.security_id + AND s. "plaid_type" = 'derivative' + AND sp. "source" = 'plaid'; \ No newline at end of file diff --git a/prisma/migrations/20220315172110_institution/migration.sql b/prisma/migrations/20220315172110_institution/migration.sql new file mode 100644 index 00000000000..b70a72616dd --- /dev/null +++ b/prisma/migrations/20220315172110_institution/migration.sql @@ -0,0 +1,20 @@ +-- CreateEnum +CREATE TYPE "Provider" AS ENUM ('PLAID', 'FINICITY'); + +-- CreateTable +CREATE TABLE "institution" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "provider" "Provider" NOT NULL, + "provider_id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "logo" TEXT, + "primary_color" TEXT, + "data" JSONB NOT NULL, + + CONSTRAINT "institution_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "institution_provider_provider_id_key" ON "institution"("provider", "provider_id"); diff --git a/prisma/migrations/20220316200652_reset_plaid_derivative_prices/migration.sql b/prisma/migrations/20220316200652_reset_plaid_derivative_prices/migration.sql new file mode 100644 index 00000000000..6ad21a7ba1c --- /dev/null +++ b/prisma/migrations/20220316200652_reset_plaid_derivative_prices/migration.sql @@ -0,0 +1,8 @@ +-- Remove incorrect derivative pricing from Plaid +DELETE FROM "security_pricing" sp USING "security" s +WHERE s.id = sp.security_id + AND s. "plaid_type" = 'derivative' + AND sp. "source" = 'plaid'; + +-- Set `pricing_last_synced_at` to null so that derivatives re-sync from Plaid +UPDATE security SET pricing_last_synced_at = NULL WHERE plaid_type = 'derivative'; \ No newline at end of file diff --git a/prisma/migrations/20220317191949_reset_plaid_derivative_prices_again/migration.sql b/prisma/migrations/20220317191949_reset_plaid_derivative_prices_again/migration.sql new file mode 100644 index 00000000000..6ad21a7ba1c --- /dev/null +++ b/prisma/migrations/20220317191949_reset_plaid_derivative_prices_again/migration.sql @@ -0,0 +1,8 @@ +-- Remove incorrect derivative pricing from Plaid +DELETE FROM "security_pricing" sp USING "security" s +WHERE s.id = sp.security_id + AND s. "plaid_type" = 'derivative' + AND sp. "source" = 'plaid'; + +-- Set `pricing_last_synced_at` to null so that derivatives re-sync from Plaid +UPDATE security SET pricing_last_synced_at = NULL WHERE plaid_type = 'derivative'; \ No newline at end of file diff --git a/prisma/migrations/20220323203441_multi_provider_updates/migration.sql b/prisma/migrations/20220323203441_multi_provider_updates/migration.sql new file mode 100644 index 00000000000..be656f11e78 --- /dev/null +++ b/prisma/migrations/20220323203441_multi_provider_updates/migration.sql @@ -0,0 +1,130 @@ +/* + Warnings: + + - A unique constraint covering the columns `[account_connection_id,finicity_account_id]` on the table `account` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[finicity_holding_id]` on the table `holding` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[finicity_transaction_id]` on the table `investment_transaction` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[finicity_transaction_id]` on the table `transaction` will be added. If there are existing duplicate values, this will fail. + - Changed the type of `type` on the `account` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + - Changed the type of `valuation_method` on the `account` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + - Changed the type of `valuation_source` on the `account` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + - Changed the type of `type` on the `account_connection` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + +*/ +-- CreateEnum +CREATE TYPE "AccountConnectionType" AS ENUM ('plaid', 'finicity'); + +-- CreateEnum +CREATE TYPE "AccountType" AS ENUM ('plaid', 'finicity', 'property', 'vehicle', 'other'); + +-- CreateEnum +CREATE TYPE "ValuationSource" AS ENUM ('PLAID', 'FINICITY', 'KBB', 'ZILLOW', 'USER'); + +-- CreateEnum +CREATE TYPE "ValuationMethod" AS ENUM ('TRANSACTION', 'INVESTMENT_TRANSACTION', 'VALUATION'); + +-- AlterTable +ALTER TABLE "account" +ADD COLUMN "finicity_account_id" TEXT, +ADD COLUMN "finicity_detail" JSONB, +ADD COLUMN "finicity_type" TEXT, +DROP COLUMN "valuation_method", +DROP COLUMN "valuation_source", +DROP COLUMN "category", +DROP COLUMN "subcategory"; + +ALTER TABLE "account" ALTER COLUMN "type" TYPE "AccountType" USING "type"::"AccountType"; + +-- Due to a limitation in postgres, we must recreate the enum if we're going to use the new values in this transaction (instead of `ALTER TYPE ADD VALUE `). +ALTER TYPE "ValuationType" RENAME TO "ValuationType_old"; +CREATE TYPE "ValuationType" AS ENUM ('PLAID_TRANSACTION', 'PLAID_INVESTMENT_TRANSACTION', 'PLAID_VALUATION', 'FINICITY_TRANSACTION', 'FINICITY_INVESTMENT_TRANSACTION', 'KBB_VALUATION', 'ZILLOW_VALUATION', 'USER_VALUATION'); +ALTER TABLE "account" ALTER COLUMN "valuation_type" TYPE "ValuationType" USING "valuation_type"::text::"ValuationType"; +DROP TYPE "ValuationType_old"; + +-- update valuation_method +ALTER TABLE "account" +ADD COLUMN "valuation_method" "ValuationMethod" GENERATED ALWAYS AS ( + CASE + WHEN "valuation_type" IN ('PLAID_TRANSACTION', 'FINICITY_TRANSACTION') THEN 'TRANSACTION'::"ValuationMethod" + WHEN "valuation_type" IN ('PLAID_INVESTMENT_TRANSACTION', 'FINICITY_INVESTMENT_TRANSACTION') THEN 'INVESTMENT_TRANSACTION'::"ValuationMethod" + WHEN "valuation_type" IN ('PLAID_VALUATION', 'KBB_VALUATION', 'ZILLOW_VALUATION', 'USER_VALUATION') THEN 'VALUATION'::"ValuationMethod" + END +) STORED; +ALTER TABLE "account" ALTER COLUMN "valuation_method" SET NOT NULL; + +-- update valuation_source +ALTER TABLE "account" +ADD COLUMN "valuation_source" "ValuationSource" GENERATED ALWAYS AS ( + CASE + WHEN "valuation_type" IN ('PLAID_TRANSACTION', 'PLAID_INVESTMENT_TRANSACTION', 'PLAID_VALUATION') THEN 'PLAID'::"ValuationSource" + WHEN "valuation_type" IN ('FINICITY_TRANSACTION', 'FINICITY_INVESTMENT_TRANSACTION') THEN 'FINICITY'::"ValuationSource" + WHEN "valuation_type" = 'KBB_VALUATION' THEN 'KBB'::"ValuationSource" + WHEN "valuation_type" = 'ZILLOW_VALUATION' THEN 'ZILLOW'::"ValuationSource" + WHEN "valuation_type" = 'USER_VALUATION' THEN 'USER'::"ValuationSource" + END +) STORED; +ALTER TABLE "account" ALTER COLUMN "valuation_source" SET NOT NULL; + +-- update category +ALTER TABLE "account" +ADD COLUMN "category" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "type" = 'plaid' AND "plaid_type" IN ('depository') THEN 'cash' + WHEN "type" = 'plaid' AND "plaid_type" IN ('investment' ,'brokerage') THEN 'investment' + WHEN "type" = 'plaid' AND "plaid_type" IN ('loan') THEN 'loan' + WHEN "type" = 'plaid' AND "plaid_type" IN ('credit') THEN 'credit' + WHEN "type" = 'property' THEN 'property' + WHEN "type" = 'vehicle' THEN 'vehicle' + ELSE 'other' + END +) STORED; +ALTER TABLE "account" ALTER COLUMN "category" SET NOT NULL; + +-- update subcategory +ALTER TABLE "account" +ADD COLUMN "subcategory" TEXT GENERATED ALWAYS AS ( + CASE + WHEN "subcategory_override" IS NOT NULL THEN "subcategory_override" + WHEN "type" = 'plaid' THEN "plaid_subtype" + WHEN "type" = 'property' THEN 'property' + WHEN "type" = 'vehicle' THEN 'vehicle' + ELSE 'other' + END +) STORED; +ALTER TABLE "account" ALTER COLUMN "subcategory" SET NOT NULL; + +-- AlterTable +ALTER TABLE "account_connection" +ADD COLUMN "finicity_institution_id" TEXT, +ADD COLUMN "finicity_institution_login_id" TEXT; + +ALTER TABLE "account_connection" ALTER COLUMN "type" TYPE "AccountConnectionType" USING "type"::"AccountConnectionType"; + +-- AlterTable +ALTER TABLE "holding" ADD COLUMN "finicity_holding_id" TEXT; + +-- AlterTable +ALTER TABLE "investment_transaction" +ADD COLUMN "finicity_investment_transaction_type" TEXT, +ADD COLUMN "finicity_transaction_id" TEXT; + +-- AlterTable +ALTER TABLE "transaction" +ADD COLUMN "finicity_categorization" JSONB, +ADD COLUMN "finicity_transaction_id" TEXT, +ADD COLUMN "finicity_type" TEXT; + +-- AlterTable +ALTER TABLE "user" ADD COLUMN "finicity_customer_id" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "account_account_connection_id_finicity_account_id_key" ON "account"("account_connection_id", "finicity_account_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "holding_finicity_holding_id_key" ON "holding"("finicity_holding_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "investment_transaction_finicity_transaction_id_key" ON "investment_transaction"("finicity_transaction_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "transaction_finicity_transaction_id_key" ON "transaction"("finicity_transaction_id"); diff --git a/prisma/migrations/20220323212807_fix_function/migration.sql b/prisma/migrations/20220323212807_fix_function/migration.sql new file mode 100644 index 00000000000..c0649ae71ef --- /dev/null +++ b/prisma/migrations/20220323212807_fix_function/migration.sql @@ -0,0 +1,28 @@ +CREATE OR REPLACE FUNCTION public.account_value_start_date(p_account_id integer) RETURNS date LANGUAGE sql STABLE AS $$ + SELECT + COALESCE( + CASE + WHEN a.valuation_method = 'VALUATION' THEN (SELECT MIN(date) FROM valuation WHERE account_id = p_account_id) + WHEN a.valuation_method = 'TRANSACTION' THEN (SELECT MIN(date) - 1 FROM transaction WHERE account_id = p_account_id) + WHEN a.valuation_method = 'INVESTMENT_TRANSACTION' THEN (SELECT MIN(date) - 1 FROM investment_transaction WHERE account_id = p_account_id) + ELSE NULL + END, now()) + FROM + account a + WHERE + a.id = p_account_id +$$; + + +CREATE OR REPLACE FUNCTION valuation_changed() RETURNS TRIGGER LANGUAGE plpgsql AS $$ + BEGIN + UPDATE account AS a + SET + start_date = account_value_start_date(a.id), + current_balance = (SELECT v.amount FROM valuation v WHERE v.account_id = a.id ORDER BY v.date DESC LIMIT 1) + WHERE + (a.id = NEW.account_id OR a.id = OLD.account_id) + AND a.valuation_method = 'VALUATION'; + RETURN NULL; + END; +$$; \ No newline at end of file diff --git a/prisma/migrations/20220411193518_stop_generating_and_enumize_account_category/migration.sql b/prisma/migrations/20220411193518_stop_generating_and_enumize_account_category/migration.sql new file mode 100644 index 00000000000..e847786e466 --- /dev/null +++ b/prisma/migrations/20220411193518_stop_generating_and_enumize_account_category/migration.sql @@ -0,0 +1,14 @@ +-- CreateEnum +CREATE TYPE "AccountCategory" AS ENUM ('cash', 'investment', 'crypto', 'property', 'vehicle', 'valuable', 'loan', 'mortgage', 'credit', 'other'); + +-- Add new column with temporary suffix +ALTER TABLE "account" ADD COLUMN "category_tmp" "AccountCategory" NOT NULL DEFAULT E'other'; + +-- Populate new column with enum values +UPDATE "account" SET "category_tmp" = "category"::"AccountCategory"; + +-- Drop old column +ALTER TABLE "account" DROP COLUMN "category"; + +-- Rename new column to take its place +ALTER TABLE "account" RENAME COLUMN "category_tmp" TO "category"; \ No newline at end of file diff --git a/prisma/migrations/20220426190758_add_url_and_logo_url_to_institution/migration.sql b/prisma/migrations/20220426190758_add_url_and_logo_url_to_institution/migration.sql new file mode 100644 index 00000000000..e6179d0d4d0 --- /dev/null +++ b/prisma/migrations/20220426190758_add_url_and_logo_url_to_institution/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "institution" ADD COLUMN "logo_url" TEXT, +ADD COLUMN "url" TEXT; diff --git a/prisma/migrations/20220504231954_finicity_updates/migration.sql b/prisma/migrations/20220504231954_finicity_updates/migration.sql new file mode 100644 index 00000000000..7d94e3def5e --- /dev/null +++ b/prisma/migrations/20220504231954_finicity_updates/migration.sql @@ -0,0 +1,23 @@ +/* + Warnings: + + - You are about to drop the column `finicity_holding_id` on the `holding` table. All the data in the column will be lost. + - A unique constraint covering the columns `[finicity_position_id]` on the table `holding` will be added. If there are existing duplicate values, this will fail. + - A unique constraint covering the columns `[finicity_security_id,finicity_security_id_type]` on the table `security` will be added. If there are existing duplicate values, this will fail. + +*/ +-- DropIndex +DROP INDEX "holding_finicity_holding_id_key"; + +-- AlterTable +ALTER TABLE "holding" RENAME COLUMN "finicity_holding_id" TO "finicity_position_id"; + +-- CreateIndex +CREATE UNIQUE INDEX "holding_finicity_position_id_key" ON "holding"("finicity_position_id"); + +-- AlterTable +ALTER TABLE "security" ADD COLUMN "finicity_security_id" TEXT, +ADD COLUMN "finicity_security_id_type" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "security_finicity_security_id_finicity_security_id_type_key" ON "security"("finicity_security_id", "finicity_security_id_type"); diff --git a/prisma/migrations/20220518005502_finicity_customer_id_uniqueness/migration.sql b/prisma/migrations/20220518005502_finicity_customer_id_uniqueness/migration.sql new file mode 100644 index 00000000000..8252ae5966e --- /dev/null +++ b/prisma/migrations/20220518005502_finicity_customer_id_uniqueness/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[finicity_customer_id]` on the table `user` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "user_finicity_customer_id_key" ON "user"("finicity_customer_id"); diff --git a/prisma/migrations/20220519192445_institution_refactor/migration.sql b/prisma/migrations/20220519192445_institution_refactor/migration.sql new file mode 100644 index 00000000000..fa972caebb1 --- /dev/null +++ b/prisma/migrations/20220519192445_institution_refactor/migration.sql @@ -0,0 +1,57 @@ +/* + Warnings: + + - You are about to drop the column `data` on the `institution` table. All the data in the column will be lost. + - You are about to drop the column `provider` on the `institution` table. All the data in the column will be lost. + - You are about to drop the column `provider_id` on the `institution` table. All the data in the column will be lost. + +*/ +-- CreateTable +CREATE TABLE "provider_institution" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "provider" "Provider" NOT NULL, + "provider_id" TEXT NOT NULL, + "institution_id" INTEGER, + "rank" INTEGER NOT NULL DEFAULT 0, + "name" TEXT NOT NULL, + "url" TEXT, + "logo" TEXT, + "logo_url" TEXT, + "primary_color" TEXT, + "data" JSONB, + + CONSTRAINT "provider_institution_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "provider_institution_provider_provider_id_key" ON "provider_institution"("provider", "provider_id"); + +-- migrate institution data into provider_institution +INSERT INTO provider_institution (provider, provider_id, name, url, logo, logo_url, primary_color, data) +SELECT + provider, + provider_id, + name, + url, + logo, + logo_url, + primary_color, + data +FROM + institution; + +-- delete data from institution table +TRUNCATE TABLE institution RESTART IDENTITY; + +-- AddForeignKey +ALTER TABLE "provider_institution" ADD CONSTRAINT "provider_institution_institution_id_fkey" FOREIGN KEY ("institution_id") REFERENCES "institution"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- DropIndex +DROP INDEX "institution_provider_provider_id_key"; + +-- AlterTable +ALTER TABLE "institution" DROP COLUMN "data", +DROP COLUMN "provider", +DROP COLUMN "provider_id"; \ No newline at end of file diff --git a/prisma/migrations/20220520161223_institution_search_algo/migration.sql b/prisma/migrations/20220520161223_institution_search_algo/migration.sql new file mode 100644 index 00000000000..12d9fa79c92 --- /dev/null +++ b/prisma/migrations/20220520161223_institution_search_algo/migration.sql @@ -0,0 +1,13 @@ +CREATE OR REPLACE FUNCTION edge_ngram_tsvector(text text, config regconfig DEFAULT 'simple') RETURNS tsvector LANGUAGE SQL IMMUTABLE AS $$ + SELECT + array_to_tsvector(( + SELECT + array_agg(DISTINCT substring(lexeme FOR len)) + FROM + unnest(to_tsvector(config, text)), + generate_series(1, length(lexeme)) len + )) +$$; + +CREATE INDEX institution_name_ngram_idx ON institution USING GIN (edge_ngram_tsvector(name)); +CREATE INDEX provider_institution_name_ngram_idx ON provider_institution USING GIN (edge_ngram_tsvector(name)); diff --git a/prisma/migrations/20220606160203_add_finicity_username_to_user/migration.sql b/prisma/migrations/20220606160203_add_finicity_username_to_user/migration.sql new file mode 100644 index 00000000000..7e0337ecdb0 --- /dev/null +++ b/prisma/migrations/20220606160203_add_finicity_username_to_user/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - A unique constraint covering the columns `[finicity_username]` on the table `user` will be added. If there are existing duplicate values, this will fail. + +*/ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "finicity_username" TEXT; + +-- CreateIndex +CREATE UNIQUE INDEX "user_finicity_username_key" ON "user"("finicity_username"); + +-- Populate new field for users that already have a finicity_customer_id +UPDATE "user" SET "finicity_username" = LPAD("user"."id"::text, 6, '0') WHERE "finicity_customer_id" IS NOT NULL; \ No newline at end of file diff --git a/prisma/migrations/20220607162542_add_crisp_session_token_to_user/migration.sql b/prisma/migrations/20220607162542_add_crisp_session_token_to_user/migration.sql new file mode 100644 index 00000000000..d2d71b730c4 --- /dev/null +++ b/prisma/migrations/20220607162542_add_crisp_session_token_to_user/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "crisp_session_token" TEXT NOT NULL DEFAULT gen_random_uuid(); diff --git a/prisma/migrations/20220608171009_add_success_rate_and_oauth_to_provider_institutions/migration.sql b/prisma/migrations/20220608171009_add_success_rate_and_oauth_to_provider_institutions/migration.sql new file mode 100644 index 00000000000..a87908e5027 --- /dev/null +++ b/prisma/migrations/20220608171009_add_success_rate_and_oauth_to_provider_institutions/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "provider_institution" ADD COLUMN "oauth" BOOLEAN NOT NULL DEFAULT false, +ADD COLUMN "success_rate" DECIMAL(65,30); diff --git a/prisma/migrations/20220608190342_add_unique_constraint_to_institution/migration.sql b/prisma/migrations/20220608190342_add_unique_constraint_to_institution/migration.sql new file mode 100644 index 00000000000..9abe74f8554 --- /dev/null +++ b/prisma/migrations/20220608190342_add_unique_constraint_to_institution/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[name,url]` on the table `institution` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "institution_name_url_key" ON "institution"("name", "url"); diff --git a/prisma/migrations/20220608202739_add_success_rate_updated_to_provider_institution/migration.sql b/prisma/migrations/20220608202739_add_success_rate_updated_to_provider_institution/migration.sql new file mode 100644 index 00000000000..c5855b375c6 --- /dev/null +++ b/prisma/migrations/20220608202739_add_success_rate_updated_to_provider_institution/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "provider_institution" ADD COLUMN "success_rate_updated" TIMESTAMPTZ(6); diff --git a/prisma/migrations/20220609195136_remove_success_rate_from_provider_institution/migration.sql b/prisma/migrations/20220609195136_remove_success_rate_from_provider_institution/migration.sql new file mode 100644 index 00000000000..2b488de7611 --- /dev/null +++ b/prisma/migrations/20220609195136_remove_success_rate_from_provider_institution/migration.sql @@ -0,0 +1,10 @@ +/* + Warnings: + + - You are about to drop the column `success_rate` on the `provider_institution` table. All the data in the column will be lost. + - You are about to drop the column `success_rate_updated` on the `provider_institution` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "provider_institution" DROP COLUMN "success_rate", +DROP COLUMN "success_rate_updated"; diff --git a/prisma/migrations/20220622160129_add_finicity_error/migration.sql b/prisma/migrations/20220622160129_add_finicity_error/migration.sql new file mode 100644 index 00000000000..69545f8cb37 --- /dev/null +++ b/prisma/migrations/20220622160129_add_finicity_error/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "account_connection" ADD COLUMN "finicity_error" JSONB; diff --git a/prisma/migrations/20220623171212_remove_holding_unique_constraint/migration.sql b/prisma/migrations/20220623171212_remove_holding_unique_constraint/migration.sql new file mode 100644 index 00000000000..436b9c9869b --- /dev/null +++ b/prisma/migrations/20220623171212_remove_holding_unique_constraint/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "holding_account_id_security_id_key"; diff --git a/prisma/migrations/20220630005107_category_overrides/migration.sql b/prisma/migrations/20220630005107_category_overrides/migration.sql new file mode 100644 index 00000000000..9efe215a9ef --- /dev/null +++ b/prisma/migrations/20220630005107_category_overrides/migration.sql @@ -0,0 +1,29 @@ +-- account +ALTER TABLE "account" RENAME COLUMN "category" TO "category_provider"; +ALTER TABLE "account" ALTER COLUMN "category_provider" DROP NOT NULL; +ALTER TABLE "account" ADD COLUMN "category_user" "AccountCategory"; +ALTER TABLE "account" ADD CONSTRAINT category_present CHECK (num_nonnulls(category_user, category_provider) > 0); +ALTER TABLE "account" ADD COLUMN "category" "AccountCategory" NOT NULL GENERATED ALWAYS AS ( + COALESCE(category_user, category_provider) +) STORED; + +ALTER TABLE "account" RENAME COLUMN "subcategory" TO "subcategory_provider"; +ALTER TABLE "account" RENAME COLUMN "subcategory_override" TO "subcategory_user"; +ALTER TABLE "account" ADD COLUMN "subcategory" TEXT NOT NULL GENERATED ALWAYS AS ( + COALESCE( + subcategory_user, + CASE + WHEN "type" = 'plaid' THEN "plaid_subtype" + WHEN "type" = 'property' THEN 'property' + WHEN "type" = 'vehicle' THEN 'vehicle' + ELSE 'other' + END + ) +) STORED; + +-- transaction +ALTER TABLE "transaction" RENAME COLUMN "category" TO "category_provider"; +ALTER TABLE "transaction" ADD COLUMN "category_user" "TransactionCategory"; +ALTER TABLE "transaction" ADD COLUMN "category" "TransactionCategory" GENERATED ALWAYS AS ( + COALESCE(category_user, category_provider) +) STORED; diff --git a/prisma/migrations/20220701013813_merge_updates/migration.sql b/prisma/migrations/20220701013813_merge_updates/migration.sql new file mode 100644 index 00000000000..577a92a49eb --- /dev/null +++ b/prisma/migrations/20220701013813_merge_updates/migration.sql @@ -0,0 +1,8 @@ +-- DropIndex +DROP INDEX "transaction_category_idx"; + +-- AlterTable +ALTER TABLE "account" ALTER COLUMN "category_provider" DROP DEFAULT; + +-- CreateIndex +CREATE INDEX "transaction_category_idx" ON "transaction"("category"); diff --git a/prisma/migrations/20220707195013_user_overrides/migration.sql b/prisma/migrations/20220707195013_user_overrides/migration.sql new file mode 100644 index 00000000000..5435aca0c87 --- /dev/null +++ b/prisma/migrations/20220707195013_user_overrides/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "monthly_debt_user" DECIMAL(19,4), +ADD COLUMN "monthly_expenses_user" DECIMAL(19,4), +ADD COLUMN "monthly_income_user" DECIMAL(19,4); diff --git a/prisma/migrations/20220708191740_txn_excluded_flag/migration.sql b/prisma/migrations/20220708191740_txn_excluded_flag/migration.sql new file mode 100644 index 00000000000..8182bf75b7f --- /dev/null +++ b/prisma/migrations/20220708191740_txn_excluded_flag/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "transaction" ADD COLUMN "excluded" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20220713134742_add_provider_field/migration.sql b/prisma/migrations/20220713134742_add_provider_field/migration.sql new file mode 100644 index 00000000000..52a852bf1b0 --- /dev/null +++ b/prisma/migrations/20220713134742_add_provider_field/migration.sql @@ -0,0 +1,11 @@ +CREATE TYPE "AccountProvider" AS ENUM ('user', 'plaid', 'finicity'); + +ALTER TABLE "account" ADD COLUMN "provider" "AccountProvider"; + +UPDATE "account" set "provider" = LOWER("valuation_source"::text)::"AccountProvider"; + +ALTER TABLE "account" + ALTER COLUMN "provider" SET NOT NULL, + DROP COLUMN "valuation_source"; + +DROP TYPE "ValuationSource"; \ No newline at end of file diff --git a/prisma/migrations/20220714180514_update_account_start_date_fn/migration.sql b/prisma/migrations/20220714180514_update_account_start_date_fn/migration.sql new file mode 100644 index 00000000000..1edcadbd710 --- /dev/null +++ b/prisma/migrations/20220714180514_update_account_start_date_fn/migration.sql @@ -0,0 +1,25 @@ +CREATE OR REPLACE FUNCTION public.account_value_start_date(p_account_id integer) RETURNS date LANGUAGE sql STABLE AS $$ + SELECT + LEAST( + (SELECT MIN(date) FROM "transaction" where "account_id" = p_account_id), + (SELECT MIN(date) FROM "valuation" where "account_id" = p_account_id), + (SELECT MIN(date) FROM "investment_transaction" where "account_id" = p_account_id), + now() + ) + FROM + account a + WHERE + a.id = p_account_id +$$; + + +CREATE OR REPLACE FUNCTION valuation_changed() RETURNS TRIGGER LANGUAGE plpgsql AS $$ + BEGIN + UPDATE account AS a + SET + start_date = account_value_start_date(a.id), + current_balance = (SELECT v.amount FROM valuation v WHERE v.account_id = a.id ORDER BY v.date DESC LIMIT 1) + WHERE a.id = NEW.account_id OR a.id = OLD.account_id; + RETURN NULL; + END; +$$; \ No newline at end of file diff --git a/prisma/migrations/20220714180819_account_category_consolidation/migration.sql b/prisma/migrations/20220714180819_account_category_consolidation/migration.sql new file mode 100644 index 00000000000..189e8a8f6e7 --- /dev/null +++ b/prisma/migrations/20220714180819_account_category_consolidation/migration.sql @@ -0,0 +1,33 @@ +BEGIN; + +ALTER TABLE "account" RENAME COLUMN "category" TO "category_old"; +ALTER TABLE "account" RENAME COLUMN "category_provider" TO "category_provider_old"; +ALTER TABLE "account" RENAME COLUMN "category_user" TO "category_user_old"; +ALTER TYPE "AccountCategory" RENAME TO "AccountCategory_old"; + +CREATE TYPE "AccountCategory" AS ENUM ('cash', 'investment', 'crypto', 'property', 'vehicle', 'valuable', 'loan', 'credit', 'other'); +ALTER TABLE "account" ADD COLUMN "category_provider" "AccountCategory"; +ALTER TABLE "account" ADD COLUMN "category_user" "AccountCategory"; + +UPDATE "account" +SET "category_provider" = CASE + WHEN "category_provider_old" = 'mortgage' THEN 'loan' + ELSE "category_provider_old"::TEXT::"AccountCategory" +END; + +UPDATE "account" +SET "category_user" = CASE + WHEN "category_user_old" = 'mortgage' THEN 'loan' + ELSE "category_user_old"::TEXT::"AccountCategory" +END; + +ALTER TABLE "account" ADD COLUMN "category" "AccountCategory" NOT NULL GENERATED ALWAYS AS ( + COALESCE(category_user, category_provider, 'other') +) STORED; + +ALTER TABLE "account" DROP COLUMN "category_old"; +ALTER TABLE "account" DROP COLUMN "category_provider_old"; +ALTER TABLE "account" DROP COLUMN "category_user_old"; +DROP TYPE "AccountCategory_old"; + +COMMIT; \ No newline at end of file diff --git a/prisma/migrations/20220714181018_update_account_type_model/migration.sql b/prisma/migrations/20220714181018_update_account_type_model/migration.sql new file mode 100644 index 00000000000..69c926ecb9e --- /dev/null +++ b/prisma/migrations/20220714181018_update_account_type_model/migration.sql @@ -0,0 +1,67 @@ +BEGIN; + +-- Make the provider subcategory field optional rather than generated +ALTER TABLE "account" ADD COLUMN "subcategory_provider_new" TEXT; +ALTER TABLE "account" RENAME COLUMN "subcategory_provider" TO "subcategory_provider_old"; +UPDATE "account" SET "subcategory_provider_new" = "subcategory_provider_old"; +ALTER TABLE "account" RENAME COLUMN "subcategory_provider_new" TO "subcategory_provider"; +ALTER TABLE "account" DROP COLUMN "subcategory_provider_old"; + +CREATE TYPE "AccountType_new" AS ENUM ('INVESTMENT', 'DEPOSITORY', 'CREDIT', 'LOAN', 'PROPERTY', 'VEHICLE', 'OTHER_ASSET', 'OTHER_LIABILITY'); +ALTER TABLE "account" ADD COLUMN "type_new" "AccountType_new"; + +UPDATE "account" +SET "type_new" = CASE + WHEN "type" IN ('vehicle', 'property') THEN UPPER("type"::text)::"AccountType_new" + WHEN "valuation_method" = 'TRANSACTION' AND "classification" = 'asset' THEN 'DEPOSITORY' + WHEN "valuation_method" = 'INVESTMENT_TRANSACTION' THEN 'INVESTMENT' + WHEN "type" = 'finicity' AND "finicity_type" = 'creditCard' THEN 'CREDIT' + WHEN "type" = 'finicity' AND "finicity_type" <> 'creditCard' THEN 'LOAN' + WHEN "type" = 'plaid' AND "plaid_liability" -> 'credit' IS NOT NULL THEN 'CREDIT' + WHEN "type" = 'plaid' AND "plaid_liability" -> 'mortgage' IS NOT NULL THEN 'LOAN' + WHEN "type" = 'plaid' AND "plaid_liability" -> 'student' IS NOT NULL THEN 'LOAN' + WHEN "type" = 'plaid' AND "plaid_type" = 'credit' THEN 'CREDIT' + WHEN "type" = 'plaid' AND "plaid_type" = 'loan' THEN 'LOAN' + WHEN "type" = 'other' AND "category" = 'loan' THEN 'LOAN' + WHEN "type" = 'other' AND "category" = 'credit' THEN 'CREDIT' + WHEN "type" = 'other' AND "classification" = 'asset' THEN 'OTHER_ASSET' + WHEN "type" = 'other' AND "classification" = 'liability' THEN 'OTHER_LIABILITY' +END; + +ALTER TABLE "account" ALTER COLUMN "type_new" SET NOT NULL; +ALTER TABLE "account" DROP COLUMN "valuation_method"; +ALTER TABLE "account" DROP COLUMN "valuation_type"; +DROP TYPE "ValuationMethod"; +DROP TYPE "ValuationType"; + +-- Recreate subcategory +ALTER TABLE "account" ADD COLUMN "subcategory_new" TEXT NOT NULL GENERATED ALWAYS AS ( + COALESCE(subcategory_user, subcategory_provider, 'other') +) STORED; +ALTER TABLE "account" RENAME COLUMN "subcategory" TO "subcategory_old"; +ALTER TABLE "account" RENAME COLUMN "subcategory_new" TO "subcategory"; +ALTER TABLE "account" DROP COLUMN "subcategory_old"; + +-- Restore type +ALTER TABLE "account" RENAME COLUMN "type" TO "type_old"; +ALTER TABLE "account" RENAME COLUMN "type_new" TO "type"; +ALTER TABLE "account" DROP COLUMN "type_old"; +ALTER TYPE "AccountType" RENAME TO "AccountType_old"; +ALTER TYPE "AccountType_new" RENAME TO "AccountType"; +DROP TYPE "AccountType_old"; + +-- Make our classification column auto-generated +ALTER TABLE "account" +ADD COLUMN "classification_new" "AccountClassification" GENERATED ALWAYS AS ( + CASE + WHEN "type" IN ('INVESTMENT', 'DEPOSITORY', 'PROPERTY', 'VEHICLE', 'OTHER_ASSET') THEN 'asset'::"AccountClassification" + WHEN "type" IN ('CREDIT', 'LOAN', 'OTHER_LIABILITY') THEN 'liability'::"AccountClassification" + END +) STORED; + +ALTER TABLE "account" RENAME COLUMN "classification" TO "classification_old"; +ALTER TABLE "account" RENAME COLUMN "classification_new" TO "classification"; +ALTER TABLE "account" ALTER COLUMN "classification" SET NOT NULL; +ALTER TABLE "account" DROP COLUMN "classification_old"; + +COMMIT; \ No newline at end of file diff --git a/prisma/migrations/20220715191415_add_liability_fields/migration.sql b/prisma/migrations/20220715191415_add_liability_fields/migration.sql new file mode 100644 index 00000000000..bacbed722f5 --- /dev/null +++ b/prisma/migrations/20220715191415_add_liability_fields/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "account" ADD COLUMN "credit_provider" JSONB, +ADD COLUMN "credit_user" JSONB, +ADD COLUMN "loan_provider" JSONB, +ADD COLUMN "loan_user" JSONB; diff --git a/prisma/migrations/20220719200317_plaid_txn_category/migration.sql b/prisma/migrations/20220719200317_plaid_txn_category/migration.sql new file mode 100644 index 00000000000..13c313875f4 --- /dev/null +++ b/prisma/migrations/20220719200317_plaid_txn_category/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "transaction" ADD COLUMN "plaid_category_id" TEXT, +ADD COLUMN "plaid_personal_finance_category" JSONB; diff --git a/prisma/migrations/20220720191551_generated_loan_credit_fields/migration.sql b/prisma/migrations/20220720191551_generated_loan_credit_fields/migration.sql new file mode 100644 index 00000000000..125804f4f10 --- /dev/null +++ b/prisma/migrations/20220720191551_generated_loan_credit_fields/migration.sql @@ -0,0 +1,15 @@ +ALTER TABLE "account" +ADD COLUMN "credit" JSONB +GENERATED ALWAYS AS ( + COALESCE(credit_provider, '{}'::JSONB) || COALESCE(credit_user, '{}'::JSONB) +) STORED; + +ALTER TABLE "account" +ADD COLUMN "loan" JSONB +GENERATED ALWAYS AS ( + COALESCE(loan_provider, '{}'::JSONB) || COALESCE(loan_user, '{}'::JSONB) +) STORED; + +ALTER TABLE "account" +ALTER COLUMN "credit" SET NOT NULL, +ALTER COLUMN "loan" SET NOT NULL; \ No newline at end of file diff --git a/prisma/migrations/20220725143246_map_credit_loan_data/migration.sql b/prisma/migrations/20220725143246_map_credit_loan_data/migration.sql new file mode 100644 index 00000000000..31e2f8ec531 --- /dev/null +++ b/prisma/migrations/20220725143246_map_credit_loan_data/migration.sql @@ -0,0 +1,56 @@ +-- update plaid mortgage accounts +UPDATE "account" +SET + "loan_provider" = json_build_object( + 'originationDate', plaid_liability->'mortgage'->>'origination_date', + 'originationPrincipal', plaid_liability->'mortgage'->'origination_principal_amount', + 'maturityDate', plaid_liability->'mortgage'->>'maturity_date', + 'interestRate', ( + CASE plaid_liability->'mortgage'->'interest_rate'->>'type' + WHEN 'fixed' THEN json_build_object( + 'type', 'fixed', + 'rate', plaid_liability->'mortgage'->'interest_rate'->'percentage' + ) + ELSE json_build_object( + 'type', 'variable' + ) + END + ), + 'loanDetail', json_build_object( + 'type', 'mortgage' + ) + ) +WHERE + "loan_provider" IS NULL AND "plaid_liability"->>'mortgage' IS NOT NULL; + +-- update plaid student loan accounts +UPDATE "account" +SET + "loan_provider" = json_build_object( + 'originationDate', plaid_liability->'student'->>'origination_date', + 'originationPrincipal', plaid_liability->'student'->'origination_principal_amount', + 'maturityDate', plaid_liability->'student'->>'maturity_date', + 'interestRate', json_build_object( + 'type', 'fixed', + 'rate', plaid_liability->'student'->'interest_rate_percentage' + ), + 'loanDetail', json_build_object( + 'type', 'student' + ) + ) +WHERE + "loan_provider" IS NULL AND "plaid_liability"->>'student' IS NOT NULL; + +-- update plaid credit accounts +UPDATE "account" +SET + "credit_provider" = json_build_object( + 'isOverdue', plaid_liability->'credit'->>'is_overdue', + 'lastPaymentAmount', plaid_liability->'credit'->'last_payment_amount', + 'lastPaymentDate', plaid_liability->'credit'->>'last_payment_date', + 'lastStatementAmount', plaid_liability->'credit'->'last_statement_balance', + 'lastStatementDate', plaid_liability->'credit'->>'last_statement_issue_date', + 'minimumPayment', plaid_liability->'credit'->'minimum_payment_amount' + ) +WHERE + "credit_provider" IS NULL AND "plaid_liability"->>'credit' IS NOT NULL; diff --git a/prisma/migrations/20220726003918_reset_loan_account_balances/migration.sql b/prisma/migrations/20220726003918_reset_loan_account_balances/migration.sql new file mode 100644 index 00000000000..74711ae5262 --- /dev/null +++ b/prisma/migrations/20220726003918_reset_loan_account_balances/migration.sql @@ -0,0 +1,23 @@ +-- remove stale account balance records now that new calculation is in place +DELETE FROM account_balance +WHERE account_id IN (SELECT id FROM account WHERE type = 'LOAN' AND (loan_provider IS NOT NULL OR loan_user IS NOT NULL)); + +-- remove stale valuations for accounts that have proper loan data +DELETE FROM valuation +WHERE account_id IN (SELECT id FROM account WHERE type = 'LOAN' AND (loan_provider IS NOT NULL OR loan_user IS NOT NULL)); + +-- update function to use loan origination date +CREATE OR REPLACE FUNCTION public.account_value_start_date(p_account_id integer) RETURNS date LANGUAGE sql STABLE AS $$ + SELECT + LEAST( + (a.loan->>'originationDate')::date, + (SELECT MIN(date) FROM "transaction" where "account_id" = a.id), + (SELECT MIN(date) FROM "valuation" where "account_id" = a.id), + (SELECT MIN(date) FROM "investment_transaction" where "account_id" = a.id), + now() + ) + FROM + account a + WHERE + a.id = p_account_id +$$; \ No newline at end of file diff --git a/prisma/migrations/20220727145316_loan_credit_json_nullable/migration.sql b/prisma/migrations/20220727145316_loan_credit_json_nullable/migration.sql new file mode 100644 index 00000000000..d61312bd321 --- /dev/null +++ b/prisma/migrations/20220727145316_loan_credit_json_nullable/migration.sql @@ -0,0 +1,14 @@ +-- AlterTable +ALTER TABLE "account" DROP COLUMN "credit", DROP COLUMN "loan"; + +ALTER TABLE "account" +ADD COLUMN "credit" JSONB +GENERATED ALWAYS AS ( + CASE WHEN num_nonnulls(credit_provider, credit_user) = 0 THEN NULL ELSE COALESCE(credit_provider, '{}'::JSONB) || COALESCE(credit_user, '{}'::JSONB) END +) STORED; + +ALTER TABLE "account" +ADD COLUMN "loan" JSONB +GENERATED ALWAYS AS ( + CASE WHEN num_nonnulls(loan_provider, loan_user) = 0 THEN NULL ELSE COALESCE(loan_provider, '{}'::JSONB) || COALESCE(loan_user, '{}'::JSONB) END +) STORED; diff --git a/prisma/migrations/20220727202956_loan_account_start_date/migration.sql b/prisma/migrations/20220727202956_loan_account_start_date/migration.sql new file mode 100644 index 00000000000..fa1c6d0418a --- /dev/null +++ b/prisma/migrations/20220727202956_loan_account_start_date/migration.sql @@ -0,0 +1,5 @@ +UPDATE account +SET + start_date = COALESCE((loan->>'originationDate')::date, start_date) +WHERE + type = 'LOAN' AND loan IS NOT NULL; diff --git a/prisma/migrations/20220729012630_security_fields/migration.sql b/prisma/migrations/20220729012630_security_fields/migration.sql new file mode 100644 index 00000000000..730b6fa9776 --- /dev/null +++ b/prisma/migrations/20220729012630_security_fields/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "security" ADD COLUMN "finicity_asset_class" TEXT, +ADD COLUMN "finicity_fi_asset_class" TEXT, +ADD COLUMN "finicity_type" TEXT, +ADD COLUMN "plaid_is_cash_equivalent" BOOLEAN; diff --git a/prisma/migrations/20220729202323_txn_updates/migration.sql b/prisma/migrations/20220729202323_txn_updates/migration.sql new file mode 100644 index 00000000000..692b261b5bd --- /dev/null +++ b/prisma/migrations/20220729202323_txn_updates/migration.sql @@ -0,0 +1,15 @@ +-- rename type -> flow +ALTER TYPE "TransactionType" RENAME TO "TransactionFlow"; + +ALTER TABLE "transaction" DROP COLUMN "type"; +ALTER TABLE "transaction" ADD COLUMN "flow" "TransactionFlow" NOT NULL GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionFlow" ELSE 'OUTFLOW'::"TransactionFlow" END) STORED; + +ALTER TABLE "investment_transaction" DROP COLUMN "type"; +ALTER TABLE "investment_transaction" ADD COLUMN "flow" "TransactionFlow" NOT NULL GENERATED ALWAYS AS (CASE WHEN amount < 0 THEN 'INFLOW'::"TransactionFlow" ELSE 'OUTFLOW'::"TransactionFlow" END) STORED; + +-- create type enum (this is only used in queries for the time being) +CREATE TYPE "TransactionType" AS ENUM ('INCOME', 'EXPENSE', 'PAYMENT', 'TRANSFER'); + +-- add defaults for currency_code +ALTER TABLE "account" ALTER COLUMN "currency_code" SET DEFAULT E'USD'; +ALTER TABLE "transaction" ALTER COLUMN "currency_code" SET DEFAULT E'USD'; diff --git a/prisma/migrations/20220804180126_holdings_view/migration.sql b/prisma/migrations/20220804180126_holdings_view/migration.sql new file mode 100644 index 00000000000..81d9cde5f99 --- /dev/null +++ b/prisma/migrations/20220804180126_holdings_view/migration.sql @@ -0,0 +1,63 @@ +-- update cost basis columns +ALTER TABLE "holding" RENAME COLUMN "cost_basis" TO "cost_basis_provider"; +ALTER TABLE "holding" ADD COLUMN "cost_basis_user" DECIMAL(23,8); +ALTER TABLE "holding" ADD COLUMN "cost_basis" DECIMAL(23,8) GENERATED ALWAYS AS ( + COALESCE(cost_basis_user, cost_basis_provider) +) STORED; + +-- create holdings view +CREATE OR REPLACE VIEW holdings_enriched AS ( + SELECT + h.id, + h.account_id, + h.security_id, + h.quantity, + COALESCE(pricing_latest.price_close * h.quantity * COALESCE(s.shares_per_contract, 1), h.value) AS "value", + COALESCE(h.cost_basis, tcb.cost_basis * h.quantity) AS "cost_basis", + COALESCE(h.cost_basis / h.quantity / COALESCE(s.shares_per_contract, 1), tcb.cost_basis) AS "cost_basis_per_share", + pricing_latest.price_close AS "price", + pricing_prev.price_close AS "price_prev" + FROM + holding h + INNER JOIN security s ON s.id = h.security_id + -- latest security pricing + LEFT JOIN LATERAL ( + SELECT + price_close + FROM + security_pricing + WHERE + security_id = h.security_id + ORDER BY + date DESC + LIMIT 1 + ) pricing_latest ON true + -- previous security pricing (for computing daily ∆) + LEFT JOIN LATERAL ( + SELECT + price_close + FROM + security_pricing + WHERE + security_id = h.security_id + ORDER BY + date DESC + LIMIT 1 + OFFSET 1 + ) pricing_prev ON true + -- calculate cost basis from transactions + LEFT JOIN ( + SELECT + it.account_id, + it.security_id, + SUM(it.quantity * it.price) / SUM(it.quantity) AS cost_basis + FROM + investment_transaction it + WHERE + (it.plaid_type = 'buy' OR it.finicity_investment_transaction_type = 'purchased') + AND it.quantity > 0 + GROUP BY + it.account_id, + it.security_id + ) tcb ON tcb.account_id = h.account_id AND tcb.security_id = s.id +); \ No newline at end of file diff --git a/prisma/migrations/20220804191558_add_excluded_to_holding/migration.sql b/prisma/migrations/20220804191558_add_excluded_to_holding/migration.sql new file mode 100644 index 00000000000..f5514a38895 --- /dev/null +++ b/prisma/migrations/20220804191558_add_excluded_to_holding/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "holding" ADD COLUMN "excluded" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20220808171116_investment_txn_fees/migration.sql b/prisma/migrations/20220808171116_investment_txn_fees/migration.sql new file mode 100644 index 00000000000..f81a7590fa8 --- /dev/null +++ b/prisma/migrations/20220808171116_investment_txn_fees/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "investment_transaction" ADD COLUMN "fees" DECIMAL(19,4); diff --git a/prisma/migrations/20220808174032_update_holdings_view/migration.sql b/prisma/migrations/20220808174032_update_holdings_view/migration.sql new file mode 100644 index 00000000000..8c8de017d8d --- /dev/null +++ b/prisma/migrations/20220808174032_update_holdings_view/migration.sql @@ -0,0 +1,57 @@ +-- create holdings view +CREATE OR REPLACE VIEW holdings_enriched AS ( + SELECT + h.id, + h.account_id, + h.security_id, + h.quantity, + COALESCE(pricing_latest.price_close * h.quantity * COALESCE(s.shares_per_contract, 1), h.value) AS "value", + COALESCE(h.cost_basis, tcb.cost_basis * h.quantity) AS "cost_basis", + COALESCE(h.cost_basis / h.quantity / COALESCE(s.shares_per_contract, 1), tcb.cost_basis) AS "cost_basis_per_share", + pricing_latest.price_close AS "price", + pricing_prev.price_close AS "price_prev", + h.excluded + FROM + holding h + INNER JOIN security s ON s.id = h.security_id + -- latest security pricing + LEFT JOIN LATERAL ( + SELECT + price_close + FROM + security_pricing + WHERE + security_id = h.security_id + ORDER BY + date DESC + LIMIT 1 + ) pricing_latest ON true + -- previous security pricing (for computing daily ∆) + LEFT JOIN LATERAL ( + SELECT + price_close + FROM + security_pricing + WHERE + security_id = h.security_id + ORDER BY + date DESC + LIMIT 1 + OFFSET 1 + ) pricing_prev ON true + -- calculate cost basis from transactions + LEFT JOIN ( + SELECT + it.account_id, + it.security_id, + SUM(it.quantity * it.price) / SUM(it.quantity) AS cost_basis + FROM + investment_transaction it + WHERE + (it.plaid_type = 'buy' OR it.finicity_investment_transaction_type = 'purchased') + AND it.quantity > 0 + GROUP BY + it.account_id, + it.security_id + ) tcb ON tcb.account_id = h.account_id AND tcb.security_id = s.id +); \ No newline at end of file diff --git a/prisma/migrations/20220810190306_transaction_category_update/migration.sql b/prisma/migrations/20220810190306_transaction_category_update/migration.sql new file mode 100644 index 00000000000..030ba68ef0a --- /dev/null +++ b/prisma/migrations/20220810190306_transaction_category_update/migration.sql @@ -0,0 +1,44 @@ +BEGIN; + +DROP INDEX "transaction_category_idx"; + +ALTER TABLE "transaction" +DROP COLUMN "category", +DROP COLUMN "category_provider", +DROP COLUMN "category_user", +ADD COLUMN "category_user" TEXT, +ADD COLUMN "category" TEXT NOT NULL GENERATED ALWAYS AS ( + COALESCE( + category_user, + CASE + WHEN plaid_personal_finance_category->>'primary' = 'INCOME' THEN 'Income' + WHEN plaid_personal_finance_category->>'detailed' IN ('LOAN_PAYMENTS_MORTGAGE_PAYMENT', 'RENT_AND_UTILITIES_RENT') THEN 'Housing Payments' + WHEN plaid_personal_finance_category->>'detailed' = 'LOAN_PAYMENTS_CAR_PAYMENT' THEN 'Vehicle Payments' + WHEN plaid_personal_finance_category->>'primary' = 'LOAN_PAYMENTS' THEN 'Other Payments' + WHEN plaid_personal_finance_category->>'primary' = 'HOME_IMPROVEMENT' THEN 'Home Improvement' + WHEN plaid_personal_finance_category->>'primary' = 'GENERAL_MERCHANDISE' THEN 'Shopping' + WHEN + (plaid_personal_finance_category->>'primary' = 'RENT_AND_UTILITIES' AND + plaid_personal_finance_category->>'detailed' <> 'RENT_AND_UTILITIES_RENT') THEN 'Utilities' + WHEN plaid_personal_finance_category->>'primary' IN ('FOOD_AND_DRINK') THEN 'Food and Drink' + WHEN plaid_personal_finance_category->>'primary' = 'TRANSPORTATION' THEN 'Transportation' + WHEN plaid_personal_finance_category->>'primary' IN ('TRAVEL') THEN 'Travel' + WHEN (plaid_personal_finance_category->>'primary' IN ('PERSONAL_CARE', 'MEDICAL') AND + plaid_personal_finance_category->>'detailed' <> 'MEDICAL_VETERINARY_SERVICES') THEN 'Health' + WHEN finicity_categorization->>'category' IN ('Income', 'Paycheck') THEN 'Income' + WHEN finicity_categorization->>'category' = 'Mortgage & Rent' THEN 'Housing Payments' + WHEN finicity_categorization->>'category' IN ('Furnishings', 'Home Services', 'Home Improvement', 'Lawn and Garden') THEN 'Home Improvement' + WHEN finicity_categorization->>'category' IN ('Streaming Services', 'Home Phone', 'Television', 'Bills & Utilities', 'Utilities', 'Internet / Broadband Charges', 'Mobile Phone') THEN 'Utilities' + WHEN finicity_categorization->>'category' IN ('Fast Food', 'Food & Dining', 'Restaurants', 'Coffee Shops', 'Alcohol & Bars', 'Groceries') THEN 'Food and Drink' + WHEN finicity_categorization->>'category' IN ('Auto & Transport', 'Gas & Fuel', 'Auto Insurance') THEN 'Transportation' + WHEN finicity_categorization->>'category' IN ('Hotel', 'Travel', 'Rental Car & Taxi') THEN 'Travel' + WHEN finicity_categorization->>'category' IN ('Health Insurance', 'Doctor', 'Pharmacy', 'Eyecare', 'Health & Fitness', 'Personal Care') THEN 'Health' + ELSE 'Other' + END + ) +) STORED; + +-- DropEnum +DROP TYPE "TransactionCategory"; + +COMMIT; diff --git a/prisma/migrations/20220817180833_dietz/migration.sql b/prisma/migrations/20220817180833_dietz/migration.sql new file mode 100644 index 00000000000..d345ae4eb62 --- /dev/null +++ b/prisma/migrations/20220817180833_dietz/migration.sql @@ -0,0 +1,63 @@ +CREATE OR REPLACE FUNCTION calculate_return_dietz(p_account_id account.id%type, p_start date, p_end date, out percentage numeric, out amount numeric) AS $$ + DECLARE + v_start date := GREATEST(p_start, (SELECT MIN(date) FROM account_balance WHERE account_id = p_account_id)); + v_end date := p_end; + v_days int := v_end - v_start; + BEGIN + SELECT + ROUND((b1.balance - b0.balance - flows.net) / (b0.balance + flows.weighted), 4) AS "percentage", + b1.balance - b0.balance - flows.net AS "amount" + INTO + percentage, amount + FROM + account a + LEFT JOIN LATERAL ( + SELECT + COALESCE(SUM(-fw.flow), 0) AS "net", + COALESCE(SUM(-fw.flow * fw.weight), 0) AS "weighted" + FROM ( + SELECT + SUM(it.amount) AS flow, + (v_days - (it.date - v_start))::numeric / v_days AS weight + FROM + investment_transaction it + WHERE + it.account_id = a.id + AND it.date BETWEEN v_start AND v_end + -- filter for investment_transactions that represent external flows + AND ( + (it.plaid_type = 'cash' AND it.plaid_subtype IN ('contribution', 'deposit', 'withdrawal')) + OR (it.plaid_type = 'transfer' AND it.plaid_subtype IN ('transfer', 'send', 'request')) + OR (it.plaid_type = 'buy' AND it.plaid_subtype IN ('contribution')) + OR (it.finicity_transaction_id IS NOT NULL AND it.finicity_investment_transaction_type IN ('contribution', 'deposit', 'transfer')) + ) + GROUP BY + it.date + ) fw + ) flows ON TRUE + LEFT JOIN LATERAL ( + SELECT + ab.balance AS "balance" + FROM + account_balance ab + WHERE + ab.account_id = a.id AND ab.date <= v_start + ORDER BY + ab.date DESC + LIMIT 1 + ) b0 ON TRUE + LEFT JOIN LATERAL ( + SELECT + COALESCE(ab.balance, a.current_balance) AS "balance" + FROM + account_balance ab + WHERE + ab.account_id = a.id AND ab.date <= v_end + ORDER BY + ab.date DESC + LIMIT 1 + ) b1 ON TRUE + WHERE + a.id = p_account_id; + END; +$$ LANGUAGE plpgsql STABLE; diff --git a/prisma/migrations/20220819151658_add_investment_transaction_category/migration.sql b/prisma/migrations/20220819151658_add_investment_transaction_category/migration.sql new file mode 100644 index 00000000000..dcea24784e4 --- /dev/null +++ b/prisma/migrations/20220819151658_add_investment_transaction_category/migration.sql @@ -0,0 +1,25 @@ +-- CreateEnum +CREATE TYPE "InvestmentTransactionCategory" AS ENUM ('buy', 'sell', 'dividend', 'transfer', 'tax', 'fee', 'cancel', 'other'); + +-- AlterTable +ALTER TABLE "investment_transaction" ADD COLUMN "category" "InvestmentTransactionCategory" NOT NULL GENERATED ALWAYS AS ( + CASE + WHEN "plaid_type" = 'buy' THEN 'buy'::"InvestmentTransactionCategory" + WHEN "plaid_type" = 'sell' THEN 'sell'::"InvestmentTransactionCategory" + WHEN "plaid_subtype" IN ('dividend', 'qualified dividend', 'non-qualified dividend') THEN 'dividend'::"InvestmentTransactionCategory" + WHEN "plaid_subtype" IN ('non-resident tax', 'tax', 'tax withheld') THEN 'tax'::"InvestmentTransactionCategory" + WHEN "plaid_type" = 'fee' OR "plaid_subtype" IN ('account fee', 'legal fee', 'management fee', 'margin expense', 'transfer fee', 'trust fee') THEN 'fee'::"InvestmentTransactionCategory" + WHEN "plaid_type" = 'cash' THEN 'transfer'::"InvestmentTransactionCategory" + WHEN "plaid_type" = 'cancel' THEN 'cancel'::"InvestmentTransactionCategory" + + WHEN "finicity_investment_transaction_type" IN ('purchased', 'purchaseToClose', 'purchaseToCover', 'dividendReinvest', 'reinvestOfIncome') THEN 'buy'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" IN ('sold', 'soldToClose', 'soldToOpen') THEN 'sell'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" = 'dividend' THEN 'dividend'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" = 'tax' THEN 'tax'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" = 'fee' THEN 'fee'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" IN ('transfer', 'contribution', 'deposit', 'income', 'interest') THEN 'transfer'::"InvestmentTransactionCategory" + WHEN "finicity_investment_transaction_type" = 'cancel' THEN 'cancel'::"InvestmentTransactionCategory" + + ELSE 'other'::"InvestmentTransactionCategory" + END +) STORED; diff --git a/prisma/migrations/20220915200544_add_plans/migration.sql b/prisma/migrations/20220915200544_add_plans/migration.sql new file mode 100644 index 00000000000..df79cf7831a --- /dev/null +++ b/prisma/migrations/20220915200544_add_plans/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "plan" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_id" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "date_of_birth" DATE NOT NULL, + "life_expectancy" INTEGER NOT NULL DEFAULT 85, + "events" JSONB NOT NULL DEFAULT '[]', + "milestones" JSONB NOT NULL DEFAULT '[]', + + CONSTRAINT "plan_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "plan" ADD CONSTRAINT "plan_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20220919203059_make_dob_optional/migration.sql b/prisma/migrations/20220919203059_make_dob_optional/migration.sql new file mode 100644 index 00000000000..1ef9ede3442 --- /dev/null +++ b/prisma/migrations/20220919203059_make_dob_optional/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "plan" ALTER COLUMN "date_of_birth" DROP NOT NULL; diff --git a/prisma/migrations/20220929161359_remove_crisp_session_token/migration.sql b/prisma/migrations/20220929161359_remove_crisp_session_token/migration.sql new file mode 100644 index 00000000000..dd0b5cd27bb --- /dev/null +++ b/prisma/migrations/20220929161359_remove_crisp_session_token/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - You are about to drop the column `crisp_session_token` on the `user` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "user" DROP COLUMN "crisp_session_token"; \ No newline at end of file diff --git a/prisma/migrations/20221004193621_security_brokerage_cash_flag/migration.sql b/prisma/migrations/20221004193621_security_brokerage_cash_flag/migration.sql new file mode 100644 index 00000000000..e17e2f03423 --- /dev/null +++ b/prisma/migrations/20221004193621_security_brokerage_cash_flag/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "security" ADD COLUMN "is_brokerage_cash" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/20221007143103_dietz_div0_fix/migration.sql b/prisma/migrations/20221007143103_dietz_div0_fix/migration.sql new file mode 100644 index 00000000000..8c0ba53fabf --- /dev/null +++ b/prisma/migrations/20221007143103_dietz_div0_fix/migration.sql @@ -0,0 +1,64 @@ +CREATE OR REPLACE FUNCTION calculate_return_dietz(p_account_id account.id%type, p_start date, p_end date, out percentage numeric, out amount numeric) AS $$ + DECLARE + v_start date := GREATEST(p_start, (SELECT MIN(date) FROM account_balance WHERE account_id = p_account_id)); + v_end date := p_end; + v_days int := v_end - v_start; + BEGIN + SELECT + ROUND((b1.balance - b0.balance - flows.net) / NULLIF(b0.balance + flows.weighted, 0), 4) AS "percentage", + b1.balance - b0.balance - flows.net AS "amount" + INTO + percentage, amount + FROM + account a + LEFT JOIN LATERAL ( + SELECT + COALESCE(SUM(-fw.flow), 0) AS "net", + COALESCE(SUM(-fw.flow * fw.weight), 0) AS "weighted" + FROM ( + SELECT + SUM(it.amount) AS flow, + (v_days - (it.date - v_start))::numeric / v_days AS weight + FROM + investment_transaction it + WHERE + it.account_id = a.id + AND it.date BETWEEN v_start AND v_end + -- filter for investment_transactions that represent external flows + AND ( + (it.plaid_type = 'cash' AND it.plaid_subtype IN ('contribution', 'deposit', 'withdrawal')) + OR (it.plaid_type = 'transfer' AND it.plaid_subtype IN ('transfer', 'send', 'request')) + OR (it.plaid_type = 'buy' AND it.plaid_subtype IN ('contribution')) + OR (it.finicity_transaction_id IS NOT NULL AND it.finicity_investment_transaction_type IN ('contribution', 'deposit', 'transfer')) + ) + GROUP BY + it.date + ) fw + ) flows ON TRUE + LEFT JOIN LATERAL ( + SELECT + ab.balance AS "balance" + FROM + account_balance ab + WHERE + ab.account_id = a.id AND ab.date <= v_start + ORDER BY + ab.date DESC + LIMIT 1 + ) b0 ON TRUE + LEFT JOIN LATERAL ( + SELECT + COALESCE(ab.balance, a.current_balance) AS "balance" + FROM + account_balance ab + WHERE + ab.account_id = a.id AND ab.date <= v_end + ORDER BY + ab.date DESC + LIMIT 1 + ) b1 ON TRUE + WHERE + a.id = p_account_id; + END; +$$ LANGUAGE plpgsql STABLE; + diff --git a/prisma/migrations/20221017145454_plan_events_milestones/migration.sql b/prisma/migrations/20221017145454_plan_events_milestones/migration.sql new file mode 100644 index 00000000000..4de682671ca --- /dev/null +++ b/prisma/migrations/20221017145454_plan_events_milestones/migration.sql @@ -0,0 +1,102 @@ +/* + Warnings: + + - You are about to drop the column `events` on the `plan` table. All the data in the column will be lost. + - You are about to drop the column `milestones` on the `plan` table. All the data in the column will be lost. + +*/ +-- CreateEnum +CREATE TYPE "PlanEventFrequency" AS ENUM ('monthly', 'yearly'); + +-- CreateEnum +CREATE TYPE "PlanMilestoneType" AS ENUM ('year', 'net_worth'); + +-- CreateTable +CREATE TABLE "plan_event" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "plan_id" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "start_year" INTEGER CHECK ("start_year" > 0), + "start_milestone_id" INTEGER, + "end_year" INTEGER CHECK ("end_year" > 0), + "end_milestone_id" INTEGER, + "frequency" "PlanEventFrequency" NOT NULL DEFAULT 'yearly', + "initial_value" DECIMAL(19,4), + "initial_value_ref" TEXT, + "rate" DECIMAL(6,4) NOT NULL DEFAULT 0, + + CONSTRAINT "plan_event_pkey" PRIMARY KEY ("id"), + + CONSTRAINT "start_check" CHECK (num_nonnulls("start_year", "start_milestone_id") <= 1), + CONSTRAINT "end_check" CHECK (num_nonnulls("end_year", "end_milestone_id") <= 1), + CONSTRAINT "initial_value_check" CHECK (num_nonnulls("initial_value", "initial_value_ref") = 1) +); + +-- CreateTable +CREATE TABLE "plan_milestone" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "plan_id" INTEGER NOT NULL, + "name" TEXT NOT NULL, + "type" "PlanMilestoneType" NOT NULL, + "year" INTEGER CHECK ("year" > 0), + "expense_multiple" DOUBLE PRECISION CHECK ("expense_multiple" >= 0), + "expense_years" INTEGER CHECK ("expense_years" >= 0), + + CONSTRAINT "plan_milestone_pkey" PRIMARY KEY ("id"), + + -- constraints for validating discriminated union + CONSTRAINT "type_year_check" CHECK ("type" <> 'year' OR ("year" IS NOT NULL)), + CONSTRAINT "type_net_worth_check" CHECK ("type" <> 'net_worth' OR ("expense_multiple" IS NOT NULL AND "expense_years" IS NOT NULL)) +); + +-- AddForeignKey +ALTER TABLE "plan_event" ADD CONSTRAINT "plan_event_plan_id_fkey" FOREIGN KEY ("plan_id") REFERENCES "plan"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "plan_event" ADD CONSTRAINT "plan_event_start_milestone_id_fkey" FOREIGN KEY ("start_milestone_id") REFERENCES "plan_milestone"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "plan_event" ADD CONSTRAINT "plan_event_end_milestone_id_fkey" FOREIGN KEY ("end_milestone_id") REFERENCES "plan_milestone"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "plan_milestone" ADD CONSTRAINT "plan_milestone_plan_id_fkey" FOREIGN KEY ("plan_id") REFERENCES "plan"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- migrate milestones +INSERT INTO plan_milestone (plan_id, name, type, year, expense_multiple, expense_years) +SELECT + p.id, + m.name, + m.type::"PlanMilestoneType", + m.year, + m.expense_multiple, + m.expense_years +FROM + plan p, + jsonb_to_recordset(p.milestones) AS m("name" text, "type" text, "year" int, "expense_multiple" float, "expense_years" int); + +-- migrate plans +INSERT INTO plan_event (plan_id, name, start_year, end_year, frequency, initial_value, initial_value_ref, rate) +SELECT + p.id, + e.name, + e.start, + e.end, + e.frequency::"PlanEventFrequency", + CASE WHEN v.initial_value IN ('income', 'expenses') THEN NULL ELSE v.initial_value::decimal END AS "initial_value", + CASE WHEN v.initial_value IN ('income', 'expenses') THEN v.initial_value ELSE NULL END AS "initial_value_ref", + v.rate +FROM + plan p, + jsonb_to_recordset(p.events) AS e("name" text, "start" int, "end" int, "frequency" text, "value" jsonb) + LEFT JOIN LATERAL ( + SELECT + COALESCE(e.value->>'initialValue', e.value->>'value') AS "initial_value", + COALESCE((e.value->>'rate')::decimal, 0) AS "rate" + ) v ON true; + +-- drop plan/milestone json columns +ALTER TABLE "plan" DROP COLUMN "events", DROP COLUMN "milestones"; \ No newline at end of file diff --git a/prisma/migrations/20221021162836_remove_dob_from_plan/migration.sql b/prisma/migrations/20221021162836_remove_dob_from_plan/migration.sql new file mode 100644 index 00000000000..198f06767cd --- /dev/null +++ b/prisma/migrations/20221021162836_remove_dob_from_plan/migration.sql @@ -0,0 +1,9 @@ +/* + Warnings: + + - You are about to drop the column `date_of_birth` on the `plan` table. All the data in the column will be lost. + +*/ + +-- AlterTable +ALTER TABLE "plan" DROP COLUMN "date_of_birth"; \ No newline at end of file diff --git a/prisma/migrations/20221024203133_plan_event_milestone_category/migration.sql b/prisma/migrations/20221024203133_plan_event_milestone_category/migration.sql new file mode 100644 index 00000000000..1368edba111 --- /dev/null +++ b/prisma/migrations/20221024203133_plan_event_milestone_category/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "plan_event" ADD COLUMN "category" TEXT; + +-- AlterTable +ALTER TABLE "plan_milestone" ADD COLUMN "category" TEXT NOT NULL DEFAULT 'retirement'; diff --git a/prisma/migrations/20221027180912_cascade_plan_milestone_deletion/migration.sql b/prisma/migrations/20221027180912_cascade_plan_milestone_deletion/migration.sql new file mode 100644 index 00000000000..33d13c4f7d2 --- /dev/null +++ b/prisma/migrations/20221027180912_cascade_plan_milestone_deletion/migration.sql @@ -0,0 +1,11 @@ +-- DropForeignKey +ALTER TABLE "plan_event" DROP CONSTRAINT "plan_event_end_milestone_id_fkey"; + +-- DropForeignKey +ALTER TABLE "plan_event" DROP CONSTRAINT "plan_event_start_milestone_id_fkey"; + +-- AddForeignKey +ALTER TABLE "plan_event" ADD CONSTRAINT "plan_event_start_milestone_id_fkey" FOREIGN KEY ("start_milestone_id") REFERENCES "plan_milestone"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "plan_event" ADD CONSTRAINT "plan_event_end_milestone_id_fkey" FOREIGN KEY ("end_milestone_id") REFERENCES "plan_milestone"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20221109192536_add_stripe_fields/migration.sql b/prisma/migrations/20221109192536_add_stripe_fields/migration.sql new file mode 100644 index 00000000000..d3e9c5b5f67 --- /dev/null +++ b/prisma/migrations/20221109192536_add_stripe_fields/migration.sql @@ -0,0 +1,13 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "stripe_cancel_at" TIMESTAMPTZ(6), +ADD COLUMN "stripe_current_period_end" TIMESTAMPTZ(6), +ADD COLUMN "stripe_customer_id" TEXT, +ADD COLUMN "stripe_price_id" TEXT, +ADD COLUMN "stripe_subscription_id" TEXT, +ADD COLUMN "stripe_trial_end" TIMESTAMPTZ(6); + +-- CreateIndex +CREATE UNIQUE INDEX "user_stripe_customer_id_key" ON "user"("stripe_customer_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_stripe_subscription_id_key" ON "user"("stripe_subscription_id"); diff --git a/prisma/migrations/20221111192223_ata/migration.sql b/prisma/migrations/20221111192223_ata/migration.sql new file mode 100644 index 00000000000..26e2d1b004c --- /dev/null +++ b/prisma/migrations/20221111192223_ata/migration.sql @@ -0,0 +1,75 @@ +-- CreateEnum +CREATE TYPE "ConversationStatus" AS ENUM ('open', 'closed'); + +-- CreateEnum +CREATE TYPE "MessageType" AS ENUM ('text', 'audio', 'video'); + +-- CreateTable +CREATE TABLE "advisor" ( + "id" INTEGER NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_id" INTEGER NOT NULL, + "title" TEXT NOT NULL, + "bio" TEXT NOT NULL, + + CONSTRAINT "advisor_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "conversation" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "status" "ConversationStatus" NOT NULL DEFAULT 'open', + "title" TEXT NOT NULL, + "user_id" INTEGER, + + CONSTRAINT "conversation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "conversation_advisor" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "conversation_id" INTEGER NOT NULL, + "advisor_id" INTEGER NOT NULL, + + CONSTRAINT "conversation_advisor_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "message" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "conversation_id" INTEGER NOT NULL, + "user_id" INTEGER, + "type" "MessageType" NOT NULL, + "body" TEXT, + "media_url" TEXT, + + CONSTRAINT "message_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "advisor_user_id_key" ON "advisor"("user_id"); + +-- AddForeignKey +ALTER TABLE "advisor" ADD CONSTRAINT "advisor_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "conversation" ADD CONSTRAINT "conversation_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "conversation_advisor" ADD CONSTRAINT "conversation_advisor_conversation_id_fkey" FOREIGN KEY ("conversation_id") REFERENCES "conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "conversation_advisor" ADD CONSTRAINT "conversation_advisor_advisor_id_fkey" FOREIGN KEY ("advisor_id") REFERENCES "advisor"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "message" ADD CONSTRAINT "message_conversation_id_fkey" FOREIGN KEY ("conversation_id") REFERENCES "conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "message" ADD CONSTRAINT "message_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20221115201138_advisor_approval_status/migration.sql b/prisma/migrations/20221115201138_advisor_approval_status/migration.sql new file mode 100644 index 00000000000..bf7f9b07ff6 --- /dev/null +++ b/prisma/migrations/20221115201138_advisor_approval_status/migration.sql @@ -0,0 +1,5 @@ +-- CreateEnum +CREATE TYPE "ApprovalStatus" AS ENUM ('pending', 'approved', 'rejected'); + +-- AlterTable +ALTER TABLE "advisor" ADD COLUMN "approval_status" "ApprovalStatus" NOT NULL DEFAULT 'pending'; diff --git a/prisma/migrations/20221117150434_update_advisor_profile/migration.sql b/prisma/migrations/20221117150434_update_advisor_profile/migration.sql new file mode 100644 index 00000000000..b7a48d4d8be --- /dev/null +++ b/prisma/migrations/20221117150434_update_advisor_profile/migration.sql @@ -0,0 +1,17 @@ + +-- DropForeignKey +ALTER TABLE "advisor" DROP CONSTRAINT "advisor_user_id_fkey"; + +-- AlterTable +CREATE SEQUENCE "advisor_id_seq"; +ALTER TABLE "advisor" ADD COLUMN "avatar_src" TEXT NOT NULL, +ADD COLUMN "full_name" TEXT NOT NULL, +ALTER COLUMN "id" SET DEFAULT nextval('advisor_id_seq'); +ALTER SEQUENCE "advisor_id_seq" OWNED BY "advisor"."id"; + +-- AlterTable +ALTER TABLE "message" DROP COLUMN "media_url", +ADD COLUMN "media_src" TEXT; + +-- AddForeignKey +ALTER TABLE "advisor" ADD CONSTRAINT "advisor_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20221117213140_add_stripe_trial_reminder_sent/migration.sql b/prisma/migrations/20221117213140_add_stripe_trial_reminder_sent/migration.sql new file mode 100644 index 00000000000..129b78b388a --- /dev/null +++ b/prisma/migrations/20221117213140_add_stripe_trial_reminder_sent/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "stripe_trial_reminder_sent" TIMESTAMPTZ(6); \ No newline at end of file diff --git a/prisma/migrations/20221121214349_add_user_goals/migration.sql b/prisma/migrations/20221121214349_add_user_goals/migration.sql new file mode 100644 index 00000000000..9c7c887f524 --- /dev/null +++ b/prisma/migrations/20221121214349_add_user_goals/migration.sql @@ -0,0 +1,8 @@ +-- CreateEnum +CREATE TYPE "UserGoal" AS ENUM ('retire', 'debt', 'save', 'invest'); + +-- AlterTable +ALTER TABLE "user" ADD COLUMN "goals" "UserGoal"[], +ADD COLUMN "goals_description" TEXT, +ADD COLUMN "risk_tolerance" SMALLINT; + diff --git a/prisma/migrations/20221129201601_conversation_advisor_unique_key/migration.sql b/prisma/migrations/20221129201601_conversation_advisor_unique_key/migration.sql new file mode 100644 index 00000000000..2cba12064e4 --- /dev/null +++ b/prisma/migrations/20221129201601_conversation_advisor_unique_key/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - A unique constraint covering the columns `[conversation_id,advisor_id]` on the table `conversation_advisor` will be added. If there are existing duplicate values, this will fail. + +*/ +-- CreateIndex +CREATE UNIQUE INDEX "conversation_advisor_conversation_id_advisor_id_key" ON "conversation_advisor"("conversation_id", "advisor_id"); diff --git a/prisma/migrations/20221202213727_notification_preferences/migration.sql b/prisma/migrations/20221202213727_notification_preferences/migration.sql new file mode 100644 index 00000000000..e5ba72166f7 --- /dev/null +++ b/prisma/migrations/20221202213727_notification_preferences/migration.sql @@ -0,0 +1,8 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "ata_all" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "ata_closed" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "ata_expire" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "ata_review" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "ata_submitted" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "ata_update" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "convert_kit_id" INTEGER; diff --git a/prisma/migrations/20221206153642_conversation_user_required/migration.sql b/prisma/migrations/20221206153642_conversation_user_required/migration.sql new file mode 100644 index 00000000000..7a4508d8a5f --- /dev/null +++ b/prisma/migrations/20221206153642_conversation_user_required/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - Made the column `user_id` on table `conversation` required. This step will fail if there are existing NULL values in that column. + +*/ +-- DropForeignKey +ALTER TABLE "conversation" DROP CONSTRAINT "conversation_user_id_fkey"; + +-- AlterTable +ALTER TABLE "conversation" ALTER COLUMN "user_id" SET NOT NULL; + +-- AddForeignKey +ALTER TABLE "conversation" ADD CONSTRAINT "conversation_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20221207235557_expiry_email_sent/migration.sql b/prisma/migrations/20221207235557_expiry_email_sent/migration.sql new file mode 100644 index 00000000000..46253856825 --- /dev/null +++ b/prisma/migrations/20221207235557_expiry_email_sent/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "conversation" ADD COLUMN "expiry_email_sent" TIMESTAMPTZ(6); diff --git a/prisma/migrations/20221209041210_user_advisor_notes/migration.sql b/prisma/migrations/20221209041210_user_advisor_notes/migration.sql new file mode 100644 index 00000000000..b93a8ea3bf3 --- /dev/null +++ b/prisma/migrations/20221209041210_user_advisor_notes/migration.sql @@ -0,0 +1,4 @@ +-- AlterTable + +ALTER TABLE "user" ADD COLUMN "advisor_notes" TEXT; + diff --git a/prisma/migrations/20221212164355_update_risk_data_type/migration.sql b/prisma/migrations/20221212164355_update_risk_data_type/migration.sql new file mode 100644 index 00000000000..d252e16f284 --- /dev/null +++ b/prisma/migrations/20221212164355_update_risk_data_type/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ALTER COLUMN "risk_tolerance" SET DATA TYPE DOUBLE PRECISION; diff --git a/prisma/migrations/20221214145140_add_audit_table_and_trigger/migration.sql b/prisma/migrations/20221214145140_add_audit_table_and_trigger/migration.sql new file mode 100644 index 00000000000..a7207633dbb --- /dev/null +++ b/prisma/migrations/20221214145140_add_audit_table_and_trigger/migration.sql @@ -0,0 +1,43 @@ +-- CreateEnum +CREATE TYPE "AuditEventType" AS ENUM ('insert', 'update', 'delete'); + +-- CreateTable +CREATE TABLE "audit_event" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "type" "AuditEventType" NOT NULL, + "model_type" TEXT NOT NULL, + "model_id" INTEGER NOT NULL, + "data" JSONB NOT NULL, + "user_id" INTEGER, + + CONSTRAINT "audit_event_pkey" PRIMARY KEY ("id") +); + +CREATE OR REPLACE FUNCTION log_conversation_messages() + RETURNS TRIGGER + LANGUAGE PLPGSQL + AS +$$ +BEGIN + + -- Handles INSERT,UPDATE,DELETE events on changes to the message table + INSERT INTO audit_event("type","model_type","model_id","data","user_id") + VALUES( + LOWER(TG_OP)::"AuditEventType", + 'Message', + COALESCE(NEW."id",OLD."id"), + to_json(COALESCE(NEW,OLD)), + COALESCE(NEW."user_id",OLD."user_id") + ); + + RETURN COALESCE(NEW,OLD); +END; +$$; + +CREATE OR REPLACE TRIGGER message_audit_event + AFTER INSERT OR UPDATE OR DELETE + ON message + FOR EACH ROW + EXECUTE PROCEDURE log_conversation_messages(); + \ No newline at end of file diff --git a/prisma/migrations/20221222200240_add_onboarding_profile_fields/migration.sql b/prisma/migrations/20221222200240_add_onboarding_profile_fields/migration.sql new file mode 100644 index 00000000000..1fa64d2f464 --- /dev/null +++ b/prisma/migrations/20221222200240_add_onboarding_profile_fields/migration.sql @@ -0,0 +1,10 @@ +-- CreateEnum +CREATE TYPE "Household" AS ENUM ('single', 'singleWithDependents', 'dual', 'dualWithDependents', 'retired'); + +-- CreateEnum +CREATE TYPE "MaybeGoal" AS ENUM ('aggregate', 'advice', 'plan'); + +-- AlterTable +ALTER TABLE "user" ADD COLUMN "household" "Household", +ADD COLUMN "maybe_goals" "MaybeGoal"[], +ADD COLUMN "maybe_goals_description" TEXT; \ No newline at end of file diff --git a/prisma/migrations/20230105203751_add_maybe_and_title/migration.sql b/prisma/migrations/20230105203751_add_maybe_and_title/migration.sql new file mode 100644 index 00000000000..b1300d9c421 --- /dev/null +++ b/prisma/migrations/20230105203751_add_maybe_and_title/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "maybe" TEXT, +ADD COLUMN "title" TEXT; diff --git a/prisma/migrations/20230105210810_add_member_number/migration.sql b/prisma/migrations/20230105210810_add_member_number/migration.sql new file mode 100644 index 00000000000..f0b2e76ad83 --- /dev/null +++ b/prisma/migrations/20230105210810_add_member_number/migration.sql @@ -0,0 +1,21 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "member_number" INT; + +CREATE SEQUENCE user_member_number_seq; + +UPDATE "user" + SET "member_number" = "u"."number" + FROM ( + SELECT "id", nextval('user_member_number_seq') AS "number" + FROM "user" + ORDER BY "id" ASC + ) "u" + WHERE "user"."id" = "u"."id"; + +ALTER TABLE "user" ALTER COLUMN "member_number" SET DEFAULT nextval('user_member_number_seq'); +ALTER TABLE "user" ALTER COLUMN "member_number" SET NOT NULL; + +ALTER SEQUENCE user_member_number_seq OWNED BY "user"."member_number"; + +-- CreateIndex +CREATE UNIQUE INDEX "user_member_number_key" ON "user"("member_number"); diff --git a/prisma/migrations/20230105221446_user_audit/migration.sql b/prisma/migrations/20230105221446_user_audit/migration.sql new file mode 100644 index 00000000000..3f5be2862e4 --- /dev/null +++ b/prisma/migrations/20230105221446_user_audit/migration.sql @@ -0,0 +1,25 @@ +-- DropForeignKey +ALTER TABLE "advisor" DROP CONSTRAINT "advisor_user_id_fkey"; + +-- AddForeignKey +ALTER TABLE "advisor" ADD CONSTRAINT "advisor_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- audit user creation/deletion +CREATE OR REPLACE FUNCTION log_user() RETURNS TRIGGER AS $$ + BEGIN + INSERT INTO audit_event ("type", "model_type", "model_id", "data") + VALUES ( + LOWER(TG_OP)::"AuditEventType", + 'User', + COALESCE(NEW."id", OLD."id"), + to_json(COALESCE(NEW, OLD)) + ); + RETURN NULL; -- result is ignored since this is an AFTER trigger + END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE TRIGGER user_audit_event + AFTER INSERT OR DELETE + ON "user" + FOR EACH ROW + EXECUTE FUNCTION log_user(); diff --git a/prisma/migrations/20230106172727_add_user_residence/migration.sql b/prisma/migrations/20230106172727_add_user_residence/migration.sql new file mode 100644 index 00000000000..b5f45ef8f8b --- /dev/null +++ b/prisma/migrations/20230106172727_add_user_residence/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "residence" TEXT NULL; diff --git a/prisma/migrations/20230106221847_user_profile/migration.sql b/prisma/migrations/20230106221847_user_profile/migration.sql new file mode 100644 index 00000000000..3f3b3c8ca45 --- /dev/null +++ b/prisma/migrations/20230106221847_user_profile/migration.sql @@ -0,0 +1,10 @@ +CREATE EXTENSION IF NOT EXISTS citext; + +ALTER TABLE "user" +ADD COLUMN "agreements_revision" TEXT, +ADD COLUMN "dob" DATE, +ADD COLUMN "email" CITEXT, +ADD COLUMN "first_name" TEXT, +ADD COLUMN "last_name" TEXT, +ADD COLUMN "onboarded" TIMESTAMPTZ(6), +ADD COLUMN "picture" TEXT; diff --git a/prisma/migrations/20230110173017_add_user_member_id/migration.sql b/prisma/migrations/20230110173017_add_user_member_id/migration.sql new file mode 100644 index 00000000000..5e19711b3f3 --- /dev/null +++ b/prisma/migrations/20230110173017_add_user_member_id/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "member_id" TEXT NOT NULL DEFAULT gen_random_uuid(); + +-- CreateIndex +CREATE UNIQUE INDEX "user_member_id_key" ON "user"("member_id"); diff --git a/prisma/migrations/20230112163100_add_agreements_table/migration.sql b/prisma/migrations/20230112163100_add_agreements_table/migration.sql new file mode 100644 index 00000000000..9e9c1e6dfc3 --- /dev/null +++ b/prisma/migrations/20230112163100_add_agreements_table/migration.sql @@ -0,0 +1,39 @@ + +-- CreateEnum +CREATE TYPE "AgreementType" AS ENUM ('fee', 'form_adv', 'form_crs', 'privacy_policy'); + +-- AlterTable +ALTER TABLE "user" DROP COLUMN "agreements_revision"; + +-- CreateTable +CREATE TABLE "agreement" ( + "id" SERIAL NOT NULL, + "type" "AgreementType" NOT NULL, + "revision" DATE NOT NULL, + "src" TEXT NOT NULL, + "active" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "agreement_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "signed_agreement" ( + "signed_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_id" INTEGER NOT NULL, + "agreement_id" INTEGER NOT NULL, + "src" TEXT, + + CONSTRAINT "signed_agreement_pkey" PRIMARY KEY ("user_id","agreement_id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "agreement_src_key" ON "agreement"("src"); + +-- CreateIndex +CREATE UNIQUE INDEX "agreement_type_revision_key" ON "agreement"("type", "revision"); + +-- AddForeignKey +ALTER TABLE "signed_agreement" ADD CONSTRAINT "signed_agreement_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "signed_agreement" ADD CONSTRAINT "signed_agreement_agreement_id_fkey" FOREIGN KEY ("agreement_id") REFERENCES "agreement"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20230113230312_user_email_required/migration.sql b/prisma/migrations/20230113230312_user_email_required/migration.sql new file mode 100644 index 00000000000..168c2c7881e --- /dev/null +++ b/prisma/migrations/20230113230312_user_email_required/migration.sql @@ -0,0 +1,3 @@ +-- delete users who no longer exist in Auth0 (determined by lack of `email` which was populated during the migration) +DELETE FROM "user" WHERE "email" IS NULL; +ALTER TABLE "user" ALTER COLUMN "email" SET NOT NULL; diff --git a/prisma/migrations/20230117131125_update_ama_onboarding/migration.sql b/prisma/migrations/20230117131125_update_ama_onboarding/migration.sql new file mode 100644 index 00000000000..92340af5996 --- /dev/null +++ b/prisma/migrations/20230117131125_update_ama_onboarding/migration.sql @@ -0,0 +1,17 @@ +ALTER TABLE "user" +DROP COLUMN "goals_description", +DROP COLUMN "risk_tolerance", +ADD COLUMN "risk_answers" JSONB NOT NULL DEFAULT '[]', +ADD COLUMN "user_notes" TEXT, +ADD COLUMN "goals_new" TEXT[]; + +UPDATE "user" +SET "goals_new" = "user"."goals"; + +ALTER TABLE "user" +DROP COLUMN "goals"; + +DROP TYPE "UserGoal"; + +ALTER TABLE "user" +RENAME COLUMN "goals_new" TO "goals"; \ No newline at end of file diff --git a/prisma/migrations/20230117150048_user_name/migration.sql b/prisma/migrations/20230117150048_user_name/migration.sql new file mode 100644 index 00000000000..4dce6565609 --- /dev/null +++ b/prisma/migrations/20230117150048_user_name/migration.sql @@ -0,0 +1,8 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "name" TEXT GENERATED ALWAYS AS ( + CASE + WHEN first_name IS NULL THEN last_name + WHEN last_name IS NULL THEN first_name + ELSE first_name || ' ' || last_name + END +) STORED; diff --git a/prisma/migrations/20230117192734_update_agreement_types/migration.sql b/prisma/migrations/20230117192734_update_agreement_types/migration.sql new file mode 100644 index 00000000000..8defc1f6202 --- /dev/null +++ b/prisma/migrations/20230117192734_update_agreement_types/migration.sql @@ -0,0 +1,27 @@ +-- Clear agreements +DELETE FROM "agreement"; + +-- AlterEnum +BEGIN; +CREATE TYPE "AgreementType_new" AS ENUM ('fee', 'form_adv_2a', 'form_adv_2b', 'form_crs', 'privacy_policy'); +ALTER TABLE "agreement" ALTER COLUMN "type" TYPE "AgreementType_new" USING ("type"::text::"AgreementType_new"); +ALTER TYPE "AgreementType" RENAME TO "AgreementType_old"; +ALTER TYPE "AgreementType_new" RENAME TO "AgreementType"; +DROP TYPE "AgreementType_old"; +COMMIT; + +-- DropIndex +DROP INDEX "agreement_type_revision_key"; + +-- CreateIndex +CREATE UNIQUE INDEX "agreement_type_revision_active_key" ON "agreement"("type", "revision", "active"); + +-- Insert initial agreements +INSERT INTO + agreement("type", "revision", "src", "active") +VALUES + ('fee', '2023-01-11', 'agreements/limited-scope-advisory-agreement-2023-01-11.pdf', true), + ('form_adv_2a', '2022-09-07', 'agreements/form-ADV-2A-2022-09-07.pdf', true), + ('form_adv_2b', '2022-11-04', 'agreements/form-ADV-2B-2022-11-04.pdf', true), + ('form_crs', '2022-09-20', 'agreements/form-CRS-2022-09-20.pdf', true), + ('privacy_policy', '2023-01-11', 'agreements/advisor-privacy-policy-2023-01-11.pdf', true); diff --git a/prisma/migrations/20230119114411_add_onboarding_steps/migration.sql b/prisma/migrations/20230119114411_add_onboarding_steps/migration.sql new file mode 100644 index 00000000000..57eb8ba2178 --- /dev/null +++ b/prisma/migrations/20230119114411_add_onboarding_steps/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "onboarding_steps" JSONB[]; diff --git a/prisma/migrations/20230123121401_separate_onboarding_flows/migration.sql b/prisma/migrations/20230123121401_separate_onboarding_flows/migration.sql new file mode 100644 index 00000000000..f0fe104c4c4 --- /dev/null +++ b/prisma/migrations/20230123121401_separate_onboarding_flows/migration.sql @@ -0,0 +1,22 @@ +ALTER TABLE "user" +DROP COLUMN "onboarded", +ADD COLUMN "onboarding" JSONB; + +UPDATE "user" +SET "onboarding" = + CASE + WHEN "onboarding_steps" IS NULL THEN NULL + ELSE json_build_object( + 'main', json_build_object( + 'markedComplete', false, + 'steps', "onboarding_steps" + ), + 'sidebar', json_build_object( + 'markedComplete', false, + 'steps', "onboarding_steps" + ) + ) + END; + +ALTER TABLE "user" +DROP COLUMN "onboarding_steps"; diff --git a/prisma/migrations/20230123192138_user_country_state/migration.sql b/prisma/migrations/20230123192138_user_country_state/migration.sql new file mode 100644 index 00000000000..101831d2c89 --- /dev/null +++ b/prisma/migrations/20230123192138_user_country_state/migration.sql @@ -0,0 +1,35 @@ +ALTER TABLE "user" ADD COLUMN "country" TEXT, ADD COLUMN "state" TEXT; + +UPDATE "user" +SET + "country" = ( + CASE + WHEN "residence" IS NOT NULL THEN 'US' + ELSE NULL + END + ), + "state" = ( + CASE "residence" + WHEN 'Alabama' THEN 'AL' + WHEN 'California' THEN 'CA' + WHEN 'Colorado' THEN 'CO' + WHEN 'Delaware' THEN 'DE' + WHEN 'District Of Columbia' THEN 'DC' + WHEN 'Florida' THEN 'FL' + WHEN 'Illinois' THEN 'IL' + WHEN 'Indiana' THEN 'IN' + WHEN 'Massachusetts' THEN 'MA' + WHEN 'Nevada' THEN 'NV' + WHEN 'New Jersey' THEN 'NJ' + WHEN 'New York' THEN 'NY' + WHEN 'Ohio' THEN 'OH' + WHEN 'Oregon' THEN 'OR' + WHEN 'Pennsylvania' THEN 'PA' + WHEN 'South Carolina' THEN 'SC' + WHEN 'Texas' THEN 'TX' + WHEN 'Virginia' THEN 'VA' + ELSE NULL + END + ); + +ALTER TABLE "user" DROP COLUMN "residence"; diff --git a/prisma/migrations/20230126230520_user_deletion/migration.sql b/prisma/migrations/20230126230520_user_deletion/migration.sql new file mode 100644 index 00000000000..7f1494af611 --- /dev/null +++ b/prisma/migrations/20230126230520_user_deletion/migration.sql @@ -0,0 +1,11 @@ +-- DropForeignKey +ALTER TABLE "signed_agreement" DROP CONSTRAINT "signed_agreement_agreement_id_fkey"; + +-- DropForeignKey +ALTER TABLE "signed_agreement" DROP CONSTRAINT "signed_agreement_user_id_fkey"; + +-- AddForeignKey +ALTER TABLE "signed_agreement" ADD CONSTRAINT "signed_agreement_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "signed_agreement" ADD CONSTRAINT "signed_agreement_agreement_id_fkey" FOREIGN KEY ("agreement_id") REFERENCES "agreement"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20230127003359_store_link_tokens/migration.sql b/prisma/migrations/20230127003359_store_link_tokens/migration.sql new file mode 100644 index 00000000000..7811fb90fd0 --- /dev/null +++ b/prisma/migrations/20230127003359_store_link_tokens/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "plaid_link_token" TEXT; diff --git a/prisma/migrations/20230130161915_account_value_start_date/migration.sql b/prisma/migrations/20230130161915_account_value_start_date/migration.sql new file mode 100644 index 00000000000..659db1595e5 --- /dev/null +++ b/prisma/migrations/20230130161915_account_value_start_date/migration.sql @@ -0,0 +1,17 @@ +-- update fn to fallback to the account creation date rather than `now()` +CREATE OR REPLACE FUNCTION public.account_value_start_date(p_account_id integer) RETURNS date LANGUAGE sql STABLE AS $$ + SELECT + COALESCE( + LEAST( + (a.loan->>'originationDate')::date, + (SELECT MIN(date) FROM "transaction" where "account_id" = a.id), + (SELECT MIN(date) FROM "valuation" where "account_id" = a.id), + (SELECT MIN(date) FROM "investment_transaction" where "account_id" = a.id) + ), + a.created_at::date -- fallback to using the date the account was added + ) + FROM + account a + WHERE + a.id = p_account_id +$$; diff --git a/prisma/migrations/20230207111117_user_account_linking/migration.sql b/prisma/migrations/20230207111117_user_account_linking/migration.sql new file mode 100644 index 00000000000..1f8f45885d0 --- /dev/null +++ b/prisma/migrations/20230207111117_user_account_linking/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "user" +ADD COLUMN "link_account_dismissed_at" TIMESTAMPTZ(6); diff --git a/prisma/migrations/20230207181233_add_conversation_relations/migration.sql b/prisma/migrations/20230207181233_add_conversation_relations/migration.sql new file mode 100644 index 00000000000..8347c30446f --- /dev/null +++ b/prisma/migrations/20230207181233_add_conversation_relations/migration.sql @@ -0,0 +1,9 @@ +-- AlterTable +ALTER TABLE "conversation" ADD COLUMN "account_id" INTEGER, +ADD COLUMN "plan_id" INTEGER; + +-- AddForeignKey +ALTER TABLE "conversation" ADD CONSTRAINT "conversation_account_id_fkey" FOREIGN KEY ("account_id") REFERENCES "account"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "conversation" ADD CONSTRAINT "conversation_plan_id_fkey" FOREIGN KEY ("plan_id") REFERENCES "plan"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20230207230108_account_balance_strategy/migration.sql b/prisma/migrations/20230207230108_account_balance_strategy/migration.sql new file mode 100644 index 00000000000..78bdf4c7df1 --- /dev/null +++ b/prisma/migrations/20230207230108_account_balance_strategy/migration.sql @@ -0,0 +1,54 @@ +CREATE TYPE "AccountBalanceStrategy" AS ENUM ('current', 'available', 'sum', 'difference'); + +ALTER TABLE "account" +ADD COLUMN "available_balance_strategy" "AccountBalanceStrategy" NOT NULL DEFAULT 'available', +ADD COLUMN "current_balance_provider" DECIMAL(19,4), +ADD COLUMN "current_balance_strategy" "AccountBalanceStrategy" NOT NULL DEFAULT 'current'; + +-- attempt to undo the Plaid `current = current + available` logic we currently have in place +UPDATE "account" +SET + "current_balance_provider" = ( + CASE + WHEN "plaid_type" = 'investment' AND "current_balance" IS NOT NULL AND "available_balance" IS NOT NULL AND "current_balance" > "available_balance" THEN "current_balance" - "available_balance" + ELSE "current_balance" + END + ), + "current_balance_strategy" = ( + CASE + WHEN "plaid_type" = 'investment' AND "current_balance" IS NOT NULL AND "available_balance" IS NOT NULL AND "current_balance" > "available_balance" THEN 'sum' + ELSE 'current' + END + )::"AccountBalanceStrategy"; + +ALTER TABLE "account" DROP COLUMN "current_balance"; + +ALTER TABLE "account" RENAME COLUMN "available_balance" TO "available_balance_provider"; +ALTER TABLE "account" ADD COLUMN "available_balance" DECIMAL(19,4) GENERATED ALWAYS AS ( + CASE "available_balance_strategy" + WHEN 'current' THEN "current_balance_provider" + WHEN 'available' THEN "available_balance_provider" + WHEN 'sum' THEN "available_balance_provider" + "current_balance_provider" + WHEN 'difference' THEN ABS("available_balance_provider" - "current_balance_provider") + END +) STORED; + +ALTER TABLE "account" ADD COLUMN "current_balance" DECIMAL(19,4) GENERATED ALWAYS AS ( + CASE "current_balance_strategy" + WHEN 'current' THEN "current_balance_provider" + WHEN 'available' THEN "available_balance_provider" + WHEN 'sum' THEN "current_balance_provider" + "available_balance_provider" + WHEN 'difference' THEN ABS("current_balance_provider" - "available_balance_provider") + END +) STORED; + +CREATE OR REPLACE FUNCTION valuation_changed() RETURNS TRIGGER LANGUAGE plpgsql AS $$ + BEGIN + UPDATE account AS a + SET + start_date = account_value_start_date(a.id), + current_balance_provider = (SELECT v.amount FROM valuation v WHERE v.account_id = a.id ORDER BY v.date DESC LIMIT 1) + WHERE a.id = NEW.account_id OR a.id = OLD.account_id; + RETURN NULL; + END; +$$; diff --git a/prisma/migrations/20230210163006_add_user_trial_end/migration.sql b/prisma/migrations/20230210163006_add_user_trial_end/migration.sql new file mode 100644 index 00000000000..11e032da467 --- /dev/null +++ b/prisma/migrations/20230210163006_add_user_trial_end/migration.sql @@ -0,0 +1,4 @@ +ALTER TABLE "user" ADD COLUMN "trial_end" TIMESTAMPTZ(6) DEFAULT NOW() + interval '14 days'; + +-- Users that are already subscribed or on a Stripe-based trial don't need a trial_end +UPDATE "user" SET "trial_end" = NULL WHERE "stripe_price_id" IS NOT NULL; \ No newline at end of file diff --git a/prisma/migrations/20230211134603_advisor_crm/migration.sql b/prisma/migrations/20230211134603_advisor_crm/migration.sql new file mode 100644 index 00000000000..5e169d95299 --- /dev/null +++ b/prisma/migrations/20230211134603_advisor_crm/migration.sql @@ -0,0 +1,29 @@ +-- CreateEnum +CREATE TYPE "TaxStatus" AS ENUM ('single', 'married_joint', 'married_separate', 'head_of_household', 'qualifying_widow'); + +-- AlterTable +ALTER TABLE "user" ADD COLUMN "dependents" INTEGER, +ADD COLUMN "gross_income" INTEGER, +ADD COLUMN "income_type" TEXT, +ADD COLUMN "tax_status" "TaxStatus"; + +-- CreateTable +CREATE TABLE "conversation_note" ( + "id" SERIAL NOT NULL, + "created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "user_id" INTEGER NOT NULL, + "conversation_id" INTEGER NOT NULL, + "body" TEXT NOT NULL, + + CONSTRAINT "conversation_note_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "conversation_note_user_id_conversation_id_key" ON "conversation_note"("user_id", "conversation_id"); + +-- AddForeignKey +ALTER TABLE "conversation_note" ADD CONSTRAINT "conversation_note_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "conversation_note" ADD CONSTRAINT "conversation_note_conversation_id_fkey" FOREIGN KEY ("conversation_id") REFERENCES "conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20230220194746_remove_stripe_trials/migration.sql b/prisma/migrations/20230220194746_remove_stripe_trials/migration.sql new file mode 100644 index 00000000000..58c79157b51 --- /dev/null +++ b/prisma/migrations/20230220194746_remove_stripe_trials/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "user" DROP COLUMN "stripe_trial_end", +DROP COLUMN "stripe_trial_reminder_sent", +ADD COLUMN "trial_reminder_sent" TIMESTAMPTZ(6), +ALTER COLUMN "trial_end" SET DEFAULT NOW() + interval '14 days'; diff --git a/prisma/migrations/20230223020847_txn_view/migration.sql b/prisma/migrations/20230223020847_txn_view/migration.sql new file mode 100644 index 00000000000..718b00d0bbb --- /dev/null +++ b/prisma/migrations/20230223020847_txn_view/migration.sql @@ -0,0 +1,58 @@ + -- AlterTable +ALTER TABLE "transaction" +ADD COLUMN "match_id" INTEGER, +ADD COLUMN "type_user" "TransactionType"; + +-- AddForeignKey +ALTER TABLE "transaction" ADD CONSTRAINT "transaction_match_id_fkey" FOREIGN KEY ("match_id") REFERENCES "transaction" ("id") ON DELETE SET NULL ON UPDATE CASCADE; + +CREATE OR REPLACE VIEW transactions_enriched AS ( + SELECT + t.id, + t.created_at as "createdAt", + t.updated_at as "updatedAt", + t.name, + t.account_id as "accountId", + t.date, + t.flow, + COALESCE( + t.type_user, + CASE + -- no matching transaction + WHEN t.match_id IS NULL THEN ( + CASE t.flow + WHEN 'INFLOW' THEN ( + CASE a.classification + WHEN 'asset' THEN 'INCOME'::"TransactionType" + WHEN 'liability' THEN 'PAYMENT'::"TransactionType" + END + ) + WHEN 'OUTFLOW' THEN 'EXPENSE'::"TransactionType" + END + ) + -- has matching transaction + ELSE ( + CASE a.classification + WHEN 'asset' THEN 'TRANSFER'::"TransactionType" + WHEN 'liability' THEN 'PAYMENT'::"TransactionType" + END + ) + END + ) AS "type", + t.type_user as "typeUser", + t.amount, + t.currency_code as "currencyCode", + t.pending, + t.merchant_name as "merchantName", + t.category, + t.category_user as "categoryUser", + t.excluded, + t.match_id as "matchId", + COALESCE(ac.user_id, a.user_id) as "userId", + a.classification as "accountClassification", + a.type as "accountType" + FROM + transaction t + inner join account a on a.id = t.account_id + left join account_connection ac on a.account_connection_id = ac.id +); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 00000000000..fbffa92c2bb --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000000..e51969df89a --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,735 @@ +// Schema follows Prisma naming conventions +// https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#naming-conventions + +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "linux-musl", "debian-openssl-1.1.x"] + previewFeatures = ["fullTextSearch", "tracing"] +} + +datasource db { + provider = "postgresql" + url = env("NX_DATABASE_URL") +} + +// hypertable +model AccountBalance { + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) + accountId Int @map("account_id") + date DateTime @db.Date + balance Decimal @db.Decimal(19, 4) + inflows Decimal? @default(0) @db.Decimal(19, 4) + outflows Decimal? @default(0) @db.Decimal(19, 4) + + @@id([accountId, date]) + @@index([date], map: "account_balance_date_idx") + @@map("account_balance") +} + +enum AccountConnectionStatus { + OK + ERROR + DISCONNECTED +} + +enum AccountSyncStatus { + IDLE + PENDING + SYNCING +} + +enum AccountConnectionType { + plaid + finicity +} + +model AccountConnection { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @map("user_id") + name String + type AccountConnectionType + status AccountConnectionStatus @default(OK) + syncStatus AccountSyncStatus @default(IDLE) @map("sync_status") + + // plaid data + plaidItemId String? @unique @map("plaid_item_id") + plaidInstitutionId String? @map("plaid_institution_id") + plaidAccessToken String? @map("plaid_access_token") + plaidConsentExpiration DateTime? @map("plaid_consent_expiration") + plaidError Json? @map("plaid_error") + plaidNewAccountsAvailable Boolean @default(false) @map("plaid_new_accounts_available") + + // finicity data + finicityInstitutionLoginId String? @map("finicity_institution_login_id") + finicityInstitutionId String? @map("finicity_institution_id") + finicityError Json? @map("finicity_error") + + accounts Account[] + + @@index([userId]) + @@map("account_connection") +} + +enum AccountType { + INVESTMENT + DEPOSITORY + CREDIT + LOAN + PROPERTY + VEHICLE + OTHER_ASSET + OTHER_LIABILITY +} + +enum AccountCategory { + cash + investment + crypto + property + vehicle + valuable + loan + credit + other +} + +enum AccountProvider { + user + plaid + finicity +} + +enum AccountBalanceStrategy { + current + available + sum + difference +} + +model Account { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + startDate DateTime? @map("start_date") @db.Date + type AccountType + provider AccountProvider + classification AccountClassification @default(dbgenerated()) + category AccountCategory @default(dbgenerated()) + categoryProvider AccountCategory? @map("category_provider") + categoryUser AccountCategory? @map("category_user") + subcategory String @default(dbgenerated()) + subcategoryProvider String? @map("subcategory_provider") + subcategoryUser String? @map("subcategory_user") + accountConnection AccountConnection? @relation(fields: [accountConnectionId], references: [id], onDelete: Cascade) + accountConnectionId Int? @map("account_connection_id") + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int? @map("user_id") + name String + mask String? + isActive Boolean @default(true) @map("is_active") + syncStatus AccountSyncStatus @default(IDLE) @map("sync_status") + currencyCode String @default("USD") @map("currency_code") + currentBalance Decimal? @default(dbgenerated()) @map("current_balance") @db.Decimal(19, 4) + currentBalanceProvider Decimal? @map("current_balance_provider") @db.Decimal(19, 4) + currentBalanceStrategy AccountBalanceStrategy @default(current) @map("current_balance_strategy") + availableBalance Decimal? @default(dbgenerated()) @map("available_balance") @db.Decimal(19, 4) + availableBalanceProvider Decimal? @map("available_balance_provider") @db.Decimal(19, 4) + availableBalanceStrategy AccountBalanceStrategy @default(available) @map("available_balance_strategy") + + // plaid data + plaidAccountId String? @map("plaid_account_id") + plaidType String? @map("plaid_type") + plaidSubtype String? @map("plaid_subtype") + plaidLiability Json? @map("plaid_liability") @db.JsonB + + // finicity data + finicityAccountId String? @map("finicity_account_id") + finicityType String? @map("finicity_type") + finicityDetail Json? @map("finicity_detail") @db.JsonB + + // manual account data + vehicleMeta Json? @map("vehicle_meta") @db.JsonB + propertyMeta Json? @map("property_meta") @db.JsonB + + loan Json? @default(dbgenerated()) @db.JsonB + loanUser Json? @map("loan_user") @db.JsonB + loanProvider Json? @map("loan_provider") @db.JsonB + + credit Json? @default(dbgenerated()) @db.JsonB + creditUser Json? @map("credit_user") @db.JsonB + creditProvider Json? @map("credit_provider") @db.JsonB + + balances AccountBalance[] + transactions Transaction[] + valuations Valuation[] + holdings Holding[] + investmentTransactions InvestmentTransaction[] + + conversations Conversation[] + + @@unique([accountConnectionId, plaidAccountId]) + @@unique([accountConnectionId, finicityAccountId]) + @@index([accountConnectionId]) + @@index([userId]) + @@map("account") +} + +enum AccountClassification { + asset + liability +} + +model Holding { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) + accountId Int @map("account_id") + security Security @relation(fields: [securityId], references: [id], onDelete: Cascade) + securityId Int @map("security_id") + value Decimal @db.Decimal(19, 4) + quantity Decimal @db.Decimal(36, 18) + costBasis Decimal? @default(dbgenerated()) @map("cost_basis") @db.Decimal(23, 8) + costBasisProvider Decimal? @map("cost_basis_provider") @db.Decimal(23, 8) + costBasisUser Decimal? @map("cost_basis_user") @db.Decimal(23, 8) + currencyCode String @default("USD") @map("currency_code") + excluded Boolean @default(false) + + // plaid data + plaidHoldingId String? @unique @map("plaid_holding_id") // this is an artificial ID `account[].security[]` + + // finicity data + finicityPositionId String? @unique @map("finicity_position_id") + + @@map("holding") +} + +enum InvestmentTransactionCategory { + buy + sell + dividend + transfer + tax + fee + cancel + other +} + +model InvestmentTransaction { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) + accountId Int @map("account_id") + security Security? @relation(fields: [securityId], references: [id], onDelete: Cascade) + securityId Int? @map("security_id") + date DateTime @db.Date + name String + amount Decimal @db.Decimal(19, 4) + fees Decimal? @db.Decimal(19, 4) + flow TransactionFlow @default(dbgenerated()) + quantity Decimal @db.Decimal(36, 18) + price Decimal @db.Decimal(23, 8) + currencyCode String @default("USD") @map("currency_code") + + // Derived from provider types + category InvestmentTransactionCategory @default(dbgenerated()) + + // plaid data + plaidInvestmentTransactionId String? @unique @map("plaid_investment_transaction_id") + plaidType String? @map("plaid_type") + plaidSubtype String? @map("plaid_subtype") + + // finicity data + finicityTransactionId String? @unique @map("finicity_transaction_id") + finicityInvestmentTransactionType String? @map("finicity_investment_transaction_type") + + @@index([accountId, date]) + @@map("investment_transaction") +} + +model Security { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + name String? + symbol String? + cusip String? + isin String? + sharesPerContract Decimal? @map("shares_per_contract") @db.Decimal(36, 18) + currencyCode String @default("USD") @map("currency_code") + pricingLastSyncedAt DateTime? @map("pricing_last_synced_at") @db.Timestamptz(6) + isBrokerageCash Boolean @default(false) @map("is_brokerage_cash") + + // plaid data + plaidSecurityId String? @unique @map("plaid_security_id") + plaidType String? @map("plaid_type") + plaidIsCashEquivalent Boolean? @map("plaid_is_cash_equivalent") + + // finicity data + finicitySecurityId String? @map("finicity_security_id") + finicitySecurityIdType String? @map("finicity_security_id_type") + finicityType String? @map("finicity_type") + finicityAssetClass String? @map("finicity_asset_class") + finicityFIAssetClass String? @map("finicity_fi_asset_class") + + holdings Holding[] + investmentTransactions InvestmentTransaction[] + pricing SecurityPricing[] + + @@unique([finicitySecurityId, finicitySecurityIdType]) + @@map("security") +} + +// hypertable +model SecurityPricing { + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + security Security @relation(fields: [securityId], references: [id], onDelete: Cascade) + securityId Int @map("security_id") + date DateTime @db.Date + priceClose Decimal @map("price_close") @db.Decimal(23, 8) + priceAsOf DateTime @default(now()) @map("price_as_of") @db.Timestamptz(6) + source String? + + @@id([securityId, date]) + @@index([date], map: "security_pricing_date_idx") + @@map("security_pricing") +} + +enum TransactionFlow { + INFLOW + OUTFLOW +} + +enum TransactionType { + INCOME // inflow to asset + EXPENSE // outflow from asset OR outflow from liability + PAYMENT // outflow from asset OR inflow/outflow from liability + TRANSFER // inflow/outflow from asset OR inflow/outflow from liability +} + +model Transaction { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) + accountId Int @map("account_id") + date DateTime @db.Date + flow TransactionFlow @default(dbgenerated()) + typeUser TransactionType? @map("type_user") + name String + amount Decimal @db.Decimal(19, 4) + currencyCode String @default("USD") @map("currency_code") + pending Boolean @default(false) + merchantName String? @map("merchant_name") + category String @default(dbgenerated()) + categoryUser String? @map("category_user") + excluded Boolean @default(false) + + // transfer matching + matchId Int? @map("match_id") + match Transaction? @relation("MatchedTransaction", fields: [matchId], references: [id]) + matches Transaction[] @relation("MatchedTransaction") + + // plaid data + plaidTransactionId String? @unique @map("plaid_transaction_id") + plaidCategory String[] @map("plaid_category") + plaidCategoryId String? @map("plaid_category_id") + plaidPersonalFinanceCategory Json? @map("plaid_personal_finance_category") + + // finicity data + finicityTransactionId String? @unique @map("finicity_transaction_id") + finicityType String? @map("finicity_type") + finicityCategorization Json? @map("finicity_categorization") @db.JsonB + + @@index([accountId, date]) + @@index([amount]) + @@map("transaction") +} + +model Valuation { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) + accountId Int @map("account_id") + source String + amount Decimal @db.Decimal(19, 4) + date DateTime @db.Date + currencyCode String @default("USD") @map("currency_code") + + @@unique([accountId, source, date]) + @@index([accountId, date]) + @@map("valuation") +} + +// User's current household +enum Household { + single + singleWithDependents + dual + dualWithDependents + retired +} + +// User's goals for using Maybe +enum MaybeGoal { + aggregate + advice + plan +} + +enum TaxStatus { + single + married_joint + married_separate + head_of_household + qualifying_widow +} + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + auth0Id String @unique @map("auth0_id") + + // profile + email String @db.Citext + firstName String? @map("first_name") + lastName String? @map("last_name") + name String? @default(dbgenerated()) + dob DateTime? @db.Date + picture String? + onboarding Json? @map("onboarding") + linkAccountDismissedAt DateTime? @map("link_account_dismissed_at") @db.Timestamptz(6) + + isoCurrencyCode String @default("USD") @map("iso_currency_code") + + // Notification preferences + convertKitId Int? @map("convert_kit_id") + ataAll Boolean @default(true) @map("ata_all") + ataSubmitted Boolean @default(true) @map("ata_submitted") + ataReview Boolean @default(true) @map("ata_review") + ataUpdate Boolean @default(true) @map("ata_update") + ataClosed Boolean @default(true) @map("ata_closed") + ataExpire Boolean @default(true) @map("ata_expire") + + // Financial preferences and info + monthlyIncomeUser Decimal? @map("monthly_income_user") @db.Decimal(19, 4) + monthlyExpensesUser Decimal? @map("monthly_expenses_user") @db.Decimal(19, 4) + monthlyDebtUser Decimal? @map("monthly_debt_user") @db.Decimal(19, 4) + + // Billing + trialEnd DateTime? @default(dbgenerated("NOW() + interval '14 days'")) @map("trial_end") @db.Timestamptz(6) + trialReminderSent DateTime? @map("trial_reminder_sent") @db.Timestamptz(6) + stripeCustomerId String? @unique @map("stripe_customer_id") + stripeSubscriptionId String? @unique @map("stripe_subscription_id") + stripePriceId String? @map("stripe_price_id") + stripeCurrentPeriodEnd DateTime? @map("stripe_current_period_end") @db.Timestamptz(6) + stripeCancelAt DateTime? @map("stripe_cancel_at") @db.Timestamptz(6) + + // finicity data + finicityUsername String? @unique @map("finicity_username") + finicityCustomerId String? @unique @map("finicity_customer_id") + + // plaid data + plaidLinkToken String? @map("plaid_link_token") // temporary token stored to maintain state across browsers + + // Ask My Advisor + advisorNotes String? @map("advisor_notes") + userNotes String? @map("user_notes") + goals String[] + riskAnswers Json @default("[]") @map("risk_answers") @db.JsonB + + // Onboarding / usage goals + household Household? + state String? + country String? + maybeGoals MaybeGoal[] @map("maybe_goals") + maybeGoalsDescription String? @map("maybe_goals_description") + maybe String? + title String? + dependents Int? + grossIncome Int? @map("gross_income") + incomeType String? @map("income_type") + taxStatus TaxStatus? @map("tax_status") + memberNumber Int @unique() @default(autoincrement()) @map("member_number") + memberId String @unique() @default(dbgenerated("gen_random_uuid()")) @map("member_id") + + accountConnections AccountConnection[] + accounts Account[] + plans Plan[] + messages Message[] + conversations Conversation[] + advisor Advisor? + signedAgreements SignedAgreement[] + conversationNotes ConversationNote[] + + @@map("user") +} + +model Institution { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + name String + url String? + logo String? + logoUrl String? @map("logo_url") + primaryColor String? @map("primary_color") + + providers ProviderInstitution[] + + @@unique([name, url]) + @@map("institution") +} + +enum Provider { + PLAID + FINICITY +} + +model ProviderInstitution { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + provider Provider + providerId String @map("provider_id") + institution Institution? @relation(fields: [institutionId], references: [id], onDelete: SetNull) + institutionId Int? @map("institution_id") + rank Int @default(0) + oauth Boolean @default(false) + name String + url String? + logo String? + logoUrl String? @map("logo_url") + primaryColor String? @map("primary_color") + data Json? @db.JsonB + + @@unique([provider, providerId]) + @@map("provider_institution") +} + +model Plan { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @map("user_id") + name String + lifeExpectancy Int @default(85) @map("life_expectancy") + + events PlanEvent[] + milestones PlanMilestone[] + + conversations Conversation[] + + @@map("plan") +} + +enum PlanEventFrequency { + monthly + yearly +} + +model PlanEvent { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + plan Plan @relation(fields: [planId], references: [id], onDelete: Cascade) + planId Int @map("plan_id") + name String + category String? + startYear Int? @map("start_year") + startMilestone PlanMilestone? @relation("StartEvents", fields: [startMilestoneId], references: [id], onDelete: Cascade) + startMilestoneId Int? @map("start_milestone_id") + endYear Int? @map("end_year") + endMilestone PlanMilestone? @relation("EndEvents", fields: [endMilestoneId], references: [id], onDelete: Cascade) + endMilestoneId Int? @map("end_milestone_id") + frequency PlanEventFrequency @default(yearly) + initialValue Decimal? @map("initial_value") @db.Decimal(19, 4) + initialValueRef String? @map("initial_value_ref") + rate Decimal @default(0) @db.Decimal(6, 4) + + @@map("plan_event") +} + +enum PlanMilestoneType { + year + net_worth +} + +model PlanMilestone { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + plan Plan @relation(fields: [planId], references: [id], onDelete: Cascade) + planId Int @map("plan_id") + name String + category String @default("retirement") + type PlanMilestoneType + year Int? + expenseMultiple Float? @map("expense_multiple") + expenseYears Int? @map("expense_years") + + startEvents PlanEvent[] @relation("StartEvents") + endEvents PlanEvent[] @relation("EndEvents") + + @@map("plan_milestone") +} + +enum ApprovalStatus { + pending + approved + rejected +} + +model Advisor { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @unique @map("user_id") + approvalStatus ApprovalStatus @default(pending) @map("approval_status") + fullName String @map("full_name") + title String + bio String + avatarSrc String @map("avatar_src") + + conversations ConversationAdvisor[] + + @@map("advisor") +} + +enum ConversationStatus { + open + closed +} + +model Conversation { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + status ConversationStatus @default(open) + title String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @map("user_id") + expiryEmailSent DateTime? @map("expiry_email_sent") @db.Timestamptz(6) + + account Account? @relation(fields: [accountId], references: [id], onDelete: SetNull) + accountId Int? @map("account_id") + plan Plan? @relation(fields: [planId], references: [id], onDelete: SetNull) + planId Int? @map("plan_id") + + messages Message[] + advisors ConversationAdvisor[] + notes ConversationNote[] + + @@map("conversation") +} + +model ConversationAdvisor { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) + conversationId Int @map("conversation_id") + advisor Advisor @relation(fields: [advisorId], references: [id], onDelete: Cascade) + advisorId Int @map("advisor_id") + + @@unique([conversationId, advisorId]) + @@map("conversation_advisor") +} + +model ConversationNote { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @map("user_id") + conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) + conversationId Int @map("conversation_id") + body String + + @@unique([userId, conversationId]) // one note per user, per conversation + @@map("conversation_note") +} + +enum MessageType { + text + audio + video +} + +model Message { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + updatedAt DateTime @default(now()) @updatedAt @map("updated_at") @db.Timestamptz(6) + conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade) + conversationId Int @map("conversation_id") + // this message will cascade delete if the user who created the conversation is deleted (via conversationId FK cascade) + // otherwise we preserve the message (using SetNull) so it doesn't disappear from the conversation + user User? @relation(fields: [userId], references: [id], onDelete: SetNull) + userId Int? @map("user_id") + type MessageType + body String? + mediaSrc String? @map("media_src") + + @@map("message") +} + +enum AuditEventType { + insert + update + delete +} + +model AuditEvent { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6) + type AuditEventType + modelType String @map("model_type") // e.g. "Message" + modelId Int @map("model_id") + data Json + userId Int? @map("user_id") // who made the change - if NULL, was an automated system change + + @@map("audit_event") +} + +enum AgreementType { + fee + form_adv_2a + form_adv_2b + form_crs + privacy_policy +} + +model Agreement { + id Int @id @default(autoincrement()) + type AgreementType + revision DateTime @db.Date + src String @unique + active Boolean @default(false) // if true and is latest version, Client will serve to users + signers SignedAgreement[] + + @@unique([type, revision, active]) + @@map("agreement") +} + +model SignedAgreement { + signedAt DateTime @default(now()) @map("signed_at") @db.Timestamptz(6) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId Int @map("user_id") + agreement Agreement @relation(fields: [agreementId], references: [id], onDelete: Cascade) + agreementId Int @map("agreement_id") + src String? // Not all agreements require S3 audit record + + @@id([userId, agreementId]) + @@map("signed_agreement") +} diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 00000000000..da910b02c06 --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,70 @@ +import { Institution, PrismaClient, Provider } from '@prisma/client' + +const prisma = new PrismaClient() + +/* + * NOTE: seeding should be idempotent + */ +async function main() { + const institutions: (Pick & { + providers: { provider: Provider; providerId: string; rank?: number }[] + })[] = [ + { + id: 1, + name: 'Capital One', + providers: [ + { provider: 'PLAID', providerId: 'ins_9', rank: 1 }, + { provider: 'FINICITY', providerId: '170778' }, + ], + }, + { + id: 2, + name: 'Discover Bank', + providers: [ + { provider: 'PLAID', providerId: 'ins_33' }, + { provider: 'FINICITY', providerId: '13796', rank: 1 }, + ], + }, + ] + + await prisma.$transaction([ + // create institution linked to provider institutions + ...institutions.map(({ id, name, providers }) => + prisma.institution.upsert({ + where: { id }, + create: { + name, + providers: { + connectOrCreate: providers.map(({ provider, providerId, rank = 0 }) => ({ + where: { + provider_providerId: { provider, providerId }, + }, + create: { + provider, + providerId, + name, + rank, + }, + })), + }, + }, + update: {}, + }) + ), + ]) +} + +// Only run the seed in preview environments, not production +if (process.env.NODE_ENV !== 'production') { + console.log('seeding...') + main() + .catch((e) => { + console.error('prisma seed failed', e) + process.exit(1) + }) + .finally(async () => { + await prisma.$disconnect() + }) +} else { + console.warn('seeding skipped', process.env.NODE_ENV) +} diff --git a/redis.Dockerfile b/redis.Dockerfile new file mode 100644 index 00000000000..a4d7e6a461b --- /dev/null +++ b/redis.Dockerfile @@ -0,0 +1,5 @@ +FROM redis:6-alpine + +COPY redis.conf . + +ENTRYPOINT ["redis-server", "./redis.conf"] \ No newline at end of file diff --git a/redis.conf b/redis.conf new file mode 100644 index 00000000000..be65dca50df --- /dev/null +++ b/redis.conf @@ -0,0 +1,1883 @@ +# Redis configuration file (default file from Redis docs) +# ==================================================================== +# See - https://raw.githubusercontent.com/redis/redis/6.0/redis.conf) +# See - https://redis.io/topics/config +# ==================================================================== + + + +# Note that in order to read the configuration file, Redis must be +# started with the file path as first argument: +# +# ./redis-server /path/to/redis.conf + +# Note on units: when memory size is needed, it is possible to specify +# it in the usual form of 1k 5GB 4M and so forth: +# +# 1k => 1000 bytes +# 1kb => 1024 bytes +# 1m => 1000000 bytes +# 1mb => 1024*1024 bytes +# 1g => 1000000000 bytes +# 1gb => 1024*1024*1024 bytes +# +# units are case insensitive so 1GB 1Gb 1gB are all the same. + +################################## INCLUDES ################################### + +# Include one or more other config files here. This is useful if you +# have a standard template that goes to all Redis servers but also need +# to customize a few per-server settings. Include files can include +# other files, so use this wisely. +# +# Note that option "include" won't be rewritten by command "CONFIG REWRITE" +# from admin or Redis Sentinel. Since Redis always uses the last processed +# line as value of a configuration directive, you'd better put includes +# at the beginning of this file to avoid overwriting config change at runtime. +# +# If instead you are interested in using includes to override configuration +# options, it is better to use include as the last line. +# +# include /path/to/local.conf +# include /path/to/other.conf + +################################## MODULES ##################################### + +# Load modules at startup. If the server is not able to load modules +# it will abort. It is possible to use multiple loadmodule directives. +# +# loadmodule /path/to/my_module.so +# loadmodule /path/to/other_module.so + +################################## NETWORK ##################################### + +# By default, if no "bind" configuration directive is specified, Redis listens +# for connections from all available network interfaces on the host machine. +# It is possible to listen to just one or multiple selected interfaces using +# the "bind" configuration directive, followed by one or more IP addresses. +# +# Examples: +# +# bind 192.168.1.100 10.0.0.1 +# bind 127.0.0.1 ::1 +# +# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the +# internet, binding to all the interfaces is dangerous and will expose the +# instance to everybody on the internet. So by default we uncomment the +# following bind directive, that will force Redis to listen only on the +# IPv4 loopback interface address (this means Redis will only be able to +# accept client connections from the same host that it is running on). +# +# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES +# JUST COMMENT OUT THE FOLLOWING LINE. +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# bind 127.0.0.1 (since we are running this in a PRIVATE Render service, okay to listen on all interfaces) + +# Protected mode is a layer of security protection, in order to avoid that +# Redis instances left open on the internet are accessed and exploited. +# +# When protected mode is on and if: +# +# 1) The server is not binding explicitly to a set of addresses using the +# "bind" directive. +# 2) No password is configured. +# +# The server only accepts connections from clients connecting from the +# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain +# sockets. +# +# By default protected mode is enabled. You should disable it only if +# you are sure you want clients from other hosts to connect to Redis +# even if no authentication is configured, nor a specific set of interfaces +# are explicitly listed using the "bind" directive. +# protected-mode yes (since we are running this in a PRIVATE Render service, okay to listen on all interfaces) + +# Accept connections on the specified port, default is 6379 (IANA #815344). +# If port 0 is specified Redis will not listen on a TCP socket. +port 6379 + +# TCP listen() backlog. +# +# In high requests-per-second environments you need a high backlog in order +# to avoid slow clients connection issues. Note that the Linux kernel +# will silently truncate it to the value of /proc/sys/net/core/somaxconn so +# make sure to raise both the value of somaxconn and tcp_max_syn_backlog +# in order to get the desired effect. +tcp-backlog 511 + +# Unix socket. +# +# Specify the path for the Unix socket that will be used to listen for +# incoming connections. There is no default, so Redis will not listen +# on a unix socket when not specified. +# +# unixsocket /tmp/redis.sock +# unixsocketperm 700 + +# Close the connection after a client is idle for N seconds (0 to disable) +timeout 0 + +# TCP keepalive. +# +# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence +# of communication. This is useful for two reasons: +# +# 1) Detect dead peers. +# 2) Force network equipment in the middle to consider the connection to be +# alive. +# +# On Linux, the specified value (in seconds) is the period used to send ACKs. +# Note that to close the connection the double of the time is needed. +# On other kernels the period depends on the kernel configuration. +# +# A reasonable value for this option is 300 seconds, which is the new +# Redis default starting with Redis 3.2.1. +tcp-keepalive 300 + +################################# TLS/SSL ##################################### + +# By default, TLS/SSL is disabled. To enable it, the "tls-port" configuration +# directive can be used to define TLS-listening ports. To enable TLS on the +# default port, use: +# +# port 0 +# tls-port 6379 + +# Configure a X.509 certificate and private key to use for authenticating the +# server to connected clients, masters or cluster peers. These files should be +# PEM formatted. +# +# tls-cert-file redis.crt +# tls-key-file redis.key + +# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange: +# +# tls-dh-params-file redis.dh + +# Configure a CA certificate(s) bundle or directory to authenticate TLS/SSL +# clients and peers. Redis requires an explicit configuration of at least one +# of these, and will not implicitly use the system wide configuration. +# +# tls-ca-cert-file ca.crt +# tls-ca-cert-dir /etc/ssl/certs + +# By default, clients (including replica servers) on a TLS port are required +# to authenticate using valid client side certificates. +# +# If "no" is specified, client certificates are not required and not accepted. +# If "optional" is specified, client certificates are accepted and must be +# valid if provided, but are not required. +# +# tls-auth-clients no +# tls-auth-clients optional + +# By default, a Redis replica does not attempt to establish a TLS connection +# with its master. +# +# Use the following directive to enable TLS on replication links. +# +# tls-replication yes + +# By default, the Redis Cluster bus uses a plain TCP connection. To enable +# TLS for the bus protocol, use the following directive: +# +# tls-cluster yes + +# Explicitly specify TLS versions to support. Allowed values are case insensitive +# and include "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" (OpenSSL >= 1.1.1) or +# any combination. To enable only TLSv1.2 and TLSv1.3, use: +# +# tls-protocols "TLSv1.2 TLSv1.3" + +# Configure allowed ciphers. See the ciphers(1ssl) manpage for more information +# about the syntax of this string. +# +# Note: this configuration applies only to <= TLSv1.2. +# +# tls-ciphers DEFAULT:!MEDIUM + +# Configure allowed TLSv1.3 ciphersuites. See the ciphers(1ssl) manpage for more +# information about the syntax of this string, and specifically for TLSv1.3 +# ciphersuites. +# +# tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256 + +# When choosing a cipher, use the server's preference instead of the client +# preference. By default, the server follows the client's preference. +# +# tls-prefer-server-ciphers yes + +# By default, TLS session caching is enabled to allow faster and less expensive +# reconnections by clients that support it. Use the following directive to disable +# caching. +# +# tls-session-caching no + +# Change the default number of TLS sessions cached. A zero value sets the cache +# to unlimited size. The default size is 20480. +# +# tls-session-cache-size 5000 + +# Change the default timeout of cached TLS sessions. The default timeout is 300 +# seconds. +# +# tls-session-cache-timeout 60 + +################################# GENERAL ##################################### + +# By default Redis does not run as a daemon. Use 'yes' if you need it. +# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. +daemonize no + +# If you run Redis from upstart or systemd, Redis can interact with your +# supervision tree. Options: +# supervised no - no supervision interaction +# supervised upstart - signal upstart by putting Redis into SIGSTOP mode +# requires "expect stop" in your upstart job config +# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET +# supervised auto - detect upstart or systemd method based on +# UPSTART_JOB or NOTIFY_SOCKET environment variables +# Note: these supervision methods only signal "process is ready." +# They do not enable continuous pings back to your supervisor. +supervised no + +# If a pid file is specified, Redis writes it where specified at startup +# and removes it at exit. +# +# When the server runs non daemonized, no pid file is created if none is +# specified in the configuration. When the server is daemonized, the pid file +# is used even if not specified, defaulting to "/var/run/redis.pid". +# +# Creating a pid file is best effort: if Redis is not able to create it +# nothing bad happens, the server will start and run normally. +pidfile /var/run/redis_6379.pid + +# Specify the server verbosity level. +# This can be one of: +# debug (a lot of information, useful for development/testing) +# verbose (many rarely useful info, but not a mess like the debug level) +# notice (moderately verbose, what you want in production probably) +# warning (only very important / critical messages are logged) +loglevel notice + +# Specify the log file name. Also the empty string can be used to force +# Redis to log on the standard output. Note that if you use standard +# output for logging but daemonize, logs will be sent to /dev/null +logfile "" + +# To enable logging to the system logger, just set 'syslog-enabled' to yes, +# and optionally update the other syslog parameters to suit your needs. +# syslog-enabled no + +# Specify the syslog identity. +# syslog-ident redis + +# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. +# syslog-facility local0 + +# Set the number of databases. The default database is DB 0, you can select +# a different one on a per-connection basis using SELECT where +# dbid is a number between 0 and 'databases'-1 +databases 16 + +# By default Redis shows an ASCII art logo only when started to log to the +# standard output and if the standard output is a TTY. Basically this means +# that normally a logo is displayed only in interactive sessions. +# +# However it is possible to force the pre-4.0 behavior and always show a +# ASCII art logo in startup logs by setting the following option to yes. +always-show-logo yes + +################################ SNAPSHOTTING ################################ +# +# Save the DB on disk: +# +# save +# +# Will save the DB if both the given number of seconds and the given +# number of write operations against the DB occurred. +# +# In the example below the behavior will be to save: +# after 900 sec (15 min) if at least 1 key changed +# after 300 sec (5 min) if at least 10 keys changed +# after 60 sec if at least 10000 keys changed +# +# Note: you can disable saving completely by commenting out all "save" lines. +# +# It is also possible to remove all the previously configured save +# points by adding a save directive with a single empty string argument +# like in the following example: +# +# save "" + +save 900 1 +save 300 10 +save 60 10000 + +# By default Redis will stop accepting writes if RDB snapshots are enabled +# (at least one save point) and the latest background save failed. +# This will make the user aware (in a hard way) that data is not persisting +# on disk properly, otherwise chances are that no one will notice and some +# disaster will happen. +# +# If the background saving process will start working again Redis will +# automatically allow writes again. +# +# However if you have setup your proper monitoring of the Redis server +# and persistence, you may want to disable this feature so that Redis will +# continue to work as usual even if there are problems with disk, +# permissions, and so forth. +stop-writes-on-bgsave-error yes + +# Compress string objects using LZF when dump .rdb databases? +# By default compression is enabled as it's almost always a win. +# If you want to save some CPU in the saving child set it to 'no' but +# the dataset will likely be bigger if you have compressible values or keys. +rdbcompression yes + +# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. +# This makes the format more resistant to corruption but there is a performance +# hit to pay (around 10%) when saving and loading RDB files, so you can disable it +# for maximum performances. +# +# RDB files created with checksum disabled have a checksum of zero that will +# tell the loading code to skip the check. +rdbchecksum yes + +# The filename where to dump the DB +dbfilename dump.rdb + +# Remove RDB files used by replication in instances without persistence +# enabled. By default this option is disabled, however there are environments +# where for regulations or other security concerns, RDB files persisted on +# disk by masters in order to feed replicas, or stored on disk by replicas +# in order to load them for the initial synchronization, should be deleted +# ASAP. Note that this option ONLY WORKS in instances that have both AOF +# and RDB persistence disabled, otherwise is completely ignored. +# +# An alternative (and sometimes better) way to obtain the same effect is +# to use diskless replication on both master and replicas instances. However +# in the case of replicas, diskless is not always an option. +rdb-del-sync-files no + +# The working directory. +# +# The DB will be written inside this directory, with the filename specified +# above using the 'dbfilename' configuration directive. +# +# The Append Only File will also be created inside this directory. +# +# Note that you must specify a directory here, not a file name. +dir ./ + +################################# REPLICATION ################################# + +# Master-Replica replication. Use replicaof to make a Redis instance a copy of +# another Redis server. A few things to understand ASAP about Redis replication. +# +# +------------------+ +---------------+ +# | Master | ---> | Replica | +# | (receive writes) | | (exact copy) | +# +------------------+ +---------------+ +# +# 1) Redis replication is asynchronous, but you can configure a master to +# stop accepting writes if it appears to be not connected with at least +# a given number of replicas. +# 2) Redis replicas are able to perform a partial resynchronization with the +# master if the replication link is lost for a relatively small amount of +# time. You may want to configure the replication backlog size (see the next +# sections of this file) with a sensible value depending on your needs. +# 3) Replication is automatic and does not need user intervention. After a +# network partition replicas automatically try to reconnect to masters +# and resynchronize with them. +# +# replicaof + +# If the master is password protected (using the "requirepass" configuration +# directive below) it is possible to tell the replica to authenticate before +# starting the replication synchronization process, otherwise the master will +# refuse the replica request. +# +# masterauth +# +# However this is not enough if you are using Redis ACLs (for Redis version +# 6 or greater), and the default user is not capable of running the PSYNC +# command and/or other commands needed for replication. In this case it's +# better to configure a special user to use with replication, and specify the +# masteruser configuration as such: +# +# masteruser +# +# When masteruser is specified, the replica will authenticate against its +# master using the new AUTH form: AUTH . + +# When a replica loses its connection with the master, or when the replication +# is still in progress, the replica can act in two different ways: +# +# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will +# still reply to client requests, possibly with out of date data, or the +# data set may just be empty if this is the first synchronization. +# +# 2) If replica-serve-stale-data is set to 'no' the replica will reply with +# an error "SYNC with master in progress" to all commands except: +# INFO, REPLICAOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, +# UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, +# HOST and LATENCY. +# +replica-serve-stale-data yes + +# You can configure a replica instance to accept writes or not. Writing against +# a replica instance may be useful to store some ephemeral data (because data +# written on a replica will be easily deleted after resync with the master) but +# may also cause problems if clients are writing to it because of a +# misconfiguration. +# +# Since Redis 2.6 by default replicas are read-only. +# +# Note: read only replicas are not designed to be exposed to untrusted clients +# on the internet. It's just a protection layer against misuse of the instance. +# Still a read only replica exports by default all the administrative commands +# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve +# security of read only replicas using 'rename-command' to shadow all the +# administrative / dangerous commands. +replica-read-only yes + +# Replication SYNC strategy: disk or socket. +# +# New replicas and reconnecting replicas that are not able to continue the +# replication process just receiving differences, need to do what is called a +# "full synchronization". An RDB file is transmitted from the master to the +# replicas. +# +# The transmission can happen in two different ways: +# +# 1) Disk-backed: The Redis master creates a new process that writes the RDB +# file on disk. Later the file is transferred by the parent +# process to the replicas incrementally. +# 2) Diskless: The Redis master creates a new process that directly writes the +# RDB file to replica sockets, without touching the disk at all. +# +# With disk-backed replication, while the RDB file is generated, more replicas +# can be queued and served with the RDB file as soon as the current child +# producing the RDB file finishes its work. With diskless replication instead +# once the transfer starts, new replicas arriving will be queued and a new +# transfer will start when the current one terminates. +# +# When diskless replication is used, the master waits a configurable amount of +# time (in seconds) before starting the transfer in the hope that multiple +# replicas will arrive and the transfer can be parallelized. +# +# With slow disks and fast (large bandwidth) networks, diskless replication +# works better. +repl-diskless-sync no + +# When diskless replication is enabled, it is possible to configure the delay +# the server waits in order to spawn the child that transfers the RDB via socket +# to the replicas. +# +# This is important since once the transfer starts, it is not possible to serve +# new replicas arriving, that will be queued for the next RDB transfer, so the +# server waits a delay in order to let more replicas arrive. +# +# The delay is specified in seconds, and by default is 5 seconds. To disable +# it entirely just set it to 0 seconds and the transfer will start ASAP. +repl-diskless-sync-delay 5 + +# ----------------------------------------------------------------------------- +# WARNING: RDB diskless load is experimental. Since in this setup the replica +# does not immediately store an RDB on disk, it may cause data loss during +# failovers. RDB diskless load + Redis modules not handling I/O reads may also +# cause Redis to abort in case of I/O errors during the initial synchronization +# stage with the master. Use only if your do what you are doing. +# ----------------------------------------------------------------------------- +# +# Replica can load the RDB it reads from the replication link directly from the +# socket, or store the RDB to a file and read that file after it was completely +# received from the master. +# +# In many cases the disk is slower than the network, and storing and loading +# the RDB file may increase replication time (and even increase the master's +# Copy on Write memory and salve buffers). +# However, parsing the RDB file directly from the socket may mean that we have +# to flush the contents of the current database before the full rdb was +# received. For this reason we have the following options: +# +# "disabled" - Don't use diskless load (store the rdb file to the disk first) +# "on-empty-db" - Use diskless load only when it is completely safe. +# "swapdb" - Keep a copy of the current db contents in RAM while parsing +# the data directly from the socket. note that this requires +# sufficient memory, if you don't have it, you risk an OOM kill. +repl-diskless-load disabled + +# Replicas send PINGs to server in a predefined interval. It's possible to +# change this interval with the repl_ping_replica_period option. The default +# value is 10 seconds. +# +# repl-ping-replica-period 10 + +# The following option sets the replication timeout for: +# +# 1) Bulk transfer I/O during SYNC, from the point of view of replica. +# 2) Master timeout from the point of view of replicas (data, pings). +# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings). +# +# It is important to make sure that this value is greater than the value +# specified for repl-ping-replica-period otherwise a timeout will be detected +# every time there is low traffic between the master and the replica. The default +# value is 60 seconds. +# +# repl-timeout 60 + +# Disable TCP_NODELAY on the replica socket after SYNC? +# +# If you select "yes" Redis will use a smaller number of TCP packets and +# less bandwidth to send data to replicas. But this can add a delay for +# the data to appear on the replica side, up to 40 milliseconds with +# Linux kernels using a default configuration. +# +# If you select "no" the delay for data to appear on the replica side will +# be reduced but more bandwidth will be used for replication. +# +# By default we optimize for low latency, but in very high traffic conditions +# or when the master and replicas are many hops away, turning this to "yes" may +# be a good idea. +repl-disable-tcp-nodelay no + +# Set the replication backlog size. The backlog is a buffer that accumulates +# replica data when replicas are disconnected for some time, so that when a +# replica wants to reconnect again, often a full resync is not needed, but a +# partial resync is enough, just passing the portion of data the replica +# missed while disconnected. +# +# The bigger the replication backlog, the longer the replica can endure the +# disconnect and later be able to perform a partial resynchronization. +# +# The backlog is only allocated if there is at least one replica connected. +# +# repl-backlog-size 1mb + +# After a master has no connected replicas for some time, the backlog will be +# freed. The following option configures the amount of seconds that need to +# elapse, starting from the time the last replica disconnected, for the backlog +# buffer to be freed. +# +# Note that replicas never free the backlog for timeout, since they may be +# promoted to masters later, and should be able to correctly "partially +# resynchronize" with other replicas: hence they should always accumulate backlog. +# +# A value of 0 means to never release the backlog. +# +# repl-backlog-ttl 3600 + +# The replica priority is an integer number published by Redis in the INFO +# output. It is used by Redis Sentinel in order to select a replica to promote +# into a master if the master is no longer working correctly. +# +# A replica with a low priority number is considered better for promotion, so +# for instance if there are three replicas with priority 10, 100, 25 Sentinel +# will pick the one with priority 10, that is the lowest. +# +# However a special priority of 0 marks the replica as not able to perform the +# role of master, so a replica with priority of 0 will never be selected by +# Redis Sentinel for promotion. +# +# By default the priority is 100. +replica-priority 100 + +# It is possible for a master to stop accepting writes if there are less than +# N replicas connected, having a lag less or equal than M seconds. +# +# The N replicas need to be in "online" state. +# +# The lag in seconds, that must be <= the specified value, is calculated from +# the last ping received from the replica, that is usually sent every second. +# +# This option does not GUARANTEE that N replicas will accept the write, but +# will limit the window of exposure for lost writes in case not enough replicas +# are available, to the specified number of seconds. +# +# For example to require at least 3 replicas with a lag <= 10 seconds use: +# +# min-replicas-to-write 3 +# min-replicas-max-lag 10 +# +# Setting one or the other to 0 disables the feature. +# +# By default min-replicas-to-write is set to 0 (feature disabled) and +# min-replicas-max-lag is set to 10. + +# A Redis master is able to list the address and port of the attached +# replicas in different ways. For example the "INFO replication" section +# offers this information, which is used, among other tools, by +# Redis Sentinel in order to discover replica instances. +# Another place where this info is available is in the output of the +# "ROLE" command of a master. +# +# The listed IP address and port normally reported by a replica is +# obtained in the following way: +# +# IP: The address is auto detected by checking the peer address +# of the socket used by the replica to connect with the master. +# +# Port: The port is communicated by the replica during the replication +# handshake, and is normally the port that the replica is using to +# listen for connections. +# +# However when port forwarding or Network Address Translation (NAT) is +# used, the replica may actually be reachable via different IP and port +# pairs. The following two options can be used by a replica in order to +# report to its master a specific set of IP and port, so that both INFO +# and ROLE will report those values. +# +# There is no need to use both the options if you need to override just +# the port or the IP address. +# +# replica-announce-ip 5.5.5.5 +# replica-announce-port 1234 + +############################### KEYS TRACKING ################################# + +# Redis implements server assisted support for client side caching of values. +# This is implemented using an invalidation table that remembers, using +# 16 millions of slots, what clients may have certain subsets of keys. In turn +# this is used in order to send invalidation messages to clients. Please +# check this page to understand more about the feature: +# +# https://redis.io/topics/client-side-caching +# +# When tracking is enabled for a client, all the read only queries are assumed +# to be cached: this will force Redis to store information in the invalidation +# table. When keys are modified, such information is flushed away, and +# invalidation messages are sent to the clients. However if the workload is +# heavily dominated by reads, Redis could use more and more memory in order +# to track the keys fetched by many clients. +# +# For this reason it is possible to configure a maximum fill value for the +# invalidation table. By default it is set to 1M of keys, and once this limit +# is reached, Redis will start to evict keys in the invalidation table +# even if they were not modified, just to reclaim memory: this will in turn +# force the clients to invalidate the cached values. Basically the table +# maximum size is a trade off between the memory you want to spend server +# side to track information about who cached what, and the ability of clients +# to retain cached objects in memory. +# +# If you set the value to 0, it means there are no limits, and Redis will +# retain as many keys as needed in the invalidation table. +# In the "stats" INFO section, you can find information about the number of +# keys in the invalidation table at every given moment. +# +# Note: when key tracking is used in broadcasting mode, no memory is used +# in the server side so this setting is useless. +# +# tracking-table-max-keys 1000000 + +################################## SECURITY ################################### + +# Warning: since Redis is pretty fast, an outside user can try up to +# 1 million passwords per second against a modern box. This means that you +# should use very strong passwords, otherwise they will be very easy to break. +# Note that because the password is really a shared secret between the client +# and the server, and should not be memorized by any human, the password +# can be easily a long string from /dev/urandom or whatever, so by using a +# long and unguessable password no brute force attack will be possible. + +# Redis ACL users are defined in the following format: +# +# user ... acl rules ... +# +# For example: +# +# user worker +@list +@connection ~jobs:* on >ffa9203c493aa99 +# +# The special username "default" is used for new connections. If this user +# has the "nopass" rule, then new connections will be immediately authenticated +# as the "default" user without the need of any password provided via the +# AUTH command. Otherwise if the "default" user is not flagged with "nopass" +# the connections will start in not authenticated state, and will require +# AUTH (or the HELLO command AUTH option) in order to be authenticated and +# start to work. +# +# The ACL rules that describe what a user can do are the following: +# +# on Enable the user: it is possible to authenticate as this user. +# off Disable the user: it's no longer possible to authenticate +# with this user, however the already authenticated connections +# will still work. +# + Allow the execution of that command +# - Disallow the execution of that command +# +@ Allow the execution of all the commands in such category +# with valid categories are like @admin, @set, @sortedset, ... +# and so forth, see the full list in the server.c file where +# the Redis command table is described and defined. +# The special category @all means all the commands, but currently +# present in the server, and that will be loaded in the future +# via modules. +# +|subcommand Allow a specific subcommand of an otherwise +# disabled command. Note that this form is not +# allowed as negative like -DEBUG|SEGFAULT, but +# only additive starting with "+". +# allcommands Alias for +@all. Note that it implies the ability to execute +# all the future commands loaded via the modules system. +# nocommands Alias for -@all. +# ~ Add a pattern of keys that can be mentioned as part of +# commands. For instance ~* allows all the keys. The pattern +# is a glob-style pattern like the one of KEYS. +# It is possible to specify multiple patterns. +# allkeys Alias for ~* +# resetkeys Flush the list of allowed keys patterns. +# > Add this password to the list of valid password for the user. +# For example >mypass will add "mypass" to the list. +# This directive clears the "nopass" flag (see later). +# < Remove this password from the list of valid passwords. +# nopass All the set passwords of the user are removed, and the user +# is flagged as requiring no password: it means that every +# password will work against this user. If this directive is +# used for the default user, every new connection will be +# immediately authenticated with the default user without +# any explicit AUTH command required. Note that the "resetpass" +# directive will clear this condition. +# resetpass Flush the list of allowed passwords. Moreover removes the +# "nopass" status. After "resetpass" the user has no associated +# passwords and there is no way to authenticate without adding +# some password (or setting it as "nopass" later). +# reset Performs the following actions: resetpass, resetkeys, off, +# -@all. The user returns to the same state it has immediately +# after its creation. +# +# ACL rules can be specified in any order: for instance you can start with +# passwords, then flags, or key patterns. However note that the additive +# and subtractive rules will CHANGE MEANING depending on the ordering. +# For instance see the following example: +# +# user alice on +@all -DEBUG ~* >somepassword +# +# This will allow "alice" to use all the commands with the exception of the +# DEBUG command, since +@all added all the commands to the set of the commands +# alice can use, and later DEBUG was removed. However if we invert the order +# of two ACL rules the result will be different: +# +# user alice on -DEBUG +@all ~* >somepassword +# +# Now DEBUG was removed when alice had yet no commands in the set of allowed +# commands, later all the commands are added, so the user will be able to +# execute everything. +# +# Basically ACL rules are processed left-to-right. +# +# For more information about ACL configuration please refer to +# the Redis web site at https://redis.io/topics/acl + +# ACL LOG +# +# The ACL Log tracks failed commands and authentication events associated +# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked +# by ACLs. The ACL Log is stored in memory. You can reclaim memory with +# ACL LOG RESET. Define the maximum entry length of the ACL Log below. +acllog-max-len 128 + +# Using an external ACL file +# +# Instead of configuring users here in this file, it is possible to use +# a stand-alone file just listing users. The two methods cannot be mixed: +# if you configure users here and at the same time you activate the external +# ACL file, the server will refuse to start. +# +# The format of the external ACL user file is exactly the same as the +# format that is used inside redis.conf to describe users. +# +# aclfile /etc/redis/users.acl + +# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatibility +# layer on top of the new ACL system. The option effect will be just setting +# the password for the default user. Clients will still authenticate using +# AUTH as usually, or more explicitly with AUTH default +# if they follow the new protocol: both will work. +# +# requirepass foobared + +# Command renaming (DEPRECATED). +# +# ------------------------------------------------------------------------ +# WARNING: avoid using this option if possible. Instead use ACLs to remove +# commands from the default user, and put them only in some admin user you +# create for administrative purposes. +# ------------------------------------------------------------------------ +# +# It is possible to change the name of dangerous commands in a shared +# environment. For instance the CONFIG command may be renamed into something +# hard to guess so that it will still be available for internal-use tools +# but not available for general clients. +# +# Example: +# +# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 +# +# It is also possible to completely kill a command by renaming it into +# an empty string: +# +# rename-command CONFIG "" +# +# Please note that changing the name of commands that are logged into the +# AOF file or transmitted to replicas may cause problems. + +################################### CLIENTS #################################### + +# Set the max number of connected clients at the same time. By default +# this limit is set to 10000 clients, however if the Redis server is not +# able to configure the process file limit to allow for the specified limit +# the max number of allowed clients is set to the current file limit +# minus 32 (as Redis reserves a few file descriptors for internal uses). +# +# Once the limit is reached Redis will close all the new connections sending +# an error 'max number of clients reached'. +# +# IMPORTANT: When Redis Cluster is used, the max number of connections is also +# shared with the cluster bus: every node in the cluster will use two +# connections, one incoming and another outgoing. It is important to size the +# limit accordingly in case of very large clusters. +# +# maxclients 10000 + +############################## MEMORY MANAGEMENT ################################ + +# Set a memory usage limit to the specified amount of bytes. +# When the memory limit is reached Redis will try to remove keys +# according to the eviction policy selected (see maxmemory-policy). +# +# If Redis can't remove keys according to the policy, or if the policy is +# set to 'noeviction', Redis will start to reply with errors to commands +# that would use more memory, like SET, LPUSH, and so on, and will continue +# to reply to read-only commands like GET. +# +# This option is usually useful when using Redis as an LRU or LFU cache, or to +# set a hard memory limit for an instance (using the 'noeviction' policy). +# +# WARNING: If you have replicas attached to an instance with maxmemory on, +# the size of the output buffers needed to feed the replicas are subtracted +# from the used memory count, so that network problems / resyncs will +# not trigger a loop where keys are evicted, and in turn the output +# buffer of replicas is full with DELs of keys evicted triggering the deletion +# of more keys, and so forth until the database is completely emptied. +# +# In short... if you have replicas attached it is suggested that you set a lower +# limit for maxmemory so that there is some free RAM on the system for replica +# output buffers (but this is not needed if the policy is 'noeviction'). +# +# maxmemory + +# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory +# is reached. You can select one from the following behaviors: +# +# volatile-lru -> Evict using approximated LRU, only keys with an expire set. +# allkeys-lru -> Evict any key using approximated LRU. +# volatile-lfu -> Evict using approximated LFU, only keys with an expire set. +# allkeys-lfu -> Evict any key using approximated LFU. +# volatile-random -> Remove a random key having an expire set. +# allkeys-random -> Remove a random key, any key. +# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) +# noeviction -> Don't evict anything, just return an error on write operations. +# +# LRU means Least Recently Used +# LFU means Least Frequently Used +# +# Both LRU, LFU and volatile-ttl are implemented using approximated +# randomized algorithms. +# +# Note: with any of the above policies, Redis will return an error on write +# operations, when there are no suitable keys for eviction. +# +# At the date of writing these commands are: set setnx setex append +# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd +# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby +# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby +# getset mset msetnx exec sort +# +# The default is: +# +# maxmemory-policy noeviction + +# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated +# algorithms (in order to save memory), so you can tune it for speed or +# accuracy. By default Redis will check five keys and pick the one that was +# used least recently, you can change the sample size using the following +# configuration directive. +# +# The default of 5 produces good enough results. 10 Approximates very closely +# true LRU but costs more CPU. 3 is faster but not very accurate. +# +# maxmemory-samples 5 + +# Starting from Redis 5, by default a replica will ignore its maxmemory setting +# (unless it is promoted to master after a failover or manually). It means +# that the eviction of keys will be just handled by the master, sending the +# DEL commands to the replica as keys evict in the master side. +# +# This behavior ensures that masters and replicas stay consistent, and is usually +# what you want, however if your replica is writable, or you want the replica +# to have a different memory setting, and you are sure all the writes performed +# to the replica are idempotent, then you may change this default (but be sure +# to understand what you are doing). +# +# Note that since the replica by default does not evict, it may end using more +# memory than the one set via maxmemory (there are certain buffers that may +# be larger on the replica, or data structures may sometimes take more memory +# and so forth). So make sure you monitor your replicas and make sure they +# have enough memory to never hit a real out-of-memory condition before the +# master hits the configured maxmemory setting. +# +# replica-ignore-maxmemory yes + +# Redis reclaims expired keys in two ways: upon access when those keys are +# found to be expired, and also in background, in what is called the +# "active expire key". The key space is slowly and interactively scanned +# looking for expired keys to reclaim, so that it is possible to free memory +# of keys that are expired and will never be accessed again in a short time. +# +# The default effort of the expire cycle will try to avoid having more than +# ten percent of expired keys still in memory, and will try to avoid consuming +# more than 25% of total memory and to add latency to the system. However +# it is possible to increase the expire "effort" that is normally set to +# "1", to a greater value, up to the value "10". At its maximum value the +# system will use more CPU, longer cycles (and technically may introduce +# more latency), and will tolerate less already expired keys still present +# in the system. It's a tradeoff between memory, CPU and latency. +# +# active-expire-effort 1 + +############################# LAZY FREEING #################################### + +# Redis has two primitives to delete keys. One is called DEL and is a blocking +# deletion of the object. It means that the server stops processing new commands +# in order to reclaim all the memory associated with an object in a synchronous +# way. If the key deleted is associated with a small object, the time needed +# in order to execute the DEL command is very small and comparable to most other +# O(1) or O(log_N) commands in Redis. However if the key is associated with an +# aggregated value containing millions of elements, the server can block for +# a long time (even seconds) in order to complete the operation. +# +# For the above reasons Redis also offers non blocking deletion primitives +# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and +# FLUSHDB commands, in order to reclaim memory in background. Those commands +# are executed in constant time. Another thread will incrementally free the +# object in the background as fast as possible. +# +# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. +# It's up to the design of the application to understand when it is a good +# idea to use one or the other. However the Redis server sometimes has to +# delete keys or flush the whole database as a side effect of other operations. +# Specifically Redis deletes objects independently of a user call in the +# following scenarios: +# +# 1) On eviction, because of the maxmemory and maxmemory policy configurations, +# in order to make room for new data, without going over the specified +# memory limit. +# 2) Because of expire: when a key with an associated time to live (see the +# EXPIRE command) must be deleted from memory. +# 3) Because of a side effect of a command that stores data on a key that may +# already exist. For example the RENAME command may delete the old key +# content when it is replaced with another one. Similarly SUNIONSTORE +# or SORT with STORE option may delete existing keys. The SET command +# itself removes any old content of the specified key in order to replace +# it with the specified string. +# 4) During replication, when a replica performs a full resynchronization with +# its master, the content of the whole database is removed in order to +# load the RDB file just transferred. +# +# In all the above cases the default is to delete objects in a blocking way, +# like if DEL was called. However you can configure each case specifically +# in order to instead release memory in a non-blocking way like if UNLINK +# was called, using the following configuration directives. + +lazyfree-lazy-eviction no +lazyfree-lazy-expire no +lazyfree-lazy-server-del no +replica-lazy-flush no + +# It is also possible, for the case when to replace the user code DEL calls +# with UNLINK calls is not easy, to modify the default behavior of the DEL +# command to act exactly like UNLINK, using the following configuration +# directive: + +lazyfree-lazy-user-del no + +################################ THREADED I/O ################################# + +# Redis is mostly single threaded, however there are certain threaded +# operations such as UNLINK, slow I/O accesses and other things that are +# performed on side threads. +# +# Now it is also possible to handle Redis clients socket reads and writes +# in different I/O threads. Since especially writing is so slow, normally +# Redis users use pipelining in order to speed up the Redis performances per +# core, and spawn multiple instances in order to scale more. Using I/O +# threads it is possible to easily speedup two times Redis without resorting +# to pipelining nor sharding of the instance. +# +# By default threading is disabled, we suggest enabling it only in machines +# that have at least 4 or more cores, leaving at least one spare core. +# Using more than 8 threads is unlikely to help much. We also recommend using +# threaded I/O only if you actually have performance problems, with Redis +# instances being able to use a quite big percentage of CPU time, otherwise +# there is no point in using this feature. +# +# So for instance if you have a four cores boxes, try to use 2 or 3 I/O +# threads, if you have a 8 cores, try to use 6 threads. In order to +# enable I/O threads use the following configuration directive: +# +# io-threads 4 +# +# Setting io-threads to 1 will just use the main thread as usual. +# When I/O threads are enabled, we only use threads for writes, that is +# to thread the write(2) syscall and transfer the client buffers to the +# socket. However it is also possible to enable threading of reads and +# protocol parsing using the following configuration directive, by setting +# it to yes: +# +# io-threads-do-reads no +# +# Usually threading reads doesn't help much. +# +# NOTE 1: This configuration directive cannot be changed at runtime via +# CONFIG SET. Aso this feature currently does not work when SSL is +# enabled. +# +# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make +# sure you also run the benchmark itself in threaded mode, using the +# --threads option to match the number of Redis threads, otherwise you'll not +# be able to notice the improvements. + +############################ KERNEL OOM CONTROL ############################## + +# On Linux, it is possible to hint the kernel OOM killer on what processes +# should be killed first when out of memory. +# +# Enabling this feature makes Redis actively control the oom_score_adj value +# for all its processes, depending on their role. The default scores will +# attempt to have background child processes killed before all others, and +# replicas killed before masters. +# +# Redis supports three options: +# +# no: Don't make changes to oom-score-adj (default). +# yes: Alias to "relative" see below. +# absolute: Values in oom-score-adj-values are written as is to the kernel. +# relative: Values are used relative to the initial value of oom_score_adj when +# the server starts and are then clamped to a range of -1000 to 1000. +# Because typically the initial value is 0, they will often match the +# absolute values. +oom-score-adj no + +# When oom-score-adj is used, this directive controls the specific values used +# for master, replica and background child processes. Values range -2000 to +# 2000 (higher means more likely to be killed). +# +# Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities) +# can freely increase their value, but not decrease it below its initial +# settings. This means that setting oom-score-adj to "relative" and setting the +# oom-score-adj-values to positive values will always succeed. +oom-score-adj-values 0 200 800 + +############################## APPEND ONLY MODE ############################### + +# By default Redis asynchronously dumps the dataset on disk. This mode is +# good enough in many applications, but an issue with the Redis process or +# a power outage may result into a few minutes of writes lost (depending on +# the configured save points). +# +# The Append Only File is an alternative persistence mode that provides +# much better durability. For instance using the default data fsync policy +# (see later in the config file) Redis can lose just one second of writes in a +# dramatic event like a server power outage, or a single write if something +# wrong with the Redis process itself happens, but the operating system is +# still running correctly. +# +# AOF and RDB persistence can be enabled at the same time without problems. +# If the AOF is enabled on startup Redis will load the AOF, that is the file +# with the better durability guarantees. +# +# Please check http://redis.io/topics/persistence for more information. + +appendonly no + +# The name of the append only file (default: "appendonly.aof") + +appendfilename "appendonly.aof" + +# The fsync() call tells the Operating System to actually write data on disk +# instead of waiting for more data in the output buffer. Some OS will really flush +# data on disk, some other OS will just try to do it ASAP. +# +# Redis supports three different modes: +# +# no: don't fsync, just let the OS flush the data when it wants. Faster. +# always: fsync after every write to the append only log. Slow, Safest. +# everysec: fsync only one time every second. Compromise. +# +# The default is "everysec", as that's usually the right compromise between +# speed and data safety. It's up to you to understand if you can relax this to +# "no" that will let the operating system flush the output buffer when +# it wants, for better performances (but if you can live with the idea of +# some data loss consider the default persistence mode that's snapshotting), +# or on the contrary, use "always" that's very slow but a bit safer than +# everysec. +# +# More details please check the following article: +# http://antirez.com/post/redis-persistence-demystified.html +# +# If unsure, use "everysec". + +# appendfsync always +appendfsync everysec +# appendfsync no + +# When the AOF fsync policy is set to always or everysec, and a background +# saving process (a background save or AOF log background rewriting) is +# performing a lot of I/O against the disk, in some Linux configurations +# Redis may block too long on the fsync() call. Note that there is no fix for +# this currently, as even performing fsync in a different thread will block +# our synchronous write(2) call. +# +# In order to mitigate this problem it's possible to use the following option +# that will prevent fsync() from being called in the main process while a +# BGSAVE or BGREWRITEAOF is in progress. +# +# This means that while another child is saving, the durability of Redis is +# the same as "appendfsync none". In practical terms, this means that it is +# possible to lose up to 30 seconds of log in the worst scenario (with the +# default Linux settings). +# +# If you have latency problems turn this to "yes". Otherwise leave it as +# "no" that is the safest pick from the point of view of durability. + +no-appendfsync-on-rewrite no + +# Automatic rewrite of the append only file. +# Redis is able to automatically rewrite the log file implicitly calling +# BGREWRITEAOF when the AOF log size grows by the specified percentage. +# +# This is how it works: Redis remembers the size of the AOF file after the +# latest rewrite (if no rewrite has happened since the restart, the size of +# the AOF at startup is used). +# +# This base size is compared to the current size. If the current size is +# bigger than the specified percentage, the rewrite is triggered. Also +# you need to specify a minimal size for the AOF file to be rewritten, this +# is useful to avoid rewriting the AOF file even if the percentage increase +# is reached but it is still pretty small. +# +# Specify a percentage of zero in order to disable the automatic AOF +# rewrite feature. + +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# An AOF file may be found to be truncated at the end during the Redis +# startup process, when the AOF data gets loaded back into memory. +# This may happen when the system where Redis is running +# crashes, especially when an ext4 filesystem is mounted without the +# data=ordered option (however this can't happen when Redis itself +# crashes or aborts but the operating system still works correctly). +# +# Redis can either exit with an error when this happens, or load as much +# data as possible (the default now) and start if the AOF file is found +# to be truncated at the end. The following option controls this behavior. +# +# If aof-load-truncated is set to yes, a truncated AOF file is loaded and +# the Redis server starts emitting a log to inform the user of the event. +# Otherwise if the option is set to no, the server aborts with an error +# and refuses to start. When the option is set to no, the user requires +# to fix the AOF file using the "redis-check-aof" utility before to restart +# the server. +# +# Note that if the AOF file will be found to be corrupted in the middle +# the server will still exit with an error. This option only applies when +# Redis will try to read more data from the AOF file but not enough bytes +# will be found. +aof-load-truncated yes + +# When rewriting the AOF file, Redis is able to use an RDB preamble in the +# AOF file for faster rewrites and recoveries. When this option is turned +# on the rewritten AOF file is composed of two different stanzas: +# +# [RDB file][AOF tail] +# +# When loading, Redis recognizes that the AOF file starts with the "REDIS" +# string and loads the prefixed RDB file, then continues loading the AOF +# tail. +aof-use-rdb-preamble yes + +################################ LUA SCRIPTING ############################### + +# Max execution time of a Lua script in milliseconds. +# +# If the maximum execution time is reached Redis will log that a script is +# still in execution after the maximum allowed time and will start to +# reply to queries with an error. +# +# When a long running script exceeds the maximum execution time only the +# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be +# used to stop a script that did not yet call any write commands. The second +# is the only way to shut down the server in the case a write command was +# already issued by the script but the user doesn't want to wait for the natural +# termination of the script. +# +# Set it to 0 or a negative value for unlimited execution without warnings. +lua-time-limit 5000 + +################################ REDIS CLUSTER ############################### + +# Normal Redis instances can't be part of a Redis Cluster; only nodes that are +# started as cluster nodes can. In order to start a Redis instance as a +# cluster node enable the cluster support uncommenting the following: +# +# cluster-enabled yes + +# Every cluster node has a cluster configuration file. This file is not +# intended to be edited by hand. It is created and updated by Redis nodes. +# Every Redis Cluster node requires a different cluster configuration file. +# Make sure that instances running in the same system do not have +# overlapping cluster configuration file names. +# +# cluster-config-file nodes-6379.conf + +# Cluster node timeout is the amount of milliseconds a node must be unreachable +# for it to be considered in failure state. +# Most other internal time limits are a multiple of the node timeout. +# +# cluster-node-timeout 15000 + +# A replica of a failing master will avoid to start a failover if its data +# looks too old. +# +# There is no simple way for a replica to actually have an exact measure of +# its "data age", so the following two checks are performed: +# +# 1) If there are multiple replicas able to failover, they exchange messages +# in order to try to give an advantage to the replica with the best +# replication offset (more data from the master processed). +# Replicas will try to get their rank by offset, and apply to the start +# of the failover a delay proportional to their rank. +# +# 2) Every single replica computes the time of the last interaction with +# its master. This can be the last ping or command received (if the master +# is still in the "connected" state), or the time that elapsed since the +# disconnection with the master (if the replication link is currently down). +# If the last interaction is too old, the replica will not try to failover +# at all. +# +# The point "2" can be tuned by user. Specifically a replica will not perform +# the failover if, since the last interaction with the master, the time +# elapsed is greater than: +# +# (node-timeout * cluster-replica-validity-factor) + repl-ping-replica-period +# +# So for example if node-timeout is 30 seconds, and the cluster-replica-validity-factor +# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the +# replica will not try to failover if it was not able to talk with the master +# for longer than 310 seconds. +# +# A large cluster-replica-validity-factor may allow replicas with too old data to failover +# a master, while a too small value may prevent the cluster from being able to +# elect a replica at all. +# +# For maximum availability, it is possible to set the cluster-replica-validity-factor +# to a value of 0, which means, that replicas will always try to failover the +# master regardless of the last time they interacted with the master. +# (However they'll always try to apply a delay proportional to their +# offset rank). +# +# Zero is the only value able to guarantee that when all the partitions heal +# the cluster will always be able to continue. +# +# cluster-replica-validity-factor 10 + +# Cluster replicas are able to migrate to orphaned masters, that are masters +# that are left without working replicas. This improves the cluster ability +# to resist to failures as otherwise an orphaned master can't be failed over +# in case of failure if it has no working replicas. +# +# Replicas migrate to orphaned masters only if there are still at least a +# given number of other working replicas for their old master. This number +# is the "migration barrier". A migration barrier of 1 means that a replica +# will migrate only if there is at least 1 other working replica for its master +# and so forth. It usually reflects the number of replicas you want for every +# master in your cluster. +# +# Default is 1 (replicas migrate only if their masters remain with at least +# one replica). To disable migration just set it to a very large value. +# A value of 0 can be set but is useful only for debugging and dangerous +# in production. +# +# cluster-migration-barrier 1 + +# By default Redis Cluster nodes stop accepting queries if they detect there +# is at least a hash slot uncovered (no available node is serving it). +# This way if the cluster is partially down (for example a range of hash slots +# are no longer covered) all the cluster becomes, eventually, unavailable. +# It automatically returns available as soon as all the slots are covered again. +# +# However sometimes you want the subset of the cluster which is working, +# to continue to accept queries for the part of the key space that is still +# covered. In order to do so, just set the cluster-require-full-coverage +# option to no. +# +# cluster-require-full-coverage yes + +# This option, when set to yes, prevents replicas from trying to failover its +# master during master failures. However the master can still perform a +# manual failover, if forced to do so. +# +# This is useful in different scenarios, especially in the case of multiple +# data center operations, where we want one side to never be promoted if not +# in the case of a total DC failure. +# +# cluster-replica-no-failover no + +# This option, when set to yes, allows nodes to serve read traffic while the +# the cluster is in a down state, as long as it believes it owns the slots. +# +# This is useful for two cases. The first case is for when an application +# doesn't require consistency of data during node failures or network partitions. +# One example of this is a cache, where as long as the node has the data it +# should be able to serve it. +# +# The second use case is for configurations that don't meet the recommended +# three shards but want to enable cluster mode and scale later. A +# master outage in a 1 or 2 shard configuration causes a read/write outage to the +# entire cluster without this option set, with it set there is only a write outage. +# Without a quorum of masters, slot ownership will not change automatically. +# +# cluster-allow-reads-when-down no + +# In order to setup your cluster make sure to read the documentation +# available at http://redis.io web site. + +########################## CLUSTER DOCKER/NAT support ######################## + +# In certain deployments, Redis Cluster nodes address discovery fails, because +# addresses are NAT-ted or because ports are forwarded (the typical case is +# Docker and other containers). +# +# In order to make Redis Cluster working in such environments, a static +# configuration where each node knows its public address is needed. The +# following two options are used for this scope, and are: +# +# * cluster-announce-ip +# * cluster-announce-port +# * cluster-announce-bus-port +# +# Each instructs the node about its address, client port, and cluster message +# bus port. The information is then published in the header of the bus packets +# so that other nodes will be able to correctly map the address of the node +# publishing the information. +# +# If the above options are not used, the normal Redis Cluster auto-detection +# will be used instead. +# +# Note that when remapped, the bus port may not be at the fixed offset of +# clients port + 10000, so you can specify any port and bus-port depending +# on how they get remapped. If the bus-port is not set, a fixed offset of +# 10000 will be used as usual. +# +# Example: +# +# cluster-announce-ip 10.1.1.5 +# cluster-announce-port 6379 +# cluster-announce-bus-port 6380 + +################################## SLOW LOG ################################### + +# The Redis Slow Log is a system to log queries that exceeded a specified +# execution time. The execution time does not include the I/O operations +# like talking with the client, sending the reply and so forth, +# but just the time needed to actually execute the command (this is the only +# stage of command execution where the thread is blocked and can not serve +# other requests in the meantime). +# +# You can configure the slow log with two parameters: one tells Redis +# what is the execution time, in microseconds, to exceed in order for the +# command to get logged, and the other parameter is the length of the +# slow log. When a new command is logged the oldest one is removed from the +# queue of logged commands. + +# The following time is expressed in microseconds, so 1000000 is equivalent +# to one second. Note that a negative number disables the slow log, while +# a value of zero forces the logging of every command. +slowlog-log-slower-than 10000 + +# There is no limit to this length. Just be aware that it will consume memory. +# You can reclaim memory used by the slow log with SLOWLOG RESET. +slowlog-max-len 128 + +################################ LATENCY MONITOR ############################## + +# The Redis latency monitoring subsystem samples different operations +# at runtime in order to collect data related to possible sources of +# latency of a Redis instance. +# +# Via the LATENCY command this information is available to the user that can +# print graphs and obtain reports. +# +# The system only logs operations that were performed in a time equal or +# greater than the amount of milliseconds specified via the +# latency-monitor-threshold configuration directive. When its value is set +# to zero, the latency monitor is turned off. +# +# By default latency monitoring is disabled since it is mostly not needed +# if you don't have latency issues, and collecting data has a performance +# impact, that while very small, can be measured under big load. Latency +# monitoring can easily be enabled at runtime using the command +# "CONFIG SET latency-monitor-threshold " if needed. +latency-monitor-threshold 0 + +############################# EVENT NOTIFICATION ############################## + +# Redis can notify Pub/Sub clients about events happening in the key space. +# This feature is documented at http://redis.io/topics/notifications +# +# For instance if keyspace events notification is enabled, and a client +# performs a DEL operation on key "foo" stored in the Database 0, two +# messages will be published via Pub/Sub: +# +# PUBLISH __keyspace@0__:foo del +# PUBLISH __keyevent@0__:del foo +# +# It is possible to select the events that Redis will notify among a set +# of classes. Every class is identified by a single character: +# +# K Keyspace events, published with __keyspace@__ prefix. +# E Keyevent events, published with __keyevent@__ prefix. +# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... +# $ String commands +# l List commands +# s Set commands +# h Hash commands +# z Sorted set commands +# x Expired events (events generated every time a key expires) +# e Evicted events (events generated when a key is evicted for maxmemory) +# t Stream commands +# m Key-miss events (Note: It is not included in the 'A' class) +# A Alias for g$lshzxet, so that the "AKE" string means all the events +# (Except key-miss events which are excluded from 'A' due to their +# unique nature). +# +# The "notify-keyspace-events" takes as argument a string that is composed +# of zero or multiple characters. The empty string means that notifications +# are disabled. +# +# Example: to enable list and generic events, from the point of view of the +# event name, use: +# +# notify-keyspace-events Elg +# +# Example 2: to get the stream of the expired keys subscribing to channel +# name __keyevent@0__:expired use: +# +# notify-keyspace-events Ex +# +# By default all notifications are disabled because most users don't need +# this feature and the feature has some overhead. Note that if you don't +# specify at least one of K or E, no events will be delivered. +notify-keyspace-events "" + +############################### GOPHER SERVER ################################# + +# Redis contains an implementation of the Gopher protocol, as specified in +# the RFC 1436 (https://www.ietf.org/rfc/rfc1436.txt). +# +# The Gopher protocol was very popular in the late '90s. It is an alternative +# to the web, and the implementation both server and client side is so simple +# that the Redis server has just 100 lines of code in order to implement this +# support. +# +# What do you do with Gopher nowadays? Well Gopher never *really* died, and +# lately there is a movement in order for the Gopher more hierarchical content +# composed of just plain text documents to be resurrected. Some want a simpler +# internet, others believe that the mainstream internet became too much +# controlled, and it's cool to create an alternative space for people that +# want a bit of fresh air. +# +# Anyway for the 10nth birthday of the Redis, we gave it the Gopher protocol +# as a gift. +# +# --- HOW IT WORKS? --- +# +# The Redis Gopher support uses the inline protocol of Redis, and specifically +# two kind of inline requests that were anyway illegal: an empty request +# or any request that starts with "/" (there are no Redis commands starting +# with such a slash). Normal RESP2/RESP3 requests are completely out of the +# path of the Gopher protocol implementation and are served as usual as well. +# +# If you open a connection to Redis when Gopher is enabled and send it +# a string like "/foo", if there is a key named "/foo" it is served via the +# Gopher protocol. +# +# In order to create a real Gopher "hole" (the name of a Gopher site in Gopher +# talking), you likely need a script like the following: +# +# https://github.com/antirez/gopher2redis +# +# --- SECURITY WARNING --- +# +# If you plan to put Redis on the internet in a publicly accessible address +# to server Gopher pages MAKE SURE TO SET A PASSWORD to the instance. +# Once a password is set: +# +# 1. The Gopher server (when enabled, not by default) will still serve +# content via Gopher. +# 2. However other commands cannot be called before the client will +# authenticate. +# +# So use the 'requirepass' option to protect your instance. +# +# Note that Gopher is not currently supported when 'io-threads-do-reads' +# is enabled. +# +# To enable Gopher support, uncomment the following line and set the option +# from no (the default) to yes. +# +# gopher-enabled no + +############################### ADVANCED CONFIG ############################### + +# Hashes are encoded using a memory efficient data structure when they have a +# small number of entries, and the biggest entry does not exceed a given +# threshold. These thresholds can be configured using the following directives. +hash-max-ziplist-entries 512 +hash-max-ziplist-value 64 + +# Lists are also encoded in a special way to save a lot of space. +# The number of entries allowed per internal list node can be specified +# as a fixed maximum size or a maximum number of elements. +# For a fixed maximum size, use -5 through -1, meaning: +# -5: max size: 64 Kb <-- not recommended for normal workloads +# -4: max size: 32 Kb <-- not recommended +# -3: max size: 16 Kb <-- probably not recommended +# -2: max size: 8 Kb <-- good +# -1: max size: 4 Kb <-- good +# Positive numbers mean store up to _exactly_ that number of elements +# per list node. +# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), +# but if your use case is unique, adjust the settings as necessary. +list-max-ziplist-size -2 + +# Lists may also be compressed. +# Compress depth is the number of quicklist ziplist nodes from *each* side of +# the list to *exclude* from compression. The head and tail of the list +# are always uncompressed for fast push/pop operations. Settings are: +# 0: disable all list compression +# 1: depth 1 means "don't start compressing until after 1 node into the list, +# going from either the head or tail" +# So: [head]->node->node->...->node->[tail] +# [head], [tail] will always be uncompressed; inner nodes will compress. +# 2: [head]->[next]->node->node->...->node->[prev]->[tail] +# 2 here means: don't compress head or head->next or tail->prev or tail, +# but compress all nodes between them. +# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] +# etc. +list-compress-depth 0 + +# Sets have a special encoding in just one case: when a set is composed +# of just strings that happen to be integers in radix 10 in the range +# of 64 bit signed integers. +# The following configuration setting sets the limit in the size of the +# set in order to use this special memory saving encoding. +set-max-intset-entries 512 + +# Similarly to hashes and lists, sorted sets are also specially encoded in +# order to save a lot of space. This encoding is only used when the length and +# elements of a sorted set are below the following limits: +zset-max-ziplist-entries 128 +zset-max-ziplist-value 64 + +# HyperLogLog sparse representation bytes limit. The limit includes the +# 16 bytes header. When an HyperLogLog using the sparse representation crosses +# this limit, it is converted into the dense representation. +# +# A value greater than 16000 is totally useless, since at that point the +# dense representation is more memory efficient. +# +# The suggested value is ~ 3000 in order to have the benefits of +# the space efficient encoding without slowing down too much PFADD, +# which is O(N) with the sparse encoding. The value can be raised to +# ~ 10000 when CPU is not a concern, but space is, and the data set is +# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. +hll-sparse-max-bytes 3000 + +# Streams macro node max size / items. The stream data structure is a radix +# tree of big nodes that encode multiple items inside. Using this configuration +# it is possible to configure how big a single node can be in bytes, and the +# maximum number of items it may contain before switching to a new node when +# appending new stream entries. If any of the following settings are set to +# zero, the limit is ignored, so for instance it is possible to set just a +# max entires limit by setting max-bytes to 0 and max-entries to the desired +# value. +stream-node-max-bytes 4096 +stream-node-max-entries 100 + +# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in +# order to help rehashing the main Redis hash table (the one mapping top-level +# keys to values). The hash table implementation Redis uses (see dict.c) +# performs a lazy rehashing: the more operation you run into a hash table +# that is rehashing, the more rehashing "steps" are performed, so if the +# server is idle the rehashing is never complete and some more memory is used +# by the hash table. +# +# The default is to use this millisecond 10 times every second in order to +# actively rehash the main dictionaries, freeing memory when possible. +# +# If unsure: +# use "activerehashing no" if you have hard latency requirements and it is +# not a good thing in your environment that Redis can reply from time to time +# to queries with 2 milliseconds delay. +# +# use "activerehashing yes" if you don't have such hard requirements but +# want to free memory asap when possible. +activerehashing yes + +# The client output buffer limits can be used to force disconnection of clients +# that are not reading data from the server fast enough for some reason (a +# common reason is that a Pub/Sub client can't consume messages as fast as the +# publisher can produce them). +# +# The limit can be set differently for the three different classes of clients: +# +# normal -> normal clients including MONITOR clients +# replica -> replica clients +# pubsub -> clients subscribed to at least one pubsub channel or pattern +# +# The syntax of every client-output-buffer-limit directive is the following: +# +# client-output-buffer-limit +# +# A client is immediately disconnected once the hard limit is reached, or if +# the soft limit is reached and remains reached for the specified number of +# seconds (continuously). +# So for instance if the hard limit is 32 megabytes and the soft limit is +# 16 megabytes / 10 seconds, the client will get disconnected immediately +# if the size of the output buffers reach 32 megabytes, but will also get +# disconnected if the client reaches 16 megabytes and continuously overcomes +# the limit for 10 seconds. +# +# By default normal clients are not limited because they don't receive data +# without asking (in a push way), but just after a request, so only +# asynchronous clients may create a scenario where data is requested faster +# than it can read. +# +# Instead there is a default limit for pubsub and replica clients, since +# subscribers and replicas receive data in a push fashion. +# +# Both the hard or the soft limit can be disabled by setting them to zero. +client-output-buffer-limit normal 0 0 0 +client-output-buffer-limit replica 256mb 64mb 60 +client-output-buffer-limit pubsub 32mb 8mb 60 + +# Client query buffers accumulate new commands. They are limited to a fixed +# amount by default in order to avoid that a protocol desynchronization (for +# instance due to a bug in the client) will lead to unbound memory usage in +# the query buffer. However you can configure it here if you have very special +# needs, such us huge multi/exec requests or alike. +# +# client-query-buffer-limit 1gb + +# In the Redis protocol, bulk requests, that are, elements representing single +# strings, are normally limited to 512 mb. However you can change this limit +# here, but must be 1mb or greater +# +# proto-max-bulk-len 512mb + +# Redis calls an internal function to perform many background tasks, like +# closing connections of clients in timeout, purging expired keys that are +# never requested, and so forth. +# +# Not all tasks are performed with the same frequency, but Redis checks for +# tasks to perform according to the specified "hz" value. +# +# By default "hz" is set to 10. Raising the value will use more CPU when +# Redis is idle, but at the same time will make Redis more responsive when +# there are many keys expiring at the same time, and timeouts may be +# handled with more precision. +# +# The range is between 1 and 500, however a value over 100 is usually not +# a good idea. Most users should use the default of 10 and raise this up to +# 100 only in environments where very low latency is required. +hz 10 + +# Normally it is useful to have an HZ value which is proportional to the +# number of clients connected. This is useful in order, for instance, to +# avoid too many clients are processed for each background task invocation +# in order to avoid latency spikes. +# +# Since the default HZ value by default is conservatively set to 10, Redis +# offers, and enables by default, the ability to use an adaptive HZ value +# which will temporarily raise when there are many connected clients. +# +# When dynamic HZ is enabled, the actual configured HZ will be used +# as a baseline, but multiples of the configured HZ value will be actually +# used as needed once more clients are connected. In this way an idle +# instance will use very little CPU time while a busy instance will be +# more responsive. +dynamic-hz yes + +# When a child rewrites the AOF file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +aof-rewrite-incremental-fsync yes + +# When redis saves RDB file, if the following option is enabled +# the file will be fsync-ed every 32 MB of data generated. This is useful +# in order to commit the file to the disk more incrementally and avoid +# big latency spikes. +rdb-save-incremental-fsync yes + +# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good +# idea to start with the default settings and only change them after investigating +# how to improve the performances and how the keys LFU change over time, which +# is possible to inspect via the OBJECT FREQ command. +# +# There are two tunable parameters in the Redis LFU implementation: the +# counter logarithm factor and the counter decay time. It is important to +# understand what the two parameters mean before changing them. +# +# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis +# uses a probabilistic increment with logarithmic behavior. Given the value +# of the old counter, when a key is accessed, the counter is incremented in +# this way: +# +# 1. A random number R between 0 and 1 is extracted. +# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). +# 3. The counter is incremented only if R < P. +# +# The default lfu-log-factor is 10. This is a table of how the frequency +# counter changes with a different number of accesses with different +# logarithmic factors: +# +# +--------+------------+------------+------------+------------+------------+ +# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | +# +--------+------------+------------+------------+------------+------------+ +# | 0 | 104 | 255 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 1 | 18 | 49 | 255 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 10 | 10 | 18 | 142 | 255 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# | 100 | 8 | 11 | 49 | 143 | 255 | +# +--------+------------+------------+------------+------------+------------+ +# +# NOTE: The above table was obtained by running the following commands: +# +# redis-benchmark -n 1000000 incr foo +# redis-cli object freq foo +# +# NOTE 2: The counter initial value is 5 in order to give new objects a chance +# to accumulate hits. +# +# The counter decay time is the time, in minutes, that must elapse in order +# for the key counter to be divided by two (or decremented if it has a value +# less <= 10). +# +# The default value for the lfu-decay-time is 1. A special value of 0 means to +# decay the counter every time it happens to be scanned. +# +# lfu-log-factor 10 +# lfu-decay-time 1 + +########################### ACTIVE DEFRAGMENTATION ####################### +# +# What is active defragmentation? +# ------------------------------- +# +# Active (online) defragmentation allows a Redis server to compact the +# spaces left between small allocations and deallocations of data in memory, +# thus allowing to reclaim back memory. +# +# Fragmentation is a natural process that happens with every allocator (but +# less so with Jemalloc, fortunately) and certain workloads. Normally a server +# restart is needed in order to lower the fragmentation, or at least to flush +# away all the data and create it again. However thanks to this feature +# implemented by Oran Agra for Redis 4.0 this process can happen at runtime +# in a "hot" way, while the server is running. +# +# Basically when the fragmentation is over a certain level (see the +# configuration options below) Redis will start to create new copies of the +# values in contiguous memory regions by exploiting certain specific Jemalloc +# features (in order to understand if an allocation is causing fragmentation +# and to allocate it in a better place), and at the same time, will release the +# old copies of the data. This process, repeated incrementally for all the keys +# will cause the fragmentation to drop back to normal values. +# +# Important things to understand: +# +# 1. This feature is disabled by default, and only works if you compiled Redis +# to use the copy of Jemalloc we ship with the source code of Redis. +# This is the default with Linux builds. +# +# 2. You never need to enable this feature if you don't have fragmentation +# issues. +# +# 3. Once you experience fragmentation, you can enable this feature when +# needed with the command "CONFIG SET activedefrag yes". +# +# The configuration parameters are able to fine tune the behavior of the +# defragmentation process. If you are not sure about what they mean it is +# a good idea to leave the defaults untouched. + +# Enabled active defragmentation +# activedefrag no + +# Minimum amount of fragmentation waste to start active defrag +# active-defrag-ignore-bytes 100mb + +# Minimum percentage of fragmentation to start active defrag +# active-defrag-threshold-lower 10 + +# Maximum percentage of fragmentation at which we use maximum effort +# active-defrag-threshold-upper 100 + +# Minimal effort for defrag in CPU percentage, to be used when the lower +# threshold is reached +# active-defrag-cycle-min 1 + +# Maximal effort for defrag in CPU percentage, to be used when the upper +# threshold is reached +# active-defrag-cycle-max 25 + +# Maximum number of set/hash/zset/list fields that will be processed from +# the main dictionary scan +# active-defrag-max-scan-fields 1000 + +# Jemalloc background thread for purging will be enabled by default +jemalloc-bg-thread yes + +# It is possible to pin different threads and processes of Redis to specific +# CPUs in your system, in order to maximize the performances of the server. +# This is useful both in order to pin different Redis threads in different +# CPUs, but also in order to make sure that multiple Redis instances running +# in the same host will be pinned to different CPUs. +# +# Normally you can do this using the "taskset" command, however it is also +# possible to this via Redis configuration directly, both in Linux and FreeBSD. +# +# You can pin the server/IO threads, bio threads, aof rewrite child process, and +# the bgsave child process. The syntax to specify the cpu list is the same as +# the taskset command: +# +# Set redis server/io threads to cpu affinity 0,2,4,6: +# server_cpulist 0-7:2 +# +# Set bio threads to cpu affinity 1,3: +# bio_cpulist 1,3 +# +# Set aof rewrite child process to cpu affinity 8,9,10,11: +# aof_rewrite_cpulist 8-11 +# +# Set bgsave child process to cpu affinity 1,10,11 +# bgsave_cpulist 1,10-11 + +# In some cases redis will emit warnings and even refuse to start if it detects +# that the system is in bad state, it is possible to suppress these warnings +# by setting the following config which takes a space delimited list of warnings +# to suppress +# +# ignore-warnings ARM64-COW-BUG \ No newline at end of file diff --git a/render.yaml b/render.yaml new file mode 100644 index 00000000000..7e679725a67 --- /dev/null +++ b/render.yaml @@ -0,0 +1,19 @@ +# https://render.com/docs/blueprint-spec + +previewsEnabled: false + +services: + - type: web + name: design-system + env: static + buildCommand: yarn && NODE_ENV=production yarn nx run design-system:build-storybook # NODE_ENV=production is required to fix this issue: https://github.com/nrwl/nx/issues/8403 + staticPublishPath: dist/storybook/design-system + autoDeploy: true + domains: + - design.maybe.co + +envVarGroups: + - name: global-env + envVars: + - key: NODE_VERSION # https://render.com/docs/node-version + value: 16.14.0 # LTS diff --git a/tools/generators/.gitkeep b/tools/generators/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/pages/projections.html b/tools/pages/projections.html new file mode 100644 index 00000000000..46a8064a8b9 --- /dev/null +++ b/tools/pages/projections.html @@ -0,0 +1,178 @@ + + + + + + + +
    + + +
    + + + + diff --git a/tools/scripts/gen-cloudfront-signing-keys.sh b/tools/scripts/gen-cloudfront-signing-keys.sh new file mode 100755 index 00000000000..c983406c10d --- /dev/null +++ b/tools/scripts/gen-cloudfront-signing-keys.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Script for documentation purposes +# Further instructions here: +# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-trusted-signers.html#private-content-creating-cloudfront-key-pairs + +# REMOVE FROM SOURCE CONTROL! +# Store in secrets manager +openssl genrsa -out private_key.pem 2048 + +# Store in param store +openssl rsa -pubout -in private_key.pem -out public_key.pem \ No newline at end of file diff --git a/tools/scripts/gen-secret.sh b/tools/scripts/gen-secret.sh new file mode 100755 index 00000000000..39375c10ab1 --- /dev/null +++ b/tools/scripts/gen-secret.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +openssl rand -hex 32 \ No newline at end of file diff --git a/tools/scripts/getAffectedApps.sh b/tools/scripts/getAffectedApps.sh new file mode 100755 index 00000000000..ed8d6198ff5 --- /dev/null +++ b/tools/scripts/getAffectedApps.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +NX_COMMAND=$(./node_modules/.bin/nx affected:apps --plain) + +echo -e "Apps that will deploy: $NX_COMMAND \n" + +APPS=(client server workers) + +for APP in "${APPS[@]}" +do + APP_AFFECTED=$(echo $NX_COMMAND | grep -wq $APP && echo 'true' || echo 'false' ) + echo "::set-output name=${APP}_affected::$APP_AFFECTED" +done \ No newline at end of file diff --git a/tools/scripts/run-production-postgres-tunnel.sh b/tools/scripts/run-production-postgres-tunnel.sh new file mode 100755 index 00000000000..3583990d445 --- /dev/null +++ b/tools/scripts/run-production-postgres-tunnel.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if test -f ~/.ssh/jumpbox_key_production; then + echo "Key exists, starting session..." +else + echo -e "Prereqs: \n\n1. Create a file called ~/.ssh/jumpbox_key_production\n2. Get key from 1Password, paste into file\n3. Run chmod 400 ~/.ssh/jumpbox_key_production\n\n" +fi + +echo "Enter Postgres string in host:port format" +read PG_HOST_PORT + +ssh -i ~/.ssh/jumpbox_key_production -L 5555:$PG_HOST_PORT ubuntu@ec2-34-222-246-3.us-west-2.compute.amazonaws.com \ No newline at end of file diff --git a/tools/scripts/run-staging-postgres-tunnel.sh b/tools/scripts/run-staging-postgres-tunnel.sh new file mode 100755 index 00000000000..ad1be25fac5 --- /dev/null +++ b/tools/scripts/run-staging-postgres-tunnel.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if test -f ~/.ssh/jumpbox_key_staging; then + echo "Key exists, starting session..." +else + echo -e "Prereqs: \n\n1. Create a file called ~/.ssh/jumpbox_key_staging\n2. Get key from 1Password, paste into file\n3. Run chmod 400 ~/.ssh/jumpbox_key_staging\n\n" +fi + +echo "Enter Postgres string in host:port format" +read PG_HOST_PORT + +ssh -i ~/.ssh/jumpbox_key_staging -L 5555:$PG_HOST_PORT ubuntu@ec2-54-185-10-3.us-west-2.compute.amazonaws.com \ No newline at end of file diff --git a/tools/scripts/runStagingE2ETests.sh b/tools/scripts/runStagingE2ETests.sh new file mode 100755 index 00000000000..d838fb55b75 --- /dev/null +++ b/tools/scripts/runStagingE2ETests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +yarn nx run e2e:e2e \ + --baseUrl https://staging-app.maybe.co \ + --headed \ + --skip-nx-cache \ + --env.AUTH0_ID 'REPLACE_THIS' \ + --env.AUTH0_DOMAIN REPLACE_THIS \ + --env.API_URL https://staging-api.maybe.co/v1 \ + --env.AUTH0_CLIENT_ID REPLACE_THIS \ + --env.STRIPE_WEBHOOK_SECRET REPLACE_THIS \ No newline at end of file diff --git a/tools/scripts/vercelBuildIgnore.js b/tools/scripts/vercelBuildIgnore.js new file mode 100644 index 00000000000..07666609f06 --- /dev/null +++ b/tools/scripts/vercelBuildIgnore.js @@ -0,0 +1,75 @@ +const https = require('https') + +const VERCEL_GIT_COMMIT_REF = process.env.VERCEL_GIT_COMMIT_REF +const CI_PROJECT_NAME = process.env.CI_PROJECT_NAME +const CI_TEAM_ID = process.env.CI_TEAM_ID +const CI_PROJECT_ID = process.env.CI_PROJECT_ID +const CI_DEPLOY_HOOK_ID = process.env.CI_DEPLOY_HOOK_ID +const CI_VERCEL_TOKEN = process.env.CI_VERCEL_TOKEN + +console.log(`VERCEL_GIT_COMMIT_REF: ${VERCEL_GIT_COMMIT_REF}`) +console.log(`CI_PROJECT_NAME: ${CI_PROJECT_NAME}`) + +/** + * Vercel currently has no easy way to determine whether a deploy was triggered + * from a deploy hook, and therefore, all manual builds would be cancelled without this logic here. + * @see https://github.com/vercel/community/discussions/285#discussioncomment-1696833 + * + * We skip automatic deploys to main because our AWS resources (server, workers) + * take longer to deploy. Instead, we programmatically deploy after these AWS + * services have successfully deployed to minimize any mismatches in app versions. + * + * We deploy the staging-client project on merge to main to avoid conflicting deploys + * with our main app's PR preview deploys. + */ +if (process.env.VERCEL_GIT_COMMIT_REF === 'main') { + if (CI_PROJECT_NAME === 'staging-client') { + console.log('✅ - Build can proceed, staging-client auto-deploys on merge to main') + process.exit(1) + } + + let data = '' + const req = https.request( + { + hostname: 'api.vercel.com', + path: `/v6/deployments?limit=1&projectId=${CI_PROJECT_ID}&teamId=${CI_TEAM_ID}&state=BUILDING&target=production`, + headers: { + Authorization: `Bearer ${CI_VERCEL_TOKEN}`, + }, + }, + (res) => { + res.on('data', (d) => (data += d.toString())) + res.on('end', (d) => { + const parsed = JSON.parse(data) + + try { + const deployment = parsed.deployments[0] + const hookId = deployment.meta.deployHookId + + if (hookId === CI_DEPLOY_HOOK_ID) { + console.log('✅ - Build can proceed, using deploy hook') + process.exit(1) + } else { + throw new Error('Could not find deployment triggered from deploy hook') + } + } catch (e) { + console.error(e) + console.log('🛑 - Build skipped, error finding deployments') + process.exit(0) + } + }) + } + ) + + req.on('error', console.error) + req.end() +} else { + if (CI_PROJECT_NAME === 'staging-client') { + console.log('🛑 - Build skipped, staging-client does not deploy PR previews') + process.exit(0) + } + + // Allow PR previews to deploy + console.log('✅ - Build can proceed') + process.exit(1) +} diff --git a/tools/scripts/wait-for-it.sh b/tools/scripts/wait-for-it.sh new file mode 100755 index 00000000000..3974640b053 --- /dev/null +++ b/tools/scripts/wait-for-it.sh @@ -0,0 +1,182 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +WAITFORIT_cmdname=${0##*/} + +echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + else + echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" + fi + WAITFORIT_start_ts=$(date +%s) + while : + do + if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then + nc -z $WAITFORIT_HOST $WAITFORIT_PORT + WAITFORIT_result=$? + else + (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 + WAITFORIT_result=$? + fi + if [[ $WAITFORIT_result -eq 0 ]]; then + WAITFORIT_end_ts=$(date +%s) + echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" + break + fi + sleep 1 + done + return $WAITFORIT_result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $WAITFORIT_QUIET -eq 1 ]]; then + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + else + timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & + fi + WAITFORIT_PID=$! + trap "kill -INT -$WAITFORIT_PID" INT + wait $WAITFORIT_PID + WAITFORIT_RESULT=$? + if [[ $WAITFORIT_RESULT -ne 0 ]]; then + echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" + fi + return $WAITFORIT_RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + WAITFORIT_hostport=(${1//:/ }) + WAITFORIT_HOST=${WAITFORIT_hostport[0]} + WAITFORIT_PORT=${WAITFORIT_hostport[1]} + shift 1 + ;; + --child) + WAITFORIT_CHILD=1 + shift 1 + ;; + -q | --quiet) + WAITFORIT_QUIET=1 + shift 1 + ;; + -s | --strict) + WAITFORIT_STRICT=1 + shift 1 + ;; + -h) + WAITFORIT_HOST="$2" + if [[ $WAITFORIT_HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + WAITFORIT_HOST="${1#*=}" + shift 1 + ;; + -p) + WAITFORIT_PORT="$2" + if [[ $WAITFORIT_PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + WAITFORIT_PORT="${1#*=}" + shift 1 + ;; + -t) + WAITFORIT_TIMEOUT="$2" + if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + WAITFORIT_TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + WAITFORIT_CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} +WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} +WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} +WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} + +# Check to see if timeout is from busybox? +WAITFORIT_TIMEOUT_PATH=$(type -p timeout) +WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) + +WAITFORIT_BUSYTIMEFLAG="" +if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then + WAITFORIT_ISBUSY=1 + # Check if busybox timeout uses -t flag + # (recent Alpine versions don't support -t anymore) + if timeout &>/dev/stdout | grep -q -e '-t '; then + WAITFORIT_BUSYTIMEFLAG="-t" + fi +else + WAITFORIT_ISBUSY=0 +fi + +if [[ $WAITFORIT_CHILD -gt 0 ]]; then + wait_for + WAITFORIT_RESULT=$? + exit $WAITFORIT_RESULT +else + if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then + wait_for_wrapper + WAITFORIT_RESULT=$? + else + wait_for + WAITFORIT_RESULT=$? + fi +fi + +if [[ $WAITFORIT_CLI != "" ]]; then + if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then + echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" + exit $WAITFORIT_RESULT + fi + exec "${WAITFORIT_CLI[@]}" +else + exit $WAITFORIT_RESULT +fi \ No newline at end of file diff --git a/tools/test-data/finicity/finicityTestData.ts b/tools/test-data/finicity/finicityTestData.ts new file mode 100644 index 00000000000..b8f102c82b1 --- /dev/null +++ b/tools/test-data/finicity/finicityTestData.ts @@ -0,0 +1,20053 @@ +import type { FinicityTypes } from '@maybe-finance/finicity-api' + +export const accounts: FinicityTypes.CustomerAccount[] = [ + { + id: '6000985583', + number: '8000008888', + accountNumberDisplay: '8888', + name: 'Auto Loan', + balance: -502.27, + type: 'loan', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490851, + aggregationAttemptDate: 1651490851, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { + payoffAmount: 101.11, + principalBalance: 1021.21, + escrowBalance: 71.45, + interestRate: '1.65', + autoPayEnrolled: 'Y', + collateral: 'test collateral', + currentSchool: 'current school', + firstMortgage: 'N', + originalSchool: 'original school', + recurringPaymentAmount: 232, + lender: 'lender', + availableBalanceAmount: 78.11, + endingBalanceAmount: 66.33, + loanTermType: 'loan term type', + paymentsMade: 4, + balloonAmount: 88.11, + projectedInterest: 54.22, + interestPaidLtd: 12.22, + interestRateType: 'interest rate type', + loanPaymentType: 'loan payment type', + paymentsRemaining: 88, + loanMinAmtDue: 250, + loanPaymentFreq: 'loan pay freq', + paymentMinAmount: 232, + loanMinAmtDueDate: 8911598400, + firstPaymentDate: 1650628800, + }, + displayPosition: 1, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Auto Loan', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985584', + number: '2000005555', + accountNumberDisplay: '5555', + name: 'Home Mortgage', + balance: -502.27, + type: 'mortgage', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490851, + aggregationAttemptDate: 1651490851, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { + payoffAmount: 101.11, + principalBalance: 1021.21, + escrowBalance: 71.45, + interestRate: '1.65', + autoPayEnrolled: 'Y', + collateral: 'test collateral', + currentSchool: 'current school', + firstMortgage: 'N', + originalSchool: 'original school', + recurringPaymentAmount: 232, + lender: 'lender', + availableBalanceAmount: 78.11, + endingBalanceAmount: 66.33, + loanTermType: 'loan term type', + paymentsMade: 4, + balloonAmount: 88.11, + projectedInterest: 54.22, + interestPaidLtd: 12.22, + interestRateType: 'interest rate type', + loanPaymentType: 'loan payment type', + paymentsRemaining: 88, + loanMinAmtDue: 250, + loanPaymentFreq: 'loan pay freq', + paymentMinAmount: 232, + loanMinAmtDueDate: 8911598400, + firstPaymentDate: 1650628800, + }, + displayPosition: 7, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Home Mortgage', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985585', + number: '2000004444', + accountNumberDisplay: '4444', + name: 'Roth IRA', + balance: 502.27, + type: 'investment', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490851, + aggregationAttemptDate: 1651490851, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { + vestedBalance: 1250, + currentLoanBalance: 4500, + loanRate: 3, + marginAllowed: true, + cashAccountAllowed: true, + }, + displayPosition: 3, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Roth IRA', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985586', + number: '0000000000', + accountNumberDisplay: '0000', + name: 'Brokerage', + balance: 502.27, + type: 'investment', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490851, + aggregationAttemptDate: 1651490851, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { + vestedBalance: 1250, + currentLoanBalance: 4500, + loanRate: 3, + employeeDeferPreTaxAmount: 45.25, + employeePreTaxAmount: 1250, + employeePreTaxPercentage: 4500, + employerYearToDate: 25.1, + rolloverContributionAmount: 45.25, + allowedCheckWriting: 'ALLOW', + allowedOptionTrade: 'ALLOW', + dailyChange: 3, + margin: 45.25, + percentageChange: 1250, + loanPaymentAmount: 3, + loanNextPaymentDate: 1550041200, + }, + position: [ + { + id: 1000175214, + symbol: 'JPM', + fundName: 'JPMORGAN CHASE & CO', + quantity: 10, + marketValue: 914.2, + changePercent: 5, + costBasis: 925.5, + dailyChange: 3, + memo: 'I am tp_account_inves_position memo', + empPretaxContribAmount: 156, + heldInAccount: '127786786.00000000', + holdType: 'INVESTMENT', + maturityValue: 5466, + units: 99, + unitPrice: 786, + type: 'Margin', + status: 'A', + invSecurityType: 'OTHERINFO', + securityCurrencyRate: 3.21, + averageCost: 10, + cashAccount: 22.22, + faceValue: 10, + holdingId: 'H213223', + securitySubType: 'S112233', + rate: 11.1, + unitType: 'U5522', + todayGLDollar: 13.1, + todayGLPercent: 11.1, + totalGLDollar: 12.1, + totalGLPercent: 14.1, + costBasisPerShare: 15, + enterpriseSymbol: 'E52122', + localMarketValue: 16, + localPrice: 17.1, + reinvestmentCapGains: 101, + reinvestmentDividend: 20, + expirationDate: 1550041200, + originalPurchaseDate: 1550041200, + }, + { + id: 1000175215, + symbol: 'ABC', + fundName: 'American Broadcasting Company', + quantity: 100, + marketValue: 502, + changePercent: 5, + costBasis: 925.5, + dailyChange: 3, + memo: 'I am tp_account_inves_position memo', + empPretaxContribAmount: 156, + heldInAccount: '127786786.00000000', + holdType: 'INVESTMENT', + maturityValue: 5466, + units: 99, + unitPrice: 786, + type: 'Margin', + status: 'A', + invSecurityType: 'OTHERINFO', + securityCurrencyRate: 3.21, + averageCost: 10, + cashAccount: 22.22, + faceValue: 10, + holdingId: 'H213223', + securitySubType: 'S112233', + rate: 11.1, + unitType: 'U5522', + todayGLDollar: 13.1, + todayGLPercent: 11.1, + totalGLDollar: 12.1, + totalGLPercent: 14.1, + costBasisPerShare: 15, + enterpriseSymbol: 'E52122', + localMarketValue: 16, + localPrice: 17.1, + reinvestmentCapGains: 101, + reinvestmentDividend: 20, + expirationDate: 1550041200, + originalPurchaseDate: 1550041200, + }, + { + id: 1000175216, + symbol: 'KO', + fundName: 'COCA COLA CO', + quantity: 22, + marketValue: 1002.98, + changePercent: 5, + costBasis: 981.88, + dailyChange: 3, + memo: 'I am tp_account_inves_position memo', + empPretaxContribAmount: 156, + heldInAccount: '127786786.00000000', + holdType: 'INVESTMENT', + maturityValue: 5466, + units: 99, + unitPrice: 786, + type: 'Margin', + status: 'A', + invSecurityType: 'OTHERINFO', + securityCurrencyRate: 3.21, + averageCost: 10, + cashAccount: 22.22, + faceValue: 10, + holdingId: 'H213223', + securitySubType: 'S112233', + rate: 11.1, + unitType: 'U5522', + todayGLDollar: 13.1, + todayGLPercent: 11.1, + totalGLDollar: 12.1, + totalGLPercent: 14.1, + costBasisPerShare: 15, + enterpriseSymbol: 'E52122', + localMarketValue: 16, + localPrice: 17.1, + reinvestmentCapGains: 101, + reinvestmentDividend: 20, + expirationDate: 1550041200, + originalPurchaseDate: 1550041200, + }, + ], + displayPosition: 8, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Brokerage', + oldestTransactionDate: 1651060800, + marketSegment: 'personal', + }, + { + id: '6000985587', + number: '8000006666', + accountNumberDisplay: '6666', + name: 'Line of Credit', + balance: -502.27, + type: 'lineOfCredit', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490852, + aggregationAttemptDate: 1651490852, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { + interestRate: '4.5', + creditAvailableAmount: 799.99, + paymentMinAmount: 1, + statementCloseBalance: -502.27, + locPrincipalBalance: 20010, + paymentDueDate: 1651816800, + statementEndDate: 1651384800, + }, + displayPosition: 4, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Line of Credit', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985588', + number: '4100007777', + accountNumberDisplay: '7777', + name: 'Credit Card', + balance: 502.27, + type: 'creditCard', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490852, + aggregationAttemptDate: 1651490852, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + displayPosition: 2, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Credit Card', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985589', + number: '1000002222', + realAccountNumberLast4: '2222', + accountNumberDisplay: '2222', + name: 'Savings', + balance: 502.27, + type: 'savings', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490852, + aggregationAttemptDate: 1651490852, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { availableBalanceAmount: 502.27, periodInterestRate: '4.5' }, + displayPosition: 6, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Savings', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, + { + id: '6000985590', + number: '1000001111', + realAccountNumberLast4: '1111', + accountNumberDisplay: '1111', + name: 'Checking', + balance: 502.27, + type: 'checking', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101732', + balanceDate: 1651490830, + aggregationSuccessDate: 1651490852, + aggregationAttemptDate: 1651490852, + createdDate: 1651089433, + currency: 'USD', + lastTransactionDate: 1651490830, + institutionLoginId: 6000483842, + detail: { availableBalanceAmount: 502.27, periodInterestRate: '4.5' }, + displayPosition: 5, + financialinstitutionAccountStatus: 'OPEN', + accountNickname: 'Checking', + oldestTransactionDate: 1650196800, + marketSegment: 'personal', + }, +] + +export const transactions: FinicityTypes.Transaction[] = [ + { + id: 6403996987, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651492800, + transactionDate: 1651492800, + createdDate: 1651490851, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6380946868, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651406400, + transactionDate: 1651406400, + createdDate: 1651404460, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6357733558, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651320000, + transactionDate: 1651320000, + createdDate: 1651318045, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6327562700, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651233600, + transactionDate: 1651233600, + createdDate: 1651231631, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6295447008, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651147200, + transactionDate: 1651147200, + createdDate: 1651145248, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6272549158, + amount: 2.23, + accountId: 6000985586, + customerId: 6000631200, + status: 'active', + description: '[No description provided by institution]', + feeAmount: 9.95, + symbol: 'ABC', + unitQuantity: 100.0, + unitAction: 'BUY', + postedDate: 1651060800, + transactionDate: 1651060800, + createdDate: 1651089698, + confirmationNumber: 'test confirmation', + payeeId: 'test PAYEE_ID', + extendedPayeeName: 'test EXTENDED_PAYEE', + originalCurrency: 'Y', + runningBalanceAmount: 23.33, + escrowTaxAmount: 12.12, + escrowInsuranceAmount: 14.14, + escrowPmiAmount: 15.15, + escrowFeesAmount: 16.16, + escrowOtherAmount: 17.17, + inv401kSource: 'test INV_401K_SOURCE', + relatedInstitutionTradeId: 'test RELATED_INSTITUTION_TRADE_ID', + subaccountSecurityType: 'test SUB_ACCOUNT_SECURITY_TYPE', + penaltyAmount: 18.18, + sharesPerContract: 19.19, + stateWithholding: 20.2, + taxesAmount: 21.21, + unitPrice: 22.22, + securedType: 'test SECURED_TYPE', + reveralInstitutionTransactionId: '599', + investmentTransactionType: 'other', + }, + { + id: 6411651816, + amount: -4.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 102', + memo: 'walmart', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644841, + amount: -4.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 102', + memo: 'walmart', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638991, + amount: -4.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 102', + memo: 'walmart', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627678, + amount: -4.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 102', + memo: 'walmart', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612966, + amount: -4.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 102', + memo: 'walmart', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510753, + tradeDate: 1649743200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603903, + amount: -4.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 102', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592595, + amount: -4.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 102', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444952, + amount: -4.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 98', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441343, + amount: -4.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 98', + memo: 'walmart', + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438831, + amount: -4.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 98', + memo: 'walmart', + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433034, + amount: -4.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 98', + memo: 'walmart', + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425785, + amount: -4.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 98', + memo: 'walmart', + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145241, + tradeDate: 1649397600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414855, + amount: -4.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 98', + memo: 'walmart', + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402239, + amount: -4.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 98', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1649419200, + transactionDate: 1649419200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651832, + amount: 4.02, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 92', + memo: 'walmart', + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '4220223', + }, + { + id: 6411644857, + amount: 4.02, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 92', + memo: 'walmart', + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '4220223', + }, + { + id: 6411639006, + amount: 4.02, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 92', + memo: 'walmart', + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627693, + amount: 4.02, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 92', + memo: 'walmart', + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612992, + amount: 4.02, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 92', + memo: 'walmart', + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510753, + tradeDate: 1648879200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603916, + amount: 4.02, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 92', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592608, + amount: 4.02, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 92', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648900800, + transactionDate: 1648900800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444958, + amount: 3.29, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 88', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441291, + amount: 3.29, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 88', + memo: 'walmart', + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '32920223', + }, + { + id: 6295438792, + amount: 3.29, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 88', + memo: 'walmart', + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '32920223', + }, + { + id: 6295432959, + amount: 3.29, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 88', + memo: 'walmart', + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425744, + amount: 3.29, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 88', + memo: 'walmart', + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145241, + tradeDate: 1648533600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414776, + amount: 3.29, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 88', + memo: 'walmart', + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402235, + amount: 3.29, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 88', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648555200, + transactionDate: 1648555200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651830, + amount: -3.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 82', + memo: 'walmart', + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644855, + amount: -3.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 82', + memo: 'walmart', + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639004, + amount: -3.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 82', + memo: 'walmart', + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627691, + amount: -3.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 82', + memo: 'walmart', + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613008, + amount: -3.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 82', + memo: 'walmart', + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510753, + tradeDate: 1648015200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603937, + amount: -3.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 82', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592629, + amount: -3.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 82', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1648036800, + transactionDate: 1648036800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444934, + amount: -3.19, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 78', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441374, + amount: -3.19, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 78', + memo: 'walmart', + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438800, + amount: -3.19, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 78', + memo: 'walmart', + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433008, + amount: -3.19, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 78', + memo: 'walmart', + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425770, + amount: -3.19, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 78', + memo: 'walmart', + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145241, + tradeDate: 1647669600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414851, + amount: -3.19, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 78', + memo: 'walmart', + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402268, + amount: -3.19, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 78', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1647691200, + transactionDate: 1647691200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651833, + amount: 3.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 72', + memo: 'walmart', + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '31320225', + }, + { + id: 6411644858, + amount: 3.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 72', + memo: 'walmart', + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '31320225', + }, + { + id: 6411639007, + amount: 3.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 72', + memo: 'walmart', + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627694, + amount: 3.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 72', + memo: 'walmart', + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613018, + amount: 3.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 72', + memo: 'walmart', + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510753, + tradeDate: 1647154800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603970, + amount: 3.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 72', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592662, + amount: 3.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 72', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1647172800, + transactionDate: 1647172800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444978, + amount: 3.09, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 68', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441289, + amount: 3.09, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 68', + memo: 'walmart', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '3920225', + }, + { + id: 6295438795, + amount: 3.09, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 68', + memo: 'walmart', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '3920225', + }, + { + id: 6295432952, + amount: 3.09, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 68', + memo: 'walmart', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425783, + amount: 3.09, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 68', + memo: 'walmart', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145241, + tradeDate: 1646809200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414789, + amount: 3.09, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 68', + memo: 'walmart', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402189, + amount: 3.09, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 68', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651875, + amount: -3.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 62', + memo: 'walmart', + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644900, + amount: -3.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 62', + memo: 'walmart', + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639047, + amount: -3.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 62', + memo: 'walmart', + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627734, + amount: -3.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 62', + memo: 'walmart', + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612963, + amount: -3.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 62', + memo: 'walmart', + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510753, + tradeDate: 1646290800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603971, + amount: -3.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 62', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592663, + amount: -3.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 62', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1646308800, + transactionDate: 1646308800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444995, + amount: -2.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 58', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441301, + amount: -2.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 58', + memo: 'walmart', + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438741, + amount: -2.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 58', + memo: 'walmart', + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432966, + amount: -2.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 58', + memo: 'walmart', + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425713, + amount: -2.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 58', + memo: 'walmart', + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145241, + tradeDate: 1645945200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414884, + amount: -2.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 58', + memo: 'walmart', + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402190, + amount: -2.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 58', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645963200, + transactionDate: 1645963200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651859, + amount: 2.21, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 52', + memo: 'walmart', + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '22120227', + }, + { + id: 6411644884, + amount: 2.21, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 52', + memo: 'walmart', + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '22120227', + }, + { + id: 6411639036, + amount: 2.21, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 52', + memo: 'walmart', + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627723, + amount: 2.21, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 52', + memo: 'walmart', + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612999, + amount: 2.21, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 52', + memo: 'walmart', + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510753, + tradeDate: 1645426800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603966, + amount: 2.21, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 52', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592658, + amount: 2.21, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 52', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645444800, + transactionDate: 1645444800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444932, + amount: 2.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 48', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441347, + amount: 2.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 48', + memo: 'walmart', + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '21720227', + }, + { + id: 6295438767, + amount: 2.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 48', + memo: 'walmart', + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '21720227', + }, + { + id: 6295433019, + amount: 2.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 48', + memo: 'walmart', + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425803, + amount: 2.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 48', + memo: 'walmart', + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145241, + tradeDate: 1645081200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414822, + amount: 2.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 48', + memo: 'walmart', + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402283, + amount: 2.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 48', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1645099200, + transactionDate: 1645099200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651879, + amount: -2.11, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 42', + memo: 'walmart', + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644904, + amount: -2.11, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 42', + memo: 'walmart', + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639054, + amount: -2.11, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 42', + memo: 'walmart', + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627741, + amount: -2.11, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 42', + memo: 'walmart', + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613013, + amount: -2.11, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 42', + memo: 'walmart', + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510753, + tradeDate: 1644562800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603965, + amount: -2.11, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 42', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592657, + amount: -2.11, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 42', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1644580800, + transactionDate: 1644580800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444975, + amount: -2.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 38', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441379, + amount: -2.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 38', + memo: 'walmart', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438793, + amount: -2.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 38', + memo: 'walmart', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433037, + amount: -2.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 38', + memo: 'walmart', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425781, + amount: -2.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 38', + memo: 'walmart', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145241, + tradeDate: 1644217200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414868, + amount: -2.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 38', + memo: 'walmart', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402262, + amount: -2.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 38', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651858, + amount: 2.01, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 32', + memo: 'walmart', + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '2120229', + }, + { + id: 6411644883, + amount: 2.01, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 32', + memo: 'walmart', + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '2120229', + }, + { + id: 6411639026, + amount: 2.01, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 32', + memo: 'walmart', + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627713, + amount: 2.01, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 32', + memo: 'walmart', + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612977, + amount: 2.01, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 32', + memo: 'walmart', + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510753, + tradeDate: 1643698800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603942, + amount: 2.01, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 32', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592634, + amount: 2.01, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 32', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1643716800, + transactionDate: 1643716800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444950, + amount: 1.28, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 28', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441283, + amount: 1.28, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 28', + memo: 'walmart', + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '12820229', + }, + { + id: 6295438789, + amount: 1.28, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 28', + memo: 'walmart', + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '12820229', + }, + { + id: 6295432998, + amount: 1.28, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 28', + memo: 'walmart', + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425798, + amount: 1.28, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 28', + memo: 'walmart', + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145241, + tradeDate: 1643353200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414784, + amount: 1.28, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 28', + memo: 'walmart', + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402217, + amount: 1.28, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 28', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1643371200, + transactionDate: 1643371200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651841, + amount: -1.22, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 22', + memo: 'walmart', + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644866, + amount: -1.22, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 22', + memo: 'walmart', + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639013, + amount: -1.22, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 22', + memo: 'walmart', + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627700, + amount: -1.22, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 22', + memo: 'walmart', + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612967, + amount: -1.22, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 22', + memo: 'walmart', + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510753, + tradeDate: 1642834800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603922, + amount: -1.22, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 22', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592614, + amount: -1.22, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 22', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1642852800, + transactionDate: 1642852800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444998, + amount: -1.18, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 18', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441330, + amount: -1.18, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 18', + memo: 'walmart', + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438776, + amount: -1.18, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 18', + memo: 'walmart', + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432993, + amount: -1.18, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 18', + memo: 'walmart', + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425761, + amount: -1.18, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 18', + memo: 'walmart', + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145241, + tradeDate: 1642489200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414865, + amount: -1.18, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 18', + memo: 'walmart', + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402254, + amount: -1.18, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 18', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1642507200, + transactionDate: 1642507200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651836, + amount: 1.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 12', + memo: 'walmart', + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '112202211', + }, + { + id: 6411644861, + amount: 1.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 12', + memo: 'walmart', + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '112202211', + }, + { + id: 6411639051, + amount: 1.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 12', + memo: 'walmart', + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627738, + amount: 1.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 12', + memo: 'walmart', + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612974, + amount: 1.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 12', + memo: 'walmart', + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510753, + tradeDate: 1641970800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603929, + amount: 1.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 12', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592621, + amount: 1.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 12', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641988800, + transactionDate: 1641988800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444945, + amount: 1.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 8', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441384, + amount: 1.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 8', + memo: 'walmart', + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '18202211', + }, + { + id: 6295438799, + amount: 1.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 8', + memo: 'walmart', + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '18202211', + }, + { + id: 6295433006, + amount: 1.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 8', + memo: 'walmart', + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425776, + amount: 1.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 8', + memo: 'walmart', + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145241, + tradeDate: 1641625200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414856, + amount: 1.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 8', + memo: 'walmart', + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402253, + amount: 1.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 8', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641643200, + transactionDate: 1641643200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651861, + amount: -1.02, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 2', + memo: 'walmart', + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644886, + amount: -1.02, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 2', + memo: 'walmart', + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639029, + amount: -1.02, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 2', + memo: 'walmart', + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627716, + amount: -1.02, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 2', + memo: 'walmart', + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612985, + amount: -1.02, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 2', + memo: 'walmart', + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510753, + tradeDate: 1641106800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603905, + amount: -1.02, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 2', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592597, + amount: -1.02, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 2', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1641124800, + transactionDate: 1641124800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444910, + amount: -12.29, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 363', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441325, + amount: -12.29, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 363', + memo: 'walmart', + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438743, + amount: -12.29, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 363', + memo: 'walmart', + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433044, + amount: -12.29, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 363', + memo: 'walmart', + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425731, + amount: -12.29, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 363', + memo: 'walmart', + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145241, + tradeDate: 1640761200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414839, + amount: -12.29, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 363', + memo: 'walmart', + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402277, + amount: -12.29, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 363', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1640779200, + transactionDate: 1640779200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651823, + amount: 12.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 357', + memo: 'walmart', + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1223202113', + }, + { + id: 6411644848, + amount: 12.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 357', + memo: 'walmart', + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1223202113', + }, + { + id: 6411638998, + amount: 12.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 357', + memo: 'walmart', + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627685, + amount: 12.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 357', + memo: 'walmart', + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613007, + amount: 12.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 357', + memo: 'walmart', + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510753, + tradeDate: 1640242800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603954, + amount: 12.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 357', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592646, + amount: 12.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 357', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1640260800, + transactionDate: 1640260800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444992, + amount: 12.19, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 353', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441286, + amount: 12.19, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 353', + memo: 'walmart', + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1219202113', + }, + { + id: 6295438763, + amount: 12.19, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 353', + memo: 'walmart', + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1219202113', + }, + { + id: 6295432957, + amount: 12.19, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 353', + memo: 'walmart', + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425793, + amount: 12.19, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 353', + memo: 'walmart', + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145241, + tradeDate: 1639897200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414866, + amount: 12.19, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 353', + memo: 'walmart', + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402228, + amount: 12.19, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 353', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639915200, + transactionDate: 1639915200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651814, + amount: -12.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 347', + memo: 'walmart', + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644839, + amount: -12.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 347', + memo: 'walmart', + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638989, + amount: -12.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 347', + memo: 'walmart', + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627676, + amount: -12.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 347', + memo: 'walmart', + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613026, + amount: -12.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 347', + memo: 'walmart', + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510753, + tradeDate: 1639378800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603947, + amount: -12.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 347', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592639, + amount: -12.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 347', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639396800, + transactionDate: 1639396800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445006, + amount: -12.09, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 343', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441360, + amount: -12.09, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 343', + memo: 'walmart', + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438832, + amount: -12.09, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 343', + memo: 'walmart', + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433032, + amount: -12.09, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 343', + memo: 'walmart', + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425710, + amount: -12.09, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 343', + memo: 'walmart', + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145241, + tradeDate: 1639033200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414862, + amount: -12.09, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 343', + memo: 'walmart', + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402225, + amount: -12.09, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 343', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1639051200, + transactionDate: 1639051200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651847, + amount: 12.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 337', + memo: 'walmart', + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '123202115', + }, + { + id: 6411644872, + amount: 12.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 337', + memo: 'walmart', + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '123202115', + }, + { + id: 6411639018, + amount: 12.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 337', + memo: 'walmart', + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627705, + amount: 12.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 337', + memo: 'walmart', + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613001, + amount: 12.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 337', + memo: 'walmart', + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510753, + tradeDate: 1638514800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603926, + amount: 12.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 337', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592618, + amount: 12.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 337', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1638532800, + transactionDate: 1638532800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444971, + amount: 11.29, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 333', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441320, + amount: 11.29, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 333', + memo: 'walmart', + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1129202115', + }, + { + id: 6295438736, + amount: 11.29, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 333', + memo: 'walmart', + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1129202115', + }, + { + id: 6295432976, + amount: 11.29, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 333', + memo: 'walmart', + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425712, + amount: 11.29, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 333', + memo: 'walmart', + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145241, + tradeDate: 1638169200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414787, + amount: 11.29, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 333', + memo: 'walmart', + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402202, + amount: 11.29, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 333', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1638187200, + transactionDate: 1638187200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651817, + amount: -11.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 327', + memo: 'walmart', + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644842, + amount: -11.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 327', + memo: 'walmart', + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638992, + amount: -11.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 327', + memo: 'walmart', + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627679, + amount: -11.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 327', + memo: 'walmart', + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612970, + amount: -11.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 327', + memo: 'walmart', + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510753, + tradeDate: 1637650800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603906, + amount: -11.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 327', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592598, + amount: -11.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 327', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1637668800, + transactionDate: 1637668800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444962, + amount: -11.19, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 323', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441293, + amount: -11.19, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 323', + memo: 'walmart', + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438829, + amount: -11.19, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 323', + memo: 'walmart', + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433049, + amount: -11.19, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 323', + memo: 'walmart', + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425745, + amount: -11.19, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 323', + memo: 'walmart', + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145241, + tradeDate: 1637305200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414778, + amount: -11.19, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 323', + memo: 'walmart', + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402195, + amount: -11.19, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 323', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1637323200, + transactionDate: 1637323200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651868, + amount: 11.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 317', + memo: 'walmart', + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1113202117', + }, + { + id: 6411644893, + amount: 11.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 317', + memo: 'walmart', + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1113202117', + }, + { + id: 6411639041, + amount: 11.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 317', + memo: 'walmart', + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627728, + amount: 11.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 317', + memo: 'walmart', + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613021, + amount: 11.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 317', + memo: 'walmart', + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510753, + tradeDate: 1636786800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603955, + amount: 11.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 317', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592647, + amount: 11.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 317', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1636804800, + transactionDate: 1636804800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444911, + amount: 11.09, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 313', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441315, + amount: 11.09, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 313', + memo: 'walmart', + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '119202117', + }, + { + id: 6295438791, + amount: 11.09, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 313', + memo: 'walmart', + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '119202117', + }, + { + id: 6295432956, + amount: 11.09, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 313', + memo: 'walmart', + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425767, + amount: 11.09, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 313', + memo: 'walmart', + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145241, + tradeDate: 1636441200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414840, + amount: 11.09, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 313', + memo: 'walmart', + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402259, + amount: 11.09, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 313', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1636459200, + transactionDate: 1636459200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651844, + amount: -11.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 307', + memo: 'walmart', + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644869, + amount: -11.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 307', + memo: 'walmart', + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639016, + amount: -11.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 307', + memo: 'walmart', + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627703, + amount: -11.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 307', + memo: 'walmart', + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613014, + amount: -11.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 307', + memo: 'walmart', + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510753, + tradeDate: 1635919200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603949, + amount: -11.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 307', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592641, + amount: -11.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 307', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635940800, + transactionDate: 1635940800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444966, + amount: -10.3, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 303', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441376, + amount: -10.3, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 303', + memo: 'walmart', + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438794, + amount: -10.3, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 303', + memo: 'walmart', + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432990, + amount: -10.3, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 303', + memo: 'walmart', + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425769, + amount: -10.3, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 303', + memo: 'walmart', + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145241, + tradeDate: 1635573600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414792, + amount: -10.3, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 303', + memo: 'walmart', + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402199, + amount: -10.3, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 303', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635595200, + transactionDate: 1635595200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651811, + amount: 10.24, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 297', + memo: 'walmart', + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1024202119', + }, + { + id: 6411644836, + amount: 10.24, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 297', + memo: 'walmart', + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1024202119', + }, + { + id: 6411638986, + amount: 10.24, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 297', + memo: 'walmart', + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627673, + amount: 10.24, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 297', + memo: 'walmart', + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613005, + amount: 10.24, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 297', + memo: 'walmart', + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510753, + tradeDate: 1635055200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603935, + amount: 10.24, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 297', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592627, + amount: 10.24, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 297', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1635076800, + transactionDate: 1635076800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444941, + amount: 10.2, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 293', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441349, + amount: 10.2, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 293', + memo: 'walmart', + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1020202119', + }, + { + id: 6295438746, + amount: 10.2, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 293', + memo: 'walmart', + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1020202119', + }, + { + id: 6295433011, + amount: 10.2, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 293', + memo: 'walmart', + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425740, + amount: 10.2, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 293', + memo: 'walmart', + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145241, + tradeDate: 1634709600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414812, + amount: 10.2, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 293', + memo: 'walmart', + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402203, + amount: 10.2, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 293', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1634731200, + transactionDate: 1634731200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651870, + amount: -10.14, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 287', + memo: 'walmart', + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644895, + amount: -10.14, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 287', + memo: 'walmart', + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639038, + amount: -10.14, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 287', + memo: 'walmart', + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627725, + amount: -10.14, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 287', + memo: 'walmart', + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612978, + amount: -10.14, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 287', + memo: 'walmart', + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510753, + tradeDate: 1634191200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603959, + amount: -10.14, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 287', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592651, + amount: -10.14, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 287', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1634212800, + transactionDate: 1634212800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444993, + amount: -10.1, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 283', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441324, + amount: -10.1, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 283', + memo: 'walmart', + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438815, + amount: -10.1, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 283', + memo: 'walmart', + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433046, + amount: -10.1, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 283', + memo: 'walmart', + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425771, + amount: -10.1, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 283', + memo: 'walmart', + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145241, + tradeDate: 1633845600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414808, + amount: -10.1, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 283', + memo: 'walmart', + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402263, + amount: -10.1, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 283', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633867200, + transactionDate: 1633867200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651851, + amount: 10.04, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 277', + memo: 'walmart', + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '104202121', + }, + { + id: 6411644876, + amount: 10.04, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 277', + memo: 'walmart', + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '104202121', + }, + { + id: 6411639021, + amount: 10.04, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 277', + memo: 'walmart', + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627708, + amount: 10.04, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 277', + memo: 'walmart', + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612979, + amount: 10.04, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 277', + memo: 'walmart', + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510753, + tradeDate: 1633327200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603932, + amount: 10.04, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 277', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592624, + amount: 10.04, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 277', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633348800, + transactionDate: 1633348800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444942, + amount: 9.3, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 273', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441278, + amount: 9.3, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 273', + memo: 'walmart', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '930202121', + }, + { + id: 6295438812, + amount: 9.3, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 273', + memo: 'walmart', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '930202121', + }, + { + id: 6295433040, + amount: 9.3, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 273', + memo: 'walmart', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425799, + amount: 9.3, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 273', + memo: 'walmart', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145241, + tradeDate: 1632981600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414879, + amount: 9.3, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 273', + memo: 'walmart', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402274, + amount: 9.3, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 273', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651821, + amount: -9.24, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 267', + memo: 'walmart', + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644846, + amount: -9.24, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 267', + memo: 'walmart', + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638996, + amount: -9.24, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 267', + memo: 'walmart', + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627683, + amount: -9.24, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 267', + memo: 'walmart', + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612980, + amount: -9.24, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 267', + memo: 'walmart', + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510753, + tradeDate: 1632463200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603909, + amount: -9.24, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 267', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592601, + amount: -9.24, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 267', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1632484800, + transactionDate: 1632484800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444973, + amount: -9.2, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 263', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441308, + amount: -9.2, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 263', + memo: 'walmart', + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438781, + amount: -9.2, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 263', + memo: 'walmart', + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432983, + amount: -9.2, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 263', + memo: 'walmart', + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425811, + amount: -9.2, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 263', + memo: 'walmart', + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145241, + tradeDate: 1632117600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414795, + amount: -9.2, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 263', + memo: 'walmart', + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402278, + amount: -9.2, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 263', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1632139200, + transactionDate: 1632139200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651850, + amount: 9.14, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 257', + memo: 'walmart', + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '914202123', + }, + { + id: 6411644875, + amount: 9.14, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 257', + memo: 'walmart', + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '914202123', + }, + { + id: 6411639043, + amount: 9.14, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 257', + memo: 'walmart', + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627730, + amount: 9.14, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 257', + memo: 'walmart', + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612960, + amount: 9.14, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 257', + memo: 'walmart', + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510753, + tradeDate: 1631599200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603938, + amount: 9.14, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 257', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592630, + amount: 9.14, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 257', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1631620800, + transactionDate: 1631620800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445008, + amount: 9.1, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 253', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441304, + amount: 9.1, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 253', + memo: 'walmart', + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '910202123', + }, + { + id: 6295438759, + amount: 9.1, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 253', + memo: 'walmart', + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '910202123', + }, + { + id: 6295432972, + amount: 9.1, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 253', + memo: 'walmart', + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425730, + amount: 9.1, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 253', + memo: 'walmart', + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145241, + tradeDate: 1631253600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414798, + amount: 9.1, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 253', + memo: 'walmart', + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402223, + amount: 9.1, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 253', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1631275200, + transactionDate: 1631275200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651865, + amount: -9.04, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 247', + memo: 'walmart', + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644890, + amount: -9.04, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 247', + memo: 'walmart', + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639033, + amount: -9.04, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 247', + memo: 'walmart', + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627720, + amount: -9.04, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 247', + memo: 'walmart', + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612996, + amount: -9.04, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 247', + memo: 'walmart', + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510753, + tradeDate: 1630735200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603917, + amount: -9.04, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 247', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592609, + amount: -9.04, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 247', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1630756800, + transactionDate: 1630756800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444931, + amount: -8.31, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 243', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441373, + amount: -8.31, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 243', + memo: 'walmart', + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438764, + amount: -8.31, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 243', + memo: 'walmart', + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432987, + amount: -8.31, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 243', + memo: 'walmart', + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425724, + amount: -8.31, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 243', + memo: 'walmart', + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145241, + tradeDate: 1630389600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414875, + amount: -8.31, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 243', + memo: 'walmart', + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402216, + amount: -8.31, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 243', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1630411200, + transactionDate: 1630411200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651842, + amount: 8.25, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 237', + memo: 'walmart', + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '825202125', + }, + { + id: 6411644867, + amount: 8.25, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 237', + memo: 'walmart', + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '825202125', + }, + { + id: 6411639014, + amount: 8.25, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 237', + memo: 'walmart', + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627701, + amount: 8.25, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 237', + memo: 'walmart', + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613019, + amount: 8.25, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 237', + memo: 'walmart', + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510753, + tradeDate: 1629871200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603957, + amount: 8.25, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 237', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592649, + amount: 8.25, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 237', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629892800, + transactionDate: 1629892800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444999, + amount: 8.21, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 233', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441282, + amount: 8.21, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 233', + memo: 'walmart', + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '821202125', + }, + { + id: 6295438769, + amount: 8.21, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 233', + memo: 'walmart', + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '821202125', + }, + { + id: 6295432975, + amount: 8.21, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 233', + memo: 'walmart', + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425735, + amount: 8.21, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 233', + memo: 'walmart', + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145241, + tradeDate: 1629525600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414830, + amount: 8.21, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 233', + memo: 'walmart', + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402232, + amount: 8.21, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 233', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629547200, + transactionDate: 1629547200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651828, + amount: -8.15, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 227', + memo: 'walmart', + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644853, + amount: -8.15, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 227', + memo: 'walmart', + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639003, + amount: -8.15, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 227', + memo: 'walmart', + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627690, + amount: -8.15, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 227', + memo: 'walmart', + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613009, + amount: -8.15, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 227', + memo: 'walmart', + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510753, + tradeDate: 1629007200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603939, + amount: -8.15, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 227', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592631, + amount: -8.15, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 227', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1629028800, + transactionDate: 1629028800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444949, + amount: -8.11, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 223', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441375, + amount: -8.11, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 223', + memo: 'walmart', + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438762, + amount: -8.11, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 223', + memo: 'walmart', + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432973, + amount: -8.11, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 223', + memo: 'walmart', + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425758, + amount: -8.11, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 223', + memo: 'walmart', + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145241, + tradeDate: 1628661600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414791, + amount: -8.11, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 223', + memo: 'walmart', + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402220, + amount: -8.11, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 223', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1628683200, + transactionDate: 1628683200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651834, + amount: 8.05, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 217', + memo: 'walmart', + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '85202127', + }, + { + id: 6411644859, + amount: 8.05, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 217', + memo: 'walmart', + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '85202127', + }, + { + id: 6411639008, + amount: 8.05, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 217', + memo: 'walmart', + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627695, + amount: 8.05, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 217', + memo: 'walmart', + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612993, + amount: 8.05, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 217', + memo: 'walmart', + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510753, + tradeDate: 1628143200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603918, + amount: 8.05, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 217', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592610, + amount: 8.05, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 217', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1628164800, + transactionDate: 1628164800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444917, + amount: 8.01, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 213', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441337, + amount: 8.01, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 213', + memo: 'walmart', + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '81202127', + }, + { + id: 6295438754, + amount: 8.01, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 213', + memo: 'walmart', + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '81202127', + }, + { + id: 6295432995, + amount: 8.01, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 213', + memo: 'walmart', + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425750, + amount: 8.01, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 213', + memo: 'walmart', + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145241, + tradeDate: 1627797600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414817, + amount: 8.01, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 213', + memo: 'walmart', + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402214, + amount: 8.01, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 213', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1627819200, + transactionDate: 1627819200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651876, + amount: -7.26, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 207', + memo: 'walmart', + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644901, + amount: -7.26, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 207', + memo: 'walmart', + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639048, + amount: -7.26, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 207', + memo: 'walmart', + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627735, + amount: -7.26, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 207', + memo: 'walmart', + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613023, + amount: -7.26, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 207', + memo: 'walmart', + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510753, + tradeDate: 1627279200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603961, + amount: -7.26, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 207', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592653, + amount: -7.26, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 207', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1627300800, + transactionDate: 1627300800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444987, + amount: -7.22, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 203', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441382, + amount: -7.22, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 203', + memo: 'walmart', + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438803, + amount: -7.22, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 203', + memo: 'walmart', + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432970, + amount: -7.22, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 203', + memo: 'walmart', + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425742, + amount: -7.22, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 203', + memo: 'walmart', + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145241, + tradeDate: 1626933600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414869, + amount: -7.22, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 203', + memo: 'walmart', + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402246, + amount: -7.22, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 203', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626955200, + transactionDate: 1626955200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651810, + amount: 7.16, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 197', + memo: 'walmart', + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '716202129', + }, + { + id: 6411644835, + amount: 7.16, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 197', + memo: 'walmart', + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '716202129', + }, + { + id: 6411638985, + amount: 7.16, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 197', + memo: 'walmart', + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627672, + amount: 7.16, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 197', + memo: 'walmart', + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612964, + amount: 7.16, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 197', + memo: 'walmart', + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510753, + tradeDate: 1626415200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603902, + amount: 7.16, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 197', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592594, + amount: 7.16, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 197', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626436800, + transactionDate: 1626436800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444920, + amount: 7.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 193', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441305, + amount: 7.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 193', + memo: 'walmart', + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '712202129', + }, + { + id: 6295438811, + amount: 7.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 193', + memo: 'walmart', + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '712202129', + }, + { + id: 6295432951, + amount: 7.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 193', + memo: 'walmart', + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425753, + amount: 7.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 193', + memo: 'walmart', + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145241, + tradeDate: 1626069600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414846, + amount: 7.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 193', + memo: 'walmart', + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402270, + amount: 7.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 193', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1626091200, + transactionDate: 1626091200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651827, + amount: -7.06, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 187', + memo: 'walmart', + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644852, + amount: -7.06, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 187', + memo: 'walmart', + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639002, + amount: -7.06, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 187', + memo: 'walmart', + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627689, + amount: -7.06, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 187', + memo: 'walmart', + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612997, + amount: -7.06, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 187', + memo: 'walmart', + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510753, + tradeDate: 1625551200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603924, + amount: -7.06, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 187', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592616, + amount: -7.06, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 187', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1625572800, + transactionDate: 1625572800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444948, + amount: -7.02, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 183', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441363, + amount: -7.02, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 183', + memo: 'walmart', + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438785, + amount: -7.02, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 183', + memo: 'walmart', + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433005, + amount: -7.02, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 183', + memo: 'walmart', + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425782, + amount: -7.02, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 183', + memo: 'walmart', + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145241, + tradeDate: 1625205600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414863, + amount: -7.02, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 183', + memo: 'walmart', + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402252, + amount: -7.02, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 183', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1625227200, + transactionDate: 1625227200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651873, + amount: 6.26, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 177', + memo: 'walmart', + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '626202131', + }, + { + id: 6411644898, + amount: 6.26, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 177', + memo: 'walmart', + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '626202131', + }, + { + id: 6411639044, + amount: 6.26, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 177', + memo: 'walmart', + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627731, + amount: 6.26, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 177', + memo: 'walmart', + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613003, + amount: 6.26, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 177', + memo: 'walmart', + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510753, + tradeDate: 1624687200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603951, + amount: 6.26, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 177', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592643, + amount: 6.26, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 177', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1624708800, + transactionDate: 1624708800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444981, + amount: 6.22, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 173', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441318, + amount: 6.22, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 173', + memo: 'walmart', + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '622202131', + }, + { + id: 6295438737, + amount: 6.22, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 173', + memo: 'walmart', + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '622202131', + }, + { + id: 6295432994, + amount: 6.22, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 173', + memo: 'walmart', + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425751, + amount: 6.22, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 173', + memo: 'walmart', + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145241, + tradeDate: 1624341600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414821, + amount: 6.22, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 173', + memo: 'walmart', + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402215, + amount: 6.22, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 173', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1624363200, + transactionDate: 1624363200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651854, + amount: -6.16, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 167', + memo: 'walmart', + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644879, + amount: -6.16, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 167', + memo: 'walmart', + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639023, + amount: -6.16, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 167', + memo: 'walmart', + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627710, + amount: -6.16, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 167', + memo: 'walmart', + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612981, + amount: -6.16, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 167', + memo: 'walmart', + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510753, + tradeDate: 1623823200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603940, + amount: -6.16, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 167', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592632, + amount: -6.16, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 167', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1623844800, + transactionDate: 1623844800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444997, + amount: -6.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 163', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441365, + amount: -6.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 163', + memo: 'walmart', + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438827, + amount: -6.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 163', + memo: 'walmart', + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433053, + amount: -6.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 163', + memo: 'walmart', + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425743, + amount: -6.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 163', + memo: 'walmart', + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145241, + tradeDate: 1623477600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414803, + amount: -6.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 163', + memo: 'walmart', + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402258, + amount: -6.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 163', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1623499200, + transactionDate: 1623499200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651835, + amount: 6.06, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 157', + memo: 'walmart', + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '66202133', + }, + { + id: 6411644860, + amount: 6.06, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 157', + memo: 'walmart', + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '66202133', + }, + { + id: 6411639009, + amount: 6.06, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 157', + memo: 'walmart', + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627696, + amount: 6.06, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 157', + memo: 'walmart', + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612968, + amount: 6.06, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 157', + memo: 'walmart', + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510753, + tradeDate: 1622959200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603919, + amount: 6.06, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 157', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592611, + amount: 6.06, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 157', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622980800, + transactionDate: 1622980800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444982, + amount: 6.02, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 153', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441279, + amount: 6.02, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 153', + memo: 'walmart', + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '62202133', + }, + { + id: 6295438813, + amount: 6.02, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 153', + memo: 'walmart', + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '62202133', + }, + { + id: 6295433033, + amount: 6.02, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 153', + memo: 'walmart', + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425810, + amount: 6.02, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 153', + memo: 'walmart', + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145241, + tradeDate: 1622613600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414873, + amount: 6.02, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 153', + memo: 'walmart', + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402273, + amount: 6.02, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 153', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622635200, + transactionDate: 1622635200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651877, + amount: -5.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 147', + memo: 'walmart', + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644902, + amount: -5.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 147', + memo: 'walmart', + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639049, + amount: -5.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 147', + memo: 'walmart', + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627736, + amount: -5.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 147', + memo: 'walmart', + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612976, + amount: -5.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 147', + memo: 'walmart', + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510753, + tradeDate: 1622095200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603927, + amount: -5.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 147', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592619, + amount: -5.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 147', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1622116800, + transactionDate: 1622116800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444957, + amount: -5.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 143', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441358, + amount: -5.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 143', + memo: 'walmart', + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438772, + amount: -5.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 143', + memo: 'walmart', + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432992, + amount: -5.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 143', + memo: 'walmart', + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425714, + amount: -5.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 143', + memo: 'walmart', + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145241, + tradeDate: 1621749600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414842, + amount: -5.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 143', + memo: 'walmart', + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402267, + amount: -5.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 143', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1621771200, + transactionDate: 1621771200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651856, + amount: 5.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 137', + memo: 'walmart', + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '517202135', + }, + { + id: 6411644881, + amount: 5.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 137', + memo: 'walmart', + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '517202135', + }, + { + id: 6411639031, + amount: 5.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 137', + memo: 'walmart', + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627718, + amount: 5.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 137', + memo: 'walmart', + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612986, + amount: 5.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 137', + memo: 'walmart', + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510753, + tradeDate: 1621231200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603904, + amount: 5.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 137', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592596, + amount: 5.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 137', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1621252800, + transactionDate: 1621252800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445002, + amount: 5.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 133', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441351, + amount: 5.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 133', + memo: 'walmart', + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '513202135', + }, + { + id: 6295438757, + amount: 5.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 133', + memo: 'walmart', + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '513202135', + }, + { + id: 6295433039, + amount: 5.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 133', + memo: 'walmart', + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425795, + amount: 5.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 133', + memo: 'walmart', + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145241, + tradeDate: 1620885600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414858, + amount: 5.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 133', + memo: 'walmart', + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402219, + amount: 5.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 133', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620907200, + transactionDate: 1620907200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651820, + amount: -5.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 127', + memo: 'walmart', + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644845, + amount: -5.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 127', + memo: 'walmart', + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638995, + amount: -5.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 127', + memo: 'walmart', + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627682, + amount: -5.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 127', + memo: 'walmart', + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613006, + amount: -5.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 127', + memo: 'walmart', + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510753, + tradeDate: 1620367200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603953, + amount: -5.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 127', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592645, + amount: -5.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 127', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620388800, + transactionDate: 1620388800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444963, + amount: -5.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 123', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441296, + amount: -5.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 123', + memo: 'walmart', + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438782, + amount: -5.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 123', + memo: 'walmart', + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432986, + amount: -5.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 123', + memo: 'walmart', + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425806, + amount: -5.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 123', + memo: 'walmart', + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145241, + tradeDate: 1620021600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414780, + amount: -5.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 123', + memo: 'walmart', + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402201, + amount: -5.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 123', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1620043200, + transactionDate: 1620043200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651812, + amount: 4.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 117', + memo: 'walmart', + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '427202137', + }, + { + id: 6411644837, + amount: 4.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 117', + memo: 'walmart', + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '427202137', + }, + { + id: 6411638987, + amount: 4.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 117', + memo: 'walmart', + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627674, + amount: 4.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 117', + memo: 'walmart', + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613027, + amount: 4.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 117', + memo: 'walmart', + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510753, + tradeDate: 1619503200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603948, + amount: 4.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 117', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592640, + amount: 4.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 117', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1619524800, + transactionDate: 1619524800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445001, + amount: 4.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 113', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441323, + amount: 4.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 113', + memo: 'walmart', + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '423202137', + }, + { + id: 6295438778, + amount: 4.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 113', + memo: 'walmart', + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '423202137', + }, + { + id: 6295432988, + amount: 4.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 113', + memo: 'walmart', + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425739, + amount: 4.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 113', + memo: 'walmart', + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145241, + tradeDate: 1619157600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414833, + amount: 4.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 113', + memo: 'walmart', + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402236, + amount: 4.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 113', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1619179200, + transactionDate: 1619179200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651845, + amount: -4.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 107', + memo: 'walmart', + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644870, + amount: -4.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 107', + memo: 'walmart', + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639017, + amount: -4.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 107', + memo: 'walmart', + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627704, + amount: -4.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 107', + memo: 'walmart', + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613002, + amount: -4.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 107', + memo: 'walmart', + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510753, + tradeDate: 1618639200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603928, + amount: -4.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 107', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592620, + amount: -4.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 107', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1618660800, + transactionDate: 1618660800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444937, + amount: -4.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 103', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441371, + amount: -4.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 103', + memo: 'walmart', + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438766, + amount: -4.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 103', + memo: 'walmart', + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432989, + amount: -4.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 103', + memo: 'walmart', + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425726, + amount: -4.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 103', + memo: 'walmart', + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145241, + tradeDate: 1618293600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414871, + amount: -4.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 103', + memo: 'walmart', + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402207, + amount: -4.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 103', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1618315200, + transactionDate: 1618315200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651866, + amount: 4.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 97', + memo: 'walmart', + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '47202139', + }, + { + id: 6411644891, + amount: 4.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 97', + memo: 'walmart', + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '47202139', + }, + { + id: 6411639034, + amount: 4.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 97', + memo: 'walmart', + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627721, + amount: 4.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 97', + memo: 'walmart', + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613015, + amount: 4.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 97', + memo: 'walmart', + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510753, + tradeDate: 1617775200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603914, + amount: 4.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 97', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592606, + amount: 4.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 97', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1617796800, + transactionDate: 1617796800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444946, + amount: 4.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 93', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441357, + amount: 4.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 93', + memo: 'walmart', + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '43202139', + }, + { + id: 6295438779, + amount: 4.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 93', + memo: 'walmart', + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '43202139', + }, + { + id: 6295433013, + amount: 4.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 93', + memo: 'walmart', + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425812, + amount: 4.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 93', + memo: 'walmart', + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145241, + tradeDate: 1617429600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414816, + amount: 4.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 93', + memo: 'walmart', + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402256, + amount: 4.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 93', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1617451200, + transactionDate: 1617451200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651826, + amount: -3.28, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 87', + memo: 'walmart', + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644851, + amount: -3.28, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 87', + memo: 'walmart', + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639001, + amount: -3.28, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 87', + memo: 'walmart', + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627688, + amount: -3.28, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 87', + memo: 'walmart', + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612989, + amount: -3.28, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 87', + memo: 'walmart', + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510753, + tradeDate: 1616911200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603913, + amount: -3.28, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 87', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592605, + amount: -3.28, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 87', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616932800, + transactionDate: 1616932800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444915, + amount: -3.24, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 83', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441355, + amount: -3.24, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 83', + memo: 'walmart', + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438756, + amount: -3.24, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 83', + memo: 'walmart', + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433027, + amount: -3.24, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 83', + memo: 'walmart', + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425757, + amount: -3.24, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 83', + memo: 'walmart', + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145241, + tradeDate: 1616565600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414883, + amount: -3.24, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 83', + memo: 'walmart', + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402279, + amount: -3.24, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 83', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616587200, + transactionDate: 1616587200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651824, + amount: 3.18, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 77', + memo: 'walmart', + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '318202141', + }, + { + id: 6411644849, + amount: 3.18, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 77', + memo: 'walmart', + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '318202141', + }, + { + id: 6411638999, + amount: 3.18, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 77', + memo: 'walmart', + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627686, + amount: 3.18, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 77', + memo: 'walmart', + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612987, + amount: 3.18, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 77', + memo: 'walmart', + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510753, + tradeDate: 1616047200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603911, + amount: 3.18, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 77', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592603, + amount: 3.18, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 77', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1616068800, + transactionDate: 1616068800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444940, + amount: 3.14, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 73', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441313, + amount: 3.14, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 73', + memo: 'walmart', + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '314202141', + }, + { + id: 6295438768, + amount: 3.14, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 73', + memo: 'walmart', + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '314202141', + }, + { + id: 6295432961, + amount: 3.14, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 73', + memo: 'walmart', + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425802, + amount: 3.14, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 73', + memo: 'walmart', + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145241, + tradeDate: 1615705200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414820, + amount: 3.14, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 73', + memo: 'walmart', + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402286, + amount: 3.14, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 73', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1615723200, + transactionDate: 1615723200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651813, + amount: -3.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 67', + memo: 'walmart', + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644838, + amount: -3.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 67', + memo: 'walmart', + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638988, + amount: -3.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 67', + memo: 'walmart', + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627675, + amount: -3.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 67', + memo: 'walmart', + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613004, + amount: -3.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 67', + memo: 'walmart', + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510753, + tradeDate: 1615186800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603936, + amount: -3.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 67', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592628, + amount: -3.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 67', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1615204800, + transactionDate: 1615204800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444906, + amount: -3.04, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 63', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441339, + amount: -3.04, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 63', + memo: 'walmart', + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438761, + amount: -3.04, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 63', + memo: 'walmart', + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433014, + amount: -3.04, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 63', + memo: 'walmart', + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425778, + amount: -3.04, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 63', + memo: 'walmart', + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145241, + tradeDate: 1614841200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414844, + amount: -3.04, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 63', + memo: 'walmart', + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402269, + amount: -3.04, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 63', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1614859200, + transactionDate: 1614859200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651863, + amount: 2.26, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 57', + memo: 'walmart', + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '226202143', + }, + { + id: 6411644888, + amount: 2.26, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 57', + memo: 'walmart', + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '226202143', + }, + { + id: 6411639039, + amount: 2.26, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 57', + memo: 'walmart', + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627726, + amount: 2.26, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 57', + memo: 'walmart', + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612982, + amount: 2.26, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 57', + memo: 'walmart', + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510753, + tradeDate: 1614322800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603960, + amount: 2.26, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 57', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592652, + amount: 2.26, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 57', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1614340800, + transactionDate: 1614340800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444935, + amount: 2.22, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 53', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441348, + amount: 2.22, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 53', + memo: 'walmart', + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '222202143', + }, + { + id: 6295438740, + amount: 2.22, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 53', + memo: 'walmart', + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '222202143', + }, + { + id: 6295433022, + amount: 2.22, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 53', + memo: 'walmart', + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425791, + amount: 2.22, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 53', + memo: 'walmart', + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145241, + tradeDate: 1613977200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414823, + amount: 2.22, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 53', + memo: 'walmart', + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402291, + amount: 2.22, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 53', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613995200, + transactionDate: 1613995200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651848, + amount: -2.16, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 47', + memo: 'walmart', + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644873, + amount: -2.16, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 47', + memo: 'walmart', + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639019, + amount: -2.16, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 47', + memo: 'walmart', + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627706, + amount: -2.16, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 47', + memo: 'walmart', + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612983, + amount: -2.16, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 47', + memo: 'walmart', + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510753, + tradeDate: 1613458800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603930, + amount: -2.16, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 47', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592622, + amount: -2.16, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 47', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613476800, + transactionDate: 1613476800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444961, + amount: -2.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 43', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441287, + amount: -2.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 43', + memo: 'walmart', + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438798, + amount: -2.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 43', + memo: 'walmart', + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432977, + amount: -2.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 43', + memo: 'walmart', + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425725, + amount: -2.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 43', + memo: 'walmart', + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145241, + tradeDate: 1613113200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414826, + amount: -2.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 43', + memo: 'walmart', + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402222, + amount: -2.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 43', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1613131200, + transactionDate: 1613131200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651818, + amount: 2.06, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 37', + memo: 'walmart', + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '26202145', + }, + { + id: 6411644843, + amount: 2.06, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 37', + memo: 'walmart', + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '26202145', + }, + { + id: 6411638993, + amount: 2.06, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 37', + memo: 'walmart', + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627680, + amount: 2.06, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 37', + memo: 'walmart', + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612971, + amount: 2.06, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 37', + memo: 'walmart', + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510753, + tradeDate: 1612594800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603907, + amount: 2.06, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 37', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592599, + amount: 2.06, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 37', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1612612800, + transactionDate: 1612612800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444924, + amount: 2.02, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 33', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441326, + amount: 2.02, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 33', + memo: 'walmart', + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '22202145', + }, + { + id: 6295438760, + amount: 2.02, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 33', + memo: 'walmart', + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '22202145', + }, + { + id: 6295432969, + amount: 2.02, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 33', + memo: 'walmart', + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425717, + amount: 2.02, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 33', + memo: 'walmart', + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145241, + tradeDate: 1612249200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414810, + amount: 2.02, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 33', + memo: 'walmart', + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402290, + amount: 2.02, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 33', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1612267200, + transactionDate: 1612267200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651872, + amount: -1.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 27', + memo: 'walmart', + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644897, + amount: -1.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 27', + memo: 'walmart', + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639042, + amount: -1.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 27', + memo: 'walmart', + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627729, + amount: -1.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 27', + memo: 'walmart', + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612965, + amount: -1.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 27', + memo: 'walmart', + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510753, + tradeDate: 1611730800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603943, + amount: -1.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 27', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592635, + amount: -1.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 27', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1611748800, + transactionDate: 1611748800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444900, + amount: -1.23, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 23', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441364, + amount: -1.23, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 23', + memo: 'walmart', + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438735, + amount: -1.23, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 23', + memo: 'walmart', + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433023, + amount: -1.23, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 23', + memo: 'walmart', + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425801, + amount: -1.23, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 23', + memo: 'walmart', + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145241, + tradeDate: 1611385200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414836, + amount: -1.23, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 23', + memo: 'walmart', + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402229, + amount: -1.23, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 23', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1611403200, + transactionDate: 1611403200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651864, + amount: 1.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 17', + memo: 'walmart', + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '117202147', + }, + { + id: 6411644889, + amount: 1.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 17', + memo: 'walmart', + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '117202147', + }, + { + id: 6411639032, + amount: 1.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 17', + memo: 'walmart', + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627719, + amount: 1.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 17', + memo: 'walmart', + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613000, + amount: 1.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 17', + memo: 'walmart', + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510753, + tradeDate: 1610866800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603923, + amount: 1.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 17', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592615, + amount: 1.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 17', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610884800, + transactionDate: 1610884800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444996, + amount: 1.13, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 13', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441290, + amount: 1.13, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 13', + memo: 'walmart', + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '113202147', + }, + { + id: 6295438820, + amount: 1.13, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 13', + memo: 'walmart', + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '113202147', + }, + { + id: 6295433054, + amount: 1.13, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 13', + memo: 'walmart', + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425737, + amount: 1.13, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 13', + memo: 'walmart', + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145241, + tradeDate: 1610521200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414824, + amount: 1.13, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 13', + memo: 'walmart', + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402210, + amount: 1.13, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 13', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610539200, + transactionDate: 1610539200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651839, + amount: -1.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 7', + memo: 'walmart', + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644864, + amount: -1.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 7', + memo: 'walmart', + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639011, + amount: -1.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 7', + memo: 'walmart', + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627698, + amount: -1.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 7', + memo: 'walmart', + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613020, + amount: -1.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 7', + memo: 'walmart', + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510753, + tradeDate: 1610002800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603958, + amount: -1.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 7', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592650, + amount: -1.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 7', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1610020800, + transactionDate: 1610020800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444991, + amount: -1.03, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 3', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441362, + amount: -1.03, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 3', + memo: 'walmart', + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438770, + amount: -1.03, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 3', + memo: 'walmart', + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433051, + amount: -1.03, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 3', + memo: 'walmart', + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425784, + amount: -1.03, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 3', + memo: 'walmart', + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145241, + tradeDate: 1609657200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414825, + amount: -1.03, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 3', + memo: 'walmart', + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402275, + amount: -1.03, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 3', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1609675200, + transactionDate: 1609675200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651829, + amount: 12.28, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 363', + memo: 'walmart', + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1228202049', + }, + { + id: 6411644854, + amount: 12.28, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 363', + memo: 'walmart', + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1228202049', + }, + { + id: 6411639052, + amount: 12.28, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 363', + memo: 'walmart', + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627739, + amount: 12.28, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 363', + memo: 'walmart', + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613028, + amount: 12.28, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 363', + memo: 'walmart', + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510753, + tradeDate: 1609138800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603967, + amount: 12.28, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 363', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592659, + amount: 12.28, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 363', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1609156800, + transactionDate: 1609156800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444969, + amount: 12.24, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 359', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441322, + amount: 12.24, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 359', + memo: 'walmart', + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1224202049', + }, + { + id: 6295438775, + amount: 12.24, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 359', + memo: 'walmart', + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1224202049', + }, + { + id: 6295432980, + amount: 12.24, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 359', + memo: 'walmart', + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425719, + amount: 12.24, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 359', + memo: 'walmart', + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145241, + tradeDate: 1608793200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414832, + amount: 12.24, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 359', + memo: 'walmart', + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402230, + amount: 12.24, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 359', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1608811200, + transactionDate: 1608811200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651860, + amount: -12.18, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 353', + memo: 'walmart', + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644885, + amount: -12.18, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 353', + memo: 'walmart', + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639027, + amount: -12.18, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 353', + memo: 'walmart', + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627714, + amount: -12.18, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 353', + memo: 'walmart', + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613010, + amount: -12.18, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 353', + memo: 'walmart', + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510753, + tradeDate: 1608274800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603944, + amount: -12.18, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 353', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592636, + amount: -12.18, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 353', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1608292800, + transactionDate: 1608292800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444965, + amount: -12.14, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 349', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441332, + amount: -12.14, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 349', + memo: 'walmart', + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438810, + amount: -12.14, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 349', + memo: 'walmart', + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432960, + amount: -12.14, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 349', + memo: 'walmart', + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425768, + amount: -12.14, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 349', + memo: 'walmart', + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145241, + tradeDate: 1607929200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414835, + amount: -12.14, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 349', + memo: 'walmart', + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402191, + amount: -12.14, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 349', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607947200, + transactionDate: 1607947200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651808, + amount: 12.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 343', + memo: 'walmart', + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '128202051', + }, + { + id: 6411644833, + amount: 12.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 343', + memo: 'walmart', + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '128202051', + }, + { + id: 6411638983, + amount: 12.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 343', + memo: 'walmart', + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627670, + amount: 12.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 343', + memo: 'walmart', + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612959, + amount: 12.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 343', + memo: 'walmart', + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510753, + tradeDate: 1607410800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603900, + amount: 12.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 343', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592592, + amount: 12.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 343', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607428800, + transactionDate: 1607428800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444925, + amount: 12.04, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 339', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441334, + amount: 12.04, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 339', + memo: 'walmart', + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '124202051', + }, + { + id: 6295438805, + amount: 12.04, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 339', + memo: 'walmart', + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '124202051', + }, + { + id: 6295433001, + amount: 12.04, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 339', + memo: 'walmart', + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425752, + amount: 12.04, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 339', + memo: 'walmart', + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145241, + tradeDate: 1607065200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414827, + amount: 12.04, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 339', + memo: 'walmart', + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402265, + amount: 12.04, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 339', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1607083200, + transactionDate: 1607083200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651809, + amount: -11.28, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 333', + memo: 'walmart', + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644834, + amount: -11.28, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 333', + memo: 'walmart', + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638984, + amount: -11.28, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 333', + memo: 'walmart', + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627671, + amount: -11.28, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 333', + memo: 'walmart', + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612961, + amount: -11.28, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 333', + memo: 'walmart', + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510753, + tradeDate: 1606546800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603901, + amount: -11.28, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 333', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592593, + amount: -11.28, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 333', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1606564800, + transactionDate: 1606564800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444972, + amount: -11.24, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 329', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441302, + amount: -11.24, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 329', + memo: 'walmart', + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438822, + amount: -11.24, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 329', + memo: 'walmart', + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433031, + amount: -11.24, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 329', + memo: 'walmart', + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425754, + amount: -11.24, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 329', + memo: 'walmart', + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145241, + tradeDate: 1606201200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414801, + amount: -11.24, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 329', + memo: 'walmart', + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402231, + amount: -11.24, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 329', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1606219200, + transactionDate: 1606219200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651831, + amount: 11.18, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 323', + memo: 'walmart', + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1118202053', + }, + { + id: 6411644856, + amount: 11.18, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 323', + memo: 'walmart', + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1118202053', + }, + { + id: 6411639005, + amount: 11.18, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 323', + memo: 'walmart', + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627692, + amount: 11.18, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 323', + memo: 'walmart', + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612998, + amount: 11.18, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 323', + memo: 'walmart', + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510753, + tradeDate: 1605682800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603925, + amount: 11.18, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 323', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592617, + amount: 11.18, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 323', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1605700800, + transactionDate: 1605700800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444905, + amount: 11.14, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 319', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441317, + amount: 11.14, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 319', + memo: 'walmart', + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1114202053', + }, + { + id: 6295438806, + amount: 11.14, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 319', + memo: 'walmart', + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1114202053', + }, + { + id: 6295432982, + amount: 11.14, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 319', + memo: 'walmart', + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425762, + amount: 11.14, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 319', + memo: 'walmart', + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145241, + tradeDate: 1605337200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414845, + amount: 11.14, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 319', + memo: 'walmart', + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402260, + amount: 11.14, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 319', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1605355200, + transactionDate: 1605355200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651874, + amount: -11.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 313', + memo: 'walmart', + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644899, + amount: -11.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 313', + memo: 'walmart', + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639045, + amount: -11.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 313', + memo: 'walmart', + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627732, + amount: -11.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 313', + memo: 'walmart', + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612962, + amount: -11.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 313', + memo: 'walmart', + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510753, + tradeDate: 1604818800, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603952, + amount: -11.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 313', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592644, + amount: -11.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 313', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1604836800, + transactionDate: 1604836800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444956, + amount: -11.04, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 309', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441281, + amount: -11.04, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 309', + memo: 'walmart', + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438788, + amount: -11.04, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 309', + memo: 'walmart', + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433000, + amount: -11.04, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 309', + memo: 'walmart', + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425790, + amount: -11.04, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 309', + memo: 'walmart', + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145241, + tradeDate: 1604473200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414797, + amount: -11.04, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 309', + memo: 'walmart', + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402213, + amount: -11.04, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 309', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1604491200, + transactionDate: 1604491200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651855, + amount: 10.29, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 303', + memo: 'walmart', + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1029202055', + }, + { + id: 6411644880, + amount: 10.29, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 303', + memo: 'walmart', + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1029202055', + }, + { + id: 6411639024, + amount: 10.29, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 303', + memo: 'walmart', + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627711, + amount: 10.29, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 303', + memo: 'walmart', + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612972, + amount: 10.29, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 303', + memo: 'walmart', + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510753, + tradeDate: 1603951200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603941, + amount: 10.29, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 303', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592633, + amount: 10.29, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 303', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603972800, + transactionDate: 1603972800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444960, + amount: 10.25, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 299', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441299, + amount: 10.25, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 299', + memo: 'walmart', + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1025202055', + }, + { + id: 6295438826, + amount: 10.25, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 299', + memo: 'walmart', + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '1025202055', + }, + { + id: 6295433018, + amount: 10.25, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 299', + memo: 'walmart', + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425705, + amount: 10.25, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 299', + memo: 'walmart', + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145241, + tradeDate: 1603605600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414775, + amount: 10.25, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 299', + memo: 'walmart', + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402294, + amount: 10.25, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 299', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603627200, + transactionDate: 1603627200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651837, + amount: -10.19, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 293', + memo: 'walmart', + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644862, + amount: -10.19, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 293', + memo: 'walmart', + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639010, + amount: -10.19, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 293', + memo: 'walmart', + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627697, + amount: -10.19, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 293', + memo: 'walmart', + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612969, + amount: -10.19, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 293', + memo: 'walmart', + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510753, + tradeDate: 1603087200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603920, + amount: -10.19, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 293', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592612, + amount: -10.19, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 293', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1603108800, + transactionDate: 1603108800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444928, + amount: -10.15, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 289', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441341, + amount: -10.15, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 289', + memo: 'walmart', + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438730, + amount: -10.15, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 289', + memo: 'walmart', + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433026, + amount: -10.15, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 289', + memo: 'walmart', + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425792, + amount: -10.15, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 289', + memo: 'walmart', + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145241, + tradeDate: 1602741600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414829, + amount: -10.15, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 289', + memo: 'walmart', + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402292, + amount: -10.15, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 289', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1602763200, + transactionDate: 1602763200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651838, + amount: 10.09, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 283', + memo: 'walmart', + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '109202057', + }, + { + id: 6411644863, + amount: 10.09, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 283', + memo: 'walmart', + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '109202057', + }, + { + id: 6411639050, + amount: 10.09, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 283', + memo: 'walmart', + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627737, + amount: 10.09, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 283', + memo: 'walmart', + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612975, + amount: 10.09, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 283', + memo: 'walmart', + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510753, + tradeDate: 1602223200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603933, + amount: 10.09, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 283', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592625, + amount: 10.09, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 283', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1602244800, + transactionDate: 1602244800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444976, + amount: 10.05, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 279', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441284, + amount: 10.05, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 279', + memo: 'walmart', + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '105202057', + }, + { + id: 6295438749, + amount: 10.05, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 279', + memo: 'walmart', + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '105202057', + }, + { + id: 6295433029, + amount: 10.05, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 279', + memo: 'walmart', + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425807, + amount: 10.05, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 279', + memo: 'walmart', + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145241, + tradeDate: 1601877600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414793, + amount: 10.05, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 279', + memo: 'walmart', + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402221, + amount: 10.05, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 279', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601899200, + transactionDate: 1601899200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651862, + amount: -9.29, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 273', + memo: 'walmart', + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644887, + amount: -9.29, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 273', + memo: 'walmart', + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639030, + amount: -9.29, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 273', + memo: 'walmart', + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627717, + amount: -9.29, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 273', + memo: 'walmart', + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612991, + amount: -9.29, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 273', + memo: 'walmart', + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510753, + tradeDate: 1601359200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603910, + amount: -9.29, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 273', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592602, + amount: -9.29, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 273', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601380800, + transactionDate: 1601380800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444927, + amount: -9.25, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 269', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441328, + amount: -9.25, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 269', + memo: 'walmart', + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438808, + amount: -9.25, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 269', + memo: 'walmart', + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432968, + amount: -9.25, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 269', + memo: 'walmart', + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425723, + amount: -9.25, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 269', + memo: 'walmart', + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145241, + tradeDate: 1601013600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414813, + amount: -9.25, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 269', + memo: 'walmart', + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402281, + amount: -9.25, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 269', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1601035200, + transactionDate: 1601035200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651852, + amount: 9.19, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 263', + memo: 'walmart', + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '919202059', + }, + { + id: 6411644877, + amount: 9.19, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 263', + memo: 'walmart', + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '919202059', + }, + { + id: 6411639022, + amount: 9.19, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 263', + memo: 'walmart', + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627709, + amount: 9.19, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 263', + memo: 'walmart', + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613011, + amount: 9.19, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 263', + memo: 'walmart', + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510753, + tradeDate: 1600495200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603945, + amount: 9.19, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 263', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592637, + amount: 9.19, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 263', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1600516800, + transactionDate: 1600516800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444939, + amount: 9.15, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 259', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441297, + amount: 9.15, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 259', + memo: 'walmart', + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '915202059', + }, + { + id: 6295438818, + amount: 9.15, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 259', + memo: 'walmart', + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '915202059', + }, + { + id: 6295432953, + amount: 9.15, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 259', + memo: 'walmart', + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425787, + amount: 9.15, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 259', + memo: 'walmart', + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145241, + tradeDate: 1600149600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414872, + amount: 9.15, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 259', + memo: 'walmart', + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402288, + amount: 9.15, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 259', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1600171200, + transactionDate: 1600171200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651822, + amount: -9.09, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 253', + memo: 'walmart', + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644847, + amount: -9.09, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 253', + memo: 'walmart', + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638997, + amount: -9.09, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 253', + memo: 'walmart', + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627684, + amount: -9.09, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 253', + memo: 'walmart', + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613025, + amount: -9.09, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 253', + memo: 'walmart', + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510753, + tradeDate: 1599631200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603964, + amount: -9.09, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 253', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592656, + amount: -9.09, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 253', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1599652800, + transactionDate: 1599652800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444979, + amount: -9.05, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 249', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441329, + amount: -9.05, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 249', + memo: 'walmart', + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438750, + amount: -9.05, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 249', + memo: 'walmart', + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433017, + amount: -9.05, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 249', + memo: 'walmart', + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425772, + amount: -9.05, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 249', + memo: 'walmart', + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145241, + tradeDate: 1599285600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414831, + amount: -9.05, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 249', + memo: 'walmart', + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402287, + amount: -9.05, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 249', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1599307200, + transactionDate: 1599307200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651846, + amount: 8.3, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 243', + memo: 'walmart', + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '830202061', + }, + { + id: 6411644871, + amount: 8.3, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 243', + memo: 'walmart', + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '830202061', + }, + { + id: 6411639046, + amount: 8.3, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 243', + memo: 'walmart', + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627733, + amount: 8.3, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 243', + memo: 'walmart', + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613030, + amount: 8.3, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 243', + memo: 'walmart', + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510753, + tradeDate: 1598767200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603969, + amount: 8.3, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 243', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592661, + amount: 8.3, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 243', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1598788800, + transactionDate: 1598788800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444943, + amount: 8.26, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 239', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441346, + amount: 8.26, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 239', + memo: 'walmart', + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '826202061', + }, + { + id: 6295438823, + amount: 8.26, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 239', + memo: 'walmart', + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '826202061', + }, + { + id: 6295433047, + amount: 8.26, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 239', + memo: 'walmart', + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425797, + amount: 8.26, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 239', + memo: 'walmart', + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145241, + tradeDate: 1598421600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414867, + amount: 8.26, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 239', + memo: 'walmart', + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402282, + amount: 8.26, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 239', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1598443200, + transactionDate: 1598443200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651867, + amount: -8.2, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 233', + memo: 'walmart', + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644892, + amount: -8.2, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 233', + memo: 'walmart', + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639035, + amount: -8.2, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 233', + memo: 'walmart', + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627722, + amount: -8.2, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 233', + memo: 'walmart', + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613016, + amount: -8.2, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 233', + memo: 'walmart', + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510753, + tradeDate: 1597903200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603915, + amount: -8.2, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 233', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592607, + amount: -8.2, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 233', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597924800, + transactionDate: 1597924800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444954, + amount: -8.16, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 229', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441367, + amount: -8.16, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 229', + memo: 'walmart', + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438780, + amount: -8.16, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 229', + memo: 'walmart', + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432997, + amount: -8.16, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 229', + memo: 'walmart', + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425763, + amount: -8.16, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 229', + memo: 'walmart', + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145241, + tradeDate: 1597557600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414796, + amount: -8.16, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 229', + memo: 'walmart', + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402211, + amount: -8.16, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 229', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597579200, + transactionDate: 1597579200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651843, + amount: 8.1, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 223', + memo: 'walmart', + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '810202063', + }, + { + id: 6411644868, + amount: 8.1, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 223', + memo: 'walmart', + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '810202063', + }, + { + id: 6411639015, + amount: 8.1, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 223', + memo: 'walmart', + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627702, + amount: 8.1, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 223', + memo: 'walmart', + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612995, + amount: 8.1, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 223', + memo: 'walmart', + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510753, + tradeDate: 1597039200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603921, + amount: 8.1, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 223', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592613, + amount: 8.1, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 223', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1597060800, + transactionDate: 1597060800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444921, + amount: 8.06, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 219', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441321, + amount: 8.06, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 219', + memo: 'walmart', + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '86202063', + }, + { + id: 6295438802, + amount: 8.06, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 219', + memo: 'walmart', + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '86202063', + }, + { + id: 6295432996, + amount: 8.06, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 219', + memo: 'walmart', + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425759, + amount: 8.06, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 219', + memo: 'walmart', + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145241, + tradeDate: 1596693600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414834, + amount: 8.06, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 219', + memo: 'walmart', + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402264, + amount: 8.06, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 219', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1596715200, + transactionDate: 1596715200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651825, + amount: -7.31, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 213', + memo: 'walmart', + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644850, + amount: -7.31, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 213', + memo: 'walmart', + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639000, + amount: -7.31, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 213', + memo: 'walmart', + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627687, + amount: -7.31, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 213', + memo: 'walmart', + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612988, + amount: -7.31, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 213', + memo: 'walmart', + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510753, + tradeDate: 1596175200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603912, + amount: -7.31, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 213', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592604, + amount: -7.31, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 213', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1596196800, + transactionDate: 1596196800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445007, + amount: -7.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 209', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441298, + amount: -7.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 209', + memo: 'walmart', + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438828, + amount: -7.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 209', + memo: 'walmart', + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433048, + amount: -7.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 209', + memo: 'walmart', + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425746, + amount: -7.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 209', + memo: 'walmart', + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145241, + tradeDate: 1595829600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414781, + amount: -7.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 209', + memo: 'walmart', + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402196, + amount: -7.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 209', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1595851200, + transactionDate: 1595851200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651815, + amount: 7.21, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 203', + memo: 'walmart', + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '721202065', + }, + { + id: 6411644840, + amount: 7.21, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 203', + memo: 'walmart', + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '721202065', + }, + { + id: 6411638990, + amount: 7.21, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 203', + memo: 'walmart', + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627677, + amount: 7.21, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 203', + memo: 'walmart', + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612990, + amount: 7.21, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 203', + memo: 'walmart', + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510753, + tradeDate: 1595311200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603934, + amount: 7.21, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 203', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592626, + amount: 7.21, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 203', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1595332800, + transactionDate: 1595332800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444951, + amount: 7.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 199', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441295, + amount: 7.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 199', + memo: 'walmart', + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '717202065', + }, + { + id: 6295438824, + amount: 7.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 199', + memo: 'walmart', + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '717202065', + }, + { + id: 6295433024, + amount: 7.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 199', + memo: 'walmart', + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425706, + amount: 7.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 199', + memo: 'walmart', + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145241, + tradeDate: 1594965600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414800, + amount: 7.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 199', + memo: 'walmart', + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402285, + amount: 7.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 199', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594987200, + transactionDate: 1594987200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651871, + amount: -7.11, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 193', + memo: 'walmart', + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644896, + amount: -7.11, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 193', + memo: 'walmart', + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639040, + amount: -7.11, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 193', + memo: 'walmart', + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627727, + amount: -7.11, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 193', + memo: 'walmart', + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612994, + amount: -7.11, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 193', + memo: 'walmart', + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510753, + tradeDate: 1594447200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603962, + amount: -7.11, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 193', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592654, + amount: -7.11, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 193', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594468800, + transactionDate: 1594468800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444916, + amount: -7.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 189', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441352, + amount: -7.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 189', + memo: 'walmart', + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438724, + amount: -7.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 189', + memo: 'walmart', + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433021, + amount: -7.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 189', + memo: 'walmart', + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425738, + amount: -7.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 189', + memo: 'walmart', + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145241, + tradeDate: 1594101600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414853, + amount: -7.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 189', + memo: 'walmart', + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402244, + amount: -7.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 189', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1594123200, + transactionDate: 1594123200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651849, + amount: 7.01, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 183', + memo: 'walmart', + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '71202067', + }, + { + id: 6411644874, + amount: 7.01, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 183', + memo: 'walmart', + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '71202067', + }, + { + id: 6411639020, + amount: 7.01, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 183', + memo: 'walmart', + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627707, + amount: 7.01, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 183', + memo: 'walmart', + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612984, + amount: 7.01, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 183', + memo: 'walmart', + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510753, + tradeDate: 1593583200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603931, + amount: 7.01, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 183', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592623, + amount: 7.01, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 183', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1593604800, + transactionDate: 1593604800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444930, + amount: 6.27, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 179', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441344, + amount: 6.27, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 179', + memo: 'walmart', + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '627202067', + }, + { + id: 6295438748, + amount: 6.27, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 179', + memo: 'walmart', + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '627202067', + }, + { + id: 6295432985, + amount: 6.27, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 179', + memo: 'walmart', + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425732, + amount: 6.27, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 179', + memo: 'walmart', + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145241, + tradeDate: 1593237600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414807, + amount: 6.27, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 179', + memo: 'walmart', + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402204, + amount: 6.27, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 179', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1593259200, + transactionDate: 1593259200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651819, + amount: -6.21, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 173', + memo: 'walmart', + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644844, + amount: -6.21, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 173', + memo: 'walmart', + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411638994, + amount: -6.21, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 173', + memo: 'walmart', + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627681, + amount: -6.21, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 173', + memo: 'walmart', + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411612973, + amount: -6.21, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 173', + memo: 'walmart', + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510753, + tradeDate: 1592719200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603908, + amount: -6.21, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 173', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592600, + amount: -6.21, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 173', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1592740800, + transactionDate: 1592740800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445009, + amount: -6.17, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 169', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441309, + amount: -6.17, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 169', + memo: 'walmart', + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438821, + amount: -6.17, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 169', + memo: 'walmart', + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433056, + amount: -6.17, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 169', + memo: 'walmart', + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425764, + amount: -6.17, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 169', + memo: 'walmart', + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145241, + tradeDate: 1592373600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414794, + amount: -6.17, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 169', + memo: 'walmart', + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402212, + amount: -6.17, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 169', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1592395200, + transactionDate: 1592395200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651869, + amount: 6.11, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 163', + memo: 'walmart', + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '611202069', + }, + { + id: 6411644894, + amount: 6.11, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 163', + memo: 'walmart', + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '611202069', + }, + { + id: 6411639037, + amount: 6.11, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 163', + memo: 'walmart', + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627724, + amount: 6.11, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 163', + memo: 'walmart', + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613022, + amount: 6.11, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 163', + memo: 'walmart', + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510753, + tradeDate: 1591855200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603956, + amount: 6.11, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 163', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592648, + amount: 6.11, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 163', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591876800, + transactionDate: 1591876800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295445000, + amount: 6.07, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 159', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441336, + amount: 6.07, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 159', + memo: 'walmart', + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '67202069', + }, + { + id: 6295438753, + amount: 6.07, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 159', + memo: 'walmart', + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '67202069', + }, + { + id: 6295433041, + amount: 6.07, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 159', + memo: 'walmart', + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425805, + amount: 6.07, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 159', + memo: 'walmart', + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145241, + tradeDate: 1591509600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414874, + amount: 6.07, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 159', + memo: 'walmart', + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402224, + amount: 6.07, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 159', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591531200, + transactionDate: 1591531200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651857, + amount: -6.01, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 153', + memo: 'walmart', + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644882, + amount: -6.01, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 153', + memo: 'walmart', + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639025, + amount: -6.01, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 153', + memo: 'walmart', + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627712, + amount: -6.01, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 153', + memo: 'walmart', + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613017, + amount: -6.01, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 153', + memo: 'walmart', + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510753, + tradeDate: 1590991200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603950, + amount: -6.01, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 153', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592642, + amount: -6.01, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 153', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1591012800, + transactionDate: 1591012800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444907, + amount: -5.28, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 149', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441359, + amount: -5.28, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 149', + memo: 'walmart', + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438825, + amount: -5.28, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 149', + memo: 'walmart', + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295433045, + amount: -5.28, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 149', + memo: 'walmart', + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425755, + amount: -5.28, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 149', + memo: 'walmart', + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145241, + tradeDate: 1590645600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414861, + amount: -5.28, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 149', + memo: 'walmart', + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402234, + amount: -5.28, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 149', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1590667200, + transactionDate: 1590667200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651840, + amount: 5.22, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 143', + memo: 'walmart', + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '522202071', + }, + { + id: 6411644865, + amount: 5.22, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 143', + memo: 'walmart', + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '522202071', + }, + { + id: 6411639012, + amount: 5.22, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 143', + memo: 'walmart', + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627699, + amount: 5.22, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 143', + memo: 'walmart', + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613024, + amount: 5.22, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 143', + memo: 'walmart', + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510753, + tradeDate: 1590127200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603963, + amount: 5.22, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 143', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592655, + amount: 5.22, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 143', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1590148800, + transactionDate: 1590148800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444968, + amount: 5.18, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD credit 139', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441377, + amount: 5.18, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS credit 139', + memo: 'walmart', + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'SAVINGS CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '518202071', + }, + { + id: 6295438739, + amount: 5.18, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING credit 139', + memo: 'walmart', + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CHECKING CREDIT WALMART', + country: 'USA', + }, + ofxCheckNumber: '518202071', + }, + { + id: 6295433038, + amount: 5.18, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc credit 139', + memo: 'walmart', + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'HELOC CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425711, + amount: 5.18, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA credit 139', + memo: 'walmart', + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145241, + tradeDate: 1589781600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414785, + amount: 5.18, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD credit 139', + memo: 'walmart', + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'CREDITCARD CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402197, + amount: 5.18, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan credit 139', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1589803200, + transactionDate: 1589803200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Income', + bestRepresentation: 'AUTOLOAN CREDIT WALMART', + country: 'USA', + }, + }, + { + id: 6411651878, + amount: -5.12, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 133', + memo: 'walmart', + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510815, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411644903, + amount: -5.12, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 133', + memo: 'walmart', + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510804, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411639053, + amount: -5.12, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 133', + memo: 'walmart', + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510791, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411627740, + amount: -5.12, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 133', + memo: 'walmart', + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510778, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411613029, + amount: -5.12, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 133', + memo: 'walmart', + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510753, + tradeDate: 1589263200, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6411603968, + amount: -5.12, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 133', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510742, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6411592660, + amount: -5.12, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 133', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1589284800, + transactionDate: 1589284800, + createdDate: 1651510727, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295444967, + amount: -5.08, + accountId: 6000985584, + customerId: 6000631200, + status: 'active', + description: 'CD debit 129', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145247, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295441300, + amount: -5.08, + accountId: 6000985589, + customerId: 6000631200, + status: 'active', + description: 'SAVINGS debit 129', + memo: 'walmart', + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145246, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'SAVINGS DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295438787, + amount: -5.08, + accountId: 6000985590, + customerId: 6000631200, + status: 'active', + description: 'CHECKING debit 129', + memo: 'walmart', + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145245, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CHECKING DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295432954, + amount: -5.08, + accountId: 6000985587, + customerId: 6000631200, + status: 'active', + description: 'Heloc debit 129', + memo: 'walmart', + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145243, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'HELOC DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295425814, + amount: -5.08, + accountId: 6000985585, + customerId: 6000631200, + status: 'active', + description: 'IRA debit 129', + memo: 'walmart', + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145241, + tradeDate: 1588917600, + securityId: 'CUSIP_NUMBER', + optionType: 'Option-1', + investmentTransactionType: 'other', + }, + { + id: 6295414799, + amount: -5.08, + accountId: 6000985588, + customerId: 6000631200, + status: 'active', + description: 'CREDITCARD debit 129', + memo: 'walmart', + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145238, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'CREDITCARD DEBIT WALMART', + country: 'USA', + }, + }, + { + id: 6295402194, + amount: -5.08, + accountId: 6000985583, + customerId: 6000631200, + status: 'active', + description: 'Autoloan debit 129', + memo: 'walmart', + interestAmount: 78.0, + principalAmount: 45690.0, + escrowAmount: 459.0, + postedDate: 1588939200, + transactionDate: 1588939200, + createdDate: 1651145235, + categorization: { + normalizedPayeeName: 'Walmart', + category: 'Shopping', + bestRepresentation: 'AUTOLOAN DEBIT WALMART', + country: 'USA', + }, + }, +] + +/** real data scrubbed of PII */ +export const bettermentAccount: FinicityTypes.CustomerAccount = { + id: '123456', + number: '123456', + realAccountNumberLast4: '1234', + accountNumberDisplay: '1234', + name: 'Betterment Test Account', + balance: 8502.11, + type: 'investment', + aggregationStatusCode: 0, + status: 'active', + customerId: '6000631200', + institutionId: '101073', + balanceDate: 1655395093, + aggregationSuccessDate: 1655395093, + aggregationAttemptDate: 1655395093, + createdDate: 1655395072, + currency: 'USD', + lastTransactionDate: 1655395093, + institutionLoginId: 6000483842, + detail: { + description: 'TotalBalance', + shortBalance: 0e-8, + availableCashBalance: 0e-8, + currentBalance: 8502.11, + marginBalance: 0e-8, + }, + position: [ + { + id: 1, + description: 'BNDX:Vanguard Total International Bond ETF', + symbol: 'BNDX', + currentPrice: 48.5, + transactionType: 'POSSTOCK', + marketValue: 326.68, + units: 6.735686, + status: 'A', + securityName: 'Vanguard Total International Bond ETF', + securityId: '92203J407', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 2, + description: 'VTIP:Vanguard Short-Term Inflation-Protected Securities ETF', + symbol: 'VTIP', + currentPrice: 50.22, + transactionType: 'POSSTOCK', + marketValue: 113.87, + units: 2.267567, + status: 'A', + securityName: 'Vanguard Short', + securityId: '922020805', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 3, + description: 'AGG:iShares Core Total US Bond Market ETF', + symbol: 'AGG', + currentPrice: 99.8, + transactionType: 'POSSTOCK', + marketValue: 320.75, + units: 3.214008, + status: 'A', + securityName: 'iShares Core Total US Bond Market ETF', + securityId: '464287226', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 4, + description: 'VTV:Vanguard Value ETF', + symbol: 'VTV', + currentPrice: 128.64, + transactionType: 'POSSTOCK', + marketValue: 734.51, + units: 5.709831, + status: 'A', + securityName: 'Vanguard Value ETF', + securityId: '922908744', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 5, + description: 'VTI:Vanguard Total Stock Market ETF', + symbol: 'VTI', + currentPrice: 183.17, + transactionType: 'POSSTOCK', + marketValue: 2523.89, + units: 13.778961, + status: 'A', + securityName: 'Vanguard Total Stock Market ETF', + securityId: '922908769', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 6, + description: 'EMB:iShares Emerging Markets USD Bond ETF', + symbol: 'EMB', + currentPrice: 85.39, + transactionType: 'POSSTOCK', + marketValue: 154.15, + units: 1.805334, + status: 'A', + securityName: 'iShares Emerging Markets USD Bond ETF', + securityId: '464288281', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 7, + description: 'VWO:Vanguard FTSE Emerging Markets', + symbol: 'VWO', + currentPrice: 41.18, + transactionType: 'POSSTOCK', + marketValue: 1176.13, + units: 28.56071, + status: 'A', + securityName: 'Vanguard FTSE Emerging Markets', + securityId: '922042858', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 8, + description: 'VOE:Vanguard Mid-Cap Value ETF', + symbol: 'VOE', + currentPrice: 126.55, + transactionType: 'POSSTOCK', + marketValue: 584.62, + units: 4.619721, + status: 'A', + securityName: 'Vanguard Mid', + securityId: '922908512', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 9, + description: 'VBR:Vanguard Small-Cap Value ETF', + symbol: 'VBR', + currentPrice: 147.61, + transactionType: 'POSSTOCK', + marketValue: 496.03, + units: 3.360443, + status: 'A', + securityName: 'Vanguard Small', + securityId: '922908611', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + { + id: 10, + description: 'VEA:Vanguard FTSE Developed Markets', + symbol: 'VEA', + currentPrice: 40.97, + transactionType: 'POSSTOCK', + marketValue: 2071.43, + units: 50.559853, + status: 'A', + securityName: 'Vanguard FTSE Developed Markets', + securityId: '921943858', + securityIdType: 'CUSIP', + posType: 'LONG', + subAccountType: 'CASH', + currentPriceDate: 1655359200, + }, + ], + displayPosition: 1, + accountNickname: 'Betterment - Test', + oldestTransactionDate: 1593432000, + marketSegment: 'personal', +} + +/** real data scrubbed of PII */ +export const bettermentTransactions: FinicityTypes.Transaction[] = [ + { + id: 1, + amount: 2.55, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 2, + amount: 1.58, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.015068, + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 104.86, + subAccountFund: 'OTHER', + securityId: '464287226', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 3, + amount: 0.97, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.010227, + postedDate: 1649764800, + transactionDate: 1649764800, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 94.85, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 4, + amount: 1.35, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.012687, + postedDate: 1648814400, + transactionDate: 1648814400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 106.41, + subAccountFund: 'OTHER', + securityId: '464287226', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 5, + amount: 19.55, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1648814400, + transactionDate: 1648814400, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 6, + amount: 4.15, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.042748, + postedDate: 1648814400, + transactionDate: 1648814400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 97.08, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 7, + amount: 14.05, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.269106, + postedDate: 1648814400, + transactionDate: 1648814400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 52.21, + subAccountFund: 'OTHER', + securityId: '92203J407', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 8, + amount: 2.08, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.040483, + postedDate: 1648728000, + transactionDate: 1648728000, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 51.38, + subAccountFund: 'OTHER', + securityId: '922020805', + securityIdType: 'CUSIP', + investmentTransactionType: 'sold', + }, + { + id: 9, + amount: -2.08, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'SRVCHG', + postedDate: 1648728000, + transactionDate: 1648641600, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'fee', + }, + { + id: 10, + amount: 8.01, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.083057, + postedDate: 1648468800, + transactionDate: 1648468800, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 96.44, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 11, + amount: 8.01, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1648468800, + transactionDate: 1648468800, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 12, + amount: 1.1, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 13, + amount: 1.1, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.024326, + postedDate: 1646827200, + transactionDate: 1646827200, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 45.22, + subAccountFund: 'OTHER', + securityId: '922042858', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 14, + amount: 0.22, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.00499, + postedDate: 1646740800, + transactionDate: 1646740800, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 44.09, + subAccountFund: 'OTHER', + securityId: '922042858', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 15, + amount: 0.22, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1646740800, + transactionDate: 1646740800, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 16, + amount: 1.93, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.037447, + postedDate: 1646049600, + transactionDate: 1646049600, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 51.54, + subAccountFund: 'OTHER', + securityId: '922020805', + securityIdType: 'CUSIP', + investmentTransactionType: 'sold', + }, + { + id: 17, + amount: -1.93, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'SRVCHG', + postedDate: 1646049600, + transactionDate: 1645963200, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'fee', + }, + { + id: 18, + amount: 1.13, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.010823, + postedDate: 1644408000, + transactionDate: 1644408000, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 104.41, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 19, + amount: 1.13, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1644408000, + transactionDate: 1644408000, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 20, + amount: 0.31, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.002974, + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 104.24, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 21, + amount: 0.31, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1644235200, + transactionDate: 1644235200, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 22, + amount: 1404.88, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 28.46768, + postedDate: 1643630400, + transactionDate: 1643630400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 49.35, + subAccountFund: 'OTHER', + securityId: '922042858', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 23, + amount: 8045.28, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 72.920123, + postedDate: 1643630400, + transactionDate: 1643630400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 110.33, + subAccountFund: 'OTHER', + securityId: '464288679', + securityIdType: 'CUSIP', + investmentTransactionType: 'sold', + }, + { + id: 24, + amount: 173.98, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 1.652389, + postedDate: 1643630400, + transactionDate: 1643630400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 105.29, + subAccountFund: 'OTHER', + securityId: '464288281', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 38, + amount: 0.92, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.018229, + postedDate: 1641470400, + transactionDate: 1641470400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 50.47, + subAccountFund: 'OTHER', + securityId: '46641Q837', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 39, + amount: 2.14, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.019382, + postedDate: 1640952000, + transactionDate: 1640952000, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 110.41, + subAccountFund: 'OTHER', + securityId: '464288679', + securityIdType: 'CUSIP', + investmentTransactionType: 'sold', + }, + { + id: 40, + amount: -2.14, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'SRVCHG', + postedDate: 1640952000, + transactionDate: 1640865600, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'fee', + }, + { + id: 41, + amount: 1.42, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1640606400, + transactionDate: 1640606400, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 42, + amount: 1.42, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.028119, + postedDate: 1640606400, + transactionDate: 1640606400, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 50.5, + subAccountFund: 'OTHER', + securityId: '46641Q837', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 43, + amount: 0.98, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1638964800, + transactionDate: 1638964800, + createdDate: 1655395074, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 44, + amount: 0.98, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.019398, + postedDate: 1638964800, + transactionDate: 1638964800, + createdDate: 1655395074, + subaccountSecurityType: 'OTHER', + unitPrice: 50.52, + subAccountFund: 'OTHER', + securityId: '46641Q837', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 50, + amount: -2.0, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'SRVCHG', + postedDate: 1635508800, + transactionDate: 1635422400, + createdDate: 1655395093, + subAccountFund: 'CASH', + investmentTransactionType: 'fee', + }, + { + id: 51, + amount: 1.08, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.021306, + postedDate: 1633694400, + transactionDate: 1633694400, + createdDate: 1655395093, + subaccountSecurityType: 'OTHER', + unitPrice: 50.69, + subAccountFund: 'OTHER', + securityId: '46641Q837', + securityIdType: 'CUSIP', + investmentTransactionType: 'purchased', + }, + { + id: 52, + amount: 1.08, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'DIV', + postedDate: 1633694400, + transactionDate: 1633694400, + createdDate: 1655395093, + subAccountFund: 'CASH', + investmentTransactionType: 'reinvestOfIncome', + }, + { + id: 53, + amount: -2.07, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + type: 'SRVCHG', + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1655395093, + subAccountFund: 'CASH', + investmentTransactionType: 'fee', + }, + { + id: 54, + amount: 1.36, + accountId: 123456, + customerId: 6000631200, + status: 'active', + description: '*scrubbed*', + memo: '*scrubbed*', + unitQuantity: 0.012312, + postedDate: 1633003200, + transactionDate: 1633003200, + createdDate: 1655395093, + subaccountSecurityType: 'OTHER', + unitPrice: 110.46, + subAccountFund: 'OTHER', + securityId: '464288679', + securityIdType: 'CUSIP', + investmentTransactionType: 'sold', + }, +] diff --git a/tools/test-data/finicity/index.ts b/tools/test-data/finicity/index.ts new file mode 100644 index 00000000000..70bd2a2eb7b --- /dev/null +++ b/tools/test-data/finicity/index.ts @@ -0,0 +1 @@ +export * from './finicityTestData' diff --git a/tools/test-data/index.ts b/tools/test-data/index.ts new file mode 100644 index 00000000000..8dabfd5f414 --- /dev/null +++ b/tools/test-data/index.ts @@ -0,0 +1,3 @@ +export * as FinicityTestData from './finicity' +export * as PlaidTestData from './plaid' +export * as PolygonTestData from './polygon' diff --git a/tools/test-data/plaid/connection-samples/index.ts b/tools/test-data/plaid/connection-samples/index.ts new file mode 100644 index 00000000000..95a7b2c6f6b --- /dev/null +++ b/tools/test-data/plaid/connection-samples/index.ts @@ -0,0 +1 @@ +export * as Wealthfront1 from './wealthfront1' diff --git a/tools/test-data/plaid/connection-samples/wealthfront1.ts b/tools/test-data/plaid/connection-samples/wealthfront1.ts new file mode 100644 index 00000000000..b089c0d9926 --- /dev/null +++ b/tools/test-data/plaid/connection-samples/wealthfront1.ts @@ -0,0 +1,1174 @@ +import * as TestUtil from '../../../../libs/shared/src/utils/test-utils' +import type { + AccountBase, + AccountsGetResponse, + Holding, + InvestmentsHoldingsGetResponse, + InvestmentsTransactionsGetResponse, + Item, + TransactionsGetResponse, +} from 'plaid' + +import { + Products, + ItemUpdateTypeEnum, + AccountSubtype, + InvestmentTransactionType, + InvestmentTransactionSubtype, + AccountType, +} from 'plaid' + +/** + * A Wealthfront connection with multiple 529 Savings plans, and multiple cash accounts + * within each of the savings plans (introducing duplicates) + * + * This is a real connection scrubbed of any sensitive data, retrieved directly from the Plaid API + * + * Date Range: + * start: '2022-06-10' + * end: '2022-06-22' + */ + +/** Reusable Entity values (Item, Accounts) */ + +const item: Item = { + available_products: [Products.Auth, Products.Balance], + billed_products: [Products.Investments, Products.Transactions], + consent_expiration_time: null, + error: null, + institution_id: 'ins_115617', + item_id: '0vOzwnRxMzCwkXmmd3wdC0Nd6vv3gYUeab1XX', + optional_products: null, + products: [Products.Investments, Products.Transactions], + update_type: ItemUpdateTypeEnum.Background, + webhook: 'https://www.mockwebhook.com/v1/plaid/webhook', +} + +const accounts: AccountBase[] = [ + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + balances: { + available: null, + current: 558.679353, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '0653', + name: '***0653', + official_name: '529 College Savings Account', + subtype: AccountSubtype._529, + type: AccountType.Investment, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + balances: { + available: null, + current: 11576.775367, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '6104', + name: '**6104', + official_name: '529 College Savings Account', + subtype: AccountSubtype._529, + type: AccountType.Investment, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + balances: { + available: null, + current: 2103.164223, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '8813', + name: '***8813', + official_name: '529 College Savings Account', + subtype: AccountSubtype._529, + type: AccountType.Investment, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + balances: { + available: null, + current: 6569.01, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '9101', + name: '***9101', + official_name: 'Joint Investment Account', + subtype: AccountSubtype._529, + type: AccountType.Investment, + }, +] + +const holdings: Holding[] = [ + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 30.7, + iso_currency_code: 'USD', + quantity: 30.7, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 41.42, + institution_price: 41.79, + institution_price_as_of: '2022-06-22', + institution_value: 41.79, + iso_currency_code: 'USD', + quantity: 1, + security_id: 'VK0EQ5Ea13u9QqNrYJ9qINz0Jd0ZYMHQ79bd0', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 45.450271, + iso_currency_code: 'USD', + quantity: 4.4867, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 69.691449, + iso_currency_code: 'USD', + quantity: 5.3159, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 43.83, + institution_price: 71.97, + institution_price_as_of: '2022-06-22', + institution_value: 71.97, + iso_currency_code: 'USD', + quantity: 1, + security_id: 'XARE85EJqKsjxyPpp93mtPABJMBv8Nf7jxaDo', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 75.253552, + iso_currency_code: 'USD', + quantity: 6.8662, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 80.197623, + iso_currency_code: 'USD', + quantity: 5.6517, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 82.057008, + iso_currency_code: 'USD', + quantity: 4.4022, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 92.207621, + iso_currency_code: 'USD', + quantity: 7.1093, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'Xxa6YXPbv6UXwNkkDmXDTnwzdkM4NqFjwev6qq', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 107.504558, + iso_currency_code: 'USD', + quantity: 5.8142, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 164.390653, + iso_currency_code: 'USD', + quantity: 16.2281, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 249.044115, + iso_currency_code: 'USD', + quantity: 18.9965, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 273.652731, + iso_currency_code: 'USD', + quantity: 19.2849, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 317.10568, + iso_currency_code: 'USD', + quantity: 28.933, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 325.061096, + iso_currency_code: 'USD', + quantity: 17.4389, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 347.328818, + iso_currency_code: 'USD', + quantity: 26.7794, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'pyp6wx7OK6HjN766D9jDF1aoVQ40AOtL1BPazL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 402.876761, + iso_currency_code: 'USD', + quantity: 21.7889, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 599.68, + institution_price: 140.96, + institution_price_as_of: '2022-06-22', + institution_value: 563.84, + iso_currency_code: 'USD', + quantity: 4, + security_id: 'NLqaJoaK5wFr6M0gmB01cnQpgJpNk6tKzrE74', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 866.06, + institution_price: 49.275, + institution_price_as_of: '2022-06-22', + institution_value: 788.4, + iso_currency_code: 'USD', + quantity: 16, + security_id: '8wyZkVZm60synVQxLxVMHn0jB5jZJat5krq4q', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 882.020113, + iso_currency_code: 'USD', + quantity: 87.0701, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 956.61, + institution_price: 49.35, + institution_price_as_of: '2022-06-22', + institution_value: 987, + iso_currency_code: 'USD', + quantity: 20, + security_id: '6aEJyVJ5dAsP6RKmkjoPC9BEkeEYpKu6ZqObP', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 1089.530015, + institution_price: 31.76, + institution_price_as_of: '2022-06-22', + institution_value: 1111.6, + iso_currency_code: 'USD', + quantity: 35, + security_id: 'x1RMwKMjNqsm40xmAAbVFoXN4rNVJ5cLpvR45', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 1402.856526, + iso_currency_code: 'USD', + quantity: 107.0066, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 1487.398638, + iso_currency_code: 'USD', + quantity: 104.8202, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 1704.47728, + iso_currency_code: 'USD', + quantity: 155.518, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 1832.14424, + iso_currency_code: 'USD', + quantity: 98.291, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 1899.706821, + iso_currency_code: 'USD', + quantity: 146.4693, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + cost_basis: null, + institution_price: 1, + institution_price_as_of: '2022-06-22', + institution_value: 2235.418812, + iso_currency_code: 'USD', + quantity: 120.8988, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + cost_basis: 3049.41002, + institution_price: 44.09, + institution_price_as_of: '2022-06-22', + institution_value: 2998.12, + iso_currency_code: 'USD', + quantity: 68, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + unofficial_currency_code: null, + }, +] + +/** API Responses */ + +export const accountsGetResponse = TestUtil.axiosSuccess({ + accounts, + item, + request_id: 'd2YZktLMJfDo2Aj', +}) + +export const holdingsGetResponse = TestUtil.axiosSuccess({ + accounts, + holdings, + item, + request_id: '0K8515MDZrUFFkS', + securities: [ + { + close_price: 49.34, + close_price_as_of: '2022-06-21', + cusip: '46434G103', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US46434G1031', + iso_currency_code: 'USD', + name: 'iShares Core MSCI Emerging Market', + proxy_security_id: null, + security_id: '6aEJyVJ5dAsP6RKmkjoPC9BEkeEYpKu6ZqObP', + sedol: 'B8NDCB6', + ticker_symbol: 'IEMG', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: '9ZZZFD989', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: true, + isin: null, + iso_currency_code: 'USD', + name: 'U S Dollar', + proxy_security_id: null, + security_id: '7dD8KV8owvUgA3rkNbmLtLPy8Kyr9dFQmvKD4', + sedol: null, + ticker_symbol: 'CUR:USD', + type: 'cash', + unofficial_currency_code: null, + }, + { + close_price: 49.25, + close_price_as_of: '2022-06-21', + cusip: '922907746', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9229077469', + iso_currency_code: 'USD', + name: 'Vanguard Tax-Exempt Bond', + proxy_security_id: null, + security_id: '8wyZkVZm60synVQxLxVMHn0jB5jZJat5krq4q', + sedol: null, + ticker_symbol: 'VTEB', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 44.08, + close_price_as_of: '2022-06-21', + cusip: '808524102', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085241029', + iso_currency_code: 'USD', + name: 'Schwab U.S. Broad Market ETF', + proxy_security_id: null, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + sedol: 'B5BQNM4', + ticker_symbol: 'SCHB', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 140.97, + close_price_as_of: '2022-06-21', + cusip: '921908844', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9219088443', + iso_currency_code: 'USD', + name: 'Vanguard Dividend Appreciation ETF', + proxy_security_id: null, + security_id: 'NLqaJoaK5wFr6M0gmB01cnQpgJpNk6tKzrE74', + sedol: 'B13V1N6', + ticker_symbol: 'VIG', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 41.8, + close_price_as_of: '2022-06-21', + cusip: '922042858', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9220428588', + iso_currency_code: 'USD', + name: 'Vanguard MSCI Emerging Markets ETF', + proxy_security_id: null, + security_id: 'VK0EQ5Ea13u9QqNrYJ9qINz0Jd0ZYMHQ79bd0', + sedol: 'B06HMH5', + ticker_symbol: 'VWO', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 71.97, + close_price_as_of: '2022-06-21', + cusip: '808524797', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085247976', + iso_currency_code: 'USD', + name: 'Schwab U.S. Dividend Equity ETF', + proxy_security_id: null, + security_id: 'XARE85EJqKsjxyPpp93mtPABJMBv8Nf7jxaDo', + sedol: 'B5MPY72', + ticker_symbol: 'SCHD', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 31.76, + close_price_as_of: '2022-06-21', + cusip: '808524805', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085248057', + iso_currency_code: 'USD', + name: 'Schwab International Equity ETF', + proxy_security_id: null, + security_id: 'x1RMwKMjNqsm40xmAAbVFoXN4rNVJ5cLpvR45', + sedol: 'B5BQR88', + ticker_symbol: 'SCHF', + type: 'etf', + unofficial_currency_code: null, + }, + ], +}) + +export const investmentTransactionsGetResponse = + TestUtil.axiosSuccess({ + request_id: 'eNt6Lip8b5NSbKx', + accounts, + item, + total_investment_transactions: 19, + investment_transactions: [ + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 42.9, + cancel_transaction_id: null, + date: '2022-06-16', + fees: 0, + investment_transaction_id: '7J1QBx4ZzQfMQ8rrqLMqt7njK3q9bDt8AgMKV', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 42.9, + quantity: 1, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 917.04, + cancel_transaction_id: null, + date: '2022-06-16', + fees: 0, + investment_transaction_id: 'gwarYP7d6rsAwQ889VA9IBNZ5JyjgwI6mNwQz', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 48.27, + quantity: 19, + security_id: '6aEJyVJ5dAsP6RKmkjoPC9BEkeEYpKu6ZqObP', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: -943.98, + cancel_transaction_id: null, + date: '2022-06-16', + fees: 0, + investment_transaction_id: 'bVMkYwRqmkf5k0jjKq5KCpXN59QDdbtPORy48', + iso_currency_code: 'USD', + name: 'automated transaction - Sell', + price: 41.04, + quantity: -23, + security_id: 'VK0EQ5Ea13u9QqNrYJ9qINz0Jd0ZYMHQ79bd0', + subtype: InvestmentTransactionSubtype.Sell, + type: InvestmentTransactionType.Sell, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 8, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: '0vOzwnRxMzCwkXmmd3wdC0NPBmpXE9f9DOnBO', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 10.01, + quantity: 0.7992, + security_id: 'eJXpMzpR65FkBv0k60zytBaE85EjmMfnE9r1R', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 12, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'jDpxz3nL1xFABQppjwAjIxnVb1joLMHvb5Pkq', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 13.05, + quantity: 0.9195, + security_id: 'JmN0JX0q5Ecg57nBygMDHYzMgoMr6psbeJjZL', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 13, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'x4Xy3rPVayf0nrqqYK0YIPdzrO8J1EFmVq0pz', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 14.36, + quantity: 0.9053, + security_id: 'M654JE4yQdCL1Op8PXNqiZJ3Lo34kKHMLZxnA', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 15, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'rkpDLrdZmDUAPYnnm5AmINAqOVMpvEIrnN4jQ', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 18.95, + quantity: 0.7916, + security_id: 'kZrEbXELBVCKoYwBVxeQt5xO8JOy1qtRnzQVz', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 15, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'vjpBqgD7rBt86V11A38ASpy3NVMw4AtpvK1Px', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 10.76, + quantity: 1.3941, + security_id: '54jm3zmZ7kFgAbJpv8EbF5b4KA4V8atB3jJP0', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 17, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'a9RKYd0yZKi5O0qqPz5PCQyPj4Dv51fKzxk3d', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 13.1, + quantity: 1.2977, + security_id: 'x1RMwKMjNqsgkVwbOdMyUoXN4rNVJ5cM9wZ0E', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: 20, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: '4m6O1nezMOT16pvvaM1atyDMbjOoa9fDZx5zL', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 18.8, + quantity: 1.0638, + security_id: '3mg4qV4JZycL34xMvXVzsbEgn8gLKkUKR3am8', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'QARaYk0Mqaf19ERRdX1dtxr5OKRoPQUoDZ3JoL', + amount: -100, + cancel_transaction_id: null, + date: '2022-06-14', + fees: 0, + investment_transaction_id: 'V48mYXDbkmf6PN55Ab6rSd16AgkVOECJJAMz5n', + iso_currency_code: 'USD', + name: 'deposit automated transaction Deposit', + price: 0, + quantity: 0, + security_id: null, + subtype: InvestmentTransactionSubtype.Deposit, + type: InvestmentTransactionType.Cash, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 132.72, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'EwRq8dLYMqs8ZnYYrz8rSAm3YEZPvqidN40KY', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 44.24, + quantity: 3, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 142.61, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'RvRxYAdMBxC7zN88Kw7KIJ9y81bQKVt9gaE6D', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 142.61, + quantity: 1, + security_id: 'NLqaJoaK5wFr6M0gmB01cnQpgJpNk6tKzrE74', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 927.13, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'M3R0VBKDL0IrveBBPKrPhqDvoPjk5YS0w6nAN', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 31.97, + quantity: 29, + security_id: 'x1RMwKMjNqsm40xmAAbVFoXN4rNVJ5cLpvR45', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: -875.17, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'V48mYXDbkmf6PN55Ab6AixOQdk1Yr8HnKvA00', + iso_currency_code: 'USD', + name: 'automated transaction - Sell', + price: 41.67, + quantity: -21, + security_id: '0AZ0De04KqsreZXmgX0dT1VgMBgJXzsMPgmjO', + subtype: InvestmentTransactionSubtype.Sell, + type: InvestmentTransactionType.Sell, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: -72.85, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'OKXrYyAM1rfBN9AALVBLsm5BA40p8jtp9jB7n', + iso_currency_code: 'USD', + name: 'automated transaction - Sell', + price: 72.85, + quantity: -1, + security_id: 'XARE85EJqKsjxyPpp93mtPABJMBv8Nf7jxaDo', + subtype: InvestmentTransactionSubtype.Sell, + type: InvestmentTransactionType.Sell, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: -200, + cancel_transaction_id: null, + date: '2022-06-13', + fees: 0, + investment_transaction_id: 'dMavYLmqbvspezDD3kp7hL4jEkozwDtPPma8pO', + iso_currency_code: 'USD', + name: 'deposit automated transaction Deposit', + price: 0, + quantity: 0, + security_id: null, + subtype: InvestmentTransactionSubtype.Deposit, + type: InvestmentTransactionType.Cash, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: 2754.6, + cancel_transaction_id: null, + date: '2022-06-10', + fees: 0, + investment_transaction_id: 'pyp6wx7OK6HjN766D9jDF1aj43Myk6IAmRM0Y', + iso_currency_code: 'USD', + name: 'automated transaction - Buy', + price: 45.91, + quantity: 60, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: 'nBpdYA7Z3dfKXQNNAxKAtK6ZO7m48JFXndO7Ln', + amount: -2741.06, + cancel_transaction_id: null, + date: '2022-06-10', + fees: 0, + investment_transaction_id: 'nBpdYA7Z3dfKXQNNAxKAtK6NmBeQrqHjVKPmD', + iso_currency_code: 'USD', + name: 'automated transaction - Sell', + price: 195.79, + quantity: -14, + security_id: 'bn4Q8gQOBJt1AbD8B9eDtLYEO5EBD1FqXRpYZ', + subtype: InvestmentTransactionSubtype.Sell, + type: InvestmentTransactionType.Sell, + unofficial_currency_code: null, + }, + ], + securities: [ + { + close_price: 40.53, + close_price_as_of: '2022-06-22', + cusip: '921943858', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9219438580', + iso_currency_code: 'USD', + name: 'Vanguard MSCI EAFE ETF', + proxy_security_id: null, + security_id: '0AZ0De04KqsreZXmgX0dT1VgMBgJXzsMPgmjO', + sedol: 'B23MX41', + ticker_symbol: 'VEA', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: '3mg4qV4JZycL34xMvXVzsbEgn8gLKkUKR3am8', + sedol: null, + ticker_symbol: 'U.S.STOCKS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: '54jm3zmZ7kFgAbJpv8EbF5b4KA4V8atB3jJP0', + sedol: null, + ticker_symbol: 'CORPORATE.BONDS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: 48.46, + close_price_as_of: '2022-06-22', + cusip: '46434G103', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US46434G1031', + iso_currency_code: 'USD', + name: 'iShares Core MSCI Emerging Market', + proxy_security_id: null, + security_id: '6aEJyVJ5dAsP6RKmkjoPC9BEkeEYpKu6ZqObP', + sedol: 'B8NDCB6', + ticker_symbol: 'IEMG', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: 'JmN0JX0q5Ecg57nBygMDHYzMgoMr6psbeJjZL', + sedol: null, + ticker_symbol: 'REAL.ESTATE', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: 43.86, + close_price_as_of: '2022-06-22', + cusip: '808524102', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085241029', + iso_currency_code: 'USD', + name: 'Schwab U.S. Broad Market ETF', + proxy_security_id: null, + security_id: 'KdDjmojBERUKxX8Ln6wdUadEgLE7o5cZ306Xk', + sedol: 'B5BQNM4', + ticker_symbol: 'SCHB', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: 'M654JE4yQdCL1Op8PXNqiZJ3Lo34kKHMLZxnA', + sedol: null, + ticker_symbol: 'EMERGING.MARKETS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: 141.11, + close_price_as_of: '2022-06-22', + cusip: '921908844', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9219088443', + iso_currency_code: 'USD', + name: 'Vanguard Dividend Appreciation ETF', + proxy_security_id: null, + security_id: 'NLqaJoaK5wFr6M0gmB01cnQpgJpNk6tKzrE74', + sedol: 'B13V1N6', + ticker_symbol: 'VIG', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 41.11, + close_price_as_of: '2022-06-22', + cusip: '922042858', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9220428588', + iso_currency_code: 'USD', + name: 'Vanguard MSCI Emerging Markets ETF', + proxy_security_id: null, + security_id: 'VK0EQ5Ea13u9QqNrYJ9qINz0Jd0ZYMHQ79bd0', + sedol: 'B06HMH5', + ticker_symbol: 'VWO', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 70.9, + close_price_as_of: '2022-06-22', + cusip: '808524797', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085247976', + iso_currency_code: 'USD', + name: 'Schwab U.S. Dividend Equity ETF', + proxy_security_id: null, + security_id: 'XARE85EJqKsjxyPpp93mtPABJMBv8Nf7jxaDo', + sedol: 'B5MPY72', + ticker_symbol: 'SCHD', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 188.13, + close_price_as_of: '2022-06-22', + cusip: '922908769', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US9229087690', + iso_currency_code: 'USD', + name: 'Vanguard Group, Inc. - Vanguard Total Stock Market ETF', + proxy_security_id: null, + security_id: 'bn4Q8gQOBJt1AbD8B9eDtLYEO5EBD1FqXRpYZ', + sedol: '2762568', + ticker_symbol: 'VTI', + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: 'eJXpMzpR65FkBv0k60zytBaE85EjmMfnE9r1R', + sedol: null, + ticker_symbol: 'EMERGING.MARKET.BONDS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: 'kZrEbXELBVCKoYwBVxeQt5xO8JOy1qtRnzQVz', + sedol: null, + ticker_symbol: 'DIVIDEND.STOCKS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: null, + close_price_as_of: null, + cusip: null, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: null, + proxy_security_id: null, + security_id: 'x1RMwKMjNqsgkVwbOdMyUoXN4rNVJ5cM9wZ0E', + sedol: null, + ticker_symbol: 'FOREIGN.STOCKS', + type: 'other', + unofficial_currency_code: null, + }, + { + close_price: 31.25, + close_price_as_of: '2022-06-22', + cusip: '808524805', + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: 'US8085248057', + iso_currency_code: 'USD', + name: 'Schwab International Equity ETF', + proxy_security_id: null, + security_id: 'x1RMwKMjNqsm40xmAAbVFoXN4rNVJ5cLpvR45', + sedol: 'B5BQR88', + ticker_symbol: 'SCHF', + type: 'etf', + unofficial_currency_code: null, + }, + ], + }) + +// This connection has no regular transactions +export const transactionsGetResponse = TestUtil.axiosSuccess({ + accounts, + item, + request_id: 'VYybyWkcRp4gT6W', + total_transactions: 0, + transactions: [], +}) + +// This connection has no liabilities +export const liabilitiesGetResponse = TestUtil.axios400Error({ + display_message: null, + documentation_url: 'https://plaid.com/docs/?ref=error#item-errors', + error_code: 'PRODUCTS_NOT_SUPPORTED', + error_message: 'the following products are not supported by this institution: ["liabilities"]', + error_type: 'ITEM_ERROR', + request_id: 'pwINPLaeVmixWgC', + suggested_action: null, +}) diff --git a/tools/test-data/plaid/index.ts b/tools/test-data/plaid/index.ts new file mode 100644 index 00000000000..3a13717b77d --- /dev/null +++ b/tools/test-data/plaid/index.ts @@ -0,0 +1,5 @@ +export * from './plaidApiResponses' +export * from './plaidTestData' +export * from './stockPrices' +export * from './testDates' +export * from './connection-samples' diff --git a/tools/test-data/plaid/plaidApiResponses.ts b/tools/test-data/plaid/plaidApiResponses.ts new file mode 100644 index 00000000000..19b9037689a --- /dev/null +++ b/tools/test-data/plaid/plaidApiResponses.ts @@ -0,0 +1,163 @@ +import type { AxiosResponse } from 'axios' +import type { + AccountsGetResponse, + InstitutionsGetByIdResponse, + InvestmentsHoldingsGetResponse, + InvestmentsTransactionsGetResponse, + LiabilitiesGetResponse, + TransactionsGetResponse, +} from 'plaid' +import { + CountryCode, + ProductStatusStatusEnum, + ProductStatusBreakdownRefreshIntervalEnum, + Products, +} from 'plaid' +import { + brokerageAccount, + checkingAccount, + checkingTransactions, + creditAccount, + creditCardLiability, + creditTransactions, + holdings, + investmentTransactions, + item, + securities, +} from './plaidTestData' +import { TestUtil } from '../../../libs/shared/src/utils' + +const accounts = [checkingAccount, creditAccount, brokerageAccount] + +export const investmentsTransactionsGetResponse: AxiosResponse = + TestUtil.axiosSuccess({ + accounts, + investment_transactions: investmentTransactions, + item, + request_id: 'iv4q3ZlytOOthkv', + securities, + total_investment_transactions: investmentTransactions.length, + }) + +export const investmentsHoldingsGetResponse = TestUtil.axiosSuccess( + { + accounts, + holdings, + securities, + item, + request_id: 'iv4q3ZlytOOt123', + } +) + +export const liabilitiesGetResponse = TestUtil.axiosSuccess({ + accounts, + item, + liabilities: { credit: [creditCardLiability], mortgage: null, student: null }, + request_id: '23kjf23ij2ekfj2ej', +}) + +export const accountsGetResponse = TestUtil.axiosSuccess({ + accounts, + item, + request_id: 'bkVE1BHWMAZ9Rnr', +}) + +export const transactionsGetResponse = TestUtil.axiosSuccess({ + accounts, + transactions: [...checkingTransactions, ...creditTransactions], + item, + total_transactions: checkingTransactions.length + creditTransactions.length, // 8 + request_id: '45QSn', +}) + +export const institutionsGetByIdResponse = TestUtil.axiosSuccess({ + institution: { + country_codes: [CountryCode.Us], + institution_id: 'ins_109512', + name: 'Houndstooth Bank', + products: [Products.Auth, Products.Balance, Products.Transactions, Products.Investments], + routing_numbers: ['011000138', '011200365', '011400495'], + oauth: false, + status: { + item_logins: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-15T15:53:00Z', + breakdown: { + success: 0.9, + error_plaid: 0.01, + error_institution: 0.09, + }, + }, + transactions_updates: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-12T08:22:00Z', + breakdown: { + success: 0.95, + error_plaid: 0.02, + error_institution: 0.03, + refresh_interval: ProductStatusBreakdownRefreshIntervalEnum.Normal, + }, + }, + auth: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-15T15:53:00Z', + breakdown: { + success: 0.91, + error_plaid: 0.01, + error_institution: 0.08, + }, + }, + identity: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-15T15:50:00Z', + breakdown: { + success: 0.42, + error_plaid: 0.08, + error_institution: 0.5, + }, + }, + investments: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-15T15:53:00Z', + breakdown: { + success: 0.89, + error_plaid: 0.02, + error_institution: 0.09, + }, + liabilities: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-15T15:53:00Z', + breakdown: { + success: 0.89, + error_plaid: 0.02, + error_institution: 0.09, + }, + }, + }, + investments_updates: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-12T08:22:00Z', + breakdown: { + success: 0.95, + error_plaid: 0.02, + error_institution: 0.03, + refresh_interval: ProductStatusBreakdownRefreshIntervalEnum.Normal, + }, + }, + liabilities_updates: { + status: ProductStatusStatusEnum.Healthy, + last_status_change: '2019-02-12T08:22:00Z', + breakdown: { + success: 0.95, + error_plaid: 0.02, + error_institution: 0.03, + refresh_interval: ProductStatusBreakdownRefreshIntervalEnum.Normal, + }, + }, + }, + primary_color: '#004966', + url: 'https://plaid.com', + logo: null, + }, + request_id: 'm8MDnv9okwxFNBV', +}) diff --git a/tools/test-data/plaid/plaidTestData.ts b/tools/test-data/plaid/plaidTestData.ts new file mode 100644 index 00000000000..9c64f0fd2a5 --- /dev/null +++ b/tools/test-data/plaid/plaidTestData.ts @@ -0,0 +1,409 @@ +import type { + Item, + Security, + Transaction, + AccountBase, + InvestmentTransaction, + CreditCardLiability, + Holding, +} from 'plaid' + +import { + ItemUpdateTypeEnum, + Products, + TransactionPaymentChannelEnum, + TransactionTransactionTypeEnum, + AccountType, + AccountSubtype, + InvestmentTransactionSubtype, + InvestmentTransactionType, + APRAprTypeEnum, +} from 'plaid' + +import { testDates } from './testDates' + +const { now } = testDates + +export const item: Item = { + available_products: [Products.Balance, Products.Transactions, Products.Investments], + billed_products: [], + consent_expiration_time: null, + error: null, + institution_id: 'ins_3', + item_id: 'DWVAAPWq4RHGlEaNyGKRTAnPLaEmo8Cvq7abc', + update_type: ItemUpdateTypeEnum.Background, + webhook: 'https://www.genericwebhookurl.com/webhook', +} + +export const checkingAccount: AccountBase = { + account_id: 'blgvvBlXw3cq5GMPwqB6s6q4dLKB9WcVqGabc', + balances: { + available: null, + current: 5000, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '1234', + name: 'Checking', + official_name: null, + type: AccountType.Depository, + subtype: AccountSubtype.Checking, +} + +export const creditAccount: AccountBase = { + account_id: '6PdjjRP6LmugpBy5NgQvUqpRXMWxzktg3rdef', + balances: { + available: null, + current: 1000, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '1234', + name: 'Credit Card', + official_name: null, + type: AccountType.Credit, + subtype: AccountSubtype.CreditCard, +} + +export const brokerageAccount: AccountBase = { + account_id: '6PdjjRP6LmugpBy5NgQvUqpRXMWxzktg3rghi', + balances: { + available: null, + current: 10000, + iso_currency_code: 'USD', + limit: null, + unofficial_currency_code: null, + }, + mask: '5847', + name: 'Brokerage Account', + official_name: null, + type: AccountType.Investment, + subtype: AccountSubtype.Brokerage, +} + +const baseTxn = { + payment_meta: { + by_order_of: null, + payee: null, + payer: null, + payment_method: null, + payment_processor: null, + ppd_id: null, + reason: null, + reference_number: null, + }, + location: { + address: null, + city: null, + region: null, + postal_code: null, + country: null, + lat: null, + lon: null, + store_number: null, + }, + iso_currency_code: 'USD', + unofficial_currency_code: null, + check_number: null, + datetime: null, + authorized_date: null, + authorized_datetime: null, + payment_channel: TransactionPaymentChannelEnum.InStore, + pending: false, + pending_transaction_id: null, + account_owner: null, + transaction_code: null, + transaction_type: TransactionTransactionTypeEnum.Place, +} + +export const checkingTransactions: Transaction[] = [ + { + account_id: checkingAccount.account_id, + amount: -1500.0, + date: '2021-12-20', + name: 'Income: Paycheck', + merchant_name: 'Acme Corp', + category: ['Transfer', 'Payroll'], + category_id: '21009000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD01', + ...baseTxn, + }, + { + account_id: checkingAccount.account_id, + amount: 5, + date: '2021-12-20', + name: 'Expense: Starbucks', + merchant_name: 'Starbucks', + category: ['Shops', 'Food and Beverage Store'], + category_id: '19025001', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD02', + ...baseTxn, + }, + { + account_id: checkingAccount.account_id, + amount: 10, + date: '2021-12-29', + name: 'Expense: CVS', + merchant_name: 'CVS Pharmacy', + category: ['Shops', 'Pharmacies'], + category_id: '19043000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD03', + ...baseTxn, + }, + { + account_id: checkingAccount.account_id, + amount: 25.55, + date: '2022-01-01', + name: 'CVS', + merchant_name: 'CVS', + category: ['Shops', 'Bookstores'], + category_id: '19009000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD04', + ...baseTxn, + }, + { + account_id: checkingAccount.account_id, + amount: 89.45, + date: now.toISODate(), + name: 'Expense: Amazon', + merchant_name: 'Amazon', + category: ['Shops', 'Bookstores'], + category_id: '19009000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD05', + ...baseTxn, + }, +] + +export const creditTransactions: Transaction[] = [ + { + account_id: creditAccount.account_id, + amount: 200.25, + date: '2022-12-24', + name: 'Grocery Store', + merchant_name: 'Grocery Store Inc.', + category: ['Shops', 'Food and Beverage Store'], + category_id: '19025001', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD06', + ...baseTxn, + }, + { + account_id: creditAccount.account_id, + amount: -600, + date: '2022-12-26', + name: 'Credit Card Payment', + merchant_name: 'None', + category: ['Payment', 'Credit Card'], + category_id: '16001000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD07', + ...baseTxn, + }, + { + account_id: creditAccount.account_id, + amount: 10.0, + date: '2022-01-01', + name: 'Expense: CVS', + merchant_name: 'CVS Pharmacy', + category: ['Shops', 'Pharmacies'], + category_id: '19043000', + transaction_id: 'lPNjeW1nR6CDn5okmGQ6hEpMo4lLNoSrzqD08', + ...baseTxn, + }, +] + +export const creditCardLiability: CreditCardLiability = { + account_id: creditAccount.account_id, + aprs: [ + { + apr_percentage: 15.24, + apr_type: APRAprTypeEnum.BalanceTransferApr, + balance_subject_to_apr: 0, + interest_charge_amount: 0, + }, + { + apr_percentage: 27.95, + apr_type: APRAprTypeEnum.CashApr, + balance_subject_to_apr: 0, + interest_charge_amount: 0, + }, + { + apr_percentage: 12.5, + apr_type: APRAprTypeEnum.PurchaseApr, + balance_subject_to_apr: 0, + interest_charge_amount: 0, + }, + { + apr_percentage: 0, + apr_type: APRAprTypeEnum.Special, + balance_subject_to_apr: 0, + interest_charge_amount: 0, + }, + ], + is_overdue: false, + last_payment_amount: 600, + last_payment_date: '2021-12-26', // see txn above + last_statement_issue_date: '2022-01-01', + last_statement_balance: 1400, + minimum_payment_amount: 25, + next_payment_due_date: '2022-12-26', // 1 month after last payment (above) +} + +const appleStock = { + id: 'JDdP7XPMklt5vwPmDN45t3KAoWAPmjtpaW7DP', + name: 'Apple Inc.', + cusip: '0378331006', + ticker: 'AAPL', +} + +const walmartStock = { + id: 'NDVQrXQoqzt5v3bAe8qRt4A7mK7wvZCLEBBJk', + name: 'Walmart Inc.', + cusip: '93114210310', + ticker: 'WMT', +} + +const sp500IndexFund = { + id: 'BDVQrXQoqzt5v3bAe8qRtjA7mK7wvZCLEBBJk', + name: 'S&P 500 Index Fund', + cusip: '922908363', + ticker: 'VOO', +} + +export const holdings: Holding[] = [ + { + account_id: brokerageAccount.account_id, + cost_basis: 432.2, + institution_price: 439.25, + institution_price_as_of: now.toISODate(), // 2022-01-03 + institution_value: 19766.25, // price * qty + iso_currency_code: 'USD', + quantity: 45, + security_id: sp500IndexFund.id, + unofficial_currency_code: null, + }, + { + account_id: brokerageAccount.account_id, + cost_basis: 172.89, // actual cost basis (calculated based on txns) + institution_price: 182.01, + institution_price_as_of: now.toISODate(), + institution_value: 1820.1, + iso_currency_code: 'USD', + quantity: 10, + security_id: appleStock.id, + unofficial_currency_code: null, + }, + { + account_id: brokerageAccount.account_id, + cost_basis: 140.76, + institution_price: 144.65, + institution_price_as_of: now.toISODate(), + institution_value: 1186.13, + iso_currency_code: 'USD', + quantity: 8.2, + security_id: walmartStock.id, + unofficial_currency_code: null, + }, +] + +export const investmentTransactions: InvestmentTransaction[] = [ + { + account_id: brokerageAccount.account_id, + amount: -270, + date: '2022-01-03', + fees: 0, + investment_transaction_id: 'pK99jB9e7mtwjA435GpVuMvmWQKVbVFLWm002', + iso_currency_code: 'USD', + name: `SELL ${appleStock.name}`, + price: 182.01, + quantity: -10, + security_id: appleStock.id, + subtype: InvestmentTransactionSubtype.Sell, + type: InvestmentTransactionType.Sell, + unofficial_currency_code: null, + }, + { + account_id: brokerageAccount.account_id, + amount: 1154.23, + date: '2021-12-27', + fees: 0, + investment_transaction_id: 'LKoo1ko93wtreBwM7yQnuQ3P5DNKbKSPRz003', + iso_currency_code: 'USD', + name: `BUY ${walmartStock.name}`, + price: 140.76, + quantity: 8.2, + security_id: walmartStock.id, + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, + { + account_id: brokerageAccount.account_id, + amount: 3275.2, // price * qty + date: '2021-12-02', + fees: 0, + investment_transaction_id: 'LKoo1ko93wtreBwM7yQnuQ3P5DNKbKSPRz004', + iso_currency_code: 'USD', + name: `BUY ${appleStock.name}`, + price: 163.76, + quantity: 20, + security_id: appleStock.id, + subtype: InvestmentTransactionSubtype.Buy, + type: InvestmentTransactionType.Buy, + unofficial_currency_code: null, + }, +] + +export const securities: Security[] = [ + { + close_price: 439.25, + close_price_as_of: now.toISODate(), + cusip: sp500IndexFund.cusip, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: sp500IndexFund.name, + proxy_security_id: null, + security_id: sp500IndexFund.id, + sedol: null, + ticker_symbol: sp500IndexFund.ticker, + type: 'etf', + unofficial_currency_code: null, + }, + { + close_price: 182.01, + close_price_as_of: now.toISODate(), + cusip: appleStock.cusip, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: appleStock.name, + proxy_security_id: null, + security_id: appleStock.id, + sedol: null, + ticker_symbol: appleStock.ticker, + type: 'equity', + unofficial_currency_code: null, + }, + { + close_price: 144.65, + close_price_as_of: now.toISODate(), + cusip: walmartStock.cusip, + institution_id: null, + institution_security_id: null, + is_cash_equivalent: false, + isin: null, + iso_currency_code: 'USD', + name: walmartStock.name, + proxy_security_id: null, + security_id: walmartStock.id, + sedol: null, + ticker_symbol: walmartStock.ticker, + type: 'equity', + unofficial_currency_code: null, + }, +] diff --git a/tools/test-data/plaid/stockPrices.ts b/tools/test-data/plaid/stockPrices.ts new file mode 100644 index 00000000000..a041388ab10 --- /dev/null +++ b/tools/test-data/plaid/stockPrices.ts @@ -0,0 +1,725 @@ +export const AAPL = { + ticker: 'AAPL', + queryCount: 23, + resultsCount: 23, + adjusted: true, + results: [ + { + v: 1.52303002e8, + vw: 167.6855, + o: 167.48, + c: 164.77, + h: 170.3, + l: 164.53, + t: 1638334800000, + n: 1191148, + }, + { + v: 1.36739174e8, + vw: 161.8301, + o: 158.735, + c: 163.76, + h: 164.2, + l: 157.8, + t: 1638421200000, + n: 1163153, + }, + { + v: 1.17949416e8, + vw: 161.8243, + o: 164.02, + c: 161.84, + h: 164.96, + l: 159.72, + t: 1638507600000, + n: 950980, + }, + { + v: 1.07496982e8, + vw: 165.8234, + o: 164.29, + c: 165.32, + h: 167.8799, + l: 164.28, + t: 1638766800000, + n: 840677, + }, + { + v: 1.20356951e8, + vw: 170.5609, + o: 169.08, + c: 171.18, + h: 171.58, + l: 168.34, + t: 1638853200000, + n: 834819, + }, + { + v: 1.16982099e8, + vw: 174.0176, + o: 172.125, + c: 175.08, + h: 175.96, + l: 170.7, + t: 1638939600000, + n: 871401, + }, + { + v: 1.08923738e8, + vw: 175.277, + o: 174.91, + c: 174.56, + h: 176.75, + l: 173.92, + t: 1639026000000, + n: 786129, + }, + { + v: 1.15402731e8, + vw: 177.2721, + o: 175.205, + c: 179.45, + h: 179.63, + l: 174.69, + t: 1639112400000, + n: 855797, + }, + { + v: 1.53237019e8, + vw: 178.5988, + o: 181.115, + c: 175.74, + h: 182.13, + l: 175.53, + t: 1639371600000, + n: 1148448, + }, + { + v: 1.39356181e8, + vw: 174.4001, + o: 175.25, + c: 174.33, + h: 177.74, + l: 172.21, + t: 1639458000000, + n: 985603, + }, + { + v: 1.31063257e8, + vw: 176.0771, + o: 175.11, + c: 179.3, + h: 179.5, + l: 172.3108, + t: 1639544400000, + n: 937053, + }, + { + v: 1.50185842e8, + vw: 174.4138, + o: 179.28, + c: 172.26, + h: 181.14, + l: 170.75, + t: 1639630800000, + n: 1202731, + }, + { + v: 1.94579041e8, + vw: 171.2628, + o: 169.93, + c: 171.14, + h: 173.47, + l: 169.69, + t: 1639717200000, + n: 994729, + }, + { + v: 1.07499114e8, + vw: 169.2626, + o: 168.28, + c: 169.75, + h: 170.58, + l: 167.46, + t: 1639976400000, + n: 816118, + }, + { + v: 9.1167005e7, + vw: 171.4815, + o: 171.555, + c: 172.99, + h: 173.2, + l: 169.12, + t: 1640062800000, + n: 651088, + }, + { + v: 9.2135303e7, + vw: 174.3316, + o: 173.04, + c: 175.64, + h: 175.86, + l: 172.15, + t: 1640149200000, + n: 643554, + }, + { + v: 6.8356567e7, + vw: 176.1477, + o: 175.85, + c: 176.28, + h: 176.8499, + l: 175.27, + t: 1640235600000, + n: 502547, + }, + { + v: 7.4901582e7, + vw: 179.0573, + o: 177.085, + c: 180.33, + h: 180.42, + l: 177.07, + t: 1640581200000, + n: 630107, + }, + { + v: 7.8894335e7, + vw: 179.7048, + o: 180.16, + c: 179.29, + h: 181.33, + l: 178.53, + t: 1640667600000, + n: 631766, + }, + { + v: 6.2348931e7, + vw: 179.4557, + o: 179.33, + c: 179.38, + h: 180.63, + l: 178.14, + t: 1640754000000, + n: 491790, + }, + { + v: 5.9773014e7, + vw: 179.3744, + o: 179.47, + c: 178.2, + h: 180.57, + l: 178.09, + t: 1640840400000, + n: 498664, + }, + { + v: 6.4062261e7, + vw: 177.8004, + o: 178.085, + c: 177.57, + h: 179.23, + l: 177.26, + t: 1640926800000, + n: 451687, + }, + { + v: 1.0467747e8, + vw: 181.4156, + o: 177.83, + c: 182.01, + h: 182.88, + l: 177.71, + t: 1641186000000, + n: 772691, + }, + ], + status: 'OK', + request_id: 'd6a6a9ef51ba04953c81341d35dbdc50', + count: 23, +} + +export const WMT = { + ticker: 'WMT', + queryCount: 23, + resultsCount: 23, + adjusted: true, + results: [ + { + v: 1.2229044e7, + vw: 138.9281, + o: 140.51, + c: 137.14, + h: 140.715, + l: 137.04, + t: 1638334800000, + n: 150351, + }, + { + v: 1.320414e7, + vw: 136.5957, + o: 137.63, + c: 135.47, + h: 138.44, + l: 135.24, + t: 1638421200000, + n: 150388, + }, + { + v: 1.4714199e7, + vw: 137.0655, + o: 135.86, + c: 137.51, + h: 137.88, + l: 135.29, + t: 1638507600000, + n: 158917, + }, + { + v: 1.0863131e7, + vw: 138.608, + o: 137.56, + c: 139, + h: 139.36, + l: 137.1, + t: 1638766800000, + n: 118558, + }, + { + v: 1.2579666e7, + vw: 138.8208, + o: 139.6, + c: 138.55, + h: 139.83, + l: 138.09, + t: 1638853200000, + n: 129484, + }, + { + v: 1.1498754e7, + vw: 136.9568, + o: 138.06, + c: 137.15, + h: 138.5, + l: 136.01, + t: 1638939600000, + n: 137999, + }, + { + v: 1.0783902e7, + vw: 137.6754, + o: 136.63, + c: 138.5, + h: 138.57, + l: 135.71, + t: 1639026000000, + n: 120057, + }, + { + v: 1.2858541e7, + vw: 139.9875, + o: 138.34, + c: 141.03, + h: 141.3, + l: 137.57, + t: 1639112400000, + n: 134986, + }, + { + v: 1.8013121e7, + vw: 142.6217, + o: 140.24, + c: 143.57, + h: 143.7499, + l: 140.01, + t: 1639371600000, + n: 138162, + }, + { + v: 1.5573758e7, + vw: 144.2492, + o: 143.3, + c: 144.94, + h: 145.28, + l: 142.1501, + t: 1639458000000, + n: 160822, + }, + { + v: 1.1163305e7, + vw: 145.4025, + o: 145.21, + c: 145.77, + h: 146.47, + l: 143.4, + t: 1639544400000, + n: 148585, + }, + { + v: 1.302316e7, + vw: 144.3513, + o: 145.77, + c: 143.34, + h: 146.526, + l: 142.89, + t: 1639630800000, + n: 134949, + }, + { + v: 2.6237272e7, + vw: 140.078, + o: 142.75, + c: 138.75, + h: 143.95, + l: 138.39, + t: 1639717200000, + n: 148046, + }, + { + v: 1.0339292e7, + vw: 138.9089, + o: 138.12, + c: 139.2, + h: 139.75, + l: 137.6, + t: 1639976400000, + n: 121394, + }, + { + v: 8.206812e6, + vw: 139.2117, + o: 138.9, + c: 139.62, + h: 139.86, + l: 138.01, + t: 1640062800000, + n: 96393, + }, + { + v: 5.67595e6, + vw: 139.5529, + o: 139.9, + c: 139.8, + h: 139.99, + l: 138.53, + t: 1640149200000, + n: 75569, + }, + { + v: 5.864216e6, + vw: 139.5687, + o: 139.8, + c: 139.49, + h: 140.16, + l: 138.85, + t: 1640235600000, + n: 71793, + }, + { + v: 5.351125e6, + vw: 140.3389, + o: 139.6, + c: 140.76, + h: 140.87, + l: 139.57, + t: 1640581200000, + n: 68345, + }, + { + v: 5.263016e6, + vw: 142.2768, + o: 140.6, + c: 142.78, + h: 142.97, + l: 140.6, + t: 1640667600000, + n: 71618, + }, + { + v: 4.34842e6, + vw: 142.7955, + o: 142.66, + c: 142.71, + h: 143.53, + l: 142.32, + t: 1640754000000, + n: 60034, + }, + { + v: 4.982997e6, + vw: 143.1826, + o: 143.26, + c: 143.17, + h: 143.695, + l: 142.48, + t: 1640840400000, + n: 67651, + }, + { + v: 5.982595e6, + vw: 144.3238, + o: 143.2, + c: 144.69, + h: 145.045, + l: 142.92, + t: 1640926800000, + n: 69498, + }, + { + v: 6.903262e6, + vw: 144.0853, + o: 144, + c: 144.65, + h: 144.7899, + l: 143.01, + t: 1641186000000, + n: 81863, + }, + ], + status: 'OK', + request_id: '51cee9c4502c0ceec2e1e9bc36a20c70', + count: 23, +} + +export const VOO = { + ticker: 'VOO', + queryCount: 23, + resultsCount: 23, + adjusted: true, + results: [ + { + v: 8.370491e6, + vw: 421.8237, + o: 424.47, + c: 414.24, + h: 427.2212, + l: 413.97, + t: 1638334800000, + n: 169458, + }, + { + v: 1.0417672e7, + vw: 419.2953, + o: 414.42, + c: 420.56, + h: 422.06, + l: 414.03, + t: 1638421200000, + n: 197937, + }, + { + v: 1.0228931e7, + vw: 416.0156, + o: 422.12, + c: 416.84, + h: 423.19, + l: 412.77, + t: 1638507600000, + n: 208546, + }, + { + v: 7.289493e6, + vw: 421.0199, + o: 419.41, + c: 421.82, + h: 423.66, + l: 417, + t: 1638766800000, + n: 145327, + }, + { + v: 4.73192e6, + vw: 429.9147, + o: 426.98, + c: 430.49, + h: 431.1, + l: 426.94, + t: 1638853200000, + n: 72583, + }, + { + v: 4.476542e6, + vw: 430.7642, + o: 430.95, + c: 431.7, + h: 432.14, + l: 429.24, + t: 1638939600000, + n: 87838, + }, + { + v: 5.035427e6, + vw: 430.1188, + o: 430.46, + c: 428.76, + h: 431.32, + l: 428.61, + t: 1639026000000, + n: 97576, + }, + { + v: 6.238301e6, + vw: 431.3396, + o: 431.44, + c: 432.85, + h: 432.96, + l: 428.95, + t: 1639112400000, + n: 95289, + }, + { + v: 5.923878e6, + vw: 430.1667, + o: 432.36, + c: 429.01, + h: 432.66, + l: 428.75, + t: 1639371600000, + n: 114541, + }, + { + v: 6.522926e6, + vw: 425.9026, + o: 425.8, + c: 426.09, + h: 428.24, + l: 423.2, + t: 1639458000000, + n: 98175, + }, + { + v: 6.871253e6, + vw: 427.6565, + o: 426.11, + c: 432.75, + h: 432.98, + l: 423.67, + t: 1639544400000, + n: 125175, + }, + { + v: 7.159519e6, + vw: 431.2728, + o: 434.56, + c: 428.84, + h: 434.8, + l: 427.35, + t: 1639630800000, + n: 121716, + }, + { + v: 8.883451e6, + vw: 425.7147, + o: 425.85, + c: 424.39, + h: 428.79, + l: 422.67, + t: 1639717200000, + n: 161448, + }, + { + v: 9.90341e6, + vw: 418.2544, + o: 419.39, + c: 419.8, + h: 420.22, + l: 416.3, + t: 1639976400000, + n: 206669, + }, + { + v: 6.916663e6, + vw: 423.2171, + o: 421.65, + c: 425.69, + h: 425.85, + l: 419.52, + t: 1640062800000, + n: 114763, + }, + { + v: 4.992213e6, + vw: 428.1321, + o: 425.45, + c: 429.99, + h: 430.09, + l: 425.32, + t: 1640149200000, + n: 88506, + }, + { + v: 5.032684e6, + vw: 432.8612, + o: 430.91, + c: 432.64, + h: 434.0963, + l: 430.87, + t: 1640235600000, + n: 82131, + }, + { + v: 4.986317e6, + vw: 436.7967, + o: 433.98, + c: 438.81, + h: 438.82, + l: 433.95, + t: 1640581200000, + n: 90064, + }, + { + v: 4.865844e6, + vw: 438.8478, + o: 439.28, + c: 438.48, + h: 440.19, + l: 437.655, + t: 1640667600000, + n: 89787, + }, + { + v: 4.596686e6, + vw: 438.832, + o: 438.5, + c: 439.01, + h: 439.95, + l: 437.54, + t: 1640754000000, + n: 84197, + }, + { + v: 3.751831e6, + vw: 439.1982, + o: 439.39, + c: 437.77, + h: 440.36, + l: 437.32, + t: 1640840400000, + n: 79753, + }, + { + v: 5.185608e6, + vw: 437.3027, + o: 437.23, + c: 436.57, + h: 438.38, + l: 436.39, + t: 1640926800000, + n: 88604, + }, + { + v: 6.017303e6, + vw: 437.8693, + o: 437.93, + c: 439.25, + h: 439.27, + l: 435.64, + t: 1641186000000, + n: 132670, + }, + ], + status: 'OK', + request_id: 'a8a4fa42c1a039ced1507b26ce7dc73a', + count: 23, +} diff --git a/tools/test-data/plaid/testDates.ts b/tools/test-data/plaid/testDates.ts new file mode 100644 index 00000000000..775583819bc --- /dev/null +++ b/tools/test-data/plaid/testDates.ts @@ -0,0 +1,32 @@ +import type { Prisma } from '@prisma/client' +import { DateTime } from 'luxon' + +/** + * For simplicity, integration tests should generally stay within the following date range: + * + * 2021-12-01 => 2022-01-02 + * + * By choosing these dates, we're covering a couple important edge cases: + * + * - Can test data when year changes + * - Can test data when month changes + * - Gives us 1 full month (Dec) + * - Dates are guaranteed to be in the past with REAL data (helpful for stock prices) + * - Range is small enough for testing + */ + +const now = DateTime.fromISO('2022-01-03', { zone: 'utc' }) + +const lowerBound = DateTime.fromISO('2021-12-01', { zone: 'utc' }) + +export const testDates = { + now, + lowerBound, + totalDays: now.diff(lowerBound, 'days').days, + prismaWhereFilter: { + date: { + gte: lowerBound.toJSDate(), + lte: now.toJSDate(), + }, + } as Prisma.AccountBalanceWhereInput, +} diff --git a/tools/test-data/polygon/index.ts b/tools/test-data/polygon/index.ts new file mode 100644 index 00000000000..1cc6e9b9173 --- /dev/null +++ b/tools/test-data/polygon/index.ts @@ -0,0 +1 @@ +export * from './snapshots' diff --git a/tools/test-data/polygon/snapshots.ts b/tools/test-data/polygon/snapshots.ts new file mode 100644 index 00000000000..70acd1788b0 --- /dev/null +++ b/tools/test-data/polygon/snapshots.ts @@ -0,0 +1,99 @@ +// https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/tickers?tickers=AAPL,VOO +export const snapshotAllTickers = { + count: 2, + status: 'DELAYED', + tickers: [ + { + day: { + c: 366.03, + h: 371.445, + l: 364.02, + o: 371.3, + v: 5033415, + vw: 366.3978, + }, + lastQuote: { + P: 366.03, + S: 4, + p: 365.98, + s: 4, + t: 1661892857579892500, + }, + lastTrade: { + c: [14, 12, 41], + i: '52983575693109', + p: 366.16, + s: 400, + t: 1661892199398749200, + x: 11, + }, + min: { + av: 5033152, + c: 366.16, + h: 366.16, + l: 366.16, + o: 366.16, + v: 412, + vw: 366.16, + }, + prevDay: { + c: 370.05, + h: 373.05, + l: 368.78, + o: 369.77, + v: 4953159, + vw: 371.0004, + }, + ticker: 'VOO', + todaysChange: -3.89, + todaysChangePerc: -1.051, + updated: 1661892240000000000, + }, + { + day: { + c: 158.91, + h: 162.56, + l: 157.72, + o: 162.13, + v: 77314864, + vw: 159.2953, + }, + lastQuote: { + P: 159.16, + S: 1, + p: 159.12, + s: 5, + t: 1661892795748055800, + }, + lastTrade: { + c: [12], + i: '61326', + p: 159.13, + s: 100, + t: 1661892795747899000, + x: 11, + }, + min: { + av: 77314859, + c: 159.13, + h: 159.13, + l: 159.12, + o: 159.12, + v: 970, + vw: 159.1241, + }, + prevDay: { + c: 161.38, + h: 162.9, + l: 159.82, + o: 161.145, + v: 73313953, + vw: 161.5291, + }, + ticker: 'AAPL', + todaysChange: -2.25, + todaysChangePerc: -1.394, + updated: 1661892840000000000, + }, + ], +} diff --git a/tools/tsconfig.tools.json b/tools/tsconfig.tools.json new file mode 100644 index 00000000000..a8090724460 --- /dev/null +++ b/tools/tsconfig.tools.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "outDir": "../dist/out-tsc/tools", + "rootDir": ".", + "module": "commonjs", + "target": "es5", + "types": ["node"], + "importHelpers": false + }, + "include": ["**/*.ts"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 00000000000..09578d91bb6 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,30 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "rootDir": ".", + "sourceMap": true, + "declaration": false, + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES6", + "module": "ESNext", + "lib": ["es2017", "dom", "ESNext"], + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "importsNotUsedAsValues": "error", + "baseUrl": ".", + "paths": { + "@maybe-finance/client/features": ["libs/client/features/src/index.ts"], + "@maybe-finance/client/shared": ["libs/client/shared/src/index.ts"], + "@maybe-finance/design-system": ["libs/design-system/src/index.ts"], + "@maybe-finance/finicity-api": ["libs/finicity-api/src/index.ts"], + "@maybe-finance/server/features": ["libs/server/features/src/index.ts"], + "@maybe-finance/server/shared": ["libs/server/shared/src/index.ts"], + "@maybe-finance/shared": ["libs/shared/src/index.ts"], + "@maybe-finance/trpc": ["apps/server/src/app/trpc.ts"] + } + }, + "exclude": ["node_modules", "tmp"] +} diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000000..a561be3efd0 --- /dev/null +++ b/vercel.json @@ -0,0 +1,5 @@ +{ + "github": { + "enabled": true + } +} diff --git a/workspace.json b/workspace.json new file mode 100644 index 00000000000..4e2c2978c24 --- /dev/null +++ b/workspace.json @@ -0,0 +1,522 @@ +{ + "version": 2, + "projects": { + "advisor": { + "root": "apps/advisor", + "sourceRoot": "apps/advisor", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/next:build", + "outputs": ["{options.outputPath}"], + "defaultConfiguration": "production", + "options": { + "root": "apps/advisor", + "outputPath": "dist/apps/advisor" + }, + "configurations": { + "development": { + "outputPath": "apps/advisor" + }, + "production": {} + } + }, + "serve": { + "executor": "@nrwl/next:server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "advisor:build", + "dev": true, + "port": 4201 + }, + "configurations": { + "development": { + "buildTarget": "advisor:build:development", + "dev": true + }, + "production": { + "buildTarget": "advisor:build:production", + "dev": false + } + } + }, + "export": { + "executor": "@nrwl/next:export", + "options": { + "buildTarget": "advisor:build:production" + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/advisor/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/advisor/**/*.{ts,tsx,js,jsx}"] + } + } + }, + "tags": ["scope:app"] + }, + "client": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "apps/client", + "sourceRoot": "apps/client", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/next:build", + "outputs": ["{options.outputPath}"], + "options": { + "root": "apps/client", + "outputPath": "dist/apps/client" + }, + "configurations": { + "production": {}, + "development": { + "outputPath": "apps/client" + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@nrwl/next:server", + "options": { + "buildTarget": "client:build", + "dev": true + }, + "configurations": { + "production": { + "buildTarget": "client:build:production", + "dev": false + }, + "development": { + "buildTarget": "client:build:development", + "dev": true + } + }, + "defaultConfiguration": "development" + }, + "export": { + "executor": "@nrwl/next:export", + "options": { + "buildTarget": "client:build:production" + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/apps/client"], + "options": { + "jestConfig": "apps/client/jest.config.ts", + "passWithNoTests": true + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/client/**/*.{ts,tsx,js,jsx}"] + } + }, + "deploy": { + "executor": "nx:run-commands", + "options": { + "command": "node tools/scripts/triggerClientDeploy.js" + } + } + }, + "tags": ["scope:app"] + }, + "client-features": { + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "root": "libs/client/features", + "sourceRoot": "libs/client/features/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/client/features/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/client/features"], + "options": { + "jestConfig": "libs/client/features/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:client"] + }, + "client-shared": { + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "root": "libs/client/shared", + "sourceRoot": "libs/client/shared/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/client/shared/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/client/shared"], + "options": { + "jestConfig": "libs/client/shared/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:client-shared"] + }, + "design-system": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "libs/design-system", + "sourceRoot": "libs/design-system/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nrwl/rollup:rollup", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/libs/design-system", + "tsConfig": "libs/design-system/tsconfig.lib.json", + "project": "libs/design-system/package.json", + "entryFile": "libs/design-system/src/index.ts", + "external": ["react/jsx-runtime"], + "rollupConfig": "@nrwl/react/plugins/bundle-rollup", + "assets": [ + { + "glob": "libs/design-system/README.md", + "input": ".", + "output": "." + } + ] + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false + } + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/design-system/**/*.{ts,tsx,js,jsx}"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/design-system"], + "options": { + "jestConfig": "libs/design-system/jest.config.ts", + "passWithNoTests": true + } + }, + "storybook": { + "executor": "@nrwl/storybook:storybook", + "options": { + "uiFramework": "@storybook/react", + "port": 4400, + "staticDir": ["libs/design-system/.storybook/public"], + "configDir": "libs/design-system/.storybook", + "docs": true + }, + "configurations": { + "ci": { + "quiet": true + } + } + }, + "build-storybook": { + "executor": "@nrwl/storybook:build", + "outputs": ["{options.outputDir}"], + "options": { + "uiFramework": "@storybook/react", + "staticDir": ["libs/design-system/.storybook/public"], + "configDir": "libs/design-system/.storybook", + "outputDir": "dist/storybook/design-system", + "docs": true + }, + "configurations": { + "ci": { + "quiet": true + } + } + }, + "deploy": { + "executor": "nx:run-commands", + "options": { + "command": "node tools/scripts/triggerDesignSystemDeploy.js" + } + } + }, + "tags": ["scope:client-shared"] + }, + "e2e": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "apps/e2e", + "sourceRoot": "apps/e2e/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nrwl/cypress:cypress", + "options": { + "cypressConfig": "apps/e2e/cypress.config.ts", + "tsConfig": "apps/e2e/tsconfig.json", + "testingType": "e2e" + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/e2e/**/*.{js,ts}"] + } + } + }, + "tags": ["scope:app"], + "implicitDependencies": ["client", "server", "workers"] + }, + "finicity-api": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "libs/finicity-api", + "sourceRoot": "libs/finicity-api/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/finicity-api/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/finicity-api"], + "options": { + "jestConfig": "libs/finicity-api/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:shared"] + }, + "server": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "apps/server", + "sourceRoot": "apps/server/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/server", + "main": "apps/server/src/main.ts", + "tsConfig": "apps/server/tsconfig.app.json", + "assets": ["apps/server/src/assets", "apps/server/src/app/admin/views"], + "target": "node", + "compiler": "tsc", + "generatePackageJson": true + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false, + "fileReplacements": [ + { + "replace": "apps/server/src/environments/environment.ts", + "with": "apps/server/src/environments/environment.prod.ts" + } + ] + } + } + }, + "serve": { + "executor": "@nrwl/node:node", + "options": { + "buildTarget": "server:build", + "inspect": true, + "port": 9228 + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/server/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/apps/server"], + "options": { + "jestConfig": "apps/server/jest.config.ts", + "passWithNoTests": true + } + }, + "deploy": { + "executor": "nx:run-commands", + "options": { + "command": "node tools/scripts/triggerServerDeploy.js" + } + } + }, + "tags": ["scope:app"] + }, + "server-features": { + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "root": "libs/server/features", + "sourceRoot": "libs/server/features/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/server/features/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/server/features"], + "options": { + "jestConfig": "libs/server/features/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:server"] + }, + "server-shared": { + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "root": "libs/server/shared", + "sourceRoot": "libs/server/shared/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/server/shared/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/server/shared"], + "options": { + "jestConfig": "libs/server/shared/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:server-shared"] + }, + "shared": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "libs/shared", + "sourceRoot": "libs/shared/src", + "projectType": "library", + "targets": { + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["libs/shared/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/libs/shared"], + "options": { + "jestConfig": "libs/shared/jest.config.ts", + "passWithNoTests": true + } + } + }, + "tags": ["scope:shared"] + }, + "workers": { + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "root": "apps/workers", + "sourceRoot": "apps/workers/src", + "projectType": "application", + "targets": { + "build": { + "executor": "@nrwl/webpack:webpack", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/workers", + "main": "apps/workers/src/main.ts", + "tsConfig": "apps/workers/tsconfig.app.json", + "assets": ["apps/workers/src/assets"], + "target": "node", + "compiler": "tsc", + "generatePackageJson": true + }, + "configurations": { + "production": { + "optimization": true, + "extractLicenses": true, + "inspect": false, + "fileReplacements": [ + { + "replace": "apps/workers/src/environments/environment.ts", + "with": "apps/workers/src/environments/environment.prod.ts" + } + ] + } + } + }, + "serve": { + "executor": "@nrwl/node:node", + "options": { + "buildTarget": "workers:build", + "inspect": true, + "port": 9227 + } + }, + "lint": { + "executor": "@nrwl/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/workers/**/*.ts"] + } + }, + "test": { + "executor": "@nrwl/jest:jest", + "outputs": ["coverage/apps/workers"], + "options": { + "jestConfig": "apps/workers/jest.config.ts", + "passWithNoTests": true + } + }, + "deploy": { + "executor": "nx:run-commands", + "options": { + "command": "node tools/scripts/triggerWorkersDeploy.js" + } + } + }, + "tags": ["scope:app"] + } + }, + "$schema": "./node_modules/nx/schemas/workspace-schema.json" +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000000..1ecf30d811c --- /dev/null +++ b/yarn.lock @@ -0,0 +1,21901 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34" + integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg== + dependencies: + "@jridgewell/trace-mapping" "^0.3.0" + +"@auth0/auth0-react@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@auth0/auth0-react/-/auth0-react-2.0.0.tgz#74e4d3662896e71dd95cca70b395715825da3b4e" + integrity sha512-3pf41wU6ksm/6uPYAwjX5bZ7ma/K4LethibagTrKkMPuS8UatBvxLDtl3Aq52ZlJi1I+I42ckEfzWqloNxssIg== + dependencies: + "@auth0/auth0-spa-js" "^2.0.2" + +"@auth0/auth0-spa-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-2.0.2.tgz#fe0d5eeb6f0da48c24913a07b3565d48792de6d5" + integrity sha512-sxK9Lb6gXGImqjmWBfndA/OSNY4YLPTPwJEVuitXIOZ1p3EoqHM4zjIHvcdiYIaVo+cUfEf3l0bf8UA7Xi4tjg== + +"@auth0/nextjs-auth0@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@auth0/nextjs-auth0/-/nextjs-auth0-2.0.0.tgz#6695c6eb0d657f4ee6e4234891d3cb50ba0893d1" + integrity sha512-LwV3AqJh0CXzzM1vUgSVKvYcZQsp3NzV4xboBCwvfzz7DIWVu1Ge1v0uQBGnzl3XDtFFQNYqVGjcPw5Wu1/L6A== + dependencies: + "@panva/hkdf" "^1.0.2" + cookie "^0.5.0" + debug "^4.3.4" + http-errors "^1.8.1" + joi "^17.6.0" + jose "^4.9.2" + openid-client "^5.2.1" + tslib "^2.4.0" + url-join "^4.0.1" + +"@aws-crypto/crc32@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-2.0.0.tgz#4ad432a3c03ec3087c5540ff6e41e6565d2dc153" + integrity sha512-TvE1r2CUueyXOuHdEigYjIZVesInd9KN+K/TFFNfkkxRThiNxO6i4ZqqAVMoEjAamZZ1AA8WXJkjCz7YShHPQA== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/crc32c@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-2.0.0.tgz#4235336ef78f169f6a05248906703b9b78da676e" + integrity sha512-vF0eMdMHx3O3MoOXUfBZry8Y4ZDtcuskjjKgJz8YfIDjLStxTZrYXk+kZqtl6A0uCmmiN/Eb/JbC/CndTV1MHg== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/ie11-detection@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz#9c39f4a5558196636031a933ec1b4792de959d6a" + integrity sha512-5XDMQY98gMAf/WRTic5G++jfmS/VLM0rwpiOpaainKi4L0nqWMSB1SzsrEG5rjFZGYN6ZAefO+/Yta2dFM0kMw== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha1-browser@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-2.0.0.tgz#71e735df20ea1d38f59259c4b1a2e00ca74a0eea" + integrity sha512-3fIVRjPFY8EG5HWXR+ZJZMdWNRpwbxGzJ9IH9q93FpbgCH8u8GHRi46mZXp3cYD7gealmyqpm3ThZwLKJjWJhA== + dependencies: + "@aws-crypto/ie11-detection" "^2.0.0" + "@aws-crypto/supports-web-crypto" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz#741c9024df55ec59b51e5b1f5d806a4852699fb5" + integrity sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A== + dependencies: + "@aws-crypto/ie11-detection" "^2.0.0" + "@aws-crypto/sha256-js" "^2.0.0" + "@aws-crypto/supports-web-crypto" "^2.0.0" + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz#f1f936039bdebd0b9e2dd834d65afdc2aac4efcb" + integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.2.tgz#c81e5d378b8a74ff1671b58632779986e50f4c99" + integrity sha512-iXLdKH19qPmIC73fVCrHWCSYjN/sxaAvZ3jNNyw6FclmHyjLKg0f69WlC9KTnyElxCR5MO9SKaG00VwlJwyAkQ== + dependencies: + "@aws-crypto/util" "^2.0.2" + "@aws-sdk/types" "^3.110.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^2.0.0": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz#9f02aafad8789cac9c0ab5faaebb1ab8aa841338" + integrity sha512-6mbSsLHwZ99CTOOswvCRP3C+VCWnzBf+1SnbWxzzJ9lR0mA0JnY2JEAhp8rqmTE0GPFy88rrM27ffgp62oErMQ== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^2.0.0", "@aws-crypto/util@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-2.0.2.tgz#adf5ff5dfbc7713082f897f1d01e551ce0edb9c0" + integrity sha512-Lgu5v/0e/BcrZ5m/IWqzPUf3UYFTy/PpeED+uc9SWUR1iZQL8XXbGQg10UfllwwBryO3hFF5dizK+78aoXC1eA== + dependencies: + "@aws-sdk/types" "^3.110.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/abort-controller@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/abort-controller/-/abort-controller-3.226.0.tgz#3adffb8ee5af57ddb154e8544a8eeec76ad32271" + integrity sha512-cJVzr1xxPBd08voknXvR0RLgtZKGKt6WyDpH/BaPCu3rfSqWCDZKzwqe940eqosjmKrxC6pUZNKASIqHOQ8xxQ== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/chunked-blob-reader-native@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/chunked-blob-reader-native/-/chunked-blob-reader-native-3.208.0.tgz#cdbd12c89a4f3ddd91bf707da8bb4af311487cc5" + integrity sha512-JeOZ95PW+fJ6bbuqPySYqLqHk1n4+4ueEEraJsiUrPBV0S1ZtyvOGHcnGztKUjr2PYNaiexmpWuvUve9K12HRA== + dependencies: + "@aws-sdk/util-base64" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/chunked-blob-reader@3.188.0": + version "3.188.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.188.0.tgz#18181b27511ab512e56b9f2cef30d2abbef639dc" + integrity sha512-zkPRFZZPL3eH+kH86LDYYXImiClA1/sW60zYOjse9Pgka+eDJlvBN6hcYxwDEKjcwATYiSRR1aVQHcfCinlGXg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/client-s3@^3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.231.0.tgz#57dcd29f323cae1e9542136ab18033e44db6fd9f" + integrity sha512-YFW5rh3tVQH0C4arMXcbK+B3b4IF7ojRo6cHOUVrZDoW0xud5JFqcU5iJNRHWbBcbRoqRL537FdF3crswGvuHg== + dependencies: + "@aws-crypto/sha1-browser" "2.0.0" + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.231.0" + "@aws-sdk/config-resolver" "3.231.0" + "@aws-sdk/credential-provider-node" "3.231.0" + "@aws-sdk/eventstream-serde-browser" "3.226.0" + "@aws-sdk/eventstream-serde-config-resolver" "3.226.0" + "@aws-sdk/eventstream-serde-node" "3.226.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-blob-browser" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/hash-stream-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/md5-js" "3.226.0" + "@aws-sdk/middleware-bucket-endpoint" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-expect-continue" "3.226.0" + "@aws-sdk/middleware-flexible-checksums" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-location-constraint" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.229.0" + "@aws-sdk/middleware-sdk-s3" "3.231.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-signing" "3.226.0" + "@aws-sdk/middleware-ssec" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/signature-v4-multi-region" "3.226.0" + "@aws-sdk/smithy-client" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.226.0" + "@aws-sdk/util-defaults-mode-node" "3.231.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-stream-browser" "3.226.0" + "@aws-sdk/util-stream-node" "3.226.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + "@aws-sdk/util-waiter" "3.226.0" + "@aws-sdk/xml-builder" "3.201.0" + fast-xml-parser "4.0.11" + tslib "^2.3.1" + +"@aws-sdk/client-secrets-manager@^3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.235.0.tgz#33dada4fab0db6e8f18964583bc7b4452674fa7c" + integrity sha512-WFrmkIhBw78yE067GJpPeXhueODyt7GMgVrQOGVDR5J/VhKfCy71hzPR3r/uttL/iJH1gWmn8jKUIHFJqvtLIA== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.235.0" + "@aws-sdk/config-resolver" "3.234.0" + "@aws-sdk/credential-provider-node" "3.235.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.235.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-signing" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.234.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.234.0" + "@aws-sdk/util-defaults-mode-node" "3.234.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/client-sso-oidc@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.231.0.tgz#de8783b608faf4b829b52e12a088f88326f9b851" + integrity sha512-yqEZW9/Q6VvMDMcQoE52oa/oa6F8z8cqyax7m29VpuVrncYcfELpkZKWPoaJVfierR5ysKfKiAU0acPgMpvllQ== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.231.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.229.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.226.0" + "@aws-sdk/util-defaults-mode-node" "3.231.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/client-sso-oidc@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.235.0.tgz#871aea9cc6f7421e97e6f19351066612f094cb30" + integrity sha512-oSF0lSPmE5jaaigxc5TZyDjqfgTiDsromEdvv5s5a/qAOZBNtsVaS4+8cn9kIt43Ceo7dxHXk7cwvXMNPwVnWw== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.234.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.235.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.234.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.234.0" + "@aws-sdk/util-defaults-mode-node" "3.234.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/client-sso@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.231.0.tgz#783322cb936da353bb047dd2fb762eab09c19c50" + integrity sha512-/q7BptaMiT6/wxW9vE/gcQuApMXio5vdTuqt77A6+mjqhNzYFfCn7RRS4BU8KEOpZObnYBKP3mYe3NDccEbMzQ== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.231.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.229.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.226.0" + "@aws-sdk/util-defaults-mode-node" "3.231.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/client-sso@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.235.0.tgz#8a18f372b8fd3b632e7757b1c3d42b63989a7d61" + integrity sha512-CdZ2EnDuB6V41u6brk/Nt19EZneLorsNkNWr4J7zkR/2gKiqdUN6PUs6jxDtK9M7V/r81jaE0ViLwLVmYhL/bQ== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.234.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.235.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.234.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.234.0" + "@aws-sdk/util-defaults-mode-node" "3.234.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/client-sts@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.231.0.tgz#ec4689dc8d4d81d4bd1a13f3508bf3c9d5a1e7fb" + integrity sha512-5WYqlcbM49ofOFBsu28QBt3t26M5D9XynhSaswSrCzawwdNkIMYQrKOCplF5mqOy+GywVIRrFeCVVrAKPMZJxQ== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.231.0" + "@aws-sdk/credential-provider-node" "3.231.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.229.0" + "@aws-sdk/middleware-sdk-sts" "3.226.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-signing" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.226.0" + "@aws-sdk/util-defaults-mode-node" "3.231.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + fast-xml-parser "4.0.11" + tslib "^2.3.1" + +"@aws-sdk/client-sts@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.235.0.tgz#013ce2cfdf48c3d8d46167c2fe7c9a103a56d2c0" + integrity sha512-P1pqvg7brdBUGrTlyMc+LCe6rnWrWufdd7bpzuC9lcVzkoOHJw8j8wDItwoCsvy1O3SeK7vtmOTLxV2yuTEO3Q== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.234.0" + "@aws-sdk/credential-provider-node" "3.235.0" + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/hash-node" "3.226.0" + "@aws-sdk/invalid-dependency" "3.226.0" + "@aws-sdk/middleware-content-length" "3.226.0" + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/middleware-host-header" "3.226.0" + "@aws-sdk/middleware-logger" "3.226.0" + "@aws-sdk/middleware-recursion-detection" "3.226.0" + "@aws-sdk/middleware-retry" "3.235.0" + "@aws-sdk/middleware-sdk-sts" "3.226.0" + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/middleware-signing" "3.226.0" + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/middleware-user-agent" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/smithy-client" "3.234.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-body-length-browser" "3.188.0" + "@aws-sdk/util-body-length-node" "3.208.0" + "@aws-sdk/util-defaults-mode-browser" "3.234.0" + "@aws-sdk/util-defaults-mode-node" "3.234.0" + "@aws-sdk/util-endpoints" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + "@aws-sdk/util-user-agent-browser" "3.226.0" + "@aws-sdk/util-user-agent-node" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + fast-xml-parser "4.0.11" + tslib "^2.3.1" + +"@aws-sdk/cloudfront-signer@^3.229.0": + version "3.229.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/cloudfront-signer/-/cloudfront-signer-3.229.0.tgz#fa65ccce0c8a0c7ed7c769055f52e587af76a970" + integrity sha512-PQWITtrTh6xB7p4g7T843afYipaYjevyBMWfYrt9X86Gq5yQrPq/GxpcPfwN+va/XJc3QuUA1F0EfoVSyGK3tQ== + dependencies: + "@aws-sdk/url-parser" "3.226.0" + +"@aws-sdk/config-resolver@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.231.0.tgz#3254a73d10c5d1eeaa2ee8d500676ca7e2ec4339" + integrity sha512-qpjV4Fw/NQ4a0p5/qwzqaShflYRlY/SPcgA7N5GTJjIjZjg3NV+5BKJSF3VeZcNKfbXq68kkn207OSCpyheYxQ== + dependencies: + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-config-provider" "3.208.0" + "@aws-sdk/util-middleware" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/config-resolver@3.234.0": + version "3.234.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.234.0.tgz#29d8936b713b7ee59b26b335d4f6715d644fc089" + integrity sha512-uZxy4wzllfvgCQxVc+Iqhde0NGAnfmV2hWR6ejadJaAFTuYNvQiRg9IqJy3pkyDPqXySiJ8Bom5PoJfgn55J/A== + dependencies: + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-config-provider" "3.208.0" + "@aws-sdk/util-middleware" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-env@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.226.0.tgz#0bcb89a9abc166b3a48f5c255b9fcabc4cb80daf" + integrity sha512-sd8uK1ojbXxaZXlthzw/VXZwCPUtU3PjObOfr3Evj7MPIM2IH8h29foOlggx939MdLQGboJf9gKvLlvKDWtJRA== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-imds@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.226.0.tgz#0a4558449eb261412b0490ea1c3242eb91659759" + integrity sha512-//z/COQm2AjYFI1Lb0wKHTQSrvLFTyuKLFQGPJsKS7DPoxGOCKB7hmYerlbl01IDoCxTdyL//TyyPxbZEOQD5Q== + dependencies: + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-ini@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.231.0.tgz#008166b300b7e3d0139b57fc8c1204fa39182290" + integrity sha512-4JJgrJg2O91Vki4m5nSQNZGX/5yAYgzG1IOjeZ+8vCDxfR+jA2O9+/Xhi2/8aDpb1da77OJ+cK1+ezzSMchIfQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.226.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/credential-provider-sso" "3.231.0" + "@aws-sdk/credential-provider-web-identity" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-ini@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.235.0.tgz#8e8c11e11521b476d96270d5b6349d718bd06504" + integrity sha512-i3efxJw+9hN/opmjlRqT0lv/gKjOHmgGBUbFCdbCmSUTc8QH3sbkIY6FLX2q0PSu4q4CpCwWZ587lkThtFerJA== + dependencies: + "@aws-sdk/credential-provider-env" "3.226.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/credential-provider-process" "3.226.0" + "@aws-sdk/credential-provider-sso" "3.235.0" + "@aws-sdk/credential-provider-web-identity" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-node@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.231.0.tgz#5484f06c15e07c788f6a6213d10d31190ee4965c" + integrity sha512-DOojjyYdLNeBQv9+PaDXmvvww9SmcZsaL1YCl27e5larcJSMfT41vn4WRnVRu2zBI2BIi464Z8ziRRKwd2YFVg== + dependencies: + "@aws-sdk/credential-provider-env" "3.226.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/credential-provider-ini" "3.231.0" + "@aws-sdk/credential-provider-process" "3.226.0" + "@aws-sdk/credential-provider-sso" "3.231.0" + "@aws-sdk/credential-provider-web-identity" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-node@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.235.0.tgz#7f7533623ed00f66eb93bc4c809cb869566301dc" + integrity sha512-xJSAntEBlbXbngSzpyMnlZzOldxV0sNOQ7ggDUmIQNVWbu3XwP6MiA8CjAHnH10vs6+pcyHtpfUj3evdQZ4JlQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.226.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/credential-provider-ini" "3.235.0" + "@aws-sdk/credential-provider-process" "3.226.0" + "@aws-sdk/credential-provider-sso" "3.235.0" + "@aws-sdk/credential-provider-web-identity" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-process@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.226.0.tgz#bcd73a6d31d1b3181917d56e54aacbee242b077f" + integrity sha512-iUDMdnrTvbvaCFhWwqyXrhvQ9+ojPqPqXhwZtY1X/Qaz+73S9gXBPJHZaZb2Ke0yKE1Ql3bJbKvmmxC/qLQMng== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-sso@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.231.0.tgz#8a1f8a95d0aaa134da2bf506d5a3db2435bfbf60" + integrity sha512-aImUD+PAqZ7A2C1ef7gskMN3KuxFT4Am1Vrl6M0oLGyrhKG2QtRT/UaXJE+Yt6d/C2qc2OsQ9j2oim7D6Qha/A== + dependencies: + "@aws-sdk/client-sso" "3.231.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/token-providers" "3.231.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-sso@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.235.0.tgz#46e417d11f51980cf3bbbe158d5b6b9a7212c947" + integrity sha512-+7UORB7Wo/d0mEz7J16/hsRumIhtdl4KekJfrXH5OrLiXXIsn68wmQkrvwD2CibbtgOY0P69G12qbcBHkg3qng== + dependencies: + "@aws-sdk/client-sso" "3.235.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/token-providers" "3.235.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-web-identity@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.226.0.tgz#2b7d20f93a40e2243c7e3857f54b103d19a946fb" + integrity sha512-CCpv847rLB0SFOHz2igvUMFAzeT2fD3YnY4C8jltuJoEkn0ITn1Hlgt13nTJ5BUuvyti2mvyXZHmNzhMIMrIlw== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-codec@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-codec/-/eventstream-codec-3.226.0.tgz#1f54fb2c0bb321d2636d068ee1e969a8c07586ab" + integrity sha512-6uPtR8vSwz3fqoZk9hrb6qBYdp3PJ22+JxV5Wimdesvow4kJXSgDQXIxEkxbv6SxB9tNRB4uJHD84RetHEi15Q== + dependencies: + "@aws-crypto/crc32" "2.0.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-hex-encoding" "3.201.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-browser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.226.0.tgz#a1b880952b5d5b367fcff57be7c00682d01cef00" + integrity sha512-otYC5aZE9eJUqAlKpy8w0rPDQ1eKGvZPtgxWXmFYSO2lDVGfI1nBBNmdZ4MdHqNuQ7ucsKMQYF8BFJ65K2tYPA== + dependencies: + "@aws-sdk/eventstream-serde-universal" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-config-resolver@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.226.0.tgz#07aa985fd8c7d417442bb48786a63bad63bb1a5a" + integrity sha512-A56Gypg+lyEfA5cna+EUH9XTrj0SvRG1gwNW7lrUzviN36SeA/LFTUIOEjxVML3Lowy+EPAcrSZ67h6aepoAig== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-node@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.226.0.tgz#2f95e686ea51e452c1a3af4fa48242573e5ea3ad" + integrity sha512-KWLnKkKDzI9RNkiK6OiSYpG/XjZfue6Bsp/vRG+H5z3fbXdHv4X2+iW+Efu2Kvn7jsUyUv82TCl57DyJ/HKYhQ== + dependencies: + "@aws-sdk/eventstream-serde-universal" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-universal@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.226.0.tgz#f071c41b9706f129efad42c083a9ab5e2f2fc583" + integrity sha512-Q8viYM1Sv90/yIUqyWNeG1GEvyVlAI3GIrInQcCMC+xT59jS+IKGy2y7ojCvSWXnhf5/HMXKcmG092QsqeKy0Q== + dependencies: + "@aws-sdk/eventstream-codec" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/fetch-http-handler@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.226.0.tgz#350f78fc18fe9cb0a889ef4870838a8fcfa8855c" + integrity sha512-JewZPMNEBXfi1xVnRa7pVtK/zgZD8/lQ/YnD8pq79WuMa2cwyhDtr8oqCoqsPW+WJT5ScXoMtuHxN78l8eKWgg== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/querystring-builder" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/hash-blob-browser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.226.0.tgz#00143ed30f9bd8b671327a94609db2403036654d" + integrity sha512-5DCvWE6L4xGoViEHyjcPFuUe1G2EtNx8TqswWaoaKgyasP/yuRm4H99Ra7rqIrjCcSTAGD9NVsUQvVVw1bGt9w== + dependencies: + "@aws-sdk/chunked-blob-reader" "3.188.0" + "@aws-sdk/chunked-blob-reader-native" "3.208.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/hash-node@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.226.0.tgz#252d98bcbb1e13c8f26d9d416db03cf8cceac185" + integrity sha512-MdlJhJ9/Espwd0+gUXdZRsHuostB2WxEVAszWxobP0FTT9PnicqnfK7ExmW+DUAc0ywxtEbR3e0UND65rlSTVw== + dependencies: + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-buffer-from" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/hash-stream-node@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-stream-node/-/hash-stream-node-3.226.0.tgz#cdf4c231fa1dd6006532afc370626702c80e4c91" + integrity sha512-cgNTGlF8SdHaQXtjEmuLXz2U8SLM2JDKtIVPku/lHTMsUsEn+fuv2C+h1f/hvd4aNw5t1zggym7sO1/h/rv56Q== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/invalid-dependency@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/invalid-dependency/-/invalid-dependency-3.226.0.tgz#74586f60859ed1813985e3d642066cc46d2e9d40" + integrity sha512-QXOYFmap8g9QzRjumcRCIo2GEZkdCwd7ePQW0OABWPhKHzlJ74vvBxywjU3s39EEBEluWXtZ7Iufg6GxZM4ifw== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/is-array-buffer@3.201.0": + version "3.201.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/is-array-buffer/-/is-array-buffer-3.201.0.tgz#06e557adc284fac2f26071c2944ae01f61b95854" + integrity sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/md5-js@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/md5-js/-/md5-js-3.226.0.tgz#1400f9af49233e2cae7f90c3c93013b4ce3e39f6" + integrity sha512-ENigJRNudqyh6xsch166SZ4gggHd3XzZJ8gkCU4CWPne04HcR3BkWSO774IuWooCHt8zkaEHKecPurRz6qR+Vw== + dependencies: + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + "@aws-sdk/util-utf8-node" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-bucket-endpoint@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.226.0.tgz#830ec9fa591667b9e848b69504f79f86717e97e7" + integrity sha512-A1Vq5W2X7jgTfjqcKPmjoHohF0poP+9fxwL97fQMvzcwmjhtoCV3bLEpo6CGYx0pKPiSlRJXZkRwRPj2hDHDmA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-arn-parser" "3.208.0" + "@aws-sdk/util-config-provider" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-content-length@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.226.0.tgz#6cc952049f6e3cdc3a3778c9dce9f2aee942b5fe" + integrity sha512-ksUzlHJN2JMuyavjA46a4sctvnrnITqt2tbGGWWrAuXY1mel2j+VbgnmJUiwHKUO6bTFBBeft5Vd1TSOb4JmiA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-endpoint@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint/-/middleware-endpoint-3.226.0.tgz#d776480be4b5a9534c2805b7425be05497f840b7" + integrity sha512-EvLFafjtUxTT0AC9p3aBQu1/fjhWdIeK58jIXaNFONfZ3F8QbEYUPuF/SqZvJM6cWfOO9qwYKkRDbCSTYhprIg== + dependencies: + "@aws-sdk/middleware-serde" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/url-parser" "3.226.0" + "@aws-sdk/util-config-provider" "3.208.0" + "@aws-sdk/util-middleware" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-expect-continue@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.226.0.tgz#46a5b720ff896ce706ee4c01b0edcdb37bd8eaf4" + integrity sha512-YxvQKTV/eA9P8AgW0hXOgj5Qa+TSnNFfyOkfeP089aP3f6p92b1cESf33TEOKsddive2mHT5LRCN6MuPcgWWrA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-flexible-checksums@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.226.0.tgz#01e2d983ffe855fb8c2b78d43cf81de26db32996" + integrity sha512-8A9Ot9A7794UP5tMGl2MnfTW/UM/jYy1wRWF9YkR/hPIcPb7OmE0hmlwIQGzb/7grxpYw66ETKf0WeH/41YfeQ== + dependencies: + "@aws-crypto/crc32" "2.0.0" + "@aws-crypto/crc32c" "2.0.0" + "@aws-sdk/is-array-buffer" "3.201.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-host-header@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.226.0.tgz#1e1ecb034929e0dbc532ae501fd93781438f9a24" + integrity sha512-haVkWVh6BUPwKgWwkL6sDvTkcZWvJjv8AgC8jiQuSl8GLZdzHTB8Qhi3IsfFta9HAuoLjxheWBE5Z/L0UrfhLA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-location-constraint@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.226.0.tgz#c6c24047953294015342d704d787be8e05df49d3" + integrity sha512-qHiYaBYPc2R37KxG2uqsUUwh4usrQMHfGkrpTUnx5d4rGzM3mC+muPsTpSHnAL63K2/yJOHQJFjss3GGwV4SSA== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-logger@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.226.0.tgz#37fd0e62f555befd526b03748c3aab60dcefecf3" + integrity sha512-m9gtLrrYnpN6yckcQ09rV7ExWOLMuq8mMPF/K3DbL/YL0TuILu9i2T1W+JuxSX+K9FMG2HrLAKivE/kMLr55xA== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-recursion-detection@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.226.0.tgz#e149b9138e94d2fa70e7752ba6b1ccb537009706" + integrity sha512-mwRbdKEUeuNH5TEkyZ5FWxp6bL2UC1WbY+LDv6YjHxmSMKpAoOueEdtU34PqDOLrpXXxIGHDFmjeGeMfktyEcA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-retry@3.229.0": + version "3.229.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.229.0.tgz#9df32a00e4183afa69d33745fd9f42b994e655d1" + integrity sha512-/y0BWio9b2RRH2QvRTohbuqE0vhH4IZKlc6k+JRbGV9aSwyOzACU/L/qkGftC/W0puvgNvZYjGxmB6cGHAEZaw== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/service-error-classification" "3.229.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-middleware" "3.226.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/middleware-retry@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.235.0.tgz#c0d938db85a771812204ed5e981eaf5eef6b580b" + integrity sha512-50WHbJGpD3SNp9763MAlHqIhXil++JdQbKejNpHg7HsJne/ao3ub+fDOfx//mMBjpzBV25BGd5UlfL6blrClSg== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/service-error-classification" "3.229.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-middleware" "3.226.0" + "@aws-sdk/util-retry" "3.229.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/middleware-sdk-s3@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.231.0.tgz#98a6f0ce1611b0dbdc96e6aaaa8979fa5d9b0644" + integrity sha512-UGaSvevd2TanfKgStF46dDSHkh4bxOr1gdUkyHm9i+1pF5lx4KdbnBZv/5SKnn7XifhHRXrs1M3lTzemXREhTA== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-arn-parser" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-sdk-sts@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.226.0.tgz#e8a8cf42bba8963259546120cde1e408628863f9" + integrity sha512-NN9T/qoSD1kZvAT+VLny3NnlqgylYQcsgV3rvi/8lYzw/G/2s8VS6sm/VTWGGZhx08wZRv20MWzYu3bftcyqUg== + dependencies: + "@aws-sdk/middleware-signing" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-serde@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-3.226.0.tgz#c837ef33b34bec2af19a1c177a0c02a1ae20da5e" + integrity sha512-nPuOOAkSfx9TxzdKFx0X2bDlinOxGrqD7iof926K/AEflxGD1DBdcaDdjlYlPDW2CVE8LV/rAgbYuLxh/E/1VA== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-signing@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.226.0.tgz#ebb1d142ac2767466f2e464bb7dba9837143b4d1" + integrity sha512-E6HmtPcl+IjYDDzi1xI2HpCbBq2avNWcjvCriMZWuTAtRVpnA6XDDGW5GY85IfS3A8G8vuWqEVPr8JcYUcjfew== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-middleware" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-ssec@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.226.0.tgz#4f160aa6822e2af1b1b8a755ff0710aefb66abfb" + integrity sha512-DR97oWoLHiMdaUP/wu99HtzG7/ijvCrjZGDH37WBO1rxFtEti6L7T09wgHzwxMN8gtL8FJA7dU8IrffGSC9VmA== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-stack@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-stack/-/middleware-stack-3.226.0.tgz#b0408370270188103987c457c758f9cf7651754f" + integrity sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/middleware-user-agent@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.226.0.tgz#26653189f3e8da86514f77688a80d0ad445c0799" + integrity sha512-N1WnfzCW1Y5yWhVAphf8OPGTe8Df3vmV7/LdsoQfmpkCZgLZeK2o0xITkUQhRj1mbw7yp8tVFLFV3R2lMurdAQ== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/node-config-provider@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-config-provider/-/node-config-provider-3.226.0.tgz#a9e21512ef824142bb928a0b2f85b39a75b8964d" + integrity sha512-B8lQDqiRk7X5izFEUMXmi8CZLOKCTWQJU9HQf3ako+sF0gexo4nHN3jhoRWyLtcgC5S3on/2jxpAcqtm7kuY3w== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/node-http-handler@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.226.0.tgz#373886e949d214a99a3521bd6c141fa17b0e89fe" + integrity sha512-xQCddnZNMiPmjr3W7HYM+f5ir4VfxgJh37eqZwX6EZmyItFpNNeVzKUgA920ka1VPz/ZUYB+2OFGiX3LCLkkaA== + dependencies: + "@aws-sdk/abort-controller" "3.226.0" + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/querystring-builder" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/property-provider@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/property-provider/-/property-provider-3.226.0.tgz#ef0ff37c319dc37a52f08fa7544f861308a3bbd8" + integrity sha512-TsljjG+Sg0LmdgfiAlWohluWKnxB/k8xenjeozZfzOr5bHmNHtdbWv6BtNvD/R83hw7SFXxbJHlD5H4u9p2NFg== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/protocol-http@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.226.0.tgz#0af7bdc331508e556b722aad0cb78eefa93466e3" + integrity sha512-zWkVqiTA9RXL6y0hhfZc9bcU4DX2NI6Hw9IhQmSPeM59mdbPjJlY4bLlMr5YxywqO3yQ/ylNoAfrEzrDjlOSRg== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-builder@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-builder/-/querystring-builder-3.226.0.tgz#11cd751abeac66f1f9349225454bac3e39808926" + integrity sha512-LVurypuNeotO4lmirKXRC4NYrZRAyMJXuwO0f2a5ZAUJCjauwYrifKue6yCfU7bls7gut7nfcR6B99WBYpHs3g== + dependencies: + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-uri-escape" "3.201.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-parser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-parser/-/querystring-parser-3.226.0.tgz#ba6a26727c98d46c95180e6cdc463039c5e4740d" + integrity sha512-FzB+VrQ47KAFxiPt2YXrKZ8AOLZQqGTLCKHzx4bjxGmwgsjV8yIbtJiJhZLMcUQV4LtGeIY9ixIqQhGvnZHE4A== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/s3-presigned-post@^3.234.0": + version "3.234.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.234.0.tgz#5752e7df844960b574792351350252a7cafc6fdb" + integrity sha512-HjlZ9IJrG4Xw1/CD2cWe/NF6xB9IhNdBc94RbK6P/XC3ghT1SaCYdX3Q7Uw2RSarMDzNCSmdVlIysbzE/kHhkQ== + dependencies: + "@aws-sdk/middleware-endpoint" "3.226.0" + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-format-url" "3.226.0" + "@aws-sdk/util-hex-encoding" "3.201.0" + tslib "^2.3.1" + +"@aws-sdk/service-error-classification@3.229.0": + version "3.229.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.229.0.tgz#768f1eb92775ca2cc99c6451a2303a0008a28fc1" + integrity sha512-dnzWWQ0/NoWMUZ5C0DW3dPm0wC1O76Y/SpKbuJzWPkx1EYy6r8p32Ly4D9vUzrKDbRGf48YHIF2kOkBmu21CLg== + +"@aws-sdk/shared-ini-file-loader@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.226.0.tgz#d0ade86834b1803ce4b9dcab459e57e0376fd6cf" + integrity sha512-661VQefsARxVyyV2FX9V61V+nNgImk7aN2hYlFKla6BCwZfMng+dEtD0xVGyg1PfRw0qvEv5LQyxMVgHcUSevA== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/signature-v4-multi-region@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.226.0.tgz#7f97056e66dde3ca07c72176994c12daaa517f94" + integrity sha512-QHxNuf9ynK208v7Y3imdsa3Cz8ynYV7ZOf3sBJdItuEtHN6uy/KxaOrtvpF8I5Hyn48Hc8z5miTSMujFKT7GEw== + dependencies: + "@aws-sdk/protocol-http" "3.226.0" + "@aws-sdk/signature-v4" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-arn-parser" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/signature-v4@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.226.0.tgz#100390b5c5b55a9b0abd05b06fceb36cfa0ecf98" + integrity sha512-/R5q5agdPd7HJB68XMzpxrNPk158EHUvkFkuRu5Qf3kkkHebEzWEBlWoVpUe6ss4rP9Tqcue6xPuaftEmhjpYw== + dependencies: + "@aws-sdk/is-array-buffer" "3.201.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-hex-encoding" "3.201.0" + "@aws-sdk/util-middleware" "3.226.0" + "@aws-sdk/util-uri-escape" "3.201.0" + tslib "^2.3.1" + +"@aws-sdk/smithy-client@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.226.0.tgz#d6869ca3627ca33024616c0ec3f707981e080d59" + integrity sha512-BWr1FhWSUhkSBp0TLzliD5AQBjA2Jmo9FlOOt+cBwd9BKkSGlGj+HgATYJ83Sjjg2+J6qvEZBxB78LKVHhorBw== + dependencies: + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/smithy-client@3.234.0": + version "3.234.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.234.0.tgz#8f0021e021f0e52730ed0a8f271f839eb63bc374" + integrity sha512-8AtR/k4vsFvjXeQbIzq/Wy7Nbk48Ou0wUEeVYPHWHPSU8QamFWORkOwmKtKMfHAyZvmqiAPeQqHFkq+UJhWyyQ== + dependencies: + "@aws-sdk/middleware-stack" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/token-providers@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.231.0.tgz#1eb789df3689942af9327baa056f3adf91389d44" + integrity sha512-sxx6X/moSdukyrnoBtLxmgQQLWqixMc/qAM5yNg5lfNoGamWslH6CnT1HlxTFv71q8/1xwnvZ4LC2kbD6vDc6Q== + dependencies: + "@aws-sdk/client-sso-oidc" "3.231.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/token-providers@3.235.0": + version "3.235.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.235.0.tgz#6ddc24753c80143fadc484c32b6ebdbdd62d3a73" + integrity sha512-TdUbQ0wWVTO7azF/8ojtd4MNFjEfQKhGoGib0g/W5pa/FJryOkiIP8U4POC/I+0ATMkLK3vAC07kNHtey0ooZg== + dependencies: + "@aws-sdk/client-sso-oidc" "3.235.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/shared-ini-file-loader" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/types@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.226.0.tgz#3dba2ba223fbb8ac1ebc84de0e036ce69a81d469" + integrity sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/types@^3.1.0", "@aws-sdk/types@^3.110.0": + version "3.215.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.215.0.tgz#72a595e2c1a5c8c3f0291bccf71d481412b1843b" + integrity sha512-eRbCVjwzTYd9C5e2mceScJ6D2kYDDEC3PLkYfJa+1wH9iiF2JlbiYozAokyeYBHQ+AjmD93MK58RBoM8iZfH0Q== + +"@aws-sdk/url-parser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/url-parser/-/url-parser-3.226.0.tgz#f53d1f868b27fe74aca091a799f2af56237b15a2" + integrity sha512-p5RLE0QWyP0OcTOLmFcLdVgUcUEzmEfmdrnOxyNzomcYb0p3vUagA5zfa1HVK2azsQJFBv28GfvMnba9bGhObg== + dependencies: + "@aws-sdk/querystring-parser" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-arn-parser@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.208.0.tgz#56b6ae4699c3140bb27dcede5146876fef04e823" + integrity sha512-QV4af+kscova9dv4VuHOgH8wEr/IIYHDGcnyVtkUEqahCejWr1Kuk+SBK0xMwnZY5LSycOtQ8aeqHOn9qOjZtA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-base64@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64/-/util-base64-3.208.0.tgz#36b430e5396251f761590f7c2f0c5c12193f353c" + integrity sha512-PQniZph5A6N7uuEOQi+1hnMz/FSOK/8kMFyFO+4DgA1dZ5pcKcn5wiFwHkcTb/BsgVqQa3Jx0VHNnvhlS8JyTg== + dependencies: + "@aws-sdk/util-buffer-from" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/util-body-length-browser@3.188.0": + version "3.188.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.188.0.tgz#e1d949318c10a621b38575a9ef01e39f9857ddb0" + integrity sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-body-length-node@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-node/-/util-body-length-node-3.208.0.tgz#baabd1fa1206ff2bd4ce3785122d86eb3258dd20" + integrity sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-buffer-from@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-buffer-from/-/util-buffer-from-3.208.0.tgz#285e86f6dc9030148a4147d65239e75cb254a1b0" + integrity sha512-7L0XUixNEFcLUGPeBF35enCvB9Xl+K6SQsmbrPk1P3mlV9mguWSDQqbOBwY1Ir0OVbD6H/ZOQU7hI/9RtRI0Zw== + dependencies: + "@aws-sdk/is-array-buffer" "3.201.0" + tslib "^2.3.1" + +"@aws-sdk/util-config-provider@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-config-provider/-/util-config-provider-3.208.0.tgz#c485fd83fbac051337e5f6be60ea3f9fa61c0139" + integrity sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-browser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.226.0.tgz#f6f3092463533f33d95d0bdb17fc5c511ad2b072" + integrity sha512-chLx+6AeMSjuPsCVbI1B4Pg3jftjjcsuTsJucjo0DKBb1VSWqPCitmOILQVvKiA2Km8TSs3VcbUuOCyDExkzAg== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-browser@3.234.0": + version "3.234.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.234.0.tgz#1151f0beabdb46c1aaca42a1ad0714b8e686acaa" + integrity sha512-IHMKXjTbOD8XMz5+2oCOsVP94BYb9YyjXdns0aAXr2NAo7k2+RCzXQ2DebJXppGda1F6opFutoKwyVSN0cmbMw== + dependencies: + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-node@3.231.0": + version "3.231.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.231.0.tgz#6cdf6b633543950667c94657f6df0f8ca86182e2" + integrity sha512-jH+9z96x8Oxv+bqBdD7x8CRvbKzM9id+VHzI9+h1oTY9J+6MkUubPshliBTQeus5pD03NBOS/2F3GX2rJ9Avuw== + dependencies: + "@aws-sdk/config-resolver" "3.231.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-node@3.234.0": + version "3.234.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.234.0.tgz#0607f1dc7a4dc896dfcaf377522535ca9ffba7a9" + integrity sha512-UGjQ+OjBYYhxFVtUY+jtr0ZZgzZh6OHtYwRhFt8IHewJXFCfZTyfsbX20szBj5y1S4HRIUJ7cwBLIytTqMbI5w== + dependencies: + "@aws-sdk/config-resolver" "3.234.0" + "@aws-sdk/credential-provider-imds" "3.226.0" + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/property-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-endpoints@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.226.0.tgz#3728b2e30f6f757ae862a0b7cf3991e75f252c3f" + integrity sha512-iqOkac/zLmyPBUJd7SLN0PeZMkOmlGgD5PHmmekTClOkce2eUjK9SNX1PzL73aXPoPTyhg9QGLH8uEZEQ8YUzg== + dependencies: + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-format-url@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.226.0.tgz#cd10c25802c95017b688dd0dea2c3205a91638b5" + integrity sha512-IhuA8iuJL1urWqZZmVzW79mHnwFEEP6rMsSKgAfMmuzowhtgk9p/sT2OVPkXk0LeZL2Ghsnw1W2JfPIs8x1qVg== + dependencies: + "@aws-sdk/querystring-builder" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-hex-encoding@3.201.0": + version "3.201.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.201.0.tgz#21d7ec319240ee68c33d938e71cb79830bea315d" + integrity sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.208.0.tgz#0f598fc238a1256e4bcb64d01459f03a922dd4c3" + integrity sha512-iua1A2+P7JJEDHVgvXrRJSvsnzG7stYSGQnBVphIUlemwl6nN5D+QrgbjECtrbxRz8asYFHSzhdhECqN+tFiBg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-middleware@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-middleware/-/util-middleware-3.226.0.tgz#7069ae96e2e00f6bb82c722e073922fb2b051ca2" + integrity sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-retry@3.229.0": + version "3.229.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-retry/-/util-retry-3.229.0.tgz#17aad47b067e81acf644d5c2c0f2325f2d8faf4f" + integrity sha512-0zKTqi0P1inD0LzIMuXRIYYQ/8c1lWMg/cfiqUcIAF1TpatlpZuN7umU0ierpBFud7S+zDgg0oemh+Nj8xliJw== + dependencies: + "@aws-sdk/service-error-classification" "3.229.0" + tslib "^2.3.1" + +"@aws-sdk/util-stream-browser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-stream-browser/-/util-stream-browser-3.226.0.tgz#a14ea5c118d448f01ed6103203404f466960e5fe" + integrity sha512-ZvjlA1ySaLd0DqUWTKmL7LsxfPhroAONpzsinaHmw9aZVL40s2cADU9eWgBdHTuAOeFklL7NP0cc6UiTFHKe8g== + dependencies: + "@aws-sdk/fetch-http-handler" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-base64" "3.208.0" + "@aws-sdk/util-hex-encoding" "3.201.0" + "@aws-sdk/util-utf8-browser" "3.188.0" + tslib "^2.3.1" + +"@aws-sdk/util-stream-node@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-stream-node/-/util-stream-node-3.226.0.tgz#7c568a947f47fea1f3b268b895b6e73235e29703" + integrity sha512-HADXiIgDGoXcCLSKuPnjCLENf0iC0lzqqnymZu9H2FoACZhJB7DvJ9LnP51Pvw9lfCu+yvLzbMqSPdbXtMbRWg== + dependencies: + "@aws-sdk/node-http-handler" "3.226.0" + "@aws-sdk/types" "3.226.0" + "@aws-sdk/util-buffer-from" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/util-uri-escape@3.201.0": + version "3.201.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-uri-escape/-/util-uri-escape-3.201.0.tgz#5e708d4cde001a4558ee616f889ceacfadd2ab03" + integrity sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-browser@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.226.0.tgz#164bb2da8d6353133784e47f0a0ae463bc9ebb73" + integrity sha512-PhBIu2h6sPJPcv2I7ELfFizdl5pNiL4LfxrasMCYXQkJvVnoXztHA1x+CQbXIdtZOIlpjC+6BjDcE0uhnpvfcA== + dependencies: + "@aws-sdk/types" "3.226.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-node@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.226.0.tgz#7569460b9efc6bbd5295275c51357e480ff469c2" + integrity sha512-othPc5Dz/pkYkxH+nZPhc1Al0HndQT8zHD4e9h+EZ+8lkd8n+IsnLfTS/mSJWrfiC6UlNRVw55cItstmJyMe/A== + dependencies: + "@aws-sdk/node-config-provider" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/util-utf8-browser@3.188.0", "@aws-sdk/util-utf8-browser@^3.0.0": + version "3.188.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.188.0.tgz#484762bd600401350e148277731d6744a4a92225" + integrity sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-utf8-node@3.208.0": + version "3.208.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-node/-/util-utf8-node-3.208.0.tgz#eba17de0f92f87b98481c2e2d0ceaa05c7994d67" + integrity sha512-jKY87Acv0yWBdFxx6bveagy5FYjz+dtV8IPT7ay1E2WPWH1czoIdMAkc8tSInK31T6CRnHWkLZ1qYwCbgRfERQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.208.0" + tslib "^2.3.1" + +"@aws-sdk/util-waiter@3.226.0": + version "3.226.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-waiter/-/util-waiter-3.226.0.tgz#6715afd59748cbc610ddfbc5e21124b20a7e85ac" + integrity sha512-qYQMRxnu5k8qQihJXoIWMkBOj0+XkHHj/drLdbRnwL6ni6NcG8++cs9M3DSjIcxmxgF/7SLpDjn1H3sC7cYo4g== + dependencies: + "@aws-sdk/abort-controller" "3.226.0" + "@aws-sdk/types" "3.226.0" + tslib "^2.3.1" + +"@aws-sdk/xml-builder@3.201.0": + version "3.201.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.201.0.tgz#acf0869855460528114bec17f290b224fe19a3e2" + integrity sha512-brRdB1wwMgjWEnOQsv7zSUhIQuh7DEicrfslAqHop4S4FtSI3GQAShpQqgOpMTNFYcpaWKmE/Y1MJmNY7xLCnw== + dependencies: + tslib "^2.3.1" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": + 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/code-frame@^7.12.13": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.8", "@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/compat-data@^7.20.0": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.1.tgz#f2e6ef7790d8c8dbf03d379502dcc246dcce0b30" + integrity sha512-EWZ4mE2diW3QALKvDMiXnbZpRvlj+nayZ112nK93SnhqOtpdsbVD4W+2tEoT3YNBAG9RBR0ISY758ZkOgsn6pQ== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@7.17.5": + version "7.17.5" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.5.tgz#6cd2e836058c28f06a4ca8ee7ed955bbf37c8225" + integrity sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.17.2" + "@babel/parser" "^7.17.3" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.3" + "@babel/types" "^7.17.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + +"@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.15.0", "@babel/core@^7.15.5", "@babel/core@^7.7.5": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.12.tgz#5edc53c1b71e54881315923ae2aedea2522bb784" + integrity sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-module-transforms" "^7.16.7" + "@babel/helpers" "^7.16.7" + "@babel/parser" "^7.16.12" + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.16.10" + "@babel/types" "^7.16.8" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/core@^7.11.6": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.2.tgz#8dc9b1620a673f92d3624bd926dc49a52cf25b92" + integrity sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.2" + "@babel/helper-compilation-targets" "^7.20.0" + "@babel/helper-module-transforms" "^7.20.2" + "@babel/helpers" "^7.20.1" + "@babel/parser" "^7.20.2" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + 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/core@^7.12.10": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" + integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.9" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + 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.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.16.8", "@babel/generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" + integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== + dependencies: + "@babel/types" "^7.18.9" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/generator@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.3.tgz#a2c30b0c4f89858cb87050c3ffdfd36bdf443200" + integrity sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg== + dependencies: + "@babel/types" "^7.17.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.20.1", "@babel/generator@^7.20.2": + version "7.20.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.4.tgz#4d9f8f0c30be75fd90a0562099a26e5839602ab8" + integrity sha512-luCf7yk/cm7yab6CAW1aiFnmEfBJplb/JojV56MYEK7ziWfGmFlTfmL9Ehwfy4gFhbjBfWO1wj7/TuSbVNEEtA== + dependencies: + "@babel/types" "^7.20.2" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/generator@^7.7.2": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" + integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== + dependencies: + "@babel/types" "^7.16.8" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz#acd4edfd7a566d1d51ea975dff38fd52906981bb" + integrity sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@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-compilation-targets@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz#6bf5374d424e1b3922822f1d9bdaa43b1a139d0a" + integrity sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ== + dependencies: + "@babel/compat-data" "^7.20.0" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.21.3" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" + integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" + integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" + +"@babel/helper-define-polyfill-provider@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz#3c2f91b7971b9fc11fe779c945c014065dea340e" + integrity sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.6", "@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-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.16.7", "@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-function-name@^7.19.0": + version "7.19.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c" + integrity sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w== + dependencies: + "@babel/template" "^7.18.10" + "@babel/types" "^7.19.0" + +"@babel/helper-hoist-variables@^7.16.7", "@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-member-expression-to-functions@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz#1531661e8375af843ad37ac692c132841e2fd815" + integrity sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-module-imports@^7.10.4": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" + integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== + dependencies: + "@babel/types" "^7.16.7" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7", "@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.12.1", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^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-module-transforms@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.2.tgz#ac53da669501edd37e658602a21ba14c08748712" + integrity sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.20.2" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.2" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@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.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + 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-remap-async-to-generator@^7.18.6": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz#1092e002feca980fbbb0bd4d51b74a65c6a500e6" + integrity sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-member-expression-to-functions" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@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-simple-access@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" + integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== + dependencies: + "@babel/types" "^7.20.2" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz#778d87b3a758d90b471e7b9918f34a9a02eb5818" + integrity sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw== + dependencies: + "@babel/types" "^7.18.9" + +"@babel/helper-split-export-declaration@^7.16.7", "@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.19.4": + version "7.19.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" + integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== + +"@babel/helper-validator-identifier@^7.16.7", "@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-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/helper-validator-option@^7.16.7", "@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/helper-wrap-function@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz#ae1feddc6ebbaa2fd79346b77821c3bd73a39646" + integrity sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ== + dependencies: + "@babel/helper-function-name" "^7.18.9" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.16.7", "@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/helpers@^7.17.2": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.17.2.tgz#23f0a0746c8e287773ccd27c14be428891f63417" + integrity sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ== + dependencies: + "@babel/template" "^7.16.7" + "@babel/traverse" "^7.17.0" + "@babel/types" "^7.17.0" + +"@babel/helpers@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.1.tgz#2ab7a0fcb0a03b5bf76629196ed63c2d7311f4c9" + integrity sha512-J77mUVaDTUJFZ5BpP6mMn6OIl3rEWymk2ZxDBQJUG3P+PbmyMcF3bYWvz0ma69Af1oobDqT/iAsvzhB58xhQUg== + dependencies: + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.1" + "@babel/types" "^7.20.0" + +"@babel/highlight@^7.16.7", "@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": + version "7.16.12" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.12.tgz#9474794f9a650cf5e2f892444227f98e28cdf8b6" + integrity sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A== + +"@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.16.10", "@babel/parser@^7.16.12", "@babel/parser@^7.16.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" + integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== + +"@babel/parser@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.3.tgz#b07702b982990bf6fdc1da5049a23fece4c5c3d0" + integrity sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA== + +"@babel/parser@^7.18.10", "@babel/parser@^7.20.1", "@babel/parser@^7.20.2": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.3.tgz#5358cf62e380cf69efcb87a7bb922ff88bfac6e2" + integrity sha512-OP/s5a94frIPXwjzEcv5S/tpQfc6XhxYUnmWpgdqMWGgYCuErA3SzozaRAMQgSZWKeTJxht9aWAkUY+0UzvOFg== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz#a11af19aa373d68d561f08e0a57242350ed0ec50" + integrity sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + +"@babel/plugin-proposal-async-generator-functions@^7.16.8", "@babel/plugin-proposal-async-generator-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" + integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.16.7", "@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-properties@^7.14.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" + integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-proposal-class-static-block@^7.16.7", "@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-decorators@^7.12.12": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.9.tgz#d09d41ffc74af8499d2ac706ed0dbd5474711665" + integrity sha512-KD7zDNaD14CRpjQjVbV4EnH9lsKYlcpUrhZH37ei2IY+AlXrfAPy5pTmRUE4X6X1k8EsKXPraykxeaogqQvSGA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/plugin-syntax-decorators" "^7.18.6" + +"@babel/plugin-proposal-decorators@^7.14.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.16.7.tgz#922907d2e3e327f5b07d2246bcfc0bd438f360d2" + integrity sha512-DoEpnuXK14XV9btI1k8tzNGCutMclpj4yru8aXKoHlVmbO1s+2A+g2+h4JhcjrxkFJqzbymnLG6j/niOf3iFXQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-decorators" "^7.16.7" + +"@babel/plugin-proposal-dynamic-import@^7.16.7", "@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-default-from@^7.12.1": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.9.tgz#9dfad26452e53cae8f045c6153e82dc50e9bee89" + integrity sha512-1qtsLNCDm5awHLIt+2qAFDi31XC94r4QepMQcOosC7FpY6O+Bgay5f2IyAQt2wvm1TARumpFprnQt5pTIJ9nUg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-default-from" "^7.18.6" + +"@babel/plugin-proposal-export-namespace-from@^7.16.7", "@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.16.7", "@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.16.7", "@babel/plugin-proposal-logical-assignment-operators@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz#8148cbb350483bf6220af06fa6db3690e14b2e23" + integrity sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.7", "@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.16.7", "@babel/plugin-proposal-object-rest-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz#f9434f6beb2c8cae9dfcf97d2a5941bbbf9ad4e7" + integrity sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q== + dependencies: + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.18.8" + +"@babel/plugin-proposal-optional-catch-binding@^7.16.7", "@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.16.7", "@babel/plugin-proposal-optional-chaining@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz#e8e8fe0723f2563960e4bf5e9690933691915993" + integrity sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.16.11", "@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.12.1", "@babel/plugin-proposal-private-property-in-object@^7.16.7", "@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@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.12.13", "@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-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.16.7", "@babel/plugin-syntax-decorators@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz#2e45af22835d0b0f8665da2bfd4463649ce5dbc1" + integrity sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-default-from@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz#8df076711a4818c4ce4f23e61d622b0ba2ff84bc" + integrity sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.16.7.tgz#202b147e5892b8452bbb0bb269c7ed2539ab8832" + integrity sha512-UDo3YGQO0jH6ytzVwgSLv9i/CzMcUjbKenL67dTrAZPPv6GFAtDhe6jqnvmoKzC/7htNTohhos+onPtDMqJwaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-syntax-import-assertions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" + integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@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.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.18.6": + 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.10.4", "@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.10.4", "@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", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@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-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@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.18.6": + 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/plugin-syntax-typescript@^7.3.3", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.7.tgz#39c9b55ee153151990fb038651d58d3fd03f98f8" + integrity sha512-YhUIJHHGkqPgEcMYkPCKTyGUdoGKWtopIycQyjJH8OjvRgOYsXsaKehLVPScKJWAULPxMa4N1vCe6szREFlZ7A== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.16.7", "@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-async-to-generator@^7.16.8", "@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + +"@babel/plugin-transform-block-scoped-functions@^7.16.7", "@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.16.7", "@babel/plugin-transform-block-scoping@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz#f9b7e018ac3f373c81452d6ada8bd5a18928926d" + integrity sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.16.7", "@babel/plugin-transform-classes@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz#90818efc5b9746879b869d5ce83eb2aa48bbc3da" + integrity sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-replace-supers" "^7.18.9" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.16.7", "@babel/plugin-transform-computed-properties@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz#2357a8224d402dad623caf6259b611e56aec746e" + integrity sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.16.7", "@babel/plugin-transform-destructuring@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz#68906549c021cb231bee1db21d3b5b095f8ee292" + integrity sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.16.7", "@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-exponentiation-operator@^7.16.7", "@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-flow-strip-types@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.16.7.tgz#291fb140c78dabbf87f2427e7c7c332b126964b8" + integrity sha512-mzmCq3cNsDpZZu9FADYYyfZJIOrSONmHcop2XEKPdBNMa4PDC4eEvcOvzZaCNcjKu72v0XQlA5y1g58aLRXdYg== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/plugin-syntax-flow" "^7.16.7" + +"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.16.7", "@babel/plugin-transform-for-of@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz#6ef8a50b244eb6a0bdbad0c7c61877e4e30097c1" + integrity sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-function-name@^7.16.7", "@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-literals@^7.16.7", "@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-member-expression-literals@^7.16.7", "@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.16.7", "@babel/plugin-transform-modules-amd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" + integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.16.8", "@babel/plugin-transform-modules-commonjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.16.7", "@babel/plugin-transform-modules-systemjs@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz#545df284a7ac6a05125e3e405e536c5853099a06" + integrity sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-validator-identifier" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.16.7", "@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8", "@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" + integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-new-target@^7.16.7", "@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.16.7", "@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.16.7", "@babel/plugin-transform-parameters@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz#ee9f1a0ce6d78af58d0956a9378ea3427cccb48a" + integrity sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-property-literals@^7.16.7", "@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-constant-elements@^7.14.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.16.7.tgz#19e9e4c2df2f6c3e6b3aea11778297d81db8df62" + integrity sha512-lF+cfsyTgwWkcw715J88JhMYJ5GpysYNLhLP1PkvkhTRN7B3e74R/1KsDxFxhRpSn0UUD3IWM4GvdBR2PEbbQQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + +"@babel/plugin-transform-react-display-name@^7.16.7", "@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.16.7", "@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" + integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/plugin-transform-react-pure-annotations@^7.16.7", "@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.16.7", "@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + regenerator-transform "^0.15.0" + +"@babel/plugin-transform-reserved-words@^7.16.7", "@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-runtime@^7.15.0": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.16.10.tgz#53d9fd3496daedce1dd99639097fa5d14f4c7c2c" + integrity sha512-9nwTiqETv2G7xI4RvXHNfpGdr8pAA+Q/YtN3yLK7OoK7n9OibVm/xymJ838a9A6E/IciOLPj82lZk0fW6O4O7w== + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + semver "^6.3.0" + +"@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.16.7", "@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.16.7", "@babel/plugin-transform-spread@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz#6ea7a6297740f381c540ac56caf75b05b74fb664" + integrity sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" + +"@babel/plugin-transform-sticky-regex@^7.16.7", "@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.16.7", "@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typeof-symbol@^7.16.7", "@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + +"@babel/plugin-transform-typescript@^7.16.7", "@babel/plugin-transform-typescript@^7.18.6": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" + integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-typescript" "^7.18.6" + +"@babel/plugin-transform-unicode-escapes@^7.16.7", "@babel/plugin-transform-unicode-escapes@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" + integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-unicode-regex@^7.16.7", "@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.12.11": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff" + integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg== + dependencies: + "@babel/compat-data" "^7.18.8" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.9" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.18.9" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.9" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@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-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.18.9" + "@babel/plugin-transform-classes" "^7.18.9" + "@babel/plugin-transform-computed-properties" "^7.18.9" + "@babel/plugin-transform-destructuring" "^7.18.9" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.8" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.18.9" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.8" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.18.9" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.18.9" + babel-plugin-polyfill-corejs2 "^0.3.1" + babel-plugin-polyfill-corejs3 "^0.5.2" + babel-plugin-polyfill-regenerator "^0.3.1" + core-js-compat "^3.22.1" + semver "^6.3.0" + +"@babel/preset-env@^7.15.0", "@babel/preset-env@^7.15.6": + version "7.16.11" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.11.tgz#5dd88fd885fae36f88fd7c8342475c9f0abe2982" + integrity sha512-qcmWG8R7ZW6WBRPZK//y+E3Cli151B20W1Rv7ln27vuPaXU/8TKms6jFdiJtF7UDTxcrb7mZd88tAeK9LjdT8g== + dependencies: + "@babel/compat-data" "^7.16.8" + "@babel/helper-compilation-targets" "^7.16.7" + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-async-generator-functions" "^7.16.8" + "@babel/plugin-proposal-class-properties" "^7.16.7" + "@babel/plugin-proposal-class-static-block" "^7.16.7" + "@babel/plugin-proposal-dynamic-import" "^7.16.7" + "@babel/plugin-proposal-export-namespace-from" "^7.16.7" + "@babel/plugin-proposal-json-strings" "^7.16.7" + "@babel/plugin-proposal-logical-assignment-operators" "^7.16.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.7" + "@babel/plugin-proposal-numeric-separator" "^7.16.7" + "@babel/plugin-proposal-object-rest-spread" "^7.16.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" + "@babel/plugin-proposal-optional-chaining" "^7.16.7" + "@babel/plugin-proposal-private-methods" "^7.16.11" + "@babel/plugin-proposal-private-property-in-object" "^7.16.7" + "@babel/plugin-proposal-unicode-property-regex" "^7.16.7" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@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-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.16.7" + "@babel/plugin-transform-async-to-generator" "^7.16.8" + "@babel/plugin-transform-block-scoped-functions" "^7.16.7" + "@babel/plugin-transform-block-scoping" "^7.16.7" + "@babel/plugin-transform-classes" "^7.16.7" + "@babel/plugin-transform-computed-properties" "^7.16.7" + "@babel/plugin-transform-destructuring" "^7.16.7" + "@babel/plugin-transform-dotall-regex" "^7.16.7" + "@babel/plugin-transform-duplicate-keys" "^7.16.7" + "@babel/plugin-transform-exponentiation-operator" "^7.16.7" + "@babel/plugin-transform-for-of" "^7.16.7" + "@babel/plugin-transform-function-name" "^7.16.7" + "@babel/plugin-transform-literals" "^7.16.7" + "@babel/plugin-transform-member-expression-literals" "^7.16.7" + "@babel/plugin-transform-modules-amd" "^7.16.7" + "@babel/plugin-transform-modules-commonjs" "^7.16.8" + "@babel/plugin-transform-modules-systemjs" "^7.16.7" + "@babel/plugin-transform-modules-umd" "^7.16.7" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8" + "@babel/plugin-transform-new-target" "^7.16.7" + "@babel/plugin-transform-object-super" "^7.16.7" + "@babel/plugin-transform-parameters" "^7.16.7" + "@babel/plugin-transform-property-literals" "^7.16.7" + "@babel/plugin-transform-regenerator" "^7.16.7" + "@babel/plugin-transform-reserved-words" "^7.16.7" + "@babel/plugin-transform-shorthand-properties" "^7.16.7" + "@babel/plugin-transform-spread" "^7.16.7" + "@babel/plugin-transform-sticky-regex" "^7.16.7" + "@babel/plugin-transform-template-literals" "^7.16.7" + "@babel/plugin-transform-typeof-symbol" "^7.16.7" + "@babel/plugin-transform-unicode-escapes" "^7.16.7" + "@babel/plugin-transform-unicode-regex" "^7.16.7" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.16.8" + babel-plugin-polyfill-corejs2 "^0.3.0" + babel-plugin-polyfill-corejs3 "^0.5.0" + babel-plugin-polyfill-regenerator "^0.3.0" + core-js-compat "^3.20.2" + semver "^6.3.0" + +"@babel/preset-flow@^7.12.1": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.16.7.tgz#7fd831323ab25eeba6e4b77a589f680e30581cbd" + integrity sha512-6ceP7IyZdUYQ3wUVqyRSQXztd1YmFHWI4Xv11MIqAlE4WqxBSd/FZ61V9k+TS5Gd4mkHOtQtPp9ymRpxH4y1Ug== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-flow-strip-types" "^7.16.7" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.12.10": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-react@^7.14.5": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.16.7.tgz#4c18150491edc69c183ff818f9f2aecbe5d93852" + integrity sha512-fWpyI8UM/HE6DfPBzD8LnhQ/OcH8AgTaqcqP2nGOXEUV+VKBR5JRN9hCk9ai+zQQ57vtm9oWeXguBCPNUjytgA== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-react-display-name" "^7.16.7" + "@babel/plugin-transform-react-jsx" "^7.16.7" + "@babel/plugin-transform-react-jsx-development" "^7.16.7" + "@babel/plugin-transform-react-pure-annotations" "^7.16.7" + +"@babel/preset-typescript@7.16.7", "@babel/preset-typescript@^7.15.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.7.tgz#ab114d68bb2020afc069cd51b37ff98a046a70b9" + integrity sha512-WbVEmgXdIyvzB77AQjGBEyYPZx+8tTsO50XtfozQrkW8QB2rLJpH2lgx0TRw5EJrBxOZQ+wCcyPVQvS8tjEHpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.16.7" + "@babel/helper-validator-option" "^7.16.7" + "@babel/plugin-transform-typescript" "^7.16.7" + +"@babel/preset-typescript@^7.12.7": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + +"@babel/register@^7.12.1": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.9.tgz#1888b24bc28d5cc41c412feb015e9ff6b96e439c" + integrity sha512-ZlbnXDcNYHMR25ITwwNKT88JiaukkdVj/nG7r3wnuXkOTHc60Uy05PwMCPre0hSkY68E6zK3xz+vUJSP2jWmcw== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.5" + source-map-support "^0.5.16" + +"@babel/runtime-corejs3@^7.10.2": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7" + integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg== + dependencies: + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.14.8", "@babel/runtime@^7.16.3", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.9.2": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.12.5", "@babel/runtime@^7.17.8", "@babel/runtime@^7.5.0", "@babel/runtime@^7.8.4": + 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/runtime@^7.18.9": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" + integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== + dependencies: + regenerator-runtime "^0.13.10" + +"@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/template@^7.18.10": + 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/template@^7.3.3": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" + integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/parser" "^7.16.7" + "@babel/types" "^7.16.7" + +"@babel/traverse@^7.1.6", "@babel/traverse@^7.16.0", "@babel/traverse@^7.7.2": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.10.tgz#448f940defbe95b5a8029975b051f75993e8239f" + integrity sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.16.8" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.16.10" + "@babel/types" "^7.16.8" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.16.10", "@babel/traverse@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" + integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.9" + "@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.9" + "@babel/types" "^7.18.9" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.17.0", "@babel/traverse@^7.17.3": + version "7.17.3" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.3.tgz#0ae0f15b27d9a92ba1f2263358ea7c4e7db47b57" + integrity sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw== + dependencies: + "@babel/code-frame" "^7.16.7" + "@babel/generator" "^7.17.3" + "@babel/helper-environment-visitor" "^7.16.7" + "@babel/helper-function-name" "^7.16.7" + "@babel/helper-hoist-variables" "^7.16.7" + "@babel/helper-split-export-declaration" "^7.16.7" + "@babel/parser" "^7.17.3" + "@babel/types" "^7.17.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.20.1": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.1.tgz#9b15ccbf882f6d107eeeecf263fbcdd208777ec8" + integrity sha512-d3tN8fkVJwFLkHkBN479SOsw4DMZnz8cdbL/gvuDuzy3TS6Nfw80HuQqhw1pITbIruHyh7d1fMA47kWzmcUEGA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.20.1" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.19.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.20.1" + "@babel/types" "^7.20.0" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.15.6", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" + integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.17.0": + version "7.17.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b" + integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + +"@babel/types@^7.18.10", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2": + version "7.20.2" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.2.tgz#67ac09266606190f496322dbaff360fdaa5e7842" + integrity sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog== + dependencies: + "@babel/helper-string-parser" "^7.19.4" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + +"@base2/pretty-print-object@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" + integrity sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA== + +"@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== + +"@bull-board/api@4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@bull-board/api/-/api-4.6.4.tgz#f79b78c576a32c0e4e38c50e58d228cc72562c77" + integrity sha512-bXUYUwxKwJB2HhHkBPwNU2k3xsBUS1rG2rG4iw9NWj7C94Wu3tWXC/kbIFz7EYB37XT38rDzx3OXa8OkgWA/cQ== + dependencies: + redis-info "^3.0.8" + +"@bull-board/express@^4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@bull-board/express/-/express-4.6.4.tgz#57ba99b42843b430f1fa5e019273ac3087c6bd90" + integrity sha512-BTbxhth7Eom8bqPxourVUMw9F9XD075zz2lOSQJHGHHmpQcXRacA9fSeTHoFtH9gqPRKbb2pXSfOo8zkHSUlRw== + dependencies: + "@bull-board/api" "4.6.4" + "@bull-board/ui" "4.6.4" + ejs "3.1.7" + express "4.17.3" + +"@bull-board/ui@4.6.4": + version "4.6.4" + resolved "https://registry.yarnpkg.com/@bull-board/ui/-/ui-4.6.4.tgz#9c2c9f98d122ef80f34a1708eb68ef1be7429e92" + integrity sha512-9d12C2M0I6/t00p7xZdvdvQ5DcWD8Wx1pCroJXIBF1IWOhjGQt93MjaPzkYZlMW/VHu6Vt9LSZ1w4HKgTWbKKw== + dependencies: + "@bull-board/api" "4.6.4" + +"@casl/ability@^6.3.2": + version "6.3.2" + resolved "https://registry.yarnpkg.com/@casl/ability/-/ability-6.3.2.tgz#a975cbb6c73542def1b0518d2894915e608062a2" + integrity sha512-ygOlg3WDu39t1ZOVdDfRpPXEiCn7F/a7uLBJIuAE6KksdBogzPszFRAuGULmo4h37fXIyouYUilVIryh0ddTRA== + dependencies: + "@ucast/mongo2js" "^1.3.0" + +"@casl/prisma@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@casl/prisma/-/prisma-1.3.3.tgz#5c4996cd085be18070d5a394d851d621c868412b" + integrity sha512-G55pyNsIJlS18lfcf4Gj56g0TSHFF3kh8G6rLuUIac2e+lLzrkSWxSS15+YqPDkkVRr0rynKyCR/60CXNTMnhA== + dependencies: + "@ucast/core" "^1.10.0" + "@ucast/js" "^3.0.1" + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@cypress/request@^2.88.10": + version "2.88.10" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" + integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + http-signature "~1.3.6" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^8.3.2" + +"@cypress/xvfb@^1.2.4": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a" + integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q== + dependencies: + debug "^3.1.0" + lodash.once "^4.1.1" + +"@dabh/diagnostics@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" + integrity sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q== + dependencies: + colorspace "1.1.x" + enabled "2.0.x" + kuler "^2.0.0" + +"@discoveryjs/json-ext@^0.5.3": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@emotion/is-prop-valid@^0.8.2": + version "0.8.8" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a" + integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA== + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" + integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== + +"@eslint/eslintrc@^1.2.3": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" + integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.2" + 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" + +"@fast-csv/format@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3" + integrity sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A== + dependencies: + "@types/node" "^14.0.1" + lodash.escaperegexp "^4.1.2" + lodash.isboolean "^3.0.3" + lodash.isequal "^4.5.0" + lodash.isfunction "^3.0.9" + lodash.isnil "^4.0.0" + +"@fast-csv/parse@^4.3.6": + version "4.3.6" + resolved "https://registry.yarnpkg.com/@fast-csv/parse/-/parse-4.3.6.tgz#ee47d0640ca0291034c7aa94039a744cfb019264" + integrity sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA== + dependencies: + "@types/node" "^14.0.1" + lodash.escaperegexp "^4.1.2" + lodash.groupby "^4.6.0" + lodash.isfunction "^3.0.9" + lodash.isnil "^4.0.0" + lodash.isundefined "^3.0.1" + lodash.uniq "^4.5.0" + +"@finicity/connect-web-sdk@^1.0.0-rc.4": + version "1.0.0-rc.4" + resolved "https://registry.yarnpkg.com/@finicity/connect-web-sdk/-/connect-web-sdk-1.0.0-rc.4.tgz#e8ec00b150a82fcc6e4adf7567e7b0d7b592f808" + integrity sha512-rt69OxN1KygXSPVlJyZrEFMpe614uEuNBXTg96wZ5kOWmCRVUOy4X3E7244iGV/Llgi3czSmz9TtMxwIMKibNA== + +"@gar/promisify@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + +"@hapi/hoek@^9.0.0": + version "9.2.1" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.2.1.tgz#9551142a1980503752536b5050fd99f4a7f13b17" + integrity sha512-gfta+H8aziZsm8pZa0vj04KO6biEiisppNgA1kbJvFrrWu9Vm7eaUEy76DIxsuTaWvti5fkJVhllWc6ZTE+Mdw== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@headlessui/react@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.2.tgz#e6a6a8d38342064a53182f1eb2bf6d9c1e53ba6a" + integrity sha512-snLv2lxwsf2HNTOBNgHYdvoYZ3ChJE8QszPi1d/hl9js8KrFrUulTaQBfSyPbJP5BybVreWh9DxCgz9S0Z6hKQ== + +"@hookform/resolvers@^2.9.6": + version "2.9.6" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.6.tgz#db4277a96d1817d94169108167014926d8a10398" + integrity sha512-f4VxF8w9rdX0WsDCk3FW1dGPj/Sj8+1Ulcgtm5ymgWEpbA/fjY+NUDh+L9hftmxDgP8+EJFtF+qFK4gPEXVrVQ== + +"@humanwhocodes/config-array@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914" + integrity sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@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== + +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + +"@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@^28.1.1", "@jest/console@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" + integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + +"@jest/core@^28.1.1", "@jest/core@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" + integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/reporters" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@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 "^28.1.3" + jest-config "^28.1.3" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-resolve-dependencies "^28.1.3" + jest-runner "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + jest-watcher "^28.1.3" + micromatch "^4.0.4" + pretty-format "^28.1.3" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^28.1.1", "@jest/environment@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" + integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== + dependencies: + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + +"@jest/expect@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" + integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== + dependencies: + expect "^28.1.3" + jest-snapshot "^28.1.3" + +"@jest/fake-timers@^28.1.1", "@jest/fake-timers@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" + integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== + dependencies: + "@jest/types" "^28.1.3" + "@sinonjs/fake-timers" "^9.1.2" + "@types/node" "*" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-util "^28.1.3" + +"@jest/globals@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" + integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/types" "^28.1.3" + +"@jest/reporters@28.1.1": + version "28.1.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.1.tgz#9389f4bb3cce4d9b586f6195f83c79cd2a1c8662" + integrity sha512-597Zj4D4d88sZrzM4atEGLuO7SdA/YrOv9SRXHXRNC+/FwPCWxZhBAEzhXoiJzfRwn8zes/EjS8Lo6DouGN5Gg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^28.1.1" + "@jest/test-result" "^28.1.1" + "@jest/transform" "^28.1.1" + "@jest/types" "^28.1.1" + "@jridgewell/trace-mapping" "^0.3.7" + "@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 "^28.1.1" + jest-util "^28.1.1" + jest-worker "^28.1.1" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + terminal-link "^2.0.0" + v8-to-istanbul "^9.0.0" + +"@jest/reporters@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" + integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + "@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 "^28.1.3" + jest-util "^28.1.3" + jest-worker "^28.1.3" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + terminal-link "^2.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/source-map@^28.1.2": + version "28.1.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" + integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== + dependencies: + "@jridgewell/trace-mapping" "^0.3.13" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@28.1.1": + version "28.1.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.1.tgz#c6f18d1bbb01aa88925dd687872a75f8414b317a" + integrity sha512-hPmkugBktqL6rRzwWAtp1JtYT4VHwv8OQ+9lE5Gymj6dHzubI/oJHMUpPOt8NrdVWSrz9S7bHjJUmv2ggFoUNQ== + dependencies: + "@jest/console" "^28.1.1" + "@jest/types" "^28.1.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-result@^28.1.1", "@jest/test-result@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" + integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== + dependencies: + "@jest/console" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^28.1.1", "@jest/test-sequencer@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" + integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== + dependencies: + "@jest/test-result" "^28.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + slash "^3.0.0" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/transform@^28.1.1", "@jest/transform@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" + integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-regex-util "^28.0.2" + jest-util "^28.1.3" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.1" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^28.1.1", "@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@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.3.0", "@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.1.0", "@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.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/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@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.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.0": + version "0.3.4" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.4.tgz#f6a0832dffd5b8a6aaa633b7d9f8e8e94c83a0c3" + integrity sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.14", "@jridgewell/trace-mapping@^0.3.7": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@juggle/resize-observer@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.3.1.tgz#b50a781709c81e10701004214340f25475a171a0" + integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw== + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@motionone/animation@^10.12.0": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.13.1.tgz#ebacb50df4b7eb4957cf398c221ae10852f28520" + integrity sha512-dxQ+1wWxL6iFHDy1uv6hhcPjIdOg36eDT56jN4LI7Z5HZRyLpq8x1t7JFQclo/IEIb+6Bk4atmyinGFdXVECuA== + dependencies: + "@motionone/easing" "^10.13.1" + "@motionone/types" "^10.13.0" + "@motionone/utils" "^10.13.1" + tslib "^2.3.1" + +"@motionone/dom@10.12.0": + version "10.12.0" + resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.12.0.tgz#ae30827fd53219efca4e1150a5ff2165c28351ed" + integrity sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw== + dependencies: + "@motionone/animation" "^10.12.0" + "@motionone/generators" "^10.12.0" + "@motionone/types" "^10.12.0" + "@motionone/utils" "^10.12.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.13.1.tgz#7927b7fe96135989e37c2cda957e4101a4b85aa8" + integrity sha512-INEsInHHDHVgx0dp5qlXi1lMXBqYicgLMMSn3zfGzaIvcaEbI1Uz8BoyNV4BiclTupG7RYIh+T6BU83ZcEe74g== + dependencies: + "@motionone/utils" "^10.13.1" + tslib "^2.3.1" + +"@motionone/generators@^10.12.0": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.13.1.tgz#d4989d887b864e0aefbbec63eed35ce298a73773" + integrity sha512-+HK5u2YcNJCckTTqfOLgSVcrWv2z1dVwrSZEMVJuAh0EnWEWGDJRvMBoPc0cFf/osbkA2Rq9bH2+vP0Ex/D8uw== + dependencies: + "@motionone/types" "^10.13.0" + "@motionone/utils" "^10.13.1" + tslib "^2.3.1" + +"@motionone/types@^10.12.0", "@motionone/types@^10.13.0": + version "10.13.0" + resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.13.0.tgz#b22c549931ebd88ed5528158b5d611dc9dbb3756" + integrity sha512-qegk4qg8U1N9ZwAJ187BG3TkZz1k9LP/pvNtCSlqdq/PMUDKlCFG4ZnjJ481P0IOH/vIw1OzIbKIuyg0A3rk9g== + +"@motionone/utils@^10.12.0", "@motionone/utils@^10.13.1": + version "10.13.1" + resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.13.1.tgz#14919dfcda36b54b184fff690bc0125d554f60cd" + integrity sha512-TjDPTIppaf3ofBXQv4ZzAketJgN0sclALXfZ6mfrkjJkOy83mLls9744F+6S+VKCpBmvbZcBY4PQfrfhAfeMtA== + dependencies: + "@motionone/types" "^10.13.0" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@next/bundle-analyzer@^13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-13.1.1.tgz#f36108dcb953ea518253df5eb9e175642f78b04a" + integrity sha512-zxC/MOj7gDjvQffHT4QZqcPe1Ny+e6o3wethCZn3liSElMA+kxgEopbziTUXdrvJcd/porq+3Itc8P+gxE/xog== + dependencies: + webpack-bundle-analyzer "4.7.0" + +"@next/env@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.1.tgz#6ff26488dc7674ef2bfdd1ca28fe43eed1113bea" + integrity sha512-vFMyXtPjSAiOXOywMojxfKIqE3VWN5RCAx+tT3AS3pcKjMLFTCJFUWsKv8hC+87Z1F4W3r68qTwDFZIFmd5Xkw== + +"@next/eslint-plugin-next@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.1.tgz#cc5e419cc85587f73f2ac0046a91df01dc6fef8b" + integrity sha512-SBrOFS8PC3nQ5aeZmawJkjKkWjwK9RoxvBSv/86nZp0ubdoVQoko8r8htALd9ufp16NhacCdqhu9bzZLDWtALQ== + dependencies: + glob "7.1.7" + +"@next/swc-android-arm-eabi@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz#b5c3cd1f79d5c7e6a3b3562785d4e5ac3555b9e1" + integrity sha512-qnFCx1kT3JTWhWve4VkeWuZiyjG0b5T6J2iWuin74lORCupdrNukxkq9Pm+Z7PsatxuwVJMhjUoYz7H4cWzx2A== + +"@next/swc-android-arm64@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.1.1.tgz#e2ca9ccbba9ef770cb19fbe96d1ac00fe4cb330d" + integrity sha512-eCiZhTzjySubNqUnNkQCjU3Fh+ep3C6b5DCM5FKzsTH/3Gr/4Y7EiaPZKILbvnXmhWtKPIdcY6Zjx51t4VeTfA== + +"@next/swc-darwin-arm64@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.1.tgz#4af00877332231bbd5a3703435fdd0b011e74767" + integrity sha512-9zRJSSIwER5tu9ADDkPw5rIZ+Np44HTXpYMr0rkM656IvssowPxmhK0rTreC1gpUCYwFsRbxarUJnJsTWiutPg== + +"@next/swc-darwin-x64@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.1.tgz#bf4cb09e7e6ec6d91e031118dde2dd17078bcbbc" + integrity sha512-qWr9qEn5nrnlhB0rtjSdR00RRZEtxg4EGvicIipqZWEyayPxhUu6NwKiG8wZiYZCLfJ5KWr66PGSNeDMGlNaiA== + +"@next/swc-freebsd-x64@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.1.tgz#6933ea1264328e8523e28818f912cd53824382d4" + integrity sha512-UwP4w/NcQ7V/VJEj3tGVszgb4pyUCt3lzJfUhjDMUmQbzG9LDvgiZgAGMYH6L21MoyAATJQPDGiAMWAPKsmumA== + +"@next/swc-linux-arm-gnueabihf@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.1.tgz#b5896967aaba3873d809c3ad2e2039e89acde419" + integrity sha512-CnsxmKHco9sosBs1XcvCXP845Db+Wx1G0qouV5+Gr+HT/ZlDYEWKoHVDgnJXLVEQzq4FmHddBNGbXvgqM1Gfkg== + +"@next/swc-linux-arm64-gnu@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.1.tgz#91b3e9ea8575b1ded421c0ea0739b7bccf228469" + integrity sha512-JfDq1eri5Dif+VDpTkONRd083780nsMCOKoFG87wA0sa4xL8LGcXIBAkUGIC1uVy9SMsr2scA9CySLD/i+Oqiw== + +"@next/swc-linux-arm64-musl@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.1.tgz#83149ea05d7d55f3664d608dbe004c0d125f9147" + integrity sha512-GA67ZbDq2AW0CY07zzGt07M5b5Yaq5qUpFIoW3UFfjOPgb0Sqf3DAW7GtFMK1sF4ROHsRDMGQ9rnT0VM2dVfKA== + +"@next/swc-linux-x64-gnu@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.1.tgz#d7d0777b56de0dd82b78055772e13e18594a15ca" + integrity sha512-nnjuBrbzvqaOJaV+XgT8/+lmXrSCOt1YYZn/irbDb2fR2QprL6Q7WJNgwsZNxiLSfLdv+2RJGGegBx9sLBEzGA== + +"@next/swc-linux-x64-musl@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.1.tgz#41655722b127133cd95ab5bc8ca1473e9ab6876f" + integrity sha512-CM9xnAQNIZ8zf/igbIT/i3xWbQZYaF397H+JroF5VMOCUleElaMdQLL5riJml8wUfPoN3dtfn2s4peSr3azz/g== + +"@next/swc-win32-arm64-msvc@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.1.tgz#f10da3dfc9b3c2bbd202f5d449a9b807af062292" + integrity sha512-pzUHOGrbgfGgPlOMx9xk3QdPJoRPU+om84hqVoe6u+E0RdwOG0Ho/2UxCgDqmvpUrMab1Deltlt6RqcXFpnigQ== + +"@next/swc-win32-ia32-msvc@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.1.tgz#4c0102b9b18ece15c818056d07e3917ee9dade78" + integrity sha512-WeX8kVS46aobM9a7Xr/kEPcrTyiwJqQv/tbw6nhJ4fH9xNZ+cEcyPoQkwPo570dCOLz3Zo9S2q0E6lJ/EAUOBg== + +"@next/swc-win32-x64-msvc@13.1.1": + version "13.1.1" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.1.tgz#c209a37da13be27b722f9c40c40ab4b094866244" + integrity sha512-mVF0/3/5QAc5EGVnb8ll31nNvf3BWpPY4pBb84tk+BfQglWLqc5AC9q1Ht/YMWiEgs8ALNKEQ3GQnbY0bJF2Gg== + +"@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.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@nodelib/fs.walk@^1.2.3": + 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" + +"@npmcli/fs@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@nrwl/cli@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/cli/-/cli-15.5.2.tgz#57bfec1d1dd9235754c38fedfdbdb39ca9b1088c" + integrity sha512-T0nN2hYDtkY9SsbtZqFwRSYC8Td2zPqQ7dj6FrwvjFGbHLAxN6rknVPPKN4S7Dt6feXX1Woe8XYwWouk9Cg6nw== + dependencies: + nx "15.5.2" + +"@nrwl/cypress@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/cypress/-/cypress-15.5.2.tgz#34660a86fd7b143045c9af08469a45038146a114" + integrity sha512-VeUmEhEibBDBbqxSpWToTDD+PRugQ7n+u8+ATuGl/p0zWh7cbBld+HoPLMz+qoemkCLF/lD3v5fQPuyoWyHJ7g== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/workspace" "15.5.2" + "@phenomnomnominal/tsquery" "4.1.1" + dotenv "~10.0.0" + semver "7.3.4" + +"@nrwl/devkit@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/devkit/-/devkit-15.5.2.tgz#bb7268d6b49450befdc0a7988c897900e3aa7f44" + integrity sha512-rvxuiYVpGDB9RzjOAWTNm7IBPwuYG6kL24LkJMUS8gCNqLBNNlYUsdkpa0PLRt5eNeGLKqpSDB8BpA+e1ty/zA== + dependencies: + "@phenomnomnominal/tsquery" "4.1.1" + ejs "^3.1.7" + ignore "^5.0.4" + semver "7.3.4" + tslib "^2.3.0" + +"@nrwl/eslint-plugin-nx@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/eslint-plugin-nx/-/eslint-plugin-nx-15.5.2.tgz#e3a85279ddf7f95f2f3f029943f0bb41e789a3ee" + integrity sha512-FrVaNWHknXjM/6m6lkx7NUVQPMjPvqQEAI6PHo03U+HF9KF4X191uuHzgsZleercn2oH72ZUta4R4nPV5swCmg== + dependencies: + "@nrwl/devkit" "15.5.2" + "@typescript-eslint/utils" "^5.36.1" + chalk "^4.1.0" + confusing-browser-globals "^1.0.9" + semver "7.3.4" + +"@nrwl/express@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/express/-/express-15.5.2.tgz#0e564ba320a5524fd47db924597fd5dacfeb93b9" + integrity sha512-ycPXbGgxkou389VyFi0j2bmUiCEPnCwMJnFX/FdQ5Gz7hXkgkiGv3J6Gd/Qypnml678dEB8Gee/trNL9B7N4sw== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/node" "15.5.2" + "@nrwl/workspace" "15.5.2" + +"@nrwl/jest@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/jest/-/jest-15.5.2.tgz#afef6f061fcc457d798cdfa9856051d512185c0d" + integrity sha512-2pKkAIepGvYa3l+2FO3UaFifutitK9HOF5NbZ4m9nDcUFStBAsyb+LpF3Mxcn/8XPiFflsM8bmRF7BrU79UQRA== + dependencies: + "@jest/reporters" "28.1.1" + "@jest/test-result" "28.1.1" + "@nrwl/devkit" "15.5.2" + "@phenomnomnominal/tsquery" "4.1.1" + chalk "^4.1.0" + dotenv "~10.0.0" + identity-obj-proxy "3.0.0" + jest-config "28.1.1" + jest-resolve "28.1.1" + jest-util "28.1.1" + resolve.exports "1.1.0" + tslib "^2.3.0" + +"@nrwl/js@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/js/-/js-15.5.2.tgz#89b06362f017f5453bcd49b8e2e0b19067ab54a3" + integrity sha512-vCyjQAmMXJlRM69y/zLJurg+a9cVOH6K4YFhfSfYGp9MlefvCT+/BnVu6vpJV++UgTb5HAnZEKenH8VX1ZS6ZQ== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/workspace" "15.5.2" + chalk "^4.1.0" + fast-glob "3.2.7" + fs-extra "^11.1.0" + ignore "^5.0.4" + js-tokens "^4.0.0" + minimatch "3.0.5" + source-map-support "0.5.19" + tree-kill "1.2.2" + tslib "^2.3.0" + +"@nrwl/linter@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/linter/-/linter-15.5.2.tgz#3cc2cfecd00db483a91c9b5a328b688c19a1fd52" + integrity sha512-9zboLfOJn3OEImclGVXhWIzNb6V+JeFN9MgajLJLQNjtKC3JMXkgeAIVSb52RUZe1xwohjOkPL6GyTxRV5TwVA== + dependencies: + "@nrwl/devkit" "15.5.2" + "@phenomnomnominal/tsquery" "4.1.1" + tmp "~0.2.1" + tslib "^2.3.0" + +"@nrwl/next@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/next/-/next-15.5.2.tgz#f3a605ade77c7261cd34c358405c717f8ebcb7c3" + integrity sha512-ryUy8Y/rwzNWUw0fui7c+76cRZVda3K2qCye8Xj/pBlqYx9YTjW4a7kqNMAx/QWP+8TDdzhRxhD0nvq8Ha5ajg== + dependencies: + "@babel/plugin-proposal-decorators" "^7.14.5" + "@nrwl/cypress" "15.5.2" + "@nrwl/devkit" "15.5.2" + "@nrwl/jest" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/react" "15.5.2" + "@nrwl/webpack" "15.5.2" + "@nrwl/workspace" "15.5.2" + "@svgr/webpack" "^6.1.2" + chalk "^4.1.0" + dotenv "~10.0.0" + fs-extra "^11.1.0" + ignore "^5.0.4" + semver "7.3.4" + ts-node "10.9.1" + tsconfig-paths "^4.1.2" + url-loader "^4.1.1" + webpack-merge "^5.8.0" + +"@nrwl/node@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/node/-/node-15.5.2.tgz#10970bfeefb24400fa94ffd203d716b3134944e4" + integrity sha512-VpWjduZXmYl5G4V2hlXVv9ec/IsasTqa9Pe+w+nz0D00FK6lPu7NqnGShaKmj6xceJ0MaRinxF++c8XFBJaSCA== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/jest" "15.5.2" + "@nrwl/js" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/webpack" "15.5.2" + "@nrwl/workspace" "15.5.2" + enquirer "~2.3.6" + tslib "^2.3.0" + +"@nrwl/react@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/react/-/react-15.5.2.tgz#7d2dc64837997eeac61e0e7bb178f16e8fe9d2e9" + integrity sha512-sJk0IOi5k5bgwCtXJoMQWYKsvi4rnOE7rSZ+wBJZ7AgXd4duoA2PtoYUiPiZXafFcpTdnHKqnjMxVbmK0rg0gg== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/workspace" "15.5.2" + "@phenomnomnominal/tsquery" "4.1.1" + chalk "^4.1.0" + enquirer "~2.3.6" + minimatch "3.0.5" + semver "7.3.4" + +"@nrwl/rollup@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/rollup/-/rollup-15.5.2.tgz#6c4413aab56c618e0316e03868215853d07d8f31" + integrity sha512-JR9sU4nEzVgxpab07D6qNcgh8AVykgQQOiwslusWjuLbojAueHk93/mHyr6igGSlZVN159LIxkNf6jzFwRiJDA== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/js" "15.5.2" + "@nrwl/workspace" "15.5.2" + "@rollup/plugin-babel" "^5.3.0" + "@rollup/plugin-commonjs" "^20.0.0" + "@rollup/plugin-image" "^2.1.0" + "@rollup/plugin-json" "^4.1.0" + "@rollup/plugin-node-resolve" "^13.0.4" + autoprefixer "^10.4.9" + babel-plugin-transform-async-to-promises "^0.8.15" + chalk "^4.1.0" + dotenv "~10.0.0" + fs-extra "^11.1.0" + postcss "^8.4.14" + rollup "^2.56.2" + rollup-plugin-copy "^3.4.0" + rollup-plugin-peer-deps-external "^2.2.4" + rollup-plugin-postcss "^4.0.1" + rollup-plugin-typescript2 "0.34.1" + rxjs "^6.5.4" + tslib "^2.3.0" + +"@nrwl/storybook@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/storybook/-/storybook-15.5.2.tgz#4d45496218def437c78cd2ec10a90624ae57fce8" + integrity sha512-/EY+kJhBEUTKLR1of/W9hSkeVkkZJ3GsR+YQir1nYox6TdsFxLdDb2OibdKLjF7/bvhm9yj7kjSc5OdnrHHLJA== + dependencies: + "@nrwl/cypress" "15.5.2" + "@nrwl/devkit" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/workspace" "15.5.2" + dotenv "~10.0.0" + semver "7.3.4" + +"@nrwl/tao@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/tao/-/tao-15.5.2.tgz#b2c0657d5d2afc0b96a434acd319258c7951b1c5" + integrity sha512-1thpCNcdpHfTf+o3p+VB6v8TEj6to3YVhA9d+3VhDKN9mq/RVcoMQQRIAgNOKbYWYNo8eC8MvuNqxgRPz2ESyg== + dependencies: + nx "15.5.2" + +"@nrwl/web@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/web/-/web-15.5.2.tgz#ca6030724b3fe8192a589a4689b259a35aec78e1" + integrity sha512-UpuRW7xHM36m1HIoFuiAC92dyeO/HhRtn7l7yVrIME7oFHQC2ef5vYMxQBF1wWW/hA0ikpdkAAVi+5Y+e16CpA== + dependencies: + "@babel/core" "^7.15.0" + "@babel/plugin-proposal-class-properties" "^7.14.5" + "@babel/plugin-proposal-decorators" "^7.14.5" + "@babel/plugin-transform-runtime" "^7.15.0" + "@babel/preset-env" "^7.15.0" + "@babel/preset-typescript" "^7.15.0" + "@babel/runtime" "^7.14.8" + "@nrwl/cypress" "15.5.2" + "@nrwl/devkit" "15.5.2" + "@nrwl/jest" "15.5.2" + "@nrwl/js" "15.5.2" + "@nrwl/linter" "15.5.2" + "@nrwl/rollup" "15.5.2" + "@nrwl/workspace" "15.5.2" + babel-plugin-const-enum "^1.0.1" + babel-plugin-macros "^2.8.0" + babel-plugin-transform-typescript-metadata "^0.3.1" + chalk "^4.1.0" + chokidar "^3.5.1" + http-server "^14.1.0" + ignore "^5.0.4" + tslib "^2.3.0" + +"@nrwl/webpack@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/webpack/-/webpack-15.5.2.tgz#7ea4eff880f8084b17b17ac759e1b6c6b8b44819" + integrity sha512-HPLzampVJZ3k3wv21ErghJdwVG665Tyb6N1hJ9ibd+nNJIIyhMHmFtPgxRJgbYcySon2KzT24QI6ko/w+AhfJQ== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/js" "15.5.2" + "@nrwl/workspace" "15.5.2" + autoprefixer "^10.4.9" + babel-loader "^8.2.2" + chalk "^4.1.0" + chokidar "^3.5.1" + copy-webpack-plugin "^10.2.4" + css-loader "^6.4.0" + css-minimizer-webpack-plugin "^3.4.1" + dotenv "~10.0.0" + file-loader "^6.2.0" + fork-ts-checker-webpack-plugin "7.2.13" + fs-extra "^11.1.0" + ignore "^5.0.4" + less "3.12.2" + less-loader "^11.1.0" + license-webpack-plugin "^4.0.2" + loader-utils "^2.0.3" + mini-css-extract-plugin "~2.4.7" + parse5 "4.0.0" + parse5-html-rewriting-stream "6.0.1" + postcss "^8.4.14" + postcss-import "~14.1.0" + postcss-loader "^6.1.1" + rxjs "^6.5.4" + sass "^1.42.1" + sass-loader "^12.2.0" + source-map-loader "^3.0.0" + style-loader "^3.3.0" + stylus "^0.55.0" + stylus-loader "^7.1.0" + terser-webpack-plugin "^5.3.3" + ts-loader "^9.3.1" + ts-node "10.9.1" + tsconfig-paths "^4.1.2" + tsconfig-paths-webpack-plugin "4.0.0" + tslib "^2.3.0" + webpack "^5.75.0" + webpack-dev-server "^4.9.3" + webpack-merge "^5.8.0" + webpack-node-externals "^3.0.0" + webpack-subresource-integrity "^5.1.0" + +"@nrwl/workspace@15.5.2": + version "15.5.2" + resolved "https://registry.yarnpkg.com/@nrwl/workspace/-/workspace-15.5.2.tgz#fb388cbbca405481e85e38ef763ea6c1b7c5dbad" + integrity sha512-f1ln2dPSrBCY19oIa1aEjRijegaXEWw9tryjef1a8TH6d37IpbOZEH4YNn41jDDmsQvjsVmTVP85D/0zH7xu/Q== + dependencies: + "@nrwl/devkit" "15.5.2" + "@nrwl/linter" "15.5.2" + "@parcel/watcher" "2.0.4" + chalk "^4.1.0" + chokidar "^3.5.1" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + dotenv "~10.0.0" + enquirer "~2.3.6" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + jsonc-parser "3.2.0" + minimatch "3.0.5" + npm-run-path "^4.0.1" + nx "15.5.2" + open "^8.4.0" + rxjs "^6.5.4" + semver "7.3.4" + tmp "~0.2.1" + tslib "^2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + +"@panva/asn1.js@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6" + integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw== + +"@panva/hkdf@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.0.2.tgz#bab0f09d09de9fd83628220d496627681bc440d6" + integrity sha512-MSAs9t3Go7GUkMhpKC44T58DJ5KGk2vBo+h1cqQeqlMfdGkxaVB78ZWpv9gYi/g2fa4sopag9gJsNvS8XGgWJA== + +"@parcel/watcher@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.4.tgz#f300fef4cc38008ff4b8c29d92588eced3ce014b" + integrity sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg== + dependencies: + node-addon-api "^3.2.1" + node-gyp-build "^4.3.0" + +"@phenomnomnominal/tsquery@4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@phenomnomnominal/tsquery/-/tsquery-4.1.1.tgz#42971b83590e9d853d024ddb04a18085a36518df" + integrity sha512-jjMmK1tnZbm1Jq5a7fBliM4gQwjxMU7TFoRNwIyzwlO+eHPRCFv/Nv+H/Gi1jc3WR7QURG8D5d0Tn12YGrUqBQ== + dependencies: + esquery "^1.0.1" + +"@pkgr/utils@^2.3.1": + version "2.3.1" + resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.3.1.tgz#0a9b06ffddee364d6642b3cd562ca76f55b34a03" + integrity sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw== + dependencies: + cross-spawn "^7.0.3" + is-glob "^4.0.3" + open "^8.4.0" + picocolors "^1.0.0" + tiny-glob "^0.2.9" + tslib "^2.4.0" + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz#58f8217ba70069cc6a73f5d7e05e85b458c150e2" + integrity sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q== + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.8.1" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + source-map "^0.7.3" + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.7": + version "0.5.10" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz#2eba163b8e7dbabb4ce3609ab5e32ab63dda3ef8" + integrity sha512-j0Ya0hCFZPd4x40qLzbhGsh9TMtdb+CJQiso+WxLOPNasohq9cc5SNUcwsZaRH6++Xh91Xkm/xHCkuIiIu0LUA== + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^3.0.0" + source-map "^0.7.3" + +"@polka/url@^1.0.0-next.20": + version "1.0.0-next.21" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" + integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== + +"@polygon.io/client-js@^6.0.6": + version "6.0.6" + resolved "https://registry.yarnpkg.com/@polygon.io/client-js/-/client-js-6.0.6.tgz#55f6b231b0d3d513d53c4fa2af89f80129daa488" + integrity sha512-w0ltaCmQYRPA7lx/uu7LJgNKQdeYWXWlb243E2K2i9NuYG12X2LadZv/TVdkGkV5EiU+bdwgT5KJIc50zSRROg== + dependencies: + cross-fetch "^3.1.4" + query-string "^7.0.1" + websocket "^1.0.34" + +"@popperjs/core@^2.11.5": + version "2.11.5" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64" + integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw== + +"@popperjs/core@^2.9.0": + version "2.11.2" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9" + integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA== + +"@prisma/client@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.8.1.tgz#51c16488dfac4e74a275a2753bf20262a65f2a2b" + integrity sha512-d4xhZhETmeXK/yZ7K0KcVOzEfI5YKGGEr4F5SBV04/MU4ncN/HcE28sy3e4Yt8UFW0ZuImKFQJE+9rWt9WbGSQ== + dependencies: + "@prisma/engines-version" "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe" + +"@prisma/engines-version@4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe": + version "4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.8.0-61.d6e67a83f971b175a593ccc12e15c4a757f93ffe.tgz#30401aba1029e7d32e3cb717e705a7c92ccc211e" + integrity sha512-MHSOSexomRMom8QN4t7bu87wPPD+pa+hW9+71JnVcF3DqyyO/ycCLhRL1we3EojRpZxKvuyGho2REQsMCvxcJw== + +"@prisma/engines@4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.8.1.tgz#8428f7dcd7912c6073024511376595017630dc85" + integrity sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw== + +"@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== + +"@rollup/plugin-babel@^5.3.0": + version "5.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz#9cb1c5146ddd6a4968ad96f209c50c62f92f9879" + integrity sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw== + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" + +"@rollup/plugin-commonjs@^20.0.0": + version "20.0.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-20.0.0.tgz#3246872dcbcb18a54aaa6277a8c7d7f1b155b745" + integrity sha512-5K0g5W2Ol8hAcTHqcTBHiA7M58tfmYi1o9KxeJuuRNpGaTa5iLjcyemBitCBcKXaHamOBBEH2dGom6v6Unmqjg== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-image@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-image/-/plugin-image-2.1.1.tgz#898d6b59ac0025d7971ef45640ab330cb0663b0c" + integrity sha512-AgP4U85zuQJdUopLUCM+hTf45RepgXeTb8EJsleExVy99dIoYpt3ZlDYJdKmAc2KLkNntCDg6BPJvgJU3uGF+g== + dependencies: + "@rollup/pluginutils" "^3.1.0" + mini-svg-data-uri "^1.2.3" + +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + +"@rollup/plugin-node-resolve@^13.0.4": + version "13.1.3" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" + integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rollup/pluginutils@^4.1.2": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + +"@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== + +"@sentry/browser@7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.22.0.tgz#c4f283cd7825db0db59ac97c3334c939982ace94" + integrity sha512-8MA+f46+T3G7fb4BYYX9Wl3bMDloG5a3Ng0GWdBeq6DE2tXVHeCvba8Yrrcnn1qFHpmnOn5Nz4xWBUDs3uBFxA== + dependencies: + "@sentry/core" "7.22.0" + "@sentry/types" "7.22.0" + "@sentry/utils" "7.22.0" + tslib "^1.9.3" + +"@sentry/core@7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.22.0.tgz#8e50f288e5e8fcaa2774daffd2487e042a392893" + integrity sha512-qYJiJrL1mfQQln84mNunBRUkXq7xDGQQoNh4Sz9VYP5698va51zmS5BLYRCZ5CkPwRYNuhUqlUXN7bpYGYOOIA== + dependencies: + "@sentry/types" "7.22.0" + "@sentry/utils" "7.22.0" + tslib "^1.9.3" + +"@sentry/node@^7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.22.0.tgz#d575481e56d3326ad457378db5ab7cc804b712fd" + integrity sha512-jKhxqKsbEEaY/g3FTzpj1fwukN0IkNv4V+0Fl+t/EmSNUL/7q5FMmDBa+fFQuQzwps8UEpzqPOzMSRapVsoP0w== + dependencies: + "@sentry/core" "7.22.0" + "@sentry/types" "7.22.0" + "@sentry/utils" "7.22.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/react@^7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.22.0.tgz#04e2f100b02e1051f8e9b3f69506efa21a18ca9a" + integrity sha512-nbZD+bobhV65r/4mpfRgGct1nrYWEmnNzTYZM4PQyPyImuk/VmNNdnzP3BLWqAnV4LvbVWEkgZIcquN8yA098g== + dependencies: + "@sentry/browser" "7.22.0" + "@sentry/types" "7.22.0" + "@sentry/utils" "7.22.0" + hoist-non-react-statics "^3.3.2" + tslib "^1.9.3" + +"@sentry/tracing@^7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.22.0.tgz#ec29325ee2c5959670097c104e47a78797cef17b" + integrity sha512-s68aSnrRaWQ+Z5oG9ozIegUkofZy9PwicuXYEPA8K/R30F1CVpHvDZ/3KlFnByl+aXTbAyLBQzN2sAObB5g4pQ== + dependencies: + "@sentry/core" "7.22.0" + "@sentry/types" "7.22.0" + "@sentry/utils" "7.22.0" + tslib "^1.9.3" + +"@sentry/types@7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.22.0.tgz#58e4ce77b523048e0f31e2ea4b597946d76f6079" + integrity sha512-LhCL+wb1Jch+OesB2CIt6xpfO1Ab6CRvoNYRRzVumWPLns1T3ZJkarYfhbLaOEIb38EIbPgREdxn2AJT560U4Q== + +"@sentry/utils@7.22.0": + version "7.22.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.22.0.tgz#fb46dc2761e2d44cf70bc3e1fba61d55852904b5" + integrity sha512-1GiNw1opIngxg0nktCTc9wibh4/LV12kclrnB9dAOHrqazZXHXZRAkjqrhQphKcMpT+3By91W6EofjaDt5a/hg== + dependencies: + "@sentry/types" "7.22.0" + tslib "^1.9.3" + +"@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" + +"@sideway/address@^4.1.3": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.3.tgz#d93cce5d45c5daec92ad76db492cc2ee3c64ab27" + integrity sha512-8ncEUtmnTsMmL7z1YPB47kPUq7LpKWJNFPsRzHiIajGC5uXlWGn+AmkYPcHNl8S4tcEGx+cnORnNYaw2wvL+LQ== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.51.tgz#645f33fe4e02defe26f2f5c0410e1c094eac7f5f" + integrity sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA== + +"@sindresorhus/is@^4.0.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.4.0.tgz#e277e5bdbdf7cb1e20d320f02f5e2ed113cd3185" + integrity sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ== + +"@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" + +"@storybook/addon-actions@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.15.tgz#ba737561dbf8a358ea8bc588f3da9fddd1a4267e" + integrity sha512-cnLzVK1S+EydFDSuvxMmMAxVqNXijBGdV9QTgsu6ys5sOkoiXRETKZmxuN8/ZRbkfc4N+1KAylSCZOOHzBQTBQ== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + polished "^4.2.2" + prop-types "^15.7.2" + react-inspector "^5.1.0" + regenerator-runtime "^0.13.7" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + uuid-browser "^3.1.0" + +"@storybook/addon-backgrounds@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.15.tgz#bb97df61a9436f6f5a61f751f4f3fedcfe19fb84" + integrity sha512-9ddB3QIL8mRurf7TvYG1P9i1sW0b8Iik3kGlHggKogHER9WJPzbiUeH0XDjkASSa4rMCZdYn5CZKNkIAoJ2jdA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + global "^4.4.0" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/addon-controls@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.15.tgz#2a1f590d5624f658561b8e0ff144667248baf907" + integrity sha512-q5y0TvD0stvQoJZ2PnFmmKIRNSOI4/k2NKyZq//J2cBUBcP1reYlFxdsNwLZWmAFpSIkc2+nsliEzNxU1WByoA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/node-logger" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + lodash "^4.17.21" + ts-dedent "^2.0.0" + +"@storybook/addon-docs@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.15.tgz#d5f4b991636953f30daea6aa89d5bfdfa97b9116" + integrity sha512-k3LAu+wVp6pNhfh6B1soCRl6+7sNTNxtqy6WTrIeVJVCGbXbyc5s7gQ48gJ4WAk6meoDEZbypiP4NK1El03YLg== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.12" + "@babel/preset-env" "^7.12.11" + "@jest/transform" "^26.6.2" + "@mdx-js/react" "^1.6.22" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/docs-tools" "6.5.15" + "@storybook/mdx1-csf" "^0.0.1" + "@storybook/node-logger" "6.5.15" + "@storybook/postinstall" "6.5.15" + "@storybook/preview-web" "6.5.15" + "@storybook/source-loader" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" + babel-loader "^8.0.0" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + regenerator-runtime "^0.13.7" + remark-external-links "^8.0.0" + remark-slug "^6.0.0" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/addon-essentials@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.15.tgz#cfbf6cacfbc7d14ac30aab118efcac818f0814ba" + integrity sha512-m3EY6BhUk6Z9Et7P5wGaRGNoEDHzJIOsLbGS/4IXvIoDfrqmNIilqUQl8kfDqpVdBSFprvpacHpKpLosu9H37w== + dependencies: + "@storybook/addon-actions" "6.5.15" + "@storybook/addon-backgrounds" "6.5.15" + "@storybook/addon-controls" "6.5.15" + "@storybook/addon-docs" "6.5.15" + "@storybook/addon-measure" "6.5.15" + "@storybook/addon-outline" "6.5.15" + "@storybook/addon-toolbars" "6.5.15" + "@storybook/addon-viewport" "6.5.15" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/node-logger" "6.5.15" + core-js "^3.8.2" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/addon-measure@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.15.tgz#a174bf498168e52469f602a8d6ba6160d4b8a9d6" + integrity sha512-j77WX/v6qpWK8ZuYscWLIc+Am4/WOJRsVgyXLIw1EZIviQsjoXPo7mmyoTneEIbbHfPtWlLRbtmkjh8DAVDrCA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + global "^4.4.0" + +"@storybook/addon-outline@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.15.tgz#7cf9127f3d321136d6780ea2db525b4fd2678281" + integrity sha512-8yGEZQOYypnliU3rsakoZlgT4Pkq8iOhX9JclVXZL/fJMQWFQGCsXqlLaRn8sx7qsa+21PPxY5bd2+Hv/Au4zQ== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/addon-postcss@3.0.0-alpha.1": + version "3.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@storybook/addon-postcss/-/addon-postcss-3.0.0-alpha.1.tgz#aa69bbeeb778fe184f9880da44b0a7883fc86ba3" + integrity sha512-j3DB//h89ctz0Orh1N4AHuzdzYhbL5XhUdh1V5rJ6tPOhHZ57v60tS4owukY4nE5gqM345lbuXK6MjbSZPqZdQ== + dependencies: + "@storybook/node-logger" "^6.1.14" + css-loader "^3.6.0" + postcss "^7.0.35" + postcss-loader "^4.2.0" + style-loader "^1.3.0" + +"@storybook/addon-toolbars@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.15.tgz#6c11c9cbcc958ffe881c04cd5f04c2f47ed22f44" + integrity sha512-btwDTgElmaaT0dBRASABbTpq6m1UiQXQmLUmxfjLxVC3I2SK5tyJKbPQ2hVLFAQHK4cQn4u45BxdZ5LDpJ830A== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + regenerator-runtime "^0.13.7" + +"@storybook/addon-viewport@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.15.tgz#c30d0f865d86658c380d31f1ae3589392046e23b" + integrity sha512-oOiVzgFMlTnzPLVoHWQNzWdmpksrUyT6Aq8ZOyBPNMQ0RN2doIgFr7W53nZ1OBB5cPQx9q2FgWwzJ7Tawo+iVA== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + global "^4.4.0" + memoizerific "^1.11.3" + prop-types "^15.7.2" + regenerator-runtime "^0.13.7" + +"@storybook/addons@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.15.tgz#3c3fafbf3c9ce2182d652cb6682f6581ba6580e1" + integrity sha512-xT31SuSX+kYGyxCNK2nqL7WTxucs3rSmhiCLovJcUjYk+QquV3c2c53Ki7lwwdDbzfXFcNAe0HJ4hoTN4jhn0Q== + dependencies: + "@storybook/api" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.15" + "@storybook/theming" "6.5.15" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + +"@storybook/api@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.15.tgz#a189dac82a57ae9cfac43c887207b1075a2a2e96" + integrity sha512-BBE0KXKvj1/3jTghbIoWfrcDM0t+xO7EYtWWAXD6XlhGsZVD2Dy82Z52ONyLulMDRpMWl0OYy3h6A1YnFUH25w== + dependencies: + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.15" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/builder-webpack4@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.15.tgz#8050b2eec84e055eee9b181e067d9a8aa76e252a" + integrity sha512-1ZkMECUUdiYplhlgyUF5fqW3XU7eWNDJbuPUguyDOeidgJ111WZzBcLuKj+SNrzdNNgXwROCWAFybiNnX33YHQ== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/preview-web" "6.5.15" + "@storybook/router" "6.5.15" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" + "@storybook/ui" "6.5.15" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/webpack" "^4.41.26" + autoprefixer "^9.8.6" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + core-js "^3.8.2" + css-loader "^3.6.0" + file-loader "^6.2.0" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^4.1.6" + glob "^7.1.6" + glob-promise "^3.4.0" + global "^4.4.0" + html-webpack-plugin "^4.0.0" + pnp-webpack-plugin "1.6.4" + postcss "^7.0.36" + postcss-flexbugs-fixes "^4.2.1" + postcss-loader "^4.2.0" + raw-loader "^4.0.2" + stable "^0.1.8" + style-loader "^1.3.0" + terser-webpack-plugin "^4.2.3" + ts-dedent "^2.0.0" + url-loader "^4.1.1" + util-deprecate "^1.0.2" + webpack "4" + webpack-dev-middleware "^3.7.3" + webpack-filter-warnings-plugin "^1.2.1" + webpack-hot-middleware "^2.25.1" + webpack-virtual-modules "^0.2.2" + +"@storybook/builder-webpack5@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.15.tgz#0c45ed6699d88a9d51f00c8c312750fcd707c37d" + integrity sha512-BnSoAmI02pvbGBSyzCx+voXb/d5EopQ78zx/lYv4CeOspBFOYEfGvAgYHILFo04V12S2/k8aSOc/tCYw5AqPtw== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/preview-web" "6.5.15" + "@storybook/router" "6.5.15" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.15" + "@storybook/theming" "6.5.15" + "@types/node" "^14.0.10 || ^16.0.0" + babel-loader "^8.0.0" + babel-plugin-named-exports-order "^0.0.2" + browser-assert "^1.2.1" + case-sensitive-paths-webpack-plugin "^2.3.0" + core-js "^3.8.2" + css-loader "^5.0.1" + fork-ts-checker-webpack-plugin "^6.0.4" + glob "^7.1.6" + glob-promise "^3.4.0" + html-webpack-plugin "^5.0.0" + path-browserify "^1.0.1" + process "^0.11.10" + stable "^0.1.8" + style-loader "^2.0.0" + terser-webpack-plugin "^5.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "^5.9.0" + webpack-dev-middleware "^4.1.0" + webpack-hot-middleware "^2.25.1" + webpack-virtual-modules "^0.4.1" + +"@storybook/channel-postmessage@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.15.tgz#a9d614be56bededf7cec41b833c46d35958b6d2b" + integrity sha512-gMpA8LWT8lC4z5KWnaMh03aazEwtDO7GtY5kZVru+EEMgExGmaR82qgekwmLmgLj2nRJEv0o138o9IqYUcou8w== + dependencies: + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + core-js "^3.8.2" + global "^4.4.0" + qs "^6.10.0" + telejson "^6.0.8" + +"@storybook/channel-websocket@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.15.tgz#3fa090412d122428a7bc3d622e4cd36fdac7dd78" + integrity sha512-K85KEgzo5ahzJNJjyUbSNyuRmkeC8glJX2hCg2j9HiJ9rasX53qugkODrKDlWAeheulo3kR13VSuAqIuwVbmbw== + dependencies: + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + core-js "^3.8.2" + global "^4.4.0" + telejson "^6.0.8" + +"@storybook/channels@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.15.tgz#586681b6ec458124da084c39bc8c518d9e96b10b" + integrity sha512-gPpsBgirv2NCXbH4WbYqdkI0JLE96aiVuu7UEWfn9yu071pQ9CLHbhXGD9fSFNrfOkyBBY10ppSE7uCXw3Wexg== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/client-api@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.15.tgz#70f3ced6d0fcc7b71217cae858bf81c3b2c50eba" + integrity sha512-0ZGpRgVz7rdbCguBqBpwObXbsVY5qlSTWDzzIBpmz8EkxW/MtK5wEyeq+0L0O+DTn41FwvH5yCGLAENpzWD8BQ== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.15" + "@types/qs" "^6.9.5" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/client-logger@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.15.tgz#0d9878af893a3493b6ee108cc097ae1436d7da4d" + integrity sha512-0uyxKvodq+FycGv6aUwC1wUR6suXf2+7ywMFAOlYolI4UvNj8NyU/5AfgKT5XnxYAgPmoCiAjOE700TrfHrosw== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + +"@storybook/components@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.15.tgz#8145be807bf48c1d010f29114411f390a9e3228f" + integrity sha512-bHTT0Oa3s4g+MBMaLBbX9ofMtb1AW59AzIUNGrfqW1XqJMGuUHMiJ7TSo+i5dRSFpbFygnwMEG9LfHxpR2Z0Dw== + dependencies: + "@storybook/client-logger" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + util-deprecate "^1.0.2" + +"@storybook/core-client@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.15.tgz#57d86a07f920ab0cb058820dbcb1f48cc9dc6257" + integrity sha512-i9t4WONy2MxJwLI1FIp5ck7b52EXyJfALnxUn4O/3GTkw09J0NOKi2DPjefUsi7IB5MzFpDjDH9vw/XiTM+OZw== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/channel-websocket" "6.5.15" + "@storybook/client-api" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/preview-web" "6.5.15" + "@storybook/store" "6.5.15" + "@storybook/ui" "6.5.15" + airbnb-js-shims "^2.2.1" + ansi-to-html "^0.6.11" + core-js "^3.8.2" + global "^4.4.0" + lodash "^4.17.21" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + unfetch "^4.2.0" + util-deprecate "^1.0.2" + +"@storybook/core-common@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.15.tgz#3ab524c7abdae52024caeb5c0349a764cb08769f" + integrity sha512-uits9o6qwHTPnjsNZP25f7hWmUBGRJ7FXtxxtEaNSmtiwk50KWxBaro7wt505lJ1Gb9vOhpNPhS7y3IxdsXNmQ== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-decorators" "^7.12.12" + "@babel/plugin-proposal-export-default-from" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-private-property-in-object" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.12" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/preset-env" "^7.12.11" + "@babel/preset-react" "^7.12.10" + "@babel/preset-typescript" "^7.12.7" + "@babel/register" "^7.12.1" + "@storybook/node-logger" "6.5.15" + "@storybook/semver" "^7.3.2" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/pretty-hrtime" "^1.0.0" + babel-loader "^8.0.0" + babel-plugin-macros "^3.0.1" + babel-plugin-polyfill-corejs3 "^0.1.0" + chalk "^4.1.0" + core-js "^3.8.2" + express "^4.17.1" + file-system-cache "^1.0.5" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.0.4" + fs-extra "^9.0.1" + glob "^7.1.6" + handlebars "^4.7.7" + interpret "^2.2.0" + json5 "^2.1.3" + lazy-universal-dotenv "^3.0.1" + picomatch "^2.3.0" + pkg-dir "^5.0.0" + pretty-hrtime "^1.0.3" + resolve-from "^5.0.0" + slash "^3.0.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "4" + +"@storybook/core-events@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.15.tgz#c12f645b50231c50eb9b26038aa67ab92b1ba24e" + integrity sha512-B1Ba6l5W7MeNclclqMMTMHgYgfdpB5SIhNCQFnzIz8blynzRhNFMdxvbAl6Je5G0S4xydYYd7Lno2kXQebs7HA== + dependencies: + core-js "^3.8.2" + +"@storybook/core-server@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.15.tgz#217c40d7a33708b9ac69f73dd51ed9d2f031d19d" + integrity sha512-m+pZwHhCjwryeqTptyyKHSbIjnnPGKoRSnkqLTOpKQf8llZMnNQWUFrn4fx6UDKzxFQ2st2+laV8O2QbMs8qwQ== + dependencies: + "@discoveryjs/json-ext" "^0.5.3" + "@storybook/builder-webpack4" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/csf-tools" "6.5.15" + "@storybook/manager-webpack4" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.15" + "@storybook/telemetry" "6.5.15" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/node-fetch" "^2.5.7" + "@types/pretty-hrtime" "^1.0.0" + "@types/webpack" "^4.41.26" + better-opn "^2.1.1" + boxen "^5.1.2" + chalk "^4.1.0" + cli-table3 "^0.6.1" + commander "^6.2.1" + compression "^1.7.4" + core-js "^3.8.2" + cpy "^8.1.2" + detect-port "^1.3.0" + express "^4.17.1" + fs-extra "^9.0.1" + global "^4.4.0" + globby "^11.0.2" + ip "^2.0.0" + lodash "^4.17.21" + node-fetch "^2.6.7" + open "^8.4.0" + pretty-hrtime "^1.0.3" + prompts "^2.4.0" + regenerator-runtime "^0.13.7" + serve-favicon "^2.5.0" + slash "^3.0.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + watchpack "^2.2.0" + webpack "4" + ws "^8.2.3" + x-default-browser "^0.4.0" + +"@storybook/core@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.15.tgz#82e0998d908fc9e66a659e1217072c425d63f9b6" + integrity sha512-T9TjLxbb5P/XvLEoj0dnbtexJa0V3pqCifRlIUNkTJO0nU3PdGLMcKMSyIYWjkthAJ9oBrajnodV0UveM/epTg== + dependencies: + "@storybook/core-client" "6.5.15" + "@storybook/core-server" "6.5.15" + +"@storybook/csf-tools@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.15.tgz#dc5d0fe946c25d60bf201e5180c4fc81b24f763b" + integrity sha512-2LwSD7yE/ccXBc58K4vdKw/oJJg6IpC4WD51rBt2mAl5JUCkxhOq7wG/Z8Wy1lZw2LVuKNTmjVou5blGRI/bTg== + dependencies: + "@babel/core" "^7.12.10" + "@babel/generator" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/plugin-transform-react-jsx" "^7.12.12" + "@babel/preset-env" "^7.12.11" + "@babel/traverse" "^7.12.11" + "@babel/types" "^7.12.11" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/mdx1-csf" "^0.0.1" + core-js "^3.8.2" + fs-extra "^9.0.1" + global "^4.4.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/csf@0.0.2--canary.4566f4d.1": + version "0.0.2--canary.4566f4d.1" + resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz#dac52a21c40ef198554e71fe4d20d61e17f65327" + integrity sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ== + dependencies: + lodash "^4.17.15" + +"@storybook/docs-tools@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.15.tgz#c9a3954719c45c3748abd6aaa735e33f5c961912" + integrity sha512-8w78NFOtlJGuIa9vPPsr87J9iQUGmLFh7CrMS2+t9LxW+0oH5MZ8QqPQUHNuTuKsYN3r+QAmmi2pj0auZmCoKA== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.15" + core-js "^3.8.2" + doctrine "^3.0.0" + lodash "^4.17.21" + regenerator-runtime "^0.13.7" + +"@storybook/manager-webpack4@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.15.tgz#09808b87b510591390765af708ab511ff63a1e5c" + integrity sha512-zRvBTMoaFO6MvHDsDLnt3tsFENhpY3k+e/UIPdqbIDMbUPGGQzxJucAM9aS/kbVSO5IVs8IflVxbeeB/uTIIfA== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@storybook/addons" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/theming" "6.5.15" + "@storybook/ui" "6.5.15" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/webpack" "^4.41.26" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + chalk "^4.1.0" + core-js "^3.8.2" + css-loader "^3.6.0" + express "^4.17.1" + file-loader "^6.2.0" + find-up "^5.0.0" + fs-extra "^9.0.1" + html-webpack-plugin "^4.0.0" + node-fetch "^2.6.7" + pnp-webpack-plugin "1.6.4" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + style-loader "^1.3.0" + telejson "^6.0.8" + terser-webpack-plugin "^4.2.3" + ts-dedent "^2.0.0" + url-loader "^4.1.1" + util-deprecate "^1.0.2" + webpack "4" + webpack-dev-middleware "^3.7.3" + webpack-virtual-modules "^0.2.2" + +"@storybook/manager-webpack5@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.15.tgz#e17ae2538c7bd013e77597f20a3843e448d1cb5b" + integrity sha512-yrHVFUHGdVRWq/oGJwQu+UOZzxELH5SS+Lpn5oIQ/Dblam9piQC0KmBZtFuA9X8acaw4BBVnXgF/aiqs9fOp/Q== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@storybook/addons" "6.5.15" + "@storybook/core-client" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/theming" "6.5.15" + "@storybook/ui" "6.5.15" + "@types/node" "^14.0.10 || ^16.0.0" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + chalk "^4.1.0" + core-js "^3.8.2" + css-loader "^5.0.1" + express "^4.17.1" + find-up "^5.0.0" + fs-extra "^9.0.1" + html-webpack-plugin "^5.0.0" + node-fetch "^2.6.7" + process "^0.11.10" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + style-loader "^2.0.0" + telejson "^6.0.8" + terser-webpack-plugin "^5.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "^5.9.0" + webpack-dev-middleware "^4.1.0" + webpack-virtual-modules "^0.4.1" + +"@storybook/mdx1-csf@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@storybook/mdx1-csf/-/mdx1-csf-0.0.1.tgz#d4184e3f6486fade9f7a6bfaf934d9bc07718d5b" + integrity sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg== + dependencies: + "@babel/generator" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/preset-env" "^7.12.11" + "@babel/types" "^7.12.11" + "@mdx-js/mdx" "^1.6.22" + "@types/lodash" "^4.14.167" + js-string-escape "^1.0.1" + loader-utils "^2.0.0" + lodash "^4.17.21" + prettier ">=2.2.1 <=2.3.0" + ts-dedent "^2.0.0" + +"@storybook/node-logger@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.15.tgz#d99695e8d5f8cf434e8fdcca719b5b5fa5c88e2e" + integrity sha512-LQjjbfMuUXm7wZTBKb+iGeCNnej4r1Jb2NxG3Svu2bVhaoB6u33jHAcbmhXpXW1jghzW3kQwOU7BoLuJiRRFIw== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + core-js "^3.8.2" + npmlog "^5.0.1" + pretty-hrtime "^1.0.3" + +"@storybook/node-logger@^6.1.14": + version "6.4.14" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.4.14.tgz#2e96f4e3e06c78c3d065e59818515209122d9ae4" + integrity sha512-mowC0adx4hLtCqGMQKRfNmiRYAL2PYdk3ojc91qzIKNrjSYnE4U8d9qlw5WLx1PKEnZVji3+QiYfNHpA/8PoKw== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + core-js "^3.8.2" + npmlog "^5.0.1" + pretty-hrtime "^1.0.3" + +"@storybook/postinstall@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.15.tgz#90989c8049357a6df6f38c933172210c9c6caf8c" + integrity sha512-l7pApTgLD10OedNOyuf4vUdVCHLOSaIUIL9gdJl1WaSFHiUpJvvzBIh5M4aRICYPbnuExQc8y2GAjERKO4Ep+g== + dependencies: + core-js "^3.8.2" + +"@storybook/preview-web@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.15.tgz#5f47899dff1580ed3dc1b5a7bfdf67d6574536fc" + integrity sha512-gIHABSAD0JS0iRaG67BnSDq/q8Zf4fFwEWBQOSYgcEx2TzhAUeSkhGZUQHdlOTCwuA2PpXT0/cWDH8u2Ev+msg== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/channel-postmessage" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.15" + ansi-to-html "^0.6.11" + core-js "^3.8.2" + global "^4.4.0" + lodash "^4.17.21" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + unfetch "^4.2.0" + util-deprecate "^1.0.2" + +"@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0": + version "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" + resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0.tgz#3103532ff494fb7dc3cf835f10740ecf6a26c0f9" + integrity sha512-eVg3BxlOm2P+chijHBTByr90IZVUtgRW56qEOLX7xlww2NBuKrcavBlcmn+HH7GIUktquWkMPtvy6e0W0NgA5w== + dependencies: + debug "^4.1.1" + endent "^2.0.1" + find-cache-dir "^3.3.1" + flat-cache "^3.0.4" + micromatch "^4.0.2" + react-docgen-typescript "^2.1.1" + tslib "^2.0.0" + +"@storybook/react@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.15.tgz#83e645b16a4d241ec84a8d0015b1a7a2d55c5091" + integrity sha512-iQta2xOs/oK0sw/zpn3g/huvOmvggzi8z2/WholmUmmRiSQRo9lOhRXH0u13T4ja4fEa+u7m58G83xOG6i73Kw== + dependencies: + "@babel/preset-flow" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core" "6.5.15" + "@storybook/core-common" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/docs-tools" "6.5.15" + "@storybook/node-logger" "6.5.15" + "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.15" + "@types/estree" "^0.0.51" + "@types/node" "^14.14.20 || ^16.0.0" + "@types/webpack-env" "^1.16.0" + acorn "^7.4.1" + acorn-jsx "^5.3.1" + acorn-walk "^7.2.0" + babel-plugin-add-react-displayname "^0.0.5" + babel-plugin-react-docgen "^4.2.1" + core-js "^3.8.2" + escodegen "^2.0.0" + fs-extra "^9.0.1" + global "^4.4.0" + html-tags "^3.1.0" + lodash "^4.17.21" + prop-types "^15.7.2" + react-element-to-jsx-string "^14.3.4" + react-refresh "^0.11.0" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack ">=4.43.0 <6.0.0" + +"@storybook/router@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.15.tgz#bf01d35bdd4603bf188629a6578489e313a312fd" + integrity sha512-9t8rI8t7/Krolau29gsdjdbRQ66orONIyP0efp0EukVgv6reNFzb/U14ARrl0uHys6Tl5Xyece9FoakQUdn8Kg== + dependencies: + "@storybook/client-logger" "6.5.15" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + +"@storybook/semver@^7.3.2": + version "7.3.2" + resolved "https://registry.yarnpkg.com/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" + integrity sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg== + dependencies: + core-js "^3.6.5" + find-up "^4.1.0" + +"@storybook/source-loader@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.15.tgz#363a9b2812a2cef6cc5cb948e55d8a2624e18643" + integrity sha512-MaWzki40g0/7NWmJgUBhOp+e7y8Ohw6G/bRr/rcDP7eXSnud6ThYylXv0QqBScLPPTy8txjmBClCoqdLGyvLWQ== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + estraverse "^5.2.0" + global "^4.4.0" + loader-utils "^2.0.4" + lodash "^4.17.21" + prettier ">=2.2.1 <=2.3.0" + regenerator-runtime "^0.13.7" + +"@storybook/store@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.15.tgz#d6ca7a3165442aabfde335f243bdd179d53bca1a" + integrity sha512-r6cYTf6GtbqgdI4ZG3xuWdJAAu5fJ3xAWMiDkHyoK2u+R2TeYXIsJvgn0BPrW87sZhELIkg4ckdFECmATs3kpQ== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + slash "^3.0.0" + stable "^0.1.8" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/telemetry@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.15.tgz#852050c1e54bf704a104e47e4e498d999096e0e7" + integrity sha512-WHMRG6xMkEGscn1q4SotwzV8hxM1g3zHyXPN77iosY5zpOIn/qAzvkmW28V1DPH9jPWMZMizBgG1TIQvUpduXg== + dependencies: + "@storybook/client-logger" "6.5.15" + "@storybook/core-common" "6.5.15" + chalk "^4.1.0" + core-js "^3.8.2" + detect-package-manager "^2.0.1" + fetch-retry "^5.0.2" + fs-extra "^9.0.1" + global "^4.4.0" + isomorphic-unfetch "^3.1.0" + nanoid "^3.3.1" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + +"@storybook/theming@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.15.tgz#048461b37ad0c29dc8d91a065a6bf1c90067524c" + integrity sha512-pgdW0lVZKKXQ4VhIfLHycMmwFSVOY7vLTKnytag4Y8Yz+aXm0bwDN/QxPntFzDH47F1Rcy2ywNnvty8ooDTvuA== + dependencies: + "@storybook/client-logger" "6.5.15" + core-js "^3.8.2" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + +"@storybook/ui@6.5.15": + version "6.5.15" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.15.tgz#e4d80465116e53d48d2c3bb616909c37f5405181" + integrity sha512-OO+TWZmI8ebWA1C3JBKNvbUbsgvt4GppqsGlkf5CTBZrT/MzmMlYiooLAtlY1ZPcMtTd5ynLxvroHWBEnMOk2A== + dependencies: + "@storybook/addons" "6.5.15" + "@storybook/api" "6.5.15" + "@storybook/channels" "6.5.15" + "@storybook/client-logger" "6.5.15" + "@storybook/components" "6.5.15" + "@storybook/core-events" "6.5.15" + "@storybook/router" "6.5.15" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.15" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + +"@stripe/stripe-js@^1.44.1": + version "1.44.1" + resolved "https://registry.yarnpkg.com/@stripe/stripe-js/-/stripe-js-1.44.1.tgz#376fdbed2b394c84deaa2041b8029b97e7eab3a7" + integrity sha512-DKj3U6tS+sCNsSXsoZbOl5gDrAVD3cAZ9QCiVSykLC3iJo085kkmw/3BAACRH54Bq2bN34yySuH6G1SLh2xHXA== + +"@svgr/babel-plugin-add-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" + integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + +"@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" + integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" + integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" + integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + +"@svgr/babel-plugin-svg-dynamic-title@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" + integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + +"@svgr/babel-plugin-svg-em-dimensions@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" + integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + +"@svgr/babel-plugin-transform-react-native-svg@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" + integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + +"@svgr/babel-plugin-transform-svg-component@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" + integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + +"@svgr/babel-preset@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" + integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "^6.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "^6.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" + "@svgr/babel-plugin-transform-svg-component" "^6.2.0" + +"@svgr/core@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.0.tgz#187a7930695635382c1ab42f476a1d4d45a65994" + integrity sha512-n5PrYAPoTpWGykqa8U05/TVTHOrVR/TxrUJ5EWHP9Db6vR3qnqzwAVLiFT1+slA7zQoJTXafQb+akwThf9SxGw== + dependencies: + "@svgr/plugin-jsx" "^6.2.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.1" + +"@svgr/hast-util-to-babel-ast@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.0.0.tgz#423329ad866b6c169009cc82b5e28ffee80c857c" + integrity sha512-S+TxtCdDyRGafH1VG1t/uPZ87aOYOHzWL8kqz4FoSZcIbzWA6rnOmjNViNiDzqmEpzp2PW5o5mZfvC9DiVZhTQ== + dependencies: + "@babel/types" "^7.15.6" + entities "^3.0.1" + +"@svgr/plugin-jsx@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.0.tgz#5e41a75b12b34cb66509e63e535606161770ff42" + integrity sha512-QJDEe7K5Hkd4Eewu4pcjiOKTCtjB47Ol6lDLXVhf+jEewi+EKJAaAmM+bNixfW6LSNEg8RwOYQN3GZcprqKfHw== + dependencies: + "@babel/core" "^7.15.5" + "@svgr/babel-preset" "^6.2.0" + "@svgr/hast-util-to-babel-ast" "^6.0.0" + svg-parser "^2.0.2" + +"@svgr/plugin-svgo@^6.2.0": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz#4cbe6a33ccccdcae4e3b63ded64cc1cbe1faf48c" + integrity sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q== + dependencies: + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.5.0" + +"@svgr/webpack@^6.1.2": + version "6.2.0" + resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.0.tgz#00fafd32e1d59add7b554c40aa2e97e83f975686" + integrity sha512-KlLdGe93A8GDs19g8kjEmHwArgMAP6cUfegr2Nx+yDAYY32IPtjzm3SoqNP+I+cnOF1CToJu1clWTPEmdd8dXg== + dependencies: + "@babel/core" "^7.15.5" + "@babel/plugin-transform-react-constant-elements" "^7.14.5" + "@babel/preset-env" "^7.15.6" + "@babel/preset-react" "^7.14.5" + "@babel/preset-typescript" "^7.15.0" + "@svgr/core" "^6.2.0" + "@svgr/plugin-jsx" "^6.2.0" + "@svgr/plugin-svgo" "^6.2.0" + +"@swc/helpers@0.4.14": + version "0.4.14" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" + integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== + dependencies: + tslib "^2.4.0" + +"@szmarczak/http-timer@^4.0.5": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" + integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== + dependencies: + defer-to-connect "^2.0.0" + +"@tailwindcss/forms@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.3.tgz#e4d7989686cbcaf416c53f1523df5225332a86e7" + integrity sha512-y5mb86JUoiUgBjY/o6FJSFZSEttfb3Q5gllE4xoKjAAD+vBrnIhE4dViwUuow3va8mpH4s9jyUbUbrRGoRdc2Q== + dependencies: + mini-svg-data-uri "^1.2.3" + +"@tailwindcss/line-clamp@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz#f353c5a8ab2c939c6267ac5b907f012e5ee130f9" + integrity sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw== + +"@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/match-sorter-utils@^8.7.0": + version "8.7.0" + resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.7.0.tgz#60b09a6d3d7974d5f86f1318053c1bd5a85fb0be" + integrity sha512-OgfIPMHTfuw9JGcXCCoEHWFP/eSP2eyhCYwkrFnWBM3NbUPAgOlFP11DbM7cozDRVB0XbPr1tD4pLAtWKlVUVg== + dependencies: + remove-accents "0.4.2" + +"@tanstack/query-core@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.19.1.tgz#2e92d9e8a50884eb231c5beb4386e131ebe34306" + integrity sha512-Zp0aIose5C8skBzqbVFGk9HJsPtUhRVDVNWIqVzFbGQQgYSeLZMd3Sdb4+EnA5wl1J7X+bre2PJGnQg9x/zHOA== + +"@tanstack/react-query-devtools@^4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.19.1.tgz#850058df8dba932362838c17f566bd717044449b" + integrity sha512-U63A+ly9JLPJj7ryR9omdXT3n+gS7jlExrHty4klsd/6xdUhC38CKZyZ0Gi3vctaVYRGTU8/vI+uKzBYdFqLaA== + dependencies: + "@tanstack/match-sorter-utils" "^8.7.0" + superjson "^1.10.0" + use-sync-external-store "^1.2.0" + +"@tanstack/react-query@^4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.19.1.tgz#43356dd537127e76d75f5a2769eb23dafd9a3690" + integrity sha512-5dvHvmc0vrWI03AJugzvKfirxCyCLe+qawrWFCXdu8t7dklIhJ7D5ZhgTypv7mMtIpdHPcECtCiT/+V74wCn2A== + dependencies: + "@tanstack/query-core" "4.19.1" + use-sync-external-store "^1.2.0" + +"@tanstack/react-table@^8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.3.0.tgz#55e8a5dbe65f8e56dbeeb5033e1c679219d5991a" + integrity sha512-SIMorxHQ68glcoYZO7KSqvyy5ldlCKRTvYBSnWbWvy4xSgRa2D7GGWq8rLPsMerNUYoEljokun3/mf7kP8FQQA== + dependencies: + "@tanstack/table-core" "8.3.0" + +"@tanstack/table-core@8.3.0": + version "8.3.0" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.3.0.tgz#2dae537cc335a0da4ec4ddc60e8e868f3d9aca4a" + integrity sha512-xcspAAv+GN+WMrX6qaFrPxBVYxlFfyu4qddiTwXOaTQOLdgLP/3GbipDoL7gC2l3Iz9KFCDXWjhW79xta3w5rg== + +"@testing-library/dom@^8.5.0": + version "8.16.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.16.0.tgz#d6fc50250aed17b1035ca1bd64655e342db3936a" + integrity sha512-uxF4zmnLHHDlmW4l+0WDjcgLVwCvH+OVLpD8Dfp+Bjfz85prwxWGbwXgJdLtkgjD0qfOzkJF9SmA6YZPsMYX4w== + 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.2": + version "5.16.2" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.2.tgz#f329b36b44aa6149cd6ced9adf567f8b6aa1c959" + integrity sha512-6ewxs1MXWwsBFZXIk4nKKskWANelkdUehchEOokHsN8X7c2eKXGw+77aRV63UU8f/DTSVUPLaGxdrj4lN7D/ug== + dependencies: + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css "^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" + +"@testing-library/user-event@^13.2.1": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== + dependencies: + "@babel/runtime" "^7.12.5" + +"@tippyjs/react@^4.2.6": + version "4.2.6" + resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.6.tgz#971677a599bf663f20bb1c60a62b9555b749cc71" + integrity sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw== + dependencies: + tippy.js "^6.3.1" + +"@tiptap/core@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.209.tgz#fd0edb8f87a5eea70c6a11e0d7e628738a10ef3f" + integrity sha512-DOOzfo2XKD5Qt2oEGW33/6ugwSnvpl4WbxtlKdPadLoApk6Kja3K1Eps3pihBgIGmo4tkctkCzmj8wNWS7KeWg== + +"@tiptap/extension-blockquote@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.209.tgz#553a620007bbf039167036eab434a354934a7ceb" + integrity sha512-ay5c+SJ1vQOL5zpsr94jN15tCt0ytd7zPMM433pkhi9ZL0qqf1fZ+D0KzDs2z8N49rfArVpoo238V3ZChBh2sA== + +"@tiptap/extension-bold@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.209.tgz#27268311f3fd53af8e658e2fd0d65f2e43130268" + integrity sha512-8jaoZSe55iwuEvwdM1mPhlgE+/tDyveECv0d1qogUcbPdIkhDQaNlIOmuH9Ftr465iIDthMjt4GB6AWi5tfsMg== + +"@tiptap/extension-bubble-menu@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.209.tgz#dcef008f84286c7719f43e54ebdd7de778837974" + integrity sha512-tceZAuDpy3J96uGyCzpJFD3fHABJDTJTq5E0hm+TRQT+eVGVqZI0PE3/4yVFgkCshioTuJq8veMDFcqNsSkKsQ== + dependencies: + "@tiptap/core" "^2.0.0-beta.209" + lodash "^4.17.21" + prosemirror-state "^1.4.1" + prosemirror-view "^1.28.2" + tippy.js "^6.3.7" + +"@tiptap/extension-bullet-list@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.209.tgz#86e5023d330ce0dc39e42083ec839af2dbec51bc" + integrity sha512-NGoSYakXCiKb5xrVe339Acu2iherOGQUR1bAeWgOKf+dINvIdjawnud6fIeB3n1h95aDvsmYuH1o9B+/bd7e3w== + +"@tiptap/extension-code-block@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.209.tgz#653043d94293e546289912bac3ded75637980d5d" + integrity sha512-FlMud3yhAilHrcHbW4iUEagAdvpOJW1lTSiiDfbtVpyybjNJQQMa5zhSKi4blG2xBEGXZhqL0XuWDGERNsVawQ== + +"@tiptap/extension-code@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.0.0-beta.209.tgz#dda857528043c5aa4c451bb9f63731aa809182cb" + integrity sha512-LCcfQMroYps6o9ASpVZqYbbdTkSwxTokjmkkKKmWZlZSJ/h+1kThOnRZgcPkfSeaaC30T+LSxAXXyf1dMgl5+Q== + +"@tiptap/extension-document@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.0.0-beta.209.tgz#7bb4025737a5361c06d52e56406d7e0649387e88" + integrity sha512-ZRTC5j0J6fNTtIcU6UnxJm5KZrfJI2pygCJ172mMNzwE89upJMhRSP0CvPWTY7nf0odmQTJ5vD99QDR4CdOTng== + +"@tiptap/extension-dropcursor@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.209.tgz#5107e255a4c1c5ca3dae606854019d6ec6051481" + integrity sha512-b4RxbZg4hza4p1Lp+m4CWkIIMVgoAKSo49OyvO/Y/igtQ0DcdQutSJDEPeEhuqy+jPdQFaU5GBonSvVi89Loog== + +"@tiptap/extension-floating-menu@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.209.tgz#1c5f7ffca9fdd2d272069949345c3b888ea57049" + integrity sha512-m5ucAguqDxuOvNcsmvuSLcN8TMkbhFmiC6dTJOyaAGjGn6d8Ly6aZh+lEwU228TebM0TKHTp8Xob1cLjV4TGgg== + dependencies: + tippy.js "^6.3.7" + +"@tiptap/extension-gapcursor@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.209.tgz#91eadf2863cfd6ba0325d378aff49af95c2783b5" + integrity sha512-F03mr2VV5bZycIVWHCIYpQTzs9tC+goWJFhbJgPrT62f1gUAnlc1ZRc79mSqw1AxTsfbDvAc65OlUJb0QfxDWA== + +"@tiptap/extension-hard-break@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.209.tgz#9e845bb69d7707fd041701513a096302073cb94f" + integrity sha512-BS0z9SshfJ5ESssiVaVe61901BrTLCAgxc9NPmi4Va2sszXJysI2Vm8q4jDHL6IehkCQpQZNAihT9eSBPHQR0w== + +"@tiptap/extension-heading@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.209.tgz#7ae7e3f1adac7eaad20dcb30effb4b25f53a453d" + integrity sha512-eqq9if0XsPjLvivM5gNUqSHj5I4Zpiv66NPO+pM4ig0Wq2CjjxWzzgmdSLfTPGRfsZe9kPCOgO86AAB07am3fQ== + +"@tiptap/extension-history@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.0-beta.209.tgz#4d082a92bd434232b3d3db6584b8dd499086f343" + integrity sha512-P5nw+r47gBdac4igeaBvW6gxsZUnS67SRgbAyQSmXVe45NXc1t0EUb2Be9YuHRKDVxhJUhGT8NawPY70Fgk4mQ== + +"@tiptap/extension-horizontal-rule@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.209.tgz#06d8c9ca43933e338fca31963332639d8dcbe7e8" + integrity sha512-53RU9kDVb1jowJ3Frx8QW0E05uEOCpeG3HfUCMjz8anGtefxFtMS7xYZ9sC+niJeVmXC+mUSjFGageL4iRIdqA== + +"@tiptap/extension-italic@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.209.tgz#892ff90de43a5e1e88197e7a9839bbd877027321" + integrity sha512-KnRdbqfD01tcCnUNypA3TX3FqmQSFwu7/9YU3vwS8Zyaz+OIc/g/vJai5twg1DzFAvIcYWzRFPTFcqkjwkcW1w== + +"@tiptap/extension-list-item@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.209.tgz#d7d6b5c3dce4c048d7216e307c2b2234a5032b65" + integrity sha512-qkHwymyGfXIVAiqLXvL66UzGLhYpD2BYbSSAIQ6Rmuvk4aeNrsBvFv9tL7+YsYLKvlOa4+Q+PN2uhST+lOH0hw== + +"@tiptap/extension-ordered-list@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.209.tgz#d22539313c3fb5879d5ee8cd0bab5aff78a6353c" + integrity sha512-PhJ9uqxqKVO97rb2MzW/TzQJ9XQicp9gsV/y0QbAEv1ZOH9QI/qF5sCe6BfeN8ZoMyYUEh6de3yxQL8iXSFWsw== + +"@tiptap/extension-paragraph@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.209.tgz#6c26e94eebfd0207e14601691c121dbf68fc17c2" + integrity sha512-XkiguVbOX/KJwux2wdurvZRwG1UulpZ3Uhw7Yl59sLBf7YDw8H781EMgVvaLSWf3B1o27/yOyc+kiepW/Pp9Wg== + +"@tiptap/extension-placeholder@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.0-beta.209.tgz#c27f81970db8075a8d5005c9512930b1998ab5e0" + integrity sha512-L94thCy0qECMP1mcDjvN7P2RtMT7cc1IB6FbB+C1gnDaPXlJbiZFGsnuyjcCfoVX1rFZXBb6Ud0aSG0VaXWQCQ== + +"@tiptap/extension-strike@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.209.tgz#f601b07e186f9079cadff7f29e29107909f5478c" + integrity sha512-k8yaeyMYBzdq5U1zv5DYZt3KtpglPHV2JX7dYfNyoFpiX+6IJ2EwSuTXUGilZGRpyUw6UxeDF0yJbiOGMeEIDA== + +"@tiptap/extension-text@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.209.tgz#f34a6cda849c16d7dbf9677e2cf6242cc6987a9e" + integrity sha512-12PTPTQViDR7xDLwxGMPiYaV89E9olH/+4Zfoh6QiOjHqhmgYu3+/c8YZ3eARgXnfpy/EzUD0PBxiAyDZJ1vdw== + +"@tiptap/react@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/react/-/react-2.0.0-beta.209.tgz#8a547b2d48f39a285dbe83d1e38f035f688bfa12" + integrity sha512-iuJ+hgaSPETPMX39QpX6e0tZmFAj9azl0qGhNm6NNB1biCehkB4qMfcfwecWFRWVpZKG5UtjJvjJ3UZM167Jlg== + dependencies: + "@tiptap/extension-bubble-menu" "^2.0.0-beta.209" + "@tiptap/extension-floating-menu" "^2.0.0-beta.209" + prosemirror-view "^1.28.2" + +"@tiptap/starter-kit@^2.0.0-beta.209": + version "2.0.0-beta.209" + resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.209.tgz#cc21d50b826abf02c97ea426f8fb04cf516f8606" + integrity sha512-uR68ZfDZ5PeygGey3xc9ZuFIP+K7VRElrABnZcM6t9/Crrs70UFwSTNlkS0ezx9woj8h+8N78a6r8W1YC04TOw== + dependencies: + "@tiptap/core" "^2.0.0-beta.209" + "@tiptap/extension-blockquote" "^2.0.0-beta.209" + "@tiptap/extension-bold" "^2.0.0-beta.209" + "@tiptap/extension-bullet-list" "^2.0.0-beta.209" + "@tiptap/extension-code" "^2.0.0-beta.209" + "@tiptap/extension-code-block" "^2.0.0-beta.209" + "@tiptap/extension-document" "^2.0.0-beta.209" + "@tiptap/extension-dropcursor" "^2.0.0-beta.209" + "@tiptap/extension-gapcursor" "^2.0.0-beta.209" + "@tiptap/extension-hard-break" "^2.0.0-beta.209" + "@tiptap/extension-heading" "^2.0.0-beta.209" + "@tiptap/extension-history" "^2.0.0-beta.209" + "@tiptap/extension-horizontal-rule" "^2.0.0-beta.209" + "@tiptap/extension-italic" "^2.0.0-beta.209" + "@tiptap/extension-list-item" "^2.0.0-beta.209" + "@tiptap/extension-ordered-list" "^2.0.0-beta.209" + "@tiptap/extension-paragraph" "^2.0.0-beta.209" + "@tiptap/extension-strike" "^2.0.0-beta.209" + "@tiptap/extension-text" "^2.0.0-beta.209" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@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== + +"@transloadit/prettier-bytes@0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz#cdb5399f445fdd606ed833872fa0cabdbc51686b" + integrity sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA== + +"@transloadit/prettier-bytes@0.0.9": + version "0.0.9" + resolved "https://registry.yarnpkg.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.9.tgz#8d3146f75fd9d3c544cb63ec7dbdeb6670d3e2d7" + integrity sha512-pCvdmea/F3Tn4hAtHqNXmjcixSaroJJ+L3STXlYJdir1g1m2mRQpWbN8a4SvgQtaw2930Ckhdx8qXdXBFMKbAA== + +"@trpc/client@^10.4.3": + version "10.4.3" + resolved "https://registry.yarnpkg.com/@trpc/client/-/client-10.4.3.tgz#a0d9963481ac3ac6f3939f827654cdaa3a0c5fda" + integrity sha512-VMdyLHr6PG2FlMxnbPt/6jniDBxv3Ys1hFhk0AriFDD9h4Lg9fznwGn5RI1eV5RCOAoCQ/+ZwPMSKOMBhiycTw== + +"@trpc/next@^10.4.3": + version "10.4.3" + resolved "https://registry.yarnpkg.com/@trpc/next/-/next-10.4.3.tgz#9ae8c50a79c90ea3e3b102c63f52b6f56b97bc35" + integrity sha512-H4sM3lrtA2seFFkgXcW3ZQLCqGS7YqJNYDQx8FOlvcoH+klmQ+UNNgurdP2R/i50nGFwc4aN9kENrsvq0xfH5A== + dependencies: + react-ssr-prepass "^1.5.0" + +"@trpc/react-query@^10.4.3": + version "10.4.3" + resolved "https://registry.yarnpkg.com/@trpc/react-query/-/react-query-10.4.3.tgz#53aa3505f9ff5e2522c92b41f02f6288a1b4bea5" + integrity sha512-gm8hnV8Q7RPc0am0paSuu30+jk9cQoJhKByAdrTRoLUEkuDb7r78sebN6RM3OYMPfWfSBGutIoWHkjJzP2kdtw== + +"@trpc/server@^10.4.3": + version "10.4.3" + resolved "https://registry.yarnpkg.com/@trpc/server/-/server-10.4.3.tgz#8db5dc9a5082feea593d58b845ceef6a1adaf3d9" + integrity sha512-U+TVFtMUnySUfKHXjdpdZdFS268naGafbjQiG/H7t0OKpiLnbJ4IhgyYzj82Hvghb6Ow9E+UTNt6ALP7KoN4/g== + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" + integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + +"@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/auth0@^2.35.7": + version "2.35.7" + resolved "https://registry.yarnpkg.com/@types/auth0/-/auth0-2.35.7.tgz#962b938c8c2dd18cef04ecae1de9196969647bdb" + integrity sha512-OajDRFP2eKwkO4olLAO1HJhK7mGQbGZSMRtDxNgomzqwPpcRJU5za1zvPLpSLUt8HGzdoy/MyWiVdQgkdS2+rQ== + +"@types/babel__core@^7.1.14": + version "7.1.18" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.18.tgz#1a29abcc411a9c05e2094c98f9a1b7da6cdf49f8" + integrity sha512-S7unDjm/C7z2A2R9NzfKCK1I+BAALDtxEmsJBwlB3EzNfb929ykjL++1CK9LO++EIp2fQrC8O+BwjKvz6UeDyQ== + 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.14.2" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.14.2.tgz#ffcd470bbb3f8bf30481678fb5502278ca833a43" + integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/cacheable-request@^6.0.1": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9" + integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA== + dependencies: + "@types/http-cache-semantics" "*" + "@types/keyv" "*" + "@types/node" "*" + "@types/responselike" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cors@^2.8.12": + version "2.8.12" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" + integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== + +"@types/crypto-js@^4.1.1": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.1.1.tgz#602859584cecc91894eb23a4892f38cfa927890d" + integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA== + +"@types/d3-array@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.3.tgz#87d990bf504d14ad6b16766979d04e943c046dac" + integrity sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ== + +"@types/d3-color@^1": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-1.4.2.tgz#944f281d04a0f06e134ea96adbb68303515b2784" + integrity sha512-fYtiVLBYy7VQX+Kx7wU/uOIkGQn8aAEY8oWMoyja3N4dLd8Yf6XgSIR/4yWvMuveNOH5VShnqCgRqqh/UNanBA== + +"@types/d3-interpolate@^1.3.1": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-1.4.2.tgz#88902a205f682773a517612299a44699285eed7b" + integrity sha512-ylycts6llFf8yAEs1tXzx2loxxzDZHseuhPokrqKprTQSTcD3JbJI1omZP1rphsELZO3Q+of3ff0ZS7+O6yVzg== + dependencies: + "@types/d3-color" "^1" + +"@types/d3-path@^1", "@types/d3-path@^1.0.8": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.9.tgz#73526b150d14cd96e701597cbf346cfd1fd4a58c" + integrity sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ== + +"@types/d3-scale@^3.3.0": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-3.3.2.tgz#18c94e90f4f1c6b1ee14a70f14bfca2bd1c61d06" + integrity sha512-gGqr7x1ost9px3FvIfUMi5XA/F/yAf4UkUDtdQhpH92XCT0Oa7zkkRzY61gPVJq+DxpHn/btouw5ohWkbBsCzQ== + dependencies: + "@types/d3-time" "^2" + +"@types/d3-shape@^1.3.1": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.8.tgz#c3c15ec7436b4ce24e38de517586850f1fea8e89" + integrity sha512-gqfnMz6Fd5H6GOLYixOZP/xlrMtJms9BaS+6oWxTKHNqPGZ93BkWWupQSCYm6YHqx6h9wjRupuJb90bun6ZaYg== + dependencies: + "@types/d3-path" "^1" + +"@types/d3-time@^2", "@types/d3-time@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-2.1.1.tgz#743fdc821c81f86537cbfece07093ac39b4bc342" + integrity sha512-9MVYlmIgmRR31C5b4FVSWtuMmBHh2mOWQYfl7XAYOa8dsnb7iEmUmRSWSFgXFtkjxO65d7hTUHQC+RhR/9IWFg== + +"@types/eslint-scope@^3.7.0": + version "3.7.3" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" + integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.1.tgz#c48251553e8759db9e656de3efc846954ac32304" + integrity sha512-GE44+DNEyxxh2Kc6ro/VkIj+9ma0pO0bwv9+uHSyBrikYOHr8zYcdPvnBOp1aw8s+CjRvuSx7CyWqRrNFQ59mA== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^0.0.50": + version "0.0.50" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" + integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/express-jwt@0.0.42": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.42.tgz#4f04e1fadf9d18725950dc041808a4a4adf7f5ae" + integrity sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag== + dependencies: + "@types/express" "*" + "@types/express-unless" "*" + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.28" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8" + integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express-unless@*": + version "0.5.2" + resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.5.2.tgz#07e29883d280778588644b03563d8796f870f20e" + integrity sha512-Q74UyYRX/zIgl1HSp9tUX2PlG8glkVm+59r7aK4KGKzC5jqKIOX6rrVLRQrzpZUQ84VukHtRoeAuon2nIssHPQ== + dependencies: + "@types/express" "*" + +"@types/express@*": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/express@4.17.14", "@types/express@^4.17.13", "@types/express@^4.17.14": + version "4.17.14" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.14.tgz#143ea0557249bc1b3b54f15db4c81c3d4eb3569c" + integrity sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/fs-extra@^8.0.1": + version "8.1.2" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.2.tgz#7125cc2e4bdd9bd2fc83005ffdb1d0ba00cca61f" + integrity sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg== + dependencies: + "@types/node" "*" + +"@types/glob@*", "@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.2", "@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/html-minifier-terser@^5.0.0": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" + integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/http-proxy@^1.17.8": + version "1.17.8" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.8.tgz#968c66903e7e42b483608030ee85800f22d03f55" + integrity sha512-5kPLG5BKpWYkw/LVOGWpiq3nEVqxiN32rTgI53Sk12/xHFQ2rG3ehI9IO+O3W2QoKeyB92dJkoka8SUm6BX1pA== + dependencies: + "@types/node" "*" + +"@types/is-ci@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3" + integrity sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ== + dependencies: + ci-info "^3.1.0" + +"@types/is-function@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.1.tgz#2d024eace950c836d9e3335a66b97960ae41d022" + integrity sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q== + +"@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 "27.4.0" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.0.tgz#037ab8b872067cae842a320841693080f9cb84ed" + integrity sha512-gHl8XuC1RZ8H2j5sHv/JqsaxXkDDM9iDOgu0Wp8sjs4u/snb2PVehyWXJPr+ORA0RPpgw231mnutWI1+0hgjIQ== + dependencies: + jest-diff "^27.0.0" + pretty-format "^27.0.0" + +"@types/jest@28.1.1": + version "28.1.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.1.tgz#8c9ba63702a11f8c386ee211280e8b68cb093cd1" + integrity sha512-C2p7yqleUKtCkVjlOur9BWVA4HgUQmEj/HWCt5WzZ5mLXrWnyIfl0wGuArc+kBXsy0ZZfLp+7dywB4HtSVYGVA== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + +"@types/jsdom@^16.2.4": + version "16.2.15" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6" + integrity sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ== + dependencies: + "@types/node" "*" + "@types/parse5" "^6.0.3" + "@types/tough-cookie" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.9": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= + +"@types/jsonwebtoken@^8.5.8": + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz#01b39711eb844777b7af1d1f2b4cf22fda1c0c44" + integrity sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A== + dependencies: + "@types/node" "*" + +"@types/jsonwebtoken@^8.5.9": + version "8.5.9" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz#2c064ecb0b3128d837d2764aa0b117b0ff6e4586" + integrity sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg== + dependencies: + "@types/node" "*" + +"@types/keyv@*": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41" + integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== + dependencies: + "@types/node" "*" + +"@types/lodash@^4.14.167", "@types/lodash@^4.14.182": + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== + +"@types/lodash@^4.14.172": + version "4.14.178" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.178.tgz#341f6d2247db528d4a13ddbb374bcdc80406f4f8" + integrity sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw== + +"@types/luxon@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.1.0.tgz#b9842d233a06b267fe4117f1c224f20b8a149825" + integrity sha512-gCd/HcCgjqSxfMrgtqxCgYk/22NBQfypwFUG7ZAyG/4pqs51WLTcUzVp1hqTbieDYeHS3WoVEh2Yv/2l+7B0Vg== + +"@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/mime-types@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.1.tgz#d9ba43490fa3a3df958759adf69396c3532cf2c1" + integrity sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw== + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + +"@types/morgan@^1.9.3": + version "1.9.3" + resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.3.tgz#ae04180dff02c437312bc0cfb1e2960086b2f540" + integrity sha512-BiLcfVqGBZCyNCnCH3F4o2GmDLrpy0HeBVnNlyZG4fo88ZiE9SoiBe3C+2ezuwbjlEyT+PDZ17//TAlRxAn75Q== + dependencies: + "@types/node" "*" + +"@types/multer@^1.4.7": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@types/multer/-/multer-1.4.7.tgz#89cf03547c28c7bbcc726f029e2a76a7232cc79e" + integrity sha512-/SNsDidUFCvqqcWDwxv2feww/yqhNeTRL5CVoL3jU4Goc4kKEL10T7Eye65ZqPNi4HRx8sAEX59pV1aEH7drNA== + dependencies: + "@types/express" "*" + +"@types/node-fetch@^2.5.7": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" + integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" + integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== + +"@types/node@18.11.9", "@types/node@>=8.1.0": + 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/node@^14.0.1": + version "14.18.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.23.tgz#70f5f20b0b1b38f696848c1d3647bb95694e615e" + integrity sha512-MhbCWN18R4GhO8ewQWAFK4TGQdBpXWByukz7cWyJmXhvRuCIaM/oWytGPqVmDzgEnnaIc9ss6HbU5mUi+vyZPA== + +"@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": + version "16.11.45" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.45.tgz#155b13a33c665ef2b136f7f245fa525da419e810" + integrity sha512-3rKg/L5x0rofKuuUt5zlXzOnKyIHXmIu5R8A0TuNDMF2062/AOIDBciFIjToLEJ/9F9DzkHNot+BpNsMI1OLdQ== + +"@types/node@^14.14.31": + version "14.18.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.11.tgz#9bd810a959e1728d78df0f68b5c825b8ea7156f4" + integrity sha512-zCoCEMA+IPpsRkyCFBqew5vGb7r8RSiB3uwdu/map7uwLAfu1MTazW26/pUDWoNnF88vJz4W3U56i5gtXNqxGg== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/npmlog@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6" + integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/parse5@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + +"@types/pg@^8.6.5": + version "8.6.5" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.5.tgz#2dce9cb468a6a5e0f1296a59aea3ac75dd27b702" + integrity sha512-tOkGtAqRVkHa/PVZicq67zuujI4Oorfglsr2IbKofDwBSysnaqSx7W1mDqFqdkGE6Fbgh+PZAl0r/BWON/mozw== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^2.2.0" + +"@types/prettier@^2.1.5": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.3.tgz#a3c65525b91fca7da00ab1a3ac2b5a2a4afbffbf" + integrity sha512-QzSuZMBuG5u8HqYz01qtMdg/Jfctlnvj1z/lYnIDXs/golxw0fxtRAHd9KrzjR7Yxz1qVeI00o0kiO3PmVdJ9w== + +"@types/pretty-hrtime@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" + integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== + +"@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/qs@*", "@types/qs@^6.9.5": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-dom@*": + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.11.tgz#e1eadc3c5e86bdb5f7684e00274ae228e7bcc466" + integrity sha512-f96K3k+24RaLGVu/Y2Ng3e1EbZ8/cVJvypZWd7cy0ofCBaf2lcM46xNhycMZ2xGwbBjRql7hOlZ+e2WlJ5MH3Q== + dependencies: + "@types/react" "*" + +"@types/react-dom@18.0.9": + version "18.0.9" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.9.tgz#ffee5e4bfc2a2f8774b15496474f8e7fe8d0b504" + integrity sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg== + dependencies: + "@types/react" "*" + +"@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-infinite-scroller@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/react-infinite-scroller/-/react-infinite-scroller-1.2.3.tgz#b8dcb0e5762c3f79cc92e574d2c77402524cab71" + integrity sha512-l60JckVoO+dxmKW2eEG7jbliEpITsTJvRPTe97GazjF5+ylagAuyYdXl8YY9DQsTP9QjhqGKZROknzgscGJy0A== + dependencies: + "@types/react" "*" + +"@types/react-ranger@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/react-ranger/-/react-ranger-2.0.1.tgz#63f1b7d71b7b3e666a1a62a0482470d965b66aa6" + integrity sha512-q+vEB4N38b/x3jpqUns05/7QoggT/A3WKVD7X153InQdlhuwYEZ1KxrlNoJLrMpQvB6sDru8+Gq9bGcndU6yMA== + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.0.15" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.15.tgz#d355644c26832dc27f3e6cbf0c4f4603fc4ab7fe" + integrity sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow== + 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/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + +"@types/responselike@*", "@types/responselike@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@types/retry@^0.12.0": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.1.tgz#d8f1c0d0dc23afad6dc16a9e993a0865774b4065" + integrity sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g== + +"@types/sanitize-html@^2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-2.8.0.tgz#c53d3114d832734fc299568a3458a49f9edc1eef" + integrity sha512-Uih6caOm3DsBYnVGOYn0A9NoTNe1c4aPStmHC/YA2JrpP9kx//jzaRcIklFvSpvVQEcpl/ZCr4DgISSf/YxTvg== + dependencies: + htmlparser2 "^8.0.0" + +"@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/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@^1.13.10": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" + integrity sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg== + dependencies: + "@types/mime" "*" + "@types/node" "*" + +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== + +"@types/sizzle@^2.3.2": + version "2.3.3" + resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" + integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + +"@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/tapable@^1", "@types/tapable@^1.0.5": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" + integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.2" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.2.tgz#564fb2b2dc827147e937a75b639a05d17ce18b44" + integrity sha512-vehbtyHUShPxIa9SioxDwCvgxukDMH//icJG90sXQBUm5lJOHLT5kNeU9tnivhnA/TkOFMzGIXN2cTc4hY8/kg== + 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/uglify-js@*": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.16.0.tgz#2cf74a0e6ebb6cd54c0d48e509d5bd91160a9602" + integrity sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g== + dependencies: + source-map "^0.6.1" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + 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/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + +"@types/webpack-env@^1.16.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" + integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw== + +"@types/webpack-sources@*": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" + integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.7.3" + +"@types/webpack@^4.41.26", "@types/webpack@^4.41.8": + version "4.41.32" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212" + integrity sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg== + dependencies: + "@types/node" "*" + "@types/tapable" "^1" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + anymatch "^3.0.0" + source-map "^0.6.0" + +"@types/ws@^8.5.1": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "20.2.1" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + +"@types/yargs@^15.0.0": + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.13" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76" + integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg== + dependencies: + "@types/yargs-parser" "*" + +"@types/yauzl@^2.9.1": + version "2.9.2" + resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a" + integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA== + dependencies: + "@types/node" "*" + +"@types/zxcvbn@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.1.tgz#46e42cbdcee681b22181478feaf4af2bc4c1abd2" + integrity sha512-3NoqvZC2W5gAC5DZbTpCeJ251vGQmgcWIHQJGq2J240HY6ErQ9aWKkwfoKJlHLx+A83WPNTZ9+3cd2ILxbvr1w== + +"@typescript-eslint/eslint-plugin@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz#4a5248eb31b454715ddfbf8cfbf497529a0a78bc" + integrity sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA== + dependencies: + "@typescript-eslint/scope-manager" "5.43.0" + "@typescript-eslint/type-utils" "5.43.0" + "@typescript-eslint/utils" "5.43.0" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@5.43.0", "@typescript-eslint/parser@^5.42.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.43.0.tgz#9c86581234b88f2ba406f0b99a274a91c11630fd" + integrity sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug== + dependencies: + "@typescript-eslint/scope-manager" "5.43.0" + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/typescript-estree" "5.43.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz#566e46303392014d5d163704724872e1f2dd3c15" + integrity sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw== + dependencies: + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/visitor-keys" "5.43.0" + +"@typescript-eslint/type-utils@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz#91110fb827df5161209ecca06f70d19a96030be6" + integrity sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg== + dependencies: + "@typescript-eslint/typescript-estree" "5.43.0" + "@typescript-eslint/utils" "5.43.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.43.0.tgz#e4ddd7846fcbc074325293515fa98e844d8d2578" + integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg== + +"@typescript-eslint/typescript-estree@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz#b6883e58ba236a602c334be116bfc00b58b3b9f2" + integrity sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg== + dependencies: + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/visitor-keys" "5.43.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.43.0", "@typescript-eslint/utils@^5.36.1": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.43.0.tgz#00fdeea07811dbdf68774a6f6eacfee17fcc669f" + integrity sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.43.0" + "@typescript-eslint/types" "5.43.0" + "@typescript-eslint/typescript-estree" "5.43.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.43.0": + version "5.43.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz#cbbdadfdfea385310a20a962afda728ea106befa" + integrity sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg== + dependencies: + "@typescript-eslint/types" "5.43.0" + eslint-visitor-keys "^3.3.0" + +"@ucast/core@^1.0.0", "@ucast/core@^1.10.0", "@ucast/core@^1.4.1", "@ucast/core@^1.6.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@ucast/core/-/core-1.10.1.tgz#03a77a7804bcb5002a5cad3681e86cd1897e2e1f" + integrity sha512-sXKbvQiagjFh2JCpaHUa64P4UdJbOxYeC5xiZFn8y6iYdb0WkismduE+RmiJrIjw/eLDYmIEXiQeIYYowmkcAw== + +"@ucast/js@^3.0.0", "@ucast/js@^3.0.1": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@ucast/js/-/js-3.0.2.tgz#862838ee68112c6c262d4f4693cc592ba83157e0" + integrity sha512-zxNkdIPVvqJjHI7D/iK8Aai1+59yqU+N7bpHFodVmiTN7ukeNiGGpNmmSjQgsUw7eNcEBnPrZHNzp5UBxwmaPw== + dependencies: + "@ucast/core" "^1.0.0" + +"@ucast/mongo2js@^1.3.0": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@ucast/mongo2js/-/mongo2js-1.3.3.tgz#a683a59cea22887a72e4302f3826e41ccf51dbbe" + integrity sha512-sBPtMUYg+hRnYeVYKL+ATm8FaRPdlU9PijMhGYKgsPGjV9J4Ks41ytIjGayvKUnBOEhiCaKUUnY4qPeifdqATw== + dependencies: + "@ucast/core" "^1.6.1" + "@ucast/js" "^3.0.0" + "@ucast/mongo" "^2.4.0" + +"@ucast/mongo@^2.4.0": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@ucast/mongo/-/mongo-2.4.2.tgz#a8a1c32e65ccab623be023e6cedb11d136d50f19" + integrity sha512-/zH1TdBJlYGKKD+Wh0oyD+aBvDSWrwHcD8b4tUL9UgHLhzHtkEnMVFuxbw3SRIRsAa01wmy06+LWt+WoZdj1Bw== + dependencies: + "@ucast/core" "^1.4.1" + +"@uppy/aws-s3@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@uppy/aws-s3/-/aws-s3-3.0.4.tgz#a2422a2091c57570ff99a77108296f09ba9e46a5" + integrity sha512-mcwRYUH1TgcCWAkmvx9SCu7A9ylgBX+b1qyVO7ZiHnSbdg1F0H9hd3KkiUHGHkuV+hHz+l4vM9tQJqhxVhpW8w== + dependencies: + "@uppy/companion-client" "^3.0.2" + "@uppy/utils" "^5.0.2" + "@uppy/xhr-upload" "^3.0.4" + nanoid "^4.0.0" + +"@uppy/companion-client@^3.0.2": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@uppy/companion-client/-/companion-client-3.1.1.tgz#8c9974f70b899a40771da9a90113941356f9842e" + integrity sha512-S1M37vjWu8gdOgdI3Hh/1fVZ9GaLdyPQjVyUujZDTsr79b4VG7v/zjdqJ0FiOTjfGbpnj8s9kr1uyYi0Zf5VFw== + dependencies: + "@uppy/utils" "^5.1.1" + namespace-emitter "^2.0.1" + +"@uppy/core@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@uppy/core/-/core-3.0.4.tgz#3bdc08e50ab72749e6f9afa60eec6c836e8b3442" + integrity sha512-vFofKmmVVsQE9bnOXozAPy94kLQMUdMH/l8m4ncXmxyyGRc2e9VfvY9wiy2EEsoj11O7YVzHOP70FYdRReUpVw== + dependencies: + "@transloadit/prettier-bytes" "0.0.9" + "@uppy/store-default" "^3.0.2" + "@uppy/utils" "^5.0.2" + lodash.throttle "^4.1.1" + mime-match "^1.0.2" + namespace-emitter "^2.0.1" + nanoid "^4.0.0" + preact "^10.5.13" + +"@uppy/dashboard@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@uppy/dashboard/-/dashboard-3.2.0.tgz#e8d92714019a7af921bd42c398ca0c4c618fd2f7" + integrity sha512-Lb7qx4AKrq66cQnmoNdGIbhwYEZKnmdufasFWctrY1g2qN7Mv+1hor+QLhN9F51qHU1VJmxgEgEWmfZREWiXXw== + dependencies: + "@transloadit/prettier-bytes" "0.0.7" + "@uppy/informer" "^3.0.1" + "@uppy/provider-views" "^3.0.2" + "@uppy/status-bar" "^3.0.1" + "@uppy/thumbnail-generator" "^3.0.2" + "@uppy/utils" "^5.1.0" + classnames "^2.2.6" + is-shallow-equal "^1.0.1" + lodash.debounce "^4.0.8" + memoize-one "^6.0.0" + nanoid "^4.0.0" + preact "^10.5.13" + +"@uppy/drag-drop@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/drag-drop/-/drag-drop-3.0.1.tgz#3d28e908d930f8247f7f3dd787eae387ce56db40" + integrity sha512-1jMlF2V5AFfvSYtbIR1N2M8WbjhQPW14FcEElpeWjzwlF3OnbUARwk4jBlGfcAsDcV4dYf6hG192xPPdnuBc6A== + dependencies: + "@uppy/utils" "^5.0.2" + preact "^10.5.13" + +"@uppy/file-input@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/file-input/-/file-input-3.0.1.tgz#5d8e54770d035dcf657d29369b1c7c7f1d5c710a" + integrity sha512-RTOm09Z1JjfSnvULY41u3VXm5nMkORmOCwo68oI4Kd1UL/h3V64bqqd34IN5vDDhJ2O58qDAKohPdReqhcNJAQ== + dependencies: + "@uppy/utils" "^5.0.2" + preact "^10.5.13" + +"@uppy/informer@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/informer/-/informer-3.0.1.tgz#03a6793a830fddfe86f15b16dabcc8c4f918900c" + integrity sha512-Mip+l2CYsTVYa0fSwD0okHRS5/UhAqCfQO2CRwwR5/vC6DpR1VAALDqx6RpssOaFiOACAx12VCcZiP5J8+2BJw== + dependencies: + "@uppy/utils" "^5.0.2" + preact "^10.5.13" + +"@uppy/progress-bar@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/progress-bar/-/progress-bar-3.0.1.tgz#517748a6b558ceba4b76f25000416bc3f883a001" + integrity sha512-cjBLOst0sPV1yF6YzkQIx/mHpytMXN7MRF7H/6Yc8ahDzm8XkbwMQCf2zksaanGfHUNCHlQFgCkfjfizK6VJIw== + dependencies: + "@uppy/utils" "^5.0.2" + preact "^10.5.13" + +"@uppy/provider-views@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@uppy/provider-views/-/provider-views-3.0.2.tgz#386b3dbbce7fba21e3da776d90d9ea0737424249" + integrity sha512-z4v6UAiNSM2h8BB+/svBU1R/6QsssoLYKd4O8g6L7iAD1S2NPtU7U0busIyTvh3CA1Q6tfvfwF1JOFGTfNPQaA== + dependencies: + "@uppy/utils" "^5.0.2" + classnames "^2.2.6" + preact "^10.5.13" + +"@uppy/react@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@uppy/react/-/react-3.0.2.tgz#38a58c8e5f01839e504254620f9e39bb30bf4406" + integrity sha512-PRvS+vuW7pwMuGWte7m1FLfcRapdGCmydeOAbJJMr+dUcW4hgp0dE37fHL66CS/qEovADhTitc0N4iqE/flllA== + dependencies: + "@uppy/utils" "^5.0.2" + prop-types "^15.6.1" + +"@uppy/screen-capture@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/screen-capture/-/screen-capture-3.0.1.tgz#d39d6f9b39c6efb7c07a5004c112effb4e6e2afe" + integrity sha512-IPUa/k0CobdQWRRBPD/mPZqiiyre47u0DadPOoMR6WOiqElOj+cM5MoHsSQCTYmKMYRajEn9OPb4zGMz0U23Yg== + dependencies: + "@uppy/utils" "^5.0.2" + preact "^10.5.13" + +"@uppy/status-bar@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@uppy/status-bar/-/status-bar-3.0.1.tgz#b0751715821770b3ddb7b03eab2fdf35d841bfe5" + integrity sha512-QQqIjiHBsNjZFR6xEc5SAT5UYbKnRcgNWwyLmJR0BYVINfx/aeIueXIm0jkBuJ4XFkebH7nDjKGW89JE9WKkgg== + dependencies: + "@transloadit/prettier-bytes" "0.0.9" + "@uppy/utils" "^5.0.2" + classnames "^2.2.6" + lodash.throttle "^4.1.1" + preact "^10.5.13" + +"@uppy/store-default@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@uppy/store-default/-/store-default-3.0.2.tgz#870724c45a2f671d625123cb4a412e3bfae935d9" + integrity sha512-kIQMCjXui6tjF1E9xGo4YHkvt71McXkU0FStrcQuBrRXuOhb+NcuWh3sMh3KryivVNgT6w5Odrlw2FUFkl9cqA== + +"@uppy/thumbnail-generator@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@uppy/thumbnail-generator/-/thumbnail-generator-3.0.2.tgz#2b3e536138625ff691d0283c8a84fddc233a3c4c" + integrity sha512-3qE1giGu1O9BkO8MCYYV78X9p95l5Ptv7JUDpqKlIMN/NGBHblMFCn5mjmt4soyQFUGgnZ7+bxq0DtjXPpWJYA== + dependencies: + "@uppy/utils" "^5.0.2" + exifr "^7.0.0" + +"@uppy/utils@^5.0.2", "@uppy/utils@^5.1.0", "@uppy/utils@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@uppy/utils/-/utils-5.1.1.tgz#9597e8696e17d71413672bd56eb082c7410514a3" + integrity sha512-uoI+PcIVQboky0ZbN4PQeK1seZnnJocomzeK7blId9HKJ6QNgZLf2ibk2CQuQxrOuNsWhgrhs5uLO5Si0oM0Yw== + dependencies: + lodash.throttle "^4.1.1" + +"@uppy/webcam@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@uppy/webcam/-/webcam-3.2.1.tgz#1257716b2835fdb1b7d930842f3bf4d6c4240ac3" + integrity sha512-CT6OnZ04rJbdGqmz3f1Ukq86x3J8YFP/BHa1LiFn43cJG/kso6EgX2jgxGSBa1u6IMN2YchxPSEgxVjJl/kppg== + dependencies: + "@uppy/utils" "^5.0.2" + is-mobile "^3.1.1" + preact "^10.5.13" + +"@uppy/xhr-upload@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@uppy/xhr-upload/-/xhr-upload-3.0.4.tgz#219a92c832bee1f089992958d27ec71dbe9d9d7d" + integrity sha512-uJ1oxcwEaSLnrexvi6Lp57hV3z3DsovgVmYIVwg+z/EnrRcL32wNRE7FcIr8Mk9e1jdMiFYlk6cQmiP2dZep8A== + dependencies: + "@uppy/companion-client" "^3.0.2" + "@uppy/utils" "^5.0.2" + nanoid "^4.0.0" + +"@vercel/analytics@^0.1.8": + version "0.1.8" + resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-0.1.8.tgz#71f1f8c7bb98ac0c5c47eb3fb8ccbe8141b9fe47" + integrity sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ== + +"@vercel/og@^0.0.25": + version "0.0.25" + resolved "https://registry.yarnpkg.com/@vercel/og/-/og-0.0.25.tgz#6f7951351e5321a8f097a10dbc6a1d4ccdf2cf23" + integrity sha512-Zirui1w0CjiOqHrMTOIDptghiEyxhiyVEdgUnJAeQYaXYrtQPUWLDWBIh5J+xHxIstWhT0C6DIw0iMGfHSXTNQ== + dependencies: + "@resvg/resvg-wasm" "2.0.0-alpha.4" + satori "0.0.46" + yoga-wasm-web "0.3.0" + +"@visx/axis@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@visx/axis/-/axis-2.11.1.tgz#e1cc978ede9cce197cd7712183301e9e89c519e2" + integrity sha512-RZdT+yhAEOXtcLc3PgD14Xh5bJh5B64IG+zvHe3YVMkiFWGT1phy0sGTRRxritHke16ErB9vndx+pwVIGSkxAw== + dependencies: + "@types/react" "*" + "@visx/group" "2.10.0" + "@visx/point" "2.6.0" + "@visx/scale" "2.2.2" + "@visx/shape" "2.11.1" + "@visx/text" "2.10.0" + classnames "^2.3.1" + prop-types "^15.6.0" + +"@visx/bounds@2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/bounds/-/bounds-2.10.0.tgz#cd0bdd7db924a5a2151174edce09f867aaeaf3af" + integrity sha512-rY7WFTIjQaXA8tFL45O2qbtSRkyF4yF75HiWz06F7BVmJ9UjF2qlomB3Y1z6gk6ZiFhwQ4zxABjOVjAQPLn7nQ== + dependencies: + "@types/react" "*" + "@types/react-dom" "*" + prop-types "^15.5.10" + +"@visx/clip-path@2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/clip-path/-/clip-path-2.10.0.tgz#0f064c82dce9a34232dc989816a9f5e26059d4ef" + integrity sha512-YIUQstsHKGysyDISOV5p+fMymkUdHCs89nSY/w6TG9EIl8x2jRhrQdojwtCYvjLkPZiZiKa8MBT6fFzsSfGImg== + dependencies: + "@types/react" "*" + prop-types "^15.5.10" + +"@visx/curve@2.1.0", "@visx/curve@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@visx/curve/-/curve-2.1.0.tgz#f614bfe3db66df7db7382db7a75ced1506b94602" + integrity sha512-9b6JOnx91gmOQiSPhUOxdsvcnW88fgqfTPKoVgQxidMsD/I3wksixtwo8TR/vtEz2aHzzsEEhlv1qK7Y3yaSDw== + dependencies: + "@types/d3-shape" "^1.3.1" + d3-shape "^1.0.6" + +"@visx/event@^2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@visx/event/-/event-2.6.0.tgz#0718eb1efabd5305cf659a153779c94ba4038996" + integrity sha512-WGp91g82s727g3NAnENF1ppC3ZAlvWg+Y+GG0WFg34NmmOZbvPI/PTOqTqZE3x6B8EUn8NJiMxRjxIMbi+IvRw== + dependencies: + "@types/react" "*" + "@visx/point" "2.6.0" + +"@visx/glyph@^2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/glyph/-/glyph-2.10.0.tgz#9f8929b8391b9862023af00d64446188208b232b" + integrity sha512-qjbnfSgV920+V4ctXeDJWwzBorZLz97cDA4b/GmJ2tk2h0AVMrAejF2LNLgPQpzsVb7BIuVXAJXgp0dop2gr6w== + dependencies: + "@types/d3-shape" "^1.3.1" + "@types/react" "*" + "@visx/group" "2.10.0" + classnames "^2.3.1" + d3-shape "^1.2.0" + prop-types "^15.6.2" + +"@visx/gradient@^2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/gradient/-/gradient-2.10.0.tgz#a3905735da18c2c074edc7becc93a46f17f2e086" + integrity sha512-qxmIFY7aRcz0BqaH3gX0tYJu6cOJZhjTzPnHCuuxOjlF28fDDaAVG+oRqlZzYBMAvlWJ9s5qtDou7OvKJF/+2g== + dependencies: + "@types/react" "*" + prop-types "^15.5.7" + +"@visx/grid@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@visx/grid/-/grid-2.11.1.tgz#100ee2977a64a7af98c86ac3c319375ba1fd5297" + integrity sha512-mcF0OHIEm5LxkC7/seKq7GDgVfLgN1FS8XFiKHnvnA9BNkCMFdeHqnM8o46yS00/hYzh7El6jzu5OrGkSeuzaA== + dependencies: + "@types/react" "*" + "@visx/curve" "2.1.0" + "@visx/group" "2.10.0" + "@visx/point" "2.6.0" + "@visx/scale" "2.2.2" + "@visx/shape" "2.11.1" + classnames "^2.3.1" + prop-types "^15.6.2" + +"@visx/group@2.10.0", "@visx/group@^2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/group/-/group-2.10.0.tgz#95839851832545621eb0d091866a61dafe552ae1" + integrity sha512-DNJDX71f65Et1+UgQvYlZbE66owYUAfcxTkC96Db6TnxV221VKI3T5l23UWbnMzwFBP9dR3PWUjjqhhF12N5pA== + dependencies: + "@types/react" "*" + classnames "^2.3.1" + prop-types "^15.6.2" + +"@visx/point@2.6.0": + version "2.6.0" + resolved "https://registry.yarnpkg.com/@visx/point/-/point-2.6.0.tgz#c4316ca409b5b829c5455f07118d8c14a92cc633" + integrity sha512-amBi7yMz4S2VSchlPdliznN41TuES64506ySI22DeKQ+mc1s1+BudlpnY90sM1EIw4xnqbKmrghTTGfy6SVqvQ== + +"@visx/responsive@^2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/responsive/-/responsive-2.10.0.tgz#3e5c5853c7b2b33481e99a64678063cef717de0b" + integrity sha512-NssDPpuUYp7hqVISuYkKZ5zk6ob0++RdTIaUjRcUdyFEbvzb9+zIb8QToOkvI90L2EC/MY4Jx0NpDbEe79GpAw== + dependencies: + "@juggle/resize-observer" "^3.3.1" + "@types/lodash" "^4.14.172" + "@types/react" "*" + lodash "^4.17.21" + prop-types "^15.6.1" + +"@visx/scale@2.2.2", "@visx/scale@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@visx/scale/-/scale-2.2.2.tgz#b8eafabdcf92bb45ab196058fe184772ad80fd25" + integrity sha512-3aDySGUTpe6VykDQmF+g2nz5paFu9iSPTcCOEgkcru0/v5tmGzUdvivy8CkYbr87HN73V/Jc53lGm+kJUQcLBw== + dependencies: + "@types/d3-interpolate" "^1.3.1" + "@types/d3-scale" "^3.3.0" + "@types/d3-time" "^2.0.0" + d3-interpolate "^1.4.0" + d3-scale "^3.3.0" + d3-time "^2.1.1" + +"@visx/shape@2.11.1", "@visx/shape@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@visx/shape/-/shape-2.11.1.tgz#ff41d839ad689fea53504597bd4bfbc8b7c20bbb" + integrity sha512-0ak3wTkXjExH7kzU62yXPu+RtuG35G1sNL58Ax4NBr4yKh2nTHcLRsMZ7k6yUG+wSjb8DgG/ywZ0bBGWXJYGbg== + dependencies: + "@types/d3-path" "^1.0.8" + "@types/d3-shape" "^1.3.1" + "@types/lodash" "^4.14.172" + "@types/react" "*" + "@visx/curve" "2.1.0" + "@visx/group" "2.10.0" + "@visx/scale" "2.2.2" + classnames "^2.3.1" + d3-path "^1.0.5" + d3-shape "^1.2.0" + lodash "^4.17.21" + prop-types "^15.5.10" + +"@visx/shape@2.12.2": + version "2.12.2" + resolved "https://registry.yarnpkg.com/@visx/shape/-/shape-2.12.2.tgz#81ed88bf823aa84a4f5f32a9c9daf8371a606897" + integrity sha512-4gN0fyHWYXiJ+Ck8VAazXX0i8TOnLJvOc5jZBnaJDVxgnSIfCjJn0+Nsy96l9Dy/bCMTh4DBYUBv9k+YICBUOA== + dependencies: + "@types/d3-path" "^1.0.8" + "@types/d3-shape" "^1.3.1" + "@types/lodash" "^4.14.172" + "@types/react" "*" + "@visx/curve" "2.1.0" + "@visx/group" "2.10.0" + "@visx/scale" "2.2.2" + classnames "^2.3.1" + d3-path "^1.0.5" + d3-shape "^1.2.0" + lodash "^4.17.21" + prop-types "^15.5.10" + +"@visx/text@2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/text/-/text-2.10.0.tgz#2ff413f4dd35617e45b24d6082d7b856358afe4c" + integrity sha512-Wbt0Mq130akOahaby2Ziwkr62LuK6td9RcIWI9o4xsLE7GCIkxeUiVci0YUo2yewgwOLwR9F1guPuHPqYd+eYg== + dependencies: + "@types/lodash" "^4.14.172" + "@types/react" "*" + classnames "^2.3.1" + lodash "^4.17.21" + prop-types "^15.7.2" + reduce-css-calc "^1.3.0" + +"@visx/threshold@^2.12.2": + version "2.12.2" + resolved "https://registry.yarnpkg.com/@visx/threshold/-/threshold-2.12.2.tgz#8a230f050247c37ccb05284696960f4999f45493" + integrity sha512-Wg33MK8t8m12SRO4W3U6pKmVK7mnIcA8EshmlS8vL/VvKBcyOyw1/7FHmBAdSvyfh4zXAmj5WtG0G8lp5TwkYw== + dependencies: + "@types/react" "*" + "@visx/clip-path" "2.10.0" + "@visx/shape" "2.12.2" + classnames "^2.3.1" + prop-types "^15.5.10" + +"@visx/tooltip@^2.10.0": + version "2.10.0" + resolved "https://registry.yarnpkg.com/@visx/tooltip/-/tooltip-2.10.0.tgz#86d6a720af573dc9853d86d99ae35052bb1629f3" + integrity sha512-6Zrd79MIEfyuLBcZ1ypSeAkpQc8oLRNB7FQnegzl3Lje4LK5lJtuf5ST0mwK6G2Uv+GlOW9REJ6VK4gfAGkq9A== + dependencies: + "@types/react" "*" + "@visx/bounds" "2.10.0" + classnames "^2.3.1" + prop-types "^15.5.10" + react-use-measure "^2.0.4" + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +"@yarnpkg/lockfile@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" + integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== + +"@yarnpkg/parsers@^3.0.0-rc.18": + version "3.0.0-rc.29" + resolved "https://registry.yarnpkg.com/@yarnpkg/parsers/-/parsers-3.0.0-rc.29.tgz#e2ceba6842399121d3e3889e8aa1522f7e5b38ec" + integrity sha512-07kAB4mEcZltoMBQqV4Tb8ucCpOQCG6fl4fH/2cmOU8Nh6nZcmvCptIuARC5dd4vfZKI5zUUoK2arR+pOi9PxQ== + dependencies: + js-yaml "^3.10.0" + tslib "^2.4.0" + +"@zkochan/js-yaml@0.0.6": + version "0.0.6" + resolved "https://registry.yarnpkg.com/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz#975f0b306e705e28b8068a07737fa46d3fc04826" + integrity sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg== + dependencies: + argparse "^2.0.1" + +abab@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + +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== + +accepts@~1.3.4: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +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-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-jsx@^5.3.1, 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, acorn-walk@^7.2.0: + 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-walk@^8.0.0, acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.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.4: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +acorn@^8.4.1: + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== + +acorn@^8.5.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== + +acorn@^8.7.1: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + +address@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9" + integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== + +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" + +aggregate-error@^3.0.0, aggregate-error@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +airbnb-js-shims@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz#db481102d682b98ed1daa4c5baa697a05ce5c040" + integrity sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + array.prototype.flatmap "^1.2.1" + es5-shim "^4.5.13" + es6-shim "^0.35.5" + function.prototype.name "^1.1.0" + globalthis "^1.0.0" + object.entries "^1.1.0" + object.fromentries "^2.0.0 || ^1.0.0" + object.getownpropertydescriptors "^2.0.3" + object.values "^1.1.0" + promise.allsettled "^1.0.0" + promise.prototype.finally "^3.1.0" + string.prototype.matchall "^4.0.0 || ^3.0.1" + string.prototype.padend "^3.0.0" + string.prototype.padstart "^3.0.0" + symbol.prototype.description "^1.0.0" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: + 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" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.9.0.tgz#738019146638824dea25edcf299dcba1b0e7eb18" + integrity sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: + 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-html-community@0.0.8, ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +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-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +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== + +ansi-styles@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.1.0.tgz#87313c102b8118abd57371afab34618bf7350ed3" + integrity sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ== + +ansi-to-html@^0.6.11: + version "0.6.15" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" + integrity sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ== + dependencies: + entities "^2.0.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.0, 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" + +apache-crypt@^1.1.2: + version "1.2.5" + resolved "https://registry.yarnpkg.com/apache-crypt/-/apache-crypt-1.2.5.tgz#4eb6b6dbaed2041ce5bc2d802f4421f5fdadc25e" + integrity sha512-ICnYQH+DFVmw+S4Q0QY2XRXD8Ne8ewh8HgbuFH4K7022zCxgHM0Hz1xkRnUlEfAXNbwp1Cnhbedu60USIfDxvg== + dependencies: + unix-crypt-td-js "^1.1.4" + +apache-md5@^1.0.6: + version "1.1.7" + resolved "https://registry.yarnpkg.com/apache-md5/-/apache-md5-1.1.7.tgz#dcef1802700cc231d60c5e08fd088f2f9b36375a" + integrity sha512-JtHjzZmJxtzfTSjsCyHgPR155HBe5WGyUyHTaEkfy46qhwCFKx1Epm6nAxgUG3WfUZP1dWhGqj9Z2NOBeZ+uBw== + +app-root-dir@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" + integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== + +append-field@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" + integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +arch@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +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== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.0.3, 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-includes@^3.1.3, array-includes@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9" + integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + +array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" + integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +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-union@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-3.0.1.tgz#da52630d327f8b88cfbfb57728e2af5cd9b6b975" + integrity sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw== + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +array.prototype.flat@^1.2.1: + 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.flat@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13" + integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + +array.prototype.flatmap@^1.2.1, 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" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" + integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.map@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.4.tgz#0d97b640cfdd036c1b41cfe706a5e699aa0711f2" + integrity sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.reduce@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" + integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" + integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asn1.js@^5.2.0, asn1.js@^5.3.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +asn1@~0.2.3: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +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 sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + +ast-types@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +async@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + +async@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" + integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + +async@^3.1.0, async@^3.2.0, async@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== + +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== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +auth0-deploy-cli@^7.15.1: + version "7.15.1" + resolved "https://registry.yarnpkg.com/auth0-deploy-cli/-/auth0-deploy-cli-7.15.1.tgz#c22268f300b5643bb0c43343e9c82dcd52008a24" + integrity sha512-laq/Rtlp3tBNsQkNIhG+NLaE661hQMY3zaq+qBZbo9iQYGkQKIaMIUXG19/i8XpaVTQoPMo06FmKDq6qnHCmyw== + dependencies: + ajv "^6.12.6" + auth0 "^2.42.0" + dot-prop "^5.2.0" + fs-extra "^10.1.0" + global-agent "^2.1.12" + js-yaml "^4.1.0" + lodash "^4.17.20" + mkdirp "^1.0.4" + nconf "^0.12.0" + promise-pool-executor "^1.1.1" + sanitize-filename "^1.6.1" + winston "^3.7.2" + yargs "^15.3.1" + +auth0@^2.42.0: + version "2.42.0" + resolved "https://registry.yarnpkg.com/auth0/-/auth0-2.42.0.tgz#8202f94ce438b7b3081a29ce7325e08662a9268d" + integrity sha512-IiA224CoqjPF9Pnx0R80wQZBGlXMia69s7lQHg328Oe2mngdp6Qi32DaIKkkVtJb8FuMleWMgI+zmbwW0znhYw== + dependencies: + axios "^0.26.1" + form-data "^3.0.1" + jsonwebtoken "^8.5.1" + jwks-rsa "^1.12.1" + lru-memoizer "^2.1.4" + rest-facade "^1.16.3" + retry "^0.13.1" + +auth0@^2.44.0: + version "2.44.0" + resolved "https://registry.yarnpkg.com/auth0/-/auth0-2.44.0.tgz#7acbe045b8c9e833ea17b535ab997177fd164f4e" + integrity sha512-TowzD2RbBVyhqW/qk0cqV5ibRc86r2gqqc+8CRdoV9/58hh3prjeSjaW+8CpnyVdBYqoCM0ItvbJnCjk/tsUig== + dependencies: + axios "^0.27.2" + form-data "^3.0.1" + jsonwebtoken "^8.5.1" + jwks-rsa "^1.12.1" + lru-memoizer "^2.1.4" + rest-facade "^1.16.3" + retry "^0.13.1" + +autoprefixer@10.4.13, autoprefixer@^10.4.9: + 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" + +autoprefixer@^9.8.6: + version "9.8.8" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" + integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + picocolors "^0.2.1" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" + integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== + +axe-core@^4.3.5: + version "4.3.5" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5" + integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA== + +axe-core@^4.4.3: + version "4.5.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.5.2.tgz#823fdf491ff717ac3c58a52631d4206930c1d9f7" + integrity sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA== + +axios@0.21.4, axios@^0.21.1: + version "0.21.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575" + integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== + dependencies: + follow-redirects "^1.14.0" + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +axios@^0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +axios@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.1.3.tgz#8274250dada2edf53814ed7db644b9c2866c1e35" + integrity sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA== + dependencies: + follow-redirects "^1.15.0" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +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@28.1.3, babel-jest@^28.1.1, babel-jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" + integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== + dependencies: + "@jest/transform" "^28.1.3" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^28.1.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-loader@8.2.3, babel-loader@^8.2.2: + version "8.2.3" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d" + integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^1.4.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-loader@^8.0.0: + version "8.2.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-add-react-displayname@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" + integrity sha1-M51M3be2X9YtHfnbn+BN4TQSK9U= + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-const-enum@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-const-enum/-/babel-plugin-const-enum-1.2.0.tgz#3d25524106f68f081e187829ba736b251c289861" + integrity sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript" "^7.3.3" + "@babel/traverse" "^7.16.0" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-istanbul@^6.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@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" + integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" + integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== + dependencies: + "@babel/runtime" "^7.7.2" + cosmiconfig "^6.0.0" + resolve "^1.12.0" + +babel-plugin-macros@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +babel-plugin-named-exports-order@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-named-exports-order/-/babel-plugin-named-exports-order-0.0.2.tgz#ae14909521cf9606094a2048239d69847540cb09" + integrity sha512-OgOYHOLoRK+/mvXU9imKHlG6GkPLYrUCvFXG/CM93R/aNNO8pOOF4aS+S8CCHMDQoNSeiOYEZb/G6RwL95Jktw== + +babel-plugin-polyfill-corejs2@^0.3.0, babel-plugin-polyfill-corejs2@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.1" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz#80449d9d6f2274912e05d9e182b54816904befd0" + integrity sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.1.5" + core-js-compat "^3.8.1" + +babel-plugin-polyfill-corejs3@^0.5.0, babel-plugin-polyfill-corejs3@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.21.0" + +babel-plugin-polyfill-regenerator@^0.3.0, babel-plugin-polyfill-regenerator@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + +babel-plugin-react-docgen@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b" + integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ== + dependencies: + ast-types "^0.14.2" + lodash "^4.17.15" + react-docgen "^5.0.0" + +babel-plugin-transform-async-to-promises@^0.8.15: + version "0.8.18" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-promises/-/babel-plugin-transform-async-to-promises-0.8.18.tgz#f4dc5980b8afa0fc9c784b8d931afde913413e39" + integrity sha512-WpOrF76nUHijnNn10eBGOHZmXQC8JYRME9rOLxStOga7Av2VO53ehVFvVNImMksVtQuL2/7ZNxEgxnx7oo/3Hw== + +babel-plugin-transform-typescript-metadata@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-typescript-metadata/-/babel-plugin-transform-typescript-metadata-0.3.2.tgz#7a327842d8c36ffe07ee1b5276434e56c297c9b7" + integrity sha512-mWEvCQTgXQf48yDqgN7CH50waTyYBeP2Lpqx4nNWab9sxEpdXVeKgfj1qYI2/TgUPQtNFZ85i3PemRtnXVYYJg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + +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@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" + integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== + dependencies: + babel-plugin-jest-hoist "^28.1.3" + babel-preset-current-node-syntax "^1.0.0" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= + +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== + +base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +basic-auth@^2.0.1, basic-auth@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a" + integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg== + dependencies: + safe-buffer "5.1.2" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +bcryptjs@^2.3.0: + version "2.4.3" + resolved "https://registry.yarnpkg.com/bcryptjs/-/bcryptjs-2.4.3.tgz#9ab5627b93e60621ff7cdac5da9733027df1d0cb" + integrity sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms= + +better-opn@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" + integrity sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA== + dependencies: + open "^7.0.3" + +big-integer@^1.6.7: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +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== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blob-util@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" + integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== + +bluebird@^3.5.5, bluebird@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.19.2: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.9.7" + raw-body "2.4.3" + type-is "~1.6.18" + +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.0.14" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.14.tgz#c346f5bc84e87802d08f8d5a60b93f758e514ee7" + integrity sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boolean@^3.0.1: + version "3.1.4" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.1.4.tgz#f51a2fb5838a99e06f9b6ec1edb674de67026435" + integrity sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w== + +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +bplist-parser@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6" + integrity sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q== + dependencies: + big-integer "^1.6.7" + +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@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.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" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browser-assert@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" + integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== + +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== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6: + version "4.19.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" + integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== + dependencies: + caniuse-lite "^1.0.30001286" + electron-to-chromium "^1.4.17" + escalade "^3.1.1" + node-releases "^2.0.1" + picocolors "^1.0.0" + +browserslist@^4.12.0, browserslist@^4.21.2: + version "4.21.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" + integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== + dependencies: + caniuse-lite "^1.0.30001366" + electron-to-chromium "^1.4.188" + node-releases "^2.0.6" + update-browserslist-db "^1.0.4" + +browserslist@^4.20.2: + version "4.20.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88" + integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA== + dependencies: + caniuse-lite "^1.0.30001317" + electron-to-chromium "^1.4.84" + escalade "^3.1.1" + node-releases "^2.0.2" + picocolors "^1.0.0" + +browserslist@^4.21.3, 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-crc32@~0.2.3: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= + +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + +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== + +buffer-writer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" + integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +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" + +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + +bull@^4.10.2: + version "4.10.2" + resolved "https://registry.yarnpkg.com/bull/-/bull-4.10.2.tgz#af53545b394001755cf7c6b30bac295eeb5e0420" + integrity sha512-xa65xtWjQsLqYU/eNaXxq9VRG8xd6qNsQEjR7yjYuae05xKrzbVMVj2QgrYsTMmSs/vsqJjHqHSRRiW1+IkGXQ== + dependencies: + cron-parser "^4.2.1" + debuglog "^1.0.0" + get-port "^5.1.1" + ioredis "^5.0.0" + lodash "^4.17.21" + msgpackr "^1.5.2" + p-timeout "^3.2.0" + semver "^7.3.2" + uuid "^8.3.0" + +busboy@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +c8@^7.6.0: + version "7.11.0" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.11.0.tgz#b3ab4e9e03295a102c47ce11d4ef6d735d9a9ac9" + integrity sha512-XqPyj1uvlHMr+Y1IeRndC2X5P7iJzJlEJwBpCdBbq2JocXOgJfr+JVfJkyNMGROke5LfKrhSFXGFXnwnRJAUJw== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.2" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.0.1" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.0.2" + rimraf "^3.0.0" + test-exclude "^6.0.0" + v8-to-istanbul "^8.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.7" + +cacache@^12.0.2: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^15.0.5: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +cacheable-lookup@^5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" + integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== + +cacheable-request@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27" + integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew== + dependencies: + clone-response "^1.0.2" + get-stream "^5.1.0" + http-cache-semantics "^4.0.0" + keyv "^4.0.0" + lowercase-keys "^2.0.0" + normalize-url "^6.0.1" + responselike "^2.0.0" + +cachedir@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" + integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== + +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" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== + +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== + +camel-case@^1.1.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-1.2.2.tgz#1aca7c4d195359a2ce9955793433c6e5542511f2" + integrity sha1-Gsp8TRlTWaLOmVV5NDPG5VQlEfI= + dependencies: + sentence-case "^1.1.1" + upper-case "^1.1.1" + +camel-case@^4.1.1, camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1, 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-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ== + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== + +camelcase@^5.0.0, 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-api@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" + integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0: + version "1.0.30001302" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001302.tgz#da57ce61c51177ef3661eeed7faef392d3790aaa" + integrity sha512-YYTMO+tfwvgUN+1ZnRViE53Ma1S/oETg+J2lISsqi/ZTNThj3ZYBOKP2rHwJc37oCsPqAzJ3w2puZHn0xlLPPw== + +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001286: + version "1.0.30001368" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713" + integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ== + +caniuse-lite@^1.0.30001317: + version "1.0.30001323" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001323.tgz#a451ff80dec7033016843f532efda18f02eec011" + integrity sha512-e4BF2RlCVELKx8+RmklSEIVub1TWrmdhvA5kEUueummz1XyySW0DVk+3x9HyhU9MuWTa2BhqLgEuEmUwASAdCA== + +caniuse-lite@^1.0.30001366: + version "1.0.30001367" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz#2b97fe472e8fa29c78c5970615d7cd2ee414108a" + integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw== + +caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406, 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== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +case-sensitive-paths-webpack-plugin@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" + integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +cb@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cb/-/cb-0.1.0.tgz#26f7e740f2808facc83cef7b20392e4d881b5203" + integrity sha1-JvfnQPKAj6zIPO97IDkuTYgbUgM= + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@^2.0.0, chalk@^2.4.1: + 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.0.2, chalk@^4.1.0, chalk@^4.1.2: + 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" + +change-case@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-2.3.1.tgz#2c4fde3f063bb41d00cd68e0d5a09db61cbe894f" + integrity sha1-LE/ePwY7tB0AzWjg1aCdthy+iU8= + dependencies: + camel-case "^1.1.1" + constant-case "^1.1.0" + dot-case "^1.1.0" + is-lower-case "^1.1.0" + is-upper-case "^1.1.0" + lower-case "^1.1.1" + lower-case-first "^1.0.0" + param-case "^1.1.0" + pascal-case "^1.1.0" + path-case "^1.1.0" + sentence-case "^1.1.1" + snake-case "^1.1.0" + swap-case "^1.1.0" + title-case "^1.1.0" + upper-case "^1.1.1" + upper-case-first "^1.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-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +check-more-types@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA= + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.1, 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" + +chokidar@^2.0.4, chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.1.0, ci-info@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +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== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +classnames@^2.2.6: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== + +classnames@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e" + integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA== + +clean-css@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + +clean-css@^5.2.2: + version "5.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.2.3.tgz#efca57c1f90303a5438939fecbc3786b1fe5e31a" + integrity sha512-qjywD7LvpZJ5+E16lf00GnMVUX5TEVBcKW1/vtGPgAerHwRwE4JP4p1Y40zbLnup2ZfWsd30P2bHdoAKH93XxA== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-cursor@3.1.0, cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-table3@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" + integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cli-table3@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== + dependencies: + string-width "^4.2.0" + optionalDependencies: + colors "1.4.0" + +cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + +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@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +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" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone-response@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" + integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= + dependencies: + mimic-response "^1.0.0" + +clone@2.x, clone@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" + integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= + +cluster-key-slot@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz#10ccb9ded0729464b6d2e7d714b100a2d1259d43" + integrity sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +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== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0, color-convert@^1.9.3: + 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.6.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +color@^3.1.3: + version "3.2.1" + resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" + integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== + dependencies: + color-convert "^1.9.3" + color-string "^1.6.0" + +colord@^2.9.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1" + integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ== + +colorette@^1.1.0, colorette@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +colorette@^2.0.10, colorette@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + +colors@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + +colorspace@1.1.x: + version "1.1.4" + resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" + integrity sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w== + dependencies: + color "^3.1.3" + text-hex "1.0.x" + +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: + 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@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" + integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +component-emitter@^1.2.1, component-emitter@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +compute-scroll-into-view@^1.0.17: + version "1.0.17" + resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab" + integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg== + +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== + +concat-stream@^1.5.0, concat-stream@^1.5.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +concat-with-sourcemaps@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" + integrity sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg== + dependencies: + source-map "^0.6.1" + +confusing-browser-globals@^1.0.9: + version "1.0.11" + resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +connect@^3.6.6: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +constant-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-1.1.2.tgz#8ec2ca5ba343e00aa38dbf4e200fd5ac907efd63" + integrity sha1-jsLKW6ND4Aqjjb9OIA/VrJB+/WM= + dependencies: + snake-case "^1.1.0" + upper-case "^1.1.1" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.4.0, 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" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cookie@0.5.0, cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +cookiejar@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" + integrity sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ== + +copy-anything@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.2.tgz#7189171ff5e1893b2287e8bf574b8cd448ed50b1" + integrity sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA== + dependencies: + is-what "^4.1.6" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +copy-webpack-plugin@^10.2.4: + version "10.2.4" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-10.2.4.tgz#6c854be3fdaae22025da34b9112ccf81c63308fe" + integrity sha512-xFVltahqlsRcyyJqQbDY6EYTtyQZF9rf+JPjwHObLdPFMEISqkFkr7mFoVOC6BfYS/dNThyoQKvziugm+OnwBg== + dependencies: + fast-glob "^3.2.7" + glob-parent "^6.0.1" + globby "^12.0.2" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + +core-js-compat@^3.20.2, core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.1: + version "3.23.5" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.5.tgz#11edce2f1c4f69a96d30ce77c805ce118909cd5b" + integrity sha512-fHYozIFIxd+91IIbXJgWd/igXIc8Mf9is0fusswjnGIWVG96y2cwyUdlCkGOw6rMLHKAxg7xtCIVaHsyOUnJIg== + dependencies: + browserslist "^4.21.2" + semver "7.0.0" + +core-js-pure@^3.20.2, core-js-pure@^3.8.1: + version "3.20.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.20.3.tgz#6cc4f36da06c61d95254efc54024fe4797fd5d02" + integrity sha512-Q2H6tQ5MtPtcC7f3HxJ48i4Q7T9ybPKgvWyuH7JXIoNa2pm0KuBnycsET/qw1SLLZYfbsbrZQNMeIOClb+6WIA== + +core-js-pure@^3.23.3: + version "3.27.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.27.1.tgz#ede4a6b8440585c7190062757069c01d37a19dca" + integrity sha512-BS2NHgwwUppfeoqOXqi08mUqS5FiZpuRuJJpKsaME7kJz0xxuk0xkhDdfMIlP/zLa80krBqss1LtD7f889heAw== + +core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: + version "3.23.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.5.tgz#1f82b0de5eece800827a2f59d597509c67650475" + integrity sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg== + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cors@^2.8.5, cors@latest: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +corser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87" + integrity sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c= + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cp-file@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" + integrity sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw== + dependencies: + graceful-fs "^4.1.2" + make-dir "^3.0.0" + nested-error-stacks "^2.0.0" + p-event "^4.1.0" + +cpy@^8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/cpy/-/cpy-8.1.2.tgz#e339ea54797ad23f8e3919a5cffd37bfc3f25935" + integrity sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg== + dependencies: + arrify "^2.0.1" + cp-file "^7.0.0" + globby "^9.2.0" + has-glob "^1.0.0" + junk "^3.1.0" + nested-error-stacks "^2.1.0" + p-all "^2.1.0" + p-filter "^2.1.0" + p-map "^3.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cron-parser@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.2.1.tgz#b43205d05ccd5c93b097dae64f3bd811f5993af3" + integrity sha512-5sJBwDYyCp+0vU5b7POl8zLWfgV5fOHxlc45FWoWdHecGC7MQHCjx0CHivCMRnGFovghKhhyYM+Zm9DcY5qcHg== + dependencies: + luxon "^1.28.0" + +cross-fetch@^3.1.4: + 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@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, 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" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +crypto-js@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + +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-declaration-sorter@^6.0.3: + version "6.1.4" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.4.tgz#b9bfb4ed9a41f8dcca9bf7184d849ea94a8294b4" + integrity sha512-lpfkqS0fctcmZotJGhnxkIyJWvBXgpyi2wsFd4J8VB7wzyrT6Ch/3Q+FMNJpjK4gu1+GN5khOnpU2ZVKrLbhCw== + dependencies: + timsort "^0.3.0" + +css-loader@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" + integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== + dependencies: + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.0" + semver "^6.3.0" + +css-loader@^5.0.1: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== + dependencies: + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" + +css-loader@^6.4.0: + version "6.5.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.5.1.tgz#0c43d4fbe0d97f699c91e9818cb585759091d1b1" + integrity sha512-gEy2w9AnJNnD9Kuo4XAP9VflW/ujKoS9c/syO+uWMlm5igc7LysKzPXaDoR2vroROkSwsTS2tGr1yGGEbZOYZQ== + dependencies: + icss-utils "^5.1.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + semver "^7.3.5" + +css-mediaquery@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/css-mediaquery/-/css-mediaquery-0.1.2.tgz#6a2c37344928618631c54bd33cedd301da18bea0" + integrity sha1-aiw3NEkoYYYxxUvTPO3TAdoYvqA= + +css-minimizer-webpack-plugin@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f" + integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q== + dependencies: + cssnano "^5.0.6" + jest-worker "^27.0.2" + postcss "^8.3.5" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +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, css-tree@^1.1.3: + 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-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + +css@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d" + integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ== + dependencies: + inherits "^2.0.4" + source-map "^0.6.1" + source-map-resolve "^0.6.0" + +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== + +cssnano-preset-default@^5.1.11: + version "5.1.11" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.11.tgz#db10fb1ecee310e8285c5aca45bd8237be206828" + integrity sha512-ETet5hqHxmzQq2ynXMOQofKuLm7VOjMiOB7E2zdtm/hSeCKlD9fabzIUV4GoPcRyJRHi+4kGf0vsfGYbQ4nmPw== + dependencies: + css-declaration-sorter "^6.0.3" + cssnano-utils "^3.0.1" + postcss-calc "^8.2.0" + postcss-colormin "^5.2.4" + postcss-convert-values "^5.0.3" + postcss-discard-comments "^5.0.2" + postcss-discard-duplicates "^5.0.2" + postcss-discard-empty "^5.0.2" + postcss-discard-overridden "^5.0.3" + postcss-merge-longhand "^5.0.5" + postcss-merge-rules "^5.0.5" + postcss-minify-font-values "^5.0.3" + postcss-minify-gradients "^5.0.5" + postcss-minify-params "^5.0.4" + postcss-minify-selectors "^5.1.2" + postcss-normalize-charset "^5.0.2" + postcss-normalize-display-values "^5.0.2" + postcss-normalize-positions "^5.0.3" + postcss-normalize-repeat-style "^5.0.3" + postcss-normalize-string "^5.0.3" + postcss-normalize-timing-functions "^5.0.2" + postcss-normalize-unicode "^5.0.3" + postcss-normalize-url "^5.0.4" + postcss-normalize-whitespace "^5.0.3" + postcss-ordered-values "^5.0.4" + postcss-reduce-initial "^5.0.2" + postcss-reduce-transforms "^5.0.3" + postcss-svgo "^5.0.3" + postcss-unique-selectors "^5.0.3" + +cssnano-utils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.0.1.tgz#d3cc0a142d3d217f8736837ec0a2ccff6a89c6ea" + integrity sha512-VNCHL364lh++/ono+S3j9NlUK+d97KNkxI77NlqZU2W3xd2/qmyN61dsa47pTpb55zuU4G4lI7qFjAXZJH1OAQ== + +cssnano@^5.0.1, cssnano@^5.0.6: + version "5.0.16" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.16.tgz#4ee97d30411693f3de24cef70b36f7ae2a843e04" + integrity sha512-ryhRI9/B9VFCwPbb1z60LLK5/ldoExi7nwdnJzpkLZkm2/r7j2X3jfY+ZvDVJhC/0fPZlrAguYdHNFg0iglPKQ== + dependencies: + cssnano-preset-default "^5.1.11" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" + integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== + dependencies: + css-tree "^1.1.2" + +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: + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== + dependencies: + array-find-index "^1.0.1" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A== + +cypress@^12.3.0: + version "12.3.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.3.0.tgz#ae3fb0540aef4b5eab1ef2bcd0760caf2992b8bf" + integrity sha512-ZQNebibi6NBt51TRxRMYKeFvIiQZ01t50HSy7z/JMgRVqBUey3cdjog5MYEbzG6Ktti5ckDt1tfcC47lmFwXkw== + dependencies: + "@cypress/request" "^2.88.10" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^14.14.31" + "@types/sinonjs__fake-timers" "8.1.1" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + buffer "^5.6.0" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.1" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.2" + enquirer "^2.3.6" + eventemitter2 "6.4.7" + execa "4.1.0" + executable "^4.1.1" + extract-zip "2.0.1" + figures "^3.2.0" + fs-extra "^9.1.0" + getos "^3.2.1" + is-ci "^3.0.0" + is-installed-globally "~0.4.0" + lazy-ass "^1.6.0" + listr2 "^3.8.3" + lodash "^4.17.21" + log-symbols "^4.0.0" + minimist "^1.2.6" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" + request-progress "^3.0.0" + semver "^7.3.2" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + yauzl "^2.10.0" + +d3-array@2, d3-array@^2.3.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +d3-array@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.0.tgz#15bf96cd9b7333e02eb8de8053d78962eafcff14" + integrity sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g== + dependencies: + internmap "1 - 2" + +d3-color@1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" + integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== + +"d3-color@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" + integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== + +"d3-format@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" + integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== + +"d3-interpolate@1.2.0 - 2": + version "2.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" + integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== + dependencies: + d3-color "1 - 2" + +d3-interpolate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" + integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== + dependencies: + d3-color "1" + +d3-path@1, d3-path@^1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf" + integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg== + +d3-scale@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.3.0.tgz#28c600b29f47e5b9cd2df9749c206727966203f3" + integrity sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ== + dependencies: + d3-array "^2.3.0" + d3-format "1 - 2" + d3-interpolate "1.2.0 - 2" + d3-time "^2.1.1" + d3-time-format "2 - 3" + +d3-shape@^1.0.6, d3-shape@^1.2.0: + version "1.3.7" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7" + integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw== + dependencies: + d3-path "1" + +"d3-time-format@2 - 3": + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" + integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== + dependencies: + d3-time "1 - 2" + +"d3-time@1 - 2", d3-time@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" + integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== + dependencies: + d3-array "2" + +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" + +damerau-levenshtein@^1.0.7, 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== + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +data-urls@^3.0.1: + 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.0.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" + integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== + +dayjs@^1.10.4: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + +dayzed@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/dayzed/-/dayzed-3.2.3.tgz#78c5ab7e28579cd3c0f9f848b744ac5c768e9a22" + integrity sha512-qXTIKs+R6ydWwNo+X1wu3lUptyRSGoyY+ZzRcQSM0zUlaG+/Ei+bFjqbQm1T2oJ+WKNkTHURBcGsxnx9N+9kfA== + dependencies: + "@babel/runtime" "^7.6.2" + date-fns "^2.0.0" + +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.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@4, debug@^4.3.2, debug@^4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, 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" + +debug@^4.1.0, debug@^4.1.1, 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@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debuglog@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= + +decamelize@^1.1.2, decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decimal.js@^10.3.1, decimal.js@^10.4.2: + version "10.4.2" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e" + integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== + +decompress-response@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc" + integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ== + dependencies: + mimic-response "^3.1.0" + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= + +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@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.3.0.tgz#d3c47fd6f3a93d517b14426b0628a17b0125f5f7" + integrity sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA== + +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== + +default-browser-id@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-1.0.4.tgz#e59d09a5d157b828b876c26816e61c3d2a2c203a" + integrity sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw== + dependencies: + bplist-parser "^0.1.0" + meow "^3.1.0" + untildify "^2.0.0" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defer-to-connect@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" + integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.2, 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" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= + +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== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +denque@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg== + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +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== + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-package-manager@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" + integrity sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A== + dependencies: + execa "^5.1.1" + +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +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@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" + integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +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@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +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== + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^5.2.2: + version "5.4.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" + integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +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.10" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz#caa6d08f60388d0bb4539dd75fe458a9a1d0014c" + integrity sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g== + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +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" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.1, domhandler@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + +dot-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-1.1.2.tgz#1e73826900de28d6de5480bc1de31d0842b06bec" + integrity sha1-HnOCaQDeKNbeVIC8HeMdCEKwa+w= + dependencies: + sentence-case "^1.1.2" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dot-prop@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== + +dotenv@^8.0.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +dotenv@~10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" + integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== + +duplexer@^0.1.1, duplexer@^0.1.2, duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +ejs@3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.7.tgz#c544d9c7f715783dd92f0bddcf73a59e6962d006" + integrity sha512-BIar7R6abbUxDA3bfXrO4DSgwo8I+fB5/1zgujl3HLLjwd6+9iOnrT+t3grn2qbk9vOgBubXOFwX2m9axoFaGw== + dependencies: + jake "^10.8.5" + +ejs@^3.1.7, ejs@^3.1.8: + version "3.1.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" + integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.17: + version "1.4.196" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz#e18cdc5c1c2c2ebf78da237d0c374cc3b244d4cb" + integrity sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA== + +electron-to-chromium@^1.4.188: + version "1.4.192" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.192.tgz#fac050058b3e0713b401a1088cc579e14c2ab165" + integrity sha512-8nCXyIQY9An88NXAp+PuPy5h3/w5ZY7Iu2lag65Q0XREprcat5F8gKhoHsBUnQcFuCRnmevpR8yEBYRU3d2HDw== + +electron-to-chromium@^1.4.251: + version "1.4.284" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592" + integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA== + +electron-to-chromium@^1.4.84: + version "1.4.103" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.103.tgz#abfe376a4d70fa1e1b4b353b95df5d6dfd05da3a" + integrity sha512-c/uKWR1Z/W30Wy/sx3dkZoj4BijbXX85QKWu9jJfjho3LBAXNEGAEW3oWiGb+dotA6C6BzCTxL2/aLes7jlUeg== + +elliptic@^6.5.3, elliptic@^6.5.4: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" + integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== + +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== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +enabled@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" + integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +endent@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/endent/-/endent-2.1.0.tgz#5aaba698fb569e5e18e69e1ff7a28ff35373cd88" + integrity sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.5" + +enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.7.0, enhanced-resolve@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0" + integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +enhanced-resolve@^5.10.0, enhanced-resolve@^5.9.3: + 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" + +enquirer@^2.3.6, enquirer@~2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + +entities@^4.2.0, entities@^4.3.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + +errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, 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.0.6" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" + integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== + dependencies: + stackframe "^1.1.1" + +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: + 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-abstract@^1.20.4: + version "1.21.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" + integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.3" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.4" + is-array-buffer "^3.0.1" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.2" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.0" + has-symbols "^1.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.5" + isarray "^2.0.5" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +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.56" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.56.tgz#fd76bc935212203a83fef35bb58cddde26ae6c3c" + integrity sha512-YUhqzoMnIjMW5y8FzaMxsCu0eWCwq32GrlwhOhbQmL5OiZReWFm/KvRiYuvqf3CaG/zZ36Kyb4KfVe674cafCQ== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es5-shim@^4.5.13: + version "4.6.7" + resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" + integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ== + +es6-error@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-shim@^0.35.5: + version "0.35.6" + resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" + integrity sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA== + +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-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +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.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.1.tgz#b1a6602b0a339820585d4b2f8d2e08866b6699a7" + integrity sha512-/5S2XGWlGaiqrRhzpn51ux5JUSLwx8PVK2keLi5xk7QmhfYB8PqE6R6SlVw6hgnf/VexvUXSrlNJ/su00NhtHQ== + dependencies: + "@next/eslint-plugin-next" "13.1.1" + "@rushstack/eslint-patch" "^1.1.3" + "@typescript-eslint/parser" "^5.42.0" + eslint-import-resolver-node "^0.3.6" + eslint-import-resolver-typescript "^3.5.2" + 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@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.2.tgz#9431acded7d898fd94591a08ea9eec3514c7de91" + integrity sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ== + dependencies: + debug "^4.3.4" + enhanced-resolve "^5.10.0" + get-tsconfig "^4.2.0" + globby "^13.1.2" + is-core-module "^2.10.0" + is-glob "^4.0.3" + synckit "^0.8.4" + +eslint-module-utils@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-plugin-cypress@^2.10.3: + version "2.12.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz#9aeee700708ca8c058e00cdafe215199918c2632" + integrity sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA== + dependencies: + globals "^11.12.0" + +eslint-plugin-import@2.26.0, 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-json@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-3.1.0.tgz#251108ba1681c332e0a442ef9513bd293619de67" + integrity sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g== + dependencies: + lodash "^4.17.21" + vscode-json-languageservice "^4.1.6" + +eslint-plugin-jsx-a11y@6.6.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-jsx-a11y@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" + integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== + dependencies: + "@babel/runtime" "^7.16.3" + aria-query "^4.2.2" + array-includes "^3.1.4" + ast-types-flow "^0.0.7" + axe-core "^4.3.5" + axobject-query "^2.2.0" + damerau-levenshtein "^1.0.7" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.2.1" + language-tags "^1.0.5" + minimatch "^3.0.4" + +eslint-plugin-react-hooks@4.6.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.11: + version "7.31.11" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz#011521d2b16dcf95795df688a4770b4eaab364c8" + integrity sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw== + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + 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.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-plugin-react@^7.31.7: + version "7.31.10" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a" + integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA== + 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@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +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.15.0: + version "8.15.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.15.0.tgz#fea1d55a7062da48d82600d2e0974c55612a11e9" + integrity sha512-GG5USZ1jhCu8HJkzGgeK8/+RGnHaNYZGrGDzUtigK3BsGESW/rs2az23XqE0WVwDxy1VRvvjSSGu5nB0Bu+6SA== + dependencies: + "@eslint/eslintrc" "^1.2.3" + "@humanwhocodes/config-array" "^0.9.2" + 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.3.2" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + 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" + v8-compile-cache "^2.0.3" + +espree@^9.3.2: + version "9.3.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" + integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== + dependencies: + acorn "^8.7.1" + 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.0.1, 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.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@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +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-to-babel@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/estree-to-babel/-/estree-to-babel-3.2.1.tgz#82e78315275c3ca74475fdc8ac1a5103c8a75bf5" + integrity sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg== + dependencies: + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.2.0" + c8 "^7.6.0" + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +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== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +event-stream@3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter2@6.4.7: + version "6.4.7" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d" + integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg== + +eventemitter3@^4.0.0, eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.0.0, events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" + integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^5.0.0, execa@^5.1.1: + 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" + +executable@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c" + integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg== + dependencies: + pify "^2.2.0" + +exifr@^7.0.0: + version "7.1.3" + resolved "https://registry.yarnpkg.com/exifr/-/exifr-7.1.3.tgz#f6218012c36dbb7d843222011b27f065fddbab6f" + integrity sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw== + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + +express-async-errors@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/express-async-errors/-/express-async-errors-3.1.1.tgz#6053236d61d21ddef4892d6bd1d736889fc9da41" + integrity sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng== + +express-jwt-authz@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/express-jwt-authz/-/express-jwt-authz-2.4.1.tgz#405b303b3479ad3e862878ac0c08be191040c8e5" + integrity sha512-ruH86e2NvWicG9maStztyAyBJV0E8RsInXUm6Kuc/9pDtVJmJw3qigv1MEVs5bH+aksZuxocYZdz+N1V/9F+Dg== + +express-jwt@^7.7.7: + version "7.7.7" + resolved "https://registry.yarnpkg.com/express-jwt/-/express-jwt-7.7.7.tgz#b0400a8c759aba0774d84459e1d2eea7e9869d40" + integrity sha512-7s04HZ7sAahx7lwKU5AInhVon1kWVitRFxd7cGUKQaLDOQJsAQjB/OEBXaN0GS22RBgX75TjOJXGY7myQmVz5A== + dependencies: + "@types/jsonwebtoken" "^8.5.8" + express-unless "^2.1.3" + jsonwebtoken "^8.5.1" + +express-openid-connect@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/express-openid-connect/-/express-openid-connect-2.10.0.tgz#45eec4d33efe8a6555ecdf2388e1cd182abf4107" + integrity sha512-EuxzoNUhMI7eVNFMHms18rN0kuSxnviuEkuD0O1sfxVaQV7RF9gYiPUC+CygWuq4eoEF7tODZh2BQdNGhgIPiA== + dependencies: + base64url "^3.0.1" + cb "^0.1.0" + clone "^2.1.2" + cookie "^0.5.0" + debug "^4.3.4" + futoin-hkdf "^1.5.1" + http-errors "^1.8.1" + joi "^17.6.3" + jose "^2.0.6" + on-headers "^1.0.2" + openid-client "^4.9.1" + p-memoize "^4.0.4" + url-join "^4.0.1" + +express-unless@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-2.1.3.tgz#f951c6cca52a24da3de32d42cfd4db57bc0f9a2e" + integrity sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ== + +express@4.17.3: + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.19.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.4.2" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.9.7" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@4.18.2, express@^4.17.3: + version "4.18.2" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +express@^4.17.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.0" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.10.3" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +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-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0, extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-zip@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" + integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== + dependencies: + debug "^4.1.1" + get-stream "^5.1.0" + yauzl "^2.10.0" + optionalDependencies: + "@types/yauzl" "^2.9.1" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" + integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +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-glob@3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1" + integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q== + 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@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.0.3, fast-glob@^3.2.7, 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.11, 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-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.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 sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= + +fast-safe-stringify@^2.0.7: + version "2.1.1" + resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" + integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== + +fast-xml-parser@4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.0.11.tgz#42332a9aca544520631c8919e6ea871c0185a985" + integrity sha512-4aUg3aNRR/WjQAcpceODG1C3x3lFANXRo8+1biqfieHmg9pyMt7qB4lQV/Ta6sJCTbA5vfD8fnA8S54JATiFUA== + dependencies: + strnum "^1.0.5" + +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" + +faye-websocket@0.11.x, faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +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" + +fd-slicer@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" + integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= + dependencies: + pend "~1.2.0" + +fecha@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" + integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q== + +fetch-retry@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.3.tgz#edfa3641892995f9afee94f25b168827aa97fe3d" + integrity sha512-uJQyMrX5IJZkhoEUBQ3EjxkeiZkppBd5jS/fMTJmfZxLSiaQjv2zD0kTvuvkSH89uFvgSlB6ueGpjD3HWN7Bxw== + +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== + +figgy-pudding@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + +figures@3.2.0, figures@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +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" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +file-system-cache@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/file-system-cache/-/file-system-cache-1.1.0.tgz#984de17b976b75a77a27e08d6828137c1aa80fa1" + integrity sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw== + dependencies: + fs-extra "^10.1.0" + ramda "^0.28.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +filelist@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" + integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== + dependencies: + minimatch "^3.0.4" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +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" + +filter-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" + integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== + +finalhandler@1.1.2, finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-cache-dir@^3.3.1, find-cache-dir@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +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" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.1.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +fn.name@1.x.x: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" + integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== + +follow-redirects@^1.0.0, follow-redirects@^1.14.0: + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== + +follow-redirects@^1.14.7: + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== + +follow-redirects@^1.14.8: + version "1.14.8" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== + +follow-redirects@^1.14.9, follow-redirects@^1.15.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +fork-ts-checker-webpack-plugin@7.2.13: + version "7.2.13" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz#51ffd6a2f96f03ab64b92f8aedf305dbf3dee0f1" + integrity sha512-fR3WRkOb4bQdWB/y7ssDUlVdrclvwtyCUIHCfivAoYxq9dF7XfrDKbMdZIfwJ7hxIAqkYSGeU7lLJE6xrxIBdg== + dependencies: + "@babel/code-frame" "^7.16.7" + chalk "^4.1.2" + chokidar "^3.5.3" + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + fs-extra "^10.0.0" + memfs "^3.4.1" + minimatch "^3.0.4" + node-abort-controller "^3.0.1" + schema-utils "^3.1.1" + semver "^7.3.5" + tapable "^2.2.1" + +fork-ts-checker-webpack-plugin@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" + integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== + dependencies: + "@babel/code-frame" "^7.5.5" + chalk "^2.4.1" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +fork-ts-checker-webpack-plugin@^6.0.4: + version "6.5.2" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340" + integrity sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +form-data@^3.0.0, form-data@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +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" + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +formidable@^1.2.2: + version "1.2.6" + resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168" + integrity sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +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== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +framer-motion@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.5.1.tgz#802448a16a6eb764124bf36d8cbdfa6dd6b931a7" + integrity sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw== + dependencies: + "@motionone/dom" "10.12.0" + framesync "6.0.1" + hey-listen "^1.0.8" + popmotion "11.0.3" + style-value-types "5.0.0" + tslib "^2.1.0" + optionalDependencies: + "@emotion/is-prop-valid" "^0.8.2" + +framesync@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20" + integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA== + dependencies: + tslib "^2.1.0" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4= + +fs-constants@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" + integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== + +fs-extra@^10.0.0, fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs-extra@^9.0.0, fs-extra@^9.0.1, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-monkey@1.0.3, fs-monkey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA== + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +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@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@^2.1.2, 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.0, 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" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + +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== + +futoin-hkdf@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/futoin-hkdf/-/futoin-hkdf-1.5.1.tgz#141f00427bc9950b38a42aa786b99c318b9b688d" + integrity sha512-g5d0Qp7ks55hYmYmfqn4Nz18XH49lcCR+vvIvHT92xXnsJaGZmY1EtWQWilJ6BQp57heCIXM/rRo+AFep8hGgg== + +fuzzysearch@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fuzzysearch/-/fuzzysearch-1.0.3.tgz#dffc80f6d6b04223f2226aa79dd194231096d008" + integrity sha512-s+kNWQuI3mo9OALw0HJ6YGmMbLqEufCh2nX/zzV5CrICQ/y4AwPxM+6TIiF9ItFCHXFCyM/BfCCmN57NTIJuPg== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +generic-names@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/generic-names/-/generic-names-4.0.0.tgz#0bd8a2fd23fe8ea16cbd0a279acd69c06933d9a3" + integrity sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A== + dependencies: + loader-utils "^3.2.0" + +gensync@^1.0.0-beta.1, 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.1, 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-intrinsic@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" + integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A== + 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-port@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^5.0.0, get-stream@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" + integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== + dependencies: + pump "^3.0.0" + +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" + +get-tsconfig@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.2.0.tgz#ff368dd7104dab47bf923404eb93838245c66543" + integrity sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg== + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +getos@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5" + integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q== + dependencies: + async "^3.2.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +github-slugger@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" + integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +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.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-promise@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" + integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== + dependencies: + "@types/glob" "*" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" + integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A== + 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.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.1.6: + 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" + +global-agent@^2.1.12: + version "2.2.0" + resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc" + integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg== + dependencies: + boolean "^3.0.1" + core-js "^3.6.5" + es6-error "^4.1.1" + matcher "^3.0.0" + roarr "^2.15.3" + semver "^7.3.2" + serialize-error "^7.0.1" + +global-dirs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" + integrity sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA== + dependencies: + ini "2.0.0" + +global@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0, globals@^11.12.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.16.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.16.0.tgz#9be4aca28f311aaeb974ea54978ebbb5e35ce46a" + integrity sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q== + dependencies: + type-fest "^0.20.2" + +globals@^13.6.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" + integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.0, globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globalthis@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" + integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== + dependencies: + define-properties "^1.1.3" + +globalyzer@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" + integrity sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q== + +globby@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" + integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== + dependencies: + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" + +globby@^11.0.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" + +globby@^12.0.2: + version "12.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-12.2.0.tgz#2ab8046b4fba4ff6eede835b29f678f90e3d3c22" + integrity sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA== + dependencies: + array-union "^3.0.1" + dir-glob "^3.0.1" + fast-glob "^3.2.7" + ignore "^5.1.9" + merge2 "^1.4.1" + slash "^4.0.0" + +globby@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" + integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.2.11" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^4.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +globrex@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098" + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg== + +goober@^2.1.10: + version "2.1.10" + resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.10.tgz#058def43ba1e3b06f973dbb372a4978aa42f1049" + integrity sha512-7PpuQMH10jaTWm33sQgBQvz45pHR8N4l3Cu3WMGEWmHShAcTuuP7I+5/DwKo39fwti5A80WAjvqgz6SSlgWmGA== + +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" + +got@^11.8.0: + version "11.8.3" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770" + integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg== + dependencies: + "@sindresorhus/is" "^4.0.0" + "@szmarczak/http-timer" "^4.0.5" + "@types/cacheable-request" "^6.0.1" + "@types/responselike" "^1.0.0" + cacheable-lookup "^5.0.3" + cacheable-request "^7.0.2" + decompress-response "^6.0.0" + http2-wrapper "^1.0.0-beta.5.2" + lowercase-keys "^2.0.0" + p-cancelable "^2.0.0" + responselike "^2.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +harmony-reflect@^1.4.6: + version "1.6.2" + resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710" + integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g== + +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-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-1.0.0.tgz#9aaa9eedbffb1ba3990a7b0010fb678ee0081207" + integrity sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g== + dependencies: + is-glob "^3.0.0" + +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-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.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-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +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" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +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-entities@^2.1.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-entities@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488" + integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ== + +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== + +html-minifier-terser@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054" + integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" + integrity sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.20" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +html-webpack-plugin@^5.0.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + domutils "^3.0.1" + entities "^4.3.0" + +http-auth@3.1.x: + version "3.1.3" + resolved "https://registry.yarnpkg.com/http-auth/-/http-auth-3.1.3.tgz#945cfadd66521eaf8f7c84913d377d7b15f24e31" + integrity sha1-lFz63WZSHq+PfISRPTd9exXyTjE= + dependencies: + apache-crypt "^1.1.2" + apache-md5 "^1.0.6" + bcryptjs "^2.3.0" + uuid "^3.0.0" + +http-cache-semantics@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.8.1, http-errors@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +http-errors@2.0.0, http-errors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" + integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +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" + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +http-server@^14.1.0: + version "14.1.1" + resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e" + integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A== + dependencies: + basic-auth "^2.0.1" + chalk "^4.1.2" + corser "^2.0.1" + he "^1.2.0" + html-encoding-sniffer "^3.0.0" + http-proxy "^1.18.1" + mime "^1.6.0" + minimist "^1.2.6" + opener "^1.5.1" + portfinder "^1.0.28" + secure-compare "3.0.1" + union "~0.5.0" + url-join "^4.0.1" + +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + +http2-wrapper@^1.0.0-beta.5.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" + integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== + dependencies: + quick-lru "^5.1.1" + resolve-alpn "^1.0.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + +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== + +husky@^7.0.0: + version "7.0.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== + +hyphenate-style-name@^1.0.0: + 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.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.6.3, 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" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + integrity sha1-Bupvg2ead0njhs/h/oEq5dsiPe0= + +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +identity-obj-proxy@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14" + integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ= + dependencies: + harmony-reflect "^1.4.6" + +ieee754@^1.1.13, ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.0.4, ignore@^5.1.1, ignore@^5.1.9, 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== + +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= + +immutable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== + +import-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-3.0.0.tgz#20845547718015126ea9b3676b7592fb8bd4cf92" + integrity sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg== + dependencies: + import-from "^3.0.0" + +import-fresh@^3.0.0, import-fresh@^3.1.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-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" + integrity sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ== + dependencies: + resolve-from "^5.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@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg== + dependencies: + repeating "^2.0.0" + +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== + +infer-owner@^1.0.3, infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +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, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +ini@2.0.0, ini@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + +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== + +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" + +internal-slot@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" + integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + side-channel "^1.0.4" + +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ioredis@^5.0.0, ioredis@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.2.4.tgz#9e262a668bc29bae98f2054c1e0d7efd86996b96" + integrity sha512-qIpuAEt32lZJQ0XyrloCRdlEdUUNGG9i0UOk6zgzK6igyudNWqEBxfH6OlbnOOoBBvr1WB02mm8fR55CnikRng== + dependencies: + "@ioredis/commands" "^1.1.1" + cluster-key-slot "^1.1.0" + debug "^4.3.4" + denque "^2.0.1" + lodash.defaults "^4.2.0" + lodash.isarguments "^3.1.0" + redis-errors "^1.2.0" + redis-parser "^3.0.0" + standard-as-callback "^2.1.0" + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-absolute-url@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arguments@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" + integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-typed-array "^1.1.10" + +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@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== + dependencies: + binary-extensions "^1.0.0" + +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@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +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.3, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +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-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-ci@^3.0.0, is-ci@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" + integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== + dependencies: + ci-info "^3.2.0" + +is-core-module@^2.10.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-core-module@^2.2.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +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@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-dom@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" + integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== + dependencies: + is-object "^1.0.1" + is-window "^1.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, 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-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +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-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-function@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +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@^3.0.0, is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== + dependencies: + is-extglob "^2.1.0" + +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@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-installed-globally@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520" + integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ== + dependencies: + global-dirs "^3.0.0" + is-path-inside "^3.0.2" + +is-lower-case@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-lower-case/-/is-lower-case-1.1.3.tgz#7e147be4768dc466db3bfb21cc60b31e6ad69393" + integrity sha1-fhR75HaNxGbbO/shzGCzHmrWk5M= + dependencies: + lower-case "^1.1.0" + +is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-mobile@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-3.1.1.tgz#3b9e48f40068e4ea2da411f5009779844ce8d6aa" + integrity sha512-RRoXXR2HNFxNkUnxtaBdGBXtFlUMFa06S0NUKf/LCF+MuGLu13gi9iBCkoEmc6+rpXuwi5Mso5V8Zf7mNynMBQ== + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +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@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +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-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-path-inside@^3.0.2: + 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@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@5.0.0, is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-plain-object@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.1.tgz#662d92d24c0aa4302407b0d45d21f2251c85f85b" + integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== + +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@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.2, 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-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shallow-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-shallow-equal/-/is-shallow-equal-1.0.1.tgz#c410b51eb1c12ee50cd02891d32d1691a132d73c" + integrity sha512-lq5RvK+85Hs5J3p4oA4256M1FEffzmI533ikeDHvJd42nouRRx5wBzt36JuviiGe5dIPyHON/d0/Up+PBo6XkQ== + +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@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= + +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-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-typedarray@^1.0.0, is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-upper-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-upper-case/-/is-upper-case-1.1.2.tgz#8d0b1fa7e7933a1e58483600ec7d9661cbaf756f" + integrity sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8= + dependencies: + upper-case "^1.1.0" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + +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" + +is-what@^4.1.6: + version "4.1.7" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.7.tgz#c41dc1d2d2d6a9285c624c2505f61849c8b1f9cc" + integrity sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ== + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-window@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" + integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + +is-wsl@^2.1.1, is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +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@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, 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== + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +isomorphic-unfetch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" + integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.0.1, 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.1.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz#7b49198b657b27a730b8e9cb601f1e1bff24c59a" + integrity sha512-czwUz525rkOFDJxfKK6mYfIs9zBKILyrZQxjz3ABhjQXhbhFsSbo1HW/BFcsDnfJYJWA6thRR5/TUY2qs5W99Q== + 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.0.2, istanbul-reports@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.3.tgz#4bcae3103b94518117930d51283690960b50d3c2" + integrity sha512-x9LtDVtfm/t1GFiLl3NffC7hz+I1ragvgX1P/Lg1NlIagifZDKUkuuaAxH/qpwj2IuEfD8G2Bs/UKp+sZ/pKkg== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iterate-iterator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" + integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== + +iterate-value@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +jake@^10.8.5: + version "10.8.5" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" + integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.1" + minimatch "^3.0.4" + +jest-changed-files@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" + integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + +jest-circus@^28.1.1, jest-circus@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" + integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + p-limit "^3.1.0" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^28.1.1: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" + integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== + dependencies: + "@jest/core" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.1.tgz#e90b97b984f14a6c24a221859e81b258990fce2f" + integrity sha512-tASynMhS+jVV85zKvjfbJ8nUyJS/jUSYZ5KQxLUN2ZCvcQc/OmhQl2j6VEL3ezQkNofxn5pQ3SPYWPHb0unTZA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^28.1.1" + "@jest/types" "^28.1.1" + babel-jest "^28.1.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^28.1.1" + jest-environment-node "^28.1.1" + jest-get-type "^28.0.2" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.1" + jest-runner "^28.1.1" + jest-util "^28.1.1" + jest-validate "^28.1.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^28.1.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-config@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" + integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^28.1.3" + "@jest/types" "^28.1.3" + babel-jest "^28.1.3" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^28.1.3" + jest-environment-node "^28.1.3" + jest-get-type "^28.0.2" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-runner "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^28.1.3" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.0.0: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.4.6.tgz#93815774d2012a2cbb6cf23f84d48c7a2618f98d" + integrity sha512-zjaB0sh0Lb13VyPsd92V7HkqF6yKRH9vm33rwBt7rPYrpQvS1nCvlIy2pICbKta+ZjWngYLNn4cCK4nyZkjS/w== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.4.0" + jest-get-type "^27.4.0" + pretty-format "^27.4.6" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.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-docblock@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" + integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== + dependencies: + detect-newline "^3.0.0" + +jest-each@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" + integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== + dependencies: + "@jest/types" "^28.1.3" + chalk "^4.0.0" + jest-get-type "^28.0.2" + jest-util "^28.1.3" + pretty-format "^28.1.3" + +jest-environment-jsdom@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-28.1.1.tgz#8bd721915b32f9b196723292c4461a0ad548b55b" + integrity sha512-41ZvgSoPNcKG5q3LuuOcAczdBxRq9DbZkPe24okN6ZCmiZdAfFtPg3z+lOtsT1fM6OAERApKT+3m0MRDQH2zIA== + dependencies: + "@jest/environment" "^28.1.1" + "@jest/fake-timers" "^28.1.1" + "@jest/types" "^28.1.1" + "@types/jsdom" "^16.2.4" + "@types/node" "*" + jest-mock "^28.1.1" + jest-util "^28.1.1" + jsdom "^19.0.0" + +jest-environment-node@^28.1.1, jest-environment-node@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" + integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + jest-util "^28.1.3" + +jest-get-type@^27.4.0: + version "27.4.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.4.0.tgz#7503d2663fffa431638337b3998d39c5e928e9b5" + integrity sha512-tk9o+ld5TWq41DkK14L4wox4s2D9MtTpKaAVzXfr5CUKm5ZK2ExcaFE0qls2W71zE/6R2TxxrK9w2r6svAFDBQ== + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +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-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-haste-map@^28.1.1, jest-haste-map@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" + integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== + dependencies: + "@jest/types" "^28.1.3" + "@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 "^28.0.2" + jest-util "^28.1.3" + jest-worker "^28.1.3" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" + integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== + dependencies: + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-matcher-utils@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^28.1.3: + 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-message-util@^28.1.1, jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock-extended@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/jest-mock-extended/-/jest-mock-extended-2.0.5.tgz#1feca6866ecbb4e5e79e57d9286969fde53a07cf" + integrity sha512-cQjKRqzZ/hUyy3AdmB7Aa+0DB45dt/eEApwfZFZzup9oefGnw2QodW/BLR39aCpPkK0Qb54P5OKYRXJ98kQ8cQ== + dependencies: + ts-essentials "^7.0.3" + +jest-mock@^28.1.1, jest-mock@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" + integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + +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@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-regex-util@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" + integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== + +jest-resolve-dependencies@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" + integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== + dependencies: + jest-regex-util "^28.0.2" + jest-snapshot "^28.1.3" + +jest-resolve@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.1.tgz#bc2eaf384abdcc1aaf3ba7c50d1adf01e59095e5" + integrity sha512-/d1UbyUkf9nvsgdBildLe6LAD4DalgkgZcKd0nZ8XUGPyA/7fsnaQIlKVnDiuUXv/IeZhPEDrRJubVSulxrShA== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.1" + jest-pnp-resolver "^1.2.2" + jest-util "^28.1.1" + jest-validate "^28.1.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-resolve@^28.1.1, jest-resolve@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" + integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-pnp-resolver "^1.2.2" + jest-util "^28.1.3" + jest-validate "^28.1.3" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^28.1.1, jest-runner@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" + integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/environment" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.10.2" + graceful-fs "^4.2.9" + jest-docblock "^28.1.1" + jest-environment-node "^28.1.3" + jest-haste-map "^28.1.3" + jest-leak-detector "^28.1.3" + jest-message-util "^28.1.3" + jest-resolve "^28.1.3" + jest-runtime "^28.1.3" + jest-util "^28.1.3" + jest-watcher "^28.1.3" + jest-worker "^28.1.3" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" + integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/globals" "^28.1.3" + "@jest/source-map" "^28.1.2" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" + integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^28.1.3" + graceful-fs "^4.2.9" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + jest-haste-map "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + natural-compare "^1.4.0" + pretty-format "^28.1.3" + semver "^7.3.5" + +jest-util@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.1.tgz#ff39e436a1aca397c0ab998db5a51ae2b7080d05" + integrity sha512-FktOu7ca1DZSyhPAxgxB6hfh2+9zMoJ7aEQA759Z6p45NuO8mWcqujH+UdHlCm/V6JTWwDztM2ITCzU1ijJAfw== + dependencies: + "@jest/types" "^28.1.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-util@^28.0.0, jest-util@^28.1.1, jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^28.1.1, jest-validate@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" + integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== + dependencies: + "@jest/types" "^28.1.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^28.0.2" + leven "^3.1.0" + pretty-format "^28.1.3" + +jest-watcher@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" + integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== + dependencies: + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.10.2" + jest-util "^28.1.3" + string-length "^4.0.1" + +jest-worker@^26.5.0, jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.0.2, jest-worker@^27.4.1: + version "27.4.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" + integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^28.1.1, jest-worker@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.1.tgz#3c39a3a09791e16e9ef283597d24ab19a0df701e" + integrity sha512-qw9YHBnjt6TCbIDMPMpJZqf9E12rh6869iZaN08/vpOGgHJSAaLLUn6H8W3IAEuy34Ls3rct064mZLETkxJ2XA== + dependencies: + "@jest/core" "^28.1.1" + "@jest/types" "^28.1.1" + import-local "^3.0.2" + jest-cli "^28.1.1" + +joi@^17.6.0: + version "17.6.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.0.tgz#0bb54f2f006c09a96e75ce687957bd04290054b2" + integrity sha512-OX5dG6DTbcr/kbMFj0KGYxuew69HPcAE3K/sZpEV2nP6e/j/C0HV+HNiBPCASxdx5T7DMoa0s8UeHWMnb6n2zw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +joi@^17.6.3: + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +jose@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.5.tgz#29746a18d9fff7dcf9d5d2a6f62cb0c7cd27abd3" + integrity sha512-BAiDNeDKTMgk4tvD0BbxJ8xHEHBZgpeRZ1zGPPsitSyMgjoMWiLGYAE7H7NpP5h0lPppQajQs871E8NHUrzVPA== + dependencies: + "@panva/asn1.js" "^1.0.0" + +jose@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.6.tgz#894ba19169af339d3911be933f913dd02fc57c7c" + integrity sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg== + dependencies: + "@panva/asn1.js" "^1.0.0" + +jose@^4.10.0, jose@^4.9.2: + version "4.11.1" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.1.tgz#8f7443549befe5bddcf4bae664a9cbc1a62da4fa" + integrity sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q== + +jose@^4.10.3: + version "4.11.0" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.11.0.tgz#1c7f5c7806383d3e836434e8f49da531cb046a9d" + integrity sha512-wLe+lJHeG8Xt6uEubS4x0LVjS/3kXXu9dGoj9BNnlhYq7Kts0Pbb2pvv5KiI0yaKH/eaiR0LUOBhOVo9ktd05A== + +js-string-escape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== + +"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@4.1.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" + +js-yaml@^3.10.0, 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" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +jsdom@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== + dependencies: + abab "^2.0.5" + acorn "^8.5.0" + acorn-globals "^6.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.1" + 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.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + 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 "^10.0.0" + ws "^8.2.3" + 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== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + 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-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== + +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 sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +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.1.2, json5@^2.1.3, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonc-parser@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + +jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" + verror "1.10.0" + +"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b" + integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA== + dependencies: + array-includes "^3.1.3" + object.assign "^4.1.2" + +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" + +junk@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" + integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jwk-to-pem@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz#151310bcfbcf731adc5ad9f379cbc8b395742906" + integrity sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A== + dependencies: + asn1.js "^5.3.0" + elliptic "^6.5.4" + safe-buffer "^5.0.1" + +jwks-rsa@^1.12.1: + version "1.12.3" + resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-1.12.3.tgz#40232f85d16734cb82837f38bb3e350a34435400" + integrity sha512-cFipFDeYYaO9FhhYJcZWX/IyZgc0+g316rcHnDpT2dNRNIE/lMOmWKKqp09TkJoYlNFzrEVODsR4GgXJMgWhnA== + dependencies: + "@types/express-jwt" "0.0.42" + axios "^0.21.1" + debug "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + jsonwebtoken "^8.5.1" + limiter "^1.1.5" + lru-memoizer "^2.1.2" + ms "^2.1.2" + proxy-from-env "^1.1.0" + +jwks-rsa@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-3.0.0.tgz#5c0b298efbf4b4a47069184b2005fe94e8c088da" + integrity sha512-x9qNrP/kD6tOfrLzBVC5HaneBTR+fCEGIjwk/xSdl+KA7Tzf+R3oiY9ibrONKVLF9fR0V03enkitYPZkO65fAQ== + dependencies: + "@types/express" "^4.17.14" + "@types/jsonwebtoken" "^8.5.9" + debug "^4.3.4" + jose "^4.10.3" + limiter "^1.1.5" + lru-memoizer "^2.1.4" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" + integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== + +keyv@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.5.tgz#bb12b467aba372fab2a44d4420c00d3c4ebd484c" + integrity sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA== + dependencies: + json-buffer "3.0.1" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +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== + +klona@^2.0.4, klona@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + +kuler@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" + integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== + +language-subtag-registry@~0.3.2: + version "0.3.21" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" + integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== + +language-tags@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" + integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= + dependencies: + language-subtag-registry "~0.3.2" + +launchdarkly-eventsource@1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/launchdarkly-eventsource/-/launchdarkly-eventsource-1.4.4.tgz#fa595af8602e487c61520787170376c6a1104459" + integrity sha512-GL+r2Y3WccJlhFyL2buNKel+9VaMnYpbE/FfCkOST5jSNSFodahlxtGyrE8o7R+Qhobyq0Ree4a7iafJDQi9VQ== + +launchdarkly-js-client-sdk@2.20.2: + version "2.20.2" + resolved "https://registry.yarnpkg.com/launchdarkly-js-client-sdk/-/launchdarkly-js-client-sdk-2.20.2.tgz#4c5a72cc8e2d0b5dbb3d4dd31a30e36b243b9e8f" + integrity sha512-KAAb1nnpAiQF4dCohe0d1M7QSxhJfa9v6+5PJUPANAVZ3EwX/y6aL7mK8Mik2IrL5I654mseXxidfSbnHVMBXA== + dependencies: + escape-string-regexp "^1.0.5" + launchdarkly-js-sdk-common "3.5.1" + +launchdarkly-js-sdk-common@3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/launchdarkly-js-sdk-common/-/launchdarkly-js-sdk-common-3.5.1.tgz#25966a5b25311bba4db7c9f3807031166b97545d" + integrity sha512-MTIn5XkREKCb87A78QTQXNEPaDaC35PzW1mU8bTf1hdxgm78LiDtXjvtKvVk+oU5tqFrH7dZrLKMCGkt03VaKw== + dependencies: + base64-js "^1.3.0" + fast-deep-equal "^2.0.1" + uuid "^3.3.2" + +launchdarkly-node-server-sdk@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/launchdarkly-node-server-sdk/-/launchdarkly-node-server-sdk-6.4.3.tgz#7a35caee3709d70776173fcd814fa0cd88de1270" + integrity sha512-n7L2mAAcKqUUKhCGHRd6soVD3Ws4ME4R+MNShWL0lBBY8yGASIaPAW4rGmkob/hs+grCNRXn8HHlZCufecaX4w== + dependencies: + async "^3.1.0" + launchdarkly-eventsource "1.4.4" + lru-cache "^6.0.0" + node-cache "^5.1.0" + semver "^7.3.0" + tunnel "0.0.6" + uuid "^8.3.2" + +launchdarkly-react-client-sdk@^2.25.1: + version "2.25.1" + resolved "https://registry.yarnpkg.com/launchdarkly-react-client-sdk/-/launchdarkly-react-client-sdk-2.25.1.tgz#fdfbb532e239227e1f64efa1dccff42ae0ce5533" + integrity sha512-OejNEZs8QvW1q/qn/ey1o6lfdLw3aW8tWfLucUmRTeazp0MtBXARnEZoqHWGmgRkRlxkTul3HqecksnoXC/wBg== + dependencies: + hoist-non-react-statics "^3.3.2" + launchdarkly-js-client-sdk "2.20.2" + lodash.camelcase "^4.3.0" + uuid "^3.3.2" + +lazy-ass@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= + +lazy-universal-dotenv@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" + integrity sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ== + dependencies: + "@babel/runtime" "^7.5.0" + app-root-dir "^1.0.2" + core-js "^3.0.4" + dotenv "^8.0.0" + dotenv-expand "^5.1.0" + +less-loader@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.1.0.tgz#a452384259bdf8e4f6d5fdcc39543609e6313f82" + integrity sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug== + dependencies: + klona "^2.0.4" + +less@3.12.2: + version "3.12.2" + resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" + integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== + dependencies: + tslib "^1.10.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + native-request "^1.0.5" + source-map "~0.6.0" + +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 sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +license-webpack-plugin@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz#1e18442ed20b754b82f1adeff42249b81d11aec6" + integrity sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw== + dependencies: + webpack-sources "^3.0.0" + +lilconfig@2.0.4, lilconfig@^2.0.3, lilconfig@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082" + integrity sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA== + +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== + +limiter@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.5.tgz#8f92a25b3b16c6131293a0cc834b4a838a2aa7c2" + integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== + +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== + +lines-and-columns@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.3.tgz#b2f0badedb556b747020ab8ea7f0373e22efac1b" + integrity sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w== + +lint-staged@^12.3.7: + version "12.3.7" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-12.3.7.tgz#ad0e2014302f704f9cf2c0ebdb97ac63d0f17be0" + integrity sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.16" + commander "^8.3.0" + debug "^4.3.3" + execa "^5.1.1" + lilconfig "2.0.4" + listr2 "^4.0.1" + micromatch "^4.0.4" + normalize-path "^3.0.0" + object-inspect "^1.12.0" + pidtree "^0.5.0" + string-argv "^0.3.1" + supports-color "^9.2.1" + yaml "^1.10.2" + +listr2@^3.8.3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e" + integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.1" + through "^2.3.8" + wrap-ansi "^7.0.0" + +listr2@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-4.0.1.tgz#e050c1fd390276e191f582603d6e3531cd6fd2b3" + integrity sha512-D65Nl+zyYHL2jQBGmxtH/pU8koPZo5C8iCNE8EoB04RwPgQG1wuaKwVbeZv9LJpiH4Nxs0FCp+nNcG8OqpniiA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.5.2" + through "^2.3.8" + wrap-ansi "^7.0.0" + +live-server@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/live-server/-/live-server-1.2.2.tgz#20b4fe5c2ca886faa61813310e28680804f48dad" + integrity sha512-t28HXLjITRGoMSrCOv4eZ88viHaBVIjKjdI5PO92Vxlu+twbk6aE0t7dVIaz6ZWkjPilYFV6OSdMYl9ybN2B4w== + dependencies: + chokidar "^2.0.4" + colors "1.4.0" + connect "^3.6.6" + cors latest + event-stream "3.3.4" + faye-websocket "0.11.x" + http-auth "3.1.x" + morgan "^1.9.1" + object-assign latest + opn latest + proxy-middleware latest + send latest + serve-index "^1.9.1" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-runner@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" + integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== + +loader-utils@^1.2.3, loader-utils@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" + integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^2.0.3, loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" + integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +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.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= + +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.clonedeep@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.escaperegexp@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" + integrity sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw== + +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + +lodash.groupby@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1" + integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw== + +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnil@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c" + integrity sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.isundefined@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" + integrity sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA== + +lodash.memoize@4.x, lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + +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.once@^4.0.0, lodash.once@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== + +lodash.uniq@4.5.0, lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= + +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + +logform@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.3.2.tgz#68babe6a74ab09a1fd15a9b1e6cbc7713d41cb5b" + integrity sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA== + dependencies: + colors "1.4.0" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^1.1.0" + triple-beam "^1.3.0" + +logform@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== + dependencies: + "@colors/colors" "1.5.0" + fecha "^4.2.0" + ms "^2.1.1" + safe-stable-stringify "^2.3.1" + triple-beam "^1.3.0" + +loose-envify@^1.0.0, 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" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ== + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case-first@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" + integrity sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E= + dependencies: + lower-case "^1.1.2" + +lower-case@^1.1.0, lower-case@^1.1.1, lower-case@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" + integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +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" + +lru-cache@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + integrity sha1-HRdnnAac2l0ECZGgnbwsDbN35V4= + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-memoizer@^2.1.2, lru-memoizer@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-2.1.4.tgz#b864d92b557f00b1eeb322156a0409cb06dafac6" + integrity sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ== + dependencies: + lodash.clonedeep "^4.5.0" + lru-cache "~4.0.0" + +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + +luxon@^1.28.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + +luxon@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.1.0.tgz#9ac33d7142b7ea18d4ec8583cdeb0b079abef60d" + integrity sha512-7w6hmKC0/aoWnEsmPCu5Br54BmbmUp5GfcqBxQngRcXJ+q5fdfjEzn7dxmJh2YdDhgW8PccYtlWKSv4tQkrTQg== + +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" + integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= + +magic-string@^0.25.7: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.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, make-error@^1.1.1, make-error@^1.3.6: + 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" + +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +matcher@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" + integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== + dependencies: + escape-string-regexp "^4.0.0" + +matchmediaquery@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/matchmediaquery/-/matchmediaquery-0.3.1.tgz#8247edc47e499ebb7c58f62a9ff9ccf5b815c6d7" + integrity sha512-Hlk20WQHRIm9EE9luN1kjRjYXAQToHOIAHPJn9buxBwuhfTHoKUcX+lXBbxc85DVQfXYbEQ4HcwQdd128E3qHQ== + dependencies: + css-mediaquery "^0.1.2" + +math-expression-evaluator@^1.2.14: + version "1.3.11" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.3.11.tgz#37828095bd29c37f1974c7b11e7410c8758df851" + integrity sha512-xOm6QnxKj4aS216gb2YMFbEl7PP4+BkvBjy0cphVq2FTaU6O3BWVPqMA4bsTeCTaMWDeLrC2fymbRQ98GACkeg== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + +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== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +mem@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122" + integrity sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^3.1.0" + +memfs@^3.1.2: + version "3.4.7" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.7.tgz#e5252ad2242a724f938cb937e3c4f7ceb1f70e5a" + integrity sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw== + dependencies: + fs-monkey "^1.0.3" + +memfs@^3.2.2: + version "3.4.1" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.1.tgz#b78092f466a0dce054d63d39275b24c71d3f1305" + integrity sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw== + dependencies: + fs-monkey "1.0.3" + +memfs@^3.4.1, memfs@^3.4.3: + version "3.4.11" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.11.tgz#3a34837ade675825d805a2c135e88cefe5e53aaf" + integrity sha512-GvsCITGAyDCxxsJ+X6prJexFQEhOCJaIlUbsAvjzSI5o5O7j2dle3jWvz5Z5aOdpOxW6ol3vI1+0ut+641F1+w== + dependencies: + fs-monkey "^1.0.3" + +memoize-one@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" + integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== + +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + integrity sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog== + dependencies: + map-or-similar "^1.5.0" + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +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.2.3, 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== + +methods@^1.1.2, methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.0, micromatch@^4.0.2: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +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" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.51.0: + version "1.51.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" + integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g== + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-match@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mime-match/-/mime-match-1.0.2.tgz#3f87c31e9af1a5fd485fb9db134428b23bbb7ba8" + integrity sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg== + dependencies: + wildcard "^1.1.0" + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.35, mime-types@~2.1.24: + 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" + +mime-types@^2.1.30, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.34: + version "2.1.34" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24" + integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A== + dependencies: + mime-db "1.51.0" + +mime@1.6.0, mime@^1.4.1, mime@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4, mime@^2.4.6: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +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== + +mimic-fn@^3.0.0, mimic-fn@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + +mimic-response@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" + integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== + +mimic-response@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" + integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +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== + +mini-css-extract-plugin@~2.4.7: + version "2.4.7" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.4.7.tgz#b9f4c4f4d727c7a3cd52a11773bb739f00177fac" + integrity sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg== + dependencies: + schema-utils "^4.0.0" + +mini-svg-data-uri@^1.2.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.3.tgz#43177b2e93766ba338931a3e2a84a3dfd3a222b8" + integrity sha512-gSfqpMRC8IxghvMcxzzmMnWpXAChSA+vy4cia33RgerMS8Fex95akUyQZPbxJJmeBGiGmK7n/1OpUX8ksRjIdA== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +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.1.1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, 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== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1: + version "3.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" + integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^0.5.5: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +morgan@^1.10.0, morgan@^1.9.1: + version "1.10.0" + resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7" + integrity sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ== + dependencies: + basic-auth "~2.0.1" + debug "2.6.9" + depd "~2.0.0" + on-finished "~2.3.0" + on-headers "~1.0.2" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ== + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +mrmime@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" + integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== + +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.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +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.3, ms@^2.1.1, ms@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +msgpackr-extract@^1.0.14: + version "1.0.16" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz#701c4f6e6f25c100ae84557092274e8fffeefe45" + integrity sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA== + dependencies: + nan "^2.14.2" + node-gyp-build "^4.2.3" + +msgpackr@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.5.2.tgz#b400c9885642bdec27b284f8bdadbd6570b448b7" + integrity sha512-OCguCkbG34x1ddO4vAzEm/4J1GTo512k9SoxV8K+EGfI/onFdpemRf0HpsVRFpxadXr4JBFgHsQUitgTlw7ZYQ== + optionalDependencies: + msgpackr-extract "^1.0.14" + +multer@^1.4.5-lts.1: + version "1.4.5-lts.1" + resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" + integrity sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ== + dependencies: + append-field "^1.0.0" + busboy "^1.0.0" + concat-stream "^1.5.2" + mkdirp "^0.5.4" + object-assign "^4.1.1" + type-is "^1.6.4" + xtend "^4.0.0" + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +namespace-emitter@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/namespace-emitter/-/namespace-emitter-2.0.1.tgz#978d51361c61313b4e6b8cf6f3853d08dfa2b17c" + integrity sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g== + +nan@^2.12.1: + version "2.16.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" + integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + +nan@^2.14.2: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + +nanoid@^3.1.30: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== + +nanoid@^3.3.1, 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== + +nanoid@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-4.0.0.tgz#6e144dee117609232c3f415c34b0e550e64999a5" + integrity sha512-IgBP8piMxe/gf73RTQx7hmnhwz0aaEXYakvqZyE302IXW3HyVNhdNGC+O2MwMAVhLEnvXlvKtGbtJf6wvHihCg== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +native-request@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.1.0.tgz#acdb30fe2eefa3e1bc8c54b3a6852e9c5c0d3cb0" + integrity sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= + +nconf@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/nconf/-/nconf-0.12.0.tgz#9cf70757aae4d440d43ed53c42f87da18471b8bf" + integrity sha512-T3fZPw3c7Dfrz8JBQEbEcZJ2s8f7cUMpKuyBtsGQe0b71pcXx6gNh4oti2xh5dxB+gO9ufNfISBlGvvWtfyMcA== + dependencies: + async "^3.0.0" + ini "^2.0.0" + secure-keys "^1.0.0" + yargs "^16.1.1" + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" + integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== + +next-tick@^1.0.0, 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@13.1.1: + version "13.1.1" + resolved "https://registry.yarnpkg.com/next/-/next-13.1.1.tgz#42b825f650410649aff1017d203a088d77c80b5b" + integrity sha512-R5eBAaIa3X7LJeYvv1bMdGnAVF4fVToEjim7MkflceFPuANY3YyvFxXee/A+acrSYwYPvOvf7f6v/BM/48ea5w== + dependencies: + "@next/env" "13.1.1" + "@swc/helpers" "0.4.14" + caniuse-lite "^1.0.30001406" + postcss "8.4.14" + styled-jsx "5.1.1" + optionalDependencies: + "@next/swc-android-arm-eabi" "13.1.1" + "@next/swc-android-arm64" "13.1.1" + "@next/swc-darwin-arm64" "13.1.1" + "@next/swc-darwin-x64" "13.1.1" + "@next/swc-freebsd-x64" "13.1.1" + "@next/swc-linux-arm-gnueabihf" "13.1.1" + "@next/swc-linux-arm64-gnu" "13.1.1" + "@next/swc-linux-arm64-musl" "13.1.1" + "@next/swc-linux-x64-gnu" "13.1.1" + "@next/swc-linux-x64-musl" "13.1.1" + "@next/swc-win32-arm64-msvc" "13.1.1" + "@next/swc-win32-ia32-msvc" "13.1.1" + "@next/swc-win32-x64-msvc" "13.1.1" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +nock@^13.2.9: + version "13.2.9" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" + integrity sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash "^4.17.21" + propagate "^2.0.0" + +node-abort-controller@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.0.1.tgz#f91fa50b1dee3f909afabb7e261b1e1d6b0cb74e" + integrity sha512-/ujIVxthRs+7q6hsdjHMaj8hRG9NuWmwrz+JdRwZ14jdFoKSkm+vDsCbF9PLpnSqjaWQJuTmVtcWHNLr+vrOFw== + +node-addon-api@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" + integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== + +node-cache@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" + integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== + dependencies: + clone "2.x" + +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + +node-fetch@2.6.7, node-fetch@^2.6.1, 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-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-gyp-build@^4.2.3, node-gyp-build@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^2.0.1, 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== + +node-releases@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" + integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + +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== + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" + integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.0, 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" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== + +nwsapi@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.2.tgz#e5418863e7905df67d51ec95938d67bf801f0bb0" + integrity sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw== + +nx@15.5.2: + version "15.5.2" + resolved "https://registry.yarnpkg.com/nx/-/nx-15.5.2.tgz#c7e45a8d2067c49e69b526770f4c1a0f605f8239" + integrity sha512-jo8DG7zt0EorKL6AihkoshaeiFkK25NOeGUEnF05BFCaqsIoS9s3uctrOumh2G9DhaGMEWzakc3kF4/LeLB9pg== + dependencies: + "@nrwl/cli" "15.5.2" + "@nrwl/tao" "15.5.2" + "@parcel/watcher" "2.0.4" + "@yarnpkg/lockfile" "^1.1.0" + "@yarnpkg/parsers" "^3.0.0-rc.18" + "@zkochan/js-yaml" "0.0.6" + axios "^1.0.0" + chalk "^4.1.0" + cli-cursor "3.1.0" + cli-spinners "2.6.1" + cliui "^7.0.2" + dotenv "~10.0.0" + enquirer "~2.3.6" + fast-glob "3.2.7" + figures "3.2.0" + flat "^5.0.2" + fs-extra "^11.1.0" + glob "7.1.4" + ignore "^5.0.4" + js-yaml "4.1.0" + jsonc-parser "3.2.0" + lines-and-columns "~2.0.3" + minimatch "3.0.5" + npm-run-path "^4.0.1" + open "^8.4.0" + semver "7.3.4" + string-width "^4.2.3" + strong-log-transformer "^2.1.0" + tar-stream "~2.2.0" + tmp "~0.2.1" + tsconfig-paths "^4.1.2" + tslib "^2.3.0" + v8-compile-cache "2.3.0" + yargs "^17.6.2" + yargs-parser "21.1.1" + +object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1, object-assign@latest: + 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-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + +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: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + +object-inspect@^1.12.2: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +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-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + 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.0, 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.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" + integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +"object.fromentries@^2.0.0 || ^1.0.0", 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.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" + integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" + integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + dependencies: + array.prototype.reduce "^1.0.4" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.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.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" + integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0, 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" + +object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" + integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +objectorarray@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" + integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +oidc-token-hash@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" + integrity sha512-EvoOtz6FIEBzE+9q253HsLCVRiK/0doEJ2HCvvqMQb3dHZrP3WlJKYtJ55CRTw4jmYomzH4wkPuCj/I3ZvpKxQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-headers@^1.0.2, on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.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" + +one-time@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/one-time/-/one-time-1.0.0.tgz#e06bc174aed214ed58edede573b433bbf827cb45" + integrity sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g== + dependencies: + fn.name "1.x.x" + +onetime@^5.1.0, 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" + +open@^7.0.3: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +open@^8.0.9, open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +opener@^1.5.1, opener@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" + integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== + +openid-client@^4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-4.9.1.tgz#4f00a9d1566c0fa08f0dd5986cf0e6b1e5d14186" + integrity sha512-DYUF07AHjI3QDKqKbn2F7RqozT4hyi4JvmpodLrq0HHoNP7t/AjeG/uqiBK1/N2PZSAQEThVjDLHSmJN4iqu/w== + dependencies: + aggregate-error "^3.1.0" + got "^11.8.0" + jose "^2.0.5" + lru-cache "^6.0.0" + make-error "^1.3.6" + object-hash "^2.0.1" + oidc-token-hash "^5.0.1" + +openid-client@^5.2.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.3.1.tgz#69a5fa7d2b5ad479032f576852d40b4d4435488a" + integrity sha512-RLfehQiHch9N6tRWNx68cicf3b1WR0x74bJWHRc25uYIbSRwjxYcTFaRnzbbpls5jroLAaB/bFIodTgA5LJMvw== + dependencies: + jose "^4.10.0" + lru-cache "^6.0.0" + object-hash "^2.0.1" + oidc-token-hash "^5.0.1" + +opn@latest: + version "6.0.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" + integrity sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ== + dependencies: + is-wsl "^1.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" + +orderedmap@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.0.tgz#819457082fa3a06abd316d83a281a1ca467437cd" + integrity sha512-/pIFexOm6S70EPdznemIz3BQZoJ4VTFrhqzu0ACBqBgeLsLxq8e6Jim63ImIfwW/zAD1AlXpRMlOv3aghmo4dA== + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== + +ospath@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" + integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= + +p-all@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-all/-/p-all-2.1.0.tgz#91419be56b7dee8fe4c5db875d55e0da084244a0" + integrity sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA== + dependencies: + p-map "^2.0.0" + +p-cancelable@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" + integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= + +p-defer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-3.0.0.tgz#d1dceb4ee9b2b604b1d94ffec83760175d4e6f83" + integrity sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw== + +p-event@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + +p-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" + integrity sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== + dependencies: + p-map "^2.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.2: + 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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.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-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-memoize@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/p-memoize/-/p-memoize-4.0.4.tgz#90a4c4668866737fc5c8364c56b06f6ca44afb15" + integrity sha512-ijdh0DP4Mk6J4FXlOM6vPPoCjPytcEseW8p/k5SDTSSfGV3E9bpt9Yzfifvzp6iohIieoLTkXRb32OWV0fB2Lw== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^3.0.0" + p-settle "^4.1.1" + +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-reflect@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-reflect/-/p-reflect-2.1.0.tgz#5d67c7b3c577c4e780b9451fc9129675bd99fe67" + integrity sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg== + +p-retry@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.1.tgz#8fcddd5cdf7a67a0911a9cf2ef0e5df7f602316c" + integrity sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA== + dependencies: + "@types/retry" "^0.12.0" + retry "^0.13.1" + +p-settle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/p-settle/-/p-settle-4.1.1.tgz#37fbceb2b02c9efc28658fc8d36949922266035f" + integrity sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ== + dependencies: + p-limit "^2.2.2" + p-reflect "^2.1.0" + +p-timeout@^3.1.0, p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + +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== + +packet-reader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" + integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-1.1.2.tgz#dcb091a43c259b9228f1c341e7b6a44ea0bf9743" + integrity sha1-3LCRpDwlm5Io8cNB57akTqC/l0M= + dependencies: + sentence-case "^1.1.2" + +param-case@^3.0.3, param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +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-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== + dependencies: + error-ex "^1.2.0" + +parse-json@^5.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" + +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q== + +parse5-html-rewriting-stream@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz#de1820559317ab4e451ea72dba05fddfd914480b" + integrity sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg== + dependencies: + parse5 "^6.0.1" + parse5-sax-parser "^6.0.1" + +parse5-sax-parser@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz#98b4d366b5b266a7cd90b4b58906667af882daba" + integrity sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg== + dependencies: + parse5 "^6.0.1" + +parse5@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" + integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== + +parse5@6.0.1, parse5@^6.0.0, parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-1.1.2.tgz#3e5d64a20043830a7c49344c2d74b41be0c9c99b" + integrity sha1-Pl1kogBDgwp8STRMLXS0G+DJyZs= + dependencies: + camel-case "^1.1.1" + upper-case-first "^1.1.0" + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-1.1.2.tgz#50ce6ba0d3bed3dd0b5c2a9c4553697434409514" + integrity sha1-UM5roNO+090LXCqcRVNpdDRAlRQ= + dependencies: + sentence-case "^1.1.2" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +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@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= + +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.6, 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-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +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== + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU= + dependencies: + through "~2.3" + +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pend@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" + integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +pg-connection-string@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34" + integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.2.tgz#ed1bed1fb8d79f1c6fd5fb1c99e990fbf9ddf178" + integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w== + +pg-protocol@*, pg-protocol@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.5.0.tgz#b5dd452257314565e2d54ab3c132adc46565a6a0" + integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ== + +pg-types@^2.1.0, pg-types@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@^8.8.0: + version "8.8.0" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.8.0.tgz#a77f41f9d9ede7009abfca54667c775a240da686" + integrity sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "^2.5.0" + pg-pool "^3.5.2" + pg-protocol "^1.5.0" + pg-types "^2.1.0" + pgpass "1.x" + +pgpass@1.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" + integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== + dependencies: + split2 "^4.1.0" + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +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.2, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.5.0.tgz#ad5fbc1de78b8a5f99d6fbdd4f6e4eee21d1aca1" + integrity sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA== + +pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f" + integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0, 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" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +plaid@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/plaid/-/plaid-12.1.0.tgz#7df05742c4aff421280754e54688437d7fe688e6" + integrity sha512-4TupBQjCqd74eXloRYT+c4IOH0HEgB2ynVZhCF6Uy4uS5liZTYGqnBpVDrFp8EyiIoMfDdVg4c/8RWVY4na+sA== + dependencies: + axios "0.21.4" + +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + +polished@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" + integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== + dependencies: + "@babel/runtime" "^7.17.8" + +popmotion@11.0.3: + version "11.0.3" + resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9" + integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA== + dependencies: + framesync "6.0.1" + hey-listen "^1.0.8" + style-value-types "5.0.0" + tslib "^2.1.0" + +portfinder@^1.0.28: + version "1.0.28" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" + integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== + dependencies: + async "^2.6.2" + debug "^3.1.1" + mkdirp "^0.5.5" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +postcss-calc@^8.2.0: + version "8.2.2" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.2.tgz#9706e7399e8ec8b61a47830dcf1f21391af23373" + integrity sha512-B5R0UeB4zLJvxNt1FVCaDZULdzsKLPc6FhjFJ+xwFiq7VG4i9cuaJLxVjNtExNK8ocm3n2o4unXXLiVX1SCqxA== + dependencies: + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.0.2" + +postcss-colormin@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.4.tgz#7726d3f3d24f111d39faff50a6500688225d5324" + integrity sha512-rYlC5015aNqVQt/B6Cy156g7sH5tRUJGmT9xeagYthtKehetbKx7jHxhyLpulP4bs4vbp8u/B2rac0J7S7qPQg== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.3.tgz#492db08a28af84d57651f10edc8f6c8fb2f6df40" + integrity sha512-fVkjHm2T0PSMqXUCIhHNWVGjhB9mHEWX2GboVs7j3iCgr6FpIl9c/IdXy0PHWZSQ9LFTRgmj98amxJE6KOnlsA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-discard-comments@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.0.2.tgz#811ed34e2b6c40713daab0beb4d7a04125927dcd" + integrity sha512-6VQ3pYTsJHEsN2Bic88Aa7J/Brn4Bv8j/rqaFQZkH+pcVkKYwxCIvoMQkykEW7fBjmofdTnQgcivt5CCBJhtrg== + +postcss-discard-duplicates@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.0.2.tgz#61076f3d256351bdaac8e20aade730fef0609f44" + integrity sha512-LKY81YjUjc78p6rbXIsnppsaFo8XzCoMZkXVILJU//sK0DgPkPSpuq/cZvHss3EtdKvWNYgWzQL+wiJFtEET4g== + +postcss-discard-empty@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.0.2.tgz#0676a9bcfc44bb00d338352a45ab80845a31d8f0" + integrity sha512-SxBsbTjlsKUvZLL+dMrdWauuNZU8TBq5IOL/DHa6jBUSXFEwmDqeXRfTIK/FQpPTa8MJMxEHjSV3UbiuyLARPQ== + +postcss-discard-overridden@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.0.3.tgz#004b9818cabb407e60616509267567150b327a3f" + integrity sha512-yRTXknIZA4k8Yo4FiF1xbsLj/VBxfXEWxJNIrtIy6HC9KQ4xJxcPtoaaskh6QptCGrrcGnhKsTsENTRPZOBu4g== + +postcss-flexbugs-fixes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz#9218a65249f30897deab1033aced8578562a6690" + integrity sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ== + dependencies: + postcss "^7.0.26" + +postcss-import@^14.1.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.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.1.tgz#2f53a17f2f543d9e63864460af42efdac0d41f87" + integrity sha512-c/9XYboIbSEUZpiD1UQD0IKiUe8n9WHYV7YFe7X7J+ZwCsEKkUJSFWjS9hBU1RR9THR7jMXst8sxiqP0jjo2mg== + dependencies: + lilconfig "^2.0.4" + yaml "^1.10.2" + +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-loader@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc" + integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + semver "^7.3.4" + +postcss-loader@^6.1.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef" + integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.5" + +postcss-merge-longhand@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.0.5.tgz#cbc217ca22fb5a3e6ee22a6a1aa6920ec1f3c628" + integrity sha512-R2BCPJJ/U2oh1uTWEYn9CcJ7MMcQ1iIbj9wfr2s/zHu5om5MP/ewKdaunpfJqR1WYzqCsgnXuRoVXPAzxdqy8g== + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.0.2" + +postcss-merge-rules@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.5.tgz#2a18669ec214019884a60f0a0d356803a8138366" + integrity sha512-3Oa26/Pb9VOFVksJjFG45SNoe4nhGvJ2Uc6TlRimqF8uhfOCEhVCaJ3rvEat5UFOn2UZqTY5Da8dFgCh3Iq0Ug== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + cssnano-utils "^3.0.1" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.0.3.tgz#48c455c4cd980ecd07ac9bf3fc58e9d8a2ae4168" + integrity sha512-bC45rVzEwsLhv/cL1eCjoo2OOjbSk9I7HKFBYnBvtyuIZlf7uMipMATXtA0Fc3jwPo3wuPIW1jRJWKzflMh1sA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.5.tgz#a5572b9c98ed52cbd7414db24b873f8b9e418290" + integrity sha512-/YjvXs8PepsoiZAIpjstOO4IHKwFAqYNqbA1yVdqklM84tbUUneh6omJxGlRlF3mi6K5Pa067Mg6IwqEnYC8Zg== + dependencies: + colord "^2.9.1" + cssnano-utils "^3.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.0.4.tgz#230a4d04456609e614db1d48c2eebc21f6490a45" + integrity sha512-Z0vjod9lRZEmEPfEmA2sCfjbfEEFKefMD3RDIQSUfXK4LpCyWkX1CniUgyNvnjJFLDPSxtgKzozhHhPHKoeGkg== + dependencies: + browserslist "^4.16.6" + cssnano-utils "^3.0.1" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.1.2.tgz#bc9698f713b9dab7f44f1ec30643fcbad9a043c0" + integrity sha512-gpn1nJDMCf3g32y/7kl+jsdamhiYT+/zmEt57RoT9GmzlixBNRPohI7k8UIHelLABhdLf3MSZhtM33xuH5eQOQ== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-modules@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules/-/postcss-modules-4.3.0.tgz#1cb32f16a8cfffe2b989598f8135eb6427106ec7" + integrity sha512-zoUttLDSsbWDinJM9jH37o7hulLRyEgH6fZm2PchxN7AZ8rkdWiALyNhnQ7+jg7cX9f10m6y5VhHsrjO0Mf/DA== + dependencies: + generic-names "^4.0.0" + icss-replace-symbols "^1.1.0" + lodash.camelcase "^4.3.0" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + string-hash "^1.1.1" + +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-normalize-charset@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.0.2.tgz#eb6130c8a8e950ce25f9ea512de1d9d6a6f81439" + integrity sha512-fEMhYXzO8My+gC009qDc/3bgnFP8Fv1Ic8uw4ec4YTlhIOw63tGPk1YFd7fk9bZUf1DAbkhiL/QPWs9JLqdF2g== + +postcss-normalize-display-values@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.0.2.tgz#8b5273c6c7d0a445e6ef226b8a5bb3204a55fb99" + integrity sha512-RxXoJPUR0shSjkMMzgEZDjGPrgXUVYyWA/YwQRicb48H15OClPuaDR7tYokLAlGZ2tCSENEN5WxjgxSD5m4cUw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.0.3.tgz#b63fcc4ff5fbf65934fafaf83270b2da214711d1" + integrity sha512-U+rmhjrNBvIGYqr/1tD4wXPFFMKUbXsYXvlUCzLi0tOCUS6LoeEAnmVXXJY/MEB/1CKZZwBSs2tmzGawcygVBA== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.0.3.tgz#488c0ad8aac0fa4f66ef56cc8d604b3fd9bf705f" + integrity sha512-uk1+xYx0AMbA3nLSNhbDrqbf/rx+Iuq5tVad2VNyaxxJzx79oGieJ6D9F6AfOL2GtiIbP7vTYlpYHtG+ERFXTg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.0.3.tgz#49e0a1d58a119d5435ef21893ad03136a6e8f0e6" + integrity sha512-Mf2V4JbIDboNGQhW6xW0YREDiYXoX3WrD3EjKkjvnpAJ6W4qqjLnK/c9aioyVFaWWHVdP5zVRw/9DI5S3oLDFw== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.0.2.tgz#db4f4f49721f47667afd1fdc5edb032f8d9cdb2e" + integrity sha512-Ao0PP6MoYsRU1LxeVUW740ioknvdIUmfr6uAA3xWlQJ9s69/Tupy8qwhuKG3xWfl+KvLMAP9p2WXF9cwuk/7Bg== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.0.3.tgz#10f0d30093598a58c48a616491cc7fa53256dd43" + integrity sha512-uNC7BmS/7h6to2UWa4RFH8sOTzu2O9dVWPE/F9Vm9GdhONiD/c1kNaCLbmsFHlKWcEx7alNUChQ+jH/QAlqsQw== + dependencies: + browserslist "^4.16.6" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.4.tgz#3b0322c425e31dd275174d0d5db0e466f50810fb" + integrity sha512-cNj3RzK2pgQQyNp7dzq0dqpUpQ/wYtdDZM3DepPmFjCmYIfceuD9VIAcOdvrNetjIU65g1B4uwdP/Krf6AFdXg== + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.0.3.tgz#fb6bcc9ff2f834448b802657c7acd0956f4591d1" + integrity sha512-333JWRnX655fSoUbufJ10HJop3c8mrpKkCCUnEmgz/Cb/QEtW+/TMZwDAUt4lnwqP6tCCk0x0b58jqvDgiQm/A== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-ordered-values@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.4.tgz#f799dca87a7f17526d31a20085e61768d0b00534" + integrity sha512-taKtGDZtyYUMVYkg+MuJeBUiTF6cGHZmo/qcW7ibvW79UlyKuSHbo6dpCIiqI+j9oJsXWzP+ovIxoyLDOeQFdw== + dependencies: + cssnano-utils "^3.0.1" + postcss-value-parser "^4.2.0" + +postcss-reduce-initial@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz#fa424ce8aa88a89bc0b6d0f94871b24abe94c048" + integrity sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw== + dependencies: + browserslist "^4.16.6" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.0.3.tgz#df60fab34698a43073e8b87938c71df7a3b040ac" + integrity sha512-yDnTUab5i7auHiNwdcL1f+pBnqQFf+7eC4cbC7D8Lc1FkvNZhtpkdad+9U4wDdFb84haupMf0rA/Zc5LcTe/3A== + dependencies: + postcss-value-parser "^4.2.0" + +postcss-selector-parser@6.0.10, postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2: + 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-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5: + version "6.0.9" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.9.tgz#ee71c3b9ff63d9cd130838876c13a2ec1a992b2f" + integrity sha512-UO3SgnZOVTwu4kyLR22UQ1xZh086RyNZppb7lLAKBFK8a32ttG5i87Y/P3+2bRSjZNyJ1B7hfFNo273tKe9YxQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.3.tgz#d945185756e5dfaae07f9edb0d3cae7ff79f9b30" + integrity sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA== + dependencies: + postcss-value-parser "^4.1.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.0.3.tgz#07fd116a8fbd9202e7030f7c4952e7b52c26c63d" + integrity sha512-V5tX2hadSSn+miVCluuK1IDGy+7jAXSOfRZ2DQ+s/4uQZb/orDYBjH0CHgFrXsRw78p4QTuEFA9kI6C956UnHQ== + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, 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, 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.19, postcss@^8.4.18: + version "8.4.19" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.19.tgz#61178e2add236b17351897c8bcc0b4c8ecab56fc" + integrity sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.2.15, postcss@^8.3.5: + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + +postcss@^8.3.11: + version "8.4.20" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" + integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +postmark@^3.0.14: + version "3.0.14" + resolved "https://registry.yarnpkg.com/postmark/-/postmark-3.0.14.tgz#3d71381593380a21cd5d1d3cce8ce3cda1805431" + integrity sha512-Bkt6msv+I4Z3Y/jwt2owTNVmahOOyJlS1aQLiWsFAz5fzeJPfEqIUMSPPQTk7lYyJvYnLXQuzhFNG7M8yxqPhA== + dependencies: + axios "^0.25.0" + +preact@^10.5.13: + version "10.11.3" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.11.3.tgz#8a7e4ba19d3992c488b0785afcc0f8aa13c78d19" + integrity sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg== + +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 sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= + +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== + +"prettier@>=2.2.1 <=2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== + +pretty-bytes@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" + integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== + +pretty-error@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" + integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== + dependencies: + lodash "^4.17.20" + renderkid "^2.0.4" + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.4.6: + version "27.4.6" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.4.6.tgz#1b784d2f53c68db31797b2348fa39b49e31846b7" + integrity sha512-NblstegA1y/RJW2VyML+3LlpFjzx62cUrtBIKIWDXEDkjNeleA7Od7nrzcs/VLQvAeV4CgSYhrN39DRN88Qi/g== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^27.5.1: + 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.1.1, 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-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== + +prisma@4.8.1: + version "4.8.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.8.1.tgz#ef93cd908809b7d02e9f4bead5eea7733ba50c68" + integrity sha512-ZMLnSjwulIeYfaU1O6/LF6PEJzxN5par5weykxMykS9Z6ara/j76JH3Yo2AH3bgJbPN4Z6NeCK9s5fDkzf33cg== + dependencies: + "@prisma/engines" "4.8.1" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +promise-batcher@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/promise-batcher/-/promise-batcher-1.1.0.tgz#99f74d8d8c58f925b2cfb169f1e774eaaa9f4d19" + integrity sha512-Q/+G7i1ZqRCx56s9W1RRjhK6xveb2UkZz4Lt6xksO3SjcglDrRrQ4YRQ4WtalOFCzUNiAhstLLOX31QJRmr/BA== + dependencies: + p-defer "^3.0.0" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-pool-executor@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/promise-pool-executor/-/promise-pool-executor-1.1.1.tgz#dd75137b253026599430c899faf98a96b46b99a8" + integrity sha512-WZTGr7E8tiW93QoSRe0+arBIrJ13m7yKov/vyWqP8nkME/OjfzZgmSJjLtcDHd6VVz2LdSoAHZLmDfis8hJd1w== + dependencies: + debug "^3.1.0" + next-tick "^1.0.0" + p-defer "^1.0.0" + promise-batcher "^1.0.1" + +promise.allsettled@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.5.tgz#2443f3d4b2aa8dfa560f6ac2aa6c4ea999d75f53" + integrity sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ== + dependencies: + array.prototype.map "^1.0.4" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + iterate-value "^1.0.2" + +promise.prototype.finally@^3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz#d3186e58fcf4df1682a150f934ccc27b7893389c" + integrity sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +promise.series@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" + integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= + +prompts@^2.0.1, prompts@^2.4.0: + 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.0.0, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, 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" + +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +prosemirror-commands@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.5.0.tgz#d10efece1647c1d984fef6f65d52fdc77785560b" + integrity sha512-zL0Fxbj3fh71GPNHn5YdYgYGX2aU2XLecZYk2ekEF0oOD259HcXtM+96VjPVi5o3h4sGUdDfEEhGiREXW6U+4A== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.0.0" + +prosemirror-dropcursor@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.6.1.tgz#31f696172105f232bd17543ccf305e0f33e59d1d" + integrity sha512-LtyqQpkIknaT7NnZl3vDr3TpkNcG4ABvGRXx37XJ8tJNUGtcrZBh40A0344rDwlRTfUEmynQS/grUsoSWz+HgA== + dependencies: + prosemirror-state "^1.0.0" + prosemirror-transform "^1.1.0" + prosemirror-view "^1.1.0" + +prosemirror-gapcursor@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.1.tgz#8cfd874592e4504d63720e14ed680c7866e64554" + integrity sha512-GKTeE7ZoMsx5uVfc51/ouwMFPq0o8YrZ7Hx4jTF4EeGbXxBveUV8CGv46mSHuBBeXGmvu50guoV2kSnOeZZnUA== + dependencies: + prosemirror-keymap "^1.0.0" + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-view "^1.0.0" + +prosemirror-history@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.3.0.tgz#bf5a1ff7759aca759ddf0c722c2fa5b14fb0ddc1" + integrity sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA== + dependencies: + prosemirror-state "^1.2.2" + prosemirror-transform "^1.0.0" + rope-sequence "^1.3.0" + +prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz#d5cc9da9b712020690a994b50b92a0e448a60bf5" + integrity sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg== + dependencies: + prosemirror-state "^1.0.0" + w3c-keyname "^2.2.0" + +prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.3.tgz#d1026a78cff928fd600e90d87cf7d162e0a4e3fd" + integrity sha512-yUVejauEY3F1r7PDy4UJKEGeIU+KFc71JQl5sNvG66CLVdKXRjhWpBW6KMeduGsmGOsw85f6EGrs6QxIKOVILA== + dependencies: + orderedmap "^2.0.0" + +prosemirror-schema-list@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.2.2.tgz#bafda37b72367d39accdcaf6ddf8fb654a16e8e5" + integrity sha512-rd0pqSDp86p0MUMKG903g3I9VmElFkQpkZ2iOd3EOVg1vo5Cst51rAsoE+5IPy0LPXq64eGcCYlW1+JPNxOj2w== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.0.0" + +prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.4.1, prosemirror-state@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.2.tgz#f93bd8a33a4454efab917ba9b738259d828db7e5" + integrity sha512-puuzLD2mz/oTdfgd8msFbe0A42j5eNudKAAPDB0+QJRw8cO1ygjLmhLrg9RvDpf87Dkd6D4t93qdef00KKNacQ== + dependencies: + prosemirror-model "^1.0.0" + prosemirror-transform "^1.0.0" + prosemirror-view "^1.27.0" + +prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.7.0.tgz#a8a0768f3ee6418d26ebef435beda9d43c65e472" + integrity sha512-O4T697Cqilw06Zvc3Wm+e237R6eZtJL/xGMliCi+Uo8VL6qHk6afz1qq0zNjT3eZMuYwnP8ZS0+YxX/tfcE9TQ== + dependencies: + prosemirror-model "^1.0.0" + +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.28.2, prosemirror-view@^1.29.1: + version "1.29.1" + resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.29.1.tgz#9a4938d1a863ca76e23c6573d30e3ece2b17d9a0" + integrity sha512-OhujVZSDsh0l0PyHNdfaBj6DBkbhYaCfbaxmTeFrMKd/eWS+G6IC+OAbmR9IsLC8Se1HSbphMaXnsXjupHL3UQ== + dependencies: + prosemirror-model "^1.16.0" + prosemirror-state "^1.0.0" + prosemirror-transform "^1.1.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" + integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +proxy-middleware@latest: + version "0.15.0" + resolved "https://registry.yarnpkg.com/proxy-middleware/-/proxy-middleware-0.15.0.tgz#a3fdf1befb730f951965872ac2f6074c61477a56" + integrity sha1-o/3xvvtzD5UZZYcqwvYHTGFHelY= + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +pseudomap@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + +psl@^1.1.28: + version "1.8.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" + integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== + +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== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +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.10.3, qs@^6.4.0, qs@^6.9.4: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + +qs@6.11.0, qs@^6.10.0, 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" + +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== + +qs@~6.5.2: + version "6.5.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" + integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== + +query-string@^7.0.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.1.tgz#754620669db978625a90f635f12617c271a088e1" + integrity sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w== + dependencies: + decode-uri-component "^0.2.0" + filter-obj "^1.1.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +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== + +ramda@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97" + integrity sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== + dependencies: + bytes "3.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +react-animate-height@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-animate-height/-/react-animate-height-3.0.4.tgz#80c9cc25e8569709ad1c626b968dbe5108d0ce46" + integrity sha512-k+mBS8yCzpFp+7BdrHsL5bXd6CO/2bYO2SvRGKfxK+Ss3nzplAJLlgnd6Zhcxe/avdpy/CgcziicFj7pIHgG5g== + dependencies: + classnames "^2.3.1" + +react-docgen-typescript@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c" + integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg== + +react-docgen@^5.0.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.4.0.tgz#2cd7236720ec2769252ef0421f23250b39a153a1" + integrity sha512-JBjVQ9cahmNlfjMGxWUxJg919xBBKAoy3hgDgKERbR+BcF4ANpDuzWAScC7j27hZfd8sJNmMPOLWo9+vB/XJEQ== + dependencies: + "@babel/core" "^7.7.5" + "@babel/generator" "^7.12.11" + "@babel/runtime" "^7.7.6" + ast-types "^0.14.2" + commander "^2.19.0" + doctrine "^3.0.0" + estree-to-babel "^3.1.0" + neo-async "^2.6.1" + node-dir "^0.1.10" + strip-indent "^3.0.0" + +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-element-to-jsx-string@^14.3.4: + version "14.3.4" + resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8" + integrity sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg== + dependencies: + "@base2/pretty-print-object" "1.0.1" + is-plain-object "5.0.0" + react-is "17.0.2" + +react-error-boundary@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0" + integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA== + dependencies: + "@babel/runtime" "^7.12.5" + +react-fast-compare@^3.0.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" + integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA== + +react-hook-form@^7.33.1: + version "7.33.1" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.33.1.tgz#8c4410e3420788d3b804d62cc4c142915c2e46d0" + integrity sha512-ydTfTxEJdvgjCZBj5DDXRc58oTEfnFupEwwTAQ9FSKzykEJkX+3CiAkGtAMiZG7IPWHuzgT6AOBfogiKhUvKgg== + +react-hot-toast@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.3.0.tgz#70b3d183ac2a4afb6b17cda4a7f4cfe02e730415" + integrity sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA== + dependencies: + goober "^2.1.10" + +react-icons@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703" + integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg== + +react-infinite-scroller@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/react-infinite-scroller/-/react-infinite-scroller-1.2.6.tgz#8b80233226dc753a597a0eb52621247f49b15f18" + integrity sha512-mGdMyOD00YArJ1S1F3TVU9y4fGSfVVl6p5gh/Vt4u99CJOptfVu/q5V/Wlle72TMgYlBwIhbxK5wF0C/R33PXQ== + dependencies: + prop-types "^15.5.8" + +react-inspector@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" + integrity sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg== + dependencies: + "@babel/runtime" "^7.0.0" + is-dom "^1.0.0" + prop-types "^15.0.0" + +react-is@17.0.2, 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@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.0.0, react-is@^18.2.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-is@^16.13.1, react-is@^16.7.0: + 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-media-recorder@1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/react-media-recorder/-/react-media-recorder-1.6.5.tgz#dd9f2f7ed02480e22498a9166b784430f088add4" + integrity sha512-YcARNZ498jtmOJ+O7GkaokR0DbMNV+YSXB0msAHMZk9wharBuVSWYCXKiIL5JAnR5cs6ebXaOtkkGsM3uIq7Bw== + +react-number-format@^5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/react-number-format/-/react-number-format-5.1.3.tgz#5534f5141cea29e0fe889f76c73dfad11ddf2e17" + integrity sha512-N4XVBXDEIu9qVZIIwKTfCwhPh1fWx/i50I6XnMLsckj7OHi7A85+AMCzUmMCpx05usVVee14Rr/P7MUY0Pp2Kg== + dependencies: + prop-types "^15.7.2" + +react-plaid-link@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/react-plaid-link/-/react-plaid-link-3.3.2.tgz#58799baf7dc35a68cc84b62f721aa51f12df927b" + integrity sha512-W2L9C4RCE/KFFia2SUFFTi8SYRQ1UMUXh4iLE/oo4EQ8UYAT6msKzWvauj8bJirlrkzeiUtY6wgkmWil/okaNg== + dependencies: + prop-types "^15.7.2" + react-script-hook "^1.6.0" + +react-popper@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba" + integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q== + dependencies: + react-fast-compare "^3.0.1" + warning "^4.0.2" + +react-ranger@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-ranger/-/react-ranger-2.1.0.tgz#a9007d67a7871736fffc5fe0d7c54a736771e9a1" + integrity sha512-d8ezhyX3v/KlN8SkyoE5e8Dybsdmn94eUAqBDsAPrVhZde8sVt6pLJw4fC784KiMmS4LyAjvtjGxhAEqjjGYgw== + +react-refresh@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.10.0.tgz#2f536c9660c0b9b1d500684d9e52a65e7404f7e3" + integrity sha512-PgidR3wST3dDYKr6b4pJoqQFpPGNKDSCDx4cZoshjXipw3LzO7mG1My2pwEzz2JVkF+inx3xRpDeQLFQGH/hsQ== + +react-refresh@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" + integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== + +react-responsive@^9.0.0-beta.10: + version "9.0.0-beta.10" + resolved "https://registry.yarnpkg.com/react-responsive/-/react-responsive-9.0.0-beta.10.tgz#892db936da48eb30babf5cb7e31737c7b5677209" + integrity sha512-41H8g4FYP46ln16rsHvs9/0ZoZxAPfnNiHET86/5pgS+Vw8fSKfLBuOS2SAquaxOxq7DgPviFoHmybgVvSKCNQ== + dependencies: + hyphenate-style-name "^1.0.0" + matchmediaquery "^0.3.0" + prop-types "^15.6.1" + shallow-equal "^1.2.1" + +react-script-hook@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/react-script-hook/-/react-script-hook-1.6.0.tgz#6a44ff5e65113cb29252eadad1b8306f5fe0c626" + integrity sha512-aJm72XGWV+wJTKiqHmAaTNC/JQZV/Drv6A1kd1VQlzhzAXLqtBRBeTt3iTESImGe5TaBDHUOUeaGNw4v+7bqDw== + +react-shallow-renderer@^16.15.0: + version "16.15.0" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457" + integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA== + dependencies: + object-assign "^4.1.1" + react-is "^16.12.0 || ^17.0.0 || ^18.0.0" + +react-ssr-prepass@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/react-ssr-prepass/-/react-ssr-prepass-1.5.0.tgz#bc4ca7fcb52365e6aea11cc254a3d1bdcbd030c5" + integrity sha512-yFNHrlVEReVYKsLI5lF05tZoHveA5pGzjFbFJY/3pOqqjGOmMmqx83N4hIjN2n6E1AOa+eQEUxs3CgRnPmT0RQ== + +react-test-renderer@18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e" + integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA== + dependencies: + react-is "^18.2.0" + react-shallow-renderer "^16.15.0" + scheduler "^0.23.0" + +react-use-measure@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-use-measure/-/react-use-measure-2.1.1.tgz#5824537f4ee01c9469c45d5f7a8446177c6cc4ba" + integrity sha512-nocZhN26cproIiIduswYpV5y5lQpSQS1y/4KuvUCjSKmw7ZWIS/+g3aFnX3WdBkyuGUtTLif3UTqnLLhbDoQig== + dependencies: + debounce "^1.2.1" + +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 sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= + dependencies: + pify "^2.3.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +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@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g== + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.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" + +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-info@^3.0.8: + version "3.1.0" + resolved "https://registry.yarnpkg.com/redis-info/-/redis-info-3.1.0.tgz#5e349c8720e82d27ac84c73136dce0931e10469a" + integrity sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg== + dependencies: + lodash "^4.17.11" + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + +reduce-css-calc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + integrity sha1-dHyRTgSWFKTJz7umKYca0dKSdxY= + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-function-call@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.3.tgz#60350f7fb252c0a67eb10fd4694d16909971300f" + integrity sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ== + dependencies: + balanced-match "^1.0.0" + +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@0.13.7: + version "0.13.7" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" + integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== + +regenerator-runtime@^0.13.10: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: + 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== + +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +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== + +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-external-links@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-8.0.0.tgz#308de69482958b5d1cd3692bc9b725ce0240f345" + integrity sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA== + dependencies: + extend "^3.0.0" + is-absolute-url "^3.0.0" + mdast-util-definitions "^4.0.0" + space-separated-tokens "^1.0.0" + unist-util-visit "^2.0.0" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-slug@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" + integrity sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ== + dependencies: + github-slugger "^1.0.0" + mdast-util-to-string "^1.0.0" + unist-util-visit "^2.0.0" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +renderkid@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" + integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^3.0.1" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.4, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A== + dependencies: + is-finite "^1.0.0" + +request-progress@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe" + integrity sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4= + dependencies: + throttleit "^1.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 sha1-jGStX9MNqxyXbiNE/+f3kqam30I= + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resolve-alpn@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" + integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== + +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-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve.exports@1.1.0, 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.12.0, resolve@^1.17.0, resolve@^1.20.0: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.22.0, resolve@^1.22.1, resolve@^1.3.2: + 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.3" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" + integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q== + dependencies: + is-core-module "^2.2.0" + path-parse "^1.0.6" + +responselike@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723" + integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw== + dependencies: + lowercase-keys "^2.0.0" + +rest-facade@^1.16.3: + version "1.16.3" + resolved "https://registry.yarnpkg.com/rest-facade/-/rest-facade-1.16.3.tgz#e4d4b44a4f6a4268f30f02e1f544dd49b040499e" + integrity sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q== + dependencies: + change-case "^2.3.0" + deepmerge "^3.2.0" + lodash.get "^4.4.2" + superagent "^5.1.1" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +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== + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.0, 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" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +roarr@^2.15.3: + version "2.15.4" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" + integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== + dependencies: + boolean "^3.0.1" + detect-node "^2.0.4" + globalthis "^1.0.1" + json-stringify-safe "^5.0.1" + semver-compare "^1.0.0" + sprintf-js "^1.1.2" + +rollup-plugin-copy@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.4.0.tgz#f1228a3ffb66ffad8606e2f3fb7ff23141ed3286" + integrity sha512-rGUmYYsYsceRJRqLVlE9FivJMxJ7X6jDlP79fmFkL8sJs7VVMSVyA2yfyL+PGyO/vJs4A87hwhgVfz61njI+uQ== + dependencies: + "@types/fs-extra" "^8.0.1" + colorette "^1.1.0" + fs-extra "^8.1.0" + globby "10.0.1" + is-plain-object "^3.0.0" + +rollup-plugin-peer-deps-external@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz#8a420bbfd6dccc30aeb68c9bf57011f2f109570d" + integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g== + +rollup-plugin-postcss@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.2.tgz#15e9462f39475059b368ce0e49c800fa4b1f7050" + integrity sha512-05EaY6zvZdmvPUDi3uCcAQoESDcYnv8ogJJQRp6V5kZ6J6P7uAVJlrTZcaaA20wTH527YTnKfkAoPxWI/jPp4w== + dependencies: + chalk "^4.1.0" + concat-with-sourcemaps "^1.1.0" + cssnano "^5.0.1" + import-cwd "^3.0.0" + p-queue "^6.6.2" + pify "^5.0.0" + postcss-load-config "^3.0.0" + postcss-modules "^4.0.0" + promise.series "^0.2.0" + resolve "^1.19.0" + rollup-pluginutils "^2.8.2" + safe-identifier "^0.4.2" + style-inject "^0.3.0" + +rollup-plugin-typescript2@0.34.1: + version "0.34.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.34.1.tgz#c457f155a71d133c142689213fce78694e30d0be" + integrity sha512-P4cHLtGikESmqi1CA+tdMDUv8WbQV48mzPYt77TSTOPJpERyZ9TXdDgjSDix8Fkqce6soYz3+fa4lrC93IEkcw== + dependencies: + "@rollup/pluginutils" "^4.1.2" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + semver "^7.3.7" + tslib "^2.4.0" + +rollup-pluginutils@^2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^2.56.2: + version "2.66.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.66.1.tgz#366b0404de353c4331d538c3ad2963934fcb4937" + integrity sha512-crSgLhSkLMnKr4s9iZ/1qJCplgAgrRY+igWv8KhG/AjKOJ0YX/WpmANyn8oxrw+zenF3BXWDLa7Xl/QZISH+7w== + optionalDependencies: + fsevents "~2.3.2" + +rope-sequence@^1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.3.tgz#3f67fc106288b84b71532b4a5fd9d4881e4457f0" + integrity sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q== + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +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" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg== + dependencies: + aproba "^1.1.1" + +rxjs@^6.5.4: + version "6.6.7" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" + integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== + dependencies: + tslib "^1.9.0" + +rxjs@^7.5.1, rxjs@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.4.tgz#3d6bd407e6b7ce9a123e76b1e770dc5761aa368d" + integrity sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ== + dependencies: + tslib "^2.1.0" + +rxjs@^7.5.2: + version "7.5.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.2.tgz#11e4a3a1dfad85dbf7fb6e33cbba17668497490b" + integrity sha512-PwDt186XaL3QN5qXj/H9DGyHhP3/RYYgZZwqBv9Tv8rsAaiwFH1IsJJlcgD37J7UW5a6O67qX0KWKS3/pu0m4w== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + +safe-buffer@5.1.2, safe-buffer@~5.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== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-identifier@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" + integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +safe-stable-stringify@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz#c8a220ab525cd94e60ebf47ddc404d610dc5d84a" + integrity sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw== + +safe-stable-stringify@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz#ab67cbe1fe7d40603ca641c5e765cb942d04fc73" + integrity sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +sanitize-filename@^1.6.1: + version "1.6.3" + resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378" + integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== + dependencies: + truncate-utf8-bytes "^1.0.0" + +sanitize-html@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.8.1.tgz#319c4fdba67e1edf35b1fd6d9362210044826d47" + integrity sha512-qK5neD0SaMxGwVv5txOYv05huC3o6ZAA4h5+7nJJgWMNFUNRjcjLO6FpwAtKzfKCZ0jrG6xTk6eVFskbvOGblg== + dependencies: + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + htmlparser2 "^8.0.0" + is-plain-object "^5.0.0" + parse-srcset "^1.0.2" + postcss "^8.3.11" + +sass-loader@^12.2.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.4.0.tgz#260b0d51a8a373bb8e88efc11f6ba5583fea0bcf" + integrity sha512-7xN+8khDIzym1oL9XyS6zP6Ges+Bo2B2xbPrjdMHEYyV3AQYhd/wXeru++3ODHF0zMjYmVadblSKrPrjEkL8mg== + dependencies: + klona "^2.0.4" + neo-async "^2.6.2" + +sass@^1.42.1: + version "1.49.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.49.0.tgz#65ec1b1d9a6bc1bae8d2c9d4b392c13f5d32c078" + integrity sha512-TVwVdNDj6p6b4QymJtNtRS2YtLJ/CqZriGg0eIAbAKMlN8Xy6kbv33FsEZSF7FufFFM705SQviHjjThfaQ4VNw== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +satori@0.0.46: + version "0.0.46" + resolved "https://registry.yarnpkg.com/satori/-/satori-0.0.46.tgz#ad1fd94df4dda172871104c2a6d9a5300ca6dab8" + integrity sha512-7RfTz38MB0n8tzmRHtUh1y0K7609CLBHpYuyZuh9rpf9FyhOd2in+6EHuqu6ul/Jebn1qD1HdYKtAMjb7uiNAQ== + 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-wasm-web "^0.3.0" + +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + 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" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.6.5, schema-utils@^2.7.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +scroll-into-view-if-needed@^2.2.28: + version "2.2.29" + resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885" + integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg== + dependencies: + compute-scroll-into-view "^1.0.17" + +secure-compare@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3" + integrity sha1-8aAymzCLIh+uN7mXTz1XjQypmeM= + +secure-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/secure-keys/-/secure-keys-1.0.0.tgz#f0c82d98a3b139a8776a8808050b824431087fca" + integrity sha1-8MgtmKOxOah3aogIBQuCRDEIf8o= + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.1.1.tgz#18a7613d714c0cd3385c48af0075abf3f266af61" + integrity sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ== + dependencies: + node-forge "^1" + +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@7.x, semver@^7.3.0: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, 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== + +semver@^7.3.2, semver@^7.3.4, 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" + +send@0.17.2, send@latest: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "1.8.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +sentence-case@^1.1.1, sentence-case@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-1.1.3.tgz#8034aafc2145772d3abe1509aa42c9e1042dc139" + integrity sha1-gDSq/CFFdy06vhUJqkLJ4QQtwTk= + dependencies: + lower-case "^1.1.1" + +serialize-error@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" + integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== + dependencies: + type-fest "^0.13.1" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-favicon@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" + integrity sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA== + dependencies: + etag "~1.8.1" + fresh "0.5.2" + ms "2.1.1" + parseurl "~1.3.2" + safe-buffer "5.1.1" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shallow-equal@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.2.1.tgz#4c16abfa56043aa20d050324efa68940b0da79da" + integrity sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA== + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= + dependencies: + shebang-regex "^1.0.0" + +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@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= + +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.0, signal-exit@^3.0.2, 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 sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= + dependencies: + is-arrayish "^0.3.1" + +sirv@^1.0.7: + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== + dependencies: + "@polka/url" "^1.0.0-next.20" + mrmime "^1.0.0" + totalist "^1.0.0" + +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@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== + +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +smooth-scroll-into-view-if-needed@^1.1.33: + version "1.1.33" + resolved "https://registry.yarnpkg.com/smooth-scroll-into-view-if-needed/-/smooth-scroll-into-view-if-needed-1.1.33.tgz#2c7b88c82784c69030cb0489b9df584e94e01533" + integrity sha512-crS8NfAaoPrtVYOCMSAnO2vHRgUp22NiiDgEQ7YiaAy5xe2jmR19Jm+QdL8+97gO8ENd7PUyQIAQojJyIiyRHw== + dependencies: + scroll-into-view-if-needed "^2.2.28" + +snake-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-1.1.2.tgz#0c2f25e305158d9a18d3d977066187fef8a5a66a" + integrity sha1-DC8l4wUVjZoY09l3BmGH/vilpmo= + dependencies: + sentence-case "^1.1.2" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, 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-loader@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.1.tgz#9ae5edc7c2d42570934be4c95d1ccc6352eba52d" + integrity sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA== + dependencies: + abab "^2.0.5" + iconv-lite "^0.6.3" + source-map-js "^1.0.1" + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + +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-support@0.5.19: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.16, source-map-support@~0.5.12, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, 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.3, source-map@~0.7.2: + 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.4: + 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@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +split2@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" + integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= + dependencies: + through "2" + +sprintf-js@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" + integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +sshpk@^1.14.1: + version "1.17.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" + integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +ssri@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== + dependencies: + figgy-pudding "^3.5.1" + +ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-trace@0.0.x: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + +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.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" + integrity sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA== + +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +store2@^2.12.0: + version "2.14.2" + resolved "https://registry.yarnpkg.com/store2/-/store2-2.14.2.tgz#56138d200f9fe5f582ad63bc2704dbc0e4a45068" + integrity sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w== + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= + dependencies: + duplexer "~0.1.1" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +strict-uri-encode@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" + integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ== + +string-argv@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + +string-hash@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b" + integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs= + +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@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, 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-width@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.0.tgz#5ab00980cfb29f43e736b113a120a73a0fb569d3" + integrity sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.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.0 || ^3.0.1", 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.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" + integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.padend@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1" + integrity sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +string.prototype.padstart@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.1.3.tgz#4551d0117d9501692ec6000b15056ac3f816cfa5" + integrity sha512-NZydyOMtYxpTjGqp0VN5PYUF/tsU15yDMZnUdj16qRUIUiMJkHHSDElYyQFrMu+/WloTpA7MQSiADhBicDfaoA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +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.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" + integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +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" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" + integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= + dependencies: + ansi-regex "^2.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-ansi@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2" + integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + +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-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= + +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@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA== + dependencies: + get-stdin "^4.0.1" + +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.17.0: + version "10.17.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-10.17.0.tgz#b65d8c9d55ec7190b866a77dffe9843941ddced7" + integrity sha512-JHV2KoL+nMQRXu3m9ervCZZvi4DDCJfzHUE6CmtJxR9TmizyYfrVuhGvnsZLLnheby9Qrnf4Hq6iOEcejGwnGQ== + dependencies: + "@types/node" ">=8.1.0" + qs "^6.11.0" + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + +strong-log-transformer@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" + integrity sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA== + dependencies: + duplexer "^0.1.1" + minimist "^1.2.0" + through "^2.3.4" + +style-inject@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-inject/-/style-inject-0.3.0.tgz#d21c477affec91811cc82355832a700d22bf8dd3" + integrity sha512-IezA2qp+vcdlhJaVm5SOdPPTUu0FCEqfNSli2vRuSIBbu5Nq5UvygTk/VzeCqfLz2Atj3dVII5QBKGZRZ0edzw== + +style-loader@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e" + integrity sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.7.0" + +style-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +style-loader@^3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" + integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== + +style-to-object@0.3.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" + +style-value-types@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad" + integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA== + dependencies: + hey-listen "^1.0.8" + tslib "^2.1.0" + +styled-jsx@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" + integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== + dependencies: + client-only "0.0.1" + +stylehacks@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.2.tgz#fa10e5181c6e8dc0bddb4a3fb372e9ac42bba2ad" + integrity sha512-114zeJdOpTrbQYRD4OU5UWJ99LKUaqCPJTU1HQ/n3q3BwmllFN8kHENaLnOeqVq6AhXrWfxHNZTl33iJ4oy3cQ== + dependencies: + browserslist "^4.16.6" + postcss-selector-parser "^6.0.4" + +stylus-loader@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-7.1.0.tgz#19e09a98b19075c246e6e3f65e38b8cb89d2d6fb" + integrity sha512-gNUEjjozR+oZ8cuC/Fx4LVXqZOgDKvpW9t2hpXHcxjfPYqSjQftaGwZUK+wL9B0QJ26uS6p1EmoWHmvld1dF7g== + dependencies: + fast-glob "^3.2.12" + klona "^2.0.5" + normalize-path "^3.0.0" + +stylus@^0.55.0: + version "0.55.0" + resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.55.0.tgz#bd404a36dd93fa87744a9dd2d2b1b8450345e5fc" + integrity sha512-MuzIIVRSbc8XxHH7FjkvWqkIcr1BvoMZoR/oFuAJDlh7VSaNJzrB4uJ38GRQa+mWjLXODAMzeDe0xi9GYbGwnw== + dependencies: + css "^3.0.0" + debug "~3.1.0" + glob "^7.1.6" + mkdirp "~1.0.4" + safer-buffer "^2.1.2" + sax "~1.2.4" + semver "^6.3.0" + source-map "^0.7.3" + +superagent@^5.1.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1" + integrity sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ== + dependencies: + component-emitter "^1.3.0" + cookiejar "^2.1.2" + debug "^4.1.1" + fast-safe-stringify "^2.0.7" + form-data "^3.0.0" + formidable "^1.2.2" + methods "^1.1.2" + mime "^2.4.6" + qs "^6.9.4" + readable-stream "^3.6.0" + semver "^7.3.2" + +superjson@^1.10.0, superjson@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/superjson/-/superjson-1.11.0.tgz#f6e2ae0d8fbac61c3fca09ab6739ac9678414d1b" + integrity sha512-6PfAg1FKhqkwWvPb2uXhH4MkMttdc17eJ91+Aoz4s1XUEDZFmLfFx/xVA3wgkPxAGy5dpozgGdK6V/n20Wj9yg== + dependencies: + copy-anything "^3.0.2" + +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.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, supports-color@^8.1.1: + 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-color@^9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-9.2.1.tgz#599dc9d45acf74c6176e0d880bab1d7d718fe891" + integrity sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ== + +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.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== + +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" + integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ== + +svgo@^2.5.0, svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" + integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +swap-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/swap-case/-/swap-case-1.1.2.tgz#c39203a4587385fad3c850a0bd1bcafa081974e3" + integrity sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM= + dependencies: + lower-case "^1.1.1" + upper-case "^1.1.1" + +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== + +symbol.prototype.description@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz#d30e01263b6020fbbd2d2884a6276ce4d49ab568" + integrity sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ== + dependencies: + call-bind "^1.0.2" + get-symbol-description "^1.0.0" + has-symbols "^1.0.2" + object.getownpropertydescriptors "^2.1.2" + +synchronous-promise@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e" + integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg== + +synckit@^0.8.4: + version "0.8.4" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.4.tgz#0e6b392b73fafdafcde56692e3352500261d64ec" + integrity sha512-Dn2ZkzMdSX827QbowGbU/4yjWuvNaCoScLLoMo/yKbu+P4GBR6cRGKZH27k6a9bRzdqcyd1DE96pQtQ6uNkmyw== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.4.0" + +tailwindcss@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.4.tgz#afe3477e7a19f3ceafb48e4b083e292ce0dc0250" + integrity sha512-AhwtHCKMtR71JgeYDaswmZXhPcW9iuI9Sp2LvZPo9upDZ7231ZJ7eA9RaURbhpXGVlrjX4cFNlB4ieTetEb7hQ== + 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@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0, tapable@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar-stream@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" + integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== + dependencies: + bl "^4.0.3" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + +tar@^6.0.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +telejson@^6.0.8: + version "6.0.8" + resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7" + integrity sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg== + dependencies: + "@types/is-function" "^1.0.0" + global "^4.4.0" + is-function "^1.0.2" + is-regex "^1.1.2" + is-symbol "^1.0.3" + isobject "^4.0.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser-webpack-plugin@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser-webpack-plugin@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" + integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== + dependencies: + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.5.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.3.4" + webpack-sources "^1.4.3" + +terser-webpack-plugin@^5.0.3, terser-webpack-plugin@^5.1.3: + version "5.3.0" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz#21641326486ecf91d8054161c816e464435bae9f" + integrity sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ== + dependencies: + jest-worker "^27.4.1" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + terser "^5.7.2" + +terser-webpack-plugin@^5.3.3: + version "5.3.6" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz#5590aec31aa3c6f771ce1b1acca60639eab3195c" + integrity sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.14" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.14.1" + +terser@^4.1.2, terser@^4.6.3: + version "4.8.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" + integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +terser@^5.10.0, terser@^5.7.2: + version "5.10.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc" + integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA== + dependencies: + commander "^2.20.0" + source-map "~0.7.2" + source-map-support "~0.5.20" + +terser@^5.14.1: + version "5.15.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.15.1.tgz#8561af6e0fd6d839669c73b92bdd5777d870ed6c" + integrity sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +terser@^5.3.4: + version "5.14.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" + integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +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-hex@1.0.x: + version "1.0.0" + resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" + integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + +throttleit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" + integrity sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw= + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@2, through@^2.3.4, through@^2.3.8, through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +timsort@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" + integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= + +tiny-glob@^0.2.9: + version "0.2.9" + resolved "https://registry.yarnpkg.com/tiny-glob/-/tiny-glob-0.2.9.tgz#2212d441ac17928033b110f8b3640683129d31e2" + integrity sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg== + dependencies: + globalyzer "0.1.0" + globrex "^0.1.2" + +tippy.js@^6.3.1, tippy.js@^6.3.7: + version "6.3.7" + resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c" + integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ== + dependencies: + "@popperjs/core" "^2.9.0" + +title-case@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/title-case/-/title-case-1.1.2.tgz#fae4a6ae546bfa22d083a0eea910a40d12ed4f5a" + integrity sha1-+uSmrlRr+iLQg6DuqRCkDRLtT1o= + dependencies: + sentence-case "^1.1.1" + upper-case "^1.0.3" + +tmp@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +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-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== + +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-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +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" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + +tough-cookie@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" + integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tough-cookie@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" + integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== + dependencies: + psl "^1.1.28" + punycode "^2.1.1" + +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== + +tree-kill@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw== + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +triple-beam@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" + integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +truncate-utf8-bytes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b" + integrity sha1-QFkjkJWS1W94pYGENLC3hInKXys= + dependencies: + utf8-byte-length "^1.0.1" + +ts-dedent@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +ts-essentials@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + +ts-jest@28.0.5: + version "28.0.5" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.5.tgz#31776f768fba6dfc8c061d488840ed0c8eeac8b9" + integrity sha512-Sx9FyP9pCY7pUzQpy4FgRZf2bhHY3za576HMKJFs+OnQ9jS96Du5vNsDKkyedQkik+sEabbKAnCliv9BEsHZgQ== + dependencies: + bs-logger "0.x" + fast-json-stable-stringify "2.x" + jest-util "^28.0.0" + json5 "^2.2.1" + lodash.memoize "4.x" + make-error "1.x" + semver "7.x" + yargs-parser "^21.0.1" + +ts-loader@^9.3.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.1.tgz#b6f3d82db0eac5a8295994f8cb5e4940ff6b1060" + integrity sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +ts-node@10.9.1: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + +ts-toolbelt@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz#50a25426cfed500d4a09bd1b3afb6f28879edfd5" + integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w== + +tsconfig-paths-webpack-plugin@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.0.0.tgz#84008fc3e3e0658fdb0262758b07b4da6265ff1a" + integrity sha512-fw/7265mIWukrSHd0i+wSwx64kYUSAKPfxRDksjKIYTxSAp9W9/xcZVBF4Kl0eqQd5eBpAQ/oQrc5RyM/0c1GQ== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.7.0" + tsconfig-paths "^4.0.0" + +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" + +tsconfig-paths@^4.0.0, tsconfig-paths@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" + integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + +tslib@^2.0.3, 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" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tunnel@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" + integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +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 sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= + 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.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" + integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== + +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-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-is@^1.6.4, type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +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.6.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" + integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typed-assert@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/typed-assert/-/typed-assert-1.0.9.tgz#8af9d4f93432c4970ec717e3006f33f135b06213" + integrity sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg== + +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" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +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== + +uglify-js@^3.1.4: + version "3.16.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def" + integrity sha512-AaQNokTNgExWrkEYA24BTNMSjyqEXPSfhqoS0AxmHkCJ4U+Dyy5AvbGV/sqxuxficEfGGoX3zWw9R7QpLFfEsg== + +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" + +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +union@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075" + integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA== + dependencies: + qs "^6.4.0" + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unix-crypt-td-js@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz#4912dfad1c8aeb7d20fa0a39e4c31918c1d5d5dd" + integrity sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +untildify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" + integrity sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig== + dependencies: + os-homedir "^1.0.0" + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-browserslist-db@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" + integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +update-browserslist-db@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" + integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +upper-case-first@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-1.1.2.tgz#5d79bedcff14419518fd2edb0a0507c9b6859115" + integrity sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU= + dependencies: + upper-case "^1.1.1" + +upper-case@^1.0.3, upper-case@^1.1.0, upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg= + +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" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== + dependencies: + punycode "1.3.2" + querystring "0.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== + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +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" + +utf8-byte-length@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61" + integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid-browser@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410" + integrity sha1-DwWkCu90+eWVHiDvv0SxGHHlZBA= + +uuid@^3.0.0, uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.0, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +v8-compile-cache@2.3.0, v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +v8-to-istanbul@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" + integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +v8-to-istanbul@^9.0.0, 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" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@^1, vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +vscode-json-languageservice@^4.1.6: + version "4.2.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-4.2.0.tgz#df0693b69ba2fbf0a6add896087b6f1c9c38f06a" + integrity sha512-XNawv0Vdy/sUK0S+hGf7cq/qsVAbIniGJr89TvZOqMCNJmpgKTy1e8PL1aWW0uy6BfWMG7vxa5lZb3ypuFtuGQ== + dependencies: + jsonc-parser "^3.0.0" + vscode-languageserver-textdocument "^1.0.3" + vscode-languageserver-types "^3.16.0" + vscode-nls "^5.0.0" + vscode-uri "^3.0.3" + +vscode-languageserver-textdocument@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff" + integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A== + +vscode-languageserver-types@^3.16.0: + version "3.16.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz#ecf393fc121ec6974b2da3efb3155644c514e247" + integrity sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA== + +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + +vscode-uri@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" + integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== + +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-keyname@^2.2.0: + version "2.2.6" + resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.6.tgz#8412046116bc16c5d73d4e612053ea10a189c85f" + integrity sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg== + +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" + +wait-on@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: + 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" + +warning@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" + +watchpack@^2.2.0, watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +watchpack@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.3.1.tgz#4200d9447b401156eeca7767ee610f8809bc9d25" + integrity sha512-x0t0JuydIo8qCNctdDrn1OzH/qDzk2+rdCOC3YzumZ42fiMqmQ7T3xQurykYMhYfHaPHTp4ZxAx2NfUo1K6QaA== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +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== + +webpack-bundle-analyzer@4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66" + integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg== + dependencies: + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^7.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" + +webpack-dev-middleware@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-middleware@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz#179cc40795882cae510b1aa7f3710cbe93c9333e" + integrity sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w== + dependencies: + colorette "^1.2.2" + mem "^8.1.1" + memfs "^3.2.2" + mime-types "^2.1.30" + range-parser "^1.2.1" + schema-utils "^3.0.0" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.9.3: + version "4.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz#ae07f0d71ca0438cf88446f09029b92ce81380b5" + integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.4.2" + +webpack-filter-warnings-plugin@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz#dc61521cf4f9b4a336fbc89108a75ae1da951cdb" + integrity sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg== + +webpack-hot-middleware@^2.25.1: + version "2.25.1" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz#581f59edf0781743f4ca4c200fd32c9266c6cf7c" + integrity sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw== + dependencies: + ansi-html-community "0.0.8" + html-entities "^2.1.0" + querystring "^0.2.0" + strip-ansi "^6.0.0" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-node-externals@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917" + integrity sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ== + +webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^3.0.0, webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack-subresource-integrity@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz#8b7606b033c6ccac14e684267cb7fb1f5c2a132a" + integrity sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q== + dependencies: + typed-assert "^1.0.8" + +webpack-virtual-modules@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz#20863dc3cb6bb2104729fff951fbe14b18bd0299" + integrity sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA== + dependencies: + debug "^3.0.0" + +webpack-virtual-modules@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.3.tgz#cd597c6d51d5a5ecb473eea1983a58fa8a17ded9" + integrity sha512-5NUqC2JquIL2pBAAo/VfBP6KuGkHIZQXW/lNKupLPfhViwh8wNsu0BObtl09yuKZszeEUfbXz8xhrHvSG16Nqw== + +webpack@4: + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.5.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + +"webpack@>=4.43.0 <6.0.0": + version "5.73.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" + integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.9.3" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.3.1" + webpack-sources "^3.2.3" + +webpack@^5.75.0: + version "5.75.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.75.0.tgz#1e440468647b2505860e94c9ff3e44d5b582c152" + integrity sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.10.0" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +webpack@^5.9.0: + version "5.67.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.67.0.tgz#cb43ca2aad5f7cc81c4cd36b626e6b819805dbfd" + integrity sha512-LjFbfMh89xBDpUMgA1W9Ur6Rn/gnr2Cq1jjHFPo4v6a79/ypznSYbAyPgGhwsxBtMIaEmDD1oJoA7BEYw/Fbrw== + dependencies: + "@types/eslint-scope" "^3.7.0" + "@types/estree" "^0.0.50" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.8.3" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-better-errors "^1.0.2" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.3.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +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@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +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-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +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" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +wildcard@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-1.1.2.tgz#a7020453084d8cd2efe70ba9d3696263de1710a5" + integrity sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng== + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +winston-transport@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" + integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + dependencies: + logform "^2.3.2" + readable-stream "^3.6.0" + triple-beam "^1.3.0" + +winston@^3.7.2: + version "3.8.1" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.1.tgz#76f15b3478cde170b780234e0c4cf805c5a7fb57" + integrity sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w== + dependencies: + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.5.0" + +winston@^3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.2.tgz#56e16b34022eb4cff2638196d9646d7430fdad50" + integrity sha512-MsE1gRx1m5jdTTO9Ld/vND4krP2To+lgDoMEHGGa4HIlAUyXJtfc7CxQcGXVyz2IBpw5hbFkj2b/AtUdQwyRew== + dependencies: + "@colors/colors" "1.5.0" + "@dabh/diagnostics" "^2.0.2" + async "^3.2.3" + is-stream "^2.0.0" + logform "^2.4.0" + one-time "^1.0.0" + readable-stream "^3.4.0" + safe-stable-stringify "^2.3.1" + stack-trace "0.0.x" + triple-beam "^1.3.0" + winston-transport "^4.5.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== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +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@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write-file-atomic@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^7.3.1: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +ws@^8.2.3: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== + +ws@^8.4.2: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + +x-default-browser@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481" + integrity sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw== + optionalDependencies: + default-browser-id "^1.0.4" + +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.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +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@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +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.0, yaml@^1.10.2, yaml@^1.7.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.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.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-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2, yargs-parser@^20.2.7: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs@^15.3.1: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^16.1.1, yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.3.1, yargs@^17.6.2: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + dependencies: + cliui "^8.0.1" + 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.1.1" + +yauzl@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" + integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= + dependencies: + buffer-crc32 "~0.2.3" + fd-slicer "~1.1.0" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +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-wasm-web@0.3.0, yoga-wasm-web@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/yoga-wasm-web/-/yoga-wasm-web-0.3.0.tgz#b14493f35dccda41701524de80ff089fcda0b4ae" + integrity sha512-rD3L4jyMlO1m+RWU60lNwZQK5zmzglCV5fI1gTRikmpv3YzmNIZQbjyfE6cMNb9Xaly/C1SwemYGbsiOekMvnQ== + +zod@^3.19.1: + version "3.19.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473" + integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==