1
0
mirror of synced 2026-02-07 21:00:08 -05:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Brandon Bayer
767926b34a change db in auth example 2021-01-01 17:30:19 -05:00
233 changed files with 2850 additions and 4033 deletions

View File

@@ -1743,116 +1743,6 @@
"contributions": [
"doc"
]
},
{
"login": "linbudu599",
"name": "Linbudu",
"avatar_url": "https://avatars0.githubusercontent.com/u/48507806?v=4",
"profile": "https://linbudu.top/",
"contributions": [
"doc"
]
},
{
"login": "creimers",
"name": "C Reimers",
"avatar_url": "https://avatars0.githubusercontent.com/u/6090492?v=4",
"profile": "http://www.superservice-international.com",
"contributions": [
"doc"
]
},
{
"login": "kyken",
"name": "Tsuyoshi Osawa",
"avatar_url": "https://avatars2.githubusercontent.com/u/20137120?v=4",
"profile": "https://github.com/kyken",
"contributions": [
"code"
]
},
{
"login": "rembrandtreyes",
"name": "Rembrandt Reyes",
"avatar_url": "https://avatars1.githubusercontent.com/u/15057964?v=4",
"profile": "https://rembrandtreyes.com/",
"contributions": [
"code",
"doc",
"test"
]
},
{
"login": "doi-t",
"name": "Toshiya Doi",
"avatar_url": "https://avatars2.githubusercontent.com/u/5877477?v=4",
"profile": "https://doi-t.net",
"contributions": [
"doc"
]
},
{
"login": "koolii",
"name": "t.kuriyama",
"avatar_url": "https://avatars1.githubusercontent.com/u/3866581?v=4",
"profile": "https://www.koolii.net/",
"contributions": [
"code"
]
},
{
"login": "malkomalko",
"name": "Robert Malko",
"avatar_url": "https://avatars3.githubusercontent.com/u/763?v=4",
"profile": "https://github.com/malkomalko",
"contributions": [
"code"
]
},
{
"login": "ranjan-purbey",
"name": "Ranjan Purbey",
"avatar_url": "https://avatars3.githubusercontent.com/u/6953187?v=4",
"profile": "https://github.com/ranjan-purbey",
"contributions": [
"code"
]
},
{
"login": "tarunama",
"name": "tarunama",
"avatar_url": "https://avatars3.githubusercontent.com/u/6047881?v=4",
"profile": "https://github.com/tarunama",
"contributions": [
"code"
]
},
{
"login": "bacongravy",
"name": "David Kramer",
"avatar_url": "https://avatars3.githubusercontent.com/u/16848768?v=4",
"profile": "http://www.bacongravy.net/",
"contributions": [
"code"
]
},
{
"login": "mikeesto",
"name": "Michael Esteban",
"avatar_url": "https://avatars1.githubusercontent.com/u/21051488?v=4",
"profile": "https://mikeesto.com",
"contributions": [
"doc"
]
},
{
"login": "marina-ki",
"name": "marina",
"avatar_url": "https://avatars0.githubusercontent.com/u/54174518?v=4",
"profile": "https://github.com/marina-ki",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 7,

View File

@@ -6,7 +6,7 @@
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
</a>
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-196-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-184-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/canary/LICENSE">
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
@@ -217,7 +217,6 @@ _Issue triage, pull request triage, community encouragement and moderation, etc_
<td align="center"><a href="https://twitter.com/myrondavis"><img src="https://avatars2.githubusercontent.com/u/1430136?v=4" width="100px;" alt=""/><br /><sub><b>Myron Davis</b></sub></a></td>
<td align="center"><a href="https://flavioander.com/"><img src="https://avatars2.githubusercontent.com/u/14948074?s=460&u=31d7ea58b5c5cd9f724d684ed578f68896c4af71&v=4" width="100px;" alt=""/><br /><sub><b>Flavio Andrade</b></sub></a></td>
<td align="center"><a href="https://twitter.com/NaReto1125_"><img src="https://avatars.githubusercontent.com/reo777" width="100px;" alt=""/><br /><sub><b>Reo Ishiyama</b></sub></a></td>
<td align="center"><a href="https://github.com/malkomalko"><img src="https://avatars.githubusercontent.com/malkomalko" width="100px;" alt=""/><br /><sub><b>Robert Malko</b></sub></a></td>
</tr>
</table>
<!-- markdownlint-enable -->
@@ -471,20 +470,6 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<tr>
<td align="center"><a href="https://github.com/phillipkregg"><img src="https://avatars0.githubusercontent.com/u/1066044?v=4" width="100px;" alt=""/><br /><sub><b>phillipkregg</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=phillipkregg" title="Documentation">📖</a></td>
<td align="center"><a href="http://timothyreynolds.co.uk"><img src="https://avatars1.githubusercontent.com/u/168870?v=4" width="100px;" alt=""/><br /><sub><b>Tim Reynolds</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=timReynolds" title="Documentation">📖</a></td>
<td align="center"><a href="https://linbudu.top/"><img src="https://avatars0.githubusercontent.com/u/48507806?v=4" width="100px;" alt=""/><br /><sub><b>Linbudu</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=linbudu599" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.superservice-international.com"><img src="https://avatars0.githubusercontent.com/u/6090492?v=4" width="100px;" alt=""/><br /><sub><b>C Reimers</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=creimers" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/kyken"><img src="https://avatars2.githubusercontent.com/u/20137120?v=4" width="100px;" alt=""/><br /><sub><b>Tsuyoshi Osawa</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=kyken" title="Code">💻</a></td>
<td align="center"><a href="https://rembrandtreyes.com/"><img src="https://avatars1.githubusercontent.com/u/15057964?v=4" width="100px;" alt=""/><br /><sub><b>Rembrandt Reyes</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=rembrandtreyes" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=rembrandtreyes" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=rembrandtreyes" title="Tests">⚠️</a></td>
<td align="center"><a href="https://doi-t.net"><img src="https://avatars2.githubusercontent.com/u/5877477?v=4" width="100px;" alt=""/><br /><sub><b>Toshiya Doi</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doi-t" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.koolii.net/"><img src="https://avatars1.githubusercontent.com/u/3866581?v=4" width="100px;" alt=""/><br /><sub><b>t.kuriyama</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=koolii" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/malkomalko"><img src="https://avatars3.githubusercontent.com/u/763?v=4" width="100px;" alt=""/><br /><sub><b>Robert Malko</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=malkomalko" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ranjan-purbey"><img src="https://avatars3.githubusercontent.com/u/6953187?v=4" width="100px;" alt=""/><br /><sub><b>Ranjan Purbey</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=ranjan-purbey" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tarunama"><img src="https://avatars3.githubusercontent.com/u/6047881?v=4" width="100px;" alt=""/><br /><sub><b>tarunama</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=tarunama" title="Code">💻</a></td>
<td align="center"><a href="http://www.bacongravy.net/"><img src="https://avatars3.githubusercontent.com/u/16848768?v=4" width="100px;" alt=""/><br /><sub><b>David Kramer</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bacongravy" title="Code">💻</a></td>
<td align="center"><a href="https://mikeesto.com"><img src="https://avatars1.githubusercontent.com/u/21051488?v=4" width="100px;" alt=""/><br /><sub><b>Michael Esteban</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=mikeesto" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/marina-ki"><img src="https://avatars0.githubusercontent.com/u/54174518?v=4" width="100px;" alt=""/><br /><sub><b>marina</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=marina-ki" title="Documentation">📖</a></td>
</tr>
</table>

View File

@@ -1,3 +1,15 @@
module.exports = {
extends: ["blitz"],
env: {
es2020: true,
"cypress/globals": true,
},
extends: ["react-app", "plugin:jsx-a11y/recommended"],
plugins: ["jsx-a11y", "cypress"],
rules: {
"import/no-anonymous-default-export": "error",
"import/no-webpack-loader-syntax": "off",
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
"jsx-a11y/label-has-associated-control": "off", //Doesn't play well with form libraries
},
}

View File

@@ -14,7 +14,7 @@ model Project {
2. DB migrate
```
blitz prisma migrate dev --preview-feature
blitz db migrate
```
3. Start the dev server

View File

@@ -0,0 +1,39 @@
import {AuthenticationError} from "blitz"
import SecurePassword from "secure-password"
import db from "db"
const SP = () => new SecurePassword()
export const hashPassword = async (password: string) => {
const hashedBuffer = await SP().hash(Buffer.from(password))
return hashedBuffer.toString("base64")
}
export const verifyPassword = async (hashedPassword: string, password: string) => {
try {
return await SP().verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"))
} catch (error) {
console.error(error)
return false
}
}
export const authenticateUser = async (email: string, password: string) => {
const user = await db().user.findFirst({where: {email}})
if (!user || !user.hashedPassword) throw new AuthenticationError()
switch (await verifyPassword(user.hashedPassword, password)) {
case SecurePassword.VALID:
break
case SecurePassword.VALID_NEEDS_REHASH:
// Upgrade hashed password with a more secure hash
const improvedHash = await hashPassword(password)
await db().user.update({where: {id: user.id}, data: {hashedPassword: improvedHash}})
break
default:
throw new AuthenticationError()
}
const {hashedPassword, ...rest} = user
return rest
}

View File

@@ -1,23 +1,7 @@
import {Ctx, SecurePassword, AuthenticationError} from "blitz"
import db from "db"
import {Ctx} from "blitz"
import {authenticateUser} from "app/auth/auth-utils"
import * as z from "zod"
export const authenticateUser = async (email: string, password: string) => {
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 const LoginInput = z.object({
email: z.string().email(),
password: z.string(),

View File

@@ -1,5 +1,6 @@
import {Ctx, SecurePassword} from "blitz"
import {Ctx} from "blitz"
import db from "db"
import {hashPassword} from "app/auth/auth-utils"
import * as z from "zod"
export const SignupInput = z.object({
@@ -12,8 +13,8 @@ export default async function signup(input: SignupInputType, {session}: Ctx) {
// This throws an error if input is invalid
const {email, password} = SignupInput.parse(input)
const hashedPassword = await SecurePassword.hash(password)
const user = await db.user.create({
const hashedPassword = await hashPassword(password)
const user = await db().user.create({
data: {email, hashedPassword, role: "user"},
select: {id: true, name: true, email: true, role: true},
})

View File

@@ -4,7 +4,7 @@ import db from "db"
export default async function getCurrentUser(_ = null, ctx: Ctx) {
if (!ctx.session.userId) return null
const user = await db.user.findFirst({
const user = await db().user.findFirst({
where: {id: ctx.session.userId},
select: {id: true, name: true, email: true, role: true},
})

View File

@@ -8,7 +8,7 @@ type GetUserInput = {
export default async function getUser({where}: GetUserInput, ctx: Ctx) {
ctx.session.authorize()
const user = await db.user.findFirst({where})
const user = await db().user.findFirst({where})
if (!user) throw new NotFoundError(`User with id ${where.id} does not exist`)

View File

@@ -6,14 +6,14 @@ type GetUsersInput = Pick<FindManyUserArgs, "where" | "orderBy" | "skip" | "take
export default async function getUsers({where, orderBy, skip = 0, take}: GetUsersInput, ctx: Ctx) {
ctx.session.authorize()
const users = await db.user.findMany({
const users = await db().user.findMany({
where,
orderBy,
take,
skip,
})
const count = await db.user.count()
const count = await db().user.count()
const hasMore = typeof take === "number" ? skip + take < count : false
const nextPage = hasMore ? {take, skip: skip + take!} : null

View File

@@ -1,7 +1,7 @@
import {makeServerOnlyPrisma} from "blitz"
import {PrismaClient} from "@prisma/client"
const ServerOnlyPrisma = makeServerOnlyPrisma(PrismaClient)
export * from "@prisma/client"
export default new ServerOnlyPrisma()
export default function db(): PrismaClient {
globalThis.prisma = globalThis.prisma || new PrismaClient()
return globalThis.prisma
}

View File

@@ -0,0 +1,82 @@
# Migration `20200704211726`
This migration has been generated by Brandon Bayer at 7/4/2020, 9:17:26 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "quaint"."User" (
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP ,"email" TEXT NOT NULL ,"hashedPassword" TEXT NOT NULL ,"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"name" TEXT ,"role" TEXT NOT NULL ,"updatedAt" DATE NOT NULL )
CREATE TABLE "quaint"."Session" (
"antiCSRFToken" TEXT ,"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP ,"expiresAt" DATE ,"handle" TEXT NOT NULL ,"hashedSessionToken" TEXT ,"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"privateData" TEXT ,"publicData" TEXT ,"updatedAt" DATE NOT NULL ,"userId" INTEGER ,FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE)
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
CREATE UNIQUE INDEX "quaint"."Session.handle" ON "Session"("handle")
PRAGMA "quaint".foreign_key_check;
PRAGMA foreign_keys=ON;
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20200704211726
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,46 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+datasource sqlite {
+ provider = "sqlite"
+ url = "***"
+}
+
+// SQLite is easy to start with, but if you use Postgres in production
+// you should also use it in development with the following:
+//datasource postgresql {
+// provider = "postgresql"
+// url = "***"
+//}
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+
+// --------------------------------------
+
+model User {
+ id Int @default(autoincrement()) @id
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ name String?
+ email String @unique
+ hashedPassword String
+ role String
+ sessions Session[]
+}
+
+model Session {
+ id Int @default(autoincrement()) @id
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ expiresAt DateTime?
+ handle String @unique
+ user User? @relation(fields: [userId], references: [id])
+ userId Int?
+ hashedSessionToken String?
+ antiCSRFToken String?
+ publicData String?
+ privateData String?
+}
```

View File

@@ -0,0 +1,46 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
url = "***"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
// url = "***"
//}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
hashedPassword String
role String
sessions Session[]
}
model Session {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
handle String @unique
user User? @relation(fields: [userId], references: [id])
userId Int?
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
}

View File

@@ -0,0 +1,373 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "sqlite"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "sqlite"
},
"argument": "provider",
"value": "\"sqlite\""
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "sqlite"
},
"argument": "url",
"value": "\"file:./db.sqlite\""
},
{
"tag": "CreateModel",
"model": "User"
},
{
"tag": "CreateField",
"model": "User",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "User",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "name",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "email"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "hashedPassword",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "User",
"field": "role",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateField",
"model": "User",
"field": "sessions",
"type": "Session",
"arity": "List"
},
{
"tag": "CreateModel",
"model": "Session"
},
{
"tag": "CreateField",
"model": "Session",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Session",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "Session",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Session",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "Session",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "Session",
"field": "expiresAt",
"type": "DateTime",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Session",
"field": "handle",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "handle"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "Session",
"field": "user",
"type": "User",
"arity": "Optional"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Session",
"field": "user"
},
"directive": "relation"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Session",
"field": "user"
},
"directive": "relation"
},
"argument": "fields",
"value": "[userId]"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Session",
"field": "user"
},
"directive": "relation"
},
"argument": "references",
"value": "[id]"
},
{
"tag": "CreateField",
"model": "Session",
"field": "userId",
"type": "Int",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Session",
"field": "hashedSessionToken",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Session",
"field": "antiCSRFToken",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Session",
"field": "publicData",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Session",
"field": "privateData",
"type": "String",
"arity": "Optional"
}
]
}

View File

@@ -0,0 +1,66 @@
# Migration `20200711145940`
This migration has been generated by Brandon Bayer at 7/11/2020, 2:59:40 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "quaint"."new_User" (
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP ,"email" TEXT NOT NULL ,"hashedPassword" TEXT ,"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"name" TEXT ,"role" TEXT NOT NULL DEFAULT 'user' ,"updatedAt" DATE NOT NULL )
INSERT INTO "quaint"."new_User" ("createdAt", "email", "hashedPassword", "id", "name", "role", "updatedAt") SELECT "createdAt", "email", "hashedPassword", "id", "name", "role", "updatedAt" FROM "quaint"."User"
PRAGMA foreign_keys=off;
DROP TABLE "quaint"."User";;
PRAGMA foreign_keys=on
ALTER TABLE "quaint"."new_User" RENAME TO "User";
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
PRAGMA "quaint".foreign_key_check;
PRAGMA foreign_keys=ON;
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20200704211726..20200711145940
--- datamodel.dml
+++ datamodel.dml
@@ -2,16 +2,16 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
- url = "***"
+ url = "***"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
-// url = "***"
+// url = "***"
//}
generator client {
provider = "prisma-client-js"
@@ -25,10 +25,10 @@
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
- hashedPassword String
- role String
+ hashedPassword String?
+ role String @default("user")
sessions Session[]
}
model Session {
```

View File

@@ -0,0 +1,46 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
url = "***"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
// url = "***"
//}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
hashedPassword String?
role String @default("user")
sessions Session[]
}
model Session {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
handle String @unique
user User? @relation(fields: [userId], references: [id])
userId Int?
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
}

View File

@@ -0,0 +1,36 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "UpdateField",
"model": "User",
"field": "hashedPassword",
"arity": "Optional"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "role"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "role"
},
"directive": "default"
},
"argument": "",
"value": "\"user\""
}
]
}

View File

@@ -1,32 +0,0 @@
-- 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,
"userId" INTEGER,
"hashedSessionToken" TEXT,
"antiCSRFToken" TEXT,
"publicData" TEXT,
"privateData" TEXT,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Session.handle_unique" ON "Session"("handle");

View File

@@ -0,0 +1,4 @@
# Prisma Migrate lockfile v1
20200704211726
20200711145940

View File

@@ -1,6 +0,0 @@
import db from "./index"
const seed = async () => {
const user = await db.user.create({data: {name: "FooBar", email: "hey@" + new Date().getTime()}})
console.log("Created user", user)
}
export default seed

View File

@@ -1,3 +1,30 @@
const {pathsToModuleNameMapper} = require("ts-jest/utils")
const {compilerOptions} = require("./tsconfig")
module.exports = {
preset: "blitz",
// Test setup file
setupFilesAfterEnv: ["<rootDir>/test/setup.ts"],
// Add type checking to Typescript test files
preset: "ts-jest",
testEnvironment: "jest-environment-jsdom-fourteen",
// Automatically clear mock calls and instances between every test
clearMocks: true,
testPathIgnorePatterns: ["/node_modules/", "/.blitz/", "/.next/", "<rootDir>/db/migrations"],
transformIgnorePatterns: ["[/\\\\]node_modules[/\\\\].+\\.(ts|tsx)$"],
transform: {
"^.+\\.(ts|tsx)$": "babel-jest",
},
// This makes absolute imports work
moduleDirectories: ["node_modules", "<rootDir>"],
modulePathIgnorePatterns: ["<rootDir>/.blitz", "<rootDir>/.next", "<rootDir>/cypress"],
moduleNameMapper: {
// This ensures any path aliases in tsconfig also work in jest
...pathsToModuleNameMapper(compilerOptions.paths || {}),
"\\.(css|less|sass|scss)$": "identity-obj-proxy",
"\\.(gif|ttf|eot|svg|png|jpg|jpeg)$": "<rootDir>/test/__mocks__/fileMock.js",
},
watchPlugins: ["jest-watch-typeahead/filename", "jest-watch-typeahead/testname"],
// Coverage output
coverageDirectory: ".coverage",
collectCoverageFrom: ["**/*.{js,jsx,ts,tsx}", "!**/*.d.ts", "!**/node_modules/**"],
}

View File

@@ -1,9 +1,9 @@
{
"name": "@examples/auth",
"version": "0.29.2",
"version": "0.28.0-canary.1",
"scripts": {
"start": "blitz start",
"studio": "blitz prisma studio",
"studio": "blitz db studio",
"build": "blitz build",
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"analyze": "cross-env ANALYZE=true blitz build",
@@ -11,7 +11,7 @@
"cy:run": "cypress run",
"test": "prisma generate && yarn test:jest && yarn test:e2e",
"test:jest": "jest",
"test:server": "blitz prisma migrate deploy --preview-feature && blitz build && blitz start --production -p 3099",
"test:server": "blitz db migrate && blitz build && blitz start --production -p 3099",
"test:e2e": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run"
},
"browserslist": [
@@ -38,9 +38,9 @@
]
},
"dependencies": {
"@prisma/cli": "2.14.0",
"@prisma/client": "2.14.0",
"blitz": "0.29.2",
"@prisma/cli": "2.12.0",
"@prisma/client": "2.12.0",
"blitz": "0.28.0-canary.1",
"final-form": "4.20.1",
"passport-auth0": "1.4.0",
"passport-github2": "0.1.12",
@@ -49,25 +49,43 @@
"react-dom": "0.0.0-experimental-3310209d0",
"react-error-boundary": "3.1.0",
"react-final-form": "6.5.2",
"secure-password": "4.0.0",
"zod": "1.11.11"
},
"devDependencies": {
"@cypress/skip-test": "2.5.1",
"@next/bundle-analyzer": "latest",
"@testing-library/react": "11.2.3",
"@testing-library/react-hooks": "4.0.1",
"@testing-library/jest-dom": "5.11.8",
"@testing-library/react": "11.2.2",
"@testing-library/react-hooks": "3.7.0",
"@types/jest": "26.0.19",
"@types/passport-auth0": "1.0.4",
"@types/passport-github2": "1.2.4",
"@types/passport-twitter": "1.0.36",
"@types/react": "17.0.0",
"@types/secure-password": "3.1.0",
"@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1",
"babel-eslint": "10.1.0",
"cross-env": "7.0.3",
"cypress": "6.2.1",
"eslint": "7.17.0",
"husky": "4.3.7",
"cypress": "6.2.0",
"eslint": "7.16.0",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-cypress": "2.11.2",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.6",
"jest": "26.6.3",
"jest-environment-jsdom-fourteen": "1.0.1",
"jest-watch-typeahead": "0.6.1",
"lint-staged": "10.5.3",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",
"start-server-and-test": "1.11.7",
"ts-jest": "26.4.4",
"typescript": "4.1.3"
},
"private": true

View File

@@ -1,2 +1,6 @@
// This is the jest 'setupFilesAfterEnv' setup file
// It's a good place to set globals, add global before/after hooks, etc
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom/extend-expect"
require("dotenv-flow").config({silent: true})

View File

@@ -73,7 +73,6 @@ export const mockRouter: BlitzRouter = {
asPath: "/",
params: {},
query: {},
isReady: true,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -1,2 +0,0 @@
# This env file should be checked into source control
# This is the place for default values that should be used in all environments

View File

@@ -1 +0,0 @@
SESSION_SECRET_KEY=A4B979A108440A218DE8F11CBB13A7B0

View File

@@ -1,14 +0,0 @@
module.exports = {
parser: "@typescript-eslint/parser",
env: {
es2020: true,
},
extends: ["react-app", "plugin:jsx-a11y/recommended"],
plugins: ["@typescript-eslint", "jsx-a11y"],
rules: {
"import/no-anonymous-default-export": "error",
"import/no-webpack-loader-syntax": "off",
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
},
}

View File

@@ -1,55 +0,0 @@
# dependencies
node_modules
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.pnp.*
.npm
web_modules/
# blitz
/.blitz/
/.next/
*.sqlite
.now
.blitz-console-history
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
# Custom build
build

View File

@@ -1 +0,0 @@
save-exact=true

View File

@@ -1,7 +0,0 @@
.gitkeep
.env*
*.ico
*.lock
db/migrations
.next
.blitz

View File

@@ -1,29 +0,0 @@
# custom-server
## Development
1. Install
```sh
yarn
```
2. Migrate
```sh
blitz prisma migrate dev --preview-feature
```
3. Start the dev server
```sh
blitz start
// Or if you want hot-reloading of server.js, use:
yarn start
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Production
```sh
blitz build
blitz start --production
```

View File

@@ -1,50 +0,0 @@
import React from "react"
import { AuthenticationError, Link, useMutation } from "blitz"
import { LabeledTextField } from "app/components/LabeledTextField"
import { Form, FORM_ERROR } from "app/components/Form"
import login from "app/auth/mutations/login"
import { LoginInput } from "app/auth/validations"
type LoginFormProps = {
onSuccess?: () => void
}
export const LoginForm = (props: LoginFormProps) => {
const [loginMutation] = useMutation(login)
return (
<div>
<h1>Login</h1>
<Form
submitText="Login"
schema={LoginInput}
initialValues={{ email: "", password: "" }}
onSubmit={async (values) => {
try {
await loginMutation(values)
props.onSuccess?.()
} catch (error) {
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(),
}
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
</Form>
<div style={{ marginTop: "1rem" }}>
Or <Link href="/signup">Sign Up</Link>
</div>
</div>
)
}
export default LoginForm

View File

@@ -1,44 +0,0 @@
import React from "react"
import { useMutation } from "blitz"
import { LabeledTextField } from "app/components/LabeledTextField"
import { Form, FORM_ERROR } from "app/components/Form"
import signup from "app/auth/mutations/signup"
import { SignupInput } from "app/auth/validations"
type SignupFormProps = {
onSuccess?: () => void
}
export const SignupForm = (props: SignupFormProps) => {
const [signupMutation] = useMutation(signup)
return (
<div>
<h1>Create an Account</h1>
<Form
submitText="Create Account"
schema={SignupInput}
initialValues={{ email: "", password: "" }}
onSubmit={async (values) => {
try {
await signupMutation(values)
props.onSuccess?.()
} catch (error) {
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() }
}
}
}}
>
<LabeledTextField name="email" label="Email" placeholder="Email" />
<LabeledTextField name="password" label="Password" placeholder="Password" type="password" />
</Form>
</div>
)
}
export default SignupForm

View File

@@ -1,15 +0,0 @@
import { Ctx } from "blitz"
import { authenticateUser } from "app/auth/auth-utils"
import { LoginInput, LoginInputType } from "../validations"
export default async function login(input: LoginInputType, { session }: Ctx) {
// This throws an error if input is invalid
const { email, password } = LoginInput.parse(input)
// This throws an error if credentials are invalid
const user = await authenticateUser(email, password)
await session.create({ userId: user.id, roles: [user.role] })
return user
}

View File

@@ -1,5 +0,0 @@
import { Ctx } from "blitz"
export default async function logout(_: any, { session }: Ctx) {
return await session.revoke()
}

View File

@@ -1,19 +0,0 @@
import { Ctx } from "blitz"
import db from "db"
import { hashPassword } from "app/auth/auth-utils"
import { SignupInput, SignupInputType } from "app/auth/validations"
export default async function signup(input: SignupInputType, { session }: Ctx) {
// This throws an error if input is invalid
const { email, password } = SignupInput.parse(input)
const hashedPassword = await hashPassword(password)
const user = await db.user.create({
data: { email: email.toLowerCase(), hashedPassword, role: "user" },
select: { id: true, name: true, email: true, role: true },
})
await session.create({ userId: user.id, roles: [user.role] })
return user
}

View File

@@ -1,18 +0,0 @@
import React from "react"
import { useRouter, BlitzPage } from "blitz"
import Layout from "app/layouts/Layout"
import { LoginForm } from "app/auth/components/LoginForm"
const LoginPage: BlitzPage = () => {
const router = useRouter()
return (
<div>
<LoginForm onSuccess={() => router.push("/")} />
</div>
)
}
LoginPage.getLayout = (page) => <Layout title="Log In">{page}</Layout>
export default LoginPage

View File

@@ -1,18 +0,0 @@
import React from "react"
import { useRouter, BlitzPage } from "blitz"
import Layout from "app/layouts/Layout"
import { SignupForm } from "app/auth/components/SignupForm"
const SignupPage: BlitzPage = () => {
const router = useRouter()
return (
<div>
<SignupForm onSuccess={() => router.push("/")} />
</div>
)
}
SignupPage.getLayout = (page) => <Layout title="Sign Up">{page}</Layout>
export default SignupPage

View File

@@ -1,13 +0,0 @@
import * as z from "zod"
export const SignupInput = z.object({
email: z.string().email(),
password: z.string().min(10).max(100),
})
export type SignupInputType = z.infer<typeof SignupInput>
export const LoginInput = z.object({
email: z.string().email(),
password: z.string(),
})
export type LoginInputType = z.infer<typeof LoginInput>

View File

@@ -1,62 +0,0 @@
import React, { ReactNode, PropsWithoutRef } from "react"
import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form"
import * as z from "zod"
export { FORM_ERROR } from "final-form"
type FormProps<S extends z.ZodType<any, any>> = {
/** All your form fields */
children: ReactNode
/** Text to display in the submit button */
submitText: string
schema?: S
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
} & Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit">
export function Form<S extends z.ZodType<any, any>>({
children,
submitText,
schema,
initialValues,
onSubmit,
...props
}: FormProps<S>) {
return (
<FinalForm
initialValues={initialValues}
validate={(values) => {
if (!schema) return
try {
schema.parse(values)
} catch (error) {
return error.formErrors.fieldErrors
}
}}
onSubmit={onSubmit}
render={({ handleSubmit, submitting, submitError }) => (
<form onSubmit={handleSubmit} className="form" {...props}>
{/* Form fields supplied as children are rendered here */}
{children}
{submitError && (
<div role="alert" style={{ color: "red" }}>
{submitError}
</div>
)}
<button type="submit" disabled={submitting}>
{submitText}
</button>
<style global jsx>{`
.form > * + * {
margin-top: 1rem;
}
`}</style>
</form>
)}
/>
)
}
export default Form

View File

@@ -1,57 +0,0 @@
import React, { PropsWithoutRef } from "react"
import { useField } from "react-final-form"
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
/** Field name. */
name: string
/** Field label. */
label: string
/** Field type. Doesn't include radio buttons and checkboxes */
type?: "text" | "password" | "email" | "number"
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
}
export const LabeledTextField = React.forwardRef<HTMLInputElement, LabeledTextFieldProps>(
({ name, label, outerProps, ...props }, ref) => {
const {
input,
meta: { touched, error, submitError, submitting },
} = useField(name)
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
return (
<div {...outerProps}>
<label>
{label}
<input {...input} disabled={submitting} {...props} ref={ref} />
</label>
{touched && normalizedError && (
<div role="alert" style={{ color: "red" }}>
{normalizedError}
</div>
)}
<style jsx>{`
label {
display: flex;
flex-direction: column;
align-items: start;
font-size: 1rem;
}
input {
font-size: 1rem;
padding: 0.25rem 0.5rem;
border-radius: 3px;
border: 1px solid purple;
appearance: none;
margin-top: 0.5rem;
}
`}</style>
</div>
)
}
)
export default LabeledTextField

View File

@@ -1,10 +0,0 @@
import { useQuery, useSession } from "blitz"
import getCurrentUser from "app/users/queries/getCurrentUser"
export const useCurrentUser = () => {
// We wouldn't have to useSession() here, but doing so improves perf on initial
// load since we can skip the getCurrentUser() request.
const session = useSession()
const [user] = useQuery(getCurrentUser, null, { enabled: !!session.userId })
return session.userId ? user : null
}

View File

@@ -1,22 +0,0 @@
import { ReactNode } from "react"
import { Head } from "blitz"
type LayoutProps = {
title?: string
children: ReactNode
}
const Layout = ({ title, children }: LayoutProps) => {
return (
<>
<Head>
<title>{title || "custom-server"}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
{children}
</>
)
}
export default Layout

View File

@@ -1,19 +0,0 @@
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 (
<>
<Head>
<title>
{statusCode}: {title}
</title>
</Head>
<ErrorComponent statusCode={statusCode} title={title} />
</>
)
}

View File

@@ -1,43 +0,0 @@
import { AppProps, ErrorComponent, useRouter, AuthenticationError, AuthorizationError } from "blitz"
import { ErrorBoundary, FallbackProps } from "react-error-boundary"
import { queryCache } from "react-query"
import LoginForm from "app/auth/components/LoginForm"
export default function App({ Component, pageProps }: AppProps) {
const getLayout = Component.getLayout || ((page) => page)
const router = useRouter()
return (
<ErrorBoundary
FallbackComponent={RootErrorFallback}
resetKeys={[router.asPath]}
onReset={() => {
// This ensures the Blitz useQuery hooks will automatically refetch
// data any time you reset the error boundary
queryCache.resetErrorBoundaries()
}}
>
{getLayout(<Component {...pageProps} />)}
</ErrorBoundary>
)
}
function RootErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
if (error instanceof AuthenticationError) {
return <LoginForm onSuccess={resetErrorBoundary} />
} else if (error instanceof AuthorizationError) {
return (
<ErrorComponent
statusCode={(error as any).statusCode}
title="Sorry, you are not authorized to access this"
/>
)
} else {
return (
<ErrorComponent
statusCode={(error as any)?.statusCode || 400}
title={error?.message || error?.name}
/>
)
}
}

View File

@@ -1,23 +0,0 @@
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 (
<Html lang="en">
<DocumentHead />
<body>
<Main />
<BlitzScript />
</body>
</Html>
)
}
}
export default MyDocument

View File

@@ -1,26 +0,0 @@
import React from "react"
import { render } from "test/utils"
import Home from "./index"
import { useCurrentUser } from "app/hooks/useCurrentUser"
jest.mock("app/hooks/useCurrentUser")
const mockUseCurrentUser = useCurrentUser as jest.MockedFunction<typeof useCurrentUser>
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(<Home />)
const linkElement = getByText(/Documentation/i)
expect(linkElement).toBeInTheDocument()
})

View File

@@ -1,272 +0,0 @@
import {Link, BlitzPage, useMutation} from "blitz"
import Layout from "app/layouts/Layout"
import logout from "app/auth/mutations/logout"
import {useCurrentUser} from "app/hooks/useCurrentUser"
import {Suspense} from "react"
/*
* 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 (
<>
<button
className="button small"
onClick={async () => {
await logoutMutation()
}}
>
Logout
</button>
<div>
User id: <code>{currentUser.id}</code>
<br />
User role: <code>{currentUser.role}</code>
</div>
</>
)
} else {
return (
<>
<Link href="/signup">
<a className="button small">
<strong>Sign Up</strong>
</a>
</Link>
<Link href="/login">
<a className="button small">
<strong>Login</strong>
</a>
</Link>
</>
)
}
}
const Home: BlitzPage = () => {
return (
<div className="container">
<main>
<div className="logo">
<img src="/logo.png" alt="blitz.js" />
</div>
<p>
<strong>Congrats!</strong> Your app is ready, including user sign-up and log-in.
</p>
<div className="buttons" style={{marginTop: "1rem", marginBottom: "1rem"}}>
<Suspense fallback="Loading...">
<UserInfo />
</Suspense>
</div>
<p>
<strong>
To add a new model to your app, <br />
run the following in your terminal:
</strong>
</p>
<pre>
<code>blitz generate all project name:string</code>
</pre>
<pre>
<code>blitz prisma migrate dev --preview-feature</code>
</pre>
<div>
<p>
Then <strong>restart the server</strong>
</p>
<pre>
<code>Ctrl + c</code>
</pre>
<pre>
<code>blitz start</code>
</pre>
<p>
and go to{" "}
<Link href="/projects">
<a>/projects</a>
</Link>
</p>
</div>
<div className="buttons" style={{marginTop: "5rem"}}>
<a
className="button"
href="https://blitzjs.com/docs/getting-started?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
<a
className="button-outline"
href="https://github.com/blitz-js/blitz"
target="_blank"
rel="noopener noreferrer"
>
Github Repo
</a>
<a
className="button-outline"
href="https://slack.blitzjs.com"
target="_blank"
rel="noopener noreferrer"
>
Slack Community
</a>
</div>
</main>
<footer>
<a
href="https://blitzjs.com?utm_source=blitz-new&utm_medium=app-template&utm_campaign=blitz-new"
target="_blank"
rel="noopener noreferrer"
>
Powered by Blitz.js
</a>
</footer>
<style jsx global>{`
@import url("https://fonts.googleapis.com/css2?family=Libre+Franklin:wght@300;700&display=swap");
html,
body {
padding: 0;
margin: 0;
font-family: "Libre Franklin", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main p {
font-size: 1.2rem;
}
p {
text-align: center;
}
footer {
width: 100%;
height: 60px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
background-color: #45009d;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
footer a {
color: #f4f4f4;
text-decoration: none;
}
.logo {
margin-bottom: 2rem;
}
.logo img {
width: 300px;
}
.buttons {
display: grid;
grid-auto-flow: column;
grid-gap: 0.5rem;
}
.button {
font-size: 1rem;
background-color: #6700eb;
padding: 1rem 2rem;
color: #f4f4f4;
text-align: center;
}
.button.small {
padding: 0.5rem 1rem;
}
.button:hover {
background-color: #45009d;
}
.button-outline {
border: 2px solid #6700eb;
padding: 1rem 2rem;
color: #6700eb;
text-align: center;
}
.button-outline:hover {
border-color: #45009d;
color: #45009d;
}
pre {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
text-align: center;
}
code {
font-size: 0.9rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
</div>
)
}
Home.getLayout = (page) => <Layout title="Home">{page}</Layout>
export default Home

View File

@@ -1,13 +0,0 @@
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.findOne({
where: { id: session.userId },
select: { id: true, name: true, email: true, role: true },
})
return user
}

View File

@@ -1,4 +0,0 @@
module.exports = {
presets: ["next/babel"],
plugins: [],
}

View File

@@ -1,17 +0,0 @@
const { sessionMiddleware, simpleRolesIsAuthorized } = require("@blitzjs/server")
module.exports = {
middleware: [
sessionMiddleware({
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
},
*/
}

View File

@@ -1,6 +0,0 @@
{
"baseUrl": "http://localhost:3099",
"defaultCommandTimeout": 10000,
"video": false,
"chromeWebSecurity": false
}

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -1,8 +0,0 @@
/// <reference types="Cypress" />
/// <reference types="@cypress/skip-test" />
declare namespace Cypress {
interface Chainable {
signup(user: { email: string; password: string }): void
}
}

View File

@@ -1,55 +0,0 @@
import { createRandomUser } from "../support/helpers"
describe("index page", () => {
beforeEach(() => {
cy.visit("/")
})
it("goes to the signup page", () => {
cy.contains("a", "Sign Up").click()
cy.location("pathname").should("equal", "/signup")
})
it("goes to the login page", () => {
cy.contains("a", /login/i).click()
cy.location("pathname").should("equal", "/login")
})
it("allows the user to signup", () => {
const user = createRandomUser()
cy.signup(user)
cy.location("pathname").should("equal", "/")
cy.contains("button", "Logout")
})
it("allows the user to log in", () => {
const user = createRandomUser()
cy.signup(user)
cy.contains("button", "Logout").click()
cy.contains("a", /login/i).click()
cy.contains("Email").find("input").type(user.email)
cy.contains("Password").find("input").type(user.password)
cy.contains("button", /login/i).click()
cy.location("pathname").should("equal", "/")
cy.contains("button", "Logout")
})
it("allows the user to logout", () => {
const user = createRandomUser()
cy.signup(user)
cy.contains("button", "Logout").click()
cy.location("pathname").should("equal", "/")
cy.contains("a", /login/i)
})
})
export {}

View File

@@ -1,22 +0,0 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
//@ts-ignore
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -1,19 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
Cypress.Commands.add("signup", ({ email, password }) => {
cy.contains("a", "Sign Up").click()
cy.contains("Email").find("input").type(email)
cy.contains("Password").find("input").type(password)
cy.contains("button", "Create Account").click()
})

View File

@@ -1,7 +0,0 @@
export const createRandomUser = () => {
const random = Math.round(Math.random() * 100000).toString()
const email = `test_${random}@example.com`
const password = `password_${random}`
return { email, password }
}

View File

@@ -1,26 +0,0 @@
// ***********************************************************
// 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.js using ES2015 syntax:
import "./commands"
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.Screenshot.defaults({
screenshotOnRunFailure: false,
})
require("@cypress/skip-test/support")

View File

@@ -1,7 +0,0 @@
import {makeServerOnlyPrisma} from "blitz"
import {PrismaClient} from "@prisma/client"
const ServerOnlyPrisma = makeServerOnlyPrisma(PrismaClient)
export * from "@prisma/client"
export default new ServerOnlyPrisma()

View File

@@ -1,32 +0,0 @@
-- 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,
"userId" INTEGER,
"hashedSessionToken" TEXT,
"antiCSRFToken" TEXT,
"publicData" TEXT,
"privateData" TEXT,
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
);
-- CreateIndex
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Session.handle_unique" ON "Session"("handle");

View File

@@ -1,38 +0,0 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "sqlite"
url = "file:./db.sqlite"
}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String?
email String @unique
hashedPassword String?
role String @default("user")
sessions Session[]
}
model Session {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
expiresAt DateTime?
handle String @unique
user User? @relation(fields: [userId], references: [id])
userId Int?
hashedSessionToken String?
antiCSRFToken String?
publicData String?
privateData String?
}

View File

@@ -1,16 +0,0 @@
// 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

View File

@@ -1,3 +0,0 @@
module.exports = {
preset: "blitz",
}

View File

@@ -1,88 +0,0 @@
{
"name": "@examples/custom-server",
"version": "0.29.2",
"scripts": {
"start": "nodemon --watch server.js --exec 'blitz start'",
"build": "blitz build",
"studio": "blitz prisma studio",
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test-watch": "jest --watch",
"cy-open": "cypress open",
"cy-run": "cypress run",
"test:migrate": "prisma generate && blitz prisma migrate deploy --preview-feature",
"test:jest": "jest",
"test-server": "blitz build && blitz start --production",
"test:e2e": "cross-env NODE_ENV=test PORT=3099 start-server-and-test test-server http://localhost:3099 cy-run",
"test": "run-s test:*"
},
"browserslist": [
"defaults"
],
"prisma": {
"schema": "db/schema.prisma"
},
"prettier": {
"semi": false,
"printWidth": 100,
"bracketSpacing": false,
"trailingComma": "all"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged && pretty-quick --staged",
"pre-push": "tsc && npm run lint && npm run test"
}
},
"lint-staged": {
"*.{js,ts,tsx}": [
"eslint --fix"
]
},
"dependencies": {
"@prisma/cli": "2.14.0",
"@prisma/client": "2.14.0",
"blitz": "0.29.2",
"final-form": "4.20.1",
"react": "0.0.0-experimental-4ead6b530",
"react-dom": "0.0.0-experimental-4ead6b530",
"react-error-boundary": "2.3.2",
"react-final-form": "6.5.2",
"secure-password": "4.0.0",
"typescript": "4.1.3",
"zod": "1.11.11"
},
"devDependencies": {
"@cypress/skip-test": "2.5.0",
"@testing-library/jest-dom": "5.11.6",
"@testing-library/react": "11.2.3",
"@testing-library/react-hooks": "4.0.1",
"@types/jest": "26.0.20",
"@types/react": "16.14.1",
"@types/secure-password": "3.1.0",
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"babel-eslint": "10.1.0",
"cypress": "6.2.1",
"eslint": "7.17.0",
"eslint-config-react-app": "5.2.1",
"eslint-plugin-cypress": "2.11.1",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.21.5",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.7",
"jest": "26.6.3",
"jest-environment-jsdom-fourteen": "1.0.1",
"jest-watch-typeahead": "0.6.1",
"lint-staged": "10.5.1",
"nodemon": "2.0.7",
"npm-run-all": "4.1.5",
"prettier": "2.2.0",
"pretty-quick": "3.1.0",
"react-test-renderer": "16.14.0",
"start-server-and-test": "1.11.2",
"ts-jest": "26.4.4"
},
"private": true
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,25 +0,0 @@
const { createServer } = require("http")
const { parse } = require("url")
const blitz = require("@blitzjs/server")
const { log } = require("@blitzjs/display")
const { PORT = "3000" } = process.env
const dev = process.env.NODE_ENV !== "production"
const app = blitz({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
const parsedUrl = parse(req.url, true)
const { pathname } = parsedUrl
if (pathname === "/hello") {
res.writeHead(200).end("world")
return
}
handle(req, res, parsedUrl)
}).listen(PORT, () => {
log.success(`Ready on http://localhost:${PORT}`)
})
})

View File

@@ -1,2 +0,0 @@
// This is the jest 'setupFilesAfterEnv' setup file
// It's a good place to set globals, add global before/after hooks, etc

View File

@@ -1,94 +0,0 @@
import React from "react"
import { RouterContext, BlitzRouter } 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(<MyComponent />, {
// router: { pathname: '/my-custom-pathname' },
// });
// --------------------------------------------------
export function render(ui: RenderUI, { wrapper, router, ...options }: RenderOptions = {}) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }) => (
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
)
}
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, ...options }: RenderHookOptions = {}
) {
if (!wrapper) {
// Add a default context wrapper if one isn't supplied from the test
wrapper = ({ children }) => (
<RouterContext.Provider value={{ ...mockRouter, ...router }}>
{children}
</RouterContext.Provider>
)
}
return defaultRenderHook(hook, { wrapper, ...options })
}
export const mockRouter: BlitzRouter = {
basePath: "",
pathname: "/",
route: "/",
asPath: "/",
params: {},
query: {},
isReady: true,
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<typeof defaultRender>
type RenderUI = DefaultParams[0]
type RenderOptions = DefaultParams[1] & { router?: Partial<BlitzRouter> }
type DefaultHookParams = Parameters<typeof defaultRenderHook>
type RenderHook = DefaultHookParams[0]
type RenderHookOptions = DefaultHookParams[1] & { router?: Partial<BlitzRouter> }

View File

@@ -1,23 +0,0 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"baseUrl": "./",
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"strictNullChecks": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"tsBuildInfoFile": ".tsbuildinfo"
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}

View File

@@ -1,20 +0,0 @@
import { DefaultCtx, SessionContext, DefaultPublicData } from "blitz"
import { User } from "db"
import React from "react"
declare module "blitz" {
export interface Ctx extends DefaultCtx {
session: SessionContext
}
export interface PublicData extends DefaultPublicData {
userId: User["id"]
}
}
// This should not be needed. Usually it isn't but for some reason in this example it is
declare module "react" {
interface StyleHTMLAttributes<T> extends React.HTMLAttributes<T> {
jsx?: boolean
global?: boolean
}
}

View File

@@ -28,6 +28,10 @@ This example use the Fauna GraphQL API since it's more familiar to most people t
FAUNA_SECRET=YOUR_AUTH_KEY
```
```
yarn blitz db migrate
```
2. Start the dev server
```

View File

@@ -1,7 +1,23 @@
import { SecurePassword, AuthenticationError } from "blitz"
import { AuthenticationError } from "blitz"
import SecurePassword from "secure-password"
import db from "db"
import { gql } from "graphql-request"
const SP = new SecurePassword()
export const hashPassword = async (password: string) => {
const hashedBuffer = await SP.hash(Buffer.from(password))
return hashedBuffer.toString("base64")
}
export const verifyPassword = async (hashedPassword: string, password: string) => {
try {
return await SP.verify(Buffer.from(password), Buffer.from(hashedPassword, "base64"))
} catch (error) {
console.error(error)
return false
}
}
export const authenticateUser = async (email: string, password: string) => {
const { user } = await db.request(
gql`
@@ -20,26 +36,30 @@ export const authenticateUser = async (email: string, password: string) => {
if (!user || !user.hashedPassword) 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.request(
gql`
mutation UpdateUser($data: UserInput!) {
updateUser(data: $data) {
id: _id
switch (await verifyPassword(user.hashedPassword, password)) {
case SecurePassword.VALID:
break
case SecurePassword.VALID_NEEDS_REHASH:
// Upgrade hashed password with a more secure hash
const improvedHash = await hashPassword(password)
await db.request(
gql`
mutation UpdateUser($data: UserInput!) {
updateUser(data: $data) {
id: _id
}
}
`,
{
data: {
id: user.id,
hashedPassword: improvedHash,
},
}
`,
{
data: {
id: user.id,
hashedPassword: improvedHash,
},
}
)
)
break
default:
throw new AuthenticationError()
}
const { hashedPassword, ...rest } = user

View File

@@ -1,5 +1,6 @@
import { Ctx, SecurePassword } from "blitz"
import { Ctx } from "blitz"
import db from "db"
import { hashPassword } from "app/auth/auth-utils"
import { SignupInput, SignupInputType } from "app/auth/validations"
import { gql } from "graphql-request"
@@ -7,7 +8,7 @@ export default async function signup(input: SignupInputType, { session }: Ctx) {
// This throws an error if input is invalid
const { email, password } = SignupInput.parse(input)
const hashedPassword = await SecurePassword.hash(password)
const hashedPassword = await hashPassword(password)
const { user } = await db.request(
gql`
mutation createUser($email: String!, $hashedPassword: String, $role: String!) {

View File

@@ -64,6 +64,35 @@ const Home: BlitzPage = () => {
<UserInfo />
</Suspense>
</div>
<p>
<strong>
To add a new model to your app, <br />
run the following in your terminal:
</strong>
</p>
<pre>
<code>blitz generate all project name:string</code>
</pre>
<pre>
<code>blitz db migrate</code>
</pre>
<div>
<p>
Then <strong>restart the server</strong>
</p>
<pre>
<code>Ctrl + c</code>
</pre>
<pre>
<code>blitz start</code>
</pre>
<p>
and go to{" "}
<Link href="/projects">
<a>/projects</a>
</Link>
</p>
</div>
<div className="buttons" style={{ marginTop: "5rem" }}>
<a
className="button"

View File

@@ -1,9 +1,9 @@
{
"name": "@examples/fauna",
"version": "0.29.2",
"version": "0.28.0-canary.1",
"scripts": {
"start": "blitz start",
"studio": "blitz prisma studio",
"studio": "blitz db studio",
"build": "blitz build",
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test": "echo \"No tests yet\""
@@ -27,7 +27,7 @@
]
},
"dependencies": {
"blitz": "0.29.2",
"blitz": "0.28.0-canary.1",
"final-form": "4.20.1",
"graphql": "15.4.0",
"graphql-request": "3.4.0",
@@ -40,22 +40,22 @@
},
"devDependencies": {
"@testing-library/jest-dom": "5.11.8",
"@testing-library/react": "11.2.3",
"@testing-library/react-hooks": "4.0.1",
"@types/jest": "26.0.20",
"@testing-library/react": "11.2.2",
"@testing-library/react-hooks": "3.7.0",
"@types/jest": "26.0.19",
"@types/react": "17.0.0",
"@types/secure-password": "3.1.0",
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1",
"babel-eslint": "10.1.0",
"eslint": "7.17.0",
"eslint": "7.16.0",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.7",
"husky": "4.3.6",
"jest": "26.6.3",
"jest-environment-jsdom-fourteen": "1.0.1",
"jest-watch-typeahead": "0.6.1",

View File

@@ -77,7 +77,6 @@ export const mockRouter: BlitzRouter = {
asPath: "/",
params: {},
query: {},
isReady: true,
push: jest.fn(),
replace: jest.fn(),
reload: jest.fn(),

View File

@@ -11,6 +11,12 @@ model Project {
}
```
2. DB migrate
```
blitz db migrate
```
3. Start the dev server
```

View File

@@ -1,6 +1,6 @@
{
"name": "no-prisma",
"version": "0.29.2",
"version": "0.28.0-canary.1",
"scripts": {
"start": "blitz start",
"build": "blitz build",
@@ -26,7 +26,7 @@
]
},
"dependencies": {
"blitz": "0.29.2",
"blitz": "0.28.0-canary.1",
"knex": "0.21.15",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
@@ -34,17 +34,17 @@
},
"devDependencies": {
"@types/react": "17.0.0",
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1",
"babel-eslint": "10.1.0",
"eslint": "7.17.0",
"eslint": "7.16.0",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.7",
"husky": "4.3.6",
"lint-staged": "10.5.3",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",

View File

@@ -14,7 +14,7 @@ model Project {
2. DB migrate
```
blitz prisma migrate dev --preview-feature
blitz db migrate
```
3. Start the dev server

View File

@@ -3,7 +3,7 @@ const modelSnippet = `model Project {
id Int @default(autoincrement()) @id
name String
}`
const migrateSnippet = `$ blitz prisma migrate dev --preview-feature
const migrateSnippet = `$ blitz db migrate
$ blitz generate all project`
const Home = () => (

View File

@@ -1,9 +1,9 @@
{
"name": "@examples/plain-js",
"version": "0.29.2",
"version": "0.28.0-canary.1",
"scripts": {
"start": "blitz start",
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
"build": "blitz db migrate && blitz build",
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
"test": "echo \"DISABLED\""
},
@@ -29,24 +29,24 @@
]
},
"dependencies": {
"@prisma/cli": "2.14.0",
"@prisma/client": "2.14.0",
"blitz": "0.29.2",
"@prisma/cli": "2.12.0",
"@prisma/client": "2.12.0",
"blitz": "0.28.0-canary.1",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1",
"babel-eslint": "10.1.0",
"eslint": "7.17.0",
"eslint": "7.16.0",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "4.3.7",
"husky": "4.3.6",
"lint-staged": "10.5.3",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",

View File

@@ -1,3 +1,15 @@
module.exports = {
extends: ["blitz"],
env: {
es2020: true,
"cypress/globals": true,
},
extends: ["react-app", "plugin:jsx-a11y/recommended"],
plugins: ["jsx-a11y", "cypress"],
rules: {
"import/no-anonymous-default-export": "error",
"import/no-webpack-loader-syntax": "off",
"react/react-in-jsx-scope": "off", // React is always in scope with Blitz
"jsx-a11y/anchor-is-valid": "off", //Doesn't play well with Blitz/Next <Link> usage
"jsx-a11y/label-has-associated-control": "off",
},
}

View File

@@ -3,7 +3,7 @@
1. DB migrate
```
yarn blitz prisma migrate dev --preview-feature
yarn blitz db migrate
```
2. Start the dev server

View File

@@ -1,7 +1,15 @@
import {makeServerOnlyPrisma} from "blitz"
import {PrismaClient} from "@prisma/client"
const ServerOnlyPrisma = makeServerOnlyPrisma(PrismaClient)
export * from "@prisma/client"
export default new ServerOnlyPrisma()
let prisma: PrismaClient
if (process.env.NODE_ENV === "production") {
prisma = new PrismaClient()
} else {
// Ensure the prisma instance is re-used during hot-reloading
// Otherwise, a new client will be created on every reload
globalThis["prisma"] = globalThis["prisma"] || new PrismaClient()
prisma = globalThis["prisma"]
}
export default prisma

View File

@@ -0,0 +1,83 @@
# Migration `20200429140440-init`
This migration has been generated by Brandon Bayer at 4/29/2020, 2:04:40 PM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "quaint"."User" (
"email" TEXT NOT NULL ,
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT ,
"role" TEXT ,
"storeId" INTEGER
)
CREATE TABLE "quaint"."Product" (
"description" TEXT ,
"handle" TEXT NOT NULL ,
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" TEXT ,
"price" INTEGER
)
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
CREATE UNIQUE INDEX "quaint"."Product.handle" ON "Product"("handle")
PRAGMA "quaint".foreign_key_check;
PRAGMA foreign_keys=ON;
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration ..20200429140440-init
--- datamodel.dml
+++ datamodel.dml
@@ -1,0 +1,37 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+datasource sqlite {
+ provider = "sqlite"
+ url = "file:./db.sqlite"
+}
+
+// SQLite is easy to start with, but if you use Postgres in production
+// you should also use it in development with the following:
+//datasource postgresql {
+// provider = "postgresql"
+// url = env("DATABASE_URL")
+//}
+
+generator client {
+ provider = "prisma-client-js"
+}
+
+
+// --------------------------------------
+
+model User {
+ id Int @default(autoincrement()) @id
+ email String @unique
+ name String?
+ role String?
+ storeId Int?
+}
+
+model Product {
+ id Int @default(autoincrement()) @id
+ handle String @unique
+ name String?
+ description String?
+ price Int?
+}
```

View File

@@ -0,0 +1,37 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
url = "***"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
// url = "***"
//}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
handle String @unique
name String?
description String?
price Int?
}

View File

@@ -0,0 +1,199 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateSource",
"source": "sqlite"
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "sqlite"
},
"argument": "provider",
"value": "\"sqlite\""
},
{
"tag": "CreateArgument",
"location": {
"tag": "Source",
"source": "sqlite"
},
"argument": "url",
"value": "\"file:./db.sqlite\""
},
{
"tag": "CreateModel",
"model": "User"
},
{
"tag": "CreateField",
"model": "User",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "email",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "email"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "User",
"field": "name",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "role",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "User",
"field": "storeId",
"type": "Int",
"arity": "Optional"
},
{
"tag": "CreateModel",
"model": "Product"
},
{
"tag": "CreateField",
"model": "Product",
"field": "id",
"type": "Int",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "default"
},
"argument": "",
"value": "autoincrement()"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "id"
},
"directive": "id"
}
},
{
"tag": "CreateField",
"model": "Product",
"field": "handle",
"type": "String",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "handle"
},
"directive": "unique"
}
},
{
"tag": "CreateField",
"model": "Product",
"field": "name",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Product",
"field": "description",
"type": "String",
"arity": "Optional"
},
{
"tag": "CreateField",
"model": "Product",
"field": "price",
"type": "Int",
"arity": "Optional"
}
]
}

View File

@@ -0,0 +1,86 @@
# Migration `20200801094346-add-dates`
This migration has been generated by Brandon Bayer at 8/1/2020, 9:43:46 AM.
You can check out the [state of the schema](./schema.prisma) after the migration.
## Database Steps
```sql
PRAGMA foreign_keys=OFF;
CREATE TABLE "quaint"."new_User" (
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP ,"email" TEXT NOT NULL ,"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"name" TEXT ,"role" TEXT ,"storeId" INTEGER ,"updatedAt" DATE NOT NULL )
INSERT INTO "quaint"."new_User" ("email", "id", "name", "role", "storeId") SELECT "email", "id", "name", "role", "storeId" FROM "quaint"."User"
PRAGMA foreign_keys=off;
DROP TABLE "quaint"."User";;
PRAGMA foreign_keys=on
ALTER TABLE "quaint"."new_User" RENAME TO "User";
CREATE UNIQUE INDEX "quaint"."User.email" ON "User"("email")
CREATE TABLE "quaint"."new_Product" (
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP ,"description" TEXT ,"handle" TEXT NOT NULL ,"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"name" TEXT ,"price" INTEGER ,"updatedAt" DATE NOT NULL )
INSERT INTO "quaint"."new_Product" ("description", "handle", "id", "name", "price") SELECT "description", "handle", "id", "name", "price" FROM "quaint"."Product"
PRAGMA foreign_keys=off;
DROP TABLE "quaint"."Product";;
PRAGMA foreign_keys=on
ALTER TABLE "quaint"."new_Product" RENAME TO "Product";
CREATE UNIQUE INDEX "quaint"."Product.handle" ON "Product"("handle")
PRAGMA "quaint".foreign_key_check;
PRAGMA foreign_keys=ON;
```
## Changes
```diff
diff --git schema.prisma schema.prisma
migration 20200429140440-init..20200801094346-add-dates
--- datamodel.dml
+++ datamodel.dml
@@ -2,16 +2,16 @@
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
- url = "***"
+ url = "file:./db.sqlite"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
-// url = "***"
+// url = env("DATABASE_URL")
//}
generator client {
provider = "prisma-client-js"
@@ -21,16 +21,20 @@
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
handle String @unique
name String?
description String?
price Int?
```

View File

@@ -0,0 +1,41 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource sqlite {
provider = "sqlite"
url = "***"
}
// SQLite is easy to start with, but if you use Postgres in production
// you should also use it in development with the following:
//datasource postgresql {
// provider = "postgresql"
// url = "***"
//}
generator client {
provider = "prisma-client-js"
}
// --------------------------------------
model User {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
email String @unique
name String?
role String?
storeId Int?
}
model Product {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
handle String @unique
name String?
description String?
price Int?
}

View File

@@ -0,0 +1,105 @@
{
"version": "0.3.14-fixed",
"steps": [
{
"tag": "CreateField",
"model": "User",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "User",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "User",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "User",
"field": "updatedAt"
},
"directive": "updatedAt"
}
},
{
"tag": "CreateField",
"model": "Product",
"field": "createdAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "createdAt"
},
"directive": "default"
}
},
{
"tag": "CreateArgument",
"location": {
"tag": "Directive",
"path": {
"tag": "Field",
"model": "Product",
"field": "createdAt"
},
"directive": "default"
},
"argument": "",
"value": "now()"
},
{
"tag": "CreateField",
"model": "Product",
"field": "updatedAt",
"type": "DateTime",
"arity": "Required"
},
{
"tag": "CreateDirective",
"location": {
"path": {
"tag": "Field",
"model": "Product",
"field": "updatedAt"
},
"directive": "updatedAt"
}
}
]
}

View File

@@ -1,27 +0,0 @@
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT,
"role" TEXT,
"storeId" INTEGER
);
-- CreateTable
CREATE TABLE "Product" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" DATETIME NOT NULL,
"handle" TEXT NOT NULL,
"name" TEXT,
"description" TEXT,
"price" INTEGER
);
-- CreateIndex
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "Product.handle_unique" ON "Product"("handle");

View File

@@ -0,0 +1,4 @@
# Prisma Migrate lockfile v1
20200429140440-init
20200801094346-add-dates

View File

@@ -1,12 +1,12 @@
{
"name": "@examples/store",
"version": "0.29.2",
"version": "0.28.0-canary.1",
"private": true,
"scripts": {
"build": "blitz prisma migrate deploy --preview-feature && blitz build",
"build": "blitz db migrate && blitz build",
"cy:open": "cypress open",
"cy:run": "cypress run",
"test:server": "prisma generate && blitz prisma migrate deploy --preview-feature && blitz db seed && blitz build && blitz start --production -p 3099",
"test:server": "blitz db migrate && blitz db seed && blitz build && blitz start --production -p 3099",
"test": "cross-env NODE_ENV=test start-server-and-test test:server http://localhost:3099 cy:run",
"posttest": "node assert-tree-shaking-works.js"
},
@@ -20,9 +20,9 @@
"trailingComma": "all"
},
"dependencies": {
"@prisma/cli": "2.14.0",
"@prisma/client": "2.14.0",
"blitz": "0.29.2",
"@prisma/cli": "2.12.0",
"@prisma/client": "2.12.0",
"blitz": "0.28.0-canary.1",
"final-form": "4.20.1",
"react": "0.0.0-experimental-3310209d0",
"react-dom": "0.0.0-experimental-3310209d0",
@@ -32,7 +32,7 @@
},
"devDependencies": {
"@types/react": "17.0.0",
"cypress": "6.2.1",
"cypress": "6.2.0",
"eslint-plugin-cypress": "2.11.2",
"sass": "1.32.0",
"start-server-and-test": "1.11.7"

View File

@@ -1,5 +1,5 @@
{
"version": "0.29.2",
"version": "0.28.0-canary.1",
"packages": ["packages/*"],
"npmClient": "yarn",
"useWorkspaces": true,

View File

@@ -43,8 +43,8 @@
"devDependencies": {
"@rollup/pluginutils": "4.1.0",
"@testing-library/jest-dom": "5.11.8",
"@testing-library/react": "11.2.3",
"@testing-library/react-hooks": "4.0.1",
"@testing-library/react": "11.2.2",
"@testing-library/react-hooks": "3.7.0",
"@types/b64-lite": "1.3.0",
"@types/cookie": "0.4.0",
"@types/cookie-session": "2.0.42",
@@ -59,14 +59,14 @@
"@types/fs-readdir-recursive": "1.0.0",
"@types/gulp-if": "0.0.33",
"@types/ink-spinner": "3.0.0",
"@types/jest": "26.0.20",
"@types/jest": "26.0.19",
"@types/jsonwebtoken": "8.5.0",
"@types/lodash": "4.14.166",
"@types/mem-fs": "1.1.2",
"@types/mem-fs-editor": "7.0.0",
"@types/merge-stream": "1.1.2",
"@types/mock-fs": "4.13.0",
"@types/node": "14.14.20",
"@types/node": "14.14.17",
"@types/node-fetch": "2.5.7",
"@types/parallel-transform": "1.1.0",
"@types/passport": "1.0.5",
@@ -83,9 +83,9 @@
"@types/through2": "2.0.36",
"@types/vinyl": "2.0.4",
"@types/vinyl-fs": "2.4.11",
"@types/webpack": "4.41.26",
"@typescript-eslint/eslint-plugin": "4.12.0",
"@typescript-eslint/parser": "4.12.0",
"@types/webpack": "4.41.25",
"@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1",
"@wessberg/cjs-to-esm-transformer": "0.0.22",
"@wessberg/rollup-plugin-ts": "1.3.8",
"babel-eslint": "10.x",
@@ -96,19 +96,19 @@
"debug": "4.3.1",
"delay": "4.4.0",
"directory-tree": "2.2.5",
"eslint": "7.17.0",
"eslint": "7.16.0",
"eslint-config-react-app": "6.0.0",
"eslint-plugin-es": "4.1.0",
"eslint-plugin-es5": "1.5.0",
"eslint-plugin-flowtype": "5.2.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jsx-a11y": "6.4.1",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-prettier": "3.3.0",
"eslint-plugin-react": "7.22.0",
"eslint-plugin-react-hooks": "4.2.0",
"eslint-plugin-simple-import-sort": "7.0.0",
"eslint-plugin-unicorn": "25.0.1",
"husky": "4.3.7",
"husky": "4.3.6",
"jest": "26.6.3",
"jest-environment-jsdom-sixteen": "1.0.3",
"lerna": "3.22.1",
@@ -124,7 +124,7 @@
"react-test-renderer": "17.0.1",
"release": "6.3.0",
"rimraf": "3.0.2",
"rollup": "2.36.1",
"rollup": "2.35.1",
"rollup-plugin-commonjs": "10.1.0",
"rollup-plugin-json": "4.0.0",
"rollup-plugin-node-polyfills": "0.2.1",
@@ -135,11 +135,10 @@
"test-listen": "1.1.0",
"ts-jest": "26.4.4",
"tsdx": "0.14.1",
"tslib": "2.1.0",
"tslib": "2.0.3",
"typescript": "4.1.3",
"wait-on": "5.2.1",
"yalc": "1.0.0-pre.49",
"eslint_d": "9.1.2"
"yalc": "1.0.0-pre.49"
},
"husky": {
"hooks": {

Some files were not shown because too many files have changed in this diff Show More