Compare commits
28 Commits
@blitzjs/c
...
siddharth/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e5d691eae | ||
|
|
d4f0230979 | ||
|
|
47554d39e7 | ||
|
|
2bb10b86cf | ||
|
|
a38caabd10 | ||
|
|
fbd64739cb | ||
|
|
f8ab8fce25 | ||
|
|
a0448fff17 | ||
|
|
83d236e456 | ||
|
|
b0bab155d4 | ||
|
|
ad493c489d | ||
|
|
f303bc6ab1 | ||
|
|
a4f4e7e098 | ||
|
|
12faac9f69 | ||
|
|
9219ec7ccc | ||
|
|
dfb5d6b360 | ||
|
|
170af30c93 | ||
|
|
362893835f | ||
|
|
f9301b6fd6 | ||
|
|
b25ec48a6f | ||
|
|
1802227f70 | ||
|
|
714f293d69 | ||
|
|
5d4d4daa2d | ||
|
|
668ed49964 | ||
|
|
19884bff01 | ||
|
|
fe8ed06a6e | ||
|
|
a362d8ac68 | ||
|
|
99cb249ee3 |
8
.changeset/wild-news-shop.md
Normal file
8
.changeset/wild-news-shop.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@blitzjs/auth": minor
|
||||
"@blitzjs/next": minor
|
||||
"@blitzjs/rpc": minor
|
||||
"blitz": minor
|
||||
---
|
||||
|
||||
chore: support next.js 15
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -148,7 +148,7 @@ jobs:
|
||||
- name: Install playwright
|
||||
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
|
||||
run: |
|
||||
pnpx playwright@1.28.0 install --with-deps
|
||||
pnpx playwright@1.49.1 install --with-deps
|
||||
shell: bash
|
||||
|
||||
- name: Build
|
||||
|
||||
2
apps/next13/.gitignore
vendored
2
apps/next13/.gitignore
vendored
@@ -36,3 +36,5 @@ yarn-error.log*
|
||||
next-env.d.ts
|
||||
|
||||
.vscode
|
||||
|
||||
certificates
|
||||
@@ -20,20 +20,21 @@
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "^4.5.0",
|
||||
"@tanstack/react-query": "4.0.10",
|
||||
"arctic": "2.3.3",
|
||||
"blitz": "2.1.3",
|
||||
"flatted": "3.2.7",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "^4.5.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"superjson": "1.11.0",
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "18.11.7",
|
||||
"@types/react": "18.0.23",
|
||||
"@types/react-dom": "18.0.7",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"eslint": "8.26.0",
|
||||
"eslint-config-next": "13.0.0",
|
||||
"typescript": "4.8.4"
|
||||
|
||||
21
apps/next13/src/app/api/auth/[...blitzAuth]/route.ts
Normal file
21
apps/next13/src/app/api/auth/[...blitzAuth]/route.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import {oAuth2Handler} from "@blitzjs/auth"
|
||||
import {GitHub, Google} from "arctic"
|
||||
|
||||
export const {GET} = oAuth2Handler([
|
||||
{
|
||||
client: new GitHub(process.env.GITHUB_CLIENT_ID!, process.env.GITHUB_CLIENT_SECRET!, null),
|
||||
name: "github",
|
||||
scopes: ["user:email"],
|
||||
withPkce: false,
|
||||
},
|
||||
{
|
||||
client: new Google(
|
||||
process.env.GOOGLE_CLIENT_ID!,
|
||||
process.env.GOOGLE_CLIENT_SECRET!,
|
||||
"https://localhost:3000/api/auth/google/callback",
|
||||
),
|
||||
name: "google",
|
||||
scopes: ["email", "profile"],
|
||||
withPkce: true,
|
||||
},
|
||||
])
|
||||
@@ -4,7 +4,7 @@ import {zodResolver} from "@hookform/resolvers/zod"
|
||||
import {z} from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import {forwardRef, PropsWithoutRef, ComponentPropsWithoutRef} from "react"
|
||||
import {useFormContext} from "react-hook-form"
|
||||
import {ErrorMessage} from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -31,23 +31,22 @@
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"openid-client": "5.2.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/jest": "29.2.2",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"eslint": "8.27.0",
|
||||
"eslint-config-next": "12.3.1",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
2
apps/toolkit-app/next-env.d.ts
vendored
2
apps/toolkit-app/next-env.d.ts
vendored
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
"@hookform/resolvers": "2.9.10",
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"next-auth": "4.24.7",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.23.8"
|
||||
@@ -44,11 +44,10 @@
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,15 @@ import { ComponentPropsWithoutRef, forwardRef, PropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
options: any[]
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps
|
||||
extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -26,17 +26,17 @@
|
||||
"blitz": "2.1.3",
|
||||
"jest": "29.3.0",
|
||||
"jest-environment-jsdom": "29.3.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"passport-mock-strategy": "2.0.0",
|
||||
"passport-twitter": "1.0.4",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"ts-node": "10.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"eslint": "8.27.0",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"delay": "5.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"react-hook-form": "7.39.1",
|
||||
"ts-node": "10.9.1",
|
||||
"zod": "3.23.8"
|
||||
@@ -37,11 +37,10 @@
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@vitejs/plugin-react": "2.2.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -23,10 +23,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"wait-port": "1.0.4"
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node": "18.7.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "2.1.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -15,16 +15,16 @@
|
||||
"@blitzjs/next": "2.1.3",
|
||||
"@blitzjs/rpc": "2.1.3",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
"get-port": "6.1.2",
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "2.1.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"wait-port": "1.0.4"
|
||||
},
|
||||
@@ -37,7 +37,7 @@
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node": "18.7.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"@tanstack/react-query": "4.0.10",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@types/react": "18.0.25",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@vitejs/plugin-react": "1.3.0",
|
||||
"delay": "5.0.0",
|
||||
"eslint": "8.27.0",
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
"@blitzjs/next": "2.1.3",
|
||||
"@blitzjs/rpc": "2.1.3",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -11,15 +11,15 @@
|
||||
"@blitzjs/next": "2.1.3",
|
||||
"@blitzjs/rpc": "2.1.3",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
"@prisma/client": "4.6.1",
|
||||
"blitz": "2.1.3",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"prisma": "4.6.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
@@ -33,7 +33,7 @@
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "8.27.0",
|
||||
"fs-extra": "10.0.1",
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
"@blitzjs/next": "workspace:2.1.3",
|
||||
"@blitzjs/rpc": "workspace:2.1.3",
|
||||
"@tanstack/react-query": "4.13.0",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/selenium-webdriver": "4.0.18",
|
||||
"chromedriver": "100.0.0",
|
||||
@@ -23,8 +23,8 @@
|
||||
"node-fetch": "3.2.3",
|
||||
"pkg-dir": "5.0.0",
|
||||
"playwright-chromium": "1.28.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"resolve-cwd": "3.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"husky": "8.0.2",
|
||||
"jsdom": "^19.0.0",
|
||||
"lint-staged": "13.0.3",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"only-allow": "1.1.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-prisma": "4.4.0",
|
||||
@@ -51,7 +51,9 @@
|
||||
},
|
||||
"overrides": {
|
||||
"@types/mime": "3.0.4",
|
||||
"next": "14.2.15"
|
||||
"next": "15.0.1",
|
||||
"@types/react": "npm:types-react@rc",
|
||||
"@types/react-dom": "npm:types-react-dom@rc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/passport": "1.0.7",
|
||||
"@types/secure-password": "3.1.1",
|
||||
"arctic": "2.3.3",
|
||||
"b64-lite": "1.4.0",
|
||||
"bad-behavior": "1.0.1",
|
||||
"cookie": "0.4.1",
|
||||
@@ -68,18 +69,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/cookie": "0.4.1",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"next-auth": "4.24.7",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"secure-password": "4.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
|
||||
@@ -7,7 +7,7 @@ import {parsePublicDataToken, getPublicDataStore, useSession} from "./index"
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN} from "../shared"
|
||||
import {toBase64} from "b64-lite"
|
||||
import {act} from "@testing-library/react"
|
||||
import {renderHook} from "@testing-library/react-hooks"
|
||||
import {renderHook} from "@testing-library/react"
|
||||
|
||||
vi.mock("blitz", async () => {
|
||||
const blitz = await vi.importActual("blitz")
|
||||
|
||||
@@ -260,7 +260,7 @@ export type RedirectAuthenticatedToFn = (
|
||||
) => RedirectAuthenticatedTo
|
||||
|
||||
export type BlitzPage<P = {}> = React.ComponentType<P> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
getLayout?: (component: React.JSX.Element) => React.JSX.Element
|
||||
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
|
||||
suppressFirstRenderFlicker?: boolean
|
||||
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn
|
||||
|
||||
@@ -2,3 +2,4 @@ import "./global"
|
||||
|
||||
export * from "./index-browser"
|
||||
export * from "./server"
|
||||
export * from "./oauth"
|
||||
|
||||
37
packages/blitz-auth/src/oauth/config.ts
Normal file
37
packages/blitz-auth/src/oauth/config.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {decodeIdToken, GitHub, Google, OAuth2Tokens} from "arctic"
|
||||
import {ArcticOAuthClient, SupportedOAuthProviders} from "./types"
|
||||
|
||||
type InternalConfig = {
|
||||
name: SupportedOAuthProviders
|
||||
profile(token: OAuth2Tokens): Record<string, unknown>
|
||||
pkce: true
|
||||
client:
|
||||
}
|
||||
|
||||
export function getInternalConfig(provider: SupportedOAuthProviders, client: ArcticOAuthClient) {
|
||||
switch (provider) {
|
||||
case SupportedOAuthProviders.Google:
|
||||
return {
|
||||
name: SupportedOAuthProviders.Google,
|
||||
profile(token: OAuth2Tokens) {
|
||||
const idToken = token.idToken()
|
||||
|
||||
return decodeIdToken(idToken)
|
||||
},
|
||||
pkce: true,
|
||||
client: client,
|
||||
}
|
||||
case SupportedOAuthProviders.GitHub:
|
||||
return {
|
||||
name: SupportedOAuthProviders.GitHub,
|
||||
profile(token: OAuth2Tokens) {
|
||||
const idToken = token.idToken()
|
||||
return decodeIdToken(idToken)
|
||||
},
|
||||
pkce: false,
|
||||
client: client,
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported provider: ${provider}`)
|
||||
}
|
||||
}
|
||||
112
packages/blitz-auth/src/oauth/index.ts
Normal file
112
packages/blitz-auth/src/oauth/index.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import {
|
||||
ArcticFetchError,
|
||||
decodeIdToken,
|
||||
generateCodeVerifier,
|
||||
OAuth2Client,
|
||||
OAuth2RequestError,
|
||||
OAuth2Tokens,
|
||||
} from "arctic"
|
||||
import {generateState} from "arctic"
|
||||
import cookie, {parse} from "cookie"
|
||||
import {ArcticOAuthClient, SupportedOAuthProviders} from "./types"
|
||||
import {getInternalConfig} from "./config"
|
||||
|
||||
type Params = Record<string, unknown>
|
||||
type OAuthConfig = Array<{
|
||||
client: ArcticOAuthClient
|
||||
name: string
|
||||
scopes: string[]
|
||||
}>
|
||||
|
||||
export function oAuth2Handler(config: OAuthConfig) {
|
||||
async function GET(req: Request, segmentData: {params: Promise<Params>}) {
|
||||
const params = await segmentData.params
|
||||
const blitzAuth = params.blitzAuth as string[]
|
||||
const clientName = blitzAuth[0]
|
||||
const clientConfig = config.find((c) => c.name === clientName)
|
||||
if (clientConfig === undefined || !clientName) {
|
||||
return new Response("Not Found", {status: 404})
|
||||
}
|
||||
const internalConfig = getInternalConfig(
|
||||
clientName as SupportedOAuthProviders,
|
||||
clientConfig.client,
|
||||
)
|
||||
const action = blitzAuth[1]
|
||||
if (action === "callback") {
|
||||
const cookies = parse(req.headers.get("Cookie") || "")
|
||||
const storedState = cookies.state
|
||||
const codeVerifier = cookies.code_verifier
|
||||
const url = new URL(req.url)
|
||||
const code = url.searchParams.get("code")
|
||||
const state = url.searchParams.get("state")
|
||||
if (
|
||||
code === null ||
|
||||
storedState === undefined ||
|
||||
codeVerifier === undefined ||
|
||||
state !== storedState
|
||||
) {
|
||||
return new Response("Bad Request", {status: 400})
|
||||
}
|
||||
|
||||
try {
|
||||
let token: OAuth2Tokens
|
||||
if (internalConfig.pkce) {
|
||||
token = await internalConfig.client.validateAuthorizationCode(code, codeVerifier)
|
||||
} else {
|
||||
token = await internalConfig.client.validateAuthorizationCode(code, clientConfig.scopes)
|
||||
}
|
||||
console.log(token)
|
||||
const idToken = token.idToken()
|
||||
console.log(idToken, decodeIdToken(idToken))
|
||||
} catch (e) {
|
||||
if (e instanceof OAuth2RequestError) {
|
||||
const code = e.code
|
||||
const message = e.message
|
||||
console.log(code, message)
|
||||
return new Response("Bad Request", {status: 400})
|
||||
}
|
||||
if (e instanceof ArcticFetchError) {
|
||||
const message = e.message
|
||||
console.log(message)
|
||||
return new Response("Bad Request", {status: 400})
|
||||
}
|
||||
console.log(e)
|
||||
return new Response("Bad Request", {status: 400})
|
||||
}
|
||||
} else {
|
||||
const state = generateState()
|
||||
const codeVerifier = generateCodeVerifier()
|
||||
let url: URL
|
||||
if (internalConfig.pkce) {
|
||||
url = internalConfig.client.createAuthorizationURL(state, codeVerifier, clientConfig.scopes)
|
||||
} else {
|
||||
url = internalConfig.client.createAuthorizationURL(state, clientConfig.scopes)
|
||||
}
|
||||
const stateCookie = cookie.serialize("state", state, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
maxAge: 60 * 10,
|
||||
path: "/",
|
||||
})
|
||||
const codeVerifierCookie = cookie.serialize("code_verifier", codeVerifier, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
maxAge: 60 * 10,
|
||||
path: "/",
|
||||
})
|
||||
|
||||
const headers = new Headers({
|
||||
Location: url.toString(),
|
||||
})
|
||||
|
||||
headers.append("Set-Cookie", stateCookie)
|
||||
headers.append("Set-Cookie", codeVerifierCookie)
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers,
|
||||
})
|
||||
}
|
||||
}
|
||||
return {GET}
|
||||
}
|
||||
165
packages/blitz-auth/src/oauth/types.ts
Normal file
165
packages/blitz-auth/src/oauth/types.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import {AmazonCognito} from "arctic"
|
||||
import {AniList} from "arctic"
|
||||
import {Apple} from "arctic"
|
||||
import {Atlassian} from "arctic"
|
||||
import {Auth0} from "arctic"
|
||||
import {Authentik} from "arctic"
|
||||
import {Bitbucket} from "arctic"
|
||||
import {Box} from "arctic"
|
||||
import {Bungie} from "arctic"
|
||||
import {Coinbase} from "arctic"
|
||||
import {Discord} from "arctic"
|
||||
import {Dribbble} from "arctic"
|
||||
import {Dropbox} from "arctic"
|
||||
import {Etsy} from "arctic"
|
||||
import {EpicGames} from "arctic"
|
||||
import {Facebook} from "arctic"
|
||||
import {Figma} from "arctic"
|
||||
import {Intuit} from "arctic"
|
||||
import {GitHub} from "arctic"
|
||||
import {GitLab} from "arctic"
|
||||
import {Google} from "arctic"
|
||||
import {Kakao} from "arctic"
|
||||
import {KeyCloak} from "arctic"
|
||||
import {Lichess} from "arctic"
|
||||
import {Line} from "arctic"
|
||||
import {Linear} from "arctic"
|
||||
import {LinkedIn} from "arctic"
|
||||
import {MicrosoftEntraId} from "arctic"
|
||||
import {MyAnimeList} from "arctic"
|
||||
import {Naver} from "arctic"
|
||||
import {Notion} from "arctic"
|
||||
import {Okta} from "arctic"
|
||||
import {Osu} from "arctic"
|
||||
import {Patreon} from "arctic"
|
||||
import {Polar} from "arctic"
|
||||
import {Reddit} from "arctic"
|
||||
import {Roblox} from "arctic"
|
||||
import {Salesforce} from "arctic"
|
||||
import {Shikimori} from "arctic"
|
||||
import {Slack} from "arctic"
|
||||
import {Spotify} from "arctic"
|
||||
import {StartGG} from "arctic"
|
||||
import {Strava} from "arctic"
|
||||
import {Tiltify} from "arctic"
|
||||
import {Tumblr} from "arctic"
|
||||
import {Twitch} from "arctic"
|
||||
import {Twitter} from "arctic"
|
||||
import {VK} from "arctic"
|
||||
import {WorkOS} from "arctic"
|
||||
import {Yahoo} from "arctic"
|
||||
import {Yandex} from "arctic"
|
||||
import {Zoom} from "arctic"
|
||||
import {FortyTwo} from "arctic"
|
||||
|
||||
export type ArcticOAuthClient =
|
||||
| AniList
|
||||
| Reddit
|
||||
| Apple
|
||||
| Atlassian
|
||||
| Auth0
|
||||
| Bitbucket
|
||||
| Box
|
||||
| Bungie
|
||||
| Coinbase
|
||||
| Discord
|
||||
| Dribbble
|
||||
| Dropbox
|
||||
| EpicGames
|
||||
| Facebook
|
||||
| Figma
|
||||
| FortyTwo
|
||||
| GitHub
|
||||
| GitLab
|
||||
| Kakao
|
||||
| LinkedIn
|
||||
| Linear
|
||||
| Naver
|
||||
| Notion
|
||||
| Osu
|
||||
| Patreon
|
||||
| Shikimori
|
||||
| Slack
|
||||
| Spotify
|
||||
| StartGG
|
||||
| Strava
|
||||
| Tiltify
|
||||
| Tumblr
|
||||
| Twitch
|
||||
| VK
|
||||
| WorkOS
|
||||
| Yahoo
|
||||
| Yandex
|
||||
| Intuit
|
||||
// pkce
|
||||
| Authentik
|
||||
| AmazonCognito
|
||||
| Twitter
|
||||
| Polar
|
||||
| MyAnimeList
|
||||
| KeyCloak
|
||||
| Salesforce
|
||||
| Roblox
|
||||
| Lichess
|
||||
| MicrosoftEntraId
|
||||
| Google
|
||||
| Okta
|
||||
| Etsy
|
||||
| Line
|
||||
| Zoom
|
||||
|
||||
export enum SupportedOAuthProviders {
|
||||
Authentik = "Authentik",
|
||||
AmazonCognito = "AmazonCognito",
|
||||
Twitter = "Twitter",
|
||||
Polar = "Polar",
|
||||
MyAnimeList = "MyAnimeList",
|
||||
KeyCloak = "KeyCloak",
|
||||
Salesforce = "Salesforce",
|
||||
Roblox = "Roblox",
|
||||
Lichess = "Lichess",
|
||||
MicrosoftEntraId = "MicrosoftEntraId",
|
||||
Google = "Google",
|
||||
Okta = "Okta",
|
||||
Etsy = "Etsy",
|
||||
Line = "Line",
|
||||
Zoom = "Zoom",
|
||||
AniList = "AniList",
|
||||
Reddit = "Reddit",
|
||||
Apple = "Apple",
|
||||
Atlassian = "Atlassian",
|
||||
Auth0 = "Auth0",
|
||||
Bitbucket = "Bitbucket",
|
||||
Box = "Box",
|
||||
Bungie = "Bungie",
|
||||
Coinbase = "Coinbase",
|
||||
Discord = "Discord",
|
||||
Dribbble = "Dribbble",
|
||||
Dropbox = "Dropbox",
|
||||
EpicGames = "EpicGames",
|
||||
Facebook = "Facebook",
|
||||
Figma = "Figma",
|
||||
FortyTwo = "FortyTwo",
|
||||
GitHub = "GitHub",
|
||||
GitLab = "GitLab",
|
||||
Kakao = "Kakao",
|
||||
LinkedIn = "LinkedIn",
|
||||
Linear = "Linear",
|
||||
Naver = "Naver",
|
||||
Notion = "Notion",
|
||||
Osu = "Osu",
|
||||
Patreon = "Patreon",
|
||||
Shikimori = "Shikimori",
|
||||
Slack = "Slack",
|
||||
Spotify = "Spotify",
|
||||
StartGG = "StartGG",
|
||||
Strava = "Strava",
|
||||
Tiltify = "Tiltify",
|
||||
Tumblr = "Tumblr",
|
||||
Twitch = "Twitch",
|
||||
VK = "VK",
|
||||
WorkOS = "WorkOS",
|
||||
Yahoo = "Yahoo",
|
||||
Yandex = "Yandex",
|
||||
Intuit = "Intuit",
|
||||
}
|
||||
@@ -289,8 +289,10 @@ const makeProxyToPublicData = <T extends SessionContextClass>(ctxClass: T): T =>
|
||||
export async function getBlitzContext(): Promise<Ctx> {
|
||||
try {
|
||||
const {headers, cookies} = require("next/headers")
|
||||
const reqHeader = Object.fromEntries(headers())
|
||||
const csrfToken = cookies().get(COOKIE_CSRF_TOKEN())
|
||||
const cookieStore = await cookies()
|
||||
const headersStore = await headers()
|
||||
const reqHeader = Object.fromEntries(headersStore)
|
||||
const csrfToken = cookieStore.get(COOKIE_CSRF_TOKEN())
|
||||
if (csrfToken) {
|
||||
reqHeader[HEADER_CSRF] = csrfToken.value
|
||||
}
|
||||
|
||||
@@ -48,21 +48,20 @@
|
||||
"@blitzjs/config": "2.1.3",
|
||||
"@testing-library/dom": "8.13.0",
|
||||
"@testing-library/jest-dom": "5.16.3",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@testing-library/user-event": "13.5.0",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"@types/testing-library__react-hooks": "4.0.0",
|
||||
"blitz": "2.1.3",
|
||||
"cross-spawn": "7.0.3",
|
||||
"find-up": "4.1.0",
|
||||
"next": "14.2.15",
|
||||
"next": "15.0.1",
|
||||
"next-router-mock": "0.9.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"resolve-from": "5.0.0",
|
||||
"ts-jest": "27.1.4",
|
||||
"tslog": "4.9.0",
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
module.exports = {}
|
||||
const exports = {
|
||||
"npm-which": {},
|
||||
"cross-spawn": {},
|
||||
fs: {},
|
||||
child_process: {},
|
||||
}
|
||||
|
||||
module.exports = exports
|
||||
|
||||
@@ -70,7 +70,7 @@ test("handleError forwards along async errors", async () => {
|
||||
//
|
||||
// React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary."
|
||||
// `)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// can recover
|
||||
@@ -116,7 +116,7 @@ test("can pass an error to useErrorHandler", async () => {
|
||||
//
|
||||
// React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary."
|
||||
// `)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// can recover
|
||||
|
||||
@@ -77,25 +77,10 @@ test("standard use-case", () => {
|
||||
const {unmount} = render(<App />)
|
||||
|
||||
userEvent.type(screen.getByRole("textbox", {name: /username/i}), "fail")
|
||||
|
||||
const [[actualError], [componentStack]] = consoleError.mock.calls
|
||||
expect(firstLine(actualError as string)).toMatchInlineSnapshot(
|
||||
`"Error: Uncaught [Error: 💥 CABOOM 💥]"`,
|
||||
)
|
||||
expect(cleanStack(componentStack)).toMatchInlineSnapshot(`
|
||||
"Error: Uncaught [Error: 💥 CABOOM 💥]
|
||||
at reportException
|
||||
at innerInvokeEventListeners
|
||||
at invokeEventListeners
|
||||
at HTMLUnknownElementImpl._dispatch
|
||||
at HTMLUnknownElementImpl.dispatchEvent
|
||||
at HTMLUnknownElement.dispatchEvent
|
||||
at Object.invokeGuardedCallbackDev
|
||||
at invokeGuardedCallback
|
||||
at beginWork\$1
|
||||
at performUnitOfWork "
|
||||
`)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
const calls = consoleError.mock.calls[0]
|
||||
//@ts-expect-error - it's a mock
|
||||
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
expect(screen.getByRole("alert")).toMatchInlineSnapshot(`
|
||||
@@ -149,7 +134,7 @@ test("fallbackRender prop", () => {
|
||||
}
|
||||
|
||||
const {unmount} = render(<App />)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// the render prop API allows a single action to reset the app state
|
||||
@@ -168,7 +153,7 @@ test("simple fallback is supported", () => {
|
||||
<span>child</span>
|
||||
</ErrorBoundary>,
|
||||
)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
expect(screen.getByText(/oh no/i)).to.exist
|
||||
expect(screen.queryByText(/child/i)).to.not.exist
|
||||
@@ -183,27 +168,16 @@ test("withErrorBoundary HOC", () => {
|
||||
() => {
|
||||
throw new Error("💥 CABOOM 💥")
|
||||
},
|
||||
{FallbackComponent: ErrorFallback, onError: onErrorHandler},
|
||||
{
|
||||
FallbackComponent: ErrorFallback,
|
||||
onError: onErrorHandler,
|
||||
},
|
||||
)
|
||||
const {unmount} = render(<Boundary />)
|
||||
|
||||
const [[actualError], [componentStack]] = consoleError.mock.calls
|
||||
const firstLineOfError = firstLine(actualError as string)
|
||||
expect(firstLineOfError).toMatchInlineSnapshot(`"Error: Uncaught [Error: 💥 CABOOM 💥]"`)
|
||||
expect(cleanStack(componentStack)).toMatchInlineSnapshot(`
|
||||
"Error: Uncaught [Error: 💥 CABOOM 💥]
|
||||
at reportException
|
||||
at innerInvokeEventListeners
|
||||
at invokeEventListeners
|
||||
at HTMLUnknownElementImpl._dispatch
|
||||
at HTMLUnknownElementImpl.dispatchEvent
|
||||
at HTMLUnknownElement.dispatchEvent
|
||||
at Object.invokeGuardedCallbackDev
|
||||
at invokeGuardedCallback
|
||||
at beginWork\$1
|
||||
at performUnitOfWork "
|
||||
`)
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
const calls = consoleError.mock.calls[0]
|
||||
//@ts-expect-error - it's a mock
|
||||
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
const [error, onErrorComponentStack] = (onErrorHandler.mock.calls as [[Error, string]])[0]
|
||||
@@ -265,7 +239,6 @@ test("requires either a fallback, fallbackRender, or FallbackComponent", () => {
|
||||
let unmount: undefined | (() => void)
|
||||
expect(() => {
|
||||
const result = render(
|
||||
// @ts-expect-error we're testing the runtime check of missing props here
|
||||
<ErrorBoundary>
|
||||
<Bomb />
|
||||
</ErrorBoundary>,
|
||||
@@ -318,7 +291,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via try again button
|
||||
@@ -333,7 +306,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up again
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via resetKeys change
|
||||
@@ -348,7 +321,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
// blow it up again
|
||||
userEvent.click(screen.getByText("toggle explode"))
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggles adding an extra resetKey to the array
|
||||
@@ -358,7 +331,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
expect(handleResetKeysChange).toHaveBeenCalledWith([true], [true, true])
|
||||
handleResetKeysChange.mockClear()
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggle explode back to false
|
||||
@@ -369,7 +342,7 @@ test("supports automatic reset of error boundary when resetKeys change", () => {
|
||||
expect(handleResetKeysChange).toHaveBeenCalledWith([true, true], [false, true])
|
||||
expect(screen.getByRole("alert")).to.exist
|
||||
handleResetKeysChange.mockClear()
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// toggle extra resetKey
|
||||
@@ -411,7 +384,7 @@ test("supports reset via resetKeys right after error is triggered on component m
|
||||
|
||||
// it blows up on render
|
||||
expect(screen.queryByRole("alert", {})).to.exist
|
||||
expect(consoleError).toHaveBeenCalledTimes(3)
|
||||
expect(consoleError).toHaveBeenCalledTimes(1)
|
||||
consoleError.mockClear()
|
||||
|
||||
// recover via "toggle explode" button
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {RedirectError} from "blitz"
|
||||
import {useRouter} from "next/compat/router"
|
||||
import type {NextRouter} from "next/router"
|
||||
import * as React from "react"
|
||||
import React from "react"
|
||||
import {RouterContext} from "./router-context"
|
||||
import _debug from "debug"
|
||||
import type {ExcludeRouterProps, WithRouterProps} from "next/dist/client/with-router"
|
||||
@@ -77,7 +77,7 @@ const initialState: ErrorBoundaryState = {error: null}
|
||||
function withRouter<P extends WithRouterProps>(
|
||||
ComposedComponent: React.ComponentType<P>,
|
||||
): React.ComponentType<ExcludeRouterProps<P>> {
|
||||
function WithRouterWrapper(props: any): JSX.Element {
|
||||
function WithRouterWrapper(props: any): React.JSX.Element {
|
||||
return <ComposedComponent router={useRouter()} {...props} />
|
||||
}
|
||||
|
||||
@@ -114,7 +114,13 @@ export const ErrorBoundary = withRouter(
|
||||
await this.props.router.push(error.url)
|
||||
return
|
||||
}
|
||||
this.props.onError?.(error, info)
|
||||
if (this.props.onError) {
|
||||
let componentStack = info.componentStack
|
||||
if (!componentStack) {
|
||||
componentStack = new Error("Stack trace").stack || ""
|
||||
}
|
||||
this.props.onError(error, {componentStack})
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -190,7 +196,7 @@ export const ErrorBoundary = withRouter(
|
||||
},
|
||||
)
|
||||
|
||||
function withErrorBoundary<P extends JSX.IntrinsicAttributes>(
|
||||
function withErrorBoundary<P extends React.JSX.IntrinsicAttributes>(
|
||||
Component: React.ComponentType<P>,
|
||||
errorBoundaryProps: ErrorBoundaryProps,
|
||||
): React.ComponentType<P> {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import React from "react"
|
||||
import {NextPageContext} from "next"
|
||||
import dynamic from "next/dynamic"
|
||||
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
})
|
||||
const Head = dynamic(
|
||||
() =>
|
||||
import("next/head").then((mod) => ({
|
||||
default: mod.default,
|
||||
})),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
},
|
||||
)
|
||||
|
||||
const statusCodes: {[code: number]: string} = {
|
||||
400: "Bad Request",
|
||||
|
||||
@@ -9,10 +9,16 @@ import type {Router} from "next/router"
|
||||
import {BlitzProvider} from "./provider"
|
||||
import dynamic from "next/dynamic"
|
||||
export {Routes} from ".blitz"
|
||||
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
})
|
||||
const Head = dynamic(
|
||||
() =>
|
||||
import("next/head").then((mod) => ({
|
||||
default: mod.default,
|
||||
})),
|
||||
{
|
||||
ssr: false,
|
||||
loading: () => null,
|
||||
},
|
||||
)
|
||||
|
||||
export {BlitzProvider} from "./provider"
|
||||
|
||||
@@ -55,7 +61,7 @@ type RedirectAuthenticatedToFnCtx = {
|
||||
}
|
||||
type RedirectAuthenticatedToFn = (args: RedirectAuthenticatedToFnCtx) => RedirectAuthenticatedTo
|
||||
export type BlitzPage<P = {}> = React.ComponentType<P> & {
|
||||
getLayout?: (component: JSX.Element) => JSX.Element
|
||||
getLayout?: (component: React.JSX.Element) => React.JSX.Element
|
||||
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
|
||||
suppressFirstRenderFlicker?: boolean
|
||||
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {QueryClient, HydrateOptions} from "@blitzjs/rpc"
|
||||
import React from "react"
|
||||
|
||||
export type BlitzProviderProps = {
|
||||
children: JSX.Element
|
||||
children: React.JSX.Element
|
||||
client?: QueryClient
|
||||
contextSharing?: boolean
|
||||
dehydratedState?: unknown
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import React from "react"
|
||||
import {describe, it, expect, vi, afterEach} from "vitest"
|
||||
import {extractRouterParams, useParam, useParams} from "./use-params"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
|
||||
import {renderHook as defaultRenderHook} from "@testing-library/react"
|
||||
import {NextRouter} from "next/router"
|
||||
import {RouterContext} from "./router-context"
|
||||
|
||||
|
||||
@@ -46,12 +46,12 @@
|
||||
"@blitzjs/config": "2.1.3",
|
||||
"@tanstack/query-core": "4.24.4",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"blitz": "2.1.3",
|
||||
"next": "14.2.15",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"next": "15.0.1",
|
||||
"react": "19.0.0",
|
||||
"react-dom": "19.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
"watch": "1.0.2",
|
||||
|
||||
@@ -221,7 +221,7 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
|
||||
// Handles:
|
||||
// - Vite
|
||||
// {
|
||||
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(runContext)
|
||||
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(run
|
||||
// if (resolverFilesLoaded) {
|
||||
// assertUsage(
|
||||
// Object.keys(resolverFilesLoaded).length > 0,
|
||||
@@ -364,17 +364,18 @@ type Params = Record<string, unknown>
|
||||
|
||||
export function rpcAppHandler(config?: RpcConfig) {
|
||||
registerBlitzErrorClasses()
|
||||
async function handleRpcRequest(req: Request, context: {params: Params}, ctx?: Ctx) {
|
||||
async function handleRpcRequest(req: Request, segmentData: {params: Promise<Params>}, ctx?: Ctx) {
|
||||
const params = await segmentData.params
|
||||
const session = ctx?.session
|
||||
const resolverMap = await getResolverMap()
|
||||
assert(resolverMap, "No query or mutation resolvers found")
|
||||
|
||||
assert(
|
||||
Array.isArray(context.params.blitz),
|
||||
Array.isArray(params.blitz),
|
||||
"It seems your Blitz RPC endpoint file is not named [[...blitz]].(jt)s. Please ensure it is",
|
||||
)
|
||||
|
||||
const relativeRoutePath = (context.params.blitz as string[])?.join("/")
|
||||
const relativeRoutePath = (params.blitz as string[])?.join("/")
|
||||
const routePath = "/" + relativeRoutePath
|
||||
const resolverName = routePath.replace(/(\/api\/rpc)?\//, "")
|
||||
const rpcLogger = new RpcLogger(resolverName, config?.logging)
|
||||
@@ -413,14 +414,14 @@ export function rpcAppHandler(config?: RpcConfig) {
|
||||
json:
|
||||
req.method === "POST"
|
||||
? body.params
|
||||
: context.params.params
|
||||
? parse(`${context.params.params}`)
|
||||
: params.params
|
||||
? parse(`${params.params}`)
|
||||
: undefined,
|
||||
meta:
|
||||
req.method === "POST"
|
||||
? body.meta?.params
|
||||
: context.params.meta
|
||||
? parse(`${context.params.meta}`)
|
||||
: params.meta
|
||||
? parse(`${params.meta}`)
|
||||
: undefined,
|
||||
})
|
||||
rpcLogger.timer.initResolver()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {QueryClientProvider} from "@tanstack/react-query"
|
||||
import React from "react"
|
||||
|
||||
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => JSX.Element
|
||||
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => React.JSX.Element
|
||||
|
||||
const BlitzProvider: BlitzProviderType = ({children}) => {
|
||||
const [queryClient] = React.useState(globalThis.queryClient)
|
||||
|
||||
@@ -95,12 +95,12 @@
|
||||
"@types/npm-which": "3.0.1",
|
||||
"@types/progress": "2.0.5",
|
||||
"@types/prompts": "2.0.14",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"@types/test-listen": "1.1.0",
|
||||
"@types/watchpack": "1.1.1",
|
||||
"express": "4.17.3",
|
||||
"react": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"test-listen": "1.1.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
registerBlitzErrorClasses,
|
||||
} from "./errors"
|
||||
import type {EventHooks, MiddlewareHooks} from "./types"
|
||||
import React from "react"
|
||||
export {
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
@@ -26,7 +27,7 @@ export * from "./utils/enhance-prisma"
|
||||
export type BlitzProviderComponentType = <TProps = any>(
|
||||
component: ComponentType<TProps>,
|
||||
) => {
|
||||
(props: TProps): JSX.Element
|
||||
(props: TProps): React.JSX.Element
|
||||
displayName: string
|
||||
}
|
||||
|
||||
|
||||
@@ -64,15 +64,15 @@
|
||||
"@types/mem-fs-editor": "7.0.1",
|
||||
"@types/pluralize": "0.0.29",
|
||||
"@types/prettier": "2.4.4",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"@types/vinyl": "2.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "5.42.1",
|
||||
"@typescript-eslint/parser": "5.9.1",
|
||||
"babylon": "6.18.0",
|
||||
"debug": "4.3.3",
|
||||
"eslint": "8.27.0",
|
||||
"react": "18.2.0",
|
||||
"react": "19.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.6.9",
|
||||
"watch": "1.0.2"
|
||||
|
||||
@@ -5,7 +5,7 @@ import { validateZodSchema } from "blitz"
|
||||
export { FORM_ERROR } from "final-form"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField } from "react-final-form"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
type?: "number" | "string"
|
||||
options: any
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<HTMLSelectElement, LabeledSelectFieldProps>(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, ComponentPropsWithoutRef, PropsWithoutRef } from "react"
|
||||
import { useField, UseFieldConfig } from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
fieldProps?: UseFieldConfig<string>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { validateZodSchema } from "blitz"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,14 @@ import { forwardRef, PropsWithoutRef } from "react";
|
||||
import { useFormikContext, ErrorMessage, Field } from "formik";
|
||||
|
||||
export interface LabeledSelectFieldProps
|
||||
extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string;
|
||||
/** Field label. */
|
||||
label: string;
|
||||
/** Field options. */
|
||||
options: any;
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>;
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>;
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField, useFormikContext, ErrorMessage } from "formik"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,14 @@ import { ComponentPropsWithoutRef, forwardRef, PropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
options: any[]
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@
|
||||
"devDependencies": {
|
||||
"@next/env": "13.4.19",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
|
||||
@@ -5,7 +5,7 @@ import { validateZodSchema } from "blitz"
|
||||
export { FORM_ERROR } from "final-form"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField } from "react-final-form"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
type?: "number" | "string"
|
||||
options: any
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<HTMLSelectElement, LabeledSelectFieldProps>(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, ComponentPropsWithoutRef, PropsWithoutRef } from "react"
|
||||
import { useField, UseFieldConfig } from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
fieldProps?: UseFieldConfig<string>
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { validateZodSchema } from "blitz"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,14 @@ import { forwardRef, PropsWithoutRef } from "react";
|
||||
import { useFormikContext, ErrorMessage, Field } from "formik";
|
||||
|
||||
export interface LabeledSelectFieldProps
|
||||
extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string;
|
||||
/** Field label. */
|
||||
label: string;
|
||||
/** Field options. */
|
||||
options: any;
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>;
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>;
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField, useFormikContext, ErrorMessage } from "formik"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
|
||||
@@ -4,7 +4,7 @@ import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
extends Omit<PropsWithoutRef<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
|
||||
@@ -2,14 +2,14 @@ import { ComponentPropsWithoutRef, forwardRef, PropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<React.JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
options: any[]
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@ import { forwardRef, PropsWithoutRef, ComponentPropsWithoutRef } from "react"
|
||||
import { useFormContext } from "react-hook-form"
|
||||
import { ErrorMessage } from "@hookform/error-message"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<React.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"]>
|
||||
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,7 @@
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@next/env": "13.4.19",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "13.4.0",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"@types/node": "18.11.9",
|
||||
"@types/preview-email": "2.0.1",
|
||||
"@types/react": "18.0.25",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import {vi} from "vitest"
|
||||
import { render as defaultRender } from "@testing-library/react"
|
||||
import { renderHook as defaultRenderHook } from "@testing-library/react-hooks"
|
||||
import { renderHook as defaultRenderHook } from "@testing-library/react"
|
||||
import { NextRouter } from "next/router"
|
||||
import {BlitzProvider, RouterContext} from "@blitzjs/next"
|
||||
import { QueryClient } from "@blitzjs/rpc"
|
||||
|
||||
@@ -5,17 +5,19 @@ import get__ModelName__ from "../../queries/get__ModelName__"
|
||||
import { Edit__ModelName__ } from "../../components/Edit__ModelName__"
|
||||
|
||||
type Edit__ModelName__PageProps = {
|
||||
params: { __modelId__: string }
|
||||
params: Promise<{ __modelId__: string }>
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Edit__ModelName__PageProps): Promise<Metadata> {
|
||||
export async function generateMetadata(props: Edit__ModelName__PageProps): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const __ModelName__ = await invoke(get__ModelName__, { id: Number(params.__modelId__) })
|
||||
return {
|
||||
title: `Edit __ModelName__ ${__ModelName__.id} - ${__ModelName__.name}`,
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page({ params }: Edit__ModelName__PageProps) {
|
||||
export default async function Page(props: Edit__ModelName__PageProps) {
|
||||
const params = await props.params;
|
||||
return (
|
||||
<div>
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
|
||||
@@ -5,7 +5,8 @@ import { invoke } from "src/app/blitz-server"
|
||||
import get__ModelName__ from "../queries/get__ModelName__"
|
||||
import { __ModelName__ } from "../components/__ModelName__"
|
||||
|
||||
export async function generateMetadata({ params }: __ModelName__PageProps): Promise<Metadata> {
|
||||
export async function generateMetadata(props: __ModelName__PageProps): Promise<Metadata> {
|
||||
const params = await props.params;
|
||||
const __ModelName__ = await invoke(get__ModelName__, { id: Number(params.__modelId__) })
|
||||
return {
|
||||
title: `__ModelName__ ${__ModelName__.id} - ${__ModelName__.name}`,
|
||||
@@ -13,10 +14,11 @@ export async function generateMetadata({ params }: __ModelName__PageProps): Prom
|
||||
}
|
||||
|
||||
type __ModelName__PageProps = {
|
||||
params: { __modelId__: string }
|
||||
params: Promise<{ __modelId__: string }>
|
||||
}
|
||||
|
||||
export default async function Page({ params }: __ModelName__PageProps) {
|
||||
export default async function Page(props: __ModelName__PageProps) {
|
||||
const params = await props.params;
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "2.1.3",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"react": "18.2.0",
|
||||
"@types/react": "npm:types-react@19.0.0",
|
||||
"@types/react-dom": "npm:types-react-dom@19.0.0",
|
||||
"react": "19.0.0",
|
||||
"typescript": "^4.8.4",
|
||||
"unbuild": "0.7.6",
|
||||
"watch": "1.0.2"
|
||||
|
||||
1690
pnpm-lock.yaml
generated
1690
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user