From 6bc93397df43e006be457b4c35a7fe21b560c02d Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Tue, 12 Oct 2021 14:31:17 +0200 Subject: [PATCH 1/9] Add Cypress example --- examples/cypress/.editorconfig | 11 + examples/cypress/.env | 3 + examples/cypress/.eslintrc.js | 3 + examples/cypress/.gitignore | 56 ++ examples/cypress/.npmrc | 7 + examples/cypress/.prettierignore | 9 + examples/cypress/README.md | 25 + examples/cypress/app/api/.keep | 0 .../cypress/app/auth/components/LoginForm.tsx | 54 ++ .../app/auth/components/SignupForm.tsx | 43 + .../app/auth/mutations/changePassword.ts | 23 + .../app/auth/mutations/forgotPassword.test.ts | 58 ++ .../app/auth/mutations/forgotPassword.ts | 41 + examples/cypress/app/auth/mutations/login.ts | 31 + examples/cypress/app/auth/mutations/logout.ts | 5 + .../app/auth/mutations/resetPassword.test.ts | 82 ++ .../app/auth/mutations/resetPassword.ts | 47 + examples/cypress/app/auth/mutations/signup.ts | 15 + .../app/auth/pages/forgot-password.tsx | 48 + examples/cypress/app/auth/pages/login.tsx | 23 + .../cypress/app/auth/pages/reset-password.tsx | 59 ++ examples/cypress/app/auth/pages/signup.tsx | 18 + examples/cypress/app/auth/validations.ts | 33 + examples/cypress/app/core/components/Form.tsx | 59 ++ .../app/core/components/LabeledTextField.tsx | 66 ++ .../cypress/app/core/hooks/useCurrentUser.ts | 7 + examples/cypress/app/core/layouts/Layout.tsx | 22 + examples/cypress/app/pages/404.tsx | 19 + examples/cypress/app/pages/_app.tsx | 40 + examples/cypress/app/pages/_document.tsx | 23 + examples/cypress/app/pages/index.test.tsx | 25 + examples/cypress/app/pages/index.tsx | 272 ++++++ .../app/users/queries/getCurrentUser.ts | 13 + examples/cypress/babel.config.js | 4 + examples/cypress/blitz.config.ts | 19 + examples/cypress/cypress.json | 11 + examples/cypress/cypress/global.d.ts | 22 + examples/cypress/cypress/plugins/index.ts | 52 ++ .../cypress/plugins/register-ts-paths.ts | 2 + examples/cypress/cypress/support/commands.ts | 10 + examples/cypress/cypress/support/index.ts | 24 + examples/cypress/cypress/tsconfig.json | 10 + examples/cypress/db/index.ts | 7 + .../20211012105814_init/migration.sql | 57 ++ .../cypress/db/migrations/migration_lock.toml | 3 + examples/cypress/db/schema.prisma | 65 ++ examples/cypress/db/seeds.ts | 16 + examples/cypress/integrations/.keep | 0 examples/cypress/jest.config.ts | 7 + examples/cypress/mailers/.keep | 0 .../cypress/mailers/forgotPasswordMailer.ts | 45 + examples/cypress/package.json | 62 ++ examples/cypress/public/favicon.ico | Bin 0 -> 4286 bytes examples/cypress/public/logo.png | Bin 0 -> 33469 bytes examples/cypress/test/e2e/login.e2e.ts | 35 + examples/cypress/test/e2e/signup.e2e.ts | 14 + examples/cypress/test/e2e/tsconfig.json | 9 + examples/cypress/test/factories/index.ts | 16 + examples/cypress/test/factories/user.ts | 22 + examples/cypress/test/setup.ts | 4 + examples/cypress/test/utils.tsx | 105 +++ examples/cypress/tsconfig.json | 25 + examples/cypress/types.ts | 18 + yarn.lock | 822 +++++++++++++++++- 64 files changed, 2702 insertions(+), 24 deletions(-) create mode 100644 examples/cypress/.editorconfig create mode 100644 examples/cypress/.env create mode 100644 examples/cypress/.eslintrc.js create mode 100644 examples/cypress/.gitignore create mode 100644 examples/cypress/.npmrc create mode 100644 examples/cypress/.prettierignore create mode 100644 examples/cypress/README.md create mode 100644 examples/cypress/app/api/.keep create mode 100644 examples/cypress/app/auth/components/LoginForm.tsx create mode 100644 examples/cypress/app/auth/components/SignupForm.tsx create mode 100644 examples/cypress/app/auth/mutations/changePassword.ts create mode 100644 examples/cypress/app/auth/mutations/forgotPassword.test.ts create mode 100644 examples/cypress/app/auth/mutations/forgotPassword.ts create mode 100644 examples/cypress/app/auth/mutations/login.ts create mode 100644 examples/cypress/app/auth/mutations/logout.ts create mode 100644 examples/cypress/app/auth/mutations/resetPassword.test.ts create mode 100644 examples/cypress/app/auth/mutations/resetPassword.ts create mode 100644 examples/cypress/app/auth/mutations/signup.ts create mode 100644 examples/cypress/app/auth/pages/forgot-password.tsx create mode 100644 examples/cypress/app/auth/pages/login.tsx create mode 100644 examples/cypress/app/auth/pages/reset-password.tsx create mode 100644 examples/cypress/app/auth/pages/signup.tsx create mode 100644 examples/cypress/app/auth/validations.ts create mode 100644 examples/cypress/app/core/components/Form.tsx create mode 100644 examples/cypress/app/core/components/LabeledTextField.tsx create mode 100644 examples/cypress/app/core/hooks/useCurrentUser.ts create mode 100644 examples/cypress/app/core/layouts/Layout.tsx create mode 100644 examples/cypress/app/pages/404.tsx create mode 100644 examples/cypress/app/pages/_app.tsx create mode 100644 examples/cypress/app/pages/_document.tsx create mode 100644 examples/cypress/app/pages/index.test.tsx create mode 100644 examples/cypress/app/pages/index.tsx create mode 100644 examples/cypress/app/users/queries/getCurrentUser.ts create mode 100644 examples/cypress/babel.config.js create mode 100644 examples/cypress/blitz.config.ts create mode 100644 examples/cypress/cypress.json create mode 100644 examples/cypress/cypress/global.d.ts create mode 100644 examples/cypress/cypress/plugins/index.ts create mode 100644 examples/cypress/cypress/plugins/register-ts-paths.ts create mode 100644 examples/cypress/cypress/support/commands.ts create mode 100644 examples/cypress/cypress/support/index.ts create mode 100644 examples/cypress/cypress/tsconfig.json create mode 100644 examples/cypress/db/index.ts create mode 100644 examples/cypress/db/migrations/20211012105814_init/migration.sql create mode 100644 examples/cypress/db/migrations/migration_lock.toml create mode 100644 examples/cypress/db/schema.prisma create mode 100644 examples/cypress/db/seeds.ts create mode 100644 examples/cypress/integrations/.keep create mode 100644 examples/cypress/jest.config.ts create mode 100644 examples/cypress/mailers/.keep create mode 100644 examples/cypress/mailers/forgotPasswordMailer.ts create mode 100644 examples/cypress/package.json create mode 100755 examples/cypress/public/favicon.ico create mode 100644 examples/cypress/public/logo.png create mode 100644 examples/cypress/test/e2e/login.e2e.ts create mode 100644 examples/cypress/test/e2e/signup.e2e.ts create mode 100644 examples/cypress/test/e2e/tsconfig.json create mode 100644 examples/cypress/test/factories/index.ts create mode 100644 examples/cypress/test/factories/user.ts create mode 100644 examples/cypress/test/setup.ts create mode 100644 examples/cypress/test/utils.tsx create mode 100644 examples/cypress/tsconfig.json create mode 100644 examples/cypress/types.ts diff --git a/examples/cypress/.editorconfig b/examples/cypress/.editorconfig new file mode 100644 index 0000000000..09d7a33a4f --- /dev/null +++ b/examples/cypress/.editorconfig @@ -0,0 +1,11 @@ +# https://EditorConfig.org + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/examples/cypress/.env b/examples/cypress/.env new file mode 100644 index 0000000000..5cd1aca530 --- /dev/null +++ b/examples/cypress/.env @@ -0,0 +1,3 @@ +# This env file should be checked into source control +# This is the place for default values for all environments +# Values in `.env.local` and `.env.production` will override these values diff --git a/examples/cypress/.eslintrc.js b/examples/cypress/.eslintrc.js new file mode 100644 index 0000000000..f845b10d52 --- /dev/null +++ b/examples/cypress/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["blitz"], +} diff --git a/examples/cypress/.gitignore b/examples/cypress/.gitignore new file mode 100644 index 0000000000..ee6565a01d --- /dev/null +++ b/examples/cypress/.gitignore @@ -0,0 +1,56 @@ +# dependencies +node_modules +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +.pnp.* +.npm +web_modules/ + +# blitz +/.blitz/ +/.next/ +*.sqlite +*.sqlite-journal +.now +.blitz** +blitz-log.log + +# misc +.DS_Store + +# local env files +.env.local +.env.*.local +.envrc + +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Testing +.coverage +*.lcov +.nyc_output +lib-cov + +# Caches +*.tsbuildinfo +.eslintcache +.node_repl_history +.yarn-integrity + +# Serverless directories +.serverless/ + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test diff --git a/examples/cypress/.npmrc b/examples/cypress/.npmrc new file mode 100644 index 0000000000..f7777f0b8c --- /dev/null +++ b/examples/cypress/.npmrc @@ -0,0 +1,7 @@ +save-exact=true +legacy-peer-deps=true + +public-hoist-pattern[]=next +public-hoist-pattern[]=secure-password +public-hoist-pattern[]=*jest* +public-hoist-pattern[]=@testing-library/* diff --git a/examples/cypress/.prettierignore b/examples/cypress/.prettierignore new file mode 100644 index 0000000000..b1d375c7de --- /dev/null +++ b/examples/cypress/.prettierignore @@ -0,0 +1,9 @@ +.gitkeep +.env* +*.ico +*.lock +db/migrations +.next +.blitz +.yarn +.pnp* diff --git a/examples/cypress/README.md b/examples/cypress/README.md new file mode 100644 index 0000000000..5e959db144 --- /dev/null +++ b/examples/cypress/README.md @@ -0,0 +1,25 @@ +# **cypress** + +This is an example [Cypress](https://www.cypress.io/) integration. + +## Getting Started + +Migrate the database: + +``` +blitz prisma migrate dev +``` + +Run e2e tests: + +``` +yarn test:e2e +``` + +Open Cypress dashboard: + +``` +yarn cypress:open +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. diff --git a/examples/cypress/app/api/.keep b/examples/cypress/app/api/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/cypress/app/auth/components/LoginForm.tsx b/examples/cypress/app/auth/components/LoginForm.tsx new file mode 100644 index 0000000000..fc99d0ecbc --- /dev/null +++ b/examples/cypress/app/auth/components/LoginForm.tsx @@ -0,0 +1,54 @@ +import { AuthenticationError, Link, useMutation, Routes } from "blitz" +import { LabeledTextField } from "app/core/components/LabeledTextField" +import { Form, FORM_ERROR } from "app/core/components/Form" +import login from "app/auth/mutations/login" +import { Login } from "app/auth/validations" + +type LoginFormProps = { + onSuccess?: () => void +} + +export const LoginForm = (props: LoginFormProps) => { + const [loginMutation] = useMutation(login) + + return ( +
+

Login

+ +
{ + try { + await loginMutation(values) + props.onSuccess?.() + } catch (error: any) { + if (error instanceof AuthenticationError) { + return { [FORM_ERROR]: "Sorry, those credentials are invalid" } + } else { + return { + [FORM_ERROR]: + "Sorry, we had an unexpected error. Please try again. - " + error.toString(), + } + } + } + }} + > + + + + + +
+ Or Sign Up +
+
+ ) +} + +export default LoginForm diff --git a/examples/cypress/app/auth/components/SignupForm.tsx b/examples/cypress/app/auth/components/SignupForm.tsx new file mode 100644 index 0000000000..a76d800dc9 --- /dev/null +++ b/examples/cypress/app/auth/components/SignupForm.tsx @@ -0,0 +1,43 @@ +import { useMutation } from "blitz" +import { LabeledTextField } from "app/core/components/LabeledTextField" +import { Form, FORM_ERROR } from "app/core/components/Form" +import signup from "app/auth/mutations/signup" +import { Signup } from "app/auth/validations" + +type SignupFormProps = { + onSuccess?: () => void +} + +export const SignupForm = (props: SignupFormProps) => { + const [signupMutation] = useMutation(signup) + + return ( +
+

Create an Account

+ +
{ + try { + await signupMutation(values) + props.onSuccess?.() + } catch (error: any) { + if (error.code === "P2002" && error.meta?.target?.includes("email")) { + // This error comes from Prisma + return { email: "This email is already being used" } + } else { + return { [FORM_ERROR]: error.toString() } + } + } + }} + > + + + +
+ ) +} + +export default SignupForm diff --git a/examples/cypress/app/auth/mutations/changePassword.ts b/examples/cypress/app/auth/mutations/changePassword.ts new file mode 100644 index 0000000000..94f7d391d3 --- /dev/null +++ b/examples/cypress/app/auth/mutations/changePassword.ts @@ -0,0 +1,23 @@ +import { NotFoundError, SecurePassword, resolver } from "blitz" +import db from "db" +import { authenticateUser } from "./login" +import { ChangePassword } from "../validations" + +export default resolver.pipe( + resolver.zod(ChangePassword), + resolver.authorize(), + async ({ currentPassword, newPassword }, ctx) => { + const user = await db.user.findFirst({ where: { id: ctx.session.userId! } }) + if (!user) throw new NotFoundError() + + await authenticateUser(user.email, currentPassword) + + const hashedPassword = await SecurePassword.hash(newPassword.trim()) + await db.user.update({ + where: { id: user.id }, + data: { hashedPassword }, + }) + + return true + } +) diff --git a/examples/cypress/app/auth/mutations/forgotPassword.test.ts b/examples/cypress/app/auth/mutations/forgotPassword.test.ts new file mode 100644 index 0000000000..20cc73e98e --- /dev/null +++ b/examples/cypress/app/auth/mutations/forgotPassword.test.ts @@ -0,0 +1,58 @@ +import { hash256, Ctx } from "blitz" +import forgotPassword from "./forgotPassword" +import db from "db" +import previewEmail from "preview-email" + +beforeEach(async () => { + await db.$reset() +}) + +const generatedToken = "plain-token" +jest.mock("blitz", () => ({ + ...jest.requireActual>("blitz")!, + generateToken: () => generatedToken, +})) +jest.mock("preview-email", () => jest.fn()) + +describe("forgotPassword mutation", () => { + it("does not throw error if user doesn't exist", async () => { + await expect(forgotPassword({ email: "no-user@email.com" }, {} as Ctx)).resolves.not.toThrow() + }) + + it("works correctly", async () => { + // Create test user + const user = await db.user.create({ + data: { + email: "user@example.com", + tokens: { + // Create old token to ensure it's deleted + create: { + type: "RESET_PASSWORD", + hashedToken: "token", + expiresAt: new Date(), + sentTo: "user@example.com", + }, + }, + }, + include: { tokens: true }, + }) + + // Invoke the mutation + await forgotPassword({ email: user.email }, {} as Ctx) + + const tokens = await db.token.findMany({ where: { userId: user.id } }) + const token = tokens[0] + if (!user.tokens[0]) throw new Error("Missing user token") + if (!token) throw new Error("Missing token") + + // delete's existing tokens + expect(tokens.length).toBe(1) + + expect(token.id).not.toBe(user.tokens[0].id) + expect(token.type).toBe("RESET_PASSWORD") + expect(token.sentTo).toBe(user.email) + expect(token.hashedToken).toBe(hash256(generatedToken)) + expect(token.expiresAt > new Date()).toBe(true) + expect(previewEmail).toBeCalled() + }) +}) diff --git a/examples/cypress/app/auth/mutations/forgotPassword.ts b/examples/cypress/app/auth/mutations/forgotPassword.ts new file mode 100644 index 0000000000..1cbf2a33cb --- /dev/null +++ b/examples/cypress/app/auth/mutations/forgotPassword.ts @@ -0,0 +1,41 @@ +import { resolver, generateToken, hash256 } from "blitz" +import db from "db" +import { forgotPasswordMailer } from "mailers/forgotPasswordMailer" +import { ForgotPassword } from "../validations" + +const RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS = 4 + +export default resolver.pipe(resolver.zod(ForgotPassword), async ({ email }) => { + // 1. Get the user + const user = await db.user.findFirst({ where: { email: email.toLowerCase() } }) + + // 2. Generate the token and expiration date. + const token = generateToken() + const hashedToken = hash256(token) + const expiresAt = new Date() + expiresAt.setHours(expiresAt.getHours() + RESET_PASSWORD_TOKEN_EXPIRATION_IN_HOURS) + + // 3. If user with this email was found + if (user) { + // 4. Delete any existing password reset tokens + await db.token.deleteMany({ where: { type: "RESET_PASSWORD", userId: user.id } }) + // 5. Save this new token in the database. + await db.token.create({ + data: { + user: { connect: { id: user.id } }, + type: "RESET_PASSWORD", + expiresAt, + hashedToken, + sentTo: user.email, + }, + }) + // 6. Send the email + await forgotPasswordMailer({ to: user.email, token }).send() + } else { + // 7. If no user found wait the same time so attackers can't tell the difference + await new Promise((resolve) => setTimeout(resolve, 750)) + } + + // 8. Return the same result whether a password reset email was sent or not + return +}) diff --git a/examples/cypress/app/auth/mutations/login.ts b/examples/cypress/app/auth/mutations/login.ts new file mode 100644 index 0000000000..1b715ec4bb --- /dev/null +++ b/examples/cypress/app/auth/mutations/login.ts @@ -0,0 +1,31 @@ +import { resolver, SecurePassword, AuthenticationError } from "blitz" +import db from "db" +import { Login } from "../validations" +import { Role } from "types" + +export const authenticateUser = async (rawEmail: string, rawPassword: string) => { + const email = rawEmail.toLowerCase().trim() + const password = rawPassword.trim() + const user = await db.user.findFirst({ where: { email } }) + if (!user) throw new AuthenticationError() + + const result = await SecurePassword.verify(user.hashedPassword, password) + + if (result === SecurePassword.VALID_NEEDS_REHASH) { + // Upgrade hashed password with a more secure hash + const improvedHash = await SecurePassword.hash(password) + await db.user.update({ where: { id: user.id }, data: { hashedPassword: improvedHash } }) + } + + const { hashedPassword, ...rest } = user + return rest +} + +export default resolver.pipe(resolver.zod(Login), async ({ email, password }, ctx) => { + // This throws an error if credentials are invalid + const user = await authenticateUser(email, password) + + await ctx.session.$create({ userId: user.id, role: user.role as Role }) + + return user +}) diff --git a/examples/cypress/app/auth/mutations/logout.ts b/examples/cypress/app/auth/mutations/logout.ts new file mode 100644 index 0000000000..c2f2fefd8d --- /dev/null +++ b/examples/cypress/app/auth/mutations/logout.ts @@ -0,0 +1,5 @@ +import { Ctx } from "blitz" + +export default async function logout(_: any, ctx: Ctx) { + return await ctx.session.$revoke() +} diff --git a/examples/cypress/app/auth/mutations/resetPassword.test.ts b/examples/cypress/app/auth/mutations/resetPassword.test.ts new file mode 100644 index 0000000000..d613716f53 --- /dev/null +++ b/examples/cypress/app/auth/mutations/resetPassword.test.ts @@ -0,0 +1,82 @@ +import resetPassword from "./resetPassword" +import db from "db" +import { hash256, SecurePassword } from "blitz" + +beforeEach(async () => { + await db.$reset() +}) + +const mockCtx: any = { + session: { + $create: jest.fn, + }, +} + +describe("resetPassword mutation", () => { + it("works correctly", async () => { + expect(true).toBe(true) + + // Create test user + const goodToken = "randomPasswordResetToken" + const expiredToken = "expiredRandomPasswordResetToken" + const future = new Date() + future.setHours(future.getHours() + 4) + const past = new Date() + past.setHours(past.getHours() - 4) + + const user = await db.user.create({ + data: { + email: "user@example.com", + tokens: { + // Create old token to ensure it's deleted + create: [ + { + type: "RESET_PASSWORD", + hashedToken: hash256(expiredToken), + expiresAt: past, + sentTo: "user@example.com", + }, + { + type: "RESET_PASSWORD", + hashedToken: hash256(goodToken), + expiresAt: future, + sentTo: "user@example.com", + }, + ], + }, + }, + include: { tokens: true }, + }) + + const newPassword = "newPassword" + + // Non-existent token + await expect( + resetPassword({ token: "no-token", password: "", passwordConfirmation: "" }, mockCtx) + ).rejects.toThrowError() + + // Expired token + await expect( + resetPassword( + { token: expiredToken, password: newPassword, passwordConfirmation: newPassword }, + mockCtx + ) + ).rejects.toThrowError() + + // Good token + await resetPassword( + { token: goodToken, password: newPassword, passwordConfirmation: newPassword }, + mockCtx + ) + + // Delete's the token + const numberOfTokens = await db.token.count({ where: { userId: user.id } }) + expect(numberOfTokens).toBe(0) + + // Updates user's password + const updatedUser = await db.user.findFirst({ where: { id: user.id } }) + expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe( + SecurePassword.VALID + ) + }) +}) diff --git a/examples/cypress/app/auth/mutations/resetPassword.ts b/examples/cypress/app/auth/mutations/resetPassword.ts new file mode 100644 index 0000000000..75d4d67626 --- /dev/null +++ b/examples/cypress/app/auth/mutations/resetPassword.ts @@ -0,0 +1,47 @@ +import { resolver, SecurePassword, hash256 } from "blitz" +import db from "db" +import { ResetPassword } from "../validations" +import login from "./login" + +export class ResetPasswordError extends Error { + name = "ResetPasswordError" + message = "Reset password link is invalid or it has expired." +} + +export default resolver.pipe(resolver.zod(ResetPassword), async ({ password, token }, ctx) => { + // 1. Try to find this token in the database + const hashedToken = hash256(token) + const possibleToken = await db.token.findFirst({ + where: { hashedToken, type: "RESET_PASSWORD" }, + include: { user: true }, + }) + + // 2. If token not found, error + if (!possibleToken) { + throw new ResetPasswordError() + } + const savedToken = possibleToken + + // 3. Delete token so it can't be used again + await db.token.delete({ where: { id: savedToken.id } }) + + // 4. If token has expired, error + if (savedToken.expiresAt < new Date()) { + throw new ResetPasswordError() + } + + // 5. Since token is valid, now we can update the user's password + const hashedPassword = await SecurePassword.hash(password.trim()) + const user = await db.user.update({ + where: { id: savedToken.userId }, + data: { hashedPassword }, + }) + + // 6. Revoke all existing login sessions for this user + await db.session.deleteMany({ where: { userId: user.id } }) + + // 7. Now log the user in with the new credentials + await login({ email: user.email, password }, ctx) + + return true +}) diff --git a/examples/cypress/app/auth/mutations/signup.ts b/examples/cypress/app/auth/mutations/signup.ts new file mode 100644 index 0000000000..4791a99f43 --- /dev/null +++ b/examples/cypress/app/auth/mutations/signup.ts @@ -0,0 +1,15 @@ +import { resolver, SecurePassword } from "blitz" +import db from "db" +import { Signup } from "app/auth/validations" +import { Role } from "types" + +export default resolver.pipe(resolver.zod(Signup), async ({ email, password }, ctx) => { + const hashedPassword = await SecurePassword.hash(password.trim()) + const user = await db.user.create({ + data: { email: email.toLowerCase().trim(), hashedPassword, role: "USER" }, + select: { id: true, name: true, email: true, role: true }, + }) + + await ctx.session.$create({ userId: user.id, role: user.role as Role }) + return user +}) diff --git a/examples/cypress/app/auth/pages/forgot-password.tsx b/examples/cypress/app/auth/pages/forgot-password.tsx new file mode 100644 index 0000000000..c2509982df --- /dev/null +++ b/examples/cypress/app/auth/pages/forgot-password.tsx @@ -0,0 +1,48 @@ +import { BlitzPage, useMutation } from "blitz" +import Layout from "app/core/layouts/Layout" +import { LabeledTextField } from "app/core/components/LabeledTextField" +import { Form, FORM_ERROR } from "app/core/components/Form" +import { ForgotPassword } from "app/auth/validations" +import forgotPassword from "app/auth/mutations/forgotPassword" + +const ForgotPasswordPage: BlitzPage = () => { + const [forgotPasswordMutation, { isSuccess }] = useMutation(forgotPassword) + + return ( +
+

Forgot your password?

+ + {isSuccess ? ( +
+

Request Submitted

+

+ If your email is in our system, you will receive instructions to reset your password + shortly. +

+
+ ) : ( +
{ + try { + await forgotPasswordMutation(values) + } catch (error: any) { + return { + [FORM_ERROR]: "Sorry, we had an unexpected error. Please try again.", + } + } + }} + > + + + )} +
+ ) +} + +ForgotPasswordPage.redirectAuthenticatedTo = "/" +ForgotPasswordPage.getLayout = (page) => {page} + +export default ForgotPasswordPage diff --git a/examples/cypress/app/auth/pages/login.tsx b/examples/cypress/app/auth/pages/login.tsx new file mode 100644 index 0000000000..de51b126df --- /dev/null +++ b/examples/cypress/app/auth/pages/login.tsx @@ -0,0 +1,23 @@ +import { useRouter, BlitzPage } from "blitz" +import Layout from "app/core/layouts/Layout" +import { LoginForm } from "app/auth/components/LoginForm" + +const LoginPage: BlitzPage = () => { + const router = useRouter() + + return ( +
+ { + const next = router.query.next ? decodeURIComponent(router.query.next as string) : "/" + router.push(next) + }} + /> +
+ ) +} + +LoginPage.redirectAuthenticatedTo = "/" +LoginPage.getLayout = (page) => {page} + +export default LoginPage diff --git a/examples/cypress/app/auth/pages/reset-password.tsx b/examples/cypress/app/auth/pages/reset-password.tsx new file mode 100644 index 0000000000..6d25e7d348 --- /dev/null +++ b/examples/cypress/app/auth/pages/reset-password.tsx @@ -0,0 +1,59 @@ +import { BlitzPage, useRouterQuery, Link, useMutation, Routes } from "blitz" +import Layout from "app/core/layouts/Layout" +import { LabeledTextField } from "app/core/components/LabeledTextField" +import { Form, FORM_ERROR } from "app/core/components/Form" +import { ResetPassword } from "app/auth/validations" +import resetPassword from "app/auth/mutations/resetPassword" + +const ResetPasswordPage: BlitzPage = () => { + const query = useRouterQuery() + const [resetPasswordMutation, { isSuccess }] = useMutation(resetPassword) + + return ( +
+

Set a New Password

+ + {isSuccess ? ( +
+

Password Reset Successfully

+

+ Go to the homepage +

+
+ ) : ( +
{ + try { + await resetPasswordMutation(values) + } catch (error: any) { + if (error.name === "ResetPasswordError") { + return { + [FORM_ERROR]: error.message, + } + } else { + return { + [FORM_ERROR]: "Sorry, we had an unexpected error. Please try again.", + } + } + } + }} + > + + + + )} +
+ ) +} + +ResetPasswordPage.redirectAuthenticatedTo = "/" +ResetPasswordPage.getLayout = (page) => {page} + +export default ResetPasswordPage diff --git a/examples/cypress/app/auth/pages/signup.tsx b/examples/cypress/app/auth/pages/signup.tsx new file mode 100644 index 0000000000..98f6bcbafd --- /dev/null +++ b/examples/cypress/app/auth/pages/signup.tsx @@ -0,0 +1,18 @@ +import { useRouter, BlitzPage, Routes } from "blitz" +import Layout from "app/core/layouts/Layout" +import { SignupForm } from "app/auth/components/SignupForm" + +const SignupPage: BlitzPage = () => { + const router = useRouter() + + return ( +
+ router.push(Routes.Home())} /> +
+ ) +} + +SignupPage.redirectAuthenticatedTo = "/" +SignupPage.getLayout = (page) => {page} + +export default SignupPage diff --git a/examples/cypress/app/auth/validations.ts b/examples/cypress/app/auth/validations.ts new file mode 100644 index 0000000000..fe47769166 --- /dev/null +++ b/examples/cypress/app/auth/validations.ts @@ -0,0 +1,33 @@ +import { z } from "zod" + +const password = z.string().min(10).max(100) + +export const Signup = z.object({ + email: z.string().email(), + password, +}) + +export const Login = z.object({ + email: z.string().email(), + password: z.string(), +}) + +export const ForgotPassword = z.object({ + email: z.string().email(), +}) + +export const ResetPassword = z + .object({ + password: password, + passwordConfirmation: password, + token: z.string(), + }) + .refine((data) => data.password === data.passwordConfirmation, { + message: "Passwords don't match", + path: ["passwordConfirmation"], // set the path of the error + }) + +export const ChangePassword = z.object({ + currentPassword: z.string(), + newPassword: password, +}) diff --git a/examples/cypress/app/core/components/Form.tsx b/examples/cypress/app/core/components/Form.tsx new file mode 100644 index 0000000000..eb45dc3b9f --- /dev/null +++ b/examples/cypress/app/core/components/Form.tsx @@ -0,0 +1,59 @@ +import { ReactNode, PropsWithoutRef } from "react" +import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form" +import { z } from "zod" +import { validateZodSchema } from "blitz" +export { FORM_ERROR } from "final-form" + +export interface FormProps> + extends Omit, "onSubmit"> { + /** All your form fields */ + children?: ReactNode + /** Text to display in the submit button */ + submitText?: string + schema?: S + onSubmit: FinalFormProps>["onSubmit"] + initialValues?: FinalFormProps>["initialValues"] +} + +export function Form>({ + children, + submitText, + schema, + initialValues, + onSubmit, + ...props +}: FormProps) { + return ( + ( +
+ {/* Form fields supplied as children are rendered here */} + {children} + + {submitError && ( +
+ {submitError} +
+ )} + + {submitText && ( + + )} + + +
+ )} + /> + ) +} + +export default Form diff --git a/examples/cypress/app/core/components/LabeledTextField.tsx b/examples/cypress/app/core/components/LabeledTextField.tsx new file mode 100644 index 0000000000..7dc98b89c9 --- /dev/null +++ b/examples/cypress/app/core/components/LabeledTextField.tsx @@ -0,0 +1,66 @@ +import { forwardRef, ComponentPropsWithoutRef, PropsWithoutRef } from "react" +import { useField, UseFieldConfig } from "react-final-form" + +export interface LabeledTextFieldProps extends PropsWithoutRef { + /** Field name. */ + name: string + /** Field label. */ + label: string + /** Field type. Doesn't include radio buttons and checkboxes */ + type?: "text" | "password" | "email" | "number" + outerProps?: PropsWithoutRef + labelProps?: ComponentPropsWithoutRef<"label"> + fieldProps?: UseFieldConfig +} + +export const LabeledTextField = forwardRef( + ({ name, label, outerProps, fieldProps, labelProps, ...props }, ref) => { + const { + input, + meta: { touched, error, submitError, submitting }, + } = useField(name, { + parse: + props.type === "number" + ? (Number as any) + : // Converting `""` to `null` ensures empty values will be set to null in the DB + (v) => (v === "" ? null : v), + ...fieldProps, + }) + + const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError + + return ( +
+ + + {touched && normalizedError && ( +
+ {normalizedError} +
+ )} + + +
+ ) + } +) + +export default LabeledTextField diff --git a/examples/cypress/app/core/hooks/useCurrentUser.ts b/examples/cypress/app/core/hooks/useCurrentUser.ts new file mode 100644 index 0000000000..2938468c7b --- /dev/null +++ b/examples/cypress/app/core/hooks/useCurrentUser.ts @@ -0,0 +1,7 @@ +import { useQuery } from "blitz" +import getCurrentUser from "app/users/queries/getCurrentUser" + +export const useCurrentUser = () => { + const [user] = useQuery(getCurrentUser, null) + return user +} diff --git a/examples/cypress/app/core/layouts/Layout.tsx b/examples/cypress/app/core/layouts/Layout.tsx new file mode 100644 index 0000000000..d403d1fce6 --- /dev/null +++ b/examples/cypress/app/core/layouts/Layout.tsx @@ -0,0 +1,22 @@ +import { ReactNode } from "react" +import { Head } from "blitz" + +type LayoutProps = { + title?: string + children: ReactNode +} + +const Layout = ({ title, children }: LayoutProps) => { + return ( + <> + + {title || "cypress"} + + + + {children} + + ) +} + +export default Layout diff --git a/examples/cypress/app/pages/404.tsx b/examples/cypress/app/pages/404.tsx new file mode 100644 index 0000000000..ab81189e6c --- /dev/null +++ b/examples/cypress/app/pages/404.tsx @@ -0,0 +1,19 @@ +import { Head, ErrorComponent } from "blitz" + +// ------------------------------------------------------ +// This page is rendered if a route match is not found +// ------------------------------------------------------ +export default function Page404() { + const statusCode = 404 + const title = "This page could not be found" + return ( + <> + + + {statusCode}: {title} + + + + + ) +} diff --git a/examples/cypress/app/pages/_app.tsx b/examples/cypress/app/pages/_app.tsx new file mode 100644 index 0000000000..aad82b3ff2 --- /dev/null +++ b/examples/cypress/app/pages/_app.tsx @@ -0,0 +1,40 @@ +import { + AppProps, + ErrorBoundary, + ErrorComponent, + AuthenticationError, + AuthorizationError, + ErrorFallbackProps, + useQueryErrorResetBoundary, +} from "blitz" +import LoginForm from "app/auth/components/LoginForm" + +export default function App({ Component, pageProps }: AppProps) { + const getLayout = Component.getLayout || ((page) => page) + + return ( + + {getLayout()} + + ) +} + +function RootErrorFallback({ error, resetErrorBoundary }: ErrorFallbackProps) { + if (error instanceof AuthenticationError) { + return + } else if (error instanceof AuthorizationError) { + return ( + + ) + } else { + return ( + + ) + } +} diff --git a/examples/cypress/app/pages/_document.tsx b/examples/cypress/app/pages/_document.tsx new file mode 100644 index 0000000000..78b9fc4d99 --- /dev/null +++ b/examples/cypress/app/pages/_document.tsx @@ -0,0 +1,23 @@ +import { Document, Html, DocumentHead, Main, BlitzScript /*DocumentContext*/ } from "blitz" + +class MyDocument extends Document { + // Only uncomment if you need to customize this behaviour + // static async getInitialProps(ctx: DocumentContext) { + // const initialProps = await Document.getInitialProps(ctx) + // return {...initialProps} + // } + + render() { + return ( + + + +
+ + + + ) + } +} + +export default MyDocument diff --git a/examples/cypress/app/pages/index.test.tsx b/examples/cypress/app/pages/index.test.tsx new file mode 100644 index 0000000000..9374c60249 --- /dev/null +++ b/examples/cypress/app/pages/index.test.tsx @@ -0,0 +1,25 @@ +import { render } from "test/utils" + +import Home from "./index" +import { useCurrentUser } from "app/core/hooks/useCurrentUser" + +jest.mock("app/core/hooks/useCurrentUser") +const mockUseCurrentUser = useCurrentUser as jest.MockedFunction + +test.skip("renders blitz documentation link", () => { + // This is an example of how to ensure a specific item is in the document + // But it's disabled by default (by test.skip) so the test doesn't fail + // when you remove the the default content from the page + + // This is an example on how to mock api hooks when testing + mockUseCurrentUser.mockReturnValue({ + id: 1, + name: "User", + email: "user@email.com", + role: "user", + }) + + const { getByText } = render() + const linkElement = getByText(/Documentation/i) + expect(linkElement).toBeInTheDocument() +}) diff --git a/examples/cypress/app/pages/index.tsx b/examples/cypress/app/pages/index.tsx new file mode 100644 index 0000000000..b49e733bbb --- /dev/null +++ b/examples/cypress/app/pages/index.tsx @@ -0,0 +1,272 @@ +import { Suspense } from "react" +import { Image, Link, BlitzPage, useMutation, Routes } from "blitz" +import Layout from "app/core/layouts/Layout" +import { useCurrentUser } from "app/core/hooks/useCurrentUser" +import logout from "app/auth/mutations/logout" +import logo from "public/logo.png" + +/* + * This file is just for a pleasant getting started page for your new app. + * You can delete everything in here and start from scratch if you like. + */ + +const UserInfo = () => { + const currentUser = useCurrentUser() + const [logoutMutation] = useMutation(logout) + + if (currentUser) { + return ( + <> + +
+ User id: {currentUser.id} +
+ User role: {currentUser.role} +
+ + ) + } else { + return ( + <> + + + Sign Up + + + + + Login + + + + ) + } +} + +const Home: BlitzPage = () => { + return ( +
+
+
+ blitzjs +
+

+ Congrats! Your app is ready, including user sign-up and log-in. +

+
+ + + +
+

+ + To add a new model to your app,
+ run the following in your terminal: +
+

+
+          blitz generate all project name:string
+        
+
(And select Yes to run prisma migrate)
+
+

+ Then restart the server +

+
+            Ctrl + c
+          
+
+            blitz dev
+          
+

+ and go to{" "} + + /projects + +

+
+ +
+ + + + +
+ ) +} + +Home.suppressFirstRenderFlicker = true +Home.getLayout = (page) => {page} + +export default Home diff --git a/examples/cypress/app/users/queries/getCurrentUser.ts b/examples/cypress/app/users/queries/getCurrentUser.ts new file mode 100644 index 0000000000..9822e16bf6 --- /dev/null +++ b/examples/cypress/app/users/queries/getCurrentUser.ts @@ -0,0 +1,13 @@ +import { Ctx } from "blitz" +import db from "db" + +export default async function getCurrentUser(_ = null, { session }: Ctx) { + if (!session.userId) return null + + const user = await db.user.findFirst({ + where: { id: session.userId }, + select: { id: true, name: true, email: true, role: true }, + }) + + return user +} diff --git a/examples/cypress/babel.config.js b/examples/cypress/babel.config.js new file mode 100644 index 0000000000..dfdf62cea1 --- /dev/null +++ b/examples/cypress/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ["blitz/babel"], + plugins: [], +} diff --git a/examples/cypress/blitz.config.ts b/examples/cypress/blitz.config.ts new file mode 100644 index 0000000000..450358086e --- /dev/null +++ b/examples/cypress/blitz.config.ts @@ -0,0 +1,19 @@ +import { BlitzConfig, sessionMiddleware, simpleRolesIsAuthorized } from "blitz" + +const config: BlitzConfig = { + middleware: [ + sessionMiddleware({ + cookiePrefix: "cypress", + isAuthorized: simpleRolesIsAuthorized, + }), + ], + /* Uncomment this to customize the webpack config + webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => { + // Note: we provide webpack above so you should not `require` it + // Perform customizations to webpack config + // Important: return the modified config + return config + }, + */ +} +module.exports = config diff --git a/examples/cypress/cypress.json b/examples/cypress/cypress.json new file mode 100644 index 0000000000..be748dca53 --- /dev/null +++ b/examples/cypress/cypress.json @@ -0,0 +1,11 @@ +{ + "baseUrl": "http://localhost:3099", + "integrationFolder": "test/e2e", + "fixturesFolder": false, + "nodeVersion": "system", + "defaultCommandTimeout": 12000, + "pageLoadTimeout": 120000, + "ignoreTestFiles": ["tsconfig.json"], + "chromeWebSecurity": false, + "video": false +} diff --git a/examples/cypress/cypress/global.d.ts b/examples/cypress/cypress/global.d.ts new file mode 100644 index 0000000000..13a74c3788 --- /dev/null +++ b/examples/cypress/cypress/global.d.ts @@ -0,0 +1,22 @@ +declare namespace Cypress { + export interface Chainable { + // task for totally resetting the database + task(event: "db:reset", options?: Partial): Chainable + // task for deleting db data without dropping tables + task(event: "db:clear", options?: Partial): Chainable + // task for seeding db with essential data for tests + task(event: "db:seed", options?: Partial): Chainable + + // task for creating a new factory + task( + event: "factory", + args: { name: string; attrs?: any }, + options?: Partial + ): Chainable + + /** + * Logs-in user by using API request + */ + login({ email: string, password: string }): Chainable + } +} diff --git a/examples/cypress/cypress/plugins/index.ts b/examples/cypress/cypress/plugins/index.ts new file mode 100644 index 0000000000..0a8f001237 --- /dev/null +++ b/examples/cypress/cypress/plugins/index.ts @@ -0,0 +1,52 @@ +process.env.NODE_ENV = "test" +require("dotenv-flow").config({ silent: true }) + +import "./register-ts-paths" +import db from "db" +import seed from "db/seeds" +import { factory } from "test/factories" + +let dbSetup = false + +const pluginConfig: Cypress.PluginConfig = (on, _config) => { + on("task", { + factory, + "db:reset": async () => { + if (!dbSetup) { + try { + // Only need to do this once at startup + console.log("Resetting database...") + await db.$reset() + console.log("Database reset.") + dbSetup = true + } catch (error) { + console.error(error) + throw new Error("Failed to set up database in cypress/plugins/index.ts") + } + } + return true + }, + "db:clear": async () => { + // Delete all data without dropping tables, so migration isn't required + await db.$executeRaw` + do + $$ + declare + l_stmt text; + begin + select 'truncate ' || string_agg(format('%I.%I', schemaname, tablename), ',') + into l_stmt from pg_tables + where schemaname in ('public'); + execute l_stmt; + end; + $$ + ` + return true + }, + "db:seed": async () => { + await seed() + return true + }, + }) +} +export default pluginConfig diff --git a/examples/cypress/cypress/plugins/register-ts-paths.ts b/examples/cypress/cypress/plugins/register-ts-paths.ts new file mode 100644 index 0000000000..544c69d023 --- /dev/null +++ b/examples/cypress/cypress/plugins/register-ts-paths.ts @@ -0,0 +1,2 @@ +// @ts-ignore +require("tsconfig-paths").register() diff --git a/examples/cypress/cypress/support/commands.ts b/examples/cypress/cypress/support/commands.ts new file mode 100644 index 0000000000..5594db4a20 --- /dev/null +++ b/examples/cypress/cypress/support/commands.ts @@ -0,0 +1,10 @@ +import "@testing-library/cypress/add-commands" + +Cypress.Commands.add("login", ({ email, password }) => { + return cy.request("POST", `/api/rpc/login`, { + params: { + email, + password, + }, + }) +}) diff --git a/examples/cypress/cypress/support/index.ts b/examples/cypress/cypress/support/index.ts new file mode 100644 index 0000000000..cdc84367ee --- /dev/null +++ b/examples/cypress/cypress/support/index.ts @@ -0,0 +1,24 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** +import "./commands" + +before(() => { + cy.task("db:reset") +}) + +beforeEach(() => { + cy.task("db:clear") + cy.task("db:seed") +}) diff --git a/examples/cypress/cypress/tsconfig.json b/examples/cypress/cypress/tsconfig.json new file mode 100644 index 0000000000..e45ed56173 --- /dev/null +++ b/examples/cypress/cypress/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["cypress", "@types/testing-library__cypress"], + "isolatedModules": false, + "tsBuildInfoFile": ".tsbuildinfo.test" + }, + "exclude": ["node_modules"], + "include": ["**/*.ts"] +} diff --git a/examples/cypress/db/index.ts b/examples/cypress/db/index.ts new file mode 100644 index 0000000000..a63b57b248 --- /dev/null +++ b/examples/cypress/db/index.ts @@ -0,0 +1,7 @@ +import { enhancePrisma } from "blitz" +import { PrismaClient } from "@prisma/client" + +const EnhancedPrisma = enhancePrisma(PrismaClient) + +export * from "@prisma/client" +export default new EnhancedPrisma() diff --git a/examples/cypress/db/migrations/20211012105814_init/migration.sql b/examples/cypress/db/migrations/20211012105814_init/migration.sql new file mode 100644 index 0000000000..1f4f306120 --- /dev/null +++ b/examples/cypress/db/migrations/20211012105814_init/migration.sql @@ -0,0 +1,57 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "name" TEXT, + "email" TEXT NOT NULL, + "hashedPassword" TEXT, + "role" TEXT NOT NULL DEFAULT E'USER', + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "expiresAt" TIMESTAMP(3), + "handle" TEXT NOT NULL, + "hashedSessionToken" TEXT, + "antiCSRFToken" TEXT, + "publicData" TEXT, + "privateData" TEXT, + "userId" INTEGER, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Token" ( + "id" SERIAL NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "hashedToken" TEXT NOT NULL, + "type" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "sentTo" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + + CONSTRAINT "Token_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle"); + +-- CreateIndex +CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type"); + +-- AddForeignKey +ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Token" ADD CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/examples/cypress/db/migrations/migration_lock.toml b/examples/cypress/db/migrations/migration_lock.toml new file mode 100644 index 0000000000..fbffa92c2b --- /dev/null +++ b/examples/cypress/db/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/examples/cypress/db/schema.prisma b/examples/cypress/db/schema.prisma new file mode 100644 index 0000000000..e19edad04f --- /dev/null +++ b/examples/cypress/db/schema.prisma @@ -0,0 +1,65 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgres" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +// -------------------------------------- + +model User { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String? + email String @unique + hashedPassword String? + role String @default("USER") + + tokens Token[] + sessions Session[] +} + +model Session { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + expiresAt DateTime? + handle String @unique + hashedSessionToken String? + antiCSRFToken String? + publicData String? + privateData String? + + user User? @relation(fields: [userId], references: [id]) + userId Int? +} + +model Token { + id Int @id @default(autoincrement()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + hashedToken String + type String + // See note below about TokenType enum + // type TokenType + expiresAt DateTime + sentTo String + + user User @relation(fields: [userId], references: [id]) + userId Int + + @@unique([hashedToken, type]) +} + +// NOTE: It's highly recommended to use an enum for the token type +// but enums only work in Postgres. +// See: https://blitzjs.com/docs/database-overview#switch-to-postgresql +// enum TokenType { +// RESET_PASSWORD +// } diff --git a/examples/cypress/db/seeds.ts b/examples/cypress/db/seeds.ts new file mode 100644 index 0000000000..2f182694ee --- /dev/null +++ b/examples/cypress/db/seeds.ts @@ -0,0 +1,16 @@ +// import db from "./index" + +/* + * This seed function is executed when you run `blitz db seed`. + * + * Probably you want to use a library like https://chancejs.com + * or https://github.com/Marak/Faker.js to easily generate + * realistic data. + */ +const seed = async () => { + // for (let i = 0; i < 5; i++) { + // await db.project.create({ data: { name: "Project " + i } }) + // } +} + +export default seed diff --git a/examples/cypress/integrations/.keep b/examples/cypress/integrations/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/cypress/jest.config.ts b/examples/cypress/jest.config.ts new file mode 100644 index 0000000000..30fe3fc967 --- /dev/null +++ b/examples/cypress/jest.config.ts @@ -0,0 +1,7 @@ +import type { Config } from "@jest/types" + +const config: Config.InitialOptions = { + preset: "blitz", +} + +export default config diff --git a/examples/cypress/mailers/.keep b/examples/cypress/mailers/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/examples/cypress/mailers/forgotPasswordMailer.ts b/examples/cypress/mailers/forgotPasswordMailer.ts new file mode 100644 index 0000000000..2566359b97 --- /dev/null +++ b/examples/cypress/mailers/forgotPasswordMailer.ts @@ -0,0 +1,45 @@ +/* TODO - You need to add a mailer integration in `integrations/` and import here. + * + * The integration file can be very simple. Instantiate the email client + * and then export it. That way you can import here and anywhere else + * and use it straight away. + */ +import previewEmail from "preview-email" + +type ResetPasswordMailer = { + to: string + token: string +} + +export function forgotPasswordMailer({ to, token }: ResetPasswordMailer) { + // In production, set APP_ORIGIN to your production server origin + const origin = process.env.APP_ORIGIN || process.env.BLITZ_DEV_SERVER_ORIGIN + const resetUrl = `${origin}/reset-password?token=${token}` + + const msg = { + from: "TODO@example.com", + to, + subject: "Your Password Reset Instructions", + html: ` +

Reset Your Password

+

NOTE: You must set up a production email integration in mailers/forgotPasswordMailer.ts

+ + + Click here to set a new password + + `, + } + + return { + async send() { + if (process.env.NODE_ENV === "production") { + // TODO - send the production email, like this: + // await postmark.sendEmail(msg) + throw new Error("No production email implementation in mailers/forgotPasswordMailer") + } else { + // Preview email in the browser + await previewEmail(msg) + } + }, + } +} diff --git a/examples/cypress/package.json b/examples/cypress/package.json new file mode 100644 index 0000000000..42496be2ad --- /dev/null +++ b/examples/cypress/package.json @@ -0,0 +1,62 @@ +{ + "name": "cypress", + "version": "1.0.0", + "scripts": { + "dev": "blitz dev", + "build": "blitz build", + "start": "blitz start", + "studio": "blitz prisma studio", + "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", + "test": "jest", + "test:watch": "jest --watch", + "test:server": "NODE_ENV=test blitz build && NODE_ENV=test blitz start -p 3099", + "test:e2e": "start-server-and-test test:server http://localhost:3099 'cypress run'", + "cypress:open": "start-server-and-test test:server http://localhost:3099 'cypress open'" + }, + "prisma": { + "schema": "db/schema.prisma" + }, + "prettier": { + "semi": false, + "printWidth": 100 + }, + "lint-staged": { + "*.{js,ts,tsx}": [ + "eslint --fix" + ] + }, + "husky": { + "hooks": { + "pre-commit": "tsc && lint-staged && pretty-quick --staged", + "pre-push": "npm run lint && npm run test" + } + }, + "dependencies": { + "@prisma/client": "3.2.1", + "blitz": "0.41.0", + "final-form": "4.20.4", + "react": "alpha", + "react-dom": "alpha", + "react-final-form": "6.5.7", + "zod": "3.9.8" + }, + "devDependencies": { + "@testing-library/cypress": "8.0.1", + "@types/preview-email": "2.0.1", + "@types/react": "17.0.28", + "@types/testing-library__cypress": "5.0.9", + "chance": "1.1.8", + "cypress": "8.6.0", + "eslint": "7.32.0", + "husky": "6.0.0", + "lint-staged": "10.5.4", + "prettier": "2.4.1", + "prettier-plugin-prisma": "0.15.0", + "pretty-quick": "3.1.1", + "preview-email": "3.0.5", + "prisma": "3.2.1", + "start-server-and-test": "1.14.0", + "typescript": "~4.3" + }, + "private": true +} diff --git a/examples/cypress/public/favicon.ico b/examples/cypress/public/favicon.ico new file mode 100755 index 0000000000000000000000000000000000000000..c7bd1c095d82464715009a27231f07d3936d367b GIT binary patch literal 4286 zcmeH~KS~2Z6vk(@SOP*c5;6EE0Sj&P2x@69%oQxO^Z>~fJc5W|W9I?ViLD0^Eo_89 z3Xy;z&+p6dVBO4wb+emF<(JV}XTF*D?YuV{6XCnnGW;}m%f_r3WA*^L%rRj9_zTZ= z4jitdfKMx{#>y?Y0E-38n0*Q>F*Gm1CwK&n?1N|gODhY|I|1)Nz!x|J<;TEE1f5;* z1V(`RZ08dLD>Z0cgKt1>rTiHD_FQ$f)flYL=&pbGr01%eYcwU)OUeOdk@i|EfKLE^`)hSJAZ~#+-WRQ@wQi z@A-5G|NfMB3jdu?V^7an|9M9#+H>Z(L(l`Nmu}0c{~9SP>(J)?n)^&^ms9^$o2|Qt z@c)bS9jLtepVW72Xh1(11NEG_s94X_+D@N$v1AO&_1wP(jY0XI`}G-vb8rJ@zVnGx zMMhv4iwwx3NFTgLB3-nkgML%Ri{BhGG)FQpZD5)t)!CN5nH=ARMz@K6as-Vw5cvVi C{Q%?u literal 0 HcmV?d00001 diff --git a/examples/cypress/public/logo.png b/examples/cypress/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6a982616eefe059523490be01aaf48e6db383f54 GIT binary patch literal 33469 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 literal 0 HcmV?d00001 diff --git a/examples/cypress/test/e2e/login.e2e.ts b/examples/cypress/test/e2e/login.e2e.ts new file mode 100644 index 0000000000..ce70626059 --- /dev/null +++ b/examples/cypress/test/e2e/login.e2e.ts @@ -0,0 +1,35 @@ +import { User } from "test/factories" + +describe("Login", () => { + describe("with an email that doesnt exist", () => { + it("shows an error", () => { + const email = "nowayshouldIexist@example.com" + const password = "test1234" + + cy.visit("/login").wait(100) + + cy.findByLabelText(/email/i).type(email) + cy.findByLabelText(/password/i).type(password) + cy.findAllByRole("button", { name: /login/i }).click() + + cy.findByText(/invalid/i).should("exist") + }) + }) + + describe("with valid credentials", () => { + it("logs in", () => { + const attrs = { password: "superstrongpassword" } + + cy.visit("/login").wait(100) + + cy.task("factory", { name: "user", attrs }).then((user: User) => { + cy.findByLabelText(/email/i).type(user.email) + cy.findByLabelText(/password/i).type(attrs.password) + cy.findAllByRole("button", { name: /login/i }).click() + + cy.location("pathname").should("equal", "/") + cy.findByText(/logout/i).should("exist") + }) + }) + }) +}) diff --git a/examples/cypress/test/e2e/signup.e2e.ts b/examples/cypress/test/e2e/signup.e2e.ts new file mode 100644 index 0000000000..1b22ea75bc --- /dev/null +++ b/examples/cypress/test/e2e/signup.e2e.ts @@ -0,0 +1,14 @@ +describe("Signup", () => { + it("creates new account", () => { + const attrs = { email: "blitz@example.com", password: "superstrongpassword" } + + cy.visit("/signup").wait(100) + + cy.findByLabelText(/email/i).type(attrs.email) + cy.findByLabelText(/password/i).type(attrs.password) + cy.findAllByRole("button", { name: /create account/i }).click() + + cy.location("pathname").should("equal", "/") + cy.findByText(/logout/i).should("exist") + }) +}) diff --git a/examples/cypress/test/e2e/tsconfig.json b/examples/cypress/test/e2e/tsconfig.json new file mode 100644 index 0000000000..da1fd184db --- /dev/null +++ b/examples/cypress/test/e2e/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["cypress", "@types/testing-library__cypress"], + "isolatedModules": false + }, + "exclude": ["node_modules"], + "include": ["**/*.ts", "../../cypress/**/*.ts"] +} diff --git a/examples/cypress/test/factories/index.ts b/examples/cypress/test/factories/index.ts new file mode 100644 index 0000000000..e6d3d2a32d --- /dev/null +++ b/examples/cypress/test/factories/index.ts @@ -0,0 +1,16 @@ +import { user } from "./user" +export * from "./user" + +export const Factories = { + user, +} + +export function factory({ + name, + attrs, +}: { + name: T + attrs: Parameters[0] +}) { + return Factories[name](attrs) +} diff --git a/examples/cypress/test/factories/user.ts b/examples/cypress/test/factories/user.ts new file mode 100644 index 0000000000..16ea486560 --- /dev/null +++ b/examples/cypress/test/factories/user.ts @@ -0,0 +1,22 @@ +import { PromiseReturnType } from "next/types/utils" +import signup from "app/auth/mutations/signup" +import Chance from "chance" + +const chance = new Chance() + +const ctx = { + session: { $create: () => {} }, +} + +type UserAttributes = { + email?: string + password: string +} + +export const user = async ({ email = chance.email(), password }: UserAttributes) => { + const user = await signup({ email, password }, ctx as any) + + return user +} + +export type User = PromiseReturnType diff --git a/examples/cypress/test/setup.ts b/examples/cypress/test/setup.ts new file mode 100644 index 0000000000..c278ddb9e6 --- /dev/null +++ b/examples/cypress/test/setup.ts @@ -0,0 +1,4 @@ +// This is the jest 'setupFilesAfterEnv' setup file +// It's a good place to set globals, add global before/after hooks, etc + +export {} // so TS doesn't complain diff --git a/examples/cypress/test/utils.tsx b/examples/cypress/test/utils.tsx new file mode 100644 index 0000000000..6d52b9baa7 --- /dev/null +++ b/examples/cypress/test/utils.tsx @@ -0,0 +1,105 @@ +import { RouterContext, BlitzRouter, BlitzProvider } from "blitz" +import { render as defaultRender } from "@testing-library/react" +import { renderHook as defaultRenderHook } from "@testing-library/react-hooks" + +export * from "@testing-library/react" + +// -------------------------------------------------------------------------------- +// This file customizes the render() and renderHook() test functions provided +// by React testing library. It adds a router context wrapper with a mocked router. +// +// You should always import `render` and `renderHook` from this file +// +// This is the place to add any other context providers you need while testing. +// -------------------------------------------------------------------------------- + +// -------------------------------------------------- +// render() +// -------------------------------------------------- +// Override the default test render with our own +// +// You can override the router mock like this: +// +// const { baseElement } = render(, { +// router: { pathname: '/my-custom-pathname' }, +// }); +// -------------------------------------------------- +export function render( + ui: RenderUI, + { wrapper, router, dehydratedState, ...options }: RenderOptions = {} +) { + if (!wrapper) { + // Add a default context wrapper if one isn't supplied from the test + wrapper = ({ children }) => ( + + + {children} + + + ) + } + return defaultRender(ui, { wrapper, ...options }) +} + +// -------------------------------------------------- +// renderHook() +// -------------------------------------------------- +// Override the default test renderHook with our own +// +// You can override the router mock like this: +// +// const result = renderHook(() => myHook(), { +// router: { pathname: '/my-custom-pathname' }, +// }); +// -------------------------------------------------- +export function renderHook( + hook: RenderHook, + { wrapper, router, dehydratedState, ...options }: RenderHookOptions = {} +) { + if (!wrapper) { + // Add a default context wrapper if one isn't supplied from the test + wrapper = ({ children }) => ( + + + {children} + + + ) + } + return defaultRenderHook(hook, { wrapper, ...options }) +} + +export const mockRouter: BlitzRouter = { + basePath: "", + pathname: "/", + route: "/", + asPath: "/", + params: {}, + query: {}, + isReady: true, + isLocaleDomain: false, + isPreview: false, + push: jest.fn(), + replace: jest.fn(), + reload: jest.fn(), + back: jest.fn(), + prefetch: jest.fn(), + beforePopState: jest.fn(), + events: { + on: jest.fn(), + off: jest.fn(), + emit: jest.fn(), + }, + isFallback: false, +} + +type DefaultParams = Parameters +type RenderUI = DefaultParams[0] +type RenderOptions = DefaultParams[1] & { router?: Partial; dehydratedState?: unknown } + +type DefaultHookParams = Parameters +type RenderHook = DefaultHookParams[0] +type RenderHookOptions = DefaultHookParams[1] & { + router?: Partial + dehydratedState?: unknown +} diff --git a/examples/cypress/tsconfig.json b/examples/cypress/tsconfig.json new file mode 100644 index 0000000000..670aedfca5 --- /dev/null +++ b/examples/cypress/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["jest", "@testing-library/react", "@testing-library/jest-dom"], + "baseUrl": "./", + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "strictNullChecks": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "noUncheckedIndexedAccess": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "tsBuildInfoFile": ".tsbuildinfo" + }, + "exclude": ["node_modules", "**/*.e2e.ts", "cypress"], + "include": ["blitz-env.d.ts", "**/*.ts", "**/*.tsx"] +} diff --git a/examples/cypress/types.ts b/examples/cypress/types.ts new file mode 100644 index 0000000000..82209331c2 --- /dev/null +++ b/examples/cypress/types.ts @@ -0,0 +1,18 @@ +import { DefaultCtx, SessionContext, SimpleRolesIsAuthorized } from "blitz" +import { User } from "db" + +// Note: You should switch to Postgres and then use a DB enum for role type +export type Role = "ADMIN" | "USER" + +declare module "blitz" { + export interface Ctx extends DefaultCtx { + session: SessionContext + } + export interface Session { + isAuthorized: SimpleRolesIsAuthorized + PublicData: { + userId: User["id"] + role: Role + } + } +} diff --git a/yarn.lock b/yarn.lock index 1f62215ba0..f62d90295c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1315,6 +1315,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.7": version "7.13.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.7.tgz#d494e39d198ee9ca04f4dcb76d25d9d7a1dc961a" @@ -1512,6 +1519,31 @@ tunnel-agent "^0.6.0" uuid "^3.3.2" +"@cypress/request@^2.88.6": + version "2.88.6" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.6.tgz#a970dd675befc6bdf8a8921576c01f51cc5798e9" + integrity sha512-z0UxBE/+qaESAHY9p9sM2h8Y4XqtsbDCt0/DPOrqA/RZgKi4PkxdpXyK4wCCnSk1xHqWHZZAE+gV6aDAR6+caQ== + 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" + har-validator "~5.1.3" + http-signature "~1.2.0" + 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/skip-test@2.6.0": version "2.6.0" resolved "https://registry.yarnpkg.com/@cypress/skip-test/-/skip-test-2.6.0.tgz#73fc58477b436638cfdfaa212b5378ff81b9b3ee" @@ -1578,6 +1610,21 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@firebase/analytics-types@0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.3.1.tgz#3c5f5d71129c88295e17e914e34b391ffda1723c" @@ -1851,6 +1898,20 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + "@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" @@ -2102,6 +2163,17 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" +"@jest/types@^27.2.5": + version "27.2.5" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.5.tgz#420765c052605e75686982d24b061b4cbba22132" + integrity sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + "@juanm04/cpx@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@juanm04/cpx/-/cpx-2.0.0.tgz#e7f8a0748fda740cb980dfeb4f8ef3e96bfc88bc" @@ -3514,6 +3586,13 @@ dependencies: "@prisma/engines-version" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" +"@prisma/client@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.2.1.tgz#b0c60b4c42ec5b701a271380780c70de55bc3311" + integrity sha512-nakt9YoDFD4cgTkRTSVbzG40AKmmbVEtXE3csVqBdDXsm0/L4PQdtbtzILNzq28xv8HH8oHgFKWnsItM23mSDw== + dependencies: + "@prisma/engines-version" "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" + "@prisma/debug@2.19.0": version "2.19.0" resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-2.19.0.tgz#5d9c6b9deb0d5214360ee23644c4e40bee0f251d" @@ -3545,6 +3624,11 @@ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#2c5813ef98bcbe659b18b521f002f5c8aabbaae2" integrity sha512-60Do+ByVfHnhJ2id5h/lXOZnDQNIf5pz3enkKWOmyr744Z2IxkBu65jRckFfMN5cPtmXDre/Ay/GKm0aoeLwrw== +"@prisma/engines-version@3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c": + version "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c.tgz#63032b40ee09f56ec423eb47617c26de125ffb1e" + integrity sha512-O4dHSbqfX7yAjFMawIEzv6wefv3LRMDK4J20Y70NvE3otbE3CnChlmghkCvMsQ1CGF1QuGlrqfw20aI2JfZcaw== + "@prisma/engines@2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d": version "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d" resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d.tgz#db2809a6f7f18584e3ca89b1f5bad884155629ec" @@ -3555,6 +3639,11 @@ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#7e542d510f0c03f41b73edbb17254f5a0b272a4d" integrity sha512-29/xO9kqeQka+wN5Ev10l5L4XQXNVXdPToJs1M29VZ2imQsNsL4rtz26m3qGM54IoGWwwfTVdvuVRxKnDl2rig== +"@prisma/engines@3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c": + version "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c.tgz#d2a41a76a89548ea411043c3198eee4a75475dbb" + integrity sha512-wnHODKLQGKkE2ZCHxHQEf/4Anq/EP0ZCvX++D5w34033mwZ94iZiOsEKZZ31fgki7MTh28F1VNF5ZSFdnRjgvQ== + "@prisma/fetch-engine@2.19.0": version "2.19.0" resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-2.19.0.tgz#a01ebfc184ab09cc0ed9d6d4ef966c3846fb5332" @@ -3773,6 +3862,14 @@ dependencies: any-observable "^0.3.0" +"@selderee/plugin-htmlparser2@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz#27e994afd1c2cb647ceb5406a185a5574188069d" + integrity sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA== + dependencies: + domhandler "^4.2.0" + selderee "^0.6.0" + "@sideway/address@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.0.tgz#0b301ada10ac4e0e3fa525c90615e0b61a72b78d" @@ -4066,6 +4163,28 @@ dependencies: chokidar "^1.7.0" +"@testing-library/cypress@8.0.1": + version "8.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/cypress/-/cypress-8.0.1.tgz#46bf2ae45ba8599a5f77b1dc2386444564cad3ce" + integrity sha512-JCGy8u4ia+OQQJxVqKqjqpb0i4kmHQhu5jKFVBp+v/YSICnf52fil2MWIJqO+tH9ZQXtp3aF7uKHsVipWaI0BQ== + dependencies: + "@babel/runtime" "^7.14.6" + "@testing-library/dom" "^8.1.0" + +"@testing-library/dom@^7.11.0": + version "7.31.2" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" + integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^4.2.2" + chalk "^4.1.0" + dom-accessibility-api "^0.5.6" + lz-string "^1.4.4" + pretty-format "^26.6.2" + "@testing-library/dom@^7.28.1": version "7.29.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.29.0.tgz#60b18065bab50a5cde21fe80275a47a43024d9cc" @@ -4080,6 +4199,20 @@ lz-string "^1.4.4" pretty-format "^26.6.2" +"@testing-library/dom@^8.1.0": + version "8.7.2" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.7.2.tgz#234315c6971be380fc9cbf0b031ada3e9f0bfe09" + integrity sha512-2zN0Zv9dMnaMAd4c/1E1ZChu4QrICyvWtkUvHFQBPhS1oG3VYGcM7SLGLYdda7187ILRXzIUOvOsbXQm4EASjA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^4.2.2" + chalk "^4.1.0" + dom-accessibility-api "^0.5.6" + lz-string "^1.4.4" + pretty-format "^27.0.2" + "@testing-library/jest-dom@5.11.9": version "5.11.9" resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.9.tgz#e6b3cd687021f89f261bd53cbe367041fbd3e975" @@ -4685,6 +4818,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.47.tgz#6eca42c3462821309b26edbc2eff0db1e37ab9bc" integrity sha512-R6851wTjN1YJza8ZIeX6puNBSi/ZULHVh4WVleA7q256l+cP2EtXnKbO455fTs2ytQk3dL9qkU+Wh8l/uROdKg== +"@types/node@^14.14.31": + version "14.17.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.21.tgz#6359d8cf73481e312a43886fa50afc70ce5592c6" + integrity sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA== + "@types/nodemailer@*": version "6.4.0" resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.0.tgz#d8c039be3ed685c4719a026455555be82c124b74" @@ -4801,6 +4939,13 @@ dependencies: "@types/nodemailer" "*" +"@types/preview-email@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/preview-email/-/preview-email-2.0.1.tgz#df5d1ce7a3034e3dfcd2b38a66c1f60401b27dc8" + integrity sha512-wHtm/Xxlxk2WKRok0ya7iUr+UvQFlq9nTIQXZi2d3n2tdrt+n4Fkybvi45BjQILl2wfGFq4y+jARCo2+ZoaTrA== + dependencies: + "@types/nodemailer" "*" + "@types/progress@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/progress/-/progress-2.0.3.tgz#7ccbd9c6d4d601319126c469e73b5bb90dfc8ccc" @@ -4880,6 +5025,15 @@ "@types/prop-types" "*" csstype "^3.0.2" +"@types/react@17.0.28": + version "17.0.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.28.tgz#b40967e79af8f32182c6f6f2655ba196bafc7202" + integrity sha512-6OmflHgk2DlnsFi49kBW3/Dql1GT32bYSk+A6tFBDAt0T0bxotBdQwXkm77lVlczHwY6+Wu6IfpsGqArjOYtaA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/readable-stream@2.3.9": version "2.3.9" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.9.tgz#40a8349e6ace3afd2dd1b6d8e9b02945de4566a9" @@ -4922,6 +5076,11 @@ "@types/glob" "*" "@types/node" "*" +"@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/secure-password@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/secure-password/-/secure-password-3.1.0.tgz#3070abfbfa63c43d0f50e2fbe59dd24dce913968" @@ -4981,6 +5140,11 @@ resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.2.tgz#3a84cf5ec3249439015e14049bd3161419bf9eae" integrity sha512-dIPoZ3g5gcx9zZEszaxLSVTvMReD3xxyyDnQUjA6IYDG9Ba2AV0otMPs+77sG9ojB4Qr2N2Vk5RnKeuA0X/0bg== +"@types/sinonjs__fake-timers@^6.0.2": + version "6.0.4" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d" + integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== + "@types/sizzle@^2.3.2": version "2.3.2" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.2.tgz#a811b8c18e2babab7d542b3365887ae2e4d9de47" @@ -5023,6 +5187,14 @@ dependencies: "@types/node" "*" +"@types/testing-library__cypress@5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@types/testing-library__cypress/-/testing-library__cypress-5.0.9.tgz#c65f2be0cbb7f11556c1a35fd767d8dd6d1dff23" + integrity sha512-WTiIZhZKWDnV+Tgo42pxff8YfHdmaNFQz/bFoTlmfw2vbXcstCcb39VRaRi5yFHj/lb7t3K47btKYEbR1fci+Q== + dependencies: + "@testing-library/dom" "^7.11.0" + cypress "*" + "@types/testing-library__jest-dom@^5.9.1": version "5.9.5" resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0" @@ -5122,6 +5294,13 @@ 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/yoga-layout@1.9.2": version "1.9.2" resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" @@ -5706,6 +5885,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.6.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" + integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + alex@9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/alex/-/alex-9.1.0.tgz#bc54b305c3e05d87085cca3af4376314be0dbeee" @@ -5798,6 +5987,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +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-split@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ansi-split/-/ansi-split-1.0.1.tgz#3cab03754ab6f1d64d4ad13cd10f22fc36db4a45" @@ -5873,7 +6067,7 @@ aproba@^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== -arch@^2.1.2: +arch@^2.1.2, arch@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== @@ -6637,7 +6831,7 @@ bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" -blob-util@2.0.2: +blob-util@2.0.2, blob-util@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb" integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== @@ -7265,6 +7459,11 @@ chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" +chance@1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.8.tgz#5d6c2b78c9170bf6eb9df7acdda04363085be909" + integrity sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg== + change-case@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" @@ -7887,6 +8086,11 @@ colorette@^1.2.2: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== +colorette@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -8943,6 +9147,54 @@ cypress@6.2.1: url "^0.11.0" yauzl "^2.10.0" +cypress@8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.6.0.tgz#8d02fa58878b37cfc45bbfce393aa974fa8a8e22" + integrity sha512-F7qEK/6Go5FsqTueR+0wEw2vOVKNgk5847Mys8vsWkzPoEKdxs+7N9Y1dit+zhaZCLtMPyrMwjfA53ZFy+lSww== + dependencies: + "@cypress/request" "^2.88.6" + "@cypress/xvfb" "^1.2.4" + "@types/node" "^14.14.31" + "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sizzle" "^2.3.2" + arch "^2.2.0" + blob-util "^2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-cursor "^3.1.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + dayjs "^1.10.4" + debug "^4.3.2" + enquirer "^2.3.6" + eventemitter2 "^6.4.3" + 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.5" + ospath "^1.2.2" + pretty-bytes "^5.6.0" + proxy-from-env "1.0.0" + ramda "~0.27.1" + request-progress "^3.0.0" + supports-color "^8.1.1" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -9002,6 +9254,11 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== +dayjs@^1.10.4, dayjs@^1.10.6: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + dayjs@^1.9.6: version "1.10.4" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2" @@ -9033,7 +9290,7 @@ debug@4, debug@4.3.1, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, de dependencies: ms "2.1.2" -debug@4.3.2: +debug@4.3.2, debug@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== @@ -9333,6 +9590,11 @@ directory-tree@2.2.5: resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.2.5.tgz#43d167eeb87b57640cc832f794f88431ebd47300" integrity sha512-qmeuql8N7hQB5b+cnlvbcHSjKBNpRjLY5KcvyFd9CTC5uTN7sJshEQ/ExZidAcEUEYcC/76i8ikLtbBMG81YRg== +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -9357,6 +9619,11 @@ dom-accessibility-api@^0.5.4: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz#b06d059cdd4a4ad9a79275f9d414a5c126241166" integrity sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ== +dom-accessibility-api@^0.5.6: + version "0.5.8" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.8.tgz#414813012e065b5dfa8998b990460c0af12a5421" + integrity sha512-rAfghuBPeJldxqsmZQtBbna4TqMgFe4xhYs24vPULNslbmXUdcga+CXiKWzZxyWw0FCkGKPgmizIysIvsAEN8w== + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -9416,6 +9683,11 @@ domelementtype@^2.0.1, domelementtype@^2.1.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== +domelementtype@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" + integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -9444,6 +9716,13 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" +domhandler@^4.2.0: + version "4.2.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" + integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + dependencies: + domelementtype "^2.2.0" + domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -9487,6 +9766,15 @@ domutils@^2.4.2: domelementtype "^2.0.1" domhandler "^4.0.0" +domutils@^2.5.2: + 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" + dot-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" @@ -10164,6 +10452,52 @@ eslint@7.21.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" +eslint@7.32.0: + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== + dependencies: + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + escape-string-regexp "^4.0.0" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.1" + 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 "^5.1.2" + globals "^13.6.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^6.0.9" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + eslint@^7.3.0: version "7.17.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0" @@ -10342,6 +10676,11 @@ eventemitter2@^6.4.2: resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz#35c563619b13f3681e7eb05cbdaf50f56ba58820" integrity sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ== +eventemitter2@^6.4.3: + version "6.4.5" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655" + integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -10391,6 +10730,36 @@ execa@3.4.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@4.1.0, execa@^4.0.0, execa@^4.0.2, 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@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" + execa@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" @@ -10417,21 +10786,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0, execa@^4.0.2, 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@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" @@ -10645,6 +10999,17 @@ extract-stack@^2.0.0: resolved "https://registry.yarnpkg.com/extract-stack/-/extract-stack-2.0.0.tgz#11367bc865bfcd9bc0db3123e5edb57786f11f9b" integrity sha512-AEo4zm+TenK7zQorGK1f9mJ8L14hnTDi2ZQPR+Mub1NX8zimka1mXpV5LpH8x9HoUmFSHZCfLHqWvp0Y4FxxzQ== +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" + extract-zip@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" @@ -10699,7 +11064,7 @@ fast-deep-equal@^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.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== @@ -10932,6 +11297,13 @@ final-form@4.20.1: dependencies: "@babel/runtime" "^7.10.0" +final-form@4.20.4: + version "4.20.4" + resolved "https://registry.yarnpkg.com/final-form/-/final-form-4.20.4.tgz#8d59e36d3248a227265cc731d76c0564dd2606f6" + integrity sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw== + dependencies: + "@babel/runtime" "^7.10.0" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -11696,7 +12068,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob-parent@^5.1.1: +glob-parent@^5.1.1, 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== @@ -11821,6 +12193,13 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" +globals@^13.6.0, globals@^13.9.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" + integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== + dependencies: + type-fest "^0.20.2" + globalthis@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" @@ -12349,6 +12728,18 @@ html-to-text@6.0.0: lodash "^4.17.20" minimist "^1.2.5" +html-to-text@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-8.0.0.tgz#5848681a5a38d657a7bb58cf5006d1c29fe64ce3" + integrity sha512-fEtul1OerF2aMEV+Wpy+Ue20tug134jOY1GIudtdqZi7D0uTudB2tVJBKfVhTL03dtqeJoF8gk8EPX9SyMEvLg== + dependencies: + "@selderee/plugin-htmlparser2" "^0.6.0" + deepmerge "^4.2.2" + he "^1.2.0" + htmlparser2 "^6.1.0" + minimist "^1.2.5" + selderee "^0.6.0" + htmlescape@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" @@ -12386,6 +12777,16 @@ htmlparser2@^4.1.0: domutils "^2.0.0" entities "^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" + http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -12516,6 +12917,11 @@ husky@5.1.2: resolved "https://registry.yarnpkg.com/husky/-/husky-5.1.2.tgz#dc6a1f68640455d8d98c28875e073087f86c5081" integrity sha512-lilaRYeDXcAOj8DuRnN9IxUyEMVbYg9rK7yVNkPB5V4hCvxIUxpMeiv9K2h77CE0HzjCnk1Br0oWe1IghXngDQ== +husky@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + hyperlinker@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" @@ -12547,6 +12953,13 @@ iconv-lite@0.6.2, iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +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" @@ -13208,6 +13621,14 @@ is-installed-globally@^0.3.1, is-installed-globally@^0.3.2: global-dirs "^2.0.1" is-path-inside "^3.0.1" +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-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -14203,6 +14624,17 @@ joi@^17.3.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" +joi@^17.4.0: + version "17.4.2" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7" + integrity sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.0" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + js-base64@^2.1.8, js-base64@^2.1.9: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -14333,6 +14765,11 @@ json-schema-traverse@^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-typed@^7.0.0: version "7.0.3" resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" @@ -14771,6 +15208,13 @@ linkify-it@3.0.2: dependencies: uc.micro "^1.0.1" +linkify-it@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" + integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== + dependencies: + uc.micro "^1.0.1" + lint-staged@10.5.4: version "10.5.4" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665" @@ -14835,6 +15279,19 @@ listr2@^3.2.2: rxjs "^6.6.3" through "^2.3.8" +listr2@^3.8.3: + version "3.12.2" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.12.2.tgz#2d55cc627111603ad4768a9e87c9c7bb9b49997e" + integrity sha512-64xC2CJ/As/xgVI3wbhlPWVPx0wfTqbUAkpb7bjDi0thSWMqrf07UFhrfsGoo8YSXmF049Rp9C0cjLC8rZxK9A== + dependencies: + cli-truncate "^2.1.0" + colorette "^1.4.0" + log-update "^4.0.0" + p-map "^4.0.0" + rxjs "^6.6.7" + through "^2.3.8" + wrap-ansi "^7.0.0" + listr@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" @@ -14971,6 +15428,11 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= +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.curry@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" @@ -15061,7 +15523,7 @@ lodash.memoize@^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.4.0: +lodash.merge@^4.4.0, 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== @@ -15131,6 +15593,11 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -15326,6 +15793,21 @@ mailparser@^3.0.1: nodemailer "6.4.16" tlds "1.214.0" +mailparser@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.4.0.tgz#249869bc5a41af9e0eabbf005197789442fbac9e" + integrity sha512-u2pfpLg+xr7m2FKDl+ohQhy2gMok1QZ+S9E5umS9ez5DSJWttrqSmBGswyj9F68pZMVTwbhLpBt7Kd04q/W4Vw== + dependencies: + encoding-japanese "1.0.30" + he "1.2.0" + html-to-text "8.0.0" + iconv-lite "0.6.3" + libmime "5.0.0" + linkify-it "3.0.3" + mailsplit "5.3.1" + nodemailer "6.7.0" + tlds "1.224.0" + mailsplit@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.0.0.tgz#0924c89142deadb75ef3825860181e436d7557a1" @@ -15335,6 +15817,15 @@ mailsplit@5.0.0: libmime "4.2.1" libqp "1.1.0" +mailsplit@5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.3.1.tgz#dd6d5c20a7b8a767fe9c9649dfcb26ee04f84c36" + integrity sha512-o6R6HCzqWYmI2/IYlB+v2IMPgYqC2EynmagZQICAhR7zAq0CO6fPcsO6CrYmVuYT+SSwvLAEZR5WniohBELcAA== + dependencies: + libbase64 "1.2.1" + libmime "5.0.0" + libqp "1.1.0" + make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -16129,6 +16620,11 @@ moment@^2.22.1, moment@^2.24.0, moment@^2.27.0: resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== +moo@^0.5.0, moo@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" + integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -16284,6 +16780,16 @@ ncp@1.0.x: resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= +nearley@^2.20.1: + version "2.20.1" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" + integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== + dependencies: + commander "^2.19.0" + moo "^0.5.0" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + needle@^2.2.1: version "2.5.2" resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.2.tgz#cf1a8fce382b5a280108bba90a14993c00e4010a" @@ -16586,6 +17092,11 @@ nodemailer@6.4.16: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.16.tgz#5cb6391b1d79ab7eff32d6f9f48366b5a7117293" integrity sha512-68K0LgZ6hmZ7PVmwL78gzNdjpj5viqBdFqKrTtr9bZbJYj6BRj5W6WGkxXrEnUl3Co3CBXi3CZBUlpV/foGnOQ== +nodemailer@6.7.0, nodemailer@^6.6.3: + version "6.7.0" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.0.tgz#86614722c4e0c33d1b5b02aecb90d6d629932b0d" + integrity sha512-AtiTVUFHLiiDnMQ43zi0YgkzHOEWUkhDgPlBXrsDzJiJvB29Alo4OKxHQ0ugF3gRqRQIneCLtZU3yiUo7pItZw== + nodemailer@^6.4.16: version "6.4.17" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.17.tgz#8de98618028953b80680775770f937243a7d7877" @@ -17071,6 +17582,14 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +open@7: + 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@^7.3.0: version "7.4.0" resolved "https://registry.yarnpkg.com/open/-/open-7.4.0.tgz#ad95b98f871d9acb0ec8fecc557082cc9986626b" @@ -17611,6 +18130,14 @@ parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parseley@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.7.0.tgz#9949e3a0ed05c5072adb04f013c2810cf49171a8" + integrity sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw== + dependencies: + moo "^0.5.1" + nearley "^2.20.1" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -18907,6 +19434,11 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" +prettier-plugin-prisma@0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/prettier-plugin-prisma/-/prettier-plugin-prisma-0.15.0.tgz#c3afd7b011e8a247ec55662ac506b4dd092fc13b" + integrity sha512-BMXs0Eaq0v0fHxSvGxjRAegn6Xq+QqfApb4lFEUfw1Bi1f2py6CP1a2hCgWrzLx7G9qI3lUSNJ8yy/bX0Cju/w== + prettier-plugin-prisma@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/prettier-plugin-prisma/-/prettier-plugin-prisma-0.4.0.tgz#3ee7d276f2ce15f3b3dfb3e3830e5fe985d5ac62" @@ -18917,12 +19449,17 @@ prettier@2.2.1, prettier@^2.0.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== + pretty-bytes@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" integrity sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg== -pretty-bytes@^5.3.0: +pretty-bytes@^5.3.0, 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== @@ -18952,6 +19489,16 @@ pretty-format@^27.0.0-next.8: ansi-styles "^5.0.0" react-is "^17.0.1" +pretty-format@^27.0.2: + version "27.2.5" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.2.5.tgz#7cfe2a8e8f01a5b5b29296a0b70f4140df0830c5" + integrity sha512-+nYn2z9GgicO9JiqmY25Xtq8SYfZ/5VCpEU3pppHHNAhd1y+ZXxmNPd1evmNcAd6Hz4iBV2kf0UpGth5A/VJ7g== + dependencies: + "@jest/types" "^27.2.5" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -18976,6 +19523,18 @@ pretty-quick@3.1.0: mri "^1.1.5" multimatch "^4.0.0" +pretty-quick@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.1.tgz#93ca4e2dd38cc4e970e3f54a0ead317a25454688" + integrity sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ== + dependencies: + chalk "^3.0.0" + execa "^4.0.0" + find-up "^4.1.0" + ignore "^5.1.4" + mri "^1.1.5" + multimatch "^4.0.0" + prettysize@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-2.0.0.tgz#902c02480d865d9cc0813011c9feb4fa02ce6996" @@ -18994,6 +19553,19 @@ preview-email@3.0.3: pug "^3.0.0" uuid "^8.3.1" +preview-email@3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-3.0.5.tgz#09c32ba43c450ead16b309d9e5cb10f90ff45a95" + integrity sha512-q37jdkVw+wic0o/7xYhOTBS4kF0WX3two0OepmR1Fhxp9NTpO3rJTccAjQm95gJx/2Wa/Nv98sr9pXIQ77/foA== + dependencies: + dayjs "^1.10.6" + debug "^4.3.2" + mailparser "^3.3.0" + nodemailer "^6.6.3" + open "7" + pug "^3.0.2" + uuid "^8.3.2" + prisma@2.24.1: version "2.24.1" resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.24.1.tgz#f8f4cb8baf407a71800256160277f69603bd43a3" @@ -19001,6 +19573,13 @@ prisma@2.24.1: dependencies: "@prisma/engines" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" +prisma@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.2.1.tgz#696871f6d0e374df2de96297df4c04756ee6e3b5" + integrity sha512-nXhldcFYemNSMqdTAEziggVWBNbCHTrr0amkCJruP3G2AFpzxrKtksPRLYNetxdKMJAt7aRRumusbtmTTDgyzw== + dependencies: + "@prisma/engines" "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" + private@^0.1.8, private@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -19147,6 +19726,11 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" 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= + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -19221,6 +19805,20 @@ pug-code-gen@^3.0.0: void-elements "^3.1.0" with "^7.0.0" +pug-code-gen@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.2.tgz#ad190f4943133bf186b60b80de483100e132e2ce" + integrity sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg== + dependencies: + constantinople "^4.0.1" + doctypes "^1.1.0" + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.0.0" + pug-runtime "^3.0.0" + void-elements "^3.1.0" + with "^7.0.0" + pug-error@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.0.0.tgz#5c62173cb09c34de2a2ce04f17b8adfec74d8ca5" @@ -19246,6 +19844,15 @@ pug-lexer@^5.0.0: is-expression "^4.0.0" pug-error "^2.0.0" +pug-lexer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== + dependencies: + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" + pug-linker@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" @@ -19275,6 +19882,11 @@ pug-runtime@^3.0.0: resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.0.tgz#d523025fdc0a1efe70929d1fd3a2d24121ffffb6" integrity sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA== +pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + pug-strip-comments@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" @@ -19301,6 +19913,20 @@ pug@^3.0.0: pug-runtime "^3.0.0" pug-strip-comments "^2.0.0" +pug@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535" + integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== + dependencies: + pug-code-gen "^3.0.2" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" + pump-chain@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pump-chain/-/pump-chain-1.0.0.tgz#7d57d8d9ad8181ea808f5413c4f2bc1e786a5e37" @@ -19464,11 +20090,29 @@ quotation@^1.0.0: resolved "https://registry.yarnpkg.com/quotation/-/quotation-1.1.3.tgz#2a4d11f70105ad398b577883f310469367f53351" integrity sha512-45gUgmX/RtQOQV1kwM06boP49OYXcKCPrYwdmAvs5YqkpiobhNKKwo524JM6Ma0ko3oN9tXNcWs9+ABq3Ry7YA== +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= + ramda@~0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ== +ramda@~0.27.1: + version "0.27.1" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" + integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + random-string@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/random-string/-/random-string-0.2.0.tgz#a46e4375352beda9a0d7b0d19ed6d321ecd1d82d" @@ -19567,6 +20211,15 @@ react-dom@0.0.0-experimental-6a589ad71: object-assign "^4.1.1" scheduler "0.0.0-experimental-6a589ad71" +react-dom@alpha: + version "18.0.0-alpha-55d75005b-20211011" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-alpha-55d75005b-20211011.tgz#d2b54296412d031aac0026d78184d4c3a8c0f6a9" + integrity sha512-bBFdYWr6PMvHlPtmm+OTL/U7XH7gIPS4+AHJwMmefemXZLM0j+2yLltSsvRF4KVcYs4qEDFptClOMtZNK1ffUg== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "0.21.0-alpha-55d75005b-20211011" + react-final-form@6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-6.5.2.tgz#d04d1eb7d92eabc6f6c35206bb0eebfc4bfd924b" @@ -19574,6 +20227,13 @@ react-final-form@6.5.2: dependencies: "@babel/runtime" "^7.12.1" +react-final-form@6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-6.5.7.tgz#0c1098accf0f0011adee5a46076ed1b99ed1b1ea" + integrity sha512-o7tvJXB+McGiXOILqIC8lnOcX4aLhIBiF/Xi9Qet35b7XOS8R7KL8HLRKTfnZWQJm6MCE15v1U0SFive0NcxyA== + dependencies: + "@babel/runtime" "^7.15.4" + react-is@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -19646,6 +20306,14 @@ react@0.0.0-experimental-6a589ad71: loose-envify "^1.1.0" object-assign "^4.1.1" +react@alpha: + version "18.0.0-alpha-55d75005b-20211011" + resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-alpha-55d75005b-20211011.tgz#1e2d921e896c935023f3b851d33205733b6f81ff" + integrity sha512-I7diwDx/kfsJsw+H380fCpqhLsCLM40M4M+Yl6o4SL1bDTW1a27ptbLE9VPX3Nbw8zipBJIGY7vaDknQxj1p0w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + read-cmd-shim@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" @@ -20278,6 +20946,11 @@ require-from-string@^1.1.0: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= +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-like@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" @@ -20680,6 +21353,20 @@ rxjs@^6.6.0: dependencies: tslib "^1.9.0" +rxjs@^6.6.7: + 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.1.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" + integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== + dependencies: + tslib "~2.1.0" + rxjs@^7.2.0: version "7.3.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" @@ -20778,6 +21465,14 @@ scheduler@0.0.0-experimental-6a589ad71: loose-envify "^1.1.0" object-assign "^4.1.1" +scheduler@0.21.0-alpha-55d75005b-20211011: + version "0.21.0-alpha-55d75005b-20211011" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-55d75005b-20211011.tgz#19db268580f41210122fe46f1a0d981bf365cd83" + integrity sha512-r9aUxDElhZxsh3W72EGlvrcJepN0TKC1WIMNGxJuhfQchNzs0OHEos72W7V5L+Xug7VkhyMZ/zPR2I0IMQ2ruw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler@0.21.0-alpha-bd255700d-20210816: version "0.21.0-alpha-bd255700d-20210816" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-bd255700d-20210816.tgz#ed764c0e6eb3f35b5a677c49eba9852c6bd25dd4" @@ -20850,6 +21545,13 @@ seedrandom@3.0.5: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== +selderee@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.6.0.tgz#f3bee66cfebcb6f33df98e4a1df77388b42a96f7" + integrity sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg== + dependencies: + parseley "^0.7.0" + selenium-standalone@6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.18.0.tgz#011e0672b1b86893f77244a86ddea1b6baadfb87" @@ -21543,6 +22245,19 @@ start-server-and-test@1.11.7: ps-tree "1.2.0" wait-on "5.2.1" +start-server-and-test@1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3" + integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw== + dependencies: + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.2" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "6.0.0" + state-toggle@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" @@ -21718,6 +22433,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.matchall@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" @@ -21838,6 +22562,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.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-buf@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572" @@ -22041,7 +22772,7 @@ supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.0: +supports-color@^8.0.0, supports-color@^8.1.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== @@ -22130,6 +22861,18 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" +table@^6.0.9: + version "6.7.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" + integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== + dependencies: + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tagged-versions@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/tagged-versions/-/tagged-versions-1.3.0.tgz#fd3cca176859817b95b1f5d311a12c9c08c8bdc4" @@ -22503,6 +23246,11 @@ tlds@1.214.0: resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.214.0.tgz#a20191443eec26fd3339a3bd98e87a0b4f3f0d89" integrity sha512-+i48KYsrCkkIZnsj31cTIj9cu5NtFxKo7xlNIB7jg8kXi//b4Ertl5qaHgqFF+y+g0nFwt/k+eph2uUNQJgfwg== +tlds@1.224.0: + version "1.224.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.224.0.tgz#dc9a5b0bda0708af0302114f6e24458770c5af01" + integrity sha512-Jgdc8SEijbDFUsmCn6Wk/f7E6jBLFZOG3U1xK0amGSfEH55Xx97ItUS/d2NngsuApjn11UeWCWj8Um3VRhseZQ== + tmp@0.0.30: version "0.0.30" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" @@ -22879,6 +23627,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +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.2: version "0.21.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.2.tgz#43b9dc71d9dc5593ea71bf7b0e013ec10f838249" @@ -22939,6 +23692,11 @@ typescript@4.4.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86" integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ== +typescript@~4.3: + version "4.3.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + ua-parser-js@^0.7.22: version "0.7.23" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.23.tgz#704d67f951e13195fbcd3d78818577f5bc1d547b" @@ -23550,7 +24308,7 @@ utils-merge@1.0.1, utils-merge@1.x.x: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= -uuid@8.3.2, uuid@^8.3.1: +uuid@8.3.2, uuid@^8.3.1, 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== @@ -23747,6 +24505,17 @@ wait-on@5.2.1: minimist "^1.2.5" rxjs "^6.6.3" +wait-on@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7" + integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== + dependencies: + axios "^0.21.1" + joi "^17.4.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.1.0" + wait-port@0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-0.2.2.tgz#d51a491e484a17bf75a947e711a2f012b4e6f2e3" @@ -24397,6 +25166,11 @@ zod@3.8.1: resolved "https://registry.yarnpkg.com/zod/-/zod-3.8.1.tgz#b1173c3b4ac2a9e06d302ff580e3b41902766b9f" integrity sha512-u4Uodl7dLh8nXZwqXL1SM5FAl5b4lXYHOxMUVb9lqhlEAZhA2znX+0oW480m0emGFMxpoRHzUncAqRkc4h8ZJA== +zod@3.9.8: + version "3.9.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.9.8.tgz#562eda33925203542dea70100ce0d3766d04b0ab" + integrity sha512-pTNhdJd45PPOBpdxO8x00Tv+HhknYGx3WdgFQndazp+G1Gd2Cxf81L5yMfCIIcd/SA3VqrcTv/G4bFcr+DsZhA== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" From fa21839e5acff6ad8ca00119be242a64c82689c1 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Tue, 12 Oct 2021 16:11:12 +0200 Subject: [PATCH 2/9] change name --- examples/cypress/package.json | 7 ++-- yarn.lock | 62 ----------------------------------- 2 files changed, 3 insertions(+), 66 deletions(-) diff --git a/examples/cypress/package.json b/examples/cypress/package.json index 42496be2ad..b39d613b18 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -1,6 +1,6 @@ { - "name": "cypress", - "version": "1.0.0", + "name": "@examples/cypress", + "version": "0.41.0", "scripts": { "dev": "blitz dev", "build": "blitz build", @@ -55,8 +55,7 @@ "pretty-quick": "3.1.1", "preview-email": "3.0.5", "prisma": "3.2.1", - "start-server-and-test": "1.14.0", - "typescript": "~4.3" + "start-server-and-test": "1.11.7" }, "private": true } diff --git a/yarn.lock b/yarn.lock index f62d90295c..bbab66c34a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10745,21 +10745,6 @@ execa@4.1.0, execa@^4.0.0, execa@^4.0.2, execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.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" - execa@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" @@ -14624,17 +14609,6 @@ joi@^17.3.0: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -joi@^17.4.0: - version "17.4.2" - resolved "https://registry.yarnpkg.com/joi/-/joi-17.4.2.tgz#02f4eb5cf88e515e614830239379dcbbe28ce7f7" - integrity sha512-Lm56PP+n0+Z2A2rfRvsfWVDXGEWjXxatPopkQ8qQ5mxCEhwHG+Ettgg5o98FFaxilOxozoa14cFhrE/hOzh/Nw== - dependencies: - "@hapi/hoek" "^9.0.0" - "@hapi/topo" "^5.0.0" - "@sideway/address" "^4.1.0" - "@sideway/formula" "^3.0.0" - "@sideway/pinpoint" "^2.0.0" - js-base64@^2.1.8, js-base64@^2.1.9: version "2.6.4" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" @@ -21360,13 +21334,6 @@ rxjs@^6.6.7: dependencies: tslib "^1.9.0" -rxjs@^7.1.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" - integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== - dependencies: - tslib "~2.1.0" - rxjs@^7.2.0: version "7.3.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6" @@ -22245,19 +22212,6 @@ start-server-and-test@1.11.7: ps-tree "1.2.0" wait-on "5.2.1" -start-server-and-test@1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.14.0.tgz#c57f04f73eac15dd51733b551d775b40837fdde3" - integrity sha512-on5ELuxO2K0t8EmNj9MtVlFqwBMxfWOhu4U7uZD1xccVpFlOQKR93CSe0u98iQzfNxRyaNTb/CdadbNllplTsw== - dependencies: - bluebird "3.7.2" - check-more-types "2.24.0" - debug "4.3.2" - execa "5.1.1" - lazy-ass "1.6.0" - ps-tree "1.2.0" - wait-on "6.0.0" - state-toggle@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" @@ -23692,11 +23646,6 @@ typescript@4.4.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86" integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ== -typescript@~4.3: - version "4.3.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" - integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== - ua-parser-js@^0.7.22: version "0.7.23" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.23.tgz#704d67f951e13195fbcd3d78818577f5bc1d547b" @@ -24505,17 +24454,6 @@ wait-on@5.2.1: minimist "^1.2.5" rxjs "^6.6.3" -wait-on@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7" - integrity sha512-tnUJr9p5r+bEYXPUdRseolmz5XqJTTj98JgOsfBn7Oz2dxfE2g3zw1jE+Mo8lopM3j3et/Mq1yW7kKX6qw7RVw== - dependencies: - axios "^0.21.1" - joi "^17.4.0" - lodash "^4.17.21" - minimist "^1.2.5" - rxjs "^7.1.0" - wait-port@0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/wait-port/-/wait-port-0.2.2.tgz#d51a491e484a17bf75a947e711a2f012b4e6f2e3" From f3c8b2b6b2fd59a069c0c61e0c8370637ee226f0 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Tue, 12 Oct 2021 16:17:11 +0200 Subject: [PATCH 3/9] remove dependency --- examples/cypress/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/cypress/package.json b/examples/cypress/package.json index b39d613b18..8a271c05c6 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -51,7 +51,6 @@ "husky": "6.0.0", "lint-staged": "10.5.4", "prettier": "2.4.1", - "prettier-plugin-prisma": "0.15.0", "pretty-quick": "3.1.1", "preview-email": "3.0.5", "prisma": "3.2.1", From bd4d829a16fb5a26f32cd36b44ae6fea5c7496b7 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Tue, 12 Oct 2021 21:03:21 +0200 Subject: [PATCH 4/9] back to sqlite --- .../app/auth/mutations/forgotPassword.test.ts | 58 -- .../app/auth/mutations/resetPassword.test.ts | 82 --- examples/cypress/app/pages/index.test.tsx | 25 - examples/cypress/cypress/plugins/index.ts | 35 +- examples/cypress/cypress/support/index.ts | 2 +- .../20211012105814_init/migration.sql | 57 -- .../20211012185114_init/migration.sql | 47 ++ .../cypress/db/migrations/migration_lock.toml | 2 +- examples/cypress/db/schema.prisma | 4 +- examples/cypress/package.json | 36 +- yarn.lock | 546 ++---------------- 11 files changed, 137 insertions(+), 757 deletions(-) delete mode 100644 examples/cypress/app/auth/mutations/forgotPassword.test.ts delete mode 100644 examples/cypress/app/auth/mutations/resetPassword.test.ts delete mode 100644 examples/cypress/app/pages/index.test.tsx delete mode 100644 examples/cypress/db/migrations/20211012105814_init/migration.sql create mode 100644 examples/cypress/db/migrations/20211012185114_init/migration.sql diff --git a/examples/cypress/app/auth/mutations/forgotPassword.test.ts b/examples/cypress/app/auth/mutations/forgotPassword.test.ts deleted file mode 100644 index 20cc73e98e..0000000000 --- a/examples/cypress/app/auth/mutations/forgotPassword.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { hash256, Ctx } from "blitz" -import forgotPassword from "./forgotPassword" -import db from "db" -import previewEmail from "preview-email" - -beforeEach(async () => { - await db.$reset() -}) - -const generatedToken = "plain-token" -jest.mock("blitz", () => ({ - ...jest.requireActual>("blitz")!, - generateToken: () => generatedToken, -})) -jest.mock("preview-email", () => jest.fn()) - -describe("forgotPassword mutation", () => { - it("does not throw error if user doesn't exist", async () => { - await expect(forgotPassword({ email: "no-user@email.com" }, {} as Ctx)).resolves.not.toThrow() - }) - - it("works correctly", async () => { - // Create test user - const user = await db.user.create({ - data: { - email: "user@example.com", - tokens: { - // Create old token to ensure it's deleted - create: { - type: "RESET_PASSWORD", - hashedToken: "token", - expiresAt: new Date(), - sentTo: "user@example.com", - }, - }, - }, - include: { tokens: true }, - }) - - // Invoke the mutation - await forgotPassword({ email: user.email }, {} as Ctx) - - const tokens = await db.token.findMany({ where: { userId: user.id } }) - const token = tokens[0] - if (!user.tokens[0]) throw new Error("Missing user token") - if (!token) throw new Error("Missing token") - - // delete's existing tokens - expect(tokens.length).toBe(1) - - expect(token.id).not.toBe(user.tokens[0].id) - expect(token.type).toBe("RESET_PASSWORD") - expect(token.sentTo).toBe(user.email) - expect(token.hashedToken).toBe(hash256(generatedToken)) - expect(token.expiresAt > new Date()).toBe(true) - expect(previewEmail).toBeCalled() - }) -}) diff --git a/examples/cypress/app/auth/mutations/resetPassword.test.ts b/examples/cypress/app/auth/mutations/resetPassword.test.ts deleted file mode 100644 index d613716f53..0000000000 --- a/examples/cypress/app/auth/mutations/resetPassword.test.ts +++ /dev/null @@ -1,82 +0,0 @@ -import resetPassword from "./resetPassword" -import db from "db" -import { hash256, SecurePassword } from "blitz" - -beforeEach(async () => { - await db.$reset() -}) - -const mockCtx: any = { - session: { - $create: jest.fn, - }, -} - -describe("resetPassword mutation", () => { - it("works correctly", async () => { - expect(true).toBe(true) - - // Create test user - const goodToken = "randomPasswordResetToken" - const expiredToken = "expiredRandomPasswordResetToken" - const future = new Date() - future.setHours(future.getHours() + 4) - const past = new Date() - past.setHours(past.getHours() - 4) - - const user = await db.user.create({ - data: { - email: "user@example.com", - tokens: { - // Create old token to ensure it's deleted - create: [ - { - type: "RESET_PASSWORD", - hashedToken: hash256(expiredToken), - expiresAt: past, - sentTo: "user@example.com", - }, - { - type: "RESET_PASSWORD", - hashedToken: hash256(goodToken), - expiresAt: future, - sentTo: "user@example.com", - }, - ], - }, - }, - include: { tokens: true }, - }) - - const newPassword = "newPassword" - - // Non-existent token - await expect( - resetPassword({ token: "no-token", password: "", passwordConfirmation: "" }, mockCtx) - ).rejects.toThrowError() - - // Expired token - await expect( - resetPassword( - { token: expiredToken, password: newPassword, passwordConfirmation: newPassword }, - mockCtx - ) - ).rejects.toThrowError() - - // Good token - await resetPassword( - { token: goodToken, password: newPassword, passwordConfirmation: newPassword }, - mockCtx - ) - - // Delete's the token - const numberOfTokens = await db.token.count({ where: { userId: user.id } }) - expect(numberOfTokens).toBe(0) - - // Updates user's password - const updatedUser = await db.user.findFirst({ where: { id: user.id } }) - expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe( - SecurePassword.VALID - ) - }) -}) diff --git a/examples/cypress/app/pages/index.test.tsx b/examples/cypress/app/pages/index.test.tsx deleted file mode 100644 index 9374c60249..0000000000 --- a/examples/cypress/app/pages/index.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { render } from "test/utils" - -import Home from "./index" -import { useCurrentUser } from "app/core/hooks/useCurrentUser" - -jest.mock("app/core/hooks/useCurrentUser") -const mockUseCurrentUser = useCurrentUser as jest.MockedFunction - -test.skip("renders blitz documentation link", () => { - // This is an example of how to ensure a specific item is in the document - // But it's disabled by default (by test.skip) so the test doesn't fail - // when you remove the the default content from the page - - // This is an example on how to mock api hooks when testing - mockUseCurrentUser.mockReturnValue({ - id: 1, - name: "User", - email: "user@email.com", - role: "user", - }) - - const { getByText } = render() - const linkElement = getByText(/Documentation/i) - expect(linkElement).toBeInTheDocument() -}) diff --git a/examples/cypress/cypress/plugins/index.ts b/examples/cypress/cypress/plugins/index.ts index 0a8f001237..5e198bd0b8 100644 --- a/examples/cypress/cypress/plugins/index.ts +++ b/examples/cypress/cypress/plugins/index.ts @@ -26,23 +26,24 @@ const pluginConfig: Cypress.PluginConfig = (on, _config) => { } return true }, - "db:clear": async () => { - // Delete all data without dropping tables, so migration isn't required - await db.$executeRaw` - do - $$ - declare - l_stmt text; - begin - select 'truncate ' || string_agg(format('%I.%I', schemaname, tablename), ',') - into l_stmt from pg_tables - where schemaname in ('public'); - execute l_stmt; - end; - $$ - ` - return true - }, + // for Postgres + // "db:clear": () => { + // Delete all data without dropping tables, so migration isn't required + // await db.$executeRaw` + // do + // $$ + // declare + // l_stmt text; + // begin + // select 'truncate ' || string_agg(format('%I.%I', schemaname, tablename), ',') + // into l_stmt from pg_tables + // where schemaname in ('public'); + // execute l_stmt; + // end; + // $$ + // ` + // return true + // }, "db:seed": async () => { await seed() return true diff --git a/examples/cypress/cypress/support/index.ts b/examples/cypress/cypress/support/index.ts index cdc84367ee..98a43a84b5 100644 --- a/examples/cypress/cypress/support/index.ts +++ b/examples/cypress/cypress/support/index.ts @@ -19,6 +19,6 @@ before(() => { }) beforeEach(() => { - cy.task("db:clear") + // you can clear the database here cy.task("db:seed") }) diff --git a/examples/cypress/db/migrations/20211012105814_init/migration.sql b/examples/cypress/db/migrations/20211012105814_init/migration.sql deleted file mode 100644 index 1f4f306120..0000000000 --- a/examples/cypress/db/migrations/20211012105814_init/migration.sql +++ /dev/null @@ -1,57 +0,0 @@ --- CreateTable -CREATE TABLE "User" ( - "id" SERIAL NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "name" TEXT, - "email" TEXT NOT NULL, - "hashedPassword" TEXT, - "role" TEXT NOT NULL DEFAULT E'USER', - - CONSTRAINT "User_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Session" ( - "id" SERIAL NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "expiresAt" TIMESTAMP(3), - "handle" TEXT NOT NULL, - "hashedSessionToken" TEXT, - "antiCSRFToken" TEXT, - "publicData" TEXT, - "privateData" TEXT, - "userId" INTEGER, - - CONSTRAINT "Session_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "Token" ( - "id" SERIAL NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "hashedToken" TEXT NOT NULL, - "type" TEXT NOT NULL, - "expiresAt" TIMESTAMP(3) NOT NULL, - "sentTo" TEXT NOT NULL, - "userId" INTEGER NOT NULL, - - CONSTRAINT "Token_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "Session_handle_key" ON "Session"("handle"); - --- CreateIndex -CREATE UNIQUE INDEX "Token_hashedToken_type_key" ON "Token"("hashedToken", "type"); - --- AddForeignKey -ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Token" ADD CONSTRAINT "Token_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/examples/cypress/db/migrations/20211012185114_init/migration.sql b/examples/cypress/db/migrations/20211012185114_init/migration.sql new file mode 100644 index 0000000000..0b34cc718b --- /dev/null +++ b/examples/cypress/db/migrations/20211012185114_init/migration.sql @@ -0,0 +1,47 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "name" TEXT, + "email" TEXT NOT NULL, + "hashedPassword" TEXT, + "role" TEXT NOT NULL DEFAULT 'USER' +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "expiresAt" DATETIME, + "handle" TEXT NOT NULL, + "hashedSessionToken" TEXT, + "antiCSRFToken" TEXT, + "publicData" TEXT, + "privateData" TEXT, + "userId" INTEGER, + FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Token" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "hashedToken" TEXT NOT NULL, + "type" TEXT NOT NULL, + "expiresAt" DATETIME NOT NULL, + "sentTo" TEXT NOT NULL, + "userId" INTEGER NOT NULL, + FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "User.email_unique" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session.handle_unique" ON "Session"("handle"); + +-- CreateIndex +CREATE UNIQUE INDEX "Token.hashedToken_type_unique" ON "Token"("hashedToken", "type"); diff --git a/examples/cypress/db/migrations/migration_lock.toml b/examples/cypress/db/migrations/migration_lock.toml index fbffa92c2b..e5e5c4705a 100644 --- a/examples/cypress/db/migrations/migration_lock.toml +++ b/examples/cypress/db/migrations/migration_lock.toml @@ -1,3 +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 +provider = "sqlite" \ No newline at end of file diff --git a/examples/cypress/db/schema.prisma b/examples/cypress/db/schema.prisma index e19edad04f..b15bcc47b2 100644 --- a/examples/cypress/db/schema.prisma +++ b/examples/cypress/db/schema.prisma @@ -2,8 +2,8 @@ // learn more about it in the docs: https://pris.ly/d/prisma-schema datasource db { - provider = "postgres" - url = env("DATABASE_URL") + provider = "sqlite" + url = "file:./db.sqlite" } generator client { diff --git a/examples/cypress/package.json b/examples/cypress/package.json index 8a271c05c6..de9c17d1b6 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -7,11 +7,11 @@ "start": "blitz start", "studio": "blitz prisma studio", "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", - "test": "jest", - "test:watch": "jest --watch", - "test:server": "NODE_ENV=test blitz build && NODE_ENV=test blitz start -p 3099", - "test:e2e": "start-server-and-test test:server http://localhost:3099 'cypress run'", - "cypress:open": "start-server-and-test test:server http://localhost:3099 'cypress open'" + "test:jest": "jest --passWithNoTests", + "test:e2e": "start-server-and-test test-server http://localhost:3099 'cypress run'", + "test": "blitz codegen && prisma generate && blitz prisma migrate deploy && run-s test:*", + "test-server": "NODE_ENV=test blitz build && NODE_ENV=test blitz start -p 3099", + "cypress:open": "start-server-and-test test-server http://localhost:3099 'cypress open'" }, "prisma": { "schema": "db/schema.prisma" @@ -32,28 +32,28 @@ } }, "dependencies": { - "@prisma/client": "3.2.1", + "@prisma/client": "2.24.1", "blitz": "0.41.0", - "final-form": "4.20.4", + "final-form": "4.20.1", "react": "alpha", "react-dom": "alpha", - "react-final-form": "6.5.7", - "zod": "3.9.8" + "react-final-form": "6.5.2", + "zod": "3.8.1" }, "devDependencies": { "@testing-library/cypress": "8.0.1", - "@types/preview-email": "2.0.1", - "@types/react": "17.0.28", + "@types/preview-email": "2.0.0", + "@types/react": "17.0.2", "@types/testing-library__cypress": "5.0.9", "chance": "1.1.8", - "cypress": "8.6.0", - "eslint": "7.32.0", - "husky": "6.0.0", + "cypress": "6.2.1", + "eslint": "7.21.0", + "husky": "5.1.2", "lint-staged": "10.5.4", - "prettier": "2.4.1", - "pretty-quick": "3.1.1", - "preview-email": "3.0.5", - "prisma": "3.2.1", + "prettier": "2.2.1", + "pretty-quick": "3.1.0", + "preview-email": "3.0.3", + "prisma": "2.24.1", "start-server-and-test": "1.11.7" }, "private": true diff --git a/yarn.lock b/yarn.lock index bbab66c34a..29f195ad3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1315,7 +1315,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4": +"@babel/runtime@^7.14.6": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -1610,21 +1610,6 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - "@firebase/analytics-types@0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@firebase/analytics-types/-/analytics-types-0.3.1.tgz#3c5f5d71129c88295e17e914e34b391ffda1723c" @@ -1898,20 +1883,6 @@ dependencies: "@hapi/hoek" "^9.0.0" -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/object-schema@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" - integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== - "@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" @@ -3586,13 +3557,6 @@ dependencies: "@prisma/engines-version" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" -"@prisma/client@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-3.2.1.tgz#b0c60b4c42ec5b701a271380780c70de55bc3311" - integrity sha512-nakt9YoDFD4cgTkRTSVbzG40AKmmbVEtXE3csVqBdDXsm0/L4PQdtbtzILNzq28xv8HH8oHgFKWnsItM23mSDw== - dependencies: - "@prisma/engines-version" "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" - "@prisma/debug@2.19.0": version "2.19.0" resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-2.19.0.tgz#5d9c6b9deb0d5214360ee23644c4e40bee0f251d" @@ -3624,11 +3588,6 @@ resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#2c5813ef98bcbe659b18b521f002f5c8aabbaae2" integrity sha512-60Do+ByVfHnhJ2id5h/lXOZnDQNIf5pz3enkKWOmyr744Z2IxkBu65jRckFfMN5cPtmXDre/Ay/GKm0aoeLwrw== -"@prisma/engines-version@3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c": - version "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c.tgz#63032b40ee09f56ec423eb47617c26de125ffb1e" - integrity sha512-O4dHSbqfX7yAjFMawIEzv6wefv3LRMDK4J20Y70NvE3otbE3CnChlmghkCvMsQ1CGF1QuGlrqfw20aI2JfZcaw== - "@prisma/engines@2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d": version "2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d" resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.19.0-39.c1455d0b443d66b0d9db9bcb1bb9ee0d5bbc511d.tgz#db2809a6f7f18584e3ca89b1f5bad884155629ec" @@ -3639,11 +3598,6 @@ resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#7e542d510f0c03f41b73edbb17254f5a0b272a4d" integrity sha512-29/xO9kqeQka+wN5Ev10l5L4XQXNVXdPToJs1M29VZ2imQsNsL4rtz26m3qGM54IoGWwwfTVdvuVRxKnDl2rig== -"@prisma/engines@3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c": - version "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c.tgz#d2a41a76a89548ea411043c3198eee4a75475dbb" - integrity sha512-wnHODKLQGKkE2ZCHxHQEf/4Anq/EP0ZCvX++D5w34033mwZ94iZiOsEKZZ31fgki7MTh28F1VNF5ZSFdnRjgvQ== - "@prisma/fetch-engine@2.19.0": version "2.19.0" resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-2.19.0.tgz#a01ebfc184ab09cc0ed9d6d4ef966c3846fb5332" @@ -3862,14 +3816,6 @@ dependencies: any-observable "^0.3.0" -"@selderee/plugin-htmlparser2@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz#27e994afd1c2cb647ceb5406a185a5574188069d" - integrity sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA== - dependencies: - domhandler "^4.2.0" - selderee "^0.6.0" - "@sideway/address@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.0.tgz#0b301ada10ac4e0e3fa525c90615e0b61a72b78d" @@ -4939,13 +4885,6 @@ dependencies: "@types/nodemailer" "*" -"@types/preview-email@2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/preview-email/-/preview-email-2.0.1.tgz#df5d1ce7a3034e3dfcd2b38a66c1f60401b27dc8" - integrity sha512-wHtm/Xxlxk2WKRok0ya7iUr+UvQFlq9nTIQXZi2d3n2tdrt+n4Fkybvi45BjQILl2wfGFq4y+jARCo2+ZoaTrA== - dependencies: - "@types/nodemailer" "*" - "@types/progress@^2.0.3": version "2.0.3" resolved "https://registry.yarnpkg.com/@types/progress/-/progress-2.0.3.tgz#7ccbd9c6d4d601319126c469e73b5bb90dfc8ccc" @@ -5025,15 +4964,6 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@17.0.28": - version "17.0.28" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.28.tgz#b40967e79af8f32182c6f6f2655ba196bafc7202" - integrity sha512-6OmflHgk2DlnsFi49kBW3/Dql1GT32bYSk+A6tFBDAt0T0bxotBdQwXkm77lVlczHwY6+Wu6IfpsGqArjOYtaA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - "@types/readable-stream@2.3.9": version "2.3.9" resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.9.tgz#40a8349e6ace3afd2dd1b6d8e9b02945de4566a9" @@ -5076,11 +5006,6 @@ "@types/glob" "*" "@types/node" "*" -"@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/secure-password@3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@types/secure-password/-/secure-password-3.1.0.tgz#3070abfbfa63c43d0f50e2fbe59dd24dce913968" @@ -5885,16 +5810,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.6.3" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" - integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - alex@9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/alex/-/alex-9.1.0.tgz#bc54b305c3e05d87085cca3af4376314be0dbeee" @@ -9103,51 +9018,7 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= -cypress@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.2.1.tgz#27d5fbcf008c698c390fdb0c03441804176d06c4" - integrity sha512-OYkSgzA4J4Q7eMjZvNf5qWpBLR4RXrkqjL3UZ1UzGGLAskO0nFTi/RomNTG6TKvL3Zp4tw4zFY1gp5MtmkCZrA== - dependencies: - "@cypress/listr-verbose-renderer" "^0.4.1" - "@cypress/request" "^2.88.5" - "@cypress/xvfb" "^1.2.4" - "@types/sinonjs__fake-timers" "^6.0.1" - "@types/sizzle" "^2.3.2" - arch "^2.1.2" - blob-util "2.0.2" - bluebird "^3.7.2" - cachedir "^2.3.0" - chalk "^4.1.0" - check-more-types "^2.24.0" - cli-table3 "~0.6.0" - commander "^5.1.0" - common-tags "^1.8.0" - debug "^4.1.1" - eventemitter2 "^6.4.2" - execa "^4.0.2" - executable "^4.1.1" - extract-zip "^1.7.0" - fs-extra "^9.0.1" - getos "^3.2.1" - is-ci "^2.0.0" - is-installed-globally "^0.3.2" - lazy-ass "^1.6.0" - listr "^0.14.3" - lodash "^4.17.19" - log-symbols "^4.0.0" - minimist "^1.2.5" - moment "^2.27.0" - ospath "^1.2.2" - pretty-bytes "^5.4.1" - ramda "~0.26.1" - request-progress "^3.0.0" - supports-color "^7.2.0" - tmp "~0.2.1" - untildify "^4.0.0" - url "^0.11.0" - yauzl "^2.10.0" - -cypress@8.6.0: +cypress@*: version "8.6.0" resolved "https://registry.yarnpkg.com/cypress/-/cypress-8.6.0.tgz#8d02fa58878b37cfc45bbfce393aa974fa8a8e22" integrity sha512-F7qEK/6Go5FsqTueR+0wEw2vOVKNgk5847Mys8vsWkzPoEKdxs+7N9Y1dit+zhaZCLtMPyrMwjfA53ZFy+lSww== @@ -9195,6 +9066,50 @@ cypress@8.6.0: url "^0.11.0" yauzl "^2.10.0" +cypress@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-6.2.1.tgz#27d5fbcf008c698c390fdb0c03441804176d06c4" + integrity sha512-OYkSgzA4J4Q7eMjZvNf5qWpBLR4RXrkqjL3UZ1UzGGLAskO0nFTi/RomNTG6TKvL3Zp4tw4zFY1gp5MtmkCZrA== + dependencies: + "@cypress/listr-verbose-renderer" "^0.4.1" + "@cypress/request" "^2.88.5" + "@cypress/xvfb" "^1.2.4" + "@types/sinonjs__fake-timers" "^6.0.1" + "@types/sizzle" "^2.3.2" + arch "^2.1.2" + blob-util "2.0.2" + bluebird "^3.7.2" + cachedir "^2.3.0" + chalk "^4.1.0" + check-more-types "^2.24.0" + cli-table3 "~0.6.0" + commander "^5.1.0" + common-tags "^1.8.0" + debug "^4.1.1" + eventemitter2 "^6.4.2" + execa "^4.0.2" + executable "^4.1.1" + extract-zip "^1.7.0" + fs-extra "^9.0.1" + getos "^3.2.1" + is-ci "^2.0.0" + is-installed-globally "^0.3.2" + lazy-ass "^1.6.0" + listr "^0.14.3" + lodash "^4.17.19" + log-symbols "^4.0.0" + minimist "^1.2.5" + moment "^2.27.0" + ospath "^1.2.2" + pretty-bytes "^5.4.1" + ramda "~0.26.1" + request-progress "^3.0.0" + supports-color "^7.2.0" + tmp "~0.2.1" + untildify "^4.0.0" + url "^0.11.0" + yauzl "^2.10.0" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -9254,7 +9169,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.10.4, dayjs@^1.10.6: +dayjs@^1.10.4: version "1.10.7" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== @@ -9590,11 +9505,6 @@ directory-tree@2.2.5: resolved "https://registry.yarnpkg.com/directory-tree/-/directory-tree-2.2.5.tgz#43d167eeb87b57640cc832f794f88431ebd47300" integrity sha512-qmeuql8N7hQB5b+cnlvbcHSjKBNpRjLY5KcvyFd9CTC5uTN7sJshEQ/ExZidAcEUEYcC/76i8ikLtbBMG81YRg== -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" - integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= - doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -9683,11 +9593,6 @@ domelementtype@^2.0.1, domelementtype@^2.1.0: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== -domelementtype@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" - integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -9716,13 +9621,6 @@ domhandler@^4.0.0: dependencies: domelementtype "^2.1.0" -domhandler@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" - integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== - dependencies: - domelementtype "^2.2.0" - domutils@1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" @@ -9766,15 +9664,6 @@ domutils@^2.4.2: domelementtype "^2.0.1" domhandler "^4.0.0" -domutils@^2.5.2: - 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" - dot-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-2.1.1.tgz#34dcf37f50a8e93c2b3bca8bb7fb9155c7da3bee" @@ -10452,52 +10341,6 @@ eslint@7.21.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@7.32.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - 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 "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - eslint@^7.3.0: version "7.17.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.17.0.tgz#4ccda5bf12572ad3bf760e6f195886f50569adb0" @@ -11049,7 +10892,7 @@ fast-deep-equal@^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: +fast-deep-equal@^3.1.1: 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== @@ -11282,13 +11125,6 @@ final-form@4.20.1: dependencies: "@babel/runtime" "^7.10.0" -final-form@4.20.4: - version "4.20.4" - resolved "https://registry.yarnpkg.com/final-form/-/final-form-4.20.4.tgz#8d59e36d3248a227265cc731d76c0564dd2606f6" - integrity sha512-hyoOVVilPLpkTvgi+FSJkFZrh0Yhy4BhE6lk/NiBwrF4aRV8/ykKEyXYvQH/pfUbRkOosvpESYouFb+FscsLrw== - dependencies: - "@babel/runtime" "^7.10.0" - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -12053,7 +11889,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob-parent@^5.1.1, glob-parent@^5.1.2: +glob-parent@^5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== @@ -12178,13 +12014,6 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -globals@^13.6.0, globals@^13.9.0: - version "13.11.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" - integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== - dependencies: - type-fest "^0.20.2" - globalthis@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" @@ -12713,18 +12542,6 @@ html-to-text@6.0.0: lodash "^4.17.20" minimist "^1.2.5" -html-to-text@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-8.0.0.tgz#5848681a5a38d657a7bb58cf5006d1c29fe64ce3" - integrity sha512-fEtul1OerF2aMEV+Wpy+Ue20tug134jOY1GIudtdqZi7D0uTudB2tVJBKfVhTL03dtqeJoF8gk8EPX9SyMEvLg== - dependencies: - "@selderee/plugin-htmlparser2" "^0.6.0" - deepmerge "^4.2.2" - he "^1.2.0" - htmlparser2 "^6.1.0" - minimist "^1.2.5" - selderee "^0.6.0" - htmlescape@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" @@ -12762,16 +12579,6 @@ htmlparser2@^4.1.0: domutils "^2.0.0" entities "^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" - http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -12902,11 +12709,6 @@ husky@5.1.2: resolved "https://registry.yarnpkg.com/husky/-/husky-5.1.2.tgz#dc6a1f68640455d8d98c28875e073087f86c5081" integrity sha512-lilaRYeDXcAOj8DuRnN9IxUyEMVbYg9rK7yVNkPB5V4hCvxIUxpMeiv9K2h77CE0HzjCnk1Br0oWe1IghXngDQ== -husky@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" - integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== - hyperlinker@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" @@ -12938,13 +12740,6 @@ iconv-lite@0.6.2, iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -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" @@ -14739,11 +14534,6 @@ json-schema-traverse@^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-typed@^7.0.0: version "7.0.3" resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" @@ -15182,13 +14972,6 @@ linkify-it@3.0.2: dependencies: uc.micro "^1.0.1" -linkify-it@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" - integrity sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ== - dependencies: - uc.micro "^1.0.1" - lint-staged@10.5.4: version "10.5.4" resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.5.4.tgz#cd153b5f0987d2371fc1d2847a409a2fe705b665" @@ -15402,11 +15185,6 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= -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.curry@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170" @@ -15497,7 +15275,7 @@ lodash.memoize@^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.4.0, lodash.merge@^4.6.2: +lodash.merge@^4.4.0: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== @@ -15567,11 +15345,6 @@ lodash.toarray@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -15767,21 +15540,6 @@ mailparser@^3.0.1: nodemailer "6.4.16" tlds "1.214.0" -mailparser@^3.3.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.4.0.tgz#249869bc5a41af9e0eabbf005197789442fbac9e" - integrity sha512-u2pfpLg+xr7m2FKDl+ohQhy2gMok1QZ+S9E5umS9ez5DSJWttrqSmBGswyj9F68pZMVTwbhLpBt7Kd04q/W4Vw== - dependencies: - encoding-japanese "1.0.30" - he "1.2.0" - html-to-text "8.0.0" - iconv-lite "0.6.3" - libmime "5.0.0" - linkify-it "3.0.3" - mailsplit "5.3.1" - nodemailer "6.7.0" - tlds "1.224.0" - mailsplit@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.0.0.tgz#0924c89142deadb75ef3825860181e436d7557a1" @@ -15791,15 +15549,6 @@ mailsplit@5.0.0: libmime "4.2.1" libqp "1.1.0" -mailsplit@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.3.1.tgz#dd6d5c20a7b8a767fe9c9649dfcb26ee04f84c36" - integrity sha512-o6R6HCzqWYmI2/IYlB+v2IMPgYqC2EynmagZQICAhR7zAq0CO6fPcsO6CrYmVuYT+SSwvLAEZR5WniohBELcAA== - dependencies: - libbase64 "1.2.1" - libmime "5.0.0" - libqp "1.1.0" - make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -16594,11 +16343,6 @@ moment@^2.22.1, moment@^2.24.0, moment@^2.27.0: resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== -moo@^0.5.0, moo@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4" - integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w== - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -16754,16 +16498,6 @@ ncp@1.0.x: resolved "https://registry.yarnpkg.com/ncp/-/ncp-1.0.1.tgz#d15367e5cb87432ba117d2bf80fdf45aecfb4246" integrity sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY= -nearley@^2.20.1: - version "2.20.1" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" - integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== - dependencies: - commander "^2.19.0" - moo "^0.5.0" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - needle@^2.2.1: version "2.5.2" resolved "https://registry.yarnpkg.com/needle/-/needle-2.5.2.tgz#cf1a8fce382b5a280108bba90a14993c00e4010a" @@ -17066,11 +16800,6 @@ nodemailer@6.4.16: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.16.tgz#5cb6391b1d79ab7eff32d6f9f48366b5a7117293" integrity sha512-68K0LgZ6hmZ7PVmwL78gzNdjpj5viqBdFqKrTtr9bZbJYj6BRj5W6WGkxXrEnUl3Co3CBXi3CZBUlpV/foGnOQ== -nodemailer@6.7.0, nodemailer@^6.6.3: - version "6.7.0" - resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.0.tgz#86614722c4e0c33d1b5b02aecb90d6d629932b0d" - integrity sha512-AtiTVUFHLiiDnMQ43zi0YgkzHOEWUkhDgPlBXrsDzJiJvB29Alo4OKxHQ0ugF3gRqRQIneCLtZU3yiUo7pItZw== - nodemailer@^6.4.16: version "6.4.17" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.17.tgz#8de98618028953b80680775770f937243a7d7877" @@ -17556,14 +17285,6 @@ onetime@^5.1.0, onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@7: - 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@^7.3.0: version "7.4.0" resolved "https://registry.yarnpkg.com/open/-/open-7.4.0.tgz#ad95b98f871d9acb0ec8fecc557082cc9986626b" @@ -18104,14 +17825,6 @@ parse5@^6.0.0, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parseley@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.7.0.tgz#9949e3a0ed05c5072adb04f013c2810cf49171a8" - integrity sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw== - dependencies: - moo "^0.5.1" - nearley "^2.20.1" - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -19408,11 +19121,6 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier-plugin-prisma@0.15.0: - version "0.15.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-prisma/-/prettier-plugin-prisma-0.15.0.tgz#c3afd7b011e8a247ec55662ac506b4dd092fc13b" - integrity sha512-BMXs0Eaq0v0fHxSvGxjRAegn6Xq+QqfApb4lFEUfw1Bi1f2py6CP1a2hCgWrzLx7G9qI3lUSNJ8yy/bX0Cju/w== - prettier-plugin-prisma@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/prettier-plugin-prisma/-/prettier-plugin-prisma-0.4.0.tgz#3ee7d276f2ce15f3b3dfb3e3830e5fe985d5ac62" @@ -19423,11 +19131,6 @@ prettier@2.2.1, prettier@^2.0.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== -prettier@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" - integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== - pretty-bytes@5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -19497,18 +19200,6 @@ pretty-quick@3.1.0: mri "^1.1.5" multimatch "^4.0.0" -pretty-quick@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-3.1.1.tgz#93ca4e2dd38cc4e970e3f54a0ead317a25454688" - integrity sha512-ZYLGiMoV2jcaas3vTJrLvKAYsxDoXQBUn8OSTxkl67Fyov9lyXivJTl0+2WVh+y6EovGcw7Lm5ThYpH+Sh3XxQ== - dependencies: - chalk "^3.0.0" - execa "^4.0.0" - find-up "^4.1.0" - ignore "^5.1.4" - mri "^1.1.5" - multimatch "^4.0.0" - prettysize@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prettysize/-/prettysize-2.0.0.tgz#902c02480d865d9cc0813011c9feb4fa02ce6996" @@ -19527,19 +19218,6 @@ preview-email@3.0.3: pug "^3.0.0" uuid "^8.3.1" -preview-email@3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-3.0.5.tgz#09c32ba43c450ead16b309d9e5cb10f90ff45a95" - integrity sha512-q37jdkVw+wic0o/7xYhOTBS4kF0WX3two0OepmR1Fhxp9NTpO3rJTccAjQm95gJx/2Wa/Nv98sr9pXIQ77/foA== - dependencies: - dayjs "^1.10.6" - debug "^4.3.2" - mailparser "^3.3.0" - nodemailer "^6.6.3" - open "7" - pug "^3.0.2" - uuid "^8.3.2" - prisma@2.24.1: version "2.24.1" resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.24.1.tgz#f8f4cb8baf407a71800256160277f69603bd43a3" @@ -19547,13 +19225,6 @@ prisma@2.24.1: dependencies: "@prisma/engines" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" -prisma@3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-3.2.1.tgz#696871f6d0e374df2de96297df4c04756ee6e3b5" - integrity sha512-nXhldcFYemNSMqdTAEziggVWBNbCHTrr0amkCJruP3G2AFpzxrKtksPRLYNetxdKMJAt7aRRumusbtmTTDgyzw== - dependencies: - "@prisma/engines" "3.2.1-1.b71d8cb16c4ddc7e3e9821f42fd09b0f82d7934c" - private@^0.1.8, private@~0.1.5: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -19779,20 +19450,6 @@ pug-code-gen@^3.0.0: void-elements "^3.1.0" with "^7.0.0" -pug-code-gen@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.2.tgz#ad190f4943133bf186b60b80de483100e132e2ce" - integrity sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg== - dependencies: - constantinople "^4.0.1" - doctypes "^1.1.0" - js-stringify "^1.0.2" - pug-attrs "^3.0.0" - pug-error "^2.0.0" - pug-runtime "^3.0.0" - void-elements "^3.1.0" - with "^7.0.0" - pug-error@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.0.0.tgz#5c62173cb09c34de2a2ce04f17b8adfec74d8ca5" @@ -19818,15 +19475,6 @@ pug-lexer@^5.0.0: is-expression "^4.0.0" pug-error "^2.0.0" -pug-lexer@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" - integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== - dependencies: - character-parser "^2.2.0" - is-expression "^4.0.0" - pug-error "^2.0.0" - pug-linker@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" @@ -19856,11 +19504,6 @@ pug-runtime@^3.0.0: resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.0.tgz#d523025fdc0a1efe70929d1fd3a2d24121ffffb6" integrity sha512-GoEPcmQNnaTsePEdVA05bDpY+Op5VLHKayg08AQiqJBWU/yIaywEYv7TetC5dEQS3fzBBoyb2InDcZEg3mPTIA== -pug-runtime@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" - integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== - pug-strip-comments@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" @@ -19887,20 +19530,6 @@ pug@^3.0.0: pug-runtime "^3.0.0" pug-strip-comments "^2.0.0" -pug@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535" - integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== - dependencies: - pug-code-gen "^3.0.2" - pug-filters "^4.0.0" - pug-lexer "^5.0.1" - pug-linker "^4.0.0" - pug-load "^3.0.0" - pug-parser "^6.0.0" - pug-runtime "^3.0.1" - pug-strip-comments "^2.0.0" - pump-chain@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/pump-chain/-/pump-chain-1.0.0.tgz#7d57d8d9ad8181ea808f5413c4f2bc1e786a5e37" @@ -20064,11 +19693,6 @@ quotation@^1.0.0: resolved "https://registry.yarnpkg.com/quotation/-/quotation-1.1.3.tgz#2a4d11f70105ad398b577883f310469367f53351" integrity sha512-45gUgmX/RtQOQV1kwM06boP49OYXcKCPrYwdmAvs5YqkpiobhNKKwo524JM6Ma0ko3oN9tXNcWs9+ABq3Ry7YA== -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" - integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= - ramda@~0.26.1: version "0.26.1" resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06" @@ -20079,14 +19703,6 @@ ramda@~0.27.1: resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.1.tgz#66fc2df3ef873874ffc2da6aa8984658abacf5c9" integrity sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw== -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - random-string@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/random-string/-/random-string-0.2.0.tgz#a46e4375352beda9a0d7b0d19ed6d321ecd1d82d" @@ -20201,13 +19817,6 @@ react-final-form@6.5.2: dependencies: "@babel/runtime" "^7.12.1" -react-final-form@6.5.7: - version "6.5.7" - resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-6.5.7.tgz#0c1098accf0f0011adee5a46076ed1b99ed1b1ea" - integrity sha512-o7tvJXB+McGiXOILqIC8lnOcX4aLhIBiF/Xi9Qet35b7XOS8R7KL8HLRKTfnZWQJm6MCE15v1U0SFive0NcxyA== - dependencies: - "@babel/runtime" "^7.15.4" - react-is@17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -20920,11 +20529,6 @@ require-from-string@^1.1.0: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" integrity sha1-UpyczvJzgK3+yaL5ZbZJu+5jZBg= -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-like@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" @@ -21512,13 +21116,6 @@ seedrandom@3.0.5: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== -selderee@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.6.0.tgz#f3bee66cfebcb6f33df98e4a1df77388b42a96f7" - integrity sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg== - dependencies: - parseley "^0.7.0" - selenium-standalone@6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/selenium-standalone/-/selenium-standalone-6.18.0.tgz#011e0672b1b86893f77244a86ddea1b6baadfb87" @@ -22387,15 +21984,6 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string.prototype.matchall@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" @@ -22516,13 +22104,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.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-buf@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572" @@ -22815,18 +22396,6 @@ table@^6.0.4: slice-ansi "^4.0.0" string-width "^4.2.0" -table@^6.0.9: - version "6.7.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" - integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - tagged-versions@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/tagged-versions/-/tagged-versions-1.3.0.tgz#fd3cca176859817b95b1f5d311a12c9c08c8bdc4" @@ -23200,11 +22769,6 @@ tlds@1.214.0: resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.214.0.tgz#a20191443eec26fd3339a3bd98e87a0b4f3f0d89" integrity sha512-+i48KYsrCkkIZnsj31cTIj9cu5NtFxKo7xlNIB7jg8kXi//b4Ertl5qaHgqFF+y+g0nFwt/k+eph2uUNQJgfwg== -tlds@1.224.0: - version "1.224.0" - resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.224.0.tgz#dc9a5b0bda0708af0302114f6e24458770c5af01" - integrity sha512-Jgdc8SEijbDFUsmCn6Wk/f7E6jBLFZOG3U1xK0amGSfEH55Xx97ItUS/d2NngsuApjn11UeWCWj8Um3VRhseZQ== - tmp@0.0.30: version "0.0.30" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" @@ -23581,11 +23145,6 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== -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.2: version "0.21.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.2.tgz#43b9dc71d9dc5593ea71bf7b0e013ec10f838249" @@ -25104,11 +24663,6 @@ zod@3.8.1: resolved "https://registry.yarnpkg.com/zod/-/zod-3.8.1.tgz#b1173c3b4ac2a9e06d302ff580e3b41902766b9f" integrity sha512-u4Uodl7dLh8nXZwqXL1SM5FAl5b4lXYHOxMUVb9lqhlEAZhA2znX+0oW480m0emGFMxpoRHzUncAqRkc4h8ZJA== -zod@3.9.8: - version "3.9.8" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.9.8.tgz#562eda33925203542dea70100ce0d3766d04b0ab" - integrity sha512-pTNhdJd45PPOBpdxO8x00Tv+HhknYGx3WdgFQndazp+G1Gd2Cxf81L5yMfCIIcd/SA3VqrcTv/G4bFcr+DsZhA== - zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" From 198ec53b051a7084ac985e53bab3afbc2a497a0b Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Tue, 12 Oct 2021 21:16:54 +0200 Subject: [PATCH 5/9] use cross-env --- examples/cypress/package.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/cypress/package.json b/examples/cypress/package.json index de9c17d1b6..9b6a153091 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -7,11 +7,12 @@ "start": "blitz start", "studio": "blitz prisma studio", "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", + "cy:open": "cypress open", + "cy:run": "cypress run --browser chrome", + "test": "prisma generate && blitz codegen && yarn test:jest && yarn test:e2e", "test:jest": "jest --passWithNoTests", - "test:e2e": "start-server-and-test test-server http://localhost:3099 'cypress run'", - "test": "blitz codegen && prisma generate && blitz prisma migrate deploy && run-s test:*", - "test-server": "NODE_ENV=test blitz build && NODE_ENV=test blitz start -p 3099", - "cypress:open": "start-server-and-test test-server http://localhost:3099 'cypress open'" + "test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy && blitz build && cross-env NODE_ENV=test blitz start -p 3099", + "test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run" }, "prisma": { "schema": "db/schema.prisma" @@ -46,6 +47,7 @@ "@types/react": "17.0.2", "@types/testing-library__cypress": "5.0.9", "chance": "1.1.8", + "cross-env": "7.0.3", "cypress": "6.2.1", "eslint": "7.21.0", "husky": "5.1.2", From c772895e529e419711ed391210ea1b9fe9e90a49 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Wed, 13 Oct 2021 11:32:45 +0200 Subject: [PATCH 6/9] update scripts and types --- .../cypress/app/auth/components/LoginForm.tsx | 9 +- .../app/auth/mutations/forgotPassword.test.ts | 56 +++++++++++++ .../app/auth/mutations/resetPassword.test.ts | 82 +++++++++++++++++++ .../cypress/app/auth/pages/reset-password.tsx | 4 +- examples/cypress/app/auth/pages/signup.tsx | 4 +- examples/cypress/app/core/components/Form.tsx | 6 -- .../app/core/components/LabeledTextField.tsx | 17 ---- examples/cypress/app/pages/index.test.tsx | 27 ++++++ examples/cypress/app/pages/index.tsx | 4 +- examples/cypress/blitz.config.ts | 2 +- examples/cypress/cypress/.eslintrc.js | 10 +++ examples/cypress/package.json | 13 +-- examples/cypress/test/e2e/.eslintrc.js | 3 + examples/cypress/types.ts | 9 +- yarn.lock | 25 ------ 15 files changed, 205 insertions(+), 66 deletions(-) create mode 100644 examples/cypress/app/auth/mutations/forgotPassword.test.ts create mode 100644 examples/cypress/app/auth/mutations/resetPassword.test.ts create mode 100644 examples/cypress/app/pages/index.test.tsx create mode 100644 examples/cypress/cypress/.eslintrc.js create mode 100644 examples/cypress/test/e2e/.eslintrc.js diff --git a/examples/cypress/app/auth/components/LoginForm.tsx b/examples/cypress/app/auth/components/LoginForm.tsx index fc99d0ecbc..4dc8ce4baf 100644 --- a/examples/cypress/app/auth/components/LoginForm.tsx +++ b/examples/cypress/app/auth/components/LoginForm.tsx @@ -1,4 +1,4 @@ -import { AuthenticationError, Link, useMutation, Routes } from "blitz" +import { AuthenticationError, Link, useMutation } from "blitz" import { LabeledTextField } from "app/core/components/LabeledTextField" import { Form, FORM_ERROR } from "app/core/components/Form" import login from "app/auth/mutations/login" @@ -37,15 +37,10 @@ export const LoginForm = (props: LoginFormProps) => { > -

- Or Sign Up + Or Sign Up
) diff --git a/examples/cypress/app/auth/mutations/forgotPassword.test.ts b/examples/cypress/app/auth/mutations/forgotPassword.test.ts new file mode 100644 index 0000000000..cdd8112c3c --- /dev/null +++ b/examples/cypress/app/auth/mutations/forgotPassword.test.ts @@ -0,0 +1,56 @@ +import { hash256, Ctx } from "blitz" +import forgotPassword from "./forgotPassword" +import db from "db" +import previewEmail from "preview-email" + +beforeEach(async () => { + await db.$reset() +}) + +const generatedToken = "plain-token" +jest.mock("next/stdlib-server", () => ({ + ...jest.requireActual("next/stdlib-server")!, + generateToken: () => generatedToken, +})) +jest.mock("preview-email", () => jest.fn()) + +describe("forgotPassword mutation", () => { + it("does not throw error if user doesn't exist", async () => { + await expect(forgotPassword({ email: "no-user@email.com" }, {} as Ctx)).resolves.not.toThrow() + }) + + it("works correctly", async () => { + // Create test user + const user = await db.user.create({ + data: { + email: "user@example.com", + tokens: { + // Create old token to ensure it's deleted + create: { + type: "RESET_PASSWORD", + hashedToken: "token", + expiresAt: new Date(), + sentTo: "user@example.com", + }, + }, + }, + include: { tokens: true }, + }) + + // Invoke the mutation + await forgotPassword({ email: user.email }, {} as Ctx) + + const tokens = await db.token.findMany({ where: { userId: user.id } }) + const token = tokens[0] + + // delete's existing tokens + expect(tokens.length).toBe(1) + + expect(token.id).not.toBe(user.tokens[0].id) + expect(token.type).toBe("RESET_PASSWORD") + expect(token.sentTo).toBe(user.email) + expect(token.hashedToken).toBe(hash256(generatedToken)) + expect(token.expiresAt > new Date()).toBe(true) + expect(previewEmail).toBeCalled() + }) +}) diff --git a/examples/cypress/app/auth/mutations/resetPassword.test.ts b/examples/cypress/app/auth/mutations/resetPassword.test.ts new file mode 100644 index 0000000000..d613716f53 --- /dev/null +++ b/examples/cypress/app/auth/mutations/resetPassword.test.ts @@ -0,0 +1,82 @@ +import resetPassword from "./resetPassword" +import db from "db" +import { hash256, SecurePassword } from "blitz" + +beforeEach(async () => { + await db.$reset() +}) + +const mockCtx: any = { + session: { + $create: jest.fn, + }, +} + +describe("resetPassword mutation", () => { + it("works correctly", async () => { + expect(true).toBe(true) + + // Create test user + const goodToken = "randomPasswordResetToken" + const expiredToken = "expiredRandomPasswordResetToken" + const future = new Date() + future.setHours(future.getHours() + 4) + const past = new Date() + past.setHours(past.getHours() - 4) + + const user = await db.user.create({ + data: { + email: "user@example.com", + tokens: { + // Create old token to ensure it's deleted + create: [ + { + type: "RESET_PASSWORD", + hashedToken: hash256(expiredToken), + expiresAt: past, + sentTo: "user@example.com", + }, + { + type: "RESET_PASSWORD", + hashedToken: hash256(goodToken), + expiresAt: future, + sentTo: "user@example.com", + }, + ], + }, + }, + include: { tokens: true }, + }) + + const newPassword = "newPassword" + + // Non-existent token + await expect( + resetPassword({ token: "no-token", password: "", passwordConfirmation: "" }, mockCtx) + ).rejects.toThrowError() + + // Expired token + await expect( + resetPassword( + { token: expiredToken, password: newPassword, passwordConfirmation: newPassword }, + mockCtx + ) + ).rejects.toThrowError() + + // Good token + await resetPassword( + { token: goodToken, password: newPassword, passwordConfirmation: newPassword }, + mockCtx + ) + + // Delete's the token + const numberOfTokens = await db.token.count({ where: { userId: user.id } }) + expect(numberOfTokens).toBe(0) + + // Updates user's password + const updatedUser = await db.user.findFirst({ where: { id: user.id } }) + expect(await SecurePassword.verify(updatedUser!.hashedPassword, newPassword)).toBe( + SecurePassword.VALID + ) + }) +}) diff --git a/examples/cypress/app/auth/pages/reset-password.tsx b/examples/cypress/app/auth/pages/reset-password.tsx index 6d25e7d348..1833510611 100644 --- a/examples/cypress/app/auth/pages/reset-password.tsx +++ b/examples/cypress/app/auth/pages/reset-password.tsx @@ -1,10 +1,12 @@ -import { BlitzPage, useRouterQuery, Link, useMutation, Routes } from "blitz" +import { BlitzPage, useRouterQuery, Link, useMutation } from "blitz" import Layout from "app/core/layouts/Layout" import { LabeledTextField } from "app/core/components/LabeledTextField" import { Form, FORM_ERROR } from "app/core/components/Form" import { ResetPassword } from "app/auth/validations" import resetPassword from "app/auth/mutations/resetPassword" +import { Routes } from ".blitz" + const ResetPasswordPage: BlitzPage = () => { const query = useRouterQuery() const [resetPasswordMutation, { isSuccess }] = useMutation(resetPassword) diff --git a/examples/cypress/app/auth/pages/signup.tsx b/examples/cypress/app/auth/pages/signup.tsx index 98f6bcbafd..a08c6554e4 100644 --- a/examples/cypress/app/auth/pages/signup.tsx +++ b/examples/cypress/app/auth/pages/signup.tsx @@ -1,7 +1,9 @@ -import { useRouter, BlitzPage, Routes } from "blitz" +import { useRouter, BlitzPage } from "blitz" import Layout from "app/core/layouts/Layout" import { SignupForm } from "app/auth/components/SignupForm" +import { Routes } from ".blitz" + const SignupPage: BlitzPage = () => { const router = useRouter() diff --git a/examples/cypress/app/core/components/Form.tsx b/examples/cypress/app/core/components/Form.tsx index eb45dc3b9f..c90fa74a68 100644 --- a/examples/cypress/app/core/components/Form.tsx +++ b/examples/cypress/app/core/components/Form.tsx @@ -44,12 +44,6 @@ export function Form>({ {submitText} )} - - )} /> diff --git a/examples/cypress/app/core/components/LabeledTextField.tsx b/examples/cypress/app/core/components/LabeledTextField.tsx index 7dc98b89c9..f426ba5ceb 100644 --- a/examples/cypress/app/core/components/LabeledTextField.tsx +++ b/examples/cypress/app/core/components/LabeledTextField.tsx @@ -41,23 +41,6 @@ export const LabeledTextField = forwardRef )} - - ) } diff --git a/examples/cypress/app/pages/index.test.tsx b/examples/cypress/app/pages/index.test.tsx new file mode 100644 index 0000000000..dfe3e47b60 --- /dev/null +++ b/examples/cypress/app/pages/index.test.tsx @@ -0,0 +1,27 @@ +import { render } from "test/utils" +import Home from "./index" + +jest.mock("@blitzjs/core", () => ({ + ...jest.requireActual("@blitzjs/core")!, + useQuery: () => [ + { + id: 1, + name: "User", + email: "user@email.com", + role: "user", + }, + ], +})) + +test("renders blitz documentation link", () => { + // This is an example of how to ensure a specific item is in the document + // But it's disabled by default (by test.skip) so the test doesn't fail + // when you remove the the default content from the page + + // This is an example on how to mock api hooks when testing + + const { getByText } = render() + const element = getByText(/powered by blitz/i) + // @ts-ignore + expect(element).toBeInTheDocument() +}) diff --git a/examples/cypress/app/pages/index.tsx b/examples/cypress/app/pages/index.tsx index b49e733bbb..1c91c8a13a 100644 --- a/examples/cypress/app/pages/index.tsx +++ b/examples/cypress/app/pages/index.tsx @@ -1,10 +1,12 @@ import { Suspense } from "react" -import { Image, Link, BlitzPage, useMutation, Routes } from "blitz" +import { Image, Link, BlitzPage, useMutation } from "blitz" import Layout from "app/core/layouts/Layout" import { useCurrentUser } from "app/core/hooks/useCurrentUser" import logout from "app/auth/mutations/logout" import logo from "public/logo.png" +import { Routes } from ".blitz" + /* * This file is just for a pleasant getting started page for your new app. * You can delete everything in here and start from scratch if you like. diff --git a/examples/cypress/blitz.config.ts b/examples/cypress/blitz.config.ts index 450358086e..5f01cf396d 100644 --- a/examples/cypress/blitz.config.ts +++ b/examples/cypress/blitz.config.ts @@ -3,7 +3,7 @@ import { BlitzConfig, sessionMiddleware, simpleRolesIsAuthorized } from "blitz" const config: BlitzConfig = { middleware: [ sessionMiddleware({ - cookiePrefix: "cypress", + cookiePrefix: "cypress-example", isAuthorized: simpleRolesIsAuthorized, }), ], diff --git a/examples/cypress/cypress/.eslintrc.js b/examples/cypress/cypress/.eslintrc.js new file mode 100644 index 0000000000..c2a9e593d5 --- /dev/null +++ b/examples/cypress/cypress/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + plugins: ["cypress"], + env: { + "cypress/globals": true, + }, + extends: ["plugin:cypress/recommended"], + rules: { + "cypress/no-unnecessary-waiting": "off", + }, +} diff --git a/examples/cypress/package.json b/examples/cypress/package.json index 9b6a153091..f4262a704b 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -8,11 +8,12 @@ "studio": "blitz prisma studio", "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", "cy:open": "cypress open", - "cy:run": "cypress run --browser chrome", - "test": "prisma generate && blitz codegen && yarn test:jest && yarn test:e2e", + "cy:run": "cypress run", + "test:server": "prisma generate && blitz prisma migrate deploy && blitz build && blitz start -p 3099", + "test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run", + "test:migrate": "prisma generate && blitz prisma migrate deploy", "test:jest": "jest --passWithNoTests", - "test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy && blitz build && cross-env NODE_ENV=test blitz start -p 3099", - "test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run" + "test": "yarn test:jest && yarn test:e2e" }, "prisma": { "schema": "db/schema.prisma" @@ -36,8 +37,8 @@ "@prisma/client": "2.24.1", "blitz": "0.41.0", "final-form": "4.20.1", - "react": "alpha", - "react-dom": "alpha", + "react": "0.0.0-experimental-6a589ad71", + "react-dom": "0.0.0-experimental-6a589ad71", "react-final-form": "6.5.2", "zod": "3.8.1" }, diff --git a/examples/cypress/test/e2e/.eslintrc.js b/examples/cypress/test/e2e/.eslintrc.js new file mode 100644 index 0000000000..49eb3030c8 --- /dev/null +++ b/examples/cypress/test/e2e/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["../../cypress/.eslintrc.js"], +} diff --git a/examples/cypress/types.ts b/examples/cypress/types.ts index 82209331c2..284b103fc9 100644 --- a/examples/cypress/types.ts +++ b/examples/cypress/types.ts @@ -1,7 +1,6 @@ import { DefaultCtx, SessionContext, SimpleRolesIsAuthorized } from "blitz" import { User } from "db" -// Note: You should switch to Postgres and then use a DB enum for role type export type Role = "ADMIN" | "USER" declare module "blitz" { @@ -16,3 +15,11 @@ declare module "blitz" { } } } + +// This should not be needed. Usually it isn't but for some reason in this example it is +declare module "react" { + interface StyleHTMLAttributes extends React.HTMLAttributes { + jsx?: boolean + global?: boolean + } +} diff --git a/yarn.lock b/yarn.lock index 29f195ad3b..e425437cb7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19801,15 +19801,6 @@ react-dom@0.0.0-experimental-6a589ad71: object-assign "^4.1.1" scheduler "0.0.0-experimental-6a589ad71" -react-dom@alpha: - version "18.0.0-alpha-55d75005b-20211011" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.0.0-alpha-55d75005b-20211011.tgz#d2b54296412d031aac0026d78184d4c3a8c0f6a9" - integrity sha512-bBFdYWr6PMvHlPtmm+OTL/U7XH7gIPS4+AHJwMmefemXZLM0j+2yLltSsvRF4KVcYs4qEDFptClOMtZNK1ffUg== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler "0.21.0-alpha-55d75005b-20211011" - react-final-form@6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-6.5.2.tgz#d04d1eb7d92eabc6f6c35206bb0eebfc4bfd924b" @@ -19889,14 +19880,6 @@ react@0.0.0-experimental-6a589ad71: loose-envify "^1.1.0" object-assign "^4.1.1" -react@alpha: - version "18.0.0-alpha-55d75005b-20211011" - resolved "https://registry.yarnpkg.com/react/-/react-18.0.0-alpha-55d75005b-20211011.tgz#1e2d921e896c935023f3b851d33205733b6f81ff" - integrity sha512-I7diwDx/kfsJsw+H380fCpqhLsCLM40M4M+Yl6o4SL1bDTW1a27ptbLE9VPX3Nbw8zipBJIGY7vaDknQxj1p0w== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - read-cmd-shim@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-2.0.0.tgz#4a50a71d6f0965364938e9038476f7eede3928d9" @@ -21036,14 +21019,6 @@ scheduler@0.0.0-experimental-6a589ad71: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@0.21.0-alpha-55d75005b-20211011: - version "0.21.0-alpha-55d75005b-20211011" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-55d75005b-20211011.tgz#19db268580f41210122fe46f1a0d981bf365cd83" - integrity sha512-r9aUxDElhZxsh3W72EGlvrcJepN0TKC1WIMNGxJuhfQchNzs0OHEos72W7V5L+Xug7VkhyMZ/zPR2I0IMQ2ruw== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - scheduler@0.21.0-alpha-bd255700d-20210816: version "0.21.0-alpha-bd255700d-20210816" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.21.0-alpha-bd255700d-20210816.tgz#ed764c0e6eb3f35b5a677c49eba9852c6bd25dd4" From e47a1f063d32feaaeb906f39e6ad0b58e0210a20 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Wed, 13 Oct 2021 11:50:44 +0200 Subject: [PATCH 7/9] add blitz codegen --- examples/cypress/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/cypress/package.json b/examples/cypress/package.json index f4262a704b..4859427019 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -9,9 +9,8 @@ "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", "cy:open": "cypress open", "cy:run": "cypress run", - "test:server": "prisma generate && blitz prisma migrate deploy && blitz build && blitz start -p 3099", + "test:server": "blitz codegen && blitz prisma migrate deploy && blitz build && blitz start -p 3099", "test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run", - "test:migrate": "prisma generate && blitz prisma migrate deploy", "test:jest": "jest --passWithNoTests", "test": "yarn test:jest && yarn test:e2e" }, From 85cbf0b2f9b8702f1e55eff44b156312ce8ac67b Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Wed, 13 Oct 2021 12:11:18 +0200 Subject: [PATCH 8/9] add prisma generate --- examples/cypress/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cypress/package.json b/examples/cypress/package.json index 4859427019..ab24a8a731 100644 --- a/examples/cypress/package.json +++ b/examples/cypress/package.json @@ -9,10 +9,10 @@ "lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .", "cy:open": "cypress open", "cy:run": "cypress run", - "test:server": "blitz codegen && blitz prisma migrate deploy && blitz build && blitz start -p 3099", + "test:server": "blitz prisma migrate deploy && blitz build && blitz start -p 3099", "test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run", "test:jest": "jest --passWithNoTests", - "test": "yarn test:jest && yarn test:e2e" + "test": "blitz codegen && prisma generate && yarn test:jest && yarn test:e2e" }, "prisma": { "schema": "db/schema.prisma" From 887862106effa96bc70cc279c8286fd465a143a3 Mon Sep 17 00:00:00 2001 From: Aleksandra Date: Wed, 13 Oct 2021 12:40:51 +0200 Subject: [PATCH 9/9] remove not needed files --- examples/cypress/app/api/.keep | 0 examples/cypress/integrations/.keep | 0 examples/cypress/mailers/.keep | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 examples/cypress/app/api/.keep delete mode 100644 examples/cypress/integrations/.keep delete mode 100644 examples/cypress/mailers/.keep diff --git a/examples/cypress/app/api/.keep b/examples/cypress/app/api/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/cypress/integrations/.keep b/examples/cypress/integrations/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/cypress/mailers/.keep b/examples/cypress/mailers/.keep deleted file mode 100644 index e69de29bb2..0000000000