1
0
mirror of synced 2026-02-06 09:00:12 -05:00

Compare commits

...

58 Commits

Author SHA1 Message Date
Siddharth Suresh
57add5f1c5 chore: update pnpm lock file 2024-10-08 19:23:13 +05:30
github-actions[bot]
3f239e78b6 Version Packages (#4379)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-08 19:15:27 +05:30
Blitz.js Bot
3bf90c167c (meta) added @doe-base as contributor 2024-09-30 10:47:47 -05:00
Daniel Idoko
56bd32b553 Fix Incorrect Zod Schema Generation for Datetime Fields (#4377)
* Fix Zod schema generation for datetime fields

* Add changeset for datetime schema fix

* Set changeset to patch release

* chore: update unit test

---------

Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-09-30 15:47:42 +00:00
Kevin Østerkilde
2711291e97 fix: remove views from public Session data (again) (#4375)
* fix: remove views from public Session data (again)

* Update .changeset/small-parents-press.md

---------

Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-09-29 21:17:38 +00:00
Blitz.js Bot
ab29c5bf3f (meta) added @cherniavskii as contributor 2024-09-29 16:07:38 -05:00
Siddharth Suresh
a096f2cd80 chore: update pnpm-lock.yaml 2024-08-14 19:33:16 +05:30
github-actions[bot]
ded16b325b Version Packages (#4368)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-08-14 19:31:29 +05:30
github-actions[bot]
4494662d6d Version Packages (#4353)
* Version Packages

* make the version 2.1.0

* Update integration-tests/no-suspense/package.json

* pnpm lock fix

* use latest pnpm version

* Revert "use latest pnpm version"

This reverts commit 5fab234a4a.

* Revert "pnpm lock fix"

This reverts commit 690a467cf7.

* Revert "make the version 2.1.0"

This reverts commit 922e95156f.

* make the version 2.1.0

* pnpm lock fix

* fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-08-14 19:27:52 +05:30
Siddharth Suresh
9a0ba87d15 add error register in rpc handler (#4356)
* add error register in rpc handler

* fix

* Create empty-pugs-prove.md

* fixes

* Update packages/blitz/src/errors.ts
2024-08-05 19:53:03 +05:30
Siddharth Suresh
c80ce51d36 chore: fix CI action versions (#4366) 2024-07-30 18:35:59 +05:30
Blitz.js Bot
b505933a16 (meta) added @bezalel6 as contributor 2024-07-30 08:03:36 -05:00
bezalel6
d53da39cbf modify parseDefaultExportName so it does not grab an HOC, but the fir… (#4359)
* modify parseDefaultExportName so it does not grab an HOC, but the first Argument it takes. presumably the actual component

* created changeset
2024-07-30 18:33:31 +05:30
Siddharth Suresh
e1055f7366 sponsor: add route4me as seedling sponsor (#4361)
* sponser: add route4me as seedling sponser

* readMe changes
2024-07-22 19:18:38 +05:30
Siddharth Suresh
3b10b13e6b feat: Blitz RPC endpoint to the app directory (#4341)
* feat: make `rpchandler` work with `Request` and return a `Response`

* rename: rpcRequestHandler

* feat: improvements

* blitz-auth now works

* return headers

* working

* cleanup

* working sveltekit, with regressin of next.js app dir

* cleanup

* pnpm lock fix

* Update packages/blitz-auth/src/server/auth-plugin.ts

* fix build

* more work

* fixes

* fix issues with auth

* maybe required breaking change

* pointless test

* Update packages/blitz-auth/package.json

* fixes

* fix

* get all tests passing

* more fixes

* changeset

* fixes

* fix

* pnpm lock update

* fix

* update pnpm lcok

* revert unnecessary changes

* imporve api naming

* cleanup

* fix

* Update integration-tests/next-13-app-dir/src/blitz-server.ts

* Apply suggestions from code review

* remove unrelated changes

* Update packages/blitz-auth/src/server/auth-sessions.ts

* fix types

* fix overload

* remove dependence on http module

* review changes

* oops

* fix

* fix types

* Revert "fix types"

This reverts commit b06a4fb3bf.

* Revert "fix"

This reverts commit 47d0cdd568.

* Revert "oops"

This reverts commit 94cb55839d.

* Revert "review changes"

This reverts commit 14d8eb2820.

* fix the logic

* sort deps

* template fixes

* chore: add changeset

* chore: remove outdated changeset

* fix changeset formatting

* Update .changeset/tidy-gorillas-confess.md

* remove `blitzAuthRpcMiddleware`

* remove uses of any

* fix jsdoc

* no var

* separate the type imports

* fix unsupported method of session in rsc

* fix

* api changes

* Apply suggestions from code review

Co-authored-by: Brandon Bayer <b@bayer.ws>

* Apply suggestions from code review

* Update .changeset/tidy-gorillas-confess.md

* fix

* fic

---------

Co-authored-by: Brandon Bayer <b@bayer.ws>
2024-07-01 23:41:12 +05:30
Siddharth Suresh
25601754a4 chore: remove check for comment time in PR release action (#4351) 2024-06-26 20:29:09 +05:30
Aviv Keller
e0cfa328ec fix: use correct bash syntax (#4349) 2024-06-26 19:29:55 +05:30
Blitz.js Bot
f02469aac8 (meta) added @RedYetiDev as contributor 2024-06-13 08:38:15 -05:00
Aviv Keller
2f3c552ac3 fix: use the correct SHA for pr-release.yml (#4348) 2024-06-13 19:08:09 +05:30
Siddharth Suresh
721461a7b9 chore: update pnpm lock to latest 2024-06-04 22:55:41 +05:30
github-actions[bot]
3193bdea48 Version Packages (#4346)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-06-04 22:48:44 +05:30
Siddharth Suresh
318e9740d6 feat: upgrade to latest next-auth v4 version (#4345)
* upgrade to latest v4

* Create warm-scissors-juggle.md

* Update .changeset/warm-scissors-juggle.md

* also check for flightcontrol platform
2024-06-03 15:37:12 +00:00
Tobias
0df368308b Delete .node-version (#4340) 2024-05-27 04:56:35 +00:00
github-actions[bot]
ad94bee56a Version Packages (#4333)
* Version Packages

* pnpm lock fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-05-09 15:37:39 +00:00
Siddharth Suresh
5a14306f7b regression: export enhancePrisma from the brower (#4326)
* move it back to index-server

* fix turbopack

* Create big-cars-raise.md

* cleanup

* fix

* fix lint
2024-05-09 14:05:23 +00:00
github-actions[bot]
6e8eeb1514 Version Packages (#4327)
* Version Packages

* update pnpm lock

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-04-17 14:14:39 +00:00
Siddharth Suresh
5e61a16815 bug: merge existing and blitz turbo config (#4323)
* do not oveeride existing config

* Create calm-deers-sin.md

* Update packages/blitz-next/src/index-server.ts
2024-04-17 19:34:19 +05:30
Siddharth Suresh
77555468f3 fix: add missing host in next-auth adapter (#4325)
* fix: add missing host in next-auth adapter

* Create red-masks-drop.md

* Update .changeset/red-masks-drop.md
2024-04-17 19:26:15 +05:30
github-actions[bot]
22e402af01 Version Packages (#4320)
* Version Packages

* pnpm lock fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-04-05 21:03:50 +05:30
Siddharth Suresh
19afc57530 internal: unblock the changeset release 2024-04-05 20:50:21 +05:30
Blitz.js Bot
b7dab1d800 (meta) added @timneutkens as contributor 2024-04-05 10:28:56 -04:00
Tim Neutkens
ee7bf87ec0 Turbopack support for Blitz (#4314)
* Use this.rootContext instead of webpack internals

Ensures the root context is read from the public API that webpack exposes. This is the first step for Turbopack support as Turbopack includes `this.rootContext` as well

* Turbopack support for Blitz

* Update packages/blitz-rpc/src/server/loader/server/loader-server.ts

Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>

* fix: CI and update next.js version in test app

* feat: add tests for turbo and expose new `turbo` boolean

* upgrade to latest next version

* use latest canary in internal packages and tests

* chore: add changeset

* chore: minor fix on how the test is run

* fix stray DYNAMIC_SERVER_USAGE thrown

* cleanup

* chore: changeset

* pnpm lock fix

* fix turbo tests

* fixes

* oops

* add turbo config only when needed

* remove need for any change to `next.config.js`

---------

Co-authored-by: Tobias Koppers <tobias.koppers@googlemail.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-04-05 10:28:50 -04:00
Siddharth Suresh
451ead089c chore: try fixing the pr release for usage with forks 2024-04-03 14:04:18 +05:30
Siddharth Suresh
5d9570f7f4 chore: update dependencies of CI workflows 2024-04-03 13:56:36 +05:30
Siddharth Suresh
178c152b2c fix: pages and app directory - Uncaught Error: DYNAMIC_SERVER_USAGE (#4318)
* fix stray DYNAMIC_SERVER_USAGE thrown

* cleanup

* chore: changeset
2024-04-02 14:34:27 +00:00
github-actions[bot]
bbe1a54d7a Version Packages (#4316)
* Version Packages

* chore: update pnpm lock

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-04-02 06:39:08 +00:00
Siddharth Suresh
9b075dbb47 chore: upgrade release CI to latest versions 2024-04-02 12:00:21 +05:30
Siddharth Suresh
60a4d90c86 chore: override version of mime blocking release CI 2024-04-02 11:53:32 +05:30
Blitz.js Bot
a674897601 (meta) added @gengjiawen as contributor 2024-04-01 08:56:45 -04:00
Jiawen Geng
5a587a6c31 fix: production issue (#4311)
* fix: production issue

* Update .changeset/soft-tables-ring.md

---------

Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-04-01 12:56:40 +00:00
Siddharth Suresh
744242f25b chore: remove --no-frozen-lockfile install step for release workflow 2024-03-08 00:47:36 +05:30
Blitz.js Bot
0f5297f1aa (meta) added @lmisea as contributor 2024-03-07 13:26:25 -05:00
Luis Isea
76a2544f9d fix: process.kill("SIGABRT") not supported on windows (#4308)
* fix: process.kill("SIGABRT") not supported on windows

* fix: use process.kill("SIGINT") across all operative systems

* Update .changeset/clever-insects-shave.md

---------

Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-03-07 18:26:20 +00:00
github-actions[bot]
6f44c2334e Version Packages (#4301)
* Version Packages

* pnpm lock

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-02-17 06:33:47 +00:00
Siddharth Suresh
8a417533f6 feat: remove restriction to use secure cookies in localhost (#4300)
* feat: remove restriction to use secure cookies in localhost

* changeset

* pnpm lock fix

* Update .changeset/grumpy-deers-rest.md

* Update .changeset/grumpy-deers-rest.md

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-02-16 23:34:36 +00:00
Siddharth Suresh
6f54841b7a fix: getBlitzContext() can only be used in React Server Components in Nextjs 13 or higher (#4299)
* fix: remove custom errors thrown by blitz

* Create curvy-cougars-lick.md

* use require and eval rather than the await which becomes a `yield import` possibly causing the issue

* pnpm lock fix

* Update .changeset/curvy-cougars-lick.md

* add comment

* use correct error type

* Apply suggestions from code review
2024-02-16 23:27:05 +00:00
github-actions[bot]
e8f564ea66 Version Packages (#4292)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-02-06 22:30:47 +05:30
Siddharth Suresh
71b9950037 fix: ajv dependency was not installed with blitz new (#4297)
* add ajv to devDeps

* Create four-dots-retire.md
2024-02-06 22:25:55 +05:30
Siddharth Suresh
28a79040e4 feat: export BlitzServerMiddleware from blitz-next (#4296)
* feat: export `BlitzServerMiddleware` from blitz-next

* Create sixty-pants-hunt.md

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-02-06 22:25:36 +05:30
Siddharth Suresh
dd604c7675 perf: add filter to query only non expired sessions (#4288)
* perf: add check to make sure only non expired sessions are selected by default

* remove console.logs

* Create chatty-ants-bake.md

* remove `expiresAt` from the publicData

* remove internal from changelog

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-02-06 16:13:32 +00:00
Blitz.js Bot
0a8d4bfdaf (meta) added @Doc0x1 as contributor 2024-01-25 03:48:39 -05:00
Doc0x1
f25aac08c0 Added codemod support for legacy projects with a /src folder (#4290)
* Added support for legacy projects that have the pages folder located inside a /src directory

* Fixed line 687 for path.resolve(`${findPagesDirectory}/api/rpc`) not actually calling the function and added path.resolve's to findPagesDirectory

* Update for lines 56 and 1340 to add support for app and pages directories located in /src
2024-01-25 14:18:34 +05:30
github-actions[bot]
a09685a63a Version Packages (#4283)
* Version Packages

* pnpm lock fix

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
2024-01-16 16:10:03 +00:00
Siddharth Suresh
595f400e9a fix: add missing devDeps to the new app template (#4284)
* add prettier and other missing devDeps

* match the devDeps to the pages template

* Create unlucky-spoons-guess.md
2024-01-16 21:26:54 +05:30
Blitz.js Bot
6f333ee643 (meta) added @LeonMueller-OneAndOnly as contributor 2024-01-16 10:55:11 -05:00
Leonidas
956a739e83 fix<codemod-upgrade-legacy>: provide correct path to template files (#4274)
* fix<codemod-upgrade-legacy>: provide correct path to template files

* fix<codemod-upgrade-legacy>: corrected path

* fix<codemod-legacy-upgrade>: add cookiePrefix step: always errored that collection was empty => invoke it only conditionally

* Create famous-worms-grab.md

---------

Co-authored-by: Siddharth Suresh <siddh.suresh@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2024-01-16 15:55:07 +00:00
Siddharth Suresh
47722e0456 Fix: BlitzCliConfig does not work when blitz-server.ts is in /src/app directory (#4279)
* fix: search any subdirectly in `app/src` to find `blitz-server.ts`

* Create giant-knives-wonder.md

* Update packages/generator/src/generator.ts
2024-01-16 15:43:22 +00:00
Siddharth Suresh
2f5c8a3a0d fix: sodium native missing prebuilds (#4276)
* fix: copy sodium native prebuilds to `vender-chunks` and `chunks` using webpack

* Create seven-cheetahs-enjoy.md
2024-01-16 15:36:45 +00:00
139 changed files with 4079 additions and 1998 deletions

View File

@@ -4014,6 +4014,96 @@
"doc", "doc",
"code" "code"
] ]
},
{
"login": "LeonMueller-OneAndOnly",
"name": "Leonidas",
"avatar_url": "https://avatars.githubusercontent.com/u/77194479?v=4",
"profile": "https://flow-office.eu/",
"contributions": [
"doc",
"code"
]
},
{
"login": "Doc0x1",
"name": "Doc0x1",
"avatar_url": "https://avatars.githubusercontent.com/u/19937034?v=4",
"profile": "https://hacknex.us",
"contributions": [
"doc",
"code"
]
},
{
"login": "lmisea",
"name": "Luis Isea",
"avatar_url": "https://avatars.githubusercontent.com/u/106825636?v=4",
"profile": "https://github.com/lmisea",
"contributions": [
"doc",
"code"
]
},
{
"login": "gengjiawen",
"name": "Jiawen Geng",
"avatar_url": "https://avatars.githubusercontent.com/u/3759816?v=4",
"profile": "https://www.gengjiawen.com",
"contributions": [
"doc",
"code"
]
},
{
"login": "timneutkens",
"name": "Tim Neutkens",
"avatar_url": "https://avatars.githubusercontent.com/u/6324199?v=4",
"profile": "https://timn.tech",
"contributions": [
"doc",
"code",
"test"
]
},
{
"login": "RedYetiDev",
"name": "Aviv Keller",
"avatar_url": "https://avatars.githubusercontent.com/u/38299977?v=4",
"profile": "https://redyetidev.github.io",
"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"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

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

View File

@@ -22,23 +22,29 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: resolve pr refs - name: Fetch PR information
id: refs id: pr_info
uses: eficode/resolve-pr-refs@main env:
with: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }} run: |
pr="$(gh api repos/${{ github.repository }}/pulls/${{ github.event.issue.number }})"
head_sha="$(echo "$pr" | jq -r .head.sha)"
echo "head_sha=$head_sha" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3 - uses: actions/checkout@v4
with: with:
ref: ${{ steps.refs.outputs.head_ref }} ref: ${{ steps.pr_info.outputs.head_sha }}
- name: Setup PNPM - name: Setup PNPM
uses: pnpm/action-setup@v2.2.1 uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with:
version: 8.9.0
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v3 uses: actions/setup-node@v4
with: with:
node-version: 16 node-version: 20
registry-url: "https://registry.npmjs.org" registry-url: "https://registry.npmjs.org"
cache: "pnpm" cache: "pnpm"

View File

@@ -19,12 +19,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repo - name: Checkout Repo
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Node.js 16.x - name: Setup Node.js 20.x
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: 16.x node-version: 20.x
- name: Creating .npmrc - name: Creating .npmrc
run: | run: |
@@ -37,8 +37,10 @@ jobs:
- name: Pre-publish - name: Pre-publish
uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2 uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
with: with:
version: 6.32.6 version: 8.9.0
- run: pnpm install --no-frozen-lockfile - run: pnpm install
env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: pnpm build - run: pnpm build
- name: Create Release Pull Request - name: Create Release Pull Request
@@ -49,10 +51,3 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# - name: Github Release
# uses: release-drafter/release-drafter@v5.19.0
# with:
# config-name: release-drafter.yml
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1 +0,0 @@
16.13.2

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=="> <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> </a>
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> <!-- 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-423-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-432-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
<!-- ALL-CONTRIBUTORS-BADGE:END --> <!-- ALL-CONTRIBUTORS-BADGE:END -->
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/main/LICENSE"> <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"> <img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
@@ -79,6 +79,9 @@ Your financial contributions help ensure Blitz continues to be developed and mai
</a></td> </a></td>
<td><a aria-label="Simon Lammes" href="https://github.com/simon-lammes"> <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"/> <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> </a></td>
</tr> </tr>
</table> </table>
@@ -747,6 +750,17 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
<td align="center"><a href="https://github.com/Zamfi99"><img src="https://avatars.githubusercontent.com/u/19189337?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zamfira Costin-Andrei</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Code">💻</a></td> <td align="center"><a href="https://github.com/Zamfi99"><img src="https://avatars.githubusercontent.com/u/19189337?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zamfira Costin-Andrei</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Zamfi99" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/potikhanovsergey"><img src="https://avatars.githubusercontent.com/u/71494201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=potikhanovsergey" title="Documentation">📖</a></td> <td align="center"><a href="https://github.com/potikhanovsergey"><img src="https://avatars.githubusercontent.com/u/71494201?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=potikhanovsergey" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/papsavas"><img src="https://avatars.githubusercontent.com/u/50584606?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Savvas Papageorgiadis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Code">💻</a></td> <td align="center"><a href="https://github.com/papsavas"><img src="https://avatars.githubusercontent.com/u/50584606?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Savvas Papageorgiadis</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=papsavas" title="Code">💻</a></td>
<td align="center"><a href="https://flow-office.eu/"><img src="https://avatars.githubusercontent.com/u/77194479?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leonidas</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=LeonMueller-OneAndOnly" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=LeonMueller-OneAndOnly" title="Code">💻</a></td>
<td align="center"><a href="https://hacknex.us"><img src="https://avatars.githubusercontent.com/u/19937034?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Doc0x1</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=Doc0x1" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=Doc0x1" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lmisea"><img src="https://avatars.githubusercontent.com/u/106825636?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luis Isea</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=lmisea" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=lmisea" title="Code">💻</a></td>
<td align="center"><a href="https://www.gengjiawen.com"><img src="https://avatars.githubusercontent.com/u/3759816?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jiawen Geng</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gengjiawen" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=gengjiawen" title="Code">💻</a></td>
</tr>
<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>
</tr> </tr>
</table> </table>

View File

@@ -1,5 +1,131 @@
# next-blitz-auth # next-blitz-auth
## 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
- Updated dependencies [318e9740d]
- @blitzjs/auth@2.0.10
- blitz@2.0.10
- @blitzjs/rpc@2.0.10
- @blitzjs/next@2.0.10
- @blitzjs/config@2.0.10
## 0.1.10
### Patch Changes
- Updated dependencies [5a14306f7]
- @blitzjs/next@2.0.9
- @blitzjs/rpc@2.0.9
- blitz@2.0.9
- @blitzjs/auth@2.0.9
- @blitzjs/config@2.0.9
## 0.1.9
### Patch Changes
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- @blitzjs/next@2.0.8
- blitz@2.0.8
- @blitzjs/auth@2.0.8
- @blitzjs/rpc@2.0.8
- @blitzjs/config@2.0.8
## 0.1.8
### Patch Changes
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
- @blitzjs/next@2.0.7
- @blitzjs/rpc@2.0.7
- @blitzjs/auth@2.0.7
- @blitzjs/config@2.0.7
## 0.1.7
### Patch Changes
- Updated dependencies [76a2544f9]
- Updated dependencies [5a587a6c3]
- blitz@2.0.6
- @blitzjs/auth@2.0.6
- @blitzjs/next@2.0.6
- @blitzjs/rpc@2.0.6
- @blitzjs/config@2.0.6
## 0.1.6
### Patch Changes
- Updated dependencies [6f54841b7]
- Updated dependencies [8a417533f]
- @blitzjs/auth@2.0.5
- blitz@2.0.5
- @blitzjs/rpc@2.0.5
- @blitzjs/next@2.0.5
- @blitzjs/config@2.0.5
## 0.1.5
### Patch Changes
- Updated dependencies [dd604c767]
- Updated dependencies [28a79040e]
- @blitzjs/auth@2.0.4
- blitz@2.0.4
- @blitzjs/next@2.0.4
- @blitzjs/rpc@2.0.4
- @blitzjs/config@2.0.4
## 0.1.4
### Patch Changes
- Updated dependencies [2f5c8a3a0]
- @blitzjs/next@2.0.3
- @blitzjs/rpc@2.0.3
- blitz@2.0.3
- @blitzjs/auth@2.0.3
- @blitzjs/config@2.0.3
## 0.1.3 ## 0.1.3
### Patch Changes ### Patch Changes

View File

@@ -1,5 +1,13 @@
const {withBlitz} = require("@blitzjs/next") const {withBlitz} = require("@blitzjs/next")
const loaderClient = require.resolve("@blitzjs/rpc/dist/loader-client.cjs")
const loaderServer = require.resolve("@blitzjs/rpc/dist/loader-server.cjs")
const loaderServerResolvers = require.resolve("@blitzjs/rpc/dist/loader-server-resolvers.cjs")
console.log("loaderClient", loaderClient)
console.log("loaderServer", loaderServer)
console.log("loaderServerResolvers", loaderServerResolvers)
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = {} const nextConfig = {}

View File

@@ -1,6 +1,6 @@
{ {
"name": "next-blitz-auth", "name": "next-blitz-auth",
"version": "0.1.3", "version": "0.1.14",
"private": true, "private": true,
"scripts": { "scripts": {
"blitz:dev": "next dev", "blitz:dev": "next dev",
@@ -12,17 +12,17 @@
"schema": "prisma/schema.prisma" "schema": "prisma/schema.prisma"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@hookform/error-message": "2.0.0", "@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10", "@hookform/resolvers": "2.9.10",
"@prisma/client": "^4.5.0", "@prisma/client": "^4.5.0",
"@tanstack/react-query": "4.0.10", "@tanstack/react-query": "4.0.10",
"blitz": "2.0.2", "blitz": "2.1.2",
"flatted": "3.2.7", "flatted": "3.2.7",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "^4.5.0", "prisma": "^4.5.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

Binary file not shown.

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
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 = { export const config = {
httpMethod: "GET", httpMethod: "POST",
} }

View File

@@ -23,15 +23,15 @@
] ]
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@hookform/error-message": "2.0.0", "@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10", "@hookform/resolvers": "2.9.10",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"openid-client": "5.2.1", "openid-client": "5.2.1",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",

View File

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

View File

@@ -24,16 +24,16 @@
] ]
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@hookform/error-message": "2.0.0", "@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10", "@hookform/resolvers": "2.9.10",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"next-auth": "4.18.7", "next-auth": "4.24.7",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

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

View File

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

View File

@@ -16,17 +16,17 @@
"schema": "./db/schema.prisma" "schema": "./db/schema.prisma"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"@types/jest": "29.2.2", "@types/jest": "29.2.2",
"@types/passport-twitter": "1.0.37", "@types/passport-twitter": "1.0.37",
"blitz": "2.0.2", "blitz": "2.1.2",
"jest": "29.3.0", "jest": "29.3.0",
"jest-environment-jsdom": "29.3.0", "jest-environment-jsdom": "29.3.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"passport-mock-strategy": "2.0.0", "passport-mock-strategy": "2.0.0",
"passport-twitter": "1.0.4", "passport-twitter": "1.0.4",
"prisma": "4.6.1", "prisma": "4.6.1",

View File

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

BIN
assets/route4me.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -17,16 +17,16 @@
"prisma:studio": "prisma studio" "prisma:studio": "prisma studio"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@hookform/error-message": "2.0.0", "@hookform/error-message": "2.0.0",
"@hookform/resolvers": "2.9.10", "@hookform/resolvers": "2.9.10",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"delay": "5.0.0", "delay": "5.0.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

@@ -79,11 +79,13 @@ const runTests = () => {
let text = await browser.elementByCss("#content").text() let text = await browser.elementByCss("#content").text()
expect(text).toMatch(/logged-out/) expect(text).toMatch(/logged-out/)
await browser.elementByCss("#login").click() await browser.elementByCss("#login").click()
await waitFor(200) await waitFor(7500)
text = await browser.elementByCss("#content").text() text = await browser.elementByCss("#content").text()
expect(text).toMatch(/logged-in/) expect(text).toMatch(/logged-in/)
await browser.elementByCss("#logout").click() await browser.elementByCss("#logout").click()
await waitFor(250) await waitFor(5000)
text = await browser.elementByCss("#content").text() text = await browser.elementByCss("#content").text()
expect(text).toMatch(/logged-out/) expect(text).toMatch(/logged-out/)
@@ -93,16 +95,16 @@ const runTests = () => {
it("should logout without infinite loop #2233", async () => { it("should logout without infinite loop #2233", async () => {
// Login // Login
let browser = await webdriver(appPort, "/login") let browser = await webdriver(appPort, "/login")
await waitFor(200) await waitFor(5000)
await browser.elementByCss("#login").click() await browser.elementByCss("#login").click()
await waitFor(200) await waitFor(5000)
await browser.eval(`window.location = "/authenticated-query"`) await browser.eval(`window.location = "/authenticated-query"`)
await browser.waitForElementByCss("#content") await browser.waitForElementByCss("#content")
let text = await browser.elementByCss("#content").text() let text = await browser.elementByCss("#content").text()
expect(text).toMatch(/authenticated-basic-result/) expect(text).toMatch(/authenticated-basic-result/)
await browser.elementByCss("#logout").click() await browser.elementByCss("#logout").click()
await waitFor(200) await waitFor(5000)
await browser.waitForElementByCss("#error") await browser.waitForElementByCss("#error")
text = await browser.elementByCss("#error").text() text = await browser.elementByCss("#error").text()
if (mode === "server") { if (mode === "server") {
@@ -115,9 +117,9 @@ const runTests = () => {
it("Page.authenticate = {role} should throw authentication error ", async () => { it("Page.authenticate = {role} should throw authentication error ", async () => {
let browser = await webdriver(appPort, "/login") let browser = await webdriver(appPort, "/login")
await waitFor(200) await waitFor(5000)
await browser.elementByCss("#login").click() await browser.elementByCss("#login").click()
await waitFor(200) await waitFor(5000)
await browser.eval(`window.location = "/page-dot-authenticate-role"`) await browser.eval(`window.location = "/page-dot-authenticate-role"`)
await browser.waitForElementByCss("#error") await browser.waitForElementByCss("#error")
let text = await browser.elementByCss("#error").text() let text = await browser.elementByCss("#error").text()
@@ -169,7 +171,7 @@ const runTests = () => {
let text = await browser.elementByCss("#content").text() let text = await browser.elementByCss("#content").text()
expect(text).toMatch(/authenticated-basic-result/) expect(text).toMatch(/authenticated-basic-result/)
await browser.elementByCss("#logout").click() await browser.elementByCss("#logout").click()
await waitFor(500) await waitFor(5000)
expect(await browser.url()).toMatch(/\/login/) expect(await browser.url()).toMatch(/\/login/)
if (browser) await browser.close() if (browser) await browser.close()
@@ -275,7 +277,7 @@ const runTests = () => {
} }
describe("Auth Tests", () => { describe("Auth Tests", () => {
describe("dev mode", () => { describe("dev mode - webpack", () => {
beforeAll(async () => { beforeAll(async () => {
mode = "dev" mode = "dev"
try { try {
@@ -290,6 +292,21 @@ describe("Auth Tests", () => {
runTests() runTests()
}) })
describe("dev mode - turbo", () => {
beforeAll(async () => {
mode = "dev"
try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
appPort = await findPort()
app = await blitzLaunchApp(appPort, {cwd: process.cwd()}, true)
} catch (error) {
console.log(error)
}
}, 5000 * 60 * 2)
afterAll(async () => await killApp(app))
runTests()
})
describe("server mode", () => { describe("server mode", () => {
beforeAll(async () => { beforeAll(async () => {
mode = "server" mode = "server"

View File

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

View File

@@ -17,13 +17,13 @@
"prisma:studio": "prisma studio" "prisma:studio": "prisma studio"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "3.0.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

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

View File

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

View File

@@ -16,19 +16,19 @@
"schema": "db/schema.prisma" "schema": "db/schema.prisma"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "2.1.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@next/bundle-analyzer": "12.0.8", "@next/bundle-analyzer": "12.0.8",
"@types/express": "4.17.13", "@types/express": "4.17.13",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",

View File

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

View File

@@ -11,11 +11,11 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next" "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
}, },
"dependencies": { "dependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },

View File

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

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

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

View File

@@ -0,0 +1,38 @@
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,34 +0,0 @@
"use client"
import {useQuery, useMutation} from "@blitzjs/rpc"
import logout from "../src/auth/mutations/logout"
import getCurrentUser from "../src/users/queries/getCurrentUser"
import {useTransition} from "react"
import {useRouter} from "next/navigation"
export default function Test() {
const router = useRouter()
const [user] = useQuery(getCurrentUser, null)
const [isPending, startTransition] = useTransition()
const [logoutMutation] = useMutation(logout)
console.log(user)
return (
<div>
<h1>Test</h1>
<p>{user?.email}</p>
<button
className="button small"
onClick={async () => {
await logoutMutation()
startTransition(() => {
// Refresh the current route and fetch new data from the server without
// losing client-side browser or React state.
router.refresh()
})
}}
>
Logout
</button>
</div>
)
}

View File

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

View File

@@ -17,14 +17,14 @@
"prisma:studio": "prisma studio" "prisma:studio": "prisma studio"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "2.1.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
@@ -45,6 +45,6 @@
"node-fetch": "3.2.3", "node-fetch": "3.2.3",
"playwright": "1.28.0", "playwright": "1.28.0",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"typescript": "^4.8.4" "typescript": "^4.9.5"
} }
} }

View File

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

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

View File

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

View File

@@ -1,34 +0,0 @@
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 db from "../db"
import {simpleRolesIsAuthorized} from "@blitzjs/auth" import {simpleRolesIsAuthorized} from "@blitzjs/auth"
import {BlitzLogger} from "blitz" import {BlitzLogger} from "blitz"
import {RpcServerPlugin} from "@blitzjs/rpc"
const {api, getBlitzContext} = setupBlitzServer({ export const {api, getBlitzContext, withBlitzAuth} = setupBlitzServer({
plugins: [ plugins: [
AuthServerPlugin({ AuthServerPlugin({
cookiePrefix: "auth-tests-cookie-prefix", cookiePrefix: "auth-tests-cookie-prefix",
storage: PrismaStorage(db), storage: PrismaStorage(db),
isAuthorized: simpleRolesIsAuthorized, isAuthorized: simpleRolesIsAuthorized,
}), }),
RpcServerPlugin({}),
], ],
logger: BlitzLogger({}), logger: BlitzLogger({}),
}) })
export {api, getBlitzContext}

View File

@@ -57,7 +57,7 @@ const runTests = (mode?: string) => {
"should render result for open query", "should render result for open query",
async () => { async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, { const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "GET", method: "POST",
headers: {"Content-Type": "application/json; charset=utf-8"}, headers: {"Content-Type": "application/json; charset=utf-8"},
}) })
expect(res.status).toBe(200) expect(res.status).toBe(200)
@@ -67,7 +67,7 @@ const runTests = (mode?: string) => {
it("sets correct cookie", async () => { it("sets correct cookie", async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, { const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "GET", method: "POST",
headers: {"Content-Type": "application/json; charset=utf-8"}, headers: {"Content-Type": "application/json; charset=utf-8"},
}) })
const cookieHeader = res.headers.get("Set-Cookie") const cookieHeader = res.headers.get("Set-Cookie")
@@ -94,6 +94,8 @@ const runTests = (mode?: string) => {
async () => { async () => {
const browser = await webdriver(appPort, "/react-query") const browser = await webdriver(appPort, "/react-query")
await browser.refresh()
browser.waitForElementByCss("#button", 0) browser.waitForElementByCss("#button", 0)
await browser.elementByCss("#button").click() await browser.elementByCss("#button").click()
@@ -133,7 +135,7 @@ const runTests = (mode?: string) => {
it("does not require CSRF header on HEAD requests", async () => { it("does not require CSRF header on HEAD requests", async () => {
const res = await fetch(`http://localhost:${appPort}/api/noauth`, { const res = await fetch(`http://localhost:${appPort}/api/noauth`, {
method: "GET", method: "POST",
headers: {"Content-Type": "application/json; charset=utf-8"}, headers: {"Content-Type": "application/json; charset=utf-8"},
}) })
const cookieHeader = res.headers.get("Set-Cookie") const cookieHeader = res.headers.get("Set-Cookie")
@@ -153,7 +155,7 @@ const runTests = (mode?: string) => {
} }
describe("Auth Tests", () => { describe("Auth Tests", () => {
describe("dev mode", async () => { describe("dev mode - webpack", async () => {
beforeAll(async () => { beforeAll(async () => {
try { try {
await runBlitzCommand(["prisma", "migrate", "reset", "--force"]) await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
@@ -169,19 +171,19 @@ describe("Auth Tests", () => {
runTests() runTests()
}) })
// describe("server mode", () => { describe("dev mode - turbo", async () => {
// beforeAll(async () => { beforeAll(async () => {
// try { try {
// await runBlitzCommand(["prisma", "generate"]) await runBlitzCommand(["prisma", "migrate", "reset", "--force"])
// await runBlitzCommand(["prisma", "migrate", "deploy"]) appPort = await findPort()
// await blitzBuild() app = await blitzLaunchApp(appPort, {cwd: process.cwd()}, true)
// // appPort = await findPort() } catch (error) {
// app = await blitzStart(appPort, {cwd: process.cwd()}) console.log(error)
// } catch (err) { }
// console.log(err) }, 5000 * 60 * 2)
// } afterAll(async () => {
// }, 5000 * 60 * 2) await killApp(app)
// afterAll(async () => await killApp(app)) })
// runTests() runTests()
// }) })
}) })

View File

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

View File

@@ -16,19 +16,19 @@
"prisma:studio": "prisma studio" "prisma:studio": "prisma studio"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "3.0.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@next/bundle-analyzer": "12.0.8", "@next/bundle-analyzer": "12.0.8",
"@types/express": "4.17.13", "@types/express": "4.17.13",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",

View File

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

View File

@@ -8,14 +8,14 @@
"clean": "rm -rf .turbo && rm -rf node_modules" "clean": "rm -rf .turbo && rm -rf node_modules"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"@tanstack/react-query": "4.0.10", "@tanstack/react-query": "4.0.10",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"

View File

@@ -16,18 +16,18 @@
"schema": "db/schema.prisma" "schema": "db/schema.prisma"
}, },
"dependencies": { "dependencies": {
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "3.0.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@next/bundle-analyzer": "12.0.8", "@next/bundle-analyzer": "12.0.8",
"@types/express": "4.17.13", "@types/express": "4.17.13",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",

View File

@@ -7,11 +7,11 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next" "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
}, },
"dependencies": { "dependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },

View File

@@ -7,11 +7,11 @@
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next" "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
}, },
"dependencies": { "dependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },

View File

@@ -16,19 +16,19 @@
"schema": "db/schema.prisma" "schema": "db/schema.prisma"
}, },
"dependencies": { "dependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/next": "2.0.2", "@blitzjs/next": "2.1.2",
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@prisma/client": "4.6.1", "@prisma/client": "4.6.1",
"blitz": "2.0.2", "blitz": "2.1.2",
"lowdb": "3.0.0", "lowdb": "3.0.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"prisma": "4.6.1", "prisma": "4.6.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0" "react-dom": "18.2.0"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@next/bundle-analyzer": "12.0.8", "@next/bundle-analyzer": "12.0.8",
"@types/express": "4.17.13", "@types/express": "4.17.13",
"@types/fs-extra": "9.0.13", "@types/fs-extra": "9.0.13",

View File

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

View File

@@ -233,6 +233,7 @@ export function runBlitzCommandDev(argv, stdOut, opts: RunNextCommandDevOptions
} }
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
console.log(`Running command "blitz ${argv.join(" ")}"`)
const instance = spawn("node", [blitzBin, ...argv], { const instance = spawn("node", [blitzBin, ...argv], {
cwd, cwd,
env, env,
@@ -294,8 +295,8 @@ export function runBlitzCommandDev(argv, stdOut, opts: RunNextCommandDevOptions
} }
// Blitz Utils // Blitz Utils
export function blitzLaunchApp(port, opts: RunNextCommandDevOptions) { export function blitzLaunchApp(port, opts: RunNextCommandDevOptions, turbo = false) {
return runBlitzCommandDev(["dev", "-p", port], undefined, opts) return runBlitzCommandDev(["dev", "-p", port, turbo ? "--turbo" : ""], undefined, opts)
} }
export function blitzBuild(args = [], opts = {}): any { export function blitzBuild(args = [], opts = {}): any {
@@ -437,7 +438,7 @@ export function runNextCommandDev(argv, stdOut, opts: RunNextCommandDevOptions =
} }
if (opts.stdout !== false) { if (opts.stdout !== false) {
process.stdout.write(message) process.stdout.write(message)
} }
} }
@@ -923,4 +924,5 @@ interface RunNextCommandDevOptions {
nodeArgs?: [] nodeArgs?: []
bootupMarker?: any bootupMarker?: any
nextStart?: boolean nextStart?: boolean
turbo?: boolean
} }

View File

@@ -3,9 +3,9 @@
"version": "0.0.0", "version": "0.0.0",
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@blitzjs/config": "workspace:2.0.2", "@blitzjs/config": "workspace:2.1.2",
"@blitzjs/next": "workspace:2.0.2", "@blitzjs/next": "workspace:2.1.2",
"@blitzjs/rpc": "workspace:2.0.2", "@blitzjs/rpc": "workspace:2.1.2",
"@tanstack/react-query": "4.13.0", "@tanstack/react-query": "4.13.0",
"@testing-library/react": "13.4.0", "@testing-library/react": "13.4.0",
"@types/express": "4.17.13", "@types/express": "4.17.13",

View File

@@ -29,9 +29,9 @@
"husky": "8.0.2", "husky": "8.0.2",
"jsdom": "^19.0.0", "jsdom": "^19.0.0",
"lint-staged": "13.0.3", "lint-staged": "13.0.3",
"next": "14.0.4", "next": "14.3.0-canary.28",
"only-allow": "1.1.0", "only-allow": "1.1.0",
"prettier": "^2.7.1", "prettier": "^2.8.8",
"prettier-plugin-prisma": "4.4.0", "prettier-plugin-prisma": "4.4.0",
"pretty-quick": "3.1.3", "pretty-quick": "3.1.3",
"turbo": "1.10.9", "turbo": "1.10.9",
@@ -47,7 +47,11 @@
}, },
"pnpm": { "pnpm": {
"patchedDependencies": { "patchedDependencies": {
"next-auth@4.18.7": "patches/next-auth@4.18.7.patch" "next-auth@4.24.7": "patches/next-auth@4.24.7.patch"
},
"overrides": {
"@types/mime": "3.0.4",
"next": "14.3.0-canary.28"
} }
} }
} }

View File

@@ -1,5 +1,133 @@
# @blitzjs/auth # @blitzjs/auth
## 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
- 318e9740d: feat: support next-auth version 4.24.7
- Updated dependencies [318e9740d]
- blitz@2.0.10
## 2.0.9
### Patch Changes
- Updated dependencies [5a14306f7]
- blitz@2.0.9
## 2.0.8
### Patch Changes
- 77555468f: fix: add missing host while initialising the next-auth adapter
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- blitz@2.0.8
## 2.0.7
### Patch Changes
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
## 2.0.6
### Patch Changes
- 5a587a6c3: Fix bundling issue that occurs in vercel due to the way imports were handled internally
- Updated dependencies [76a2544f9]
- blitz@2.0.6
## 2.0.5
### Patch Changes
- 6f54841b7: fix: getBlitzContext() can only be used in React Server Components in Nextjs 13 or higher
- 8a417533f: fix: remove restriction to use `secure` cookies in localhost / during development following spec in [developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
- Updated dependencies [6f54841b7]
- blitz@2.0.5
## 2.0.4
### Patch Changes
- dd604c767: perf: add filter to select only non expired sessions
- Updated dependencies [dd604c767]
- Updated dependencies [28a79040e]
- blitz@2.0.4
## 2.0.3
### Patch Changes
- Updated dependencies [2f5c8a3a0]
- blitz@2.0.3
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@blitzjs/auth", "name": "@blitzjs/auth",
"version": "2.0.2", "version": "2.1.2",
"homepage": "https://blitzjs.com/", "homepage": "https://blitzjs.com/",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -50,7 +50,7 @@
"url": "0.11.0" "url": "0.11.0"
}, },
"peerDependencies": { "peerDependencies": {
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "*", "next": "*",
"next-auth": "*", "next-auth": "*",
"secure-password": "4.0.0" "secure-password": "4.0.0"
@@ -67,7 +67,7 @@
} }
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@testing-library/react": "13.4.0", "@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "8.0.1", "@testing-library/react-hooks": "8.0.1",
"@types/cookie": "0.4.1", "@types/cookie": "0.4.1",
@@ -75,9 +75,9 @@
"@types/jsonwebtoken": "8.5.8", "@types/jsonwebtoken": "8.5.8",
"@types/react": "18.0.25", "@types/react": "18.0.25",
"@types/react-dom": "17.0.14", "@types/react-dom": "17.0.14",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"next-auth": "4.18.7", "next-auth": "4.24.7",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"secure-password": "4.0.0", "secure-password": "4.0.0",

View File

@@ -161,6 +161,8 @@ export const useSession = (options: UseSessionOptions = {}): ClientSession => {
if (isServer) { if (isServer) {
const e = new Error() const e = new Error()
e.name = "Rendering Suspense fallback..." e.name = "Rendering Suspense fallback..."
;(e as any).digest = "DYNAMIC_SERVER_USAGE"
e.message = "DYNAMIC_SERVER_USAGE"
delete e.stack delete e.stack
throw e throw e
} else { } else {

View File

@@ -107,13 +107,8 @@ export function NextAuthAdapter<P extends Provider[]>(
providerId = providerId.split("?")[0] providerId = providerId.split("?")[0]
} }
const {options, cookies} = await init({ const {options, cookies} = await init({
// @ts-ignore
url: new URL(
// @ts-ignore
internalRequest.url!,
process.env.APP_ORIGIN || process.env.BLITZ_DEV_SERVER_ORIGIN,
),
authOptions: config as unknown as AuthOptions, authOptions: config as unknown as AuthOptions,
origin: internalRequest.origin,
action, action,
providerId, providerId,
callbackUrl: req.body?.callbackUrl ?? (req.query?.callbackUrl as string), callbackUrl: req.body?.callbackUrl ?? (req.query?.callbackUrl as string),

View File

@@ -47,7 +47,12 @@ async function readJSONBody(
// prettier-ignore // prettier-ignore
const actions = [ "providers", "session", "csrf", "login", "signout", "callback", "verify-request", "error", "_log"] const actions = [ "providers", "session", "csrf", "login", "signout", "callback", "verify-request", "error", "_log"]
export async function toInternalRequest(req: Request): Promise<RequestInternal | Error> { export async function toInternalRequest(req: Request): Promise<
| (RequestInternal & {
url: URL
})
| Error
> {
try { try {
// TODO: url.toString() should not include action and providerId // TODO: url.toString() should not include action and providerId
// see init.ts // see init.ts
@@ -69,17 +74,28 @@ export async function toInternalRequest(req: Request): Promise<RequestInternal |
providerId = providerIdOrAction providerId = providerIdOrAction
} }
const headers = Object.fromEntries(req.headers)
const detectOrigin = (host?: string, proto?: string) => {
if (process.env.VERCEL ?? process.env.FLIGHTCONTROL ?? process.env.AUTH_TRUST_HOST)
return `${proto === "http" ? "http" : "https"}://${host}`
return process.env.NEXTAUTH_URL
}
return { return {
//@ts-ignore
url, url,
action, action,
providerId, providerId,
method: req.method ?? "GET", method: req.method ?? "GET",
headers: Object.fromEntries(req.headers), headers,
body: req.body ? await readJSONBody(req.body) : undefined, body: req.body ? await readJSONBody(req.body) : undefined,
cookies: parseCookie(req.headers.get("cookie") ?? "") ?? {}, cookies: parseCookie(req.headers.get("cookie") ?? "") ?? {},
error: url.searchParams.get("error") ?? undefined, error: url.searchParams.get("error") ?? undefined,
query: Object.fromEntries(url.searchParams), query: Object.fromEntries(url.searchParams),
origin: detectOrigin(
headers["x-forwarded-host"] ?? headers.host,
headers["x-forwarded-proto"],
),
} }
} catch (error) { } catch (error) {
return error as Error return error as Error

View File

@@ -1,6 +1,6 @@
import {RequestMiddleware, Ctx, createServerPlugin} from "blitz" import {RequestMiddleware, Ctx, createServerPlugin} from "blitz"
import {assert} from "blitz" import {assert} from "blitz"
import {IncomingMessage, ServerResponse} from "http" import type {IncomingMessage, ServerResponse} from "http"
import {PublicData, SessionModel, SessionConfigMethods} from "../shared/types" import {PublicData, SessionModel, SessionConfigMethods} from "../shared/types"
import {getBlitzContext, getSession, useAuthenticatedBlitzContext} from "./auth-sessions" import {getBlitzContext, getSession, useAuthenticatedBlitzContext} from "./auth-sessions"
@@ -22,7 +22,9 @@ interface IsAuthorized {
interface PrismaClientWithSession { interface PrismaClientWithSession {
session: { session: {
findFirst(args?: {where?: {handle?: SessionModel["handle"]}}): Promise<SessionModel | null> findFirst(args?: {where?: {handle?: SessionModel["handle"]}}): Promise<SessionModel | null>
findMany(args?: {where?: {userId?: PublicData["userId"]}}): Promise<SessionModel[]> findMany(args?: {
where?: {userId?: PublicData["userId"]; expiresAt?: {gt?: Date}}
}): Promise<SessionModel[]>
create(args: { create(args: {
data: SessionModel & { data: SessionModel & {
userId?: any userId?: any
@@ -42,7 +44,7 @@ export const PrismaStorage = <Client extends PrismaClientWithSession>(
): SessionConfigMethods => { ): SessionConfigMethods => {
return { return {
getSession: (handle) => db.session.findFirst({where: {handle}}), getSession: (handle) => db.session.findFirst({where: {handle}}),
getSessions: (userId) => db.session.findMany({where: {userId}}), getSessions: (userId) => db.session.findMany({where: {userId, expiresAt: {gt: new Date()}}}),
createSession: (session) => { createSession: (session) => {
let user let user
if (session.userId) { if (session.userId) {
@@ -128,11 +130,28 @@ export const AuthServerPlugin = createServerPlugin((options: AuthPluginOptions)
if (!globalThis.__BLITZ_GET_RSC_CONTEXT) { if (!globalThis.__BLITZ_GET_RSC_CONTEXT) {
globalThis.__BLITZ_GET_RSC_CONTEXT = getBlitzContext 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 { return {
requestMiddlewares: [authPluginSessionMiddleware()], requestMiddlewares: [authPluginSessionMiddleware()],
exports: () => ({ exports: () => ({
getBlitzContext, getBlitzContext,
useAuthenticatedBlitzContext, useAuthenticatedBlitzContext,
withBlitzAuth,
}), }),
} }
}) })

View File

@@ -1,41 +0,0 @@
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,7 +1,6 @@
import {fromBase64, toBase64} from "b64-lite" import {fromBase64, toBase64} from "b64-lite"
import cookie, {parse} from "cookie" import cookie, {parse} from "cookie"
import {IncomingMessage, ServerResponse} from "http" import jsonwebtoken from "jsonwebtoken"
import {sign as jwtSign, verify as jwtVerify} from "jsonwebtoken"
import { import {
assert, assert,
isPast, isPast,
@@ -13,9 +12,9 @@ import {
AuthorizationError, AuthorizationError,
CSRFTokenMismatchError, CSRFTokenMismatchError,
log, log,
AuthenticatedCtx,
baseLogger, baseLogger,
chalk, chalk,
AuthenticatedCtx,
} from "blitz" } from "blitz"
import { import {
EmptyPublicData, EmptyPublicData,
@@ -39,12 +38,69 @@ import {
AuthenticatedSessionContext, AuthenticatedSessionContext,
} from "../shared" } from "../shared"
import {generateToken, hash256} from "./auth-utils" import {generateToken, hash256} from "./auth-utils"
import {Socket} from "net"
import {UrlObject} from "url"
import {formatWithValidation} from "../shared/url-utils" import {formatWithValidation} from "../shared/url-utils"
export function isLocalhost(req: any): boolean { import type {UrlObject} from "url"
let {host} = req.headers 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 || ""
}
let localhost = false let localhost = false
if (host) { if (host) {
host = host.split(":")[0] host = host.split(":")[0]
@@ -69,7 +125,8 @@ export function getCookieParser(headers: {[key: string]: undefined | string | st
} }
} }
const debug = require("debug")("blitz:session") import Debug from "debug"
const debug = Debug("blitz:session")
export interface SimpleRolesIsAuthorized<RoleType = string> { export interface SimpleRolesIsAuthorized<RoleType = string> {
({ctx, args}: {ctx: any; args: [roleOrRoles?: RoleType | RoleType[]]}): boolean ({ctx, args}: {ctx: any; args: [roleOrRoles?: RoleType | RoleType[]]}): boolean
@@ -131,14 +188,6 @@ type AuthedSessionKernel = {
} }
type SessionKernel = AnonSessionKernel | 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( function ensureMiddlewareResponse(
res: ServerResponse & {[key: string]: any}, res: ServerResponse & {[key: string]: any},
): asserts res is ServerResponse & {blitzCtx: Ctx} { ): asserts res is ServerResponse & {blitzCtx: Ctx} {
@@ -147,71 +196,126 @@ 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( export async function getSession(
req: IncomingMessage, req: IncomingMessage,
res: ServerResponse, res: ServerResponse,
appDir = false, isRsc: boolean,
): Promise<SessionContext>
export async function getSession(
req: IncomingMessage | Request,
res?: ServerResponse,
isRsc?: boolean,
): Promise<SessionContext> { ): Promise<SessionContext> {
ensureApiRequest(req) const headers = convertRequestToHeader(req)
ensureMiddlewareResponse(res) if (res) {
ensureMiddlewareResponse(res)
debug("cookiePrefix", globalThis.__BLITZ_SESSION_COOKIE_PREFIX) debug("cookiePrefix", globalThis.__BLITZ_SESSION_COOKIE_PREFIX)
if (res.blitzCtx.session) {
if (res.blitzCtx.session) { debug("Returning existing session")
debug("Returning existing session") return res.blitzCtx.session
return res.blitzCtx.session }
} }
const method = req.method
let sessionKernel = await getSessionKernel(req, res) let sessionKernel = await getSessionKernel({headers, method})
if (sessionKernel) { if (sessionKernel) {
debug("Got existing session", sessionKernel) debug("Got existing session", sessionKernel)
} }
if (!sessionKernel) { if (!sessionKernel) {
debug("No session found, creating anonymous session") sessionKernel = await createAnonymousSession({headers})
sessionKernel = await createAnonymousSession(req, res)
} }
const sessionContext = makeProxyToPublicData( const sessionContext = makeProxyToPublicData(
new SessionContextClass(req, res, sessionKernel, appDir), new SessionContextClass(headers, sessionKernel, !!isRsc, res),
) )
debug("New session context") debug("New session context")
res.blitzCtx.session = sessionContext if (res) {
;(res as any).blitzCtx = {
session: sessionContext,
}
sessionContext.setSession(res)
}
return sessionContext return sessionContext
} }
export async function getBlitzContext(): Promise<Ctx> {
const {headers, cookies} = await import("next/headers").catch(() => {
throw new Error(
"getBlitzContext() can only be used in React Server Components in Nextjs 13 or higher",
)
})
const req = new IncomingMessage(new Socket()) as IncomingMessage & {
cookies: {[key: string]: string}
}
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,
}
return ctx
}
interface RouteUrlObject extends Pick<UrlObject, "pathname" | "query" | "href"> { interface RouteUrlObject extends Pick<UrlObject, "pathname" | "query" | "href"> {
pathname: string 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 reqHeader = Object.fromEntries(headers())
const csrfToken = cookies().get(COOKIE_CSRF_TOKEN())
if (csrfToken) {
reqHeader[HEADER_CSRF] = csrfToken.value
}
const session = await getSession(
{
headers: new Headers(reqHeader),
method: "POST",
} as Request,
null as never,
true,
)
const ctx: Ctx = {
session,
}
return 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.",
)
}
throw e
}
}
export async function useAuthenticatedBlitzContext({ export async function useAuthenticatedBlitzContext({
redirectTo, redirectTo,
redirectAuthenticatedTo, redirectAuthenticatedTo,
@@ -227,70 +331,63 @@ export async function useAuthenticatedBlitzContext({
}) })
const ctx: Ctx = await getBlitzContext() const ctx: Ctx = await getBlitzContext()
const userId = ctx.session.userId const userId = ctx.session.userId
const {redirect} = await import("next/navigation").catch(() => { try {
throw new Error( const {redirect} = require("next/navigation")
"useAuthenticatedBlitzContext() can only be used in React Server Components in Nextjs 13 or higher", if (userId) {
) debug("[useAuthenticatedBlitzContext] User is authenticated")
}) if (redirectAuthenticatedTo) {
if (userId) { if (typeof redirectAuthenticatedTo === "function") {
debug("[useAuthenticatedBlitzContext] User is authenticated") redirectAuthenticatedTo = redirectAuthenticatedTo(ctx)
if (redirectAuthenticatedTo) { }
if (typeof redirectAuthenticatedTo === "function") { const redirectUrl =
redirectAuthenticatedTo = redirectAuthenticatedTo(ctx) typeof redirectAuthenticatedTo === "string"
? redirectAuthenticatedTo
: formatWithValidation(redirectAuthenticatedTo)
debug("[useAuthenticatedBlitzContext] Redirecting to", redirectUrl)
if (role) {
try {
ctx.session.$authorize(role)
} catch (e) {
log.info("Authentication Redirect: " + customChalk.dim(`Role ${role}`), redirectTo)
redirect(redirectUrl)
}
} else {
log.info("Authentication Redirect: " + customChalk.dim("(Authenticated)"), redirectUrl)
redirect(redirectUrl)
}
} }
const redirectUrl = if (redirectTo && role) {
typeof redirectAuthenticatedTo === "string" debug("[useAuthenticatedBlitzContext] redirectTo and role are both defined.")
? redirectAuthenticatedTo
: formatWithValidation(redirectAuthenticatedTo)
debug("[useAuthenticatedBlitzContext] Redirecting to", redirectUrl)
if (role) {
try { try {
ctx.session.$authorize(role) ctx.session.$authorize(role)
} catch (e) { } catch (e) {
log.info("Authentication Redirect: " + customChalk.dim(`Role ${role}`), redirectTo) log.error("Authorization Error: " + (e as Error).message)
redirect(redirectUrl) if (typeof redirectTo !== "string") {
redirectTo = formatWithValidation(redirectTo)
}
log.info("Authorization Redirect: " + customChalk.dim(`Role ${role}`), redirectTo)
redirect(redirectTo)
} }
} else {
log.info("Authentication Redirect: " + customChalk.dim("(Authenticated)"), redirectUrl)
redirect(redirectUrl)
} }
} } else {
if (redirectTo && role) { debug("[useAuthenticatedBlitzContext] User is not authenticated")
debug("[useAuthenticatedBlitzContext] redirectTo and role are both defined.") if (redirectTo) {
try {
ctx.session.$authorize(role)
} catch (e) {
log.error("Authorization Error: " + (e as Error).message)
if (typeof redirectTo !== "string") { if (typeof redirectTo !== "string") {
redirectTo = formatWithValidation(redirectTo) redirectTo = formatWithValidation(redirectTo)
} }
log.info("Authorization Redirect: " + customChalk.dim(`Role ${role}`), redirectTo) log.info("Authentication Redirect: " + customChalk.dim("(Not authenticated)"), redirectTo)
redirect(redirectTo) redirect(redirectTo)
} }
} }
} else { return ctx as AuthenticatedCtx
debug("[useAuthenticatedBlitzContext] User is not authenticated") } catch (e) {
if (redirectTo) { if ((e as NodeJS.ErrnoException).code === "MODULE_NOT_FOUND") {
if (typeof redirectTo !== "string") { throw new Error(
redirectTo = formatWithValidation(redirectTo) "Usage of `useAuthenticatedBlitzContext` is supported only in next.js 13.0.0 and above. Please upgrade your next.js version.",
} )
log.info("Authentication Redirect: " + customChalk.dim("(Not authenticated)"), redirectTo)
redirect(redirectTo)
} }
throw e
} }
return ctx as AuthenticatedCtx
}
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 NotSupportedMessage = async (method: string) => {
@@ -300,21 +397,27 @@ const NotSupportedMessage = async (method: string) => {
} }
export class SessionContextClass implements SessionContext { export class SessionContextClass implements SessionContext {
private _req: IncomingMessage & {cookies: {[key: string]: string}} private _headers: Headers
private _res: ServerResponse & {blitzCtx: Ctx}
private _kernel: SessionKernel private _kernel: SessionKernel
private _appDir: boolean private _isRsc: boolean
private _response?: ServerResponse
constructor( private static headersToIncludeInResponse = [
req: IncomingMessage & {cookies: {[key: string]: string}}, HEADER_CSRF,
res: ServerResponse & {blitzCtx: Ctx}, HEADER_CSRF_ERROR,
kernel: SessionKernel, HEADER_PUBLIC_DATA_TOKEN,
appDir: boolean, HEADER_SESSION_CREATED,
) { ]
this._req = req
this._res = res constructor(headers: Headers, kernel: SessionKernel, isRsc: boolean, response?: ServerResponse) {
this._headers = headers
this._kernel = kernel this._kernel = kernel
this._appDir = appDir this._isRsc = isRsc
this._response = response
}
$antiCSRFToken() {
return this._kernel.antiCSRFToken
} }
get $handle() { get $handle() {
@@ -342,38 +445,69 @@ export class SessionContextClass implements SessionContext {
$isAuthorized(...args: IsAuthorizedArgs) { $isAuthorized(...args: IsAuthorizedArgs) {
if (!this.userId) return false if (!this.userId) return false
return global.sessionConfig.isAuthorized({ctx: this._res.blitzCtx, args}) return global.sessionConfig.isAuthorized({
ctx: {
session: this as AuthenticatedSessionContext,
},
args,
})
} }
$thisIsAuthorized(...args: IsAuthorizedArgs): this is AuthenticatedSessionContext { $thisIsAuthorized(...args: IsAuthorizedArgs): this is AuthenticatedSessionContext {
return this.$isAuthorized(...args) 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>) { async $create(publicData: PublicData, privateData?: Record<any, any>) {
if (this._appDir) { if (this._isRsc) {
void NotSupportedMessage("$create") void NotSupportedMessage("$create")
return return
} }
this._kernel = await createNewSession({ this._kernel = await createNewSession({
req: this._req, headers: this._headers,
res: this._res,
publicData, publicData,
privateData, privateData,
jwtPayload: this._kernel.jwtPayload, jwtPayload: this._kernel.jwtPayload,
anonymous: false, anonymous: false,
}) })
if (this._response) this.setSession(this._response)
} }
async $revoke() { async $revoke() {
if (this._appDir) { if (this._isRsc) {
void NotSupportedMessage("$revoke") void NotSupportedMessage("$revoke")
return return
} }
this._kernel = await revokeSession(this._req, this._res, this.$handle) this._kernel = await revokeSession(this._headers, this.$handle)
if (this._response) this.setSession(this._response)
} }
async $revokeAll() { async $revokeAll() {
if (this._appDir) { if (this._isRsc) {
void NotSupportedMessage("$revokeAll") void NotSupportedMessage("$revokeAll")
return return
} }
@@ -385,25 +519,28 @@ export class SessionContextClass implements SessionContext {
} }
async $setPublicData(data: Record<any, any>) { async $setPublicData(data: Record<any, any>) {
if (this._appDir) { if (this._isRsc) {
void NotSupportedMessage("$setPublicData") void NotSupportedMessage("$setPublicData")
return return
} }
if (this.userId) { if (this.userId) {
await syncPubicDataFieldsForUserIfNeeded(this.userId, data) await syncPubicDataFieldsForUserIfNeeded(this.userId, data)
} }
this._kernel.publicData = await setPublicData(this._req, this._res, this._kernel, data) this._kernel.publicData = await setPublicData(this._headers, this._kernel, data)
if (this._response) this.setSession(this._response)
} }
async $getPrivateData() { async $getPrivateData() {
return (await getPrivateData(this.$handle)) || {} return (await getPrivateData(this.$handle)) || {}
} }
$setPrivateData(data: Record<any, any>) {
if (this._appDir) { async $setPrivateData(data: Record<any, any>) {
if (this._isRsc) {
void NotSupportedMessage("$setPrivateData") void NotSupportedMessage("$setPrivateData")
return Promise.resolve() return Promise.resolve()
} }
return setPrivateData(this._kernel, data) await setPrivateData(this._kernel, data)
if (this._response) this.setSession(this._response)
} }
} }
@@ -495,7 +632,7 @@ const JWT_ANONYMOUS_SUBJECT = "anonymous"
const JWT_ALGORITHM = "HS256" const JWT_ALGORITHM = "HS256"
const createAnonymousSessionToken = (payload: AnonymousSessionPayload) => { const createAnonymousSessionToken = (payload: AnonymousSessionPayload) => {
return jwtSign({[JWT_NAMESPACE]: payload}, getSessionSecretKey() || "", { return jsonwebtoken.sign({[JWT_NAMESPACE]: payload}, getSessionSecretKey() || "", {
algorithm: JWT_ALGORITHM, algorithm: JWT_ALGORITHM,
issuer: JWT_ISSUER, issuer: JWT_ISSUER,
audience: JWT_AUDIENCE, audience: JWT_AUDIENCE,
@@ -509,7 +646,7 @@ const parseAnonymousSessionToken = (token: string) => {
const secret = getSessionSecretKey() const secret = getSessionSecretKey()
try { try {
const fullPayload = jwtVerify(token, secret!, { const fullPayload = jsonwebtoken.verify(token, secret!, {
algorithms: [JWT_ALGORITHM], algorithms: [JWT_ALGORITHM],
issuer: JWT_ISSUER, issuer: JWT_ISSUER,
audience: JWT_AUDIENCE, audience: JWT_AUDIENCE,
@@ -526,136 +663,127 @@ const parseAnonymousSessionToken = (token: string) => {
} }
} }
export const setCookie = (res: ServerResponse, cookieStr: string) => { const cookieOptions = (headers: Headers, expires: Date, httpOnly: boolean) => {
const getCookieName = (c: string) => c.split("=", 2)[0] return {
const appendCookie = () => append(res, "Set-Cookie", cookieStr) path: "/",
secure:
const cookiesHeader = res.getHeader("Set-Cookie") global.sessionConfig.secureCookies &&
const cookieName = getCookieName(cookieStr) !isLocalhost({
headers,
if (typeof cookiesHeader !== "string" && !Array.isArray(cookiesHeader)) { } as Request),
appendCookie() sameSite: global.sessionConfig.sameSite,
return domain: global.sessionConfig.domain,
expires: new Date(expires),
httpOnly,
} }
}
if (typeof cookiesHeader === "string") { function replaceOrAppendValueInSetCookieHeader(
if (cookieName === getCookieName(cookiesHeader)) { headers: Headers,
res.setHeader("Set-Cookie", cookieStr) 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(", ")
} else { } else {
appendCookie() if (i === cookiesAsArray.length - 1) {
} cookiesAsArray.push(newValue)
} else { return cookiesAsArray.join(", ")
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 setHeader = (res: ServerResponse, name: string, value: string) => { const setSessionCookie = (headers: Headers, sessionToken: string, expiresAt: Date) => {
res.setHeader(name, value) const sessionCookie = cookie.serialize(
if ("_blitz" in res) { COOKIE_SESSION_TOKEN(),
;(res as any)._blitz[name] = value sessionToken,
} cookieOptions(headers, expiresAt, true),
}
const setSessionCookie = (
req: IncomingMessage,
res: ServerResponse,
sessionToken: string,
expiresAt: Date,
) => {
setCookie(
res,
cookie.serialize(COOKIE_SESSION_TOKEN(), sessionToken, {
path: "/",
httpOnly: true,
secure: global.sessionConfig.secureCookies && !isLocalhost(req),
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
) )
} const newCookies = replaceOrAppendValueInSetCookieHeader(
headers,
const setAnonymousSessionCookie = ( COOKIE_SESSION_TOKEN(),
req: IncomingMessage, sessionCookie,
res: ServerResponse,
token: string,
expiresAt: Date,
) => {
setCookie(
res,
cookie.serialize(COOKIE_ANONYMOUS_SESSION_TOKEN(), token, {
path: "/",
httpOnly: true,
secure: global.sessionConfig.secureCookies && !isLocalhost(req),
sameSite: global.sessionConfig.sameSite,
domain: global.sessionConfig.domain,
expires: expiresAt,
}),
) )
headers.set("Set-Cookie", newCookies)
} }
const setCSRFCookie = ( const setAnonymousSessionCookie = (headers: Headers, token: string, expiresAt: Date) => {
req: IncomingMessage, const anonCookie = cookie.serialize(
res: ServerResponse, COOKIE_ANONYMOUS_SESSION_TOKEN(),
antiCSRFToken: string, token,
expiresAt: Date, cookieOptions(headers, expiresAt, true),
) => { )
const newCookies = replaceOrAppendValueInSetCookieHeader(
headers,
COOKIE_ANONYMOUS_SESSION_TOKEN(),
anonCookie,
)
headers.set("Set-Cookie", newCookies)
}
const setCSRFCookie = (headers: Headers, antiCSRFToken: string, expiresAt: Date) => {
debug("setCSRFCookie", antiCSRFToken) debug("setCSRFCookie", antiCSRFToken)
assert(antiCSRFToken !== undefined, "Internal error: antiCSRFToken is being set to undefined") assert(antiCSRFToken !== undefined, "Internal error: antiCSRFToken is being set to undefined")
setCookie( const csrfCookie = cookie.serialize(
res, COOKIE_CSRF_TOKEN(),
cookie.serialize(COOKIE_CSRF_TOKEN(), antiCSRFToken, { antiCSRFToken,
path: "/", cookieOptions(headers, expiresAt, false),
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 = ( const setPublicDataCookie = (headers: Headers, publicDataToken: string, expiresAt: Date) => {
req: IncomingMessage, headers.set(HEADER_PUBLIC_DATA_TOKEN, "updated")
res: ServerResponse, const publicDataCookie = cookie.serialize(
publicDataToken: string, COOKIE_PUBLIC_DATA_TOKEN(),
expiresAt: Date, publicDataToken,
) => { cookieOptions(headers, expiresAt, false),
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 // Get Session
// -------------------------------- // --------------------------------
async function getSessionKernel( async function getSessionKernel({
req: IncomingMessage & {cookies: {[key: string]: string}}, headers,
res: ServerResponse, method,
): Promise<SessionKernel | null> { }: {
const anonymousSessionToken = req.cookies[COOKIE_ANONYMOUS_SESSION_TOKEN()] headers: Headers
const sessionToken = req.cookies[COOKIE_SESSION_TOKEN()] // for essential method method: string | undefined
const idRefreshToken = req.cookies[COOKIE_REFRESH_TOKEN()] // for advanced method }): 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,
})
const enableCsrfProtection = const enableCsrfProtection =
req.method !== "GET" && method !== "GET" &&
req.method !== "OPTIONS" && method !== "OPTIONS" &&
req.method !== "HEAD" && method !== "HEAD" &&
!process.env.DANGEROUSLY_DISABLE_CSRF_PROTECTION !process.env.DANGEROUSLY_DISABLE_CSRF_PROTECTION
const antiCSRFToken = req.headers[HEADER_CSRF] as string | undefined
if (sessionToken) { if (sessionToken) {
debug("[getSessionKernel] Request has sessionToken") debug("[getSessionKernel] Request has sessionToken")
@@ -698,7 +826,7 @@ async function getSessionKernel(
) )
} }
setHeader(res, HEADER_CSRF_ERROR, "true") headers.set(HEADER_CSRF_ERROR, "true")
throw new CSRFTokenMismatchError() throw new CSRFTokenMismatchError()
} }
@@ -710,7 +838,7 @@ async function getSessionKernel(
* But only renew with non-GET requests because a GET request could be from a * But only renew with non-GET requests because a GET request could be from a
* browser level navigation * browser level navigation
*/ */
if (req.method !== "GET") { if (method !== "GET") {
// The publicData in the DB could have been updated since this client last made // 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 // a request. If so, then we generate a new access token
const hasPublicDataChanged = const hasPublicDataChanged =
@@ -733,8 +861,7 @@ async function getSessionKernel(
if (hasPublicDataChanged || hasQuarterExpiryTimePassed) { if (hasPublicDataChanged || hasQuarterExpiryTimePassed) {
await refreshSession( await refreshSession(
req, headers,
res,
{ {
handle, handle,
publicData: JSON.parse(persistedSession.publicData || ""), publicData: JSON.parse(persistedSession.publicData || ""),
@@ -774,7 +901,7 @@ async function getSessionKernel(
) )
} }
setHeader(res, HEADER_CSRF_ERROR, "true") headers.set(HEADER_CSRF_ERROR, "true")
throw new CSRFTokenMismatchError() throw new CSRFTokenMismatchError()
} }
@@ -795,16 +922,14 @@ async function getSessionKernel(
// Create Session // Create Session
// -------------------------------- // --------------------------------
interface CreateNewAnonSession { interface CreateNewAnonSession {
req: IncomingMessage headers: Headers
res: ServerResponse
publicData: EmptyPublicData publicData: EmptyPublicData
privateData?: Record<any, any> privateData?: Record<any, any>
anonymous: true anonymous: true
jwtPayload?: JwtPayload jwtPayload?: JwtPayload
} }
interface CreateNewAuthedSession { interface CreateNewAuthedSession {
req: IncomingMessage headers: Headers
res: ServerResponse
publicData: PublicData publicData: PublicData
privateData?: Record<any, any> privateData?: Record<any, any>
anonymous: false anonymous: false
@@ -814,7 +939,6 @@ interface CreateNewAuthedSession {
async function createNewSession( async function createNewSession(
args: CreateNewAnonSession | CreateNewAuthedSession, args: CreateNewAnonSession | CreateNewAuthedSession,
): Promise<SessionKernel> { ): Promise<SessionKernel> {
const {req, res} = args
assert(args.publicData.userId !== undefined, "You must provide publicData.userId") assert(args.publicData.userId !== undefined, "You must provide publicData.userId")
const antiCSRFToken = createAntiCSRFToken() const antiCSRFToken = createAntiCSRFToken()
@@ -835,12 +959,12 @@ async function createNewSession(
new Date(), new Date(),
global.sessionConfig.anonSessionExpiryMinutes as number, global.sessionConfig.anonSessionExpiryMinutes as number,
) )
setAnonymousSessionCookie(req, res, anonymousSessionToken, expiresAt) setAnonymousSessionCookie(args.headers, anonymousSessionToken, expiresAt)
setCSRFCookie(req, res, antiCSRFToken, expiresAt) setCSRFCookie(args.headers, antiCSRFToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt) setPublicDataCookie(args.headers, publicDataToken, expiresAt)
// Clear the essential session cookie in case it was previously set // Clear the essential session cookie in case it was previously set
setSessionCookie(req, res, "", new Date(0)) setSessionCookie(args.headers, "", new Date(0))
setHeader(res, HEADER_SESSION_CREATED, "true") args.headers.set(HEADER_SESSION_CREATED, "true")
return { return {
handle, handle,
@@ -891,12 +1015,13 @@ async function createNewSession(
privateData: JSON.stringify(newPrivateData), privateData: JSON.stringify(newPrivateData),
}) })
setSessionCookie(req, res, sessionToken, expiresAt) setSessionCookie(args.headers, sessionToken, expiresAt)
setCSRFCookie(req, res, antiCSRFToken, expiresAt) debug("Session created", {handle, publicData: newPublicData, expiresAt})
setPublicDataCookie(req, res, publicDataToken, expiresAt) setCSRFCookie(args.headers, antiCSRFToken, expiresAt)
setPublicDataCookie(args.headers, publicDataToken, expiresAt)
// Clear the anonymous session cookie in case it was previously set // Clear the anonymous session cookie in case it was previously set
setAnonymousSessionCookie(req, res, "", new Date(0)) setAnonymousSessionCookie(args.headers, "", new Date(0))
setHeader(res, HEADER_SESSION_CREATED, "true") args.headers.set(HEADER_SESSION_CREATED, "true")
return { return {
handle, handle,
@@ -914,10 +1039,9 @@ async function createNewSession(
} }
} }
async function createAnonymousSession(req: IncomingMessage, res: ServerResponse) { async function createAnonymousSession({headers}: {headers: Headers}) {
return await createNewSession({ return await createNewSession({
req, headers,
res,
publicData: {userId: null}, publicData: {userId: null},
anonymous: true, anonymous: true,
}) })
@@ -928,8 +1052,7 @@ async function createAnonymousSession(req: IncomingMessage, res: ServerResponse)
// -------------------------------- // --------------------------------
async function refreshSession( async function refreshSession(
req: IncomingMessage, headers: Headers,
res: ServerResponse,
sessionKernel: SessionKernel, sessionKernel: SessionKernel,
{publicDataChanged}: {publicDataChanged: boolean}, {publicDataChanged}: {publicDataChanged: boolean},
) { ) {
@@ -943,8 +1066,8 @@ async function refreshSession(
const publicDataToken = createPublicDataToken(sessionKernel.publicData) const publicDataToken = createPublicDataToken(sessionKernel.publicData)
const expiresAt = addYears(new Date(), 30) const expiresAt = addYears(new Date(), 30)
setAnonymousSessionCookie(req, res, anonymousSessionToken, expiresAt) setAnonymousSessionCookie(headers, anonymousSessionToken, expiresAt)
setPublicDataCookie(req, res, publicDataToken, expiresAt) setPublicDataCookie(headers, publicDataToken, expiresAt)
} else if (global.sessionConfig.method === "essential" && "sessionToken" in sessionKernel) { } else if (global.sessionConfig.method === "essential" && "sessionToken" in sessionKernel) {
const expiresAt = addMinutes(new Date(), global.sessionConfig.sessionExpiryMinutes as number) const expiresAt = addMinutes(new Date(), global.sessionConfig.sessionExpiryMinutes as number)
@@ -952,7 +1075,7 @@ async function refreshSession(
if (publicDataChanged) { if (publicDataChanged) {
debug("Public data has changed") debug("Public data has changed")
const publicDataToken = createPublicDataToken(sessionKernel.publicData) const publicDataToken = createPublicDataToken(sessionKernel.publicData)
setPublicDataCookie(req, res, publicDataToken, expiresAt) setPublicDataCookie(headers, publicDataToken, expiresAt)
await global.sessionConfig.updateSession(sessionKernel.handle, { await global.sessionConfig.updateSession(sessionKernel.handle, {
expiresAt, expiresAt,
publicData: JSON.stringify(sessionKernel.publicData), publicData: JSON.stringify(sessionKernel.publicData),
@@ -994,12 +1117,7 @@ async function syncPubicDataFieldsForUserIfNeeded(
} }
} }
async function revokeSession( async function revokeSession(headers: Headers, handle: string, anonymous: boolean = false) {
req: IncomingMessage,
res: ServerResponse,
handle: string,
anonymous: boolean = false,
) {
debug("Revoking session", handle) debug("Revoking session", handle)
if (!anonymous) { if (!anonymous) {
try { try {
@@ -1012,7 +1130,9 @@ async function revokeSession(
// This fixes race condition where all client side queries get refreshed // This fixes race condition where all client side queries get refreshed
// in parallel and each creates a new anon session // in parallel and each creates a new anon session
// https://github.com/blitz-js/blitz/issues/2746 // https://github.com/blitz-js/blitz/issues/2746
return createAnonymousSession(req, res) return createAnonymousSession({
headers,
})
} }
async function revokeAllSessionsForUser(userId: PublicData["userId"]) { async function revokeAllSessionsForUser(userId: PublicData["userId"]) {
@@ -1078,8 +1198,7 @@ async function setPrivateData(sessionKernel: SessionKernel, data: Record<any, an
} }
async function setPublicData( async function setPublicData(
req: IncomingMessage, headers: Headers,
res: ServerResponse,
sessionKernel: SessionKernel, sessionKernel: SessionKernel,
data: Record<any, any>, data: Record<any, any>,
) { ) {
@@ -1091,7 +1210,7 @@ async function setPublicData(
...data, ...data,
} as PublicData } as PublicData
await refreshSession(req, res, {...sessionKernel, publicData}, {publicDataChanged: true}) await refreshSession(headers, {...sessionKernel, publicData}, {publicDataChanged: true})
return publicData return publicData
} }

View File

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

View File

@@ -1,5 +1,6 @@
//@ts-nocheck //@ts-nocheck
import {Ctx} from "blitz" import type {Ctx} from "blitz"
import type {ServerResponse} from "http"
export interface Session { export interface Session {
// isAuthorize can be injected here // isAuthorize can be injected here
@@ -66,6 +67,12 @@ export interface SessionContextBase {
$getPrivateData: () => Promise<Record<any, any>> $getPrivateData: () => Promise<Record<any, any>>
$setPrivateData: (data: Record<any, any>) => Promise<void> $setPrivateData: (data: Record<any, any>) => Promise<void>
$setPublicData: (data: Partial<Omit<PublicData, "userId">>) => 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 // Could be anonymous

View File

@@ -1,5 +1,152 @@
# @blitzjs/next # @blitzjs/next
## 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
- Updated dependencies [318e9740d]
- blitz@2.0.10
- @blitzjs/rpc@2.0.10
## 2.0.9
### Patch Changes
- 5a14306f7: fix export `enhancePrisma` in client
- Updated dependencies [5a14306f7]
- @blitzjs/rpc@2.0.9
- blitz@2.0.9
## 2.0.8
### Patch Changes
- 5e61a1681: bug: merge existing and custom blitz turbo configs
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- blitz@2.0.8
- @blitzjs/rpc@2.0.8
## 2.0.7
### Patch Changes
- ee7bf87ec: Turbopack support for Blitz
This PR includes the changes required to make the Blitz loaders work with Turbopack.
Usage:
```bash
pnpm blitz dev --turbo
```
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
- @blitzjs/rpc@2.0.7
## 2.0.6
### Patch Changes
- Updated dependencies [76a2544f9]
- blitz@2.0.6
- @blitzjs/rpc@2.0.6
## 2.0.5
### Patch Changes
- Updated dependencies [6f54841b7]
- blitz@2.0.5
- @blitzjs/rpc@2.0.5
## 2.0.4
### Patch Changes
- 28a79040e: feat: export `BlitzServerMiddleware` from blitz-next with stronger types
- Updated dependencies [dd604c767]
- Updated dependencies [28a79040e]
- blitz@2.0.4
- @blitzjs/rpc@2.0.4
## 2.0.3
### Patch Changes
- 2f5c8a3a0: Address missing sodium native prebuilds required to use secure-password during server rendering
- Updated dependencies [2f5c8a3a0]
- @blitzjs/rpc@2.0.3
- blitz@2.0.3
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@blitzjs/next", "name": "@blitzjs/next",
"version": "2.0.2", "version": "2.1.2",
"homepage": "https://blitzjs.com/", "homepage": "https://blitzjs.com/",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -29,8 +29,9 @@
"eslint.js" "eslint.js"
], ],
"dependencies": { "dependencies": {
"@blitzjs/rpc": "2.0.2", "@blitzjs/rpc": "2.1.2",
"@types/hoist-non-react-statics": "3.3.1", "@types/hoist-non-react-statics": "3.3.1",
"copy-webpack-plugin": "11.0.0",
"debug": "4.3.3", "debug": "4.3.3",
"fs-extra": "10.0.1", "fs-extra": "10.0.1",
"hoist-non-react-statics": "3.3.2", "hoist-non-react-statics": "3.3.2",
@@ -38,13 +39,13 @@
"supports-color": "8.1.1" "supports-color": "8.1.1"
}, },
"peerDependencies": { "peerDependencies": {
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "*", "next": "*",
"react": "*", "react": "*",
"tslog": "4.9.0" "tslog": "4.9.0"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@testing-library/dom": "8.13.0", "@testing-library/dom": "8.13.0",
"@testing-library/jest-dom": "5.16.3", "@testing-library/jest-dom": "5.16.3",
"@testing-library/react": "13.4.0", "@testing-library/react": "13.4.0",
@@ -55,10 +56,10 @@
"@types/react": "18.0.25", "@types/react": "18.0.25",
"@types/react-dom": "17.0.14", "@types/react-dom": "17.0.14",
"@types/testing-library__react-hooks": "4.0.0", "@types/testing-library__react-hooks": "4.0.0",
"blitz": "2.0.2", "blitz": "2.1.2",
"cross-spawn": "7.0.3", "cross-spawn": "7.0.3",
"find-up": "4.1.0", "find-up": "4.1.0",
"next": "14.0.4", "next": "14.3.0-canary.28",
"next-router-mock": "0.9.1", "next-router-mock": "0.9.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

View File

@@ -170,6 +170,7 @@ function codegen() {
const defaultIndexJsPath = path.join(dotBlitzDir, "index.js") const defaultIndexJsPath = path.join(dotBlitzDir, "index.js")
const defaultIndexBrowserJSPath = path.join(dotBlitzDir, "index-browser.js") const defaultIndexBrowserJSPath = path.join(dotBlitzDir, "index-browser.js")
const defaultIndexDTSPath = path.join(dotBlitzDir, "index.d.ts") const defaultIndexDTSPath = path.join(dotBlitzDir, "index.d.ts")
const emptyJSPath = path.join(dotBlitzDir, "turbopack-empty.js")
if (!fs.existsSync(defaultIndexJsPath)) { if (!fs.existsSync(defaultIndexJsPath)) {
await copyFile(path.join(__dirname, "default-index.js"), defaultIndexJsPath) await copyFile(path.join(__dirname, "default-index.js"), defaultIndexJsPath)
@@ -181,6 +182,10 @@ function codegen() {
if (!fs.existsSync(defaultIndexDTSPath)) { if (!fs.existsSync(defaultIndexDTSPath)) {
await copyFile(path.join(__dirname, "default-index.d.ts"), defaultIndexDTSPath) await copyFile(path.join(__dirname, "default-index.d.ts"), defaultIndexDTSPath)
} }
if (!fs.existsSync(emptyJSPath)) {
await copyFile(path.join(__dirname, "turbopack-empty.js"), emptyJSPath)
}
} catch (e) { } catch (e) {
console.error(e) console.error(e)
} }

View File

@@ -0,0 +1 @@
export {}

View File

@@ -22,11 +22,13 @@ import {
RouteUrlObject, RouteUrlObject,
startWatcher, startWatcher,
stopWatcher, stopWatcher,
RequestMiddleware,
} from "blitz" } from "blitz"
import { import {
getInfiniteQueryKey, getInfiniteQueryKey,
getQueryKey, getQueryKey,
installWebpackConfig, installWebpackConfig,
installTurboConfig,
InstallWebpackConfigOptions, InstallWebpackConfigOptions,
ResolverPathOptions, ResolverPathOptions,
DefaultOptions, DefaultOptions,
@@ -38,6 +40,9 @@ import {withSuperJsonProps} from "./superjson"
import {ParsedUrlQuery} from "querystring" import {ParsedUrlQuery} from "querystring"
import {PreviewData} from "next/types" import {PreviewData} from "next/types"
import {resolveHref} from "next/dist/client/resolve-href" import {resolveHref} from "next/dist/client/resolve-href"
import fs from "fs"
import path from "path"
import CopyPlugin from "copy-webpack-plugin"
export * from "./index-browser" export * from "./index-browser"
@@ -238,6 +243,7 @@ export interface BlitzConfig extends NextConfig {
} }
} }
export function withBlitz(nextConfig: BlitzConfig = {}): NextConfig { export function withBlitz(nextConfig: BlitzConfig = {}): NextConfig {
if ( if (
process.env.NODE_ENV !== "production" && process.env.NODE_ENV !== "production" &&
@@ -268,6 +274,31 @@ export function withBlitz(nextConfig: BlitzConfig = {}): NextConfig {
}, },
}) })
if (!process.env.TURBOPACK) {
try {
const sodiumNativePath = fs.realpathSync(
path.join(require.resolve("sodium-native"), ".."),
)
const dotNextDirectory = `${process.cwd()}/.next`
config.plugins.push(
new CopyPlugin({
patterns: [
{
//dev
from: `${sodiumNativePath}/prebuilds/`,
to: `${dotNextDirectory}/server/vendor-chunks/prebuilds/`,
},
{
//prod
from: `${sodiumNativePath}/prebuilds/`,
to: `${dotNextDirectory}/server/chunks/prebuilds/`,
},
],
}),
)
} catch {}
}
if (typeof nextConfig.webpack === "function") { if (typeof nextConfig.webpack === "function") {
return nextConfig.webpack(config, options) return nextConfig.webpack(config, options)
} }
@@ -275,6 +306,26 @@ export function withBlitz(nextConfig: BlitzConfig = {}): NextConfig {
}, },
}) })
if (process.env.TURBOPACK) {
const blitzTurboConfig = installTurboConfig()
config.experimental = {
...config.experimental,
turbo: {
loaders: {
...config.experimental?.turbo?.loaders,
},
resolveAlias: {
...config.experimental?.turbo?.resolveAlias,
...blitzTurboConfig.resolveAlias,
},
rules: {
...config.experimental?.turbo?.rules,
...blitzTurboConfig.rules,
},
},
}
}
const {blitz, ...rest} = config const {blitz, ...rest} = config
return rest return rest
} }
@@ -323,3 +374,15 @@ declare module "blitz" {
prefetchInfiniteQuery: PrefetchQueryFn prefetchInfiniteQuery: PrefetchQueryFn
} }
} }
export const BlitzServerMiddleware = <
TMiddleware extends RequestMiddleware<NextApiRequest, BlitzNextApiResponse> = RequestMiddleware<
NextApiRequest,
BlitzNextApiResponse
>,
>(
middleware: TMiddleware,
): BlitzServerPlugin<{}> => ({
requestMiddlewares: [middleware],
exports: () => ({}),
})

View File

@@ -1,5 +1,140 @@
# @blitzjs/rpc # @blitzjs/rpc
## 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
- Updated dependencies [318e9740d]
- blitz@2.0.10
## 2.0.9
### Patch Changes
- 5a14306f7: fix export `enhancePrisma` in client
- Updated dependencies [5a14306f7]
- blitz@2.0.9
## 2.0.8
### Patch Changes
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- blitz@2.0.8
## 2.0.7
### Patch Changes
- ee7bf87ec: Turbopack support for Blitz
This PR includes the changes required to make the Blitz loaders work with Turbopack.
Usage:
```bash
pnpm blitz dev --turbo
```
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
## 2.0.6
### Patch Changes
- Updated dependencies [76a2544f9]
- blitz@2.0.6
## 2.0.5
### Patch Changes
- Updated dependencies [6f54841b7]
- blitz@2.0.5
## 2.0.4
### Patch Changes
- Updated dependencies [dd604c767]
- Updated dependencies [28a79040e]
- blitz@2.0.4
## 2.0.3
### Patch Changes
- 2f5c8a3a0: Address missing sodium native prebuilds required to use secure-password during server rendering
- Updated dependencies [2f5c8a3a0]
- blitz@2.0.3
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@blitzjs/rpc", "name": "@blitzjs/rpc",
"version": "2.0.2", "version": "2.1.2",
"homepage": "https://blitzjs.com/", "homepage": "https://blitzjs.com/",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -37,19 +37,19 @@
}, },
"peerDependencies": { "peerDependencies": {
"@tanstack/query-core": "4.24.4", "@tanstack/query-core": "4.24.4",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "*", "next": "*",
"react": "*" "react": "*"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/auth": "2.0.2", "@blitzjs/auth": "2.1.2",
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@tanstack/query-core": "4.24.4", "@tanstack/query-core": "4.24.4",
"@types/debug": "4.1.7", "@types/debug": "4.1.7",
"@types/react": "18.0.25", "@types/react": "18.0.25",
"@types/react-dom": "17.0.14", "@types/react-dom": "17.0.14",
"blitz": "2.0.2", "blitz": "2.1.2",
"next": "14.0.4", "next": "14.3.0-canary.28",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"typescript": "^4.8.4", "typescript": "^4.8.4",

View File

@@ -1,4 +1,4 @@
import {assert, Ctx, ResolverConfig} from "blitz" import {assert, Ctx, ResolverConfig, registerBlitzErrorClasses} from "blitz"
import {NextApiRequest, NextApiResponse} from "next" import {NextApiRequest, NextApiResponse} from "next"
import {resolve} from "path" import {resolve} from "path"
import {deserialize, parse, serialize as superjsonSerialize} from "superjson" import {deserialize, parse, serialize as superjsonSerialize} from "superjson"
@@ -119,6 +119,7 @@ export interface InstallWebpackConfigOptions {
[key: string]: boolean | string [key: string]: boolean | string
} }
} }
plugins: any[]
module: { module: {
rules: WebpackRule[] rules: WebpackRule[]
} }
@@ -156,6 +157,51 @@ export function installWebpackConfig({
}) })
} }
export function installTurboConfig() {
return {
resolveAlias: {
"cross-spawn": {browser: ".blitz/turbopack-empty.js"},
"npm-which": {browser: ".blitz/turbopack-empty.js"},
fs: {browser: ".blitz/turbopack-empty.js"},
child_process: {browser: ".blitz/turbopack-empty.js"},
},
rules: {
"**/*...blitz*.{jsx,tsx,js,ts}": {
default: {
loaders: [{loader: loaderServer, options: {}}],
as: "*.ts",
},
},
"**/*...blitz*/route.{jsx,tsx,js,ts}": {
default: {
loaders: [{loader: loaderServer, options: {}}],
as: "*.ts",
},
},
"**/{queries,mutations}/**": {
browser: {
loaders: [
{
loader: loaderClient,
options: {},
},
],
as: "*.ts",
},
default: {
loaders: [
{
loader: loaderServerResolvers,
options: {},
},
],
as: "*.ts",
},
},
},
}
}
// ---------- // ----------
// END LOADER // END LOADER
// ---------- // ----------
@@ -187,12 +233,13 @@ async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
} }
interface RpcConfig { interface RpcConfig {
onError?: (error: Error, ctx: Ctx) => void onError?: (error: Error, ctx?: Ctx) => void
formatError?: (error: Error, ctx: Ctx) => Error formatError?: (error: Error, ctx?: Ctx) => Error
logging?: RpcLoggerOptions logging?: RpcLoggerOptions
} }
export function rpcHandler(config: RpcConfig) { export function rpcHandler(config?: RpcConfig) {
registerBlitzErrorClasses()
return async function handleRpcRequest(req: NextApiRequest, res: NextApiResponse, ctx: Ctx) { return async function handleRpcRequest(req: NextApiRequest, res: NextApiResponse, ctx: Ctx) {
const resolverMap = await getResolverMap() const resolverMap = await getResolverMap()
assert(resolverMap, "No query or mutation resolvers found") assert(resolverMap, "No query or mutation resolvers found")
@@ -204,7 +251,7 @@ export function rpcHandler(config: RpcConfig) {
const relativeRoutePath = (req.query.blitz as string[])?.join("/") const relativeRoutePath = (req.query.blitz as string[])?.join("/")
const routePath = "/" + relativeRoutePath const routePath = "/" + relativeRoutePath
const resolverName = routePath.replace(/(\/api\/rpc)?\//, "") 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 const loadableResolver = resolverMap?.[routePath]?.resolver
if (!loadableResolver) { if (!loadableResolver) {
@@ -263,6 +310,7 @@ export function rpcHandler(config: RpcConfig) {
rpcLogger.timer.initNextJsSerialization() rpcLogger.timer.initNextJsSerialization()
;(res as any).blitzResult = result ;(res as any).blitzResult = result
ctx?.session?.setSession(res)
res.json({ res.json({
result: serializedResult.json, result: serializedResult.json,
error: null, error: null,
@@ -282,16 +330,18 @@ export function rpcHandler(config: RpcConfig) {
error.stack = "" error.stack = ""
} }
config.onError?.(error, ctx) config?.onError?.(error, ctx)
rpcLogger.error(error) rpcLogger.error(error)
if (!error.statusCode) { if (!error.statusCode) {
error.statusCode = 500 error.statusCode = 500
} }
const formattedError = config.formatError?.(error, ctx) ?? error const formattedError = config?.formatError?.(error, ctx) ?? error
const serializedError = superjsonSerialize(formattedError) const serializedError = superjsonSerialize(formattedError)
ctx?.session?.setSession(res)
res.json({ res.json({
result: null, result: null,
error: serializedError.json, error: serializedError.json,
@@ -309,3 +359,129 @@ export function rpcHandler(config: RpcConfig) {
} }
} }
} }
type Params = Record<string, unknown>
export function rpcAppHandler(config?: RpcConfig) {
registerBlitzErrorClasses()
async function handleRpcRequest(req: Request, context: {params: Params}, ctx?: Ctx) {
const session = ctx?.session
const resolverMap = await getResolverMap()
assert(resolverMap, "No query or mutation resolvers found")
assert(
Array.isArray(context.params.blitz),
"It seems your Blitz RPC endpoint file is not named [[...blitz]].(jt)s. Please ensure it is",
)
const relativeRoutePath = (context.params.blitz as string[])?.join("/")
const 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
: context.params.params
? parse(`${context.params.params}`)
: undefined,
meta:
req.method === "POST"
? body.meta?.params
: context.params.meta
? parse(`${context.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

@@ -14,12 +14,24 @@ import {getResolverConfig} from "../../parsers/parse-rpc-config"
// Subset of `import type { LoaderDefinitionFunction } from 'webpack'` // Subset of `import type { LoaderDefinitionFunction } from 'webpack'`
export async function loader(this: Loader, input: string): Promise<string> { export async function loader(this: Loader, input: string): Promise<string> {
const compiler = this._compiler!
const id = this.resource const id = this.resource
const root = this._compiler!.context const root = this.rootContext
const isSSR = compiler.name === "server" // Webpack has `_compiler` property. Turbopack does not.
if (!isSSR) { const webpackCompilerName = this._compiler?.name
if (webpackCompilerName) {
const isSSR = webpackCompilerName === "server"
if (!isSSR) {
return await transformBlitzRpcResolverClient(
input,
toPosixPath(id),
toPosixPath(root),
this.query,
)
}
// Handle Turbopack / other bundlers case.
// The decision of which environment to run the loader in is decided by the loader configuration instead.
} else {
return await transformBlitzRpcResolverClient( return await transformBlitzRpcResolverClient(
input, input,
toPosixPath(id), toPosixPath(id),

View File

@@ -13,12 +13,24 @@ import {posix} from "path"
// Subset of `import type { LoaderDefinitionFunction } from 'webpack'` // Subset of `import type { LoaderDefinitionFunction } from 'webpack'`
export async function loader(this: Loader, input: string): Promise<string> { export async function loader(this: Loader, input: string): Promise<string> {
const compiler = this._compiler!
const id = this.resource const id = this.resource
const root = this._compiler!.context const root = this.rootContext
const isSSR = compiler.name === "server" // Webpack has `_compiler` property. Turbopack does not.
if (isSSR) { const webpackCompilerName = this._compiler?.name
if (webpackCompilerName) {
const isSSR = webpackCompilerName === "server"
if (isSSR) {
return await transformBlitzRpcResolverServer(
input,
toPosixPath(id),
toPosixPath(root),
this.query,
)
}
// Handle Turbopack / other bundlers case.
// The decision of which environment to run the loader in is decided by the loader configuration instead.
} else {
return await transformBlitzRpcResolverServer( return await transformBlitzRpcResolverServer(
input, input,
toPosixPath(id), toPosixPath(id),

View File

@@ -1,4 +1,4 @@
import {join} from "path" import {join, relative} from "path"
import {promises} from "fs" import {promises} from "fs"
import { import {
assertPosixPath, assertPosixPath,
@@ -14,17 +14,37 @@ import {
// Subset of `import type { LoaderDefinitionFunction } from 'webpack'` // Subset of `import type { LoaderDefinitionFunction } from 'webpack'`
export async function loader(this: Loader, input: string): Promise<string> { export async function loader(this: Loader, input: string): Promise<string> {
const compiler = this._compiler!
const id = this.resource const id = this.resource
const root = this._compiler!.context const root = this.rootContext
const rpcFolders = this.query.includeRPCFolders ? this.query.includeRPCFolders : [] const rpcFolders = this.query.includeRPCFolders ? this.query.includeRPCFolders : []
const isSSR = compiler.name === "server" // Webpack has `_compiler` property. Turbopack does not.
if (isSSR) { const webpackCompilerName = this._compiler?.name
if (webpackCompilerName) {
const isSSR = webpackCompilerName === "server"
if (isSSR) {
this.cacheable(false)
const resolvers = await collectResolvers(root, rpcFolders, ["ts", "js", "tsx", "jsx"])
return await transformBlitzRpcServer(
this.context,
input,
toPosixPath(id),
toPosixPath(root),
resolvers,
this.query,
)
}
// Handle Turbopack / other bundlers case.
// The decision of which environment to run the loader in is decided by the loader configuration instead.
} else {
this.cacheable(false) this.cacheable(false)
const resolvers = await collectResolvers(root, rpcFolders, ["ts", "js", "tsx", "jsx"]) const resolvers = await collectResolvers(root, rpcFolders, ["ts", "js", "tsx", "jsx"])
return await transformBlitzRpcServer( return await transformBlitzRpcServer(
this.context,
input, input,
toPosixPath(id), toPosixPath(id),
toPosixPath(root), toPosixPath(root),
@@ -43,6 +63,7 @@ function slash(str: string) {
} }
export async function transformBlitzRpcServer( export async function transformBlitzRpcServer(
context: string,
src: string, src: string,
id: string, id: string,
root: string, root: string,
@@ -68,7 +89,9 @@ export async function transformBlitzRpcServer(
code += `__internal_addBlitzRpcResolver('${routePath}','${slash( code += `__internal_addBlitzRpcResolver('${routePath}','${slash(
resolverFilePath, resolverFilePath,
)}',() => ${importStrategy}('${slash(resolverFilePath)}'));` )}',() => ${importStrategy}('${slash(
relative(context, resolverFilePath).replace(/\\/g, "/"),
)}'))`
code += "\n" code += "\n"
} }

View File

@@ -9,10 +9,11 @@ export interface LoaderOptions {
} }
export interface Loader { export interface Loader {
_compiler?: { _compiler: {
name: string name: string
context: string
} }
rootContext: string
context: string
resource: string resource: string
cacheable: (enabled: boolean) => void cacheable: (enabled: boolean) => void
query: LoaderOptions query: LoaderOptions

View File

@@ -1,5 +1,141 @@
# blitz # blitz
## 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
- 318e9740d: feat: support next-auth version 4.24.7
- @blitzjs/generator@2.0.10
## 2.0.9
### Patch Changes
- 5a14306f7: fix export `enhancePrisma` in client
- @blitzjs/generator@2.0.9
## 2.0.8
### Patch Changes
- 5e61a1681: bug: merge existing and custom blitz turbo configs
- 77555468f: fix: add missing host while initialising the next-auth adapter
- @blitzjs/generator@2.0.8
## 2.0.7
### Patch Changes
- ee7bf87ec: Turbopack support for Blitz
This PR includes the changes required to make the Blitz loaders work with Turbopack.
Usage:
```bash
pnpm blitz dev --turbo
```
- 178c152b2: fix: patch next.js to hide intentional throws of `DYNAMIC_SERVER_USAGE`
- @blitzjs/generator@2.0.7
## 2.0.6
### Patch Changes
- 76a2544f9: Use `SIGINT` signal instead of `SIGABRT` to stop the process, when using custom server for better compatibility with operative systems
- @blitzjs/generator@2.0.6
## 2.0.5
### Patch Changes
- 6f54841b7: fix: getBlitzContext() can only be used in React Server Components in Nextjs 13 or higher
- @blitzjs/generator@2.0.5
## 2.0.4
### Patch Changes
- dd604c767: perf: add filter to select only non expired sessions
- 28a79040e: feat: export `BlitzServerMiddleware` from blitz-next with stronger types
- Updated dependencies [71b995003]
- @blitzjs/generator@2.0.4
## 2.0.3
### Patch Changes
- 2f5c8a3a0: Address missing sodium native prebuilds required to use secure-password during server rendering
- Updated dependencies [47722e045]
- Updated dependencies [595f400e9]
- @blitzjs/generator@2.0.3
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "blitz", "name": "blitz",
"version": "2.0.2", "version": "2.1.2",
"homepage": "https://blitzjs.com/", "homepage": "https://blitzjs.com/",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -30,7 +30,7 @@
"blitz": "bin/blitz" "blitz": "bin/blitz"
}, },
"dependencies": { "dependencies": {
"@blitzjs/generator": "2.0.2", "@blitzjs/generator": "2.1.2",
"@mrleebo/prisma-ast": "0.2.6", "@mrleebo/prisma-ast": "0.2.6",
"@types/global-agent": "2.1.1", "@types/global-agent": "2.1.1",
"arg": "5.0.1", "arg": "5.0.1",
@@ -80,7 +80,7 @@
"watchpack": "2.1.1" "watchpack": "2.1.1"
}, },
"devDependencies": { "devDependencies": {
"@blitzjs/config": "2.0.2", "@blitzjs/config": "2.1.2",
"@types/cookie": "0.4.1", "@types/cookie": "0.4.1",
"@types/cross-spawn": "6.0.2", "@types/cross-spawn": "6.0.2",
"@types/debug": "4.1.7", "@types/debug": "4.1.7",

View File

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

View File

@@ -16,7 +16,14 @@ export const codegenTasks = async () => {
*/ */
const nextDir = await resolveCwd("next") const nextDir = await resolveCwd("next")
const nextClientIndex = join(nextDir, "../..", "client", "index.js") const nextClientIndex = join(nextDir, "../..", "client", "index.js")
const nextClientOnRecoverableErrorIndex = join(
nextDir,
"../..",
"client",
"on-recoverable-error.js",
)
const readFile = await fs.readFile(nextClientIndex) const readFile = await fs.readFile(nextClientIndex)
const readOnRecoverableErrorFile = await fs.readFile(nextClientOnRecoverableErrorIndex)
const packageJson = await getPackageJson() const packageJson = await getPackageJson()
const version = packageJson.dependencies.next const version = packageJson.dependencies.next
const nextVersion = semver.clean(version, {loose: true}) || semver.valid(semver.coerce(version)) const nextVersion = semver.clean(version, {loose: true}) || semver.valid(semver.coerce(version))
@@ -48,14 +55,16 @@ export const codegenTasks = async () => {
) )
await fs.writeFile(nextClientIndex, updatedFile) await fs.writeFile(nextClientIndex, updatedFile)
log.success("Next.js was successfully patched with a React Suspense fix") log.success("Next.js was successfully patched with a React Suspense fix")
} else if (nextVersion && semver.satisfies(nextVersion, ">=13.3.1")) { } else {
const updatedFile = readFile const updatedFile = readOnRecoverableErrorFile.toString().replace(
.toString() /defaultOnRecoverableError\(err\);/gm,
.replace( ` if (err.toString().includes("DYNAMIC_SERVER_USAGE") || err.toString().includes("could not finish this Suspense boundary") || err.toString().includes("Minified React error #419")) {
/_onrecoverableerror\.default$/gm, return;
`(err) => (err.toString().includes("DYNAMIC_SERVER_USAGE") || err.toString().includes("could not finish this Suspense boundary") || err.toString().includes("Minified React error #419")) ? null : _onrecoverableerror.default(err)`, } else {
) defaultOnRecoverableError(err)
await fs.writeFile(nextClientIndex, updatedFile) }`,
)
await fs.writeFile(nextClientOnRecoverableErrorIndex, updatedFile)
log.success("Next.js was successfully patched with a React Suspense fix") log.success("Next.js was successfully patched with a React Suspense fix")
} }
} catch (err) { } catch (err) {

View File

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

View File

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

View File

@@ -2,12 +2,13 @@ import {ChildProcess} from "child_process"
import {spawn} from "cross-spawn" import {spawn} from "cross-spawn"
import detect from "detect-port" import detect from "detect-port"
import path from "path" import path from "path"
import {existsSync, readJSONSync} from "fs-extra" import {copySync, existsSync, readJSONSync} from "fs-extra"
import * as esbuild from "esbuild" import * as esbuild from "esbuild"
import pkgDir from "pkg-dir" import pkgDir from "pkg-dir"
import type {ServerConfig} from "./config" import type {ServerConfig} from "./config"
const debug = require("debug")("blitz:utils") import Debug from "debug"
const debug = Debug("blitz:utils")
export function getProjectRootSync() { export function getProjectRootSync() {
return process.cwd() return process.cwd()
@@ -127,7 +128,7 @@ export function startCustomServer(
console.log("\n") console.log("\n")
//@ts-ignore -- incorrect TS type from node //@ts-ignore -- incorrect TS type from node
process.exitCode = RESTART_CODE process.exitCode = RESTART_CODE
process.kill("SIGABRT") process.kill("SIGINT")
} }
}, },
} }
@@ -151,22 +152,12 @@ function getSpawnEnv(config: ServerConfig) {
return spawnEnv return spawnEnv
} }
async function createCommandAndPort(config: ServerConfig, command: string) { async function detectAvailablePort(config: ServerConfig) {
let spawnCommand: string[] = [command] return await detect({port: config.port ? config.port : 3000})
let availablePort: number
availablePort = await detect({port: config.port ? config.port : 3000})
spawnCommand = spawnCommand.concat(["-p", `${availablePort}`])
if (config.hostname) {
spawnCommand = spawnCommand.concat(["-H", `${config.hostname}`])
}
const spawnEnv = getSpawnEnv(config)
return {spawnCommand, spawnEnv, availablePort}
} }
const frameworkCommands = ["blitz", "next"]
export async function nextStartDev( export async function nextStartDev(
nextBin: string, nextBin: string,
cwd: string, cwd: string,
@@ -174,12 +165,11 @@ export async function nextStartDev(
_buildFolder: string, _buildFolder: string,
config: ServerConfig, config: ServerConfig,
) { ) {
const {spawnCommand, spawnEnv, availablePort} = await createCommandAndPort(config, "dev") const spawnEnv = getSpawnEnv(config)
const availablePort = await detectAvailablePort(config)
process.env.BLITZ_DEV_SERVER_ORIGIN = `http://localhost:${availablePort}` process.env.BLITZ_DEV_SERVER_ORIGIN = `http://localhost:${availablePort}`
debug("cwd ", cwd) const spawnCommand = process.argv.slice(2).filter((arg) => !frameworkCommands.includes(arg))
debug("spawn ", nextBin, spawnCommand)
return new Promise<void>((res, rej) => { return new Promise<void>((res, rej) => {
spawn(nextBin, spawnCommand, { spawn(nextBin, spawnCommand, {
@@ -248,7 +238,8 @@ export function nextExport(nextBin: string, config: ServerConfig) {
} }
export async function nextStart(nextBin: string, _buildFolder: string, config: ServerConfig) { export async function nextStart(nextBin: string, _buildFolder: string, config: ServerConfig) {
const {spawnCommand, spawnEnv} = await createCommandAndPort(config, "start") const spawnEnv = getSpawnEnv(config)
const spawnCommand = process.argv.slice(2).filter((arg) => !frameworkCommands.includes(arg))
return new Promise<void>((res, rej) => { return new Promise<void>((res, rej) => {
spawn(nextBin, spawnCommand, { spawn(nextBin, spawnCommand, {

View File

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

View File

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

View File

@@ -8,6 +8,7 @@ import {
PaginationArgumentError, PaginationArgumentError,
RedirectError, RedirectError,
OAuthError, OAuthError,
registerBlitzErrorClasses,
} from "./errors" } from "./errors"
import type {EventHooks, MiddlewareHooks} from "./types" import type {EventHooks, MiddlewareHooks} from "./types"
export { export {
@@ -18,7 +19,9 @@ export {
PaginationArgumentError, PaginationArgumentError,
RedirectError, RedirectError,
OAuthError, OAuthError,
registerBlitzErrorClasses,
} }
export * from "./utils/enhance-prisma"
export type BlitzProviderComponentType = <TProps = any>( export type BlitzProviderComponentType = <TProps = any>(
component: ComponentType<TProps>, component: ComponentType<TProps>,
@@ -84,6 +87,5 @@ if (typeof window !== "undefined" && process.env.NODE_ENV === "development") {
export * from "./utils" export * from "./utils"
export * from "./types" export * from "./types"
export * from "./utils/enhance-prisma"
export * from "./utils/zod" export * from "./utils/zod"
export {reduceBlitzClientPlugins} from "./plugin" export {reduceBlitzClientPlugins} from "./plugin"

View File

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

View File

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

View File

@@ -1,5 +1,103 @@
# @blitzjs/codemod # @blitzjs/codemod
## 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
- Updated dependencies [318e9740d]
- blitz@2.0.10
- @blitzjs/generator@2.0.10
## 2.0.9
### Patch Changes
- Updated dependencies [5a14306f7]
- blitz@2.0.9
- @blitzjs/generator@2.0.9
## 2.0.8
### Patch Changes
- Updated dependencies [5e61a1681]
- Updated dependencies [77555468f]
- blitz@2.0.8
- @blitzjs/generator@2.0.8
## 2.0.7
### Patch Changes
- Updated dependencies [ee7bf87ec]
- Updated dependencies [178c152b2]
- blitz@2.0.7
- @blitzjs/generator@2.0.7
## 2.0.6
### Patch Changes
- Updated dependencies [76a2544f9]
- blitz@2.0.6
- @blitzjs/generator@2.0.6
## 2.0.5
### Patch Changes
- Updated dependencies [6f54841b7]
- blitz@2.0.5
- @blitzjs/generator@2.0.5
## 2.0.4
### Patch Changes
- f25aac08c: Added support to codemod upgrade-legacy for projects that have their pages folder nested in a src/ folder
- Updated dependencies [dd604c767]
- Updated dependencies [71b995003]
- Updated dependencies [28a79040e]
- blitz@2.0.4
- @blitzjs/generator@2.0.4
## 2.0.3
### Patch Changes
- 956a739e8: codemod: provide correct path to new template paths
- Updated dependencies [47722e045]
- Updated dependencies [2f5c8a3a0]
- Updated dependencies [595f400e9]
- @blitzjs/generator@2.0.3
- blitz@2.0.3
## 2.0.2 ## 2.0.2
### Patch Changes ### Patch Changes

View File

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

View File

@@ -32,6 +32,12 @@ class ExpectedError extends Error {
} }
} }
const findPagesDirectory = () => {
const srcPagesDir = path.join("src", "pages")
const pagesDir = path.resolve("pages")
return fs.existsSync(srcPagesDir) ? path.resolve(srcPagesDir) : pagesDir
}
const isInternalBlitzMonorepoDevelopment = fs.existsSync( const isInternalBlitzMonorepoDevelopment = fs.existsSync(
path.join(__dirname, "../../../blitz-next"), path.join(__dirname, "../../../blitz-next"),
) )
@@ -46,7 +52,22 @@ const upgradeLegacy = async () => {
if (blitzConfigFile === "") { if (blitzConfigFile === "") {
throw new ExpectedError("Could not identify Legacy Blitz Config file") throw new ExpectedError("Could not identify Legacy Blitz Config file")
} }
const appDir = path.resolve("app") // Check if app directory exists in either app/ or src/app
const appDir = fs.existsSync(path.resolve("app"))
? path.resolve("app")
: fs.existsSync(path.resolve("src"))
? path.resolve(path.join("src", "app"))
: ""
try {
// Throw Error if appDir empty
if (appDir === "") {
throw new ExpectedError(
"Could not identify Legacy Blitz App directory in project (no app/ or src/ directory found)",
)
}
} catch (e) {
console.error(e)
}
let failedAt = let failedAt =
fs.existsSync(path.resolve(".migration.json")) && fs.readJSONSync("./.migration.json").failedAt fs.existsSync(path.resolve(".migration.json")) && fs.readJSONSync("./.migration.json").failedAt
let collectedErrors: {message: string; step: number}[] = [] let collectedErrors: {message: string; step: number}[] = []
@@ -536,10 +557,10 @@ const upgradeLegacy = async () => {
isInternalBlitzMonorepoDevelopment ? "templates" : "dist/templates", isInternalBlitzMonorepoDevelopment ? "templates" : "dist/templates",
) )
const blitzServer = fs const blitzServer = fs
.readFileSync(path.join(templatePath, "app", "app", "blitz-server.ts")) .readFileSync(path.join(templatePath, "app", "src", "app", "blitz-server.ts"))
.toString() .toString()
const blitzClient = fs const blitzClient = fs
.readFileSync(path.join(templatePath, "app", "app", "blitz-client.ts")) .readFileSync(path.join(templatePath, "app", "src", "app", "blitz-client.ts"))
.toString() .toString()
const replaceTemplateValues = (input: string) => { const replaceTemplateValues = (input: string) => {
@@ -584,7 +605,9 @@ const upgradeLegacy = async () => {
j.Identifier, j.Identifier,
(node) => node.name === "cookiePrefix", (node) => node.name === "cookiePrefix",
) )
cookieIdentifierBlitzClient.get().parentPath.value.value.value = cookiePrefix if (cookieIdentifierBlitzClient.length) {
cookieIdentifierBlitzClient.get().parentPath.value.value.value = cookiePrefix
}
fs.writeFileSync( fs.writeFileSync(
`${appDir}/blitz-client.${isTypescript ? "ts" : "js"}`, `${appDir}/blitz-client.${isTypescript ? "ts" : "js"}`,
@@ -676,7 +699,7 @@ const upgradeLegacy = async () => {
steps.push({ steps.push({
name: "create pages/api/rpc directory and add [[...blitz]].ts wildecard API route", name: "create pages/api/rpc directory and add [[...blitz]].ts wildecard API route",
action: async () => { action: async () => {
const pagesDir = path.resolve("pages/api/rpc") const pagesDir = path.resolve(`${findPagesDirectory()}/api/rpc`)
const templatePath = path.join( const templatePath = path.join(
require.resolve("@blitzjs/generator"), require.resolve("@blitzjs/generator"),
"..", "..",
@@ -684,7 +707,9 @@ const upgradeLegacy = async () => {
isInternalBlitzMonorepoDevelopment ? "templates" : "dist/templates", isInternalBlitzMonorepoDevelopment ? "templates" : "dist/templates",
) )
const rpcRoute = fs const rpcRoute = fs
.readFileSync(path.join(templatePath, "app", "pages", "api", "rpc", "blitzrpcroute.ts")) .readFileSync(
path.join(templatePath, "app", "src", "pages", "api", "rpc", "blitzrpcroute.ts"),
)
.toString() .toString()
if (!fs.existsSync(pagesDir)) { if (!fs.existsSync(pagesDir)) {
@@ -925,7 +950,7 @@ const upgradeLegacy = async () => {
name: "convert useRouterQuery to useRouter", name: "convert useRouterQuery to useRouter",
action: async () => { action: async () => {
//First check ./pages //First check ./pages
const pagesDir = path.resolve("pages") const pagesDir = findPagesDirectory()
getAllFiles(pagesDir, [], [], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => { getAllFiles(pagesDir, [], [], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => {
try { try {
const filepath = path.resolve(pagesDir, file) const filepath = path.resolve(pagesDir, file)
@@ -1057,7 +1082,7 @@ const upgradeLegacy = async () => {
steps.push({ steps.push({
name: "wrap App component with withBlitz HOC", name: "wrap App component with withBlitz HOC",
action: async () => { action: async () => {
const pagesDir = path.resolve("pages") const pagesDir = findPagesDirectory()
const program = getCollectionFromSource( const program = getCollectionFromSource(
path.join(pagesDir, `_app.${isTypescript ? "tsx" : "jsx"}`), path.join(pagesDir, `_app.${isTypescript ? "tsx" : "jsx"}`),
@@ -1107,7 +1132,7 @@ const upgradeLegacy = async () => {
steps.push({ steps.push({
name: "update imports in the _document file", name: "update imports in the _document file",
action: async () => { action: async () => {
const pagesDir = path.resolve("pages") const pagesDir = findPagesDirectory()
if (fs.existsSync(path.join(pagesDir, `_document.${isTypescript ? "tsx" : "jsx"}`))) { if (fs.existsSync(path.join(pagesDir, `_document.${isTypescript ? "tsx" : "jsx"}`))) {
const program = getCollectionFromSource( const program = getCollectionFromSource(
@@ -1187,7 +1212,7 @@ const upgradeLegacy = async () => {
steps.push({ steps.push({
name: "wrap getServerSideProps, getStaticProps and API handlers with gSSP, gSP, and api", name: "wrap getServerSideProps, getStaticProps and API handlers with gSSP, gSP, and api",
action: async () => { action: async () => {
const pagesDir = path.resolve("pages") const pagesDir = findPagesDirectory()
getAllFiles(pagesDir, [], ["api"], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => { getAllFiles(pagesDir, [], ["api"], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => {
try { try {
const program = getCollectionFromSource(file) const program = getCollectionFromSource(file)
@@ -1310,7 +1335,9 @@ const upgradeLegacy = async () => {
steps.push({ steps.push({
name: "check for usages of invokeWithMiddleware", name: "check for usages of invokeWithMiddleware",
action: async () => { action: async () => {
getAllFiles(path.resolve("pages"), [], [], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => { const srcPagesDir = path.resolve(path.join("src/pages"))
const pagesDir = fs.existsSync(srcPagesDir) ? srcPagesDir : path.resolve("pages")
getAllFiles(pagesDir, [], [], [".ts", ".tsx", ".js", ".jsx"]).forEach((file) => {
const program = getCollectionFromSource(file) const program = getCollectionFromSource(file)
try { try {
const invokeWithMiddlewarePath = findCallExpression(program, "invokeWithMiddleware") const invokeWithMiddlewarePath = findCallExpression(program, "invokeWithMiddleware")

View File

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

View File

@@ -1,5 +1,27 @@
# @blitzjs/config # @blitzjs/config
## 2.1.2
## 2.1.1
## 2.1.0
## 2.0.10
## 2.0.9
## 2.0.8
## 2.0.7
## 2.0.6
## 2.0.5
## 2.0.4
## 2.0.3
## 2.0.2 ## 2.0.2
## 2.0.1 ## 2.0.1

View File

@@ -1,7 +1,7 @@
{ {
"name": "@blitzjs/config", "name": "@blitzjs/config",
"private": true, "private": true,
"version": "2.0.2", "version": "2.1.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@typescript-eslint/eslint-plugin": "5.42.1", "@typescript-eslint/eslint-plugin": "5.42.1",

View File

@@ -1,5 +1,83 @@
# @blitzjs/generator # @blitzjs/generator
## 2.1.2
### Patch Changes
- 2711291e9: Remove `views` property from `Session.PublicData` in `types.ts` file again
- 56bd32b55: Fix Zod schema generation for datetime fields by removing extra parentheses.
## 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
## 2.0.10
## 2.0.9
## 2.0.8
## 2.0.7
## 2.0.6
## 2.0.5
## 2.0.4
### Patch Changes
- 71b995003: fix: ajv dependency was not installed with blitz new
## 2.0.3
### Patch Changes
- 47722e045: Fix: search inside any subdirectory to inside `src|app` directories to find `blitz-server.ts` to use the `BlitzCliConfig` configurations.
- 595f400e9: fix: add missing `prettier`, `pretty-quick` and other missing dev dependencies in the new app template
## 2.0.2 ## 2.0.2
## 2.0.1 ## 2.0.1

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