1
0
mirror of synced 2026-02-07 03:00:10 -05:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Siddharth Suresh
e929eb6248 Revert "fix: use correct bash syntax (#4349)"
This reverts commit e0cfa328ec.
2024-06-26 19:44:19 +05:30
160 changed files with 3220 additions and 3261 deletions

View File

@@ -4074,46 +4074,6 @@
"contributions": [
"code"
]
},
{
"login": "bezalel6",
"name": "bezalel6",
"avatar_url": "https://avatars.githubusercontent.com/u/51681171?v=4",
"profile": "https://github.com/bezalel6",
"contributions": [
"doc",
"code"
]
},
{
"login": "cherniavskii",
"name": "Andrew Cherniavskii",
"avatar_url": "https://avatars.githubusercontent.com/u/13808724?v=4",
"profile": "cherniavskii.com",
"contributions": [
"doc"
]
},
{
"login": "doe-base",
"name": "Daniel Idoko",
"avatar_url": "https://avatars.githubusercontent.com/u/95912955?v=4",
"profile": "https://danielidoko-r3zt.vercel.app/",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "fungilation",
"name": "Gary Fung",
"avatar_url": "https://avatars.githubusercontent.com/u/3803466?v=4",
"profile": "https://garyfung.medium.com",
"contributions": [
"doc",
"code"
]
}
],
"contributorsPerLine": 7,

View File

@@ -17,13 +17,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v4.0.0
- uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.6
version: 8.6.5
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 20
node-version: 18
cache: "pnpm"
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -38,13 +38,13 @@ jobs:
name: Build
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v4.0.0
- uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.6
version: 8.6.5
- name: Setup node
uses: actions/setup-node@v2
with:
node-version: 20
node-version: 18
cache: "pnpm"
- run: pnpm install --frozen-lockfile
- name: Build
@@ -68,14 +68,14 @@ jobs:
uses: actions/checkout@v3
- name: Setup PNPM
uses: pnpm/action-setup@v4.0.0
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.6
version: 8.6.5
- name: Setup node@16
uses: actions/setup-node@v2
with:
node-version: 20
node-version: 18
cache: "pnpm"
- name: Install dependencies
@@ -129,15 +129,15 @@ jobs:
- name: Setup PNPM
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
uses: pnpm/action-setup@v4.0.0
uses: pnpm/action-setup@v2.2.4
with:
version: 8.6.6
version: 8.6.5
- name: Setup node@18
if: matrix.folder != 'next-13-app-dir' || matrix.os != 'windows-latest'
uses: actions/setup-node@v2
with:
node-version: 20
node-version: 18
cache: "pnpm"
- name: Install dependencies

View File

@@ -26,9 +26,13 @@ jobs:
id: pr_info
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COMMENT_AT: ${{ github.event.comment.created_at }}
run: |
pr="$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})"
head_sha="$(echo "$pr" | jq -r .head.sha)"
updated_at="$(echo "$pr" | jq -r .updated_at)"
if [[ $(date -d $updated_at) > $(date -d $COMMENT_AT) ]]; exit 1; fi
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT

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-433-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-429-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/main/LICENSE">
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
@@ -79,9 +79,6 @@ Your financial contributions help ensure Blitz continues to be developed and mai
</a></td>
<td><a aria-label="Simon Lammes" href="https://github.com/simon-lammes">
<img alt="" src="https://avatars.githubusercontent.com/u/46446421?v=4" width="40px"/>
</a></td>
<td><a aria-label="Route Optimizer and Route Planning Software" href="https://route4me.com">
<img alt="" src="https://raw.githubusercontent.com/blitz-js/blitz/main/assets/route4me.png" width="40px"/>
</a></td>
</tr>
</table>
@@ -758,10 +755,6 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<tr>
<td align="center"><a href="https://timn.tech"><img src="https://avatars.githubusercontent.com/u/6324199?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Neutkens</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=timneutkens" title="Tests">⚠️</a></td>
<td align="center"><a href="https://redyetidev.github.io"><img src="https://avatars.githubusercontent.com/u/38299977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aviv Keller</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=RedYetiDev" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/bezalel6"><img src="https://avatars.githubusercontent.com/u/51681171?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bezalel6</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=bezalel6" title="Code">💻</a></td>
<td align="center"><a href="cherniavskii.com"><img src="https://avatars.githubusercontent.com/u/13808724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Cherniavskii</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=cherniavskii" title="Documentation">📖</a></td>
<td align="center"><a href="https://danielidoko-r3zt.vercel.app/"><img src="https://avatars.githubusercontent.com/u/95912955?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Idoko</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=doe-base" title="Tests">⚠️</a></td>
<td align="center"><a href="https://garyfung.medium.com"><img src="https://avatars.githubusercontent.com/u/3803466?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gary Fung</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=fungilation" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=fungilation" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -1,73 +1,5 @@
# next-blitz-auth
## 0.1.17
### Patch Changes
- Updated dependencies [565db3c5a]
- Updated dependencies [3fa3a4ef3]
- blitz@2.2.0
- @blitzjs/auth@2.2.0
- @blitzjs/next@2.2.0
- @blitzjs/rpc@2.2.0
- @blitzjs/config@2.2.0
## 0.1.16
### Patch Changes
- Updated dependencies [ce23d4ed0]
- @blitzjs/next@2.1.4
- blitz@2.1.4
- @blitzjs/auth@2.1.4
- @blitzjs/rpc@2.1.4
- @blitzjs/config@2.1.4
## 0.1.15
### Patch Changes
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- @blitzjs/auth@2.1.3
- @blitzjs/next@2.1.3
- @blitzjs/rpc@2.1.3
- blitz@2.1.3
- @blitzjs/config@2.1.3
## 0.1.14
### Patch Changes
- blitz@2.1.2
- @blitzjs/auth@2.1.2
- @blitzjs/next@2.1.2
- @blitzjs/rpc@2.1.2
- @blitzjs/config@2.1.2
## 0.1.13
### Patch Changes
- Updated dependencies [9a0ba87d1]
- @blitzjs/rpc@2.1.1
- blitz@2.1.1
- @blitzjs/next@2.1.1
- @blitzjs/auth@2.1.1
- @blitzjs/config@2.1.1
## 0.1.12
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
- @blitzjs/auth@2.1.0
- @blitzjs/next@2.1.0
- @blitzjs/rpc@2.1.0
- @blitzjs/config@2.1.0
## 0.1.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "next-blitz-auth",
"version": "0.1.17",
"version": "0.1.11",
"private": true,
"scripts": {
"blitz:dev": "next dev",
@@ -12,28 +12,28 @@
"schema": "prisma/schema.prisma"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "^4.5.0",
"@tanstack/react-query": "4.0.10",
"blitz": "2.2.0",
"blitz": "2.0.10",
"flatted": "3.2.7",
"next": "15.0.1",
"next": "canary",
"prisma": "^4.5.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"superjson": "1.11.0",
"zod": "3.23.8"
"zod": "3.20.2"
},
"devDependencies": {
"@types/node": "18.11.7",
"@types/react": "npm:types-react@19.0.0",
"@types/react-dom": "npm:types-react-dom@19.0.0",
"@types/react": "18.0.23",
"@types/react-dom": "18.0.7",
"eslint": "8.26.0",
"eslint-config-next": "13.0.0",
"typescript": "4.8.4"

Binary file not shown.

View File

@@ -1,4 +0,0 @@
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "src/blitz-server"
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())

View File

@@ -6,27 +6,26 @@ import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import {BlitzLogger} from "blitz"
import {RpcServerPlugin} from "@blitzjs/rpc"
const {api, getBlitzContext, useAuthenticatedBlitzContext, invoke, withBlitzAuth} =
setupBlitzServer({
plugins: [
AuthServerPlugin({
cookiePrefix: "web-cookie-prefix",
storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized,
}),
RpcServerPlugin({
logging: {
disablelevel: "debug",
},
onInvokeError(error) {
console.log("onInvokeError", error)
},
}),
],
logger: BlitzLogger({}),
})
const {api, getBlitzContext, useAuthenticatedBlitzContext, invoke} = setupBlitzServer({
plugins: [
AuthServerPlugin({
cookiePrefix: "web-cookie-prefix",
storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized,
}),
RpcServerPlugin({
logging: {
disablelevel: "debug",
},
onInvokeError(error) {
console.log("onInvokeError", error)
},
}),
],
logger: BlitzLogger({}),
})
export {api, getBlitzContext, useAuthenticatedBlitzContext, invoke, withBlitzAuth}
export {api, getBlitzContext, useAuthenticatedBlitzContext, invoke}
export const cliConfig: BlitzCliConfig = {
customTemplates: "src/templates",

View File

@@ -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<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */

View File

@@ -2,15 +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<React.JSX.IntrinsicElements["input"]> {
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<React.JSX.IntrinsicElements["div"]>
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}

View File

@@ -0,0 +1,4 @@
import {rpcHandler} from "@blitzjs/rpc"
import {api} from "../../../blitz-server"
export default api(rpcHandler({onError: (error, ctx) => console.log(error)}))

View File

@@ -12,5 +12,5 @@ export default async function getCurrentUser(input: null, ctx: Ctx) {
}
export const config = {
httpMethod: "POST",
httpMethod: "GET",
}

View File

@@ -23,30 +23,31 @@
]
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"next": "15.0.1",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"next": "canary",
"openid-client": "5.2.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"ts-node": "10.9.1",
"zod": "3.23.8"
"zod": "3.20.2"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@testing-library/react": "16.0.1",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "29.2.2",
"@types/node": "18.11.9",
"@types/preview-email": "2.0.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"@typescript-eslint/eslint-plugin": "5.42.1",
"eslint": "8.27.0",
"eslint-config-next": "12.3.1",

View File

@@ -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<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */

View File

@@ -2,15 +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<React.JSX.IntrinsicElements["input"]> {
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<React.JSX.IntrinsicElements["div"]>
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -24,30 +24,31 @@
]
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"next": "15.0.1",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"next": "canary",
"next-auth": "4.24.7",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"ts-node": "10.9.1",
"zod": "3.23.8"
"zod": "3.20.2"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "16.0.1",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/node": "18.11.9",
"@types/preview-email": "2.0.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"@typescript-eslint/eslint-plugin": "5.42.1",
"@vitejs/plugin-react": "2.2.0",
"eslint": "8.27.0",

View File

@@ -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<React.JSX.IntrinsicElements["form"]>, "onSubmit"> {
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
/** All your form fields */
children?: ReactNode
/** Text to display in the submit button */

View File

@@ -2,15 +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<React.JSX.IntrinsicElements["select"]> {
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
/** Field name. */
name: string
/** Field label. */
label: string
/** Field type. Doesn't include radio buttons and checkboxes */
options: any[]
outerProps?: PropsWithoutRef<React.JSX.IntrinsicElements["div"]>
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}

View File

@@ -2,15 +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<React.JSX.IntrinsicElements["input"]> {
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<React.JSX.IntrinsicElements["div"]>
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
labelProps?: ComponentPropsWithoutRef<"label">
}

View File

@@ -11,11 +11,15 @@ const LoginPage: BlitzPage = () => {
<LoginForm
onSuccess={(_user) => {
const next = router.query.next ? decodeURIComponent(router.query.next as string) : "/"
return router.push(next)
// return router.push(next)
}}
/>
</Layout>
)
}
LoginPage.authenticate = {
redirectTo: "/",
}
export default LoginPage

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -16,27 +16,27 @@
"schema": "./db/schema.prisma"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"@types/jest": "29.2.2",
"@types/passport-twitter": "1.0.37",
"blitz": "2.2.0",
"blitz": "2.0.10",
"jest": "29.3.0",
"jest-environment-jsdom": "29.3.0",
"next": "15.0.1",
"next": "canary",
"passport-mock-strategy": "2.0.0",
"passport-twitter": "1.0.4",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"ts-node": "10.9.1"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"eslint": "8.27.0",
"typescript": "^4.8.4"
}

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -17,30 +17,31 @@
"prisma:studio": "prisma studio"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"delay": "5.0.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "7.39.1",
"ts-node": "10.9.1",
"zod": "3.23.8"
"zod": "3.20.2"
},
"devDependencies": {
"@next/bundle-analyzer": "12.0.8",
"@testing-library/jest-dom": "5.16.5",
"@testing-library/react": "16.0.1",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/node": "18.11.9",
"@types/preview-email": "2.0.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"@typescript-eslint/eslint-plugin": "5.42.1",
"@vitejs/plugin-react": "2.2.0",
"eslint": "8.27.0",

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -17,16 +17,16 @@
"prisma:studio": "prisma studio"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -21,11 +21,14 @@ export const authenticateUser = async (email: string, password: string) => {
}
export default api(async (req, res, ctx) => {
const blitzContext = ctx
const user = await authenticateUser(req.query.email as string, req.query.password as string)
await ctx.session.$create({
await blitzContext.session.$create({
userId: user.id,
role: user.role as Role,
})
res.status(200).json({email: req.query.email, userId: ctx.session.userId})
res.status(200).json({email: req.query.email, userId: blitzContext.session.userId})
})

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -16,24 +16,24 @@
"schema": "db/schema.prisma"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"lowdb": "2.1.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
"@blitzjs/auth": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -11,20 +11,20 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
},
"dependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"blitz": "2.2.0",
"next": "15.0.1",
"react": "19.0.0",
"react-dom": "19.0.0"
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"blitz": "2.0.10",
"next": "canary",
"react": "18.2.0",
"react-dom": "18.2.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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"eslint": "8.27.0",
"fs-extra": "10.0.1",
"get-port": "6.1.2",

View File

@@ -1,15 +0,0 @@
import {withBlitzAuth} from "../../../src/blitz-server"
export const {POST} = withBlitzAuth({
POST: async (_request, _params, ctx) => {
const session = ctx.session
await session.$revoke()
return new Response(
JSON.stringify({
userId: session.userId,
}),
{status: 200},
)
},
})

View File

@@ -1,11 +0,0 @@
import {H} from "@blitzjs/auth/dist/index-0ecbee46"
import {withBlitzAuth} from "../../../src/blitz-server"
const emptyResponse = async () => {
return new Response(null, {status: 200})
}
export const {POST, HEAD} = withBlitzAuth({
POST: emptyResponse,
HEAD: emptyResponse,
})

View File

@@ -1,4 +0,0 @@
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "../../../../src/blitz-server"
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())

View File

@@ -1,38 +0,0 @@
import {withBlitzAuth} from "../../../src/blitz-server"
import prisma from "../../../db/index"
import {Role} from "../../../types"
export const authenticateUser = async (email: string, password: string) => {
const user = await prisma.user.findFirst({where: {email}})
if (!user) throw new Error("Authentication Error")
await prisma.user.update({where: {id: user.id}, data: {hashedPassword: password}})
const {hashedPassword, ...rest} = user
return rest
}
export const {POST} = withBlitzAuth({
POST: async (request: Request, context, ctx) => {
const {searchParams} = new URL(request.url)
const user = await authenticateUser(
searchParams.get("email") as string,
searchParams.get("password") as string,
)
await ctx.session.$create({
userId: user.id,
role: user.role as Role,
})
return new Response(
JSON.stringify({email: searchParams.get("email"), userId: ctx.session.userId}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
},
)
},
})

View File

@@ -1,5 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@@ -17,17 +17,17 @@
"prisma:studio": "prisma studio"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"lowdb": "2.1.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",
@@ -45,6 +45,6 @@
"node-fetch": "3.2.3",
"playwright": "1.28.0",
"ts-node": "10.9.1",
"typescript": "^4.9.5"
"typescript": "^4.8.4"
}
}

View File

@@ -0,0 +1,9 @@
import {api} from "../../src/blitz-server"
export default api(async (_req, res, ctx) => {
const blitzContext = ctx
await blitzContext.session.$revoke()
res.status(200).json({userId: blitzContext.session.userId})
})

View File

@@ -0,0 +1,5 @@
import {api} from "../../src/blitz-server"
export default api(async (_req, res, ctx) => {
res.status(200).end()
})

View File

@@ -0,0 +1,4 @@
import {rpcHandler} from "@blitzjs/rpc"
import {api} from "../../../src/blitz-server"
export default api(rpcHandler({onError: (error, ctx) => console.log(error)}))

View File

@@ -0,0 +1,34 @@
import {api} from "../../src/blitz-server"
import prisma from "../../db/index"
import {SecurePassword} from "@blitzjs/auth/secure-password"
import {Role} from "../../types"
export const authenticateUser = async (email: string, password: string) => {
const user = await prisma.user.findFirst({where: {email}})
if (!user) throw new Error("Authentication Error")
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 prisma.user.update({where: {id: user.id}, data: {hashedPassword: improvedHash}})
}
const {hashedPassword, ...rest} = user
return rest
}
export default api(async (req, res, ctx) => {
const blitzContext = ctx
const user = await authenticateUser(req.query.email as string, req.query.password as string)
await blitzContext.session.$create({
userId: user.id,
role: user.role as Role,
})
res.status(200).json({email: req.query.email, userId: blitzContext.session.userId})
})

View File

@@ -3,16 +3,16 @@ import {AuthServerPlugin, PrismaStorage} from "@blitzjs/auth"
import db from "../db"
import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import {BlitzLogger} from "blitz"
import {RpcServerPlugin} from "@blitzjs/rpc"
export const {api, getBlitzContext, withBlitzAuth} = setupBlitzServer({
const {api, getBlitzContext} = setupBlitzServer({
plugins: [
AuthServerPlugin({
cookiePrefix: "auth-tests-cookie-prefix",
storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized,
}),
RpcServerPlugin({}),
],
logger: BlitzLogger({}),
})
export {api, getBlitzContext}

View File

@@ -57,7 +57,7 @@ const runTests = (mode?: string) => {
"should render result for open query",
async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "POST",
method: "GET",
headers: {"Content-Type": "application/json; charset=utf-8"},
})
expect(res.status).toBe(200)
@@ -67,7 +67,7 @@ const runTests = (mode?: string) => {
it("sets correct cookie", async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "POST",
method: "GET",
headers: {"Content-Type": "application/json; charset=utf-8"},
})
const cookieHeader = res.headers.get("Set-Cookie")
@@ -94,8 +94,6 @@ const runTests = (mode?: string) => {
async () => {
const browser = await webdriver(appPort, "/react-query")
await browser.refresh()
browser.waitForElementByCss("#button", 0)
await browser.elementByCss("#button").click()
@@ -135,7 +133,7 @@ const runTests = (mode?: string) => {
it("does not require CSRF header on HEAD requests", async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "POST",
method: "GET",
headers: {"Content-Type": "application/json; charset=utf-8"},
})
const cookieHeader = res.headers.get("Set-Cookie")

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -16,24 +16,24 @@
"prisma:studio": "prisma studio"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -8,21 +8,21 @@
"clean": "rm -rf .turbo && rm -rf node_modules"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"@tanstack/react-query": "4.0.10",
"blitz": "2.2.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
"blitz": "2.0.10",
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@testing-library/react": "16.0.1",
"@types/react": "npm:types-react@19.0.0",
"@testing-library/react": "13.4.0",
"@types/react": "18.0.25",
"@vitejs/plugin-react": "1.3.0",
"delay": "5.0.0",
"eslint": "8.27.0",

View File

@@ -16,23 +16,23 @@
"schema": "db/schema.prisma"
},
"dependencies": {
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -7,19 +7,19 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
},
"dependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"blitz": "2.2.0",
"next": "15.0.1",
"react": "19.0.0",
"react-dom": "19.0.0"
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"blitz": "2.0.10",
"next": "canary",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/express": "4.17.13",
"@types/fs-extra": "9.0.13",
"@types/node-fetch": "2.6.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -7,19 +7,19 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
},
"dependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"blitz": "2.2.0",
"next": "15.0.1",
"react": "19.0.0",
"react-dom": "19.0.0"
"@blitzjs/config": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"blitz": "2.0.10",
"next": "canary",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@types/express": "4.17.13",
"@types/fs-extra": "9.0.13",
"@types/node-fetch": "2.6.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -16,24 +16,24 @@
"schema": "db/schema.prisma"
},
"dependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/next": "2.2.0",
"@blitzjs/rpc": "2.2.0",
"@prisma/client": "6.1.0",
"blitz": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/next": "2.0.10",
"@blitzjs/rpc": "2.0.10",
"@prisma/client": "4.6.1",
"blitz": "2.0.10",
"lowdb": "3.0.0",
"next": "15.0.1",
"prisma": "6.1.0",
"react": "19.0.0",
"react-dom": "19.0.0"
"next": "canary",
"prisma": "4.6.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@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": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"b64-lite": "1.4.0",
"eslint": "8.27.0",
"fs-extra": "10.0.1",

View File

@@ -9,6 +9,7 @@ declare module "@blitzjs/auth" {
PublicData: {
userId: User["id"]
role: Role
views?: number
}
}
}

View File

@@ -3,15 +3,15 @@
"version": "0.0.0",
"private": true,
"devDependencies": {
"@blitzjs/config": "workspace:2.2.0",
"@blitzjs/next": "workspace:2.2.0",
"@blitzjs/rpc": "workspace:2.2.0",
"@blitzjs/config": "workspace:2.0.10",
"@blitzjs/next": "workspace:2.0.10",
"@blitzjs/rpc": "workspace:2.0.10",
"@tanstack/react-query": "4.13.0",
"@testing-library/react": "16.0.1",
"@testing-library/react": "13.4.0",
"@types/express": "4.17.13",
"@types/fs-extra": "9.0.13",
"@types/node-fetch": "2.6.1",
"@types/react": "npm:types-react@19.0.0",
"@types/react": "18.0.25",
"@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": "19.0.0",
"react-dom": "19.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"resolve-cwd": "3.0.0",
"resolve-from": "5.0.0",
"rimraf": "3.0.2",

View File

@@ -29,9 +29,9 @@
"husky": "8.0.2",
"jsdom": "^19.0.0",
"lint-staged": "13.0.3",
"next": "15.0.1",
"next": "canary",
"only-allow": "1.1.0",
"prettier": "^2.8.8",
"prettier": "^2.7.1",
"prettier-plugin-prisma": "4.4.0",
"pretty-quick": "3.1.3",
"turbo": "1.10.9",
@@ -50,10 +50,7 @@
"next-auth@4.24.7": "patches/next-auth@4.24.7.patch"
},
"overrides": {
"@types/mime": "3.0.4",
"next": "15.0.1",
"@types/react": "npm:types-react@rc",
"@types/react-dom": "npm:types-react-dom@rc"
"@types/mime": "3.0.4"
}
}
}

View File

@@ -1,95 +1,5 @@
# @blitzjs/auth
## 2.2.0
### Minor Changes
- 3fa3a4ef3: chore: support next.js 15
### Patch Changes
- Updated dependencies [565db3c5a]
- Updated dependencies [3fa3a4ef3]
- blitz@2.2.0
## 2.1.4
### Patch Changes
- blitz@2.1.4
## 2.1.3
### Patch Changes
- 0b3286468: chore: bump `next.js` and `zod` versions
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- blitz@2.1.3
## 2.1.2
### Patch Changes
- blitz@2.1.2
## 2.1.1
### Patch Changes
- Updated dependencies [9a0ba87d1]
- blitz@2.1.1
## 2.1.0
### Minor Changes
- 3b10b13e6: feat: add blitz auth support for the Web `Request` API standard
Usage using the new `withBlitzAuth` adapter in the App Router:
```ts
import {withBlitzAuth} from "app/blitz-server"
export const {POST} = withBlitzAuth({
POST: async (_request, _params, ctx) => {
const session = ctx.session
await session.$revoke()
return new Response(
JSON.stringify({
userId: session.userId,
}),
{status: 200},
)
},
})
```
feat: New Blitz RPC handler meant to with the next.js app router `route.ts` files
Usage using the new `rpcAppHandler` function
```ts
// app/api/rpc/[[...blitz]]/route.ts
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "app/blitz-server"
// Usage with blitz auth
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())
// Standalone usage
export const {GET, POST, HEAD} = rpcAppHandler()
```
chore: Update the app directory starter
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
## 2.0.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/auth",
"version": "2.2.0",
"version": "2.0.10",
"homepage": "https://blitzjs.com/",
"repository": {
"type": "git",
@@ -50,7 +50,7 @@
"url": "0.11.0"
},
"peerDependencies": {
"blitz": "2.2.0",
"blitz": "2.0.10",
"next": "*",
"next-auth": "*",
"secure-password": "4.0.0"
@@ -67,18 +67,19 @@
}
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@testing-library/react": "16.0.1",
"@blitzjs/config": "2.0.10",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@types/cookie": "0.4.1",
"@types/debug": "4.1.7",
"@types/jsonwebtoken": "8.5.8",
"@types/react": "npm:types-react@19.0.0",
"@types/react-dom": "npm:types-react-dom@19.0.0",
"blitz": "2.2.0",
"next": "15.0.1",
"@types/react": "18.0.25",
"@types/react-dom": "17.0.14",
"blitz": "2.0.10",
"next": "canary",
"next-auth": "4.24.7",
"react": "19.0.0",
"react-dom": "19.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"secure-password": "4.0.0",
"typescript": "^4.8.4",
"unbuild": "0.7.6",

View File

@@ -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"
import {renderHook} from "@testing-library/react-hooks"
vi.mock("blitz", async () => {
const blitz = await vi.importActual("blitz")

View File

@@ -260,7 +260,7 @@ export type RedirectAuthenticatedToFn = (
) => RedirectAuthenticatedTo
export type BlitzPage<P = {}> = React.ComponentType<P> & {
getLayout?: (component: React.JSX.Element) => React.JSX.Element
getLayout?: (component: JSX.Element) => JSX.Element
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
suppressFirstRenderFlicker?: boolean
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn

View File

@@ -1,6 +1,6 @@
import {RequestMiddleware, Ctx, createServerPlugin} from "blitz"
import {assert} from "blitz"
import type {IncomingMessage, ServerResponse} from "http"
import {IncomingMessage, ServerResponse} from "http"
import {PublicData, SessionModel, SessionConfigMethods} from "../shared/types"
import {getBlitzContext, getSession, useAuthenticatedBlitzContext} from "./auth-sessions"
@@ -130,28 +130,11 @@ export const AuthServerPlugin = createServerPlugin((options: AuthPluginOptions)
if (!globalThis.__BLITZ_GET_RSC_CONTEXT) {
globalThis.__BLITZ_GET_RSC_CONTEXT = getBlitzContext
}
type Handler = (request: Request, context: any, ctx: Ctx) => Promise<Response> | Response
function withBlitzAuth<T extends {[method: string]: Handler}>(handlers: T): T {
return Object.fromEntries(
Object.entries(handlers).map(([method, handler]) => [
method,
async (request: Request, params: unknown) => {
const session = await getSession(request)
const response = await handler(request, params, {session})
session.setSession(response)
return response
},
]),
) as unknown as T
}
return {
requestMiddlewares: [authPluginSessionMiddleware()],
exports: () => ({
getBlitzContext,
useAuthenticatedBlitzContext,
withBlitzAuth,
}),
}
})

View File

@@ -0,0 +1,41 @@
import {expect, describe, it} from "vitest"
import {setCookie} from "./auth-sessions"
import cookie from "cookie"
import {ServerResponse} from "http"
describe("blitz-auth", () => {
describe("setCookie", () => {
it("works with empty start", async () => {
const res = new ServerResponse({} as any)
setCookie(res, cookie.serialize("A", "a-value", {}))
expect(res.getHeader("Set-Cookie")).toBe("A=a-value")
})
it("works with string start", async () => {
const res = new ServerResponse({} as any)
res.setHeader("Set-Cookie", cookie.serialize("A", "a-value", {}))
setCookie(res, cookie.serialize("B", "b-value", {}))
expect(res.getHeader("Set-Cookie")).toEqual(["A=a-value", "B=b-value"])
})
it("works with array start for new name", async () => {
const res = new ServerResponse({} as any)
res.setHeader("Set-Cookie", [
cookie.serialize("A", "a-value", {}),
cookie.serialize("B", "b-value", {}),
])
setCookie(res, cookie.serialize("C", "c-value", {}))
expect(res.getHeader("Set-Cookie")).toEqual(["A=a-value", "B=b-value", "C=c-value"])
})
it("works with array start for existing name", async () => {
const res = new ServerResponse({} as any)
res.setHeader("Set-Cookie", [
cookie.serialize("A", "a-value", {}),
cookie.serialize("B", "b-value", {}),
])
setCookie(res, cookie.serialize("A", "new-a-value", {}))
expect(res.getHeader("Set-Cookie")).toEqual(["A=new-a-value", "B=b-value"])
})
})
})

View File

@@ -1,6 +1,7 @@
import {fromBase64, toBase64} from "b64-lite"
import cookie, {parse} from "cookie"
import jsonwebtoken from "jsonwebtoken"
import {IncomingMessage, ServerResponse} from "http"
import {sign as jwtSign, verify as jwtVerify} from "jsonwebtoken"
import {
assert,
isPast,
@@ -12,9 +13,9 @@ import {
AuthorizationError,
CSRFTokenMismatchError,
log,
AuthenticatedCtx,
baseLogger,
chalk,
AuthenticatedCtx,
} from "blitz"
import {
EmptyPublicData,
@@ -38,69 +39,12 @@ import {
AuthenticatedSessionContext,
} from "../shared"
import {generateToken, hash256} from "./auth-utils"
import {Socket} from "net"
import {UrlObject} from "url"
import {formatWithValidation} from "../shared/url-utils"
import type {UrlObject} from "url"
import type {IncomingMessage, ServerResponse} from "http"
function splitCookiesString(cookiesString: string) {
if (!cookiesString) return []
let cookiesStrings = []
let pos = 0
let start
let ch
let lastComma
let nextStart
let cookiesSeparatorFound
function skipWhitespace() {
while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
pos += 1
}
return pos < cookiesString.length
}
function notSpecialChar() {
ch = cookiesString.charAt(pos)
return ch !== "=" && ch !== ";" && ch !== ","
}
while (pos < cookiesString.length) {
start = pos
cookiesSeparatorFound = false
while (skipWhitespace()) {
ch = cookiesString.charAt(pos)
if (ch === ",") {
lastComma = pos
pos += 1
skipWhitespace()
nextStart = pos
while (pos < cookiesString.length && notSpecialChar()) {
pos += 1
}
if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
cookiesSeparatorFound = true
pos = nextStart
cookiesStrings.push(cookiesString.substring(start, lastComma))
start = pos
} else {
pos = lastComma + 1
}
} else {
pos += 1
}
}
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
cookiesStrings.push(cookiesString.substring(start, cookiesString.length))
}
}
return cookiesStrings
}
export function isLocalhost(req: IncomingMessage | Request): boolean {
let host: string | undefined
if (req instanceof Request) {
host = req.headers.get("host") || ""
} else {
host = req.headers.host || ""
}
export function isLocalhost(req: IncomingMessage): boolean {
let {host} = req.headers
let localhost = false
if (host) {
host = host.split(":")[0]
@@ -125,8 +69,7 @@ export function getCookieParser(headers: {[key: string]: undefined | string | st
}
}
import Debug from "debug"
const debug = Debug("blitz:session")
const debug = require("debug")("blitz:session")
export interface SimpleRolesIsAuthorized<RoleType = string> {
({ctx, args}: {ctx: any; args: [roleOrRoles?: RoleType | RoleType[]]}): boolean
@@ -188,6 +131,14 @@ type AuthedSessionKernel = {
}
type SessionKernel = AnonSessionKernel | AuthedSessionKernel
function ensureApiRequest(
req: IncomingMessage & {[key: string]: any},
): asserts req is IncomingMessage & {cookies: {[key: string]: string}} {
if (!("cookies" in req)) {
// Cookie parser isn't include inside getServerSideProps, so we have to add it
req.cookies = getCookieParser(req.headers)()
}
}
function ensureMiddlewareResponse(
res: ServerResponse & {[key: string]: any},
): asserts res is ServerResponse & {blitzCtx: Ctx} {
@@ -196,114 +147,58 @@ function ensureMiddlewareResponse(
}
}
function convertRequestToHeader(req: IncomingMessage | Request): Headers {
const headersFromRequest = req.headers
if (headersFromRequest instanceof Headers) {
return headersFromRequest
} else {
const headers = new Headers()
Object.entries(headersFromRequest).forEach(([key, value]) => {
if (value) {
if (Array.isArray(value)) {
headers.append(key, value.join(","))
} else {
headers.append(key, value)
}
}
})
return headers
}
}
function getCookiesFromHeader(headers: Headers) {
const cookieHeader = headers.get("Cookie")
if (cookieHeader) {
return cookie.parse(cookieHeader)
} else {
return {}
}
}
export async function getSession(req: Request): Promise<SessionContext>
export async function getSession(req: Request, res: never, isRsc: boolean): Promise<SessionContext>
export async function getSession(req: IncomingMessage, res: ServerResponse): Promise<SessionContext>
export async function getSession(
req: IncomingMessage,
res: ServerResponse,
isRsc: boolean,
): Promise<SessionContext>
export async function getSession(
req: IncomingMessage | Request,
res?: ServerResponse,
isRsc?: boolean,
appDir = false,
): Promise<SessionContext> {
const headers = convertRequestToHeader(req)
if (res) {
ensureMiddlewareResponse(res)
debug("cookiePrefix", globalThis.__BLITZ_SESSION_COOKIE_PREFIX)
if (res.blitzCtx.session) {
debug("Returning existing session")
return res.blitzCtx.session
}
ensureApiRequest(req)
ensureMiddlewareResponse(res)
debug("cookiePrefix", globalThis.__BLITZ_SESSION_COOKIE_PREFIX)
if (res.blitzCtx.session) {
debug("Returning existing session")
return res.blitzCtx.session
}
const method = req.method
let sessionKernel = await getSessionKernel({headers, method})
let sessionKernel = await getSessionKernel(req, res)
if (sessionKernel) {
debug("Got existing session", sessionKernel)
}
if (!sessionKernel) {
sessionKernel = await createAnonymousSession({headers})
debug("No session found, creating anonymous session")
sessionKernel = await createAnonymousSession(req, res)
}
const sessionContext = makeProxyToPublicData(
new SessionContextClass(headers, sessionKernel, !!isRsc, res),
new SessionContextClass(req, res, sessionKernel, appDir),
)
debug("New session context")
if (res) {
;(res as any).blitzCtx = {
session: sessionContext,
}
sessionContext.setSession(res)
}
res.blitzCtx.session = sessionContext
return sessionContext
}
interface RouteUrlObject extends Pick<UrlObject, "pathname" | "query" | "href"> {
pathname: string
}
const makeProxyToPublicData = <T extends SessionContextClass>(ctxClass: T): T => {
return new Proxy(ctxClass, {
get(target, prop, receiver) {
if (prop in target || prop === "then") {
return Reflect.get(target, prop, receiver)
} else {
return Reflect.get(target.$publicData, prop, receiver)
}
},
})
}
export async function getBlitzContext(): Promise<Ctx> {
try {
const {headers, cookies} = require("next/headers")
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
const req = new IncomingMessage(new Socket()) as IncomingMessage & {
cookies: {[key: string]: string}
}
const session = await getSession(
{
headers: new Headers(reqHeader),
method: "POST",
} as Request,
null as never,
true,
req.headers = Object.fromEntries(headers())
const csrfToken = cookies().get(COOKIE_CSRF_TOKEN())
if (csrfToken) {
req.headers[HEADER_CSRF] = csrfToken.value
}
req.cookies = Object.fromEntries(
cookies()
.getAll()
.map((c: {name: string; value: string}) => [c.name, c.value]),
)
const res = new ServerResponse(req)
const session = await getSession(req, res, true)
const ctx: Ctx = {
session,
}
@@ -311,13 +206,17 @@ export async function getBlitzContext(): Promise<Ctx> {
} catch (e) {
if ((e as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND") {
throw new Error(
"Usage of `getBlitzContext` is supported only in next.js 13.0.0 and above. Please upgrade your next.js version.",
"Usage of `useAuthenticatedBlitzContext` is supported only in next.js 13.0.0 and above. Please upgrade your next.js version.",
)
}
throw e
}
}
interface RouteUrlObject extends Pick<UrlObject, "pathname" | "query" | "href"> {
pathname: string
}
export async function useAuthenticatedBlitzContext({
redirectTo,
redirectAuthenticatedTo,
@@ -392,6 +291,18 @@ export async function useAuthenticatedBlitzContext({
}
}
const makeProxyToPublicData = <T extends SessionContextClass>(ctxClass: T): T => {
return new Proxy(ctxClass, {
get(target, prop, receiver) {
if (prop in target || prop === "then") {
return Reflect.get(target, prop, receiver)
} else {
return Reflect.get(target.$publicData, prop, receiver)
}
},
})
}
const NotSupportedMessage = async (method: string) => {
const message = `Method ${method} is not yet supported in React Server Components`
const _box = await log.box(message, log.chalk.hex("8a3df0").bold("Blitz Auth"))
@@ -399,27 +310,21 @@ const NotSupportedMessage = async (method: string) => {
}
export class SessionContextClass implements SessionContext {
private _headers: Headers
private _req: IncomingMessage & {cookies: {[key: string]: string}}
private _res: ServerResponse & {blitzCtx: Ctx}
private _kernel: SessionKernel
private _isRsc: boolean
private _response?: ServerResponse
private _appDir: boolean
private static headersToIncludeInResponse = [
HEADER_CSRF,
HEADER_CSRF_ERROR,
HEADER_PUBLIC_DATA_TOKEN,
HEADER_SESSION_CREATED,
]
constructor(headers: Headers, kernel: SessionKernel, isRsc: boolean, response?: ServerResponse) {
this._headers = headers
constructor(
req: IncomingMessage & {cookies: {[key: string]: string}},
res: ServerResponse & {blitzCtx: Ctx},
kernel: SessionKernel,
appDir: boolean,
) {
this._req = req
this._res = res
this._kernel = kernel
this._isRsc = isRsc
this._response = response
}
$antiCSRFToken() {
return this._kernel.antiCSRFToken
this._appDir = appDir
}
get $handle() {
@@ -447,69 +352,38 @@ export class SessionContextClass implements SessionContext {
$isAuthorized(...args: IsAuthorizedArgs) {
if (!this.userId) return false
return global.sessionConfig.isAuthorized({
ctx: {
session: this as AuthenticatedSessionContext,
},
args,
})
return global.sessionConfig.isAuthorized({ctx: this._res.blitzCtx, args})
}
$thisIsAuthorized(...args: IsAuthorizedArgs): this is AuthenticatedSessionContext {
return this.$isAuthorized(...args)
}
setSession(response: Response | ServerResponse) {
if (this._isRsc) {
void NotSupportedMessage("setSession")
return
}
const cookieHeaders = this._headers.get("set-cookie")
if (response instanceof Response) {
response.headers.set("Set-Cookie", cookieHeaders!)
} else {
response.setHeader("Set-Cookie", splitCookiesString(cookieHeaders!))
}
const headers = this._headers.entries()
for (const [key, value] of headers) {
if (SessionContextClass.headersToIncludeInResponse.includes(key)) {
if (response instanceof Response) {
response.headers.set(key, value)
} else {
response.setHeader(key, value)
}
}
}
}
async $create(publicData: PublicData, privateData?: Record<any, any>) {
if (this._isRsc) {
if (this._appDir) {
void NotSupportedMessage("$create")
return
}
this._kernel = await createNewSession({
headers: this._headers,
req: this._req,
res: this._res,
publicData,
privateData,
jwtPayload: this._kernel.jwtPayload,
anonymous: false,
})
if (this._response) this.setSession(this._response)
}
async $revoke() {
if (this._isRsc) {
if (this._appDir) {
void NotSupportedMessage("$revoke")
return
}
this._kernel = await revokeSession(this._headers, this.$handle)
if (this._response) this.setSession(this._response)
this._kernel = await revokeSession(this._req, this._res, this.$handle)
}
async $revokeAll() {
if (this._isRsc) {
if (this._appDir) {
void NotSupportedMessage("$revokeAll")
return
}
@@ -521,28 +395,25 @@ export class SessionContextClass implements SessionContext {
}
async $setPublicData(data: Record<any, any>) {
if (this._isRsc) {
if (this._appDir) {
void NotSupportedMessage("$setPublicData")
return
}
if (this.userId) {
await syncPubicDataFieldsForUserIfNeeded(this.userId, data)
}
this._kernel.publicData = await setPublicData(this._headers, this._kernel, data)
if (this._response) this.setSession(this._response)
this._kernel.publicData = await setPublicData(this._req, this._res, this._kernel, data)
}
async $getPrivateData() {
return (await getPrivateData(this.$handle)) || {}
}
async $setPrivateData(data: Record<any, any>) {
if (this._isRsc) {
$setPrivateData(data: Record<any, any>) {
if (this._appDir) {
void NotSupportedMessage("$setPrivateData")
return Promise.resolve()
}
await setPrivateData(this._kernel, data)
if (this._response) this.setSession(this._response)
return setPrivateData(this._kernel, data)
}
}
@@ -634,7 +505,7 @@ const JWT_ANONYMOUS_SUBJECT = "anonymous"
const JWT_ALGORITHM = "HS256"
const createAnonymousSessionToken = (payload: AnonymousSessionPayload) => {
return jsonwebtoken.sign({[JWT_NAMESPACE]: payload}, getSessionSecretKey() || "", {
return jwtSign({[JWT_NAMESPACE]: payload}, getSessionSecretKey() || "", {
algorithm: JWT_ALGORITHM,
issuer: JWT_ISSUER,
audience: JWT_AUDIENCE,
@@ -648,7 +519,7 @@ const parseAnonymousSessionToken = (token: string) => {
const secret = getSessionSecretKey()
try {
const fullPayload = jsonwebtoken.verify(token, secret!, {
const fullPayload = jwtVerify(token, secret!, {
algorithms: [JWT_ALGORITHM],
issuer: JWT_ISSUER,
audience: JWT_AUDIENCE,
@@ -665,127 +536,126 @@ const parseAnonymousSessionToken = (token: string) => {
}
}
const cookieOptions = (headers: Headers, expires: Date, httpOnly: boolean) => {
return {
path: "/",
secure:
global.sessionConfig.secureCookies &&
!isLocalhost({
headers,
} as Request),
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: new Date(expires),
httpOnly,
}
}
export const setCookie = (res: ServerResponse, cookieStr: string) => {
const getCookieName = (c: string) => c.split("=", 2)[0]
const appendCookie = () => append(res, "Set-Cookie", cookieStr)
function replaceOrAppendValueInSetCookieHeader(
headers: Headers,
cookieName: string,
newValue: string,
): string {
const cookies = headers.get("set-cookie")
if (!cookies) return newValue
const cookiesAsArray = splitCookiesString(cookies!)
for (let i = 0; i < cookiesAsArray.length; i++) {
const cookie = cookiesAsArray[i]
if (cookie?.startsWith(cookieName)) {
cookiesAsArray[i] = newValue
return cookiesAsArray.join(", ")
const cookiesHeader = res.getHeader("Set-Cookie")
const cookieName = getCookieName(cookieStr)
if (typeof cookiesHeader !== "string" && !Array.isArray(cookiesHeader)) {
appendCookie()
return
}
if (typeof cookiesHeader === "string") {
if (cookieName === getCookieName(cookiesHeader)) {
res.setHeader("Set-Cookie", cookieStr)
} else {
if (i === cookiesAsArray.length - 1) {
cookiesAsArray.push(newValue)
return cookiesAsArray.join(", ")
appendCookie()
}
} else {
for (let i = 0; i < cookiesHeader.length; i++) {
if (cookieName === getCookieName(cookiesHeader[i] || "")) {
cookiesHeader[i] = cookieStr
res.setHeader("Set-Cookie", cookiesHeader)
return
}
}
appendCookie()
}
return cookiesAsArray.filter(Boolean).join(", ")
}
const setSessionCookie = (headers: Headers, sessionToken: string, expiresAt: Date) => {
const sessionCookie = cookie.serialize(
COOKIE_SESSION_TOKEN(),
sessionToken,
cookieOptions(headers, expiresAt, true),
)
const newCookies = replaceOrAppendValueInSetCookieHeader(
headers,
COOKIE_SESSION_TOKEN(),
sessionCookie,
)
headers.set("Set-Cookie", newCookies)
const setHeader = (res: ServerResponse, name: string, value: string) => {
res.setHeader(name, value)
if ("_blitz" in res) {
;(res as any)._blitz[name] = value
}
}
const setAnonymousSessionCookie = (headers: Headers, token: string, expiresAt: Date) => {
const anonCookie = cookie.serialize(
COOKIE_ANONYMOUS_SESSION_TOKEN(),
token,
cookieOptions(headers, expiresAt, true),
const setSessionCookie = (res: ServerResponse, sessionToken: string, expiresAt: Date) => {
setCookie(
res,
cookie.serialize(COOKIE_SESSION_TOKEN(), sessionToken, {
path: "/",
httpOnly: true,
secure: global.sessionConfig.secureCookies,
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
)
const newCookies = replaceOrAppendValueInSetCookieHeader(
headers,
COOKIE_ANONYMOUS_SESSION_TOKEN(),
anonCookie,
)
headers.set("Set-Cookie", newCookies)
}
const setCSRFCookie = (headers: Headers, antiCSRFToken: string, expiresAt: Date) => {
const setAnonymousSessionCookie = (res: ServerResponse, token: string, expiresAt: Date) => {
setCookie(
res,
cookie.serialize(COOKIE_ANONYMOUS_SESSION_TOKEN(), token, {
path: "/",
httpOnly: true,
secure: global.sessionConfig.secureCookies,
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
)
}
const setCSRFCookie = (
req: IncomingMessage,
res: ServerResponse,
antiCSRFToken: string,
expiresAt: Date,
) => {
debug("setCSRFCookie", antiCSRFToken)
assert(antiCSRFToken !== undefined, "Internal error: antiCSRFToken is being set to undefined")
const csrfCookie = cookie.serialize(
COOKIE_CSRF_TOKEN(),
antiCSRFToken,
cookieOptions(headers, expiresAt, false),
setCookie(
res,
cookie.serialize(COOKIE_CSRF_TOKEN(), antiCSRFToken, {
path: "/",
secure: global.sessionConfig.secureCookies && !isLocalhost(req),
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
)
const newCookies = replaceOrAppendValueInSetCookieHeader(headers, COOKIE_CSRF_TOKEN(), csrfCookie)
headers.set("Set-Cookie", newCookies)
}
const setPublicDataCookie = (headers: Headers, publicDataToken: string, expiresAt: Date) => {
headers.set(HEADER_PUBLIC_DATA_TOKEN, "updated")
const publicDataCookie = cookie.serialize(
COOKIE_PUBLIC_DATA_TOKEN(),
publicDataToken,
cookieOptions(headers, expiresAt, false),
const setPublicDataCookie = (
req: IncomingMessage,
res: ServerResponse,
publicDataToken: string,
expiresAt: Date,
) => {
setHeader(res, HEADER_PUBLIC_DATA_TOKEN, "updated")
setCookie(
res,
cookie.serialize(COOKIE_PUBLIC_DATA_TOKEN(), publicDataToken, {
path: "/",
secure: global.sessionConfig.secureCookies && !isLocalhost(req),
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
)
const newCookies = replaceOrAppendValueInSetCookieHeader(
headers,
COOKIE_PUBLIC_DATA_TOKEN(),
publicDataCookie,
)
headers.set("Set-Cookie", newCookies)
}
// --------------------------------
// Get Session
// --------------------------------
async function getSessionKernel({
headers,
method,
}: {
headers: Headers
method: string | undefined
}): Promise<SessionKernel | null> {
const cookies = getCookiesFromHeader(headers)
const anonymousSessionToken = cookies[COOKIE_ANONYMOUS_SESSION_TOKEN()]
const sessionToken = cookies[COOKIE_SESSION_TOKEN()] // for essential method
const idRefreshToken = cookies[COOKIE_REFRESH_TOKEN()] // for advanced method
const antiCSRFToken = headers.get(HEADER_CSRF)
debug("getSessionKernel", {
anonymousSessionToken,
sessionToken,
idRefreshToken,
antiCSRFToken,
})
async function getSessionKernel(
req: IncomingMessage & {cookies: {[key: string]: string}},
res: ServerResponse,
): Promise<SessionKernel | null> {
const anonymousSessionToken = req.cookies[COOKIE_ANONYMOUS_SESSION_TOKEN()]
const sessionToken = req.cookies[COOKIE_SESSION_TOKEN()] // for essential method
const idRefreshToken = req.cookies[COOKIE_REFRESH_TOKEN()] // for advanced method
const enableCsrfProtection =
method !== "GET" &&
method !== "OPTIONS" &&
method !== "HEAD" &&
req.method !== "GET" &&
req.method !== "OPTIONS" &&
req.method !== "HEAD" &&
!process.env.DANGEROUSLY_DISABLE_CSRF_PROTECTION
const antiCSRFToken = req.headers[HEADER_CSRF] as string | undefined
if (sessionToken) {
debug("[getSessionKernel] Request has sessionToken")
@@ -828,7 +698,7 @@ async function getSessionKernel({
)
}
headers.set(HEADER_CSRF_ERROR, "true")
setHeader(res, HEADER_CSRF_ERROR, "true")
throw new CSRFTokenMismatchError()
}
@@ -840,7 +710,7 @@ async function getSessionKernel({
* But only renew with non-GET requests because a GET request could be from a
* browser level navigation
*/
if (method !== "GET") {
if (req.method !== "GET") {
// The publicData in the DB could have been updated since this client last made
// a request. If so, then we generate a new access token
const hasPublicDataChanged =
@@ -863,7 +733,8 @@ async function getSessionKernel({
if (hasPublicDataChanged || hasQuarterExpiryTimePassed) {
await refreshSession(
headers,
req,
res,
{
handle,
publicData: JSON.parse(persistedSession.publicData || ""),
@@ -903,7 +774,7 @@ async function getSessionKernel({
)
}
headers.set(HEADER_CSRF_ERROR, "true")
setHeader(res, HEADER_CSRF_ERROR, "true")
throw new CSRFTokenMismatchError()
}
@@ -924,14 +795,16 @@ async function getSessionKernel({
// Create Session
// --------------------------------
interface CreateNewAnonSession {
headers: Headers
req: IncomingMessage
res: ServerResponse
publicData: EmptyPublicData
privateData?: Record<any, any>
anonymous: true
jwtPayload?: JwtPayload
}
interface CreateNewAuthedSession {
headers: Headers
req: IncomingMessage
res: ServerResponse
publicData: PublicData
privateData?: Record<any, any>
anonymous: false
@@ -941,6 +814,7 @@ interface CreateNewAuthedSession {
async function createNewSession(
args: CreateNewAnonSession | CreateNewAuthedSession,
): Promise<SessionKernel> {
const {req, res} = args
assert(args.publicData.userId !== undefined, "You must provide publicData.userId")
const antiCSRFToken = createAntiCSRFToken()
@@ -961,12 +835,12 @@ async function createNewSession(
new Date(),
global.sessionConfig.anonSessionExpiryMinutes as number,
)
setAnonymousSessionCookie(args.headers, anonymousSessionToken, expiresAt)
setCSRFCookie(args.headers, antiCSRFToken, expiresAt)
setPublicDataCookie(args.headers, publicDataToken, expiresAt)
setAnonymousSessionCookie(res, anonymousSessionToken, expiresAt)
setCSRFCookie(req, res, antiCSRFToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt)
// Clear the essential session cookie in case it was previously set
setSessionCookie(args.headers, "", new Date(0))
args.headers.set(HEADER_SESSION_CREATED, "true")
setSessionCookie(res, "", new Date(0))
setHeader(res, HEADER_SESSION_CREATED, "true")
return {
handle,
@@ -1017,13 +891,12 @@ async function createNewSession(
privateData: JSON.stringify(newPrivateData),
})
setSessionCookie(args.headers, sessionToken, expiresAt)
debug("Session created", {handle, publicData: newPublicData, expiresAt})
setCSRFCookie(args.headers, antiCSRFToken, expiresAt)
setPublicDataCookie(args.headers, publicDataToken, expiresAt)
setSessionCookie(res, sessionToken, expiresAt)
setCSRFCookie(req, res, antiCSRFToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt)
// Clear the anonymous session cookie in case it was previously set
setAnonymousSessionCookie(args.headers, "", new Date(0))
args.headers.set(HEADER_SESSION_CREATED, "true")
setAnonymousSessionCookie(res, "", new Date(0))
setHeader(res, HEADER_SESSION_CREATED, "true")
return {
handle,
@@ -1041,9 +914,10 @@ async function createNewSession(
}
}
async function createAnonymousSession({headers}: {headers: Headers}) {
async function createAnonymousSession(req: IncomingMessage, res: ServerResponse) {
return await createNewSession({
headers,
req,
res,
publicData: {userId: null},
anonymous: true,
})
@@ -1054,7 +928,8 @@ async function createAnonymousSession({headers}: {headers: Headers}) {
// --------------------------------
async function refreshSession(
headers: Headers,
req: IncomingMessage,
res: ServerResponse,
sessionKernel: SessionKernel,
{publicDataChanged}: {publicDataChanged: boolean},
) {
@@ -1068,8 +943,8 @@ async function refreshSession(
const publicDataToken = createPublicDataToken(sessionKernel.publicData)
const expiresAt = addYears(new Date(), 30)
setAnonymousSessionCookie(headers, anonymousSessionToken, expiresAt)
setPublicDataCookie(headers, publicDataToken, expiresAt)
setAnonymousSessionCookie(res, anonymousSessionToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt)
} else if (global.sessionConfig.method === "essential" && "sessionToken" in sessionKernel) {
const expiresAt = addMinutes(new Date(), global.sessionConfig.sessionExpiryMinutes as number)
@@ -1077,7 +952,7 @@ async function refreshSession(
if (publicDataChanged) {
debug("Public data has changed")
const publicDataToken = createPublicDataToken(sessionKernel.publicData)
setPublicDataCookie(headers, publicDataToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt)
await global.sessionConfig.updateSession(sessionKernel.handle, {
expiresAt,
publicData: JSON.stringify(sessionKernel.publicData),
@@ -1119,7 +994,12 @@ async function syncPubicDataFieldsForUserIfNeeded(
}
}
async function revokeSession(headers: Headers, handle: string, anonymous: boolean = false) {
async function revokeSession(
req: IncomingMessage,
res: ServerResponse,
handle: string,
anonymous: boolean = false,
) {
debug("Revoking session", handle)
if (!anonymous) {
try {
@@ -1132,9 +1012,7 @@ async function revokeSession(headers: Headers, handle: string, anonymous: boolea
// This fixes race condition where all client side queries get refreshed
// in parallel and each creates a new anon session
// https://github.com/blitz-js/blitz/issues/2746
return createAnonymousSession({
headers,
})
return createAnonymousSession(req, res)
}
async function revokeAllSessionsForUser(userId: PublicData["userId"]) {
@@ -1200,7 +1078,8 @@ async function setPrivateData(sessionKernel: SessionKernel, data: Record<any, an
}
async function setPublicData(
headers: Headers,
req: IncomingMessage,
res: ServerResponse,
sessionKernel: SessionKernel,
data: Record<any, any>,
) {
@@ -1212,7 +1091,7 @@ async function setPublicData(
...data,
} as PublicData
await refreshSession(headers, {...sessionKernel, publicData}, {publicDataChanged: true})
await refreshSession(req, res, {...sessionKernel, publicData}, {publicDataChanged: true})
return publicData
}

View File

@@ -9,6 +9,8 @@ export {
getSession,
isLocalhost,
setPublicDataForUser,
setCookie,
simpleRolesIsAuthorized,
getBlitzContext,
} from "./auth-sessions"
export type {AnonymousSessionPayload, SimpleRolesIsAuthorized} from "./auth-sessions"

View File

@@ -1,6 +1,5 @@
//@ts-nocheck
import type {Ctx} from "blitz"
import type {ServerResponse} from "http"
import {Ctx} from "blitz"
export interface Session {
// isAuthorize can be injected here
@@ -67,12 +66,6 @@ export interface SessionContextBase {
$getPrivateData: () => Promise<Record<any, any>>
$setPrivateData: (data: Record<any, any>) => Promise<void>
$setPublicData: (data: Partial<Omit<PublicData, "userId">>) => Promise<void>
/**
* This function is only for manual session handling
*
* Instead use {@link https://blitzjs.com/docs/auth-server#with-blitz-auth-api withBlitzAuth} to handle session creation and update
*/
setSession: (res: Response | ServerResponse) => void
}
// Could be anonymous

View File

@@ -1,102 +1,5 @@
# @blitzjs/next
## 2.2.0
### Minor Changes
- 3fa3a4ef3: chore: support next.js 15
### Patch Changes
- Updated dependencies [565db3c5a]
- Updated dependencies [3fa3a4ef3]
- blitz@2.2.0
- @blitzjs/rpc@2.2.0
## 2.1.4
### Patch Changes
- ce23d4ed0: fix: Update `turbopack-empty.js` syntax to support latest turbopack and next.js versions
- blitz@2.1.4
- @blitzjs/rpc@2.1.4
## 2.1.3
### Patch Changes
- 0b3286468: chore: bump `next.js` and `zod` versions
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- @blitzjs/rpc@2.1.3
- blitz@2.1.3
## 2.1.2
### Patch Changes
- blitz@2.1.2
- @blitzjs/rpc@2.1.2
## 2.1.1
### Patch Changes
- Updated dependencies [9a0ba87d1]
- @blitzjs/rpc@2.1.1
- blitz@2.1.1
## 2.1.0
### Minor Changes
- 3b10b13e6: feat: add blitz auth support for the Web `Request` API standard
Usage using the new `withBlitzAuth` adapter in the App Router:
```ts
import {withBlitzAuth} from "app/blitz-server"
export const {POST} = withBlitzAuth({
POST: async (_request, _params, ctx) => {
const session = ctx.session
await session.$revoke()
return new Response(
JSON.stringify({
userId: session.userId,
}),
{status: 200},
)
},
})
```
feat: New Blitz RPC handler meant to with the next.js app router `route.ts` files
Usage using the new `rpcAppHandler` function
```ts
// app/api/rpc/[[...blitz]]/route.ts
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "app/blitz-server"
// Usage with blitz auth
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())
// Standalone usage
export const {GET, POST, HEAD} = rpcAppHandler()
```
chore: Update the app directory starter
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
- @blitzjs/rpc@2.1.0
## 2.0.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/next",
"version": "2.2.0",
"version": "2.0.10",
"homepage": "https://blitzjs.com/",
"repository": {
"type": "git",
@@ -29,7 +29,7 @@
"eslint.js"
],
"dependencies": {
"@blitzjs/rpc": "2.2.0",
"@blitzjs/rpc": "2.0.10",
"@types/hoist-non-react-statics": "3.3.1",
"copy-webpack-plugin": "11.0.0",
"debug": "4.3.3",
@@ -39,29 +39,30 @@
"supports-color": "8.1.1"
},
"peerDependencies": {
"blitz": "2.2.0",
"blitz": "2.0.10",
"next": "*",
"react": "*",
"tslog": "4.9.0"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@testing-library/dom": "8.13.0",
"@testing-library/jest-dom": "5.16.3",
"@testing-library/react": "16.0.1",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1",
"@testing-library/user-event": "13.5.0",
"@types/debug": "4.1.7",
"@types/node": "18.11.9",
"@types/react": "npm:types-react@19.0.0",
"@types/react-dom": "npm:types-react-dom@19.0.0",
"@types/react": "18.0.25",
"@types/react-dom": "17.0.14",
"@types/testing-library__react-hooks": "4.0.0",
"blitz": "2.2.0",
"blitz": "2.0.10",
"cross-spawn": "7.0.3",
"find-up": "4.1.0",
"next": "15.0.1",
"next": "canary",
"next-router-mock": "0.9.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"resolve-from": "5.0.0",
"ts-jest": "27.1.4",
"tslog": "4.9.0",

View File

@@ -1,8 +1 @@
const exports = {
"npm-which": {},
"cross-spawn": {},
fs: {},
child_process: {},
}
module.exports = exports
export {}

View File

@@ -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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// can recover

View File

@@ -77,10 +77,25 @@ test("standard use-case", () => {
const {unmount} = render(<App />)
userEvent.type(screen.getByRole("textbox", {name: /username/i}), "fail")
const calls = consoleError.mock.calls[0]
//@ts-expect-error - it's a mock
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
expect(consoleError).toHaveBeenCalledTimes(1)
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)
consoleError.mockClear()
expect(screen.getByRole("alert")).toMatchInlineSnapshot(`
@@ -134,7 +149,7 @@ test("fallbackRender prop", () => {
}
const {unmount} = render(<App />)
expect(consoleError).toHaveBeenCalledTimes(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// the render prop API allows a single action to reset the app state
@@ -153,7 +168,7 @@ test("simple fallback is supported", () => {
<span>child</span>
</ErrorBoundary>,
)
expect(consoleError).toHaveBeenCalledTimes(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
expect(screen.getByText(/oh no/i)).to.exist
expect(screen.queryByText(/child/i)).to.not.exist
@@ -168,16 +183,27 @@ test("withErrorBoundary HOC", () => {
() => {
throw new Error("💥 CABOOM 💥")
},
{
FallbackComponent: ErrorFallback,
onError: onErrorHandler,
},
{FallbackComponent: ErrorFallback, onError: onErrorHandler},
)
const {unmount} = render(<Boundary />)
const calls = consoleError.mock.calls[0]
//@ts-expect-error - it's a mock
expect(calls[1]).toMatchInlineSnapshot("[Error: 💥 CABOOM 💥]")
expect(consoleError).toHaveBeenCalledTimes(1)
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)
consoleError.mockClear()
const [error, onErrorComponentStack] = (onErrorHandler.mock.calls as [[Error, string]])[0]
@@ -239,6 +265,7 @@ 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>,
@@ -291,7 +318,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// recover via try again button
@@ -306,7 +333,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// recover via resetKeys change
@@ -321,7 +348,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// toggles adding an extra resetKey to the array
@@ -331,7 +358,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// toggle explode back to false
@@ -342,7 +369,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// toggle extra resetKey
@@ -384,7 +411,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(1)
expect(consoleError).toHaveBeenCalledTimes(3)
consoleError.mockClear()
// recover via "toggle explode" button

View File

@@ -1,7 +1,7 @@
import {RedirectError} from "blitz"
import {useRouter} from "next/compat/router"
import type {NextRouter} from "next/router"
import React from "react"
import * as 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): React.JSX.Element {
function WithRouterWrapper(props: any): JSX.Element {
return <ComposedComponent router={useRouter()} {...props} />
}
@@ -114,13 +114,7 @@ export const ErrorBoundary = withRouter(
await this.props.router.push(error.url)
return
}
if (this.props.onError) {
let componentStack = info.componentStack
if (!componentStack) {
componentStack = new Error("Stack trace").stack || ""
}
this.props.onError(error, {componentStack})
}
this.props.onError?.(error, info)
}
componentDidMount() {
@@ -196,7 +190,7 @@ export const ErrorBoundary = withRouter(
},
)
function withErrorBoundary<P extends React.JSX.IntrinsicAttributes>(
function withErrorBoundary<P extends JSX.IntrinsicAttributes>(
Component: React.ComponentType<P>,
errorBoundaryProps: ErrorBoundaryProps,
): React.ComponentType<P> {

View File

@@ -1,16 +1,10 @@
import React from "react"
import {NextPageContext} from "next"
import dynamic from "next/dynamic"
const Head = dynamic(
() =>
import("next/head").then((mod) => ({
default: mod.default,
})),
{
ssr: false,
loading: () => null,
},
)
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
ssr: false,
loading: () => null,
})
const statusCodes: {[code: number]: string} = {
400: "Bad Request",

View File

@@ -9,16 +9,10 @@ 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) => ({
default: mod.default,
})),
{
ssr: false,
loading: () => null,
},
)
const Head = dynamic(() => import("next/head").then((mod) => mod.default), {
ssr: false,
loading: () => null,
})
export {BlitzProvider} from "./provider"
@@ -61,7 +55,7 @@ type RedirectAuthenticatedToFnCtx = {
}
type RedirectAuthenticatedToFn = (args: RedirectAuthenticatedToFnCtx) => RedirectAuthenticatedTo
export type BlitzPage<P = {}> = React.ComponentType<P> & {
getLayout?: (component: React.JSX.Element) => React.JSX.Element
getLayout?: (component: JSX.Element) => JSX.Element
authenticate?: boolean | {redirectTo?: string | RouteUrlObject; role?: string | Array<string>}
suppressFirstRenderFlicker?: boolean
redirectAuthenticatedTo?: RedirectAuthenticatedTo | RedirectAuthenticatedToFn

View File

@@ -243,7 +243,6 @@ export interface BlitzConfig extends NextConfig {
}
}
export function withBlitz(nextConfig: BlitzConfig = {}): NextConfig {
if (
process.env.NODE_ENV !== "production" &&

View File

@@ -3,7 +3,7 @@ import type {QueryClient, HydrateOptions} from "@blitzjs/rpc"
import React from "react"
export type BlitzProviderProps = {
children: React.JSX.Element
children: JSX.Element
client?: QueryClient
contextSharing?: boolean
dehydratedState?: unknown

View File

@@ -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"
import {renderHook as defaultRenderHook} from "@testing-library/react-hooks"
import {NextRouter} from "next/router"
import {RouterContext} from "./router-context"

View File

@@ -1,96 +1,5 @@
# @blitzjs/rpc
## 2.2.0
### Minor Changes
- 3fa3a4ef3: chore: support next.js 15
### Patch Changes
- Updated dependencies [565db3c5a]
- Updated dependencies [3fa3a4ef3]
- blitz@2.2.0
## 2.1.4
### Patch Changes
- blitz@2.1.4
## 2.1.3
### Patch Changes
- 0b3286468: chore: bump `next.js` and `zod` versions
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- blitz@2.1.3
## 2.1.2
### Patch Changes
- blitz@2.1.2
## 2.1.1
### Patch Changes
- 9a0ba87d1: fix: make sure blitz superjson custom error registers in rpc handler
- Updated dependencies [9a0ba87d1]
- blitz@2.1.1
## 2.1.0
### Minor Changes
- 3b10b13e6: feat: add blitz auth support for the Web `Request` API standard
Usage using the new `withBlitzAuth` adapter in the App Router:
```ts
import {withBlitzAuth} from "app/blitz-server"
export const {POST} = withBlitzAuth({
POST: async (_request, _params, ctx) => {
const session = ctx.session
await session.$revoke()
return new Response(
JSON.stringify({
userId: session.userId,
}),
{status: 200},
)
},
})
```
feat: New Blitz RPC handler meant to with the next.js app router `route.ts` files
Usage using the new `rpcAppHandler` function
```ts
// app/api/rpc/[[...blitz]]/route.ts
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "app/blitz-server"
// Usage with blitz auth
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())
// Standalone usage
export const {GET, POST, HEAD} = rpcAppHandler()
```
chore: Update the app directory starter
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
## 2.0.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/rpc",
"version": "2.2.0",
"version": "2.0.10",
"homepage": "https://blitzjs.com/",
"repository": {
"type": "git",
@@ -37,25 +37,25 @@
},
"peerDependencies": {
"@tanstack/query-core": "4.24.4",
"blitz": "2.2.0",
"blitz": "2.0.10",
"next": "*",
"react": "*"
},
"devDependencies": {
"@blitzjs/auth": "2.2.0",
"@blitzjs/config": "2.2.0",
"@blitzjs/auth": "2.0.10",
"@blitzjs/config": "2.0.10",
"@tanstack/query-core": "4.24.4",
"@types/debug": "4.1.7",
"@types/react": "npm:types-react@19.0.0",
"@types/react-dom": "npm:types-react-dom@19.0.0",
"blitz": "2.2.0",
"next": "15.0.1",
"react": "19.0.0",
"react-dom": "19.0.0",
"@types/react": "18.0.25",
"@types/react-dom": "17.0.14",
"blitz": "2.0.10",
"next": "canary",
"react": "18.2.0",
"react-dom": "18.2.0",
"typescript": "^4.8.4",
"unbuild": "0.7.6",
"watch": "1.0.2",
"zod": "3.23.8"
"zod": "3.20.2"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,4 @@
import {assert, Ctx, ResolverConfig, registerBlitzErrorClasses} from "blitz"
import {assert, Ctx, ResolverConfig} from "blitz"
import {NextApiRequest, NextApiResponse} from "next"
import {resolve} from "path"
import {deserialize, parse, serialize as superjsonSerialize} from "superjson"
@@ -172,12 +172,6 @@ export function installTurboConfig() {
as: "*.ts",
},
},
"**/*...blitz*/route.{jsx,tsx,js,ts}": {
default: {
loaders: [{loader: loaderServer, options: {}}],
as: "*.ts",
},
},
"**/{queries,mutations}/**": {
browser: {
loaders: [
@@ -221,7 +215,7 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
// Handles:
// - Vite
// {
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(run
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(runContext)
// if (resolverFilesLoaded) {
// assertUsage(
// Object.keys(resolverFilesLoaded).length > 0,
@@ -233,13 +227,12 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
}
interface RpcConfig {
onError?: (error: Error, ctx?: Ctx) => void
formatError?: (error: Error, ctx?: Ctx) => Error
onError?: (error: Error, ctx: Ctx) => void
formatError?: (error: Error, ctx: Ctx) => Error
logging?: RpcLoggerOptions
}
export function rpcHandler(config?: RpcConfig) {
registerBlitzErrorClasses()
export function rpcHandler(config: RpcConfig) {
return async function handleRpcRequest(req: NextApiRequest, res: NextApiResponse, ctx: Ctx) {
const resolverMap = await getResolverMap()
assert(resolverMap, "No query or mutation resolvers found")
@@ -251,7 +244,7 @@ export function rpcHandler(config?: RpcConfig) {
const relativeRoutePath = (req.query.blitz as string[])?.join("/")
const routePath = "/" + relativeRoutePath
const resolverName = routePath.replace(/(\/api\/rpc)?\//, "")
const rpcLogger = new RpcLogger(resolverName, config?.logging)
const rpcLogger = new RpcLogger(resolverName, config.logging)
const loadableResolver = resolverMap?.[routePath]?.resolver
if (!loadableResolver) {
@@ -310,7 +303,6 @@ export function rpcHandler(config?: RpcConfig) {
rpcLogger.timer.initNextJsSerialization()
;(res as any).blitzResult = result
ctx?.session?.setSession(res)
res.json({
result: serializedResult.json,
error: null,
@@ -330,18 +322,16 @@ export function rpcHandler(config?: RpcConfig) {
error.stack = ""
}
config?.onError?.(error, ctx)
config.onError?.(error, ctx)
rpcLogger.error(error)
if (!error.statusCode) {
error.statusCode = 500
}
const formattedError = config?.formatError?.(error, ctx) ?? error
const formattedError = config.formatError?.(error, ctx) ?? error
const serializedError = superjsonSerialize(formattedError)
ctx?.session?.setSession(res)
res.json({
result: null,
error: serializedError.json,
@@ -359,130 +349,3 @@ export function rpcHandler(config?: RpcConfig) {
}
}
}
type Params = Record<string, unknown>
export function rpcAppHandler(config?: RpcConfig) {
registerBlitzErrorClasses()
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(params.blitz),
"It seems your Blitz RPC endpoint file is not named [[...blitz]].(jt)s. Please ensure it is",
)
const relativeRoutePath = (params.blitz as string[])?.join("/")
const routePath = "/" + relativeRoutePath
const resolverName = routePath.replace(/(\/api\/rpc)?\//, "")
const rpcLogger = new RpcLogger(resolverName, config?.logging)
const loadableResolver = resolverMap?.[routePath]?.resolver
if (!loadableResolver) {
throw new Error("No resolver for path: " + routePath)
}
const {default: resolver, config: resolverConfig} = await loadableResolver()
if (!resolver) {
throw new Error("No default export for resolver path: " + routePath)
}
const resolverConfigWithDefaults = {...defaultConfig, ...resolverConfig}
if (req.method === "HEAD") {
// We used to initiate database connection here
return new Response(null, {status: 200})
}
if (
req.method === "POST" ||
(req.method === "GET" && resolverConfigWithDefaults.httpMethod === "GET")
) {
const body = await req.json()
if (req.method === "POST" && typeof body.params === "undefined") {
const error = {message: "Request body is missing the `params` key"}
rpcLogger.error(error.message)
return new Response(JSON.stringify({result: null, error}), {status: 400})
}
try {
const data = deserialize({
json:
req.method === "POST"
? body.params
: params.params
? parse(`${params.params}`)
: undefined,
meta:
req.method === "POST"
? body.meta?.params
: params.meta
? parse(`${params.meta}`)
: undefined,
})
rpcLogger.timer.initResolver()
rpcLogger.preResolver(data)
const result = await resolver(data, {session})
rpcLogger.timer.resolverDuration()
rpcLogger.postResolver(result)
rpcLogger.timer.initSerialization()
const serializedResult = superjsonSerialize(result)
rpcLogger.timer.initNextJsSerialization()
const response = new Response(
JSON.stringify({
result: serializedResult.json,
error: null,
meta: {
result: serializedResult.meta,
},
}),
)
session?.setSession(response)
return response
} catch (error: any) {
if (error._clearStack) {
error.stack = ""
}
config?.onError?.(error, {session} as Ctx)
rpcLogger.error(error)
if (!error.statusCode) {
error.statusCode = 500
}
const formattedError = config?.formatError?.(error, {session} as Ctx) ?? error
const serializedError = superjsonSerialize(formattedError)
const response = new Response(
JSON.stringify({
result: null,
error: serializedError.json,
meta: {
error: serializedError.meta,
},
}),
)
session?.setSession(response)
return response
}
} else {
// Everything else is error
rpcLogger.warn(`${req.method} method not supported`)
return new Response(null, {status: 404})
}
}
return {
GET: handleRpcRequest,
POST: handleRpcRequest,
HEAD: handleRpcRequest,
}
}

View File

@@ -1,7 +1,7 @@
import {QueryClientProvider} from "@tanstack/react-query"
import React from "react"
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => React.JSX.Element
export type BlitzProviderType = ({children}: {children: React.ReactNode}) => JSX.Element
const BlitzProvider: BlitzProviderType = ({children}) => {
const [queryClient] = React.useState(globalThis.queryClient)

View File

@@ -1,97 +1,5 @@
# blitz
## 2.2.0
### Minor Changes
- 3fa3a4ef3: chore: support next.js 15
### Patch Changes
- 565db3c5a: Fixed incorrect terminal message after creating a new Blitz project to use the correct command (`npm run dev` or `yarn dev`).
- @blitzjs/generator@2.2.0
## 2.1.4
### Patch Changes
- Updated dependencies [38d320fd2]
- @blitzjs/generator@2.1.4
## 2.1.3
### Patch Changes
- 0b3286468: chore: bump `next.js` and `zod` versions
- 50f17d21c: Update prisma-ast dependency to prevent Blitz generator from failing when Prisma keywords are used as model names
- Updated dependencies [0b3286468]
- @blitzjs/generator@2.1.3
## 2.1.2
### Patch Changes
- Updated dependencies [2711291e9]
- Updated dependencies [56bd32b55]
- @blitzjs/generator@2.1.2
## 2.1.1
### Patch Changes
- 9a0ba87d1: fix: make sure blitz superjson custom error registers in rpc handler
- @blitzjs/generator@2.1.1
## 2.1.0
### Minor Changes
- 3b10b13e6: feat: add blitz auth support for the Web `Request` API standard
Usage using the new `withBlitzAuth` adapter in the App Router:
```ts
import {withBlitzAuth} from "app/blitz-server"
export const {POST} = withBlitzAuth({
POST: async (_request, _params, ctx) => {
const session = ctx.session
await session.$revoke()
return new Response(
JSON.stringify({
userId: session.userId,
}),
{status: 200},
)
},
})
```
feat: New Blitz RPC handler meant to with the next.js app router `route.ts` files
Usage using the new `rpcAppHandler` function
```ts
// app/api/rpc/[[...blitz]]/route.ts
import {rpcAppHandler} from "@blitzjs/rpc"
import {withBlitzAuth} from "app/blitz-server"
// Usage with blitz auth
export const {GET, POST, HEAD} = withBlitzAuth(rpcAppHandler())
// Standalone usage
export const {GET, POST, HEAD} = rpcAppHandler()
```
chore: Update the app directory starter
### Patch Changes
- d53da39cb: Improved parsing of default export names to handle higher-order components (HOCs) in the `parseDefaultExportName` function.
- Updated dependencies [3b10b13e6]
- @blitzjs/generator@2.1.0
## 2.0.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "blitz",
"version": "2.2.0",
"version": "2.0.10",
"homepage": "https://blitzjs.com/",
"repository": {
"type": "git",
@@ -30,8 +30,8 @@
"blitz": "bin/blitz"
},
"dependencies": {
"@blitzjs/generator": "2.2.0",
"@mrleebo/prisma-ast": "0.4.1",
"@blitzjs/generator": "2.0.10",
"@mrleebo/prisma-ast": "0.2.6",
"@types/global-agent": "2.1.1",
"arg": "5.0.1",
"ast-types": "0.14.2",
@@ -80,7 +80,7 @@
"watchpack": "2.1.1"
},
"devDependencies": {
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@types/cookie": "0.4.1",
"@types/cross-spawn": "6.0.2",
"@types/debug": "4.1.7",
@@ -95,17 +95,17 @@
"@types/npm-which": "3.0.1",
"@types/progress": "2.0.5",
"@types/prompts": "2.0.14",
"@types/react": "npm:types-react@19.0.0",
"@types/react-dom": "npm:types-react-dom@19.0.0",
"@types/react": "18.0.25",
"@types/react-dom": "17.0.14",
"@types/test-listen": "1.1.0",
"@types/watchpack": "1.1.1",
"express": "4.17.3",
"react": "19.0.0",
"react": "18.2.0",
"test-listen": "1.1.0",
"typescript": "^4.8.4",
"unbuild": "0.7.6",
"watch": "1.0.2",
"zod": "3.23.8"
"zod": "3.20.2"
},
"publishConfig": {
"access": "public"

View File

@@ -3,8 +3,7 @@ import {CliCommand} from "../index"
import prompts from "prompts"
import {bootstrap} from "global-agent"
import {baseLogger, log} from "../../logging"
import Debug from "debug"
const debug = Debug("blitz:cli")
const debug = require("debug")("blitz:cli")
import {join, resolve, dirname} from "path"
import {Stream} from "stream"
import {promisify} from "util"

View File

@@ -274,7 +274,7 @@ const newApp: CliCommand = async () => {
)
}
postInstallSteps.push(`${projectPkgManger} ${projectPkgManger === "npm" ? "run " : ""}dev`);
postInstallSteps.push(`${projectPkgManger} blitz dev`)
console.log("\n Your new Blitz app is ready! Next steps:")
postInstallSteps.forEach((step, index) => {

View File

@@ -4,8 +4,7 @@ import {readJSON} from "fs-extra"
import path from "path"
import pkgDir from "pkg-dir"
import resolveCwd from "resolve-cwd"
import Debug from "debug"
const debug = Debug("blitz:utils")
const debug = require("debug")("blitz:utils")
export async function resolveBinAsync(pkg: string, executable = pkg) {
const packageDir = await pkgDir(resolveCwd(pkg))

View File

@@ -5,8 +5,7 @@ import path from "path"
import * as REPL from "repl"
import {REPLCommand, REPLServer} from "repl"
// eslint-disable-next-line @next/next/no-assign-module-variable
import Debug from "debug"
const debug = Debug("blitz:repl")
const debug = require("debug")("blitz:repl")
import ProgressBar from "progress"
import {log} from "../../logging"

View File

@@ -7,8 +7,7 @@ import * as esbuild from "esbuild"
import pkgDir from "pkg-dir"
import type {ServerConfig} from "./config"
import Debug from "debug"
const debug = Debug("blitz:utils")
const debug = require("debug")("blitz:utils")
export function getProjectRootSync() {
return process.cwd()

View File

@@ -5,8 +5,7 @@ import {outputFile, readdir, readFile} from "fs-extra"
import Watchpack from "watchpack"
import {findNodeModulesRoot} from "./find-node-modules"
import Debug from "debug"
const debug = Debug("blitz")
const debug = require("debug")("blitz")
export const CONFIG_FILE = ".blitz.config.compiled.js"
export const NEXT_CONFIG_FILE = "next.config.js"
export const PHASE_PRODUCTION_SERVER = "phase-production-server"
@@ -504,13 +503,12 @@ const pascalCase = (value: string): string => {
return val.substr(0, 1).toUpperCase() + val.substr(1)
}
export function parseDefaultExportName(contents: string): string | null {
const result = contents.match(/export\s+default(?:\s+(const|let|class|var|function))?\s+(\w+)(?:\(([a-zA-Z_$][a-zA-Z0-9_$]*\b).*\))?/)
const result = contents.match(/export\s+default(?:\s+(?:const|let|class|var|function))?\s+(\w+)/)
if (!result) {
return null
}
const [,declaration,compOrHOCName,comp] = result
if(declaration||!comp) return compOrHOCName ?? null;
return comp ?? null
return result[1] ?? null
}
export async function generateManifest() {
const config = await loadConfig(process.cwd())

View File

@@ -89,10 +89,7 @@ export class PaginationArgumentError extends Error {
}
}
let _blitzErrorClassRegistered = false
export function registerBlitzErrorClasses() {
if (_blitzErrorClassRegistered || !isNotInUserTestEnvironment()) return
if (isNotInUserTestEnvironment() && !globalThis._BLITZ_ERROR_CLASS_REGISTERED) {
SuperJson.registerClass(AuthenticationError, {
identifier: "BlitzAuthenticationError",
allowProps: errorProps,
@@ -128,7 +125,5 @@ export function registerBlitzErrorClasses() {
allowProps: errorProps,
})
_blitzErrorClassRegistered = true
globalThis._BLITZ_ERROR_CLASS_REGISTERED = true
}
registerBlitzErrorClasses()

View File

@@ -8,10 +8,8 @@ import {
PaginationArgumentError,
RedirectError,
OAuthError,
registerBlitzErrorClasses,
} from "./errors"
import type {EventHooks, MiddlewareHooks} from "./types"
import React from "react"
export {
AuthenticationError,
AuthorizationError,
@@ -20,14 +18,13 @@ export {
PaginationArgumentError,
RedirectError,
OAuthError,
registerBlitzErrorClasses,
}
export * from "./utils/enhance-prisma"
export type BlitzProviderComponentType = <TProps = any>(
component: ComponentType<TProps>,
) => {
(props: TProps): React.JSX.Element
(props: TProps): JSX.Element
displayName: string
}

View File

@@ -9,7 +9,7 @@ export * from "./utils/enhance-prisma"
export * from "./middleware"
export * from "./paginate"
export * from "./logging"
export {reduceBlitzServerPlugins, merge, pipe} from "./plugin"
export {reduceBlitzServerPlugins} from "./plugin"
export {findNodeModulesRoot, findNodeModulesRootSync} from "./cli/utils/find-node-modules"
export {startWatcher, stopWatcher} from "./cli/utils/routes-manifest"

View File

@@ -99,8 +99,8 @@ const branded = (msg: string) => {
* @param {string} msg
*/
const clearLine = (msg?: string) => {
readline.clearLine(process.stdout as any, 0)
readline.cursorTo(process.stdout as any, 0)
readline.clearLine(process.stdout, 0)
readline.cursorTo(process.stdout, 0)
msg && process.stdout.write(msg)
}
@@ -173,8 +173,7 @@ const box = async (mes: string, title: string) => {
* If the DEBUG env var is set this will write to the console
* @param str msg
*/
import Debug from "debug"
const debug = Debug("blitz")
const debug = require("debug")("blitz")
export const log = {
withBrand,

View File

@@ -1,57 +1,5 @@
# @blitzjs/codemod
## 2.2.0
### Patch Changes
- Updated dependencies [565db3c5a]
- Updated dependencies [3fa3a4ef3]
- blitz@2.2.0
- @blitzjs/generator@2.2.0
## 2.1.4
### Patch Changes
- Updated dependencies [38d320fd2]
- @blitzjs/generator@2.1.4
- blitz@2.1.4
## 2.1.3
### Patch Changes
- Updated dependencies [0b3286468]
- Updated dependencies [50f17d21c]
- blitz@2.1.3
- @blitzjs/generator@2.1.3
## 2.1.2
### Patch Changes
- Updated dependencies [2711291e9]
- Updated dependencies [56bd32b55]
- @blitzjs/generator@2.1.2
- blitz@2.1.2
## 2.1.1
### Patch Changes
- Updated dependencies [9a0ba87d1]
- blitz@2.1.1
- @blitzjs/generator@2.1.1
## 2.1.0
### Patch Changes
- Updated dependencies [d53da39cb]
- Updated dependencies [3b10b13e6]
- blitz@2.1.0
- @blitzjs/generator@2.1.0
## 2.0.10
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@blitzjs/codemod",
"version": "2.2.0",
"version": "2.0.10",
"scripts": {
"build": "unbuild",
"dev": "watch unbuild src --wait=0.2",
@@ -25,9 +25,9 @@
"@babel/plugin-proposal-class-properties": "7.17.12",
"@babel/plugin-syntax-jsx": "7.17.12",
"@babel/plugin-syntax-typescript": "7.17.12",
"@blitzjs/generator": "2.2.0",
"@blitzjs/generator": "2.0.10",
"arg": "5.0.1",
"blitz": "2.2.0",
"blitz": "2.0.10",
"chalk": "^4.1.0",
"cross-spawn": "7.0.3",
"debug": "4.3.3",
@@ -38,7 +38,7 @@
},
"devDependencies": {
"@babel/preset-env": "7.12.10",
"@blitzjs/config": "2.2.0",
"@blitzjs/config": "2.0.10",
"@types/jscodeshift": "0.11.2",
"@types/node": "18.11.9",
"ast-types": "0.14.2",

View File

@@ -10,7 +10,7 @@ describe("replaceBlitzPkgsVersions", () => {
return {
fetchDistTags: vi.fn((pkg: string) => {
if (pkg === "blitz") {
return {alpha: "1.0.0", beta: "2.0.0", danger: "2.1.0"}
return {alpha: "1.0.0", beta: "2.0.0", danger: "3.0.0"}
}
if (pkg === "zod") {
return {latest: "1.2.3"}
@@ -52,10 +52,10 @@ describe("replaceBlitzPkgsVersions", () => {
it("correctly updates versions with the danger tag", async () => {
expect(await replaceBlitzPkgsVersions(pkgJson, "danger")).toEqual({
dependencies: {
blitz: "2.1.0",
"@blitzjs/rpc": "2.1.0",
"@blitzjs/auth": "2.1.0",
"@blitzjs/next": "2.1.0",
blitz: "3.0.0",
"@blitzjs/rpc": "3.0.0",
"@blitzjs/auth": "3.0.0",
"@blitzjs/next": "3.0.0",
next: "12.2.0",
zod: "1.2.3",
},

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