Compare commits
1 Commits
@blitzjs/a
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c83167fda |
@@ -1,8 +0,0 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@2.0.0/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [["blitz"], ["@blitzjs/*"]],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": ["web", "test-*"]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
downgrade pkg-dir to non-esm only version
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
"blitz": patch
|
||||
"@blitzjs/auth": patch
|
||||
"@blitzjs/next": patch
|
||||
"@blitzjs/rpc": patch
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
initial publish
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"@blitzjs/generator": patch
|
||||
---
|
||||
|
||||
fix generator npm package dist
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"mode": "pre",
|
||||
"tag": "alpha",
|
||||
"initialVersions": {
|
||||
"web": "0.0.0",
|
||||
"test-auth": "0.0.0",
|
||||
"test-rpc": "0.0.0",
|
||||
"test-utils": "0.0.0",
|
||||
"blitz": "2.0.0-alpha.0",
|
||||
"@blitzjs/auth": "2.0.0-alpha.0",
|
||||
"@blitzjs/next": "2.0.0-alpha.0",
|
||||
"@blitzjs/rpc": "2.0.0-alpha.0",
|
||||
"@blitzjs/config": "0.0.0",
|
||||
"@blitzjs/generator": "2.0.0-alpha.0",
|
||||
"template": "0.0.0"
|
||||
},
|
||||
"changesets": ["nine-onions-admire", "ninety-pets-heal", "poor-peas-lick", "ten-rivers-burn"]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"blitz": patch
|
||||
---
|
||||
|
||||
fix more cli problems
|
||||
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: pnpm/action-setup@646cdf48217256a3d0b80361c5a50727664284f2
|
||||
with:
|
||||
version: 6.32.6
|
||||
version: 6.10.0
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
cache: "pnpm"
|
||||
- run: pnpm install --frozen-lockfile
|
||||
- run: pnpm manypkg check
|
||||
- run: pnpm build
|
||||
- run: pnpm lint
|
||||
- run: pnpm build
|
||||
- run: pnpm build:apps
|
||||
- run: pnpm test
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
pnpm manypkg check
|
||||
pnpm lint
|
||||
pnpm pretty-quick --staged
|
||||
pnpx pretty-quick --staged
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
# Contributing
|
||||
|
||||
[Read the Contributing Guide at Blitzjs.com](https://blitzjs.com/docs/contributing)
|
||||
|
||||
## To run tests
|
||||
|
||||
Make sure you have `chromedriver` installed for your Chrome version. You can install it with
|
||||
|
||||
- `brew install --cask chromedriver` on Mac OS X
|
||||
- `chocolatey install chromedriver` on Windows
|
||||
- Or manually download the version that matches your installed chrome version (if there's no match, download a version under it, but not above) from the [chromedriver repo](https://chromedriver.storage.googleapis.com/index.html) and add the binary to `<next-repo>/node_modules/.bin`
|
||||
|
||||
You may also have to [install Rust](https://www.rust-lang.org/tools/install) and build our native packages to see all tests pass locally. We check in binaries for the most common targets and those required for CI so that most people don't have to, but if you do not see a binary for your target in `packages/next/native`, you can build it by running `yarn --cwd packages/next build-native`. If you are working on the Rust code and you need to build the binaries for ci, you can manually trigger [the workflow](https://github.com/vercel/next.js/actions/workflows/build_native.yml) to build and commit with the "Run workflow" button.
|
||||
|
||||
Running all tests:
|
||||
|
||||
```sh
|
||||
pnpm test
|
||||
```
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupClient} from "@blitzjs/next"
|
||||
import {BlitzRpcPlugin} from "@blitzjs/rpc"
|
||||
|
||||
const {withBlitz} = setupClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
}),
|
||||
BlitzRpcPlugin({
|
||||
reactQueryOptions: {
|
||||
queries: {
|
||||
staleTime: 7000,
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {withBlitz}
|
||||
@@ -1,14 +0,0 @@
|
||||
import {Ctx} from "blitz"
|
||||
import {prisma} from "../../prisma"
|
||||
import {User} from "prisma"
|
||||
|
||||
export default async function createUser(
|
||||
input: {name: string; email: string},
|
||||
ctx: Ctx,
|
||||
): Promise<User> {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const user = await prisma.user.create({data: {name: input.name, email: input.email}})
|
||||
|
||||
return user
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export default function setBasic(input, ctx) {
|
||||
console.log("SET BASIC input", input)
|
||||
return
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default async function getBasic(input, ctx) {
|
||||
console.log("INPUT", input)
|
||||
|
||||
return "basic-result"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import {Ctx} from "blitz"
|
||||
import {prisma} from "../../prisma"
|
||||
import {User} from "prisma"
|
||||
|
||||
export default async function getUsers(_input: {}, ctx: Ctx): Promise<User[]> {
|
||||
ctx.session.$authorize()
|
||||
|
||||
const users = await prisma.user.findMany()
|
||||
|
||||
return users
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default function getV2Basic(input, ctx) {
|
||||
console.log("INPUT", input)
|
||||
|
||||
return "basic-result"
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
|
||||
module.exports = withBlitz(
|
||||
withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
}),
|
||||
)
|
||||
module.exports = withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
})
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"@prisma/client": "3.9.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"blitz": "workspace:*",
|
||||
"jest": "27.5.1",
|
||||
"next": "12.1.1",
|
||||
"next": "12.1.4",
|
||||
"prisma": "3.9.0",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {ErrorFallbackProps, ErrorComponent, ErrorBoundary} from "@blitzjs/next"
|
||||
import {AuthenticationError, AuthorizationError} from "blitz"
|
||||
import type {AppProps} from "next/app"
|
||||
import React, {Suspense} from "react"
|
||||
import {withBlitz} from "app/blitz-client"
|
||||
import React from "react"
|
||||
import {withBlitz} from "../src/client-setup"
|
||||
|
||||
function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
@@ -27,9 +27,7 @@ function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
function MyApp({Component, pageProps}: AppProps) {
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={RootErrorFallback}>
|
||||
<Suspense fallback="Loading...">
|
||||
<Component {...pageProps} />
|
||||
</Suspense>
|
||||
<Component {...pageProps} />
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const {session} = ctx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default api(async (_req, res) => {
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import {rpcHandler} from "@blitzjs/rpc"
|
||||
import {api} from "app/blitz-server"
|
||||
|
||||
export default api(rpcHandler({onError: console.log}))
|
||||
@@ -1,5 +1,6 @@
|
||||
import {setPublicDataForUser} from "@blitzjs/auth"
|
||||
import {api} from "app/blitz-server"
|
||||
|
||||
import {api} from "../../src/server-setup"
|
||||
import {prisma} from "../../prisma/index"
|
||||
|
||||
export default api(async (req, res, ctx) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
import {prisma} from "../../prisma/index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
import {prisma} from "../../prisma/index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import {useState} from "react"
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {useMutation} from "@blitzjs/rpc"
|
||||
import createUser from "app/mutations/createUser"
|
||||
import {User} from "prisma"
|
||||
|
||||
function Page() {
|
||||
const [name, setName] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [createUserMutation, {error}] = useMutation(createUser)
|
||||
const [newUser, setNewUser] = useState<null | User>(null)
|
||||
|
||||
return (
|
||||
<div>
|
||||
New User Form:
|
||||
<form
|
||||
onSubmit={async (e) => {
|
||||
e.preventDefault()
|
||||
const user = await createUserMutation({name, email})
|
||||
setNewUser(user)
|
||||
}}
|
||||
>
|
||||
<label>
|
||||
Name:
|
||||
<input type="text" value={name} onChange={(e) => setName(e.target.value)} />
|
||||
</label>
|
||||
<label>
|
||||
Email:
|
||||
<input type="text" value={email} onChange={(e) => setEmail(e.target.value)} />
|
||||
</label>
|
||||
<button type="submit">Create User</button>
|
||||
</form>
|
||||
<div style={{paddingTop: 20}}>
|
||||
<div>Error: {JSON.stringify(error, null, 2)}</div>
|
||||
New user: {JSON.stringify(newUser, null, 2)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -1,6 +1,3 @@
|
||||
import {invoke} from "@blitzjs/rpc"
|
||||
import getBasic from "app/queries/getBasic"
|
||||
|
||||
export const getServerSideProps = () => {
|
||||
return {props: {}}
|
||||
}
|
||||
@@ -9,7 +6,6 @@ export default function Web() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Web</h1>
|
||||
<button onClick={() => invoke(getBasic, "FROM BROWSER")}>GetBasic</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const PageWithAuthRedirect = () => {
|
||||
const PageWithRedirect = () => {
|
||||
return (
|
||||
<div>
|
||||
{JSON.stringify(
|
||||
@@ -13,6 +13,6 @@ const PageWithAuthRedirect = () => {
|
||||
)
|
||||
}
|
||||
|
||||
PageWithAuthRedirect.redirectAuthenticatedTo = "/"
|
||||
PageWithRedirect.redirectAuthenticatedTo = "/"
|
||||
|
||||
export default PageWithAuthRedirect
|
||||
export default PageWithRedirect
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {gSP} from "app/blitz-server"
|
||||
import {gSP} from "../src/server-setup"
|
||||
|
||||
export const getStaticProps = gSP(async ({ctx}) => {
|
||||
return {
|
||||
@@ -14,8 +14,8 @@ export const getStaticProps = gSP(async ({ctx}) => {
|
||||
}
|
||||
})
|
||||
|
||||
function PageWithGsp({data}) {
|
||||
function Page({data}) {
|
||||
return <div>{JSON.stringify(data, null, 2)}</div>
|
||||
}
|
||||
|
||||
export default PageWithGsp
|
||||
export default Page
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {SessionContext} from "@blitzjs/auth"
|
||||
import {gSSP} from "app/blitz-server"
|
||||
import {gSSP} from "../src/server-setup"
|
||||
|
||||
type Props = {
|
||||
userId: unknown
|
||||
@@ -16,8 +16,8 @@ export const getServerSideProps = gSSP<Props>(async ({ctx}) => {
|
||||
}
|
||||
})
|
||||
|
||||
function PageWithGssp(props: Props) {
|
||||
function Page(props: Props) {
|
||||
return <div>{JSON.stringify(props, null, 2)}</div>
|
||||
}
|
||||
|
||||
export default PageWithGssp
|
||||
export default Page
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useAuthenticatedSession} from "@blitzjs/auth"
|
||||
import {useAuthenticatedSession} from "../src/client-setup"
|
||||
|
||||
export default function PageWithUseAuthSession() {
|
||||
export default function PgaeWithUseAuthorizeIf() {
|
||||
useAuthenticatedSession()
|
||||
return <div>This page is using useAuthenticatedSession</div>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useAuthorizeIf} from "@blitzjs/auth"
|
||||
import {useAuthorizeIf} from "../src/client-setup"
|
||||
|
||||
export default function PageWithUseAuthorizeIf() {
|
||||
export default function PgaeWithUseAuthorizeIf() {
|
||||
useAuthorizeIf(Math.random() > 0.5)
|
||||
return <div>This page is using useAuthorizeIf</div>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useAuthorize} from "@blitzjs/auth"
|
||||
import {useAuthorize} from "../src/client-setup"
|
||||
|
||||
export default function PgaeWithUseAuthorize() {
|
||||
useAuthorize()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import {useRedirectAuthenticated} from "@blitzjs/auth"
|
||||
import {useRedirectAuthenticated} from "../src/client-setup"
|
||||
|
||||
export default function PageWithUseRedirectAuth() {
|
||||
export default function PgaeWithUseAuthorizeIf() {
|
||||
useRedirectAuthenticated("/")
|
||||
return <div>This page is using useRedirectAuthenticated</div>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useSession} from "@blitzjs/auth"
|
||||
import {useSession} from "../src/client-setup"
|
||||
|
||||
export default function PgaeWithUseSession() {
|
||||
const data = useSession()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const PageWithoutFlicker = ({data}) => {
|
||||
const PageWithRedirect = ({data}) => {
|
||||
return <div>{JSON.stringify(data)}</div>
|
||||
}
|
||||
|
||||
PageWithoutFlicker.suppressFirstRenderFlicker = true
|
||||
PageWithRedirect.suppressFirstRenderFlicker = true
|
||||
|
||||
export default PageWithoutFlicker
|
||||
export default PageWithRedirect
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import {useQuery} from "@blitzjs/rpc"
|
||||
import getUsers from "app/queries/getUsers"
|
||||
|
||||
function UsersPage() {
|
||||
const [users] = useQuery(getUsers, {})
|
||||
return (
|
||||
<div>
|
||||
Users:
|
||||
<ul>
|
||||
{users.map((user) => (
|
||||
<li key={user.id}>
|
||||
{user.name} - {user.email}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default UsersPage
|
||||
26
apps/web/src/client-setup.ts
Normal file
26
apps/web/src/client-setup.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupClient} from "@blitzjs/next"
|
||||
|
||||
const {
|
||||
withBlitz,
|
||||
useSession,
|
||||
useAuthorize,
|
||||
useAuthorizeIf,
|
||||
useRedirectAuthenticated,
|
||||
useAuthenticatedSession,
|
||||
} = setupClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {
|
||||
withBlitz,
|
||||
useSession,
|
||||
useAuthorize,
|
||||
useAuthorizeIf,
|
||||
useRedirectAuthenticated,
|
||||
useAuthenticatedSession,
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"extends": "@blitzjs/config/tsconfig.nextjs.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "."
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "jest.config.js"],
|
||||
"exclude": ["node_modules", "test"]
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupClient} from "@blitzjs/next"
|
||||
|
||||
const {withBlitz} = setupClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "auth-tests-cookie-prefix",
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {withBlitz}
|
||||
@@ -1,4 +1,10 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({
|
||||
// update me
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
})
|
||||
|
||||
module.exports = withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
esmExternals: "loose",
|
||||
},
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"@prisma/client": "3.9.0",
|
||||
"blitz": "workspace:*",
|
||||
"lowdb": "3.0.0",
|
||||
"next": "12.1.1",
|
||||
"next": "12.1.4",
|
||||
"prisma": "3.9.0",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0"
|
||||
@@ -28,13 +28,22 @@
|
||||
"@next/bundle-analyzer": "12.0.8",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/get-port": "4.2.0",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/selenium-webdriver": "4.0.18",
|
||||
"b64-lite": "1.4.0",
|
||||
"chromedriver": "100.0.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"eslint": "7.32.0",
|
||||
"express": "4.17.3",
|
||||
"fs-extra": "10.0.1",
|
||||
"get-port": "6.1.2",
|
||||
"node-fetch": "3.2.3",
|
||||
"rimraf": "3.0.2",
|
||||
"selenium-webdriver": "4.1.1",
|
||||
"tree-kill": "1.2.2",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {ErrorFallbackProps, ErrorComponent, ErrorBoundary} from "@blitzjs/next"
|
||||
import {AuthenticationError, AuthorizationError} from "blitz"
|
||||
import type {AppProps} from "next/app"
|
||||
import React from "react"
|
||||
import {withBlitz} from "../app/blitz-client"
|
||||
import {withBlitz} from "../src/client-setup"
|
||||
|
||||
function RootErrorFallback({error}: ErrorFallbackProps) {
|
||||
if (error instanceof AuthenticationError) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "../../app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
const blitzContext = ctx
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "../../app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
|
||||
export default api(async (_req, res, ctx) => {
|
||||
res.status(200).end()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {api} from "../../app/blitz-server"
|
||||
import {api} from "../../src/server-setup"
|
||||
import {prisma} from "../../prisma/index"
|
||||
import {SecurePassword} from "@blitzjs/auth"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {useAuthorize} from "@blitzjs/auth"
|
||||
import {useAuthorize} from "../src/client-setup"
|
||||
|
||||
export const getServerSideProps = () => {
|
||||
return {props: {}}
|
||||
|
||||
26
integration-tests/auth/src/client-setup.ts
Normal file
26
integration-tests/auth/src/client-setup.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {AuthClientPlugin} from "@blitzjs/auth"
|
||||
import {setupClient} from "@blitzjs/next"
|
||||
|
||||
const {
|
||||
withBlitz,
|
||||
useSession,
|
||||
useAuthorize,
|
||||
useAuthorizeIf,
|
||||
useRedirectAuthenticated,
|
||||
useAuthenticatedSession,
|
||||
} = setupClient({
|
||||
plugins: [
|
||||
AuthClientPlugin({
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export {
|
||||
withBlitz,
|
||||
useSession,
|
||||
useAuthorize,
|
||||
useAuthorizeIf,
|
||||
useRedirectAuthenticated,
|
||||
useAuthenticatedSession,
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import {prisma as db} from "../prisma/index"
|
||||
const {gSSP, gSP, api} = setupBlitz({
|
||||
plugins: [
|
||||
AuthServerPlugin({
|
||||
cookiePrefix: "auth-tests-cookie-prefix",
|
||||
cookiePrefix: "webapp-cookie-prefix",
|
||||
storage: PrismaStorage(db as any),
|
||||
isAuthorized: simpleRolesIsAuthorized,
|
||||
}),
|
||||
@@ -1,7 +1,6 @@
|
||||
import {describe, it, expect, beforeAll, afterAll} from "vitest"
|
||||
import {killApp, findPort, launchApp, nextBuild, nextStart} from "../../utils/next-test-utils"
|
||||
import webdriver from "../../utils/next-webdriver"
|
||||
|
||||
import {killApp, findPort, launchApp, nextBuild, nextStart} from "./next-test-utils"
|
||||
import webdriver from "./next-webdriver"
|
||||
import {join} from "path"
|
||||
import seed from "../prisma/seed"
|
||||
import fetch from "node-fetch"
|
||||
@@ -11,10 +10,10 @@ let app: any
|
||||
let appPort: number
|
||||
const appDir = join(__dirname, "../")
|
||||
const HEADER_CSRF = "anti-csrf"
|
||||
const COOKIE_PUBLIC_DATA_TOKEN = "auth-tests-cookie-prefix_sPublicDataToken"
|
||||
const COOKIE_SESSION_TOKEN = "auth-tests-cookie-prefix_sSessionToken"
|
||||
const COOKIE_ANONYMOUS_SESSION_TOKEN = "auth-tests-cookie-prefix_sAnonymousSessionToken"
|
||||
const COOKIE_REFRESH_TOKEN = "auth-tests-cookie-prefix_sIdRefreshToken"
|
||||
const COOKIE_PUBLIC_DATA_TOKEN = "webapp-cookie-prefix_sPublicDataToken"
|
||||
const COOKIE_SESSION_TOKEN = "webapp-cookie-prefix_sSessionToken"
|
||||
const COOKIE_ANONYMOUS_SESSION_TOKEN = "webapp-cookie-prefix_sAnonymousSessionToken"
|
||||
const COOKIE_REFRESH_TOKEN = "webapp-cookie-prefix_sIdRefreshToken"
|
||||
const HEADER_PUBLIC_DATA_TOKEN = "public-data-token"
|
||||
|
||||
function readCookie(cookieHeader, name) {
|
||||
@@ -128,7 +127,7 @@ describe("dev mode", () => {
|
||||
console.log(error)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
afterAll(() => killApp(app))
|
||||
runTests()
|
||||
})
|
||||
|
||||
@@ -142,7 +141,7 @@ describe("server mode", () => {
|
||||
console.log(err)
|
||||
}
|
||||
}, 5000 * 60 * 2)
|
||||
afterAll(async () => await killApp(app))
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests()
|
||||
})
|
||||
|
||||
@@ -96,7 +96,9 @@ interface RunNextCommandOptions {
|
||||
}
|
||||
|
||||
export function runNextCommand(argv: any[], options: RunNextCommandOptions = {}) {
|
||||
const cwd = options.cwd
|
||||
const nextDir = path.dirname(require.resolve("next/package"))
|
||||
const nextBin = path.join(nextDir, "dist/bin/next")
|
||||
const cwd = options.cwd || nextDir
|
||||
// Let Next.js decide the environment
|
||||
const env = {
|
||||
...process.env,
|
||||
@@ -107,7 +109,7 @@ export function runNextCommand(argv: any[], options: RunNextCommandOptions = {})
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
console.log(`Running command "next ${argv.join(" ")}"`)
|
||||
const instance = spawn("pnpm", ["exec", "next", ...argv], {
|
||||
const instance = spawn("node", ["--no-deprecation", nextBin, ...argv], {
|
||||
...options.spawnOptions,
|
||||
cwd,
|
||||
env,
|
||||
@@ -165,8 +167,11 @@ interface RunNextCommandDevOptions {
|
||||
nextStart?: boolean
|
||||
}
|
||||
|
||||
export function runNextCommandDev(argv, opts: RunNextCommandDevOptions = {}) {
|
||||
const cwd = opts.cwd // || nextDir
|
||||
export function runNextCommandDev(argv, stdOut, opts: RunNextCommandDevOptions = {}) {
|
||||
const nextDir = path.dirname(require.resolve("next/package"))
|
||||
const nextBin = path.join(nextDir, "dist/bin/next")
|
||||
|
||||
const cwd = opts.cwd || nextDir
|
||||
const env = {
|
||||
...process.env,
|
||||
NODE_ENV: opts.nextStart ? ("production" as const) : ("development" as const),
|
||||
@@ -174,8 +179,9 @@ export function runNextCommandDev(argv, opts: RunNextCommandDevOptions = {}) {
|
||||
...opts.env,
|
||||
}
|
||||
|
||||
const nodeArgs = opts.nodeArgs || []
|
||||
return new Promise<void | string | ChildProcess>((resolve, reject) => {
|
||||
const instance = spawn("pnpm", ["exec", "next", ...argv], {
|
||||
const instance = spawn("node", [...nodeArgs, "--no-deprecation", nextBin, ...argv], {
|
||||
cwd,
|
||||
env,
|
||||
})
|
||||
@@ -230,7 +236,7 @@ export function runNextCommandDev(argv, opts: RunNextCommandDevOptions = {}) {
|
||||
|
||||
// Launch the app in dev mode.
|
||||
export function launchApp(dir, port, opts) {
|
||||
return runNextCommandDev(["-p", port], {cwd: dir, ...opts})
|
||||
return runNextCommandDev([dir, "-p", port], undefined, opts)
|
||||
}
|
||||
|
||||
export function nextBuild(dir, args = [], opts = {}) {
|
||||
@@ -238,27 +244,26 @@ export function nextBuild(dir, args = [], opts = {}) {
|
||||
}
|
||||
|
||||
export function nextExport(dir, {outdir}, opts = {}) {
|
||||
return runNextCommand(["export", dir, "--outdir", outdir], {cwd: dir, ...opts})
|
||||
return runNextCommand(["export", dir, "--outdir", outdir], opts)
|
||||
}
|
||||
|
||||
export function nextExportDefault(dir, opts = {}) {
|
||||
return runNextCommand(["export", dir], {cwd: dir, ...opts})
|
||||
return runNextCommand(["export", dir], opts)
|
||||
}
|
||||
|
||||
export function nextLint(dir, args = [], opts = {}) {
|
||||
return runNextCommand(["lint", dir, ...args], {cwd: dir, ...opts})
|
||||
return runNextCommand(["lint", dir, ...args], opts)
|
||||
}
|
||||
|
||||
export function nextStart(dir, port, opts = {}) {
|
||||
return runNextCommandDev(["start", "-p", port], {
|
||||
cwd: dir,
|
||||
return runNextCommandDev(["start", "-p", port, dir], undefined, {
|
||||
...opts,
|
||||
nextStart: true,
|
||||
})
|
||||
}
|
||||
|
||||
export function buildTS(args = [], cwd, env = {NODE_ENV: "production" as const}) {
|
||||
cwd = cwd
|
||||
cwd = cwd || path.dirname(require.resolve("next/package"))
|
||||
env = {...process.env, ...env}
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
@@ -144,19 +144,6 @@ const getDeviceIP = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const closeBrowser = async () => {
|
||||
// First we close all extra windows left over
|
||||
let allWindows = await browser.getAllWindowHandles()
|
||||
|
||||
for (const win of allWindows) {
|
||||
if (win === initialWindow) continue
|
||||
try {
|
||||
await browser.switchTo().window(win)
|
||||
await browser.close()
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const freshWindow = async (appPort) => {
|
||||
// First we close all extra windows left over
|
||||
@@ -1,4 +0,0 @@
|
||||
export default async function setBasic(input, ctx) {
|
||||
global.basic = input
|
||||
return global.basic
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("This should not be loaded on the client")
|
||||
}
|
||||
|
||||
export default async function getBasic() {
|
||||
if (typeof window !== "undefined") {
|
||||
throw new Error("This should not be loaded on the client")
|
||||
}
|
||||
|
||||
global.basic ??= "basic-result"
|
||||
return global.basic
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default async function getFailure() {
|
||||
throw new Error("error on purpose for test")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export default async function getNestedBasic() {
|
||||
return "nested-basic"
|
||||
}
|
||||
5
integration-tests/rpc/next-env.d.ts
vendored
5
integration-tests/rpc/next-env.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@@ -1,4 +0,0 @@
|
||||
const {withBlitz} = require("@blitzjs/next")
|
||||
module.exports = withBlitz({
|
||||
// update me
|
||||
})
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "test-rpc",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest --config ./vitest.config.ts run",
|
||||
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "workspace:*",
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@blitzjs/next": "workspace:*",
|
||||
"@blitzjs/rpc": "workspace:*",
|
||||
"blitz": "workspace:*",
|
||||
"next": "12.1.1",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "17.0.43",
|
||||
"b64-lite": "1.4.0",
|
||||
"eslint": "7.32.0",
|
||||
"fs-extra": "10.0.1",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import {rpcHandler} from "@blitzjs/rpc"
|
||||
|
||||
export default rpcHandler({onError: console.log})
|
||||
@@ -1,4 +0,0 @@
|
||||
const Page = () => {
|
||||
return <div id="page-container">Hello World</div>
|
||||
}
|
||||
export default Page
|
||||
@@ -1,7 +0,0 @@
|
||||
import getBasic from "../app/queries/getBasic"
|
||||
|
||||
const Page = () => {
|
||||
getBasic().then(console.log)
|
||||
return <div id="page-container">Hello World</div>
|
||||
}
|
||||
export default Page
|
||||
@@ -1,199 +0,0 @@
|
||||
import {describe, it, expect, beforeAll, afterAll} from "vitest"
|
||||
import fs from "fs-extra"
|
||||
import {join} from "path"
|
||||
import {
|
||||
killApp,
|
||||
findPort,
|
||||
launchApp,
|
||||
fetchViaHTTP,
|
||||
nextBuild,
|
||||
nextStart,
|
||||
nextExport,
|
||||
getPageFileFromBuildManifest,
|
||||
getPageFileFromPagesManifest,
|
||||
} from "../../utils/next-test-utils"
|
||||
|
||||
// jest.setTimeout(1000 * 60 * 2)
|
||||
const appDir = join(__dirname, "../")
|
||||
const nextConfig = join(appDir, "next.config.js")
|
||||
let appPort
|
||||
let mode
|
||||
let app
|
||||
|
||||
function runTests(dev = false) {
|
||||
describe("api requests", () => {
|
||||
it(
|
||||
"returns 200 for HEAD",
|
||||
async () => {
|
||||
const res = await fetchViaHTTP(appPort, "/api/rpc/getBasic", null, {
|
||||
method: "HEAD",
|
||||
})
|
||||
expect(res.status).toEqual(200)
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"returns 404 for GET",
|
||||
async () => {
|
||||
const res = await fetchViaHTTP(appPort, "/api/rpc/getBasic", null, {
|
||||
method: "GET",
|
||||
})
|
||||
expect(res.status).toEqual(404)
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"requires params",
|
||||
async () => {
|
||||
const res = await fetchViaHTTP(appPort, "/api/rpc/getBasic", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
})
|
||||
const json = await res.json()
|
||||
expect(res.status).toEqual(400)
|
||||
expect(json.error.message).toBe("Request body is missing the `params` key")
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"query works",
|
||||
async () => {
|
||||
const data = await fetchViaHTTP(appPort, "/api/rpc/getBasic", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
body: JSON.stringify({params: {}}),
|
||||
}).then((res) => res.ok && res.json())
|
||||
|
||||
expect(data).toEqual({result: "basic-result", error: null, meta: {}})
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"mutation works",
|
||||
async () => {
|
||||
const data = await fetchViaHTTP(appPort, "/api/rpc/setBasic", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
body: JSON.stringify({params: "new-basic"}),
|
||||
}).then((res) => res.ok && res.json())
|
||||
|
||||
expect(data).toEqual({result: "new-basic", error: null, meta: {}})
|
||||
|
||||
const data2 = await fetchViaHTTP(appPort, "/api/rpc/getBasic", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
body: JSON.stringify({params: {}}),
|
||||
}).then((res) => res.ok && res.json())
|
||||
|
||||
expect(data2).toEqual({result: "new-basic", error: null, meta: {}})
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"handles resolver errors",
|
||||
async () => {
|
||||
const res = await fetchViaHTTP(appPort, "/api/rpc/getFailure", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
body: JSON.stringify({params: {}}),
|
||||
})
|
||||
const json = await res.json()
|
||||
expect(res.status).toEqual(200)
|
||||
expect(json).toEqual({
|
||||
result: null,
|
||||
error: {name: "Error", message: "error on purpose for test", statusCode: 500},
|
||||
meta: {error: {values: ["Error"]}},
|
||||
})
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
it(
|
||||
"nested query works",
|
||||
async () => {
|
||||
const data = await fetchViaHTTP(appPort, "/api/rpc/v2/getNestedBasic", null, {
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json; charset=utf-8"},
|
||||
body: JSON.stringify({params: {}}),
|
||||
}).then((res) => res.ok && res.json())
|
||||
|
||||
expect(data).toEqual({result: "nested-basic", error: null, meta: {}})
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
})
|
||||
|
||||
if (!dev) {
|
||||
it("should show warning with next export", async () => {
|
||||
const {stderr} = await nextExport(appDir, {outdir: join(appDir, "out")}, {stderr: true})
|
||||
expect(stderr).toContain("https://nextjs.org/docs/messages/api-routes-static-export")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
describe("RPC", () => {
|
||||
describe(
|
||||
"dev mode",
|
||||
() => {
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
appPort = await findPort()
|
||||
app = await launchApp(appDir, appPort)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests(true)
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
describe(
|
||||
"server mode",
|
||||
() => {
|
||||
beforeAll(async () => {
|
||||
await nextBuild(appDir)
|
||||
mode = "server"
|
||||
appPort = await findPort()
|
||||
app = await nextStart(appDir, appPort)
|
||||
})
|
||||
afterAll(() => killApp(app))
|
||||
|
||||
runTests()
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
|
||||
describe(
|
||||
"serverless mode",
|
||||
() => {
|
||||
let nextConfigContent = ""
|
||||
const nextConfigPath = join(appDir, "next.config.js")
|
||||
beforeAll(async () => {
|
||||
nextConfigContent = await fs.readFile(nextConfigPath, "utf8")
|
||||
await fs.writeFile(
|
||||
nextConfigPath,
|
||||
nextConfigContent.replace("// update me", `target: 'experimental-serverless-trace',`),
|
||||
)
|
||||
await nextBuild(appDir)
|
||||
mode = "serverless"
|
||||
appPort = await findPort()
|
||||
app = await nextStart(appDir, appPort)
|
||||
})
|
||||
afterAll(async () => {
|
||||
await killApp(app)
|
||||
await fs.writeFile(nextConfigPath, nextConfigContent)
|
||||
})
|
||||
|
||||
runTests()
|
||||
},
|
||||
5000 * 60 * 2,
|
||||
)
|
||||
})
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import {defineConfig} from "vitest/config"
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
testTimeout: 5000 * 60 * 2,
|
||||
hookTimeout: 5000 * 60 * 2,
|
||||
},
|
||||
})
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "test-utils",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@types/express": "4.17.13",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/node-fetch": "2.6.1",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/rimraf": "3.0.2",
|
||||
"@types/selenium-webdriver": "4.0.18",
|
||||
"chromedriver": "100.0.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"eslint": "7.32.0",
|
||||
"express": "4.17.3",
|
||||
"fs-extra": "10.0.1",
|
||||
"get-port": "6.1.2",
|
||||
"node-fetch": "3.2.3",
|
||||
"rimraf": "3.0.2",
|
||||
"selenium-webdriver": "4.1.1",
|
||||
"tree-kill": "1.2.2",
|
||||
"typescript": "^4.5.3"
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,15 @@
|
||||
"lint": "turbo run lint",
|
||||
"test": "turbo run test",
|
||||
"clean": "turbo run clean && rm -rf node_modules",
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
|
||||
"pre-publish": "pnpm i && pnpm build && changeset add && changeset version",
|
||||
"publish-release": "changeset publish && git push --follow-tags"
|
||||
"format": "prettier --write \"**/*.{ts,tsx,md}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzjs/manypkg": "0.19.1",
|
||||
"@changesets/cli": "2.22.0",
|
||||
"eslint": "7.32.0",
|
||||
"husky": "7.0.4",
|
||||
"jsdom": "19.0.0",
|
||||
"lint-staged": "12.1.7",
|
||||
"next": "12.1.1",
|
||||
"next": "12.1.4",
|
||||
"only-allow": "1.1.0",
|
||||
"patch-package": "6.4.7",
|
||||
"prettier": "^2.5.1",
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# @blitzjs/auth
|
||||
|
||||
## 2.0.0-alpha.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- blitz@2.0.0-alpha.4
|
||||
|
||||
## 2.0.0-alpha.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- blitz@2.0.0-alpha.3
|
||||
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- blitz@2.0.0-alpha.2
|
||||
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 46a34c7b: initial publish
|
||||
- Updated dependencies [46a34c7b]
|
||||
- blitz@2.0.0-alpha.1
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@blitzjs/auth",
|
||||
"version": "2.0.0-alpha.4",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts",
|
||||
"dev": "pnpm run predev && watch unbuild src --wait=0.2",
|
||||
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts",
|
||||
"lint": "eslint . --fix",
|
||||
"test": "vitest run",
|
||||
"test-watch": "vitest",
|
||||
@@ -19,8 +19,26 @@
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@testing-library/react": "13.0.0",
|
||||
"@testing-library/react-hooks": "7.0.2",
|
||||
"@types/cookie": "0.4.1",
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
"typescript": "^4.5.3",
|
||||
"unbuild": "0.6.9",
|
||||
"watch": "1.0.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/b64-lite": "1.3.0",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/secure-password": "3.1.1",
|
||||
"b64-lite": "1.4.0",
|
||||
"bad-behavior": "1.0.1",
|
||||
@@ -33,23 +51,5 @@
|
||||
"path": "0.12.7",
|
||||
"secure-password": "4.0.0",
|
||||
"url": "0.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@testing-library/react": "13.0.0",
|
||||
"@testing-library/react-hooks": "7.0.2",
|
||||
"@types/cookie": "0.4.1",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
"typescript": "^4.5.3",
|
||||
"unbuild": "0.6.9",
|
||||
"watch": "1.0.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export interface UseSessionOptions {
|
||||
}
|
||||
|
||||
export const useSession = (options: UseSessionOptions = {}): ClientSession => {
|
||||
const suspense = options?.suspense ?? Boolean(globalThis.__BLITZ_SUSPENSE_ENABLED)
|
||||
const suspense = options?.suspense ?? Boolean(process.env.__BLITZ_SUSPENSE_ENABLED)
|
||||
|
||||
let initialState: ClientSession
|
||||
if (options.initialPublicData) {
|
||||
|
||||
@@ -3,5 +3,4 @@ import {SessionConfig} from "./shared/types"
|
||||
declare global {
|
||||
var sessionConfig: SessionConfig
|
||||
var __BLITZ_SESSION_COOKIE_PREFIX: string | undefined
|
||||
var __BLITZ_SUSPENSE_ENABLED: boolean
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "./global"
|
||||
|
||||
export * from "./client"
|
||||
export * from "./shared/constants"
|
||||
export type {
|
||||
SessionContextBase,
|
||||
SessionContext,
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
# @blitzjs/next
|
||||
|
||||
## 2.0.0-alpha.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @blitzjs/rpc@2.0.0-alpha.4
|
||||
|
||||
## 2.0.0-alpha.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @blitzjs/rpc@2.0.0-alpha.3
|
||||
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @blitzjs/rpc@2.0.0-alpha.2
|
||||
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 46a34c7b: initial publish
|
||||
- Updated dependencies [46a34c7b]
|
||||
- @blitzjs/rpc@2.0.0-alpha.1
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "@blitzjs/next",
|
||||
"version": "2.0.0-alpha.4",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"dev": "pnpm predev && pnpm watch unbuild src --wait=0.2",
|
||||
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts && wait-on -d 250 ../blitz-rpc/dist/index-server.d.ts",
|
||||
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts",
|
||||
"lint": "eslint . --fix",
|
||||
"test": "vitest run",
|
||||
"test-watch": "vitest",
|
||||
@@ -20,10 +20,8 @@
|
||||
"dist/**"
|
||||
],
|
||||
"dependencies": {
|
||||
"@blitzjs/rpc": "2.0.0-alpha.4",
|
||||
"debug": "4.3.3",
|
||||
"fs-extra": "10.0.1",
|
||||
"react-query": "3.21.1"
|
||||
"react-query": "3.34.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
@@ -39,7 +37,7 @@
|
||||
"@types/testing-library__react-hooks": "4.0.0",
|
||||
"blitz": "workspace:*",
|
||||
"lodash.frompairs": "4.0.1",
|
||||
"next": "12.1.1",
|
||||
"next": "12.1.4",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
"ts-jest": "27.1.4",
|
||||
|
||||
@@ -220,7 +220,8 @@ test("withErrorBoundary HOC", () => {
|
||||
expect(cleanStack(onErrorComponentStack)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"componentStack": "
|
||||
at ErrorBoundary
|
||||
at __vite_ssr_import_4__.withErrorBoundary.FallbackComponent
|
||||
at ErrorBoundary
|
||||
at withErrorBoundary",
|
||||
}
|
||||
`)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import {QueryClient} from "react-query"
|
||||
|
||||
declare global {
|
||||
var queryClient: QueryClient
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import "./global"
|
||||
import type {
|
||||
ClientPlugin,
|
||||
BlitzProvider as BlitzProviderType,
|
||||
@@ -7,9 +6,8 @@ import type {
|
||||
} from "blitz"
|
||||
import {AppProps} from "next/app"
|
||||
import Head from "next/head"
|
||||
import React from "react"
|
||||
import {QueryClient, QueryClientProvider} from "react-query"
|
||||
import {Hydrate, HydrateOptions} from "react-query/hydration"
|
||||
import React, {FC} from "react"
|
||||
import {Hydrate, HydrateOptions, QueryClient, QueryClientProvider} from "react-query"
|
||||
|
||||
export * from "./error-boundary"
|
||||
export * from "./error-component"
|
||||
@@ -38,11 +36,9 @@ const buildWithBlitz = <TPlugins extends readonly ClientPlugin<object>[]>(plugin
|
||||
|
||||
return (
|
||||
<BlitzProvider dehydratedState={props.pageProps?.dehydratedState}>
|
||||
<>
|
||||
{/* @ts-ignore todo */}
|
||||
{props.Component.suppressFirstRenderFlicker && <NoPageFlicker />}
|
||||
<UserAppRoot {...props} Component={component} />
|
||||
</>
|
||||
{/* @ts-ignore todo */}
|
||||
{props.Component.suppressFirstRenderFlicker && <NoPageFlicker />}
|
||||
<UserAppRoot {...props} Component={component} />
|
||||
</BlitzProvider>
|
||||
)
|
||||
}
|
||||
@@ -57,27 +53,20 @@ export type BlitzProviderProps = {
|
||||
hydrateOptions?: HydrateOptions
|
||||
}
|
||||
|
||||
const BlitzProvider = ({
|
||||
const BlitzProvider: FC<BlitzProviderProps> = ({
|
||||
client,
|
||||
contextSharing = false,
|
||||
dehydratedState,
|
||||
hydrateOptions,
|
||||
children,
|
||||
}: BlitzProviderProps & {children: JSX.Element}) => {
|
||||
if (globalThis.queryClient) {
|
||||
return (
|
||||
<QueryClientProvider
|
||||
client={client || globalThis.queryClient}
|
||||
contextSharing={contextSharing}
|
||||
>
|
||||
<Hydrate state={dehydratedState} options={hydrateOptions}>
|
||||
{children}
|
||||
</Hydrate>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
return children
|
||||
}) => {
|
||||
return (
|
||||
<QueryClientProvider client={client || queryClient} contextSharing={contextSharing}>
|
||||
<Hydrate state={dehydratedState} options={hydrateOptions}>
|
||||
{children}
|
||||
</Hydrate>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export type PluginsExports<TPlugins extends readonly ClientPlugin<object>[]> = Simplify<
|
||||
@@ -123,6 +112,34 @@ const setupClient = <TPlugins extends readonly ClientPlugin<object>[]>({
|
||||
|
||||
export {setupClient}
|
||||
|
||||
// ------------------------------------ QUERY CLIENT CODE --------------------------------------------
|
||||
|
||||
const initializeQueryClient = () => {
|
||||
let suspenseEnabled = true
|
||||
if (!process.env.CLI_COMMAND_CONSOLE && !process.env.CLI_COMMAND_DB) {
|
||||
suspenseEnabled = Boolean(process.env.__BLITZ_SUSPENSE_ENABLED)
|
||||
}
|
||||
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
...(typeof window === "undefined" && {cacheTime: 0}),
|
||||
suspense: suspenseEnabled,
|
||||
retry: (failureCount, error: any) => {
|
||||
if (process.env.NODE_ENV !== "production") return false
|
||||
|
||||
// Retry (max. 3 times) only if network error detected
|
||||
if (error.message === "Network request failed" && failureCount <= 3) return true
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const queryClient = initializeQueryClient()
|
||||
|
||||
const customCSS = `
|
||||
body::before {
|
||||
content: "";
|
||||
|
||||
@@ -73,18 +73,3 @@ export const setupBlitz = ({plugins}: SetupBlitzOptions) => {
|
||||
|
||||
return {gSSP, gSP, api}
|
||||
}
|
||||
|
||||
import type {NextConfig} from "next"
|
||||
import {installWebpackConfig} from "@blitzjs/rpc"
|
||||
|
||||
export function withBlitz(nextConfig: NextConfig = {}) {
|
||||
return Object.assign({}, nextConfig, {
|
||||
webpack: (config: any, options: any) => {
|
||||
installWebpackConfig(config)
|
||||
if (typeof nextConfig.webpack === "function") {
|
||||
return nextConfig.webpack(config, options)
|
||||
}
|
||||
return config
|
||||
},
|
||||
} as NextConfig)
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# @blitzjs/rpc
|
||||
|
||||
## 2.0.0-alpha.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- blitz@2.0.0-alpha.4
|
||||
- @blitzjs/auth@2.0.0-alpha.4
|
||||
|
||||
## 2.0.0-alpha.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- blitz@2.0.0-alpha.3
|
||||
- @blitzjs/auth@2.0.0-alpha.3
|
||||
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- blitz@2.0.0-alpha.2
|
||||
- @blitzjs/auth@2.0.0-alpha.2
|
||||
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 46a34c7b: initial publish
|
||||
- Updated dependencies [46a34c7b]
|
||||
- blitz@2.0.0-alpha.1
|
||||
- @blitzjs/auth@2.0.0-alpha.1
|
||||
@@ -1,19 +1,8 @@
|
||||
import {BuildConfig} from "unbuild"
|
||||
|
||||
const config: BuildConfig = {
|
||||
entries: [
|
||||
"./src/index-browser",
|
||||
"./src/index-server",
|
||||
"./src/loader-server",
|
||||
"./src/loader-client",
|
||||
],
|
||||
externals: [
|
||||
"index-browser.cjs",
|
||||
"index-browser.mjs",
|
||||
"index-server.cjs",
|
||||
"index-server.mjs",
|
||||
"react",
|
||||
],
|
||||
entries: ["./src/index-browser", "./src/index-server"],
|
||||
externals: ["index-browser.cjs", "index-browser.mjs", "react"],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "@blitzjs/rpc",
|
||||
"version": "2.0.0-alpha.4",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "unbuild",
|
||||
"predev": "wait-on -d 250 ../blitz/dist/index-server.d.ts && wait-on -d 250 ../blitz-auth/dist/index-browser.d.ts",
|
||||
"dev": "pnpm run predev && watch unbuild src --wait=0.2",
|
||||
"dev": "watch unbuild src --wait=0.2",
|
||||
"lint": "eslint . --fix",
|
||||
"test": "vitest run",
|
||||
"test-watch": "vitest",
|
||||
@@ -19,32 +18,13 @@
|
||||
"files": [
|
||||
"dist/**"
|
||||
],
|
||||
"dependencies": {
|
||||
"@blitzjs/auth": "2.0.0-alpha.4",
|
||||
"b64-lite": "1.4.0",
|
||||
"bad-behavior": "1.0.1",
|
||||
"chalk": "^4.1.0",
|
||||
"debug": "4.3.3",
|
||||
"react-query": "3.21.1",
|
||||
"superjson": "1.8.0"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "workspace:*",
|
||||
"@types/debug": "4.1.7",
|
||||
"@types/react": "17.0.43",
|
||||
"@types/react-dom": "17.0.14",
|
||||
"blitz": "2.0.0-alpha.4",
|
||||
"next": "12.1.1",
|
||||
"react": "18.0.0",
|
||||
"react-dom": "18.0.0",
|
||||
"typescript": "^4.5.3",
|
||||
"unbuild": "0.6.9",
|
||||
"watch": "1.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"blitz": "2.0.0-alpha.4",
|
||||
"next": "*"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export * from "./rpc"
|
||||
export {useQuery, usePaginatedQuery, useInfiniteQuery, useMutation} from "./react-query"
|
||||
export type {MutateFunction} from "./react-query"
|
||||
export {
|
||||
queryClient,
|
||||
getQueryKey,
|
||||
getInfiniteQueryKey,
|
||||
invalidateQuery,
|
||||
setQueryData,
|
||||
} from "./react-query-utils"
|
||||
export {useQueryErrorResetBoundary, QueryClient} from "react-query"
|
||||
export {dehydrate} from "react-query/hydration"
|
||||
export {invoke} from "./invoke"
|
||||
@@ -1,21 +0,0 @@
|
||||
import {FirstParam, PromiseReturnType, isClient} from "blitz"
|
||||
import {RpcClient} from "./rpc"
|
||||
|
||||
export function invoke<T extends (...args: any) => any, TInput = FirstParam<T>>(
|
||||
queryFn: T,
|
||||
params: TInput,
|
||||
): Promise<PromiseReturnType<T>> {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error(
|
||||
"invoke is missing the first argument - it must be a query or mutation function",
|
||||
)
|
||||
}
|
||||
|
||||
if (isClient) {
|
||||
const fn = queryFn as unknown as RpcClient
|
||||
return fn(params, {fromInvoke: true}) as ReturnType<T>
|
||||
} else {
|
||||
const fn = queryFn as unknown as RpcClient
|
||||
return fn(params) as ReturnType<T>
|
||||
}
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
import {QueryClient, QueryKey} from "react-query"
|
||||
import {serialize} from "superjson"
|
||||
import {isClient, isServer, AsyncFunc} from "blitz"
|
||||
import {ResolverType, RpcClient} from "./rpc"
|
||||
|
||||
export type Resolver<TInput, TResult> = (input: TInput, ctx?: any) => Promise<TResult>
|
||||
|
||||
type RequestIdleCallbackDeadline = {
|
||||
readonly didTimeout: boolean
|
||||
timeRemaining: () => number
|
||||
}
|
||||
|
||||
export const requestIdleCallback =
|
||||
(typeof self !== "undefined" &&
|
||||
self.requestIdleCallback &&
|
||||
self.requestIdleCallback.bind(window)) ||
|
||||
function (cb: (deadline: RequestIdleCallbackDeadline) => void): NodeJS.Timeout {
|
||||
let start = Date.now()
|
||||
return setTimeout(function () {
|
||||
cb({
|
||||
didTimeout: false,
|
||||
timeRemaining: function () {
|
||||
return Math.max(0, 50 - (Date.now() - start))
|
||||
},
|
||||
})
|
||||
}, 1)
|
||||
}
|
||||
|
||||
type MutateOptions = {
|
||||
refetch?: boolean
|
||||
}
|
||||
|
||||
export const initializeQueryClient = () => {
|
||||
let suspenseEnabled = true
|
||||
if (!process.env.CLI_COMMAND_CONSOLE && !process.env.CLI_COMMAND_DB) {
|
||||
suspenseEnabled = Boolean(globalThis.__BLITZ_SUSPENSE_ENABLED)
|
||||
}
|
||||
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
...(isServer && {cacheTime: 0}),
|
||||
suspense: suspenseEnabled,
|
||||
retry: (failureCount, error: any) => {
|
||||
if (process.env.NODE_ENV !== "production") return false
|
||||
|
||||
// Retry (max. 3 times) only if network error detected
|
||||
if (error.message === "Network request failed" && failureCount <= 3) return true
|
||||
|
||||
return false
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Create internal QueryClient instance
|
||||
export const queryClient = initializeQueryClient()
|
||||
|
||||
function isRpcClient(f: any): f is RpcClient<any, any> {
|
||||
return !!f._isRpcClient
|
||||
}
|
||||
|
||||
export interface QueryCacheFunctions<T> {
|
||||
setQueryData: (
|
||||
newData: T | ((oldData: T | undefined) => T),
|
||||
opts?: MutateOptions,
|
||||
) => ReturnType<typeof setQueryData>
|
||||
}
|
||||
|
||||
export const getQueryCacheFunctions = <TInput, TResult, T extends AsyncFunc>(
|
||||
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
params: TInput,
|
||||
): QueryCacheFunctions<TResult> => ({
|
||||
setQueryData: (newData, opts = {refetch: true}) => {
|
||||
return setQueryData(resolver, params, newData, opts)
|
||||
},
|
||||
})
|
||||
|
||||
export const emptyQueryFn: RpcClient<unknown, unknown> = (() => {
|
||||
const fn = (() => new Promise(() => {})) as any as RpcClient
|
||||
fn._isRpcClient = true
|
||||
return fn
|
||||
})()
|
||||
|
||||
const isNotInUserTestEnvironment = () => {
|
||||
if (process.env.JEST_WORKER_ID === undefined) return true
|
||||
if (process.env.BLITZ_TEST_ENVIRONMENT !== undefined) return true
|
||||
return false
|
||||
}
|
||||
|
||||
export const validateQueryFn = <TInput, TResult>(
|
||||
queryFn: Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
) => {
|
||||
if (isClient && !isRpcClient(queryFn) && isNotInUserTestEnvironment()) {
|
||||
throw new Error(
|
||||
`Either the file path to your resolver is incorrect (must be in a "queries" or "mutations" folder that isn't nested inside "pages" or "api") or you are trying to use Blitz's useQuery to fetch from third-party APIs (to do that, import useQuery directly from "react-query")`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const sanitize =
|
||||
(type: ResolverType) =>
|
||||
<TInput, TResult>(
|
||||
queryFn: Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
): RpcClient<TInput, TResult> => {
|
||||
if (isServer) return queryFn as any
|
||||
|
||||
validateQueryFn(queryFn)
|
||||
|
||||
const rpcClient = queryFn as RpcClient<TInput, TResult>
|
||||
|
||||
const queryFnName = type === "mutation" ? "useMutation" : "useQuery"
|
||||
|
||||
if (rpcClient._resolverType !== type && isNotInUserTestEnvironment()) {
|
||||
throw new Error(
|
||||
`"${queryFnName}" was expected to be called with a ${type} but was called with a "${rpcClient._resolverType}"`,
|
||||
)
|
||||
}
|
||||
|
||||
return rpcClient
|
||||
}
|
||||
|
||||
export const sanitizeQuery = sanitize("query")
|
||||
export const sanitizeMutation = sanitize("mutation")
|
||||
|
||||
export const getQueryKeyFromUrlAndParams = (url: string, params: unknown) => {
|
||||
const queryKey = [url]
|
||||
|
||||
const args = typeof params === "function" ? (params as Function)() : params
|
||||
queryKey.push(serialize(args) as any)
|
||||
|
||||
return queryKey as [string, any]
|
||||
}
|
||||
|
||||
export function getQueryKey<TInput, TResult, T extends AsyncFunc>(
|
||||
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
params?: TInput,
|
||||
) {
|
||||
if (typeof resolver === "undefined") {
|
||||
throw new Error("getQueryKey is missing the first argument - it must be a resolver function")
|
||||
}
|
||||
|
||||
return getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._routePath, params)
|
||||
}
|
||||
|
||||
export function getInfiniteQueryKey<TInput, TResult, T extends AsyncFunc>(
|
||||
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
params?: TInput,
|
||||
) {
|
||||
if (typeof resolver === "undefined") {
|
||||
throw new Error(
|
||||
"getInfiniteQueryKey is missing the first argument - it must be a resolver function",
|
||||
)
|
||||
}
|
||||
|
||||
const queryKey = getQueryKeyFromUrlAndParams(sanitizeQuery(resolver)._routePath, params)
|
||||
return [...queryKey, "infinite"]
|
||||
}
|
||||
|
||||
export function invalidateQuery<TInput, TResult, T extends AsyncFunc>(
|
||||
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
params?: TInput,
|
||||
) {
|
||||
if (typeof resolver === "undefined") {
|
||||
throw new Error(
|
||||
"invalidateQuery is missing the first argument - it must be a resolver function",
|
||||
)
|
||||
}
|
||||
|
||||
const fullQueryKey = getQueryKey(resolver, params)
|
||||
let queryKey: QueryKey
|
||||
if (params) {
|
||||
queryKey = fullQueryKey
|
||||
} else {
|
||||
// Params not provided, only use first query key item (url)
|
||||
queryKey = fullQueryKey[0]
|
||||
}
|
||||
return queryClient.invalidateQueries(queryKey)
|
||||
}
|
||||
|
||||
export function setQueryData<TInput, TResult, T extends AsyncFunc>(
|
||||
resolver: T | Resolver<TInput, TResult> | RpcClient<TInput, TResult>,
|
||||
params: TInput,
|
||||
newData: TResult | ((oldData: TResult | undefined) => TResult),
|
||||
opts: MutateOptions = {refetch: true},
|
||||
): Promise<void | ReturnType<typeof queryClient.invalidateQueries>> {
|
||||
if (typeof resolver === "undefined") {
|
||||
throw new Error("setQueryData is missing the first argument - it must be a resolver function")
|
||||
}
|
||||
const queryKey = getQueryKey(resolver, params)
|
||||
|
||||
return new Promise((res) => {
|
||||
queryClient.setQueryData(queryKey, newData)
|
||||
let result: void | ReturnType<typeof queryClient.invalidateQueries>
|
||||
if (opts.refetch) {
|
||||
result = invalidateQuery(resolver, params)
|
||||
}
|
||||
if (isClient) {
|
||||
// Fix for https://github.com/blitz-js/blitz/issues/1174
|
||||
requestIdleCallback(() => {
|
||||
res(result)
|
||||
})
|
||||
} else {
|
||||
res(result)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
import {
|
||||
useInfiniteQuery as useInfiniteReactQuery,
|
||||
UseInfiniteQueryOptions,
|
||||
UseInfiniteQueryResult,
|
||||
useQuery as useReactQuery,
|
||||
UseQueryOptions,
|
||||
UseQueryResult,
|
||||
MutateOptions,
|
||||
useMutation as useReactQueryMutation,
|
||||
UseMutationOptions,
|
||||
UseMutationResult,
|
||||
} from "react-query"
|
||||
import {useSession} from "@blitzjs/auth"
|
||||
import {isServer, FirstParam, PromiseReturnType, AsyncFunc} from "blitz"
|
||||
import {
|
||||
emptyQueryFn,
|
||||
getQueryCacheFunctions,
|
||||
getQueryKey,
|
||||
QueryCacheFunctions,
|
||||
sanitizeQuery,
|
||||
sanitizeMutation,
|
||||
getInfiniteQueryKey,
|
||||
} from "./react-query-utils"
|
||||
import {useRouter} from "next/router"
|
||||
|
||||
type QueryLazyOptions = {suspense: unknown} | {enabled: unknown}
|
||||
type QueryNonLazyOptions =
|
||||
| {suspense: true; enabled?: never}
|
||||
| {suspense?: never; enabled: true}
|
||||
| {suspense: true; enabled: true}
|
||||
| {suspense?: never; enabled?: never}
|
||||
|
||||
// -------------------------
|
||||
// useQuery
|
||||
// -------------------------
|
||||
type RestQueryResult<TResult, TError> = Omit<UseQueryResult<TResult, TError>, "data"> &
|
||||
QueryCacheFunctions<TResult>
|
||||
|
||||
export function useQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options?: UseQueryOptions<TResult, TError, TSelectedData> & QueryNonLazyOptions,
|
||||
): [TSelectedData, RestQueryResult<TSelectedData, TError>]
|
||||
export function useQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: UseQueryOptions<TResult, TError, TSelectedData> & QueryLazyOptions,
|
||||
): [TSelectedData | undefined, RestQueryResult<TSelectedData, TError>]
|
||||
export function useQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: UseQueryOptions<TResult, TError, TSelectedData> = {},
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("useQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
const suspenseEnabled = Boolean(globalThis.__BLITZ_SUSPENSE_ENABLED)
|
||||
let enabled = isServer && suspenseEnabled ? false : options?.enabled ?? options?.enabled !== null
|
||||
const suspense = enabled === false ? false : options?.suspense
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady || (isServer && suspenseEnabled)
|
||||
const enhancedResolverRpcClient = sanitizeQuery(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
|
||||
const {data, ...queryRest} = useReactQuery({
|
||||
queryKey: routerIsReady ? queryKey : ["_routerNotReady_"],
|
||||
queryFn: routerIsReady
|
||||
? () => enhancedResolverRpcClient(params, {fromQueryHook: true})
|
||||
: (emptyQueryFn as any),
|
||||
...options,
|
||||
enabled,
|
||||
})
|
||||
|
||||
if (
|
||||
queryRest.isIdle &&
|
||||
isServer &&
|
||||
suspenseEnabled !== false &&
|
||||
!data &&
|
||||
(!options || !("suspense" in options) || options.suspense) &&
|
||||
(!options || !("enabled" in options) || options.enabled)
|
||||
) {
|
||||
throw new Promise(() => {})
|
||||
}
|
||||
|
||||
const rest = {
|
||||
...queryRest,
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
|
||||
}
|
||||
|
||||
// return [data, rest as RestQueryResult<TResult>]
|
||||
return [data, rest]
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// usePaginatedQuery
|
||||
// -------------------------
|
||||
type RestPaginatedResult<TResult, TError> = Omit<UseQueryResult<TResult, TError>, "data"> &
|
||||
QueryCacheFunctions<TResult>
|
||||
|
||||
export function usePaginatedQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options?: UseQueryOptions<TResult, TError, TSelectedData> & QueryNonLazyOptions,
|
||||
): [TSelectedData, RestPaginatedResult<TSelectedData, TError>]
|
||||
export function usePaginatedQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: UseQueryOptions<TResult, TError, TSelectedData> & QueryLazyOptions,
|
||||
): [TSelectedData | undefined, RestPaginatedResult<TSelectedData, TError>]
|
||||
export function usePaginatedQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
params: FirstParam<T>,
|
||||
options: UseQueryOptions<TResult, TError, TSelectedData> = {},
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("usePaginatedQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
const suspenseEnabled = Boolean(globalThis.__BLITZ_SUSPENSE_ENABLED)
|
||||
let enabled = isServer && suspenseEnabled ? false : options?.enabled ?? options?.enabled !== null
|
||||
const suspense = enabled === false ? false : options?.suspense
|
||||
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady || (isServer && suspenseEnabled)
|
||||
const enhancedResolverRpcClient = sanitizeQuery(queryFn)
|
||||
const queryKey = getQueryKey(queryFn, params)
|
||||
|
||||
const {data, ...queryRest} = useReactQuery({
|
||||
queryKey: routerIsReady ? queryKey : ["_routerNotReady_"],
|
||||
queryFn: routerIsReady
|
||||
? () => enhancedResolverRpcClient(params, {fromQueryHook: true})
|
||||
: (emptyQueryFn as any),
|
||||
...options,
|
||||
keepPreviousData: true,
|
||||
enabled,
|
||||
})
|
||||
|
||||
if (
|
||||
queryRest.isIdle &&
|
||||
isServer &&
|
||||
suspenseEnabled !== false &&
|
||||
!data &&
|
||||
(!options || !("suspense" in options) || options.suspense) &&
|
||||
(!options || !("enabled" in options) || options.enabled)
|
||||
) {
|
||||
throw new Promise(() => {})
|
||||
}
|
||||
|
||||
const rest = {
|
||||
...queryRest,
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, params),
|
||||
}
|
||||
|
||||
// return [data, rest as RestPaginatedResult<TResult>]
|
||||
return [data, rest]
|
||||
}
|
||||
|
||||
// -------------------------
|
||||
// useInfiniteQuery
|
||||
// -------------------------
|
||||
interface RestInfiniteResult<TResult, TError>
|
||||
extends Omit<UseInfiniteQueryResult<TResult, TError>, "data">,
|
||||
QueryCacheFunctions<TResult> {
|
||||
pageParams: any
|
||||
}
|
||||
|
||||
interface InfiniteQueryConfig<TResult, TError, TSelectedData>
|
||||
extends UseInfiniteQueryOptions<TResult, TError, TSelectedData, TResult> {
|
||||
// getPreviousPageParam?: (lastPage: TResult, allPages: TResult[]) => TGetPageParamResult
|
||||
// getNextPageParam?: (lastPage: TResult, allPages: TResult[]) => TGetPageParamResult
|
||||
}
|
||||
|
||||
export function useInfiniteQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
getQueryParams: (pageParam: any) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TError, TSelectedData> & QueryNonLazyOptions,
|
||||
): [TSelectedData[], RestInfiniteResult<TSelectedData, TError>]
|
||||
export function useInfiniteQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
getQueryParams: (pageParam: any) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TError, TSelectedData> & QueryLazyOptions,
|
||||
): [TSelectedData[] | undefined, RestInfiniteResult<TSelectedData, TError>]
|
||||
export function useInfiniteQuery<
|
||||
T extends AsyncFunc,
|
||||
TResult = PromiseReturnType<T>,
|
||||
TError = unknown,
|
||||
TSelectedData = TResult,
|
||||
>(
|
||||
queryFn: T,
|
||||
getQueryParams: (pageParam: any) => FirstParam<T>,
|
||||
options: InfiniteQueryConfig<TResult, TError, TSelectedData>,
|
||||
) {
|
||||
if (typeof queryFn === "undefined") {
|
||||
throw new Error("useInfiniteQuery is missing the first argument - it must be a query function")
|
||||
}
|
||||
|
||||
const suspenseEnabled = Boolean(globalThis.__BLITZ_SUSPENSE_ENABLED)
|
||||
let enabled = isServer && suspenseEnabled ? false : options?.enabled ?? options?.enabled !== null
|
||||
const suspense = enabled === false ? false : options?.suspense
|
||||
const session = useSession({suspense})
|
||||
if (session.isLoading) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
const routerIsReady = useRouter().isReady || (isServer && suspenseEnabled)
|
||||
const enhancedResolverRpcClient = sanitizeQuery(queryFn)
|
||||
const queryKey = getInfiniteQueryKey(queryFn, getQueryParams)
|
||||
|
||||
const {data, ...queryRest} = useInfiniteReactQuery({
|
||||
// we need an extra cache key for infinite loading so that the cache for
|
||||
// for this query is stored separately since the hook result is an array of results.
|
||||
// Without this cache for usePaginatedQuery and this will conflict and break.
|
||||
queryKey: routerIsReady ? queryKey : ["_routerNotReady_"],
|
||||
queryFn: routerIsReady
|
||||
? ({pageParam}) =>
|
||||
enhancedResolverRpcClient(getQueryParams(pageParam), {
|
||||
fromQueryHook: true,
|
||||
})
|
||||
: (emptyQueryFn as any),
|
||||
...options,
|
||||
enabled,
|
||||
})
|
||||
|
||||
if (
|
||||
queryRest.isIdle &&
|
||||
isServer &&
|
||||
suspenseEnabled !== false &&
|
||||
!data &&
|
||||
(!options || !("suspense" in options) || options.suspense) &&
|
||||
(!options || !("enabled" in options) || options.enabled)
|
||||
) {
|
||||
throw new Promise(() => {})
|
||||
}
|
||||
|
||||
const rest = {
|
||||
...queryRest,
|
||||
...getQueryCacheFunctions<FirstParam<T>, TResult, T>(queryFn, getQueryParams),
|
||||
pageParams: data?.pageParams,
|
||||
}
|
||||
|
||||
return [data?.pages as any, rest]
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// useMutation
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* We have to override react-query's MutationFunction and MutationResultPair
|
||||
* types so because we have throwOnError:true by default. And by the RQ types
|
||||
* have the mutate function result typed as TData|undefined which isn't typed
|
||||
* properly with throwOnError.
|
||||
*
|
||||
* So this fixes that.
|
||||
*/
|
||||
export declare type MutateFunction<
|
||||
TData,
|
||||
TError = unknown,
|
||||
TVariables = unknown,
|
||||
TContext = unknown,
|
||||
> = (
|
||||
variables?: TVariables,
|
||||
config?: MutateOptions<TData, TError, TVariables, TContext>,
|
||||
) => Promise<TData>
|
||||
|
||||
export declare type MutationResultPair<TData, TError, TVariables, TContext> = [
|
||||
MutateFunction<TData, TError, TVariables, TContext>,
|
||||
Omit<UseMutationResult<TData, TError>, "mutate" | "mutateAsync">,
|
||||
]
|
||||
|
||||
export declare type MutationFunction<TData, TVariables = unknown> = (
|
||||
variables: TVariables,
|
||||
ctx?: any,
|
||||
) => Promise<TData>
|
||||
|
||||
export function useMutation<
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
TVariables = void,
|
||||
TContext = unknown,
|
||||
>(
|
||||
mutationResolver: MutationFunction<TData, TVariables>,
|
||||
config?: UseMutationOptions<TData, TError, TVariables, TContext>,
|
||||
): MutationResultPair<TData, TError, TVariables, TContext> {
|
||||
const enhancedResolverRpcClient = sanitizeMutation(mutationResolver)
|
||||
|
||||
const {mutate, mutateAsync, ...rest} = useReactQueryMutation<TData, TError, TVariables, TContext>(
|
||||
(variables) => enhancedResolverRpcClient(variables, {fromQueryHook: true}),
|
||||
{
|
||||
throwOnError: true,
|
||||
...config,
|
||||
} as any,
|
||||
)
|
||||
|
||||
return [mutateAsync, rest] as MutationResultPair<TData, TError, TVariables, TContext>
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
import {normalizePathTrailingSlash} from "next/dist/client/normalize-trailing-slash"
|
||||
import {addBasePath} from "next/dist/shared/lib/router/router"
|
||||
import {deserialize, serialize} from "superjson"
|
||||
import {SuperJSONResult} from "superjson/dist/types"
|
||||
import {isServer, CSRFTokenMismatchError} from "blitz"
|
||||
import {getQueryKeyFromUrlAndParams, queryClient} from "./react-query-utils"
|
||||
import {
|
||||
getAntiCSRFToken,
|
||||
getPublicDataStore,
|
||||
HEADER_CSRF,
|
||||
HEADER_CSRF_ERROR,
|
||||
HEADER_PUBLIC_DATA_TOKEN,
|
||||
HEADER_SESSION_CREATED,
|
||||
} from "@blitzjs/auth"
|
||||
|
||||
export function normalizeApiRoute(path: string): string {
|
||||
return normalizePathTrailingSlash(addBasePath(path))
|
||||
}
|
||||
|
||||
export type ResolverType = "query" | "mutation"
|
||||
|
||||
export interface BuildRpcClientParams {
|
||||
resolverName: string
|
||||
resolverType: ResolverType
|
||||
routePath: string
|
||||
}
|
||||
|
||||
export interface RpcOptions {
|
||||
fromQueryHook?: boolean
|
||||
fromInvoke?: boolean
|
||||
alreadySerialized?: boolean
|
||||
}
|
||||
|
||||
export interface EnhancedRpc {
|
||||
_isRpcClient: true
|
||||
_resolverType: ResolverType
|
||||
_resolverName: string
|
||||
_routePath: string
|
||||
}
|
||||
|
||||
export interface RpcClientBase<Input = unknown, Result = unknown> {
|
||||
(params: Input, opts?: RpcOptions): Promise<Result>
|
||||
}
|
||||
|
||||
export interface RpcClient<Input = unknown, Result = unknown>
|
||||
extends EnhancedRpc,
|
||||
RpcClientBase<Input, Result> {}
|
||||
|
||||
// export interface RpcResolver<Input = unknown, Result = unknown> extends EnhancedRpc {
|
||||
// (params: Input, ctx?: Ctx): Promise<Result>
|
||||
// }
|
||||
|
||||
export function __internal_buildRpcClient({
|
||||
resolverName,
|
||||
resolverType,
|
||||
routePath,
|
||||
}: BuildRpcClientParams): RpcClient {
|
||||
const fullRoutePath = normalizeApiRoute("/api/rpc/" + routePath)
|
||||
|
||||
const httpClient: RpcClientBase = async (params, opts = {}) => {
|
||||
const debug = (await import("debug")).default("blitz:rpc")
|
||||
if (!opts.fromQueryHook && !opts.fromInvoke) {
|
||||
console.warn(
|
||||
"[Deprecation] Directly calling queries/mutations is deprecated in favor of invoke(queryFn, params)",
|
||||
)
|
||||
}
|
||||
|
||||
if (isServer) {
|
||||
return Promise.resolve() as unknown
|
||||
}
|
||||
debug("Starting request for", fullRoutePath, "with", params, "and", opts)
|
||||
|
||||
const headers: Record<string, any> = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
const antiCSRFToken = getAntiCSRFToken()
|
||||
if (antiCSRFToken) {
|
||||
debug("Adding antiCSRFToken cookie header", antiCSRFToken)
|
||||
headers[HEADER_CSRF] = antiCSRFToken
|
||||
} else {
|
||||
debug("No antiCSRFToken cookie found")
|
||||
}
|
||||
|
||||
let serialized: SuperJSONResult
|
||||
if (opts.alreadySerialized) {
|
||||
// params is already serialized with superjson when it gets here
|
||||
// We have to serialize the params before passing to react-query in the query key
|
||||
// because otherwise react-query will use JSON.parse(JSON.stringify)
|
||||
// so by the time the arguments come here the real JS objects are lost
|
||||
serialized = params as unknown as SuperJSONResult
|
||||
} else {
|
||||
serialized = serialize(params)
|
||||
}
|
||||
|
||||
// Create a new AbortController instance for this request
|
||||
const controller = new AbortController()
|
||||
|
||||
const promise = window
|
||||
.fetch(fullRoutePath, {
|
||||
method: "POST",
|
||||
headers,
|
||||
credentials: "include",
|
||||
redirect: "follow",
|
||||
body: JSON.stringify({
|
||||
params: serialized.json,
|
||||
meta: {
|
||||
params: serialized.meta,
|
||||
},
|
||||
}),
|
||||
signal: controller.signal,
|
||||
})
|
||||
.then(async (response) => {
|
||||
debug("Received request for", routePath)
|
||||
if (response.headers) {
|
||||
if (response.headers.get(HEADER_PUBLIC_DATA_TOKEN)) {
|
||||
getPublicDataStore().updateState()
|
||||
debug("Public data updated")
|
||||
}
|
||||
if (response.headers.get(HEADER_SESSION_CREATED)) {
|
||||
// This also runs on logout, because on logout a new anon session is created
|
||||
debug("Session created")
|
||||
setTimeout(async () => {
|
||||
// Do these in the next tick to prevent various bugs like https://github.com/blitz-js/blitz/issues/2207
|
||||
debug("Invalidating react-query cache...")
|
||||
await queryClient.cancelQueries()
|
||||
await queryClient.resetQueries()
|
||||
queryClient.getMutationCache().clear()
|
||||
// We have a 100ms delay here to prevent unnecessary stale queries from running
|
||||
// This prevents the case where you logout on a page with
|
||||
// Page.authenticate = {redirectTo: '/login'}
|
||||
// Without this delay, queries that require authentication on the original page
|
||||
// will still run (but fail because you are now logged out)
|
||||
// Ref: https://github.com/blitz-js/blitz/issues/1935
|
||||
}, 100)
|
||||
}
|
||||
if (response.headers.get(HEADER_CSRF_ERROR)) {
|
||||
const err = new CSRFTokenMismatchError()
|
||||
err.stack = null!
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(response.statusText)
|
||||
;(error as any).statusCode = response.status
|
||||
;(error as any).path = routePath
|
||||
error.stack = null!
|
||||
throw error
|
||||
} else {
|
||||
let payload
|
||||
try {
|
||||
payload = await response.json()
|
||||
} catch (error) {
|
||||
const err = new Error(`Failed to parse json from ${routePath}`)
|
||||
err.stack = null!
|
||||
throw err
|
||||
}
|
||||
|
||||
if (payload.error) {
|
||||
let error = deserialize({
|
||||
json: payload.error,
|
||||
meta: payload.meta?.error,
|
||||
}) as any
|
||||
// We don't clear the publicDataStore for anonymous users,
|
||||
// because there is not sensitive data
|
||||
if (error.name === "AuthenticationError" && getPublicDataStore().getData().userId) {
|
||||
getPublicDataStore().clear()
|
||||
}
|
||||
|
||||
const prismaError = error.message.match(/invalid.*prisma.*invocation/i)
|
||||
if (prismaError && !("code" in error)) {
|
||||
error = new Error(prismaError[0])
|
||||
error.statusCode = 500
|
||||
}
|
||||
|
||||
error.stack = null
|
||||
throw error
|
||||
} else {
|
||||
const data = deserialize({
|
||||
json: payload.result,
|
||||
meta: payload.meta?.result,
|
||||
})
|
||||
|
||||
if (!opts.fromQueryHook) {
|
||||
const queryKey = getQueryKeyFromUrlAndParams(routePath, params)
|
||||
queryClient.setQueryData(queryKey, data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
const rpcClient = httpClient as RpcClient
|
||||
|
||||
rpcClient._isRpcClient = true
|
||||
rpcClient._resolverName = resolverName
|
||||
rpcClient._resolverType = resolverType
|
||||
rpcClient._routePath = fullRoutePath
|
||||
|
||||
return rpcClient
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import {QueryClient} from "react-query"
|
||||
|
||||
declare global {
|
||||
var queryClient: QueryClient
|
||||
var __BLITZ_SUSPENSE_ENABLED: boolean
|
||||
}
|
||||
@@ -1,47 +1 @@
|
||||
import "./global"
|
||||
import {createClientPlugin} from "blitz"
|
||||
import {DefaultOptions, QueryClient} from "react-query"
|
||||
|
||||
export * from "./data-client/index"
|
||||
|
||||
export const queryClient = globalThis.queryClient
|
||||
|
||||
interface BlitzRpcOptions {
|
||||
reactQueryOptions?: DefaultOptions
|
||||
}
|
||||
export const BlitzRpcPlugin = createClientPlugin<BlitzRpcOptions, any>(
|
||||
({reactQueryOptions}: BlitzRpcOptions) => {
|
||||
const initializeQueryClient = () => {
|
||||
let suspenseEnabled = reactQueryOptions?.queries?.suspense ?? true
|
||||
if (!process.env.CLI_COMMAND_CONSOLE && !process.env.CLI_COMMAND_DB) {
|
||||
globalThis.__BLITZ_SUSPENSE_ENABLED = suspenseEnabled
|
||||
}
|
||||
|
||||
return new QueryClient({
|
||||
defaultOptions: {
|
||||
...reactQueryOptions,
|
||||
queries: {
|
||||
...(typeof window === "undefined" && {cacheTime: 0}),
|
||||
retry: (failureCount, error: any) => {
|
||||
if (process.env.NODE_ENV !== "production") return false
|
||||
|
||||
// Retry (max. 3 times) only if network error detected
|
||||
if (error.message === "Network request failed" && failureCount <= 3) return true
|
||||
|
||||
return false
|
||||
},
|
||||
...reactQueryOptions?.queries,
|
||||
suspense: suspenseEnabled,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
globalThis.queryClient = initializeQueryClient()
|
||||
return {
|
||||
events: {},
|
||||
middleware: {},
|
||||
exports: () => {},
|
||||
}
|
||||
},
|
||||
)
|
||||
export const todo = true
|
||||
|
||||
@@ -1,216 +1,3 @@
|
||||
import {assert, Ctx, baseLogger, prettyMs, newLine} from "blitz"
|
||||
import {NextApiRequest, NextApiResponse} from "next"
|
||||
import {deserialize, serialize as superjsonSerialize} from "superjson"
|
||||
import chalk from "chalk"
|
||||
|
||||
// TODO - optimize end user server bundles by not exporting all client stuff here
|
||||
export * from "./index-browser"
|
||||
|
||||
// Mechanism used by Vite/Next/Nuxt plugins for automatically loading query and mutation resolvers
|
||||
function isObject(value: unknown): value is Record<string | symbol, unknown> {
|
||||
return typeof value === "object" && value !== null
|
||||
}
|
||||
function getGlobalObject<T extends Record<string, unknown>>(key: string, defaultValue: T): T {
|
||||
assert(key.startsWith("__internal_blitz"), "unsupported key")
|
||||
if (typeof global === "undefined") {
|
||||
return defaultValue
|
||||
}
|
||||
assert(isObject(global), "not an object")
|
||||
return ((global as Record<string, unknown>)[key] =
|
||||
((global as Record<string, unknown>)[key] as T) || defaultValue)
|
||||
}
|
||||
|
||||
type Resolver = (...args: unknown[]) => Promise<unknown>
|
||||
type ResolverFiles = Record<string, () => Promise<{default?: Resolver}>>
|
||||
|
||||
// We define `global.__internal_blitzRpcResolverFiles` to ensure we use the same global object.
|
||||
// Needed for Next.js. I'm guessing that Next.js is including the `node_modules/` files in a seperate bundle than user files.
|
||||
const g = getGlobalObject<{blitzRpcResolverFilesLoaded: ResolverFiles | null}>(
|
||||
"__internal_blitzRpcResolverFiles",
|
||||
{
|
||||
blitzRpcResolverFilesLoaded: null,
|
||||
},
|
||||
)
|
||||
|
||||
export function loadBlitzRpcResolverFilesWithInternalMechanism() {
|
||||
return g.blitzRpcResolverFilesLoaded
|
||||
}
|
||||
|
||||
export function __internal_addBlitzRpcResolver(
|
||||
routePath: string,
|
||||
resolver: () => Promise<{default?: Resolver}>,
|
||||
) {
|
||||
g.blitzRpcResolverFilesLoaded = g.blitzRpcResolverFilesLoaded || {}
|
||||
g.blitzRpcResolverFilesLoaded[routePath] = resolver
|
||||
return resolver
|
||||
}
|
||||
|
||||
import {resolve} from "path"
|
||||
const dir = __dirname + (() => "")() // trick to avoid `@vercel/ncc` to glob import
|
||||
const loaderServer = resolve(dir, "./loader-server.cjs")
|
||||
const loaderClient = resolve(dir, "./loader-client.cjs")
|
||||
|
||||
export function installWebpackConfig<T extends any[]>(config: {module?: {rules?: T}}) {
|
||||
config.module!.rules!.push({
|
||||
test: /\/\[\[\.\.\.blitz]]\.[jt]s$/,
|
||||
use: [{loader: loaderServer}],
|
||||
})
|
||||
config.module!.rules!.push({
|
||||
test: /[\\/](queries|mutations)[\\/]/,
|
||||
use: [{loader: loaderClient}],
|
||||
})
|
||||
}
|
||||
|
||||
// ----------
|
||||
// END LOADER
|
||||
// ----------
|
||||
|
||||
async function getResolverMap(): Promise<ResolverFiles | null | undefined> {
|
||||
// Handles:
|
||||
// - Next.js
|
||||
// - Nuxt
|
||||
// - Vite with `importBuild.js`
|
||||
{
|
||||
const resolverFilesLoaded = loadBlitzRpcResolverFilesWithInternalMechanism()
|
||||
if (resolverFilesLoaded) {
|
||||
return resolverFilesLoaded
|
||||
}
|
||||
}
|
||||
|
||||
// Handles:
|
||||
// - Vite
|
||||
// {
|
||||
// const {resolverFilesLoaded, viteProvider} = await loadTelefuncFilesWithVite(runContext)
|
||||
// if (resolverFilesLoaded) {
|
||||
// assertUsage(
|
||||
// Object.keys(resolverFilesLoaded).length > 0,
|
||||
// getErrMsg(`Vite [\`${viteProvider}\`]`),
|
||||
// )
|
||||
// return resolverFilesLoaded
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
interface RpcConfig {
|
||||
onError?: (error: Error) => void
|
||||
}
|
||||
|
||||
export function rpcHandler(config: RpcConfig) {
|
||||
return async function handleRpcRequest(req: NextApiRequest, res: NextApiResponse, ctx: Ctx) {
|
||||
const resolverMap = await getResolverMap()
|
||||
assert(resolverMap, "No query or mutation resolvers found")
|
||||
assert(
|
||||
Array.isArray(req.query.blitz),
|
||||
"It seems your Blitz RPC endpoint file is not named [[...blitz]].(jt)s. Please ensure it is",
|
||||
)
|
||||
|
||||
const relativeRoutePath = req.query.blitz.join("/")
|
||||
const routePath = "/" + relativeRoutePath
|
||||
|
||||
const loadableResolver = resolverMap[routePath]
|
||||
if (!loadableResolver) {
|
||||
throw new Error("No resolver for path: " + routePath)
|
||||
}
|
||||
|
||||
const resolver = (await loadableResolver()).default
|
||||
if (!resolver) {
|
||||
throw new Error("No default export for resolver path: " + routePath)
|
||||
}
|
||||
|
||||
const log = baseLogger().getChildLogger({
|
||||
prefix: [relativeRoutePath + "()"],
|
||||
})
|
||||
|
||||
const customChalk = new chalk.Instance({
|
||||
level: log.settings.type === "json" ? 0 : chalk.level,
|
||||
})
|
||||
|
||||
if (req.method === "HEAD") {
|
||||
// We used to initiate database connection here
|
||||
res.status(200).end()
|
||||
return
|
||||
} else if (req.method === "POST") {
|
||||
// Handle RPC call
|
||||
|
||||
if (typeof req.body.params === "undefined") {
|
||||
const error = {message: "Request body is missing the `params` key"}
|
||||
log.error(error.message)
|
||||
res.status(400).json({
|
||||
result: null,
|
||||
error,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const data = deserialize({
|
||||
json: req.body.params,
|
||||
meta: req.body.meta?.params,
|
||||
})
|
||||
|
||||
log.info(customChalk.dim("Starting with input:"), data ? data : JSON.stringify(data))
|
||||
const startTime = Date.now()
|
||||
const result = await resolver(data, (res as any).blitzCtx)
|
||||
const resolverDuration = Date.now() - startTime
|
||||
log.debug(customChalk.dim("Result:"), result ? result : JSON.stringify(result))
|
||||
|
||||
const serializerStartTime = Date.now()
|
||||
const serializedResult = superjsonSerialize(result)
|
||||
|
||||
const nextSerializerStartTime = Date.now()
|
||||
;(res as any).blitzResult = result
|
||||
res.json({
|
||||
result: serializedResult.json,
|
||||
error: null,
|
||||
meta: {
|
||||
result: serializedResult.meta,
|
||||
},
|
||||
})
|
||||
log.debug(
|
||||
customChalk.dim(
|
||||
`Next.js serialization:${prettyMs(Date.now() - nextSerializerStartTime)}`,
|
||||
),
|
||||
)
|
||||
const serializerDuration = Date.now() - serializerStartTime
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
log.info(
|
||||
customChalk.dim(
|
||||
`Finished: resolver:${prettyMs(resolverDuration)} serializer:${prettyMs(
|
||||
serializerDuration,
|
||||
)} total:${prettyMs(duration)}`,
|
||||
),
|
||||
)
|
||||
newLine()
|
||||
|
||||
return
|
||||
} catch (error: any) {
|
||||
if (error._clearStack) {
|
||||
delete error.stack
|
||||
}
|
||||
log.error(error)
|
||||
newLine()
|
||||
|
||||
if (!error.statusCode) {
|
||||
error.statusCode = 500
|
||||
}
|
||||
|
||||
const serializedError = superjsonSerialize(error)
|
||||
|
||||
res.json({
|
||||
result: null,
|
||||
error: serializedError.json,
|
||||
meta: {
|
||||
error: serializedError.meta,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Everything else is error
|
||||
log.warn(`${req.method} method not supported`)
|
||||
res.status(404).end()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
export const todoServer = true
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
import {
|
||||
assertPosixPath,
|
||||
convertFilePathToResolverName,
|
||||
convertFilePathToResolverType,
|
||||
convertPageFilePathToRoutePath,
|
||||
toPosixPath,
|
||||
} from "./loader-utils"
|
||||
import {assert} from "blitz"
|
||||
import {posix} from "path"
|
||||
|
||||
// Subset of `import type { LoaderDefinitionFunction } from 'webpack'`
|
||||
type Loader = {
|
||||
_compiler?: {
|
||||
name: string
|
||||
context: string
|
||||
}
|
||||
resource: string
|
||||
cacheable: (enabled: boolean) => void
|
||||
}
|
||||
|
||||
export async function loader(this: Loader, input: string): Promise<string> {
|
||||
const compiler = this._compiler!
|
||||
const id = this.resource
|
||||
const root = this._compiler!.context
|
||||
|
||||
const isSSR = compiler.name === "server"
|
||||
if (!isSSR) {
|
||||
const code = await transformBlitzRpcResolverClient(input, toPosixPath(id), toPosixPath(root))
|
||||
return code
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
module.exports = loader
|
||||
|
||||
export async function transformBlitzRpcResolverClient(_src: string, id: string, root: string) {
|
||||
assertPosixPath(id)
|
||||
assertPosixPath(root)
|
||||
|
||||
const resolverFilePath = "/" + posix.relative(root, id)
|
||||
assertPosixPath(resolverFilePath)
|
||||
const routePath = convertPageFilePathToRoutePath(resolverFilePath)
|
||||
const resolverName = convertFilePathToResolverName(resolverFilePath)
|
||||
const resolverType = convertFilePathToResolverType(resolverFilePath)
|
||||
|
||||
const code = `
|
||||
// @ts-nocheck
|
||||
import { __internal_buildRpcClient } from "@blitzjs/rpc";
|
||||
export default __internal_buildRpcClient({
|
||||
resolverName: "${resolverName}",
|
||||
resolverType: "${resolverType}",
|
||||
routePath: "${routePath}",
|
||||
});
|
||||
`
|
||||
|
||||
return code
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import {posix, join, dirname} from "path"
|
||||
import {promises} from "fs"
|
||||
import {
|
||||
assertPosixPath,
|
||||
toPosixPath,
|
||||
buildPageExtensionRegex,
|
||||
getIsRpcFile,
|
||||
topLevelFoldersThatMayContainResolvers,
|
||||
convertPageFilePathToRoutePath,
|
||||
} from "./loader-utils"
|
||||
|
||||
// Subset of `import type { LoaderDefinitionFunction } from 'webpack'`
|
||||
type Loader = {
|
||||
_compiler?: {
|
||||
name: string
|
||||
context: string
|
||||
}
|
||||
resource: string
|
||||
cacheable: (enabled: boolean) => void
|
||||
}
|
||||
|
||||
export async function loader(this: Loader, input: string): Promise<string> {
|
||||
const compiler = this._compiler!
|
||||
const id = this.resource
|
||||
const root = this._compiler!.context
|
||||
|
||||
const isSSR = compiler.name === "server"
|
||||
if (isSSR) {
|
||||
this.cacheable(false)
|
||||
|
||||
const resolvers = await collectResolvers(root, ["ts", "js"])
|
||||
const code = await transformBlitzRpcServer(input, toPosixPath(id), toPosixPath(root), resolvers)
|
||||
return code
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
module.exports = loader
|
||||
|
||||
export async function transformBlitzRpcServer(
|
||||
src: string,
|
||||
id: string,
|
||||
root: string,
|
||||
resolvers: string[],
|
||||
) {
|
||||
assertPosixPath(id)
|
||||
assertPosixPath(root)
|
||||
|
||||
const blitzImport = 'import { __internal_addBlitzRpcResolver } from "@blitzjs/rpc";'
|
||||
|
||||
// No break line between `blitzImport` and `src` in order to preserve the source map's line mapping
|
||||
let code = blitzImport + src
|
||||
code += "\n\n"
|
||||
|
||||
for (let resolverFilePath of resolvers) {
|
||||
const relativeResolverPath = posix.relative(dirname(id), join(root, resolverFilePath))
|
||||
const routePath = convertPageFilePathToRoutePath(resolverFilePath)
|
||||
code += `__internal_addBlitzRpcResolver('${routePath}', () => import('${relativeResolverPath}'));`
|
||||
code += "\n"
|
||||
}
|
||||
|
||||
// console.log("NEW CODE", code)
|
||||
return code
|
||||
}
|
||||
|
||||
export function collectResolvers(directory: string, pageExtensions: string[]): Promise<string[]> {
|
||||
return recursiveFindResolvers(directory, buildPageExtensionRegex(pageExtensions))
|
||||
}
|
||||
|
||||
export async function recursiveFindResolvers(
|
||||
dir: string,
|
||||
filter: RegExp,
|
||||
ignore?: RegExp,
|
||||
arr: string[] = [],
|
||||
rootDir: string = dir,
|
||||
): Promise<string[]> {
|
||||
let folders = await promises.readdir(dir)
|
||||
|
||||
if (dir === rootDir) {
|
||||
folders = folders.filter((folder) => topLevelFoldersThatMayContainResolvers.includes(folder))
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
folders.map(async (part: string) => {
|
||||
const absolutePath = join(dir, part)
|
||||
if (ignore && ignore.test(part)) return
|
||||
|
||||
const pathStat = await promises.stat(absolutePath)
|
||||
|
||||
if (pathStat.isDirectory()) {
|
||||
await recursiveFindResolvers(absolutePath, filter, ignore, arr, rootDir)
|
||||
return
|
||||
}
|
||||
|
||||
if (!filter.test(part)) {
|
||||
return
|
||||
}
|
||||
|
||||
const relativeFromRoot = absolutePath.replace(rootDir, "")
|
||||
if (getIsRpcFile(relativeFromRoot)) {
|
||||
arr.push(relativeFromRoot)
|
||||
return
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
return arr.sort()
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import {assert} from "blitz"
|
||||
import {win32, posix, sep} from "path"
|
||||
|
||||
export function assertPosixPath(path: string) {
|
||||
const errMsg = `Wrongly formatted path: ${path}`
|
||||
assert(!path.includes(win32.sep), errMsg)
|
||||
// assert(path.startsWith('/'), errMsg)
|
||||
}
|
||||
|
||||
export function toPosixPath(path: string) {
|
||||
if (process.platform !== "win32") {
|
||||
assert(sep === posix.sep, "TODO")
|
||||
assertPosixPath(path)
|
||||
return path
|
||||
} else {
|
||||
assert(sep === win32.sep, "TODO")
|
||||
const pathPosix = path.split(win32.sep).join(posix.sep)
|
||||
assertPosixPath(pathPosix)
|
||||
return pathPosix
|
||||
}
|
||||
}
|
||||
|
||||
export function toSystemPath(path: string) {
|
||||
path = path.split(posix.sep).join(sep)
|
||||
path = path.split(win32.sep).join(sep)
|
||||
return path
|
||||
}
|
||||
|
||||
export const topLevelFoldersThatMayContainResolvers = ["src", "app", "integrations"]
|
||||
|
||||
export function buildPageExtensionRegex(pageExtensions: string[]) {
|
||||
return new RegExp(`(?<!\\.test|\\.spec)\\.(?:${pageExtensions.join("|")})$`)
|
||||
}
|
||||
|
||||
const fileExtensionRegex = /\.([a-z]+)$/
|
||||
|
||||
export function convertPageFilePathToRoutePath(filePath: string) {
|
||||
return filePath
|
||||
.replace(/^.*?[\\/]queries[\\/]/, "/")
|
||||
.replace(/^.*?[\\/]mutations[\\/]/, "/")
|
||||
.replace(fileExtensionRegex, "")
|
||||
}
|
||||
|
||||
export function convertFilePathToResolverName(filePathFromAppRoot: string) {
|
||||
return filePathFromAppRoot
|
||||
.replace(/^.*[\\/](queries|mutations)[\\/]/, "")
|
||||
.replace(fileExtensionRegex, "")
|
||||
}
|
||||
|
||||
export function convertFilePathToResolverType(filePathFromAppRoot: string) {
|
||||
return filePathFromAppRoot.match(/[\\/]queries[\\/]/) ? "query" : "mutation"
|
||||
}
|
||||
|
||||
export function getIsRpcFile(filePathFromAppRoot: string) {
|
||||
return (
|
||||
/[\\/]queries[\\/]/.test(filePathFromAppRoot) || /[\\/]mutations[\\/]/.test(filePathFromAppRoot)
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"extends": "@blitzjs/config/tsconfig.library.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "ES2015"]
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# blitz
|
||||
|
||||
## 2.0.0-alpha.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- fix more cli problems
|
||||
- @blitzjs/generator@2.0.0-alpha.4
|
||||
|
||||
## 2.0.0-alpha.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- downgrade pkg-dir to non-esm only version
|
||||
- @blitzjs/generator@2.0.0-alpha.3
|
||||
|
||||
## 2.0.0-alpha.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @blitzjs/generator@2.0.0-alpha.2
|
||||
|
||||
## 2.0.0-alpha.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 46a34c7b: initial publish
|
||||
- Updated dependencies [46a34c7b]
|
||||
- @blitzjs/generator@2.0.0-alpha.1
|
||||
@@ -2,7 +2,15 @@ import {BuildConfig} from "unbuild"
|
||||
|
||||
const config: BuildConfig = {
|
||||
entries: ["./src/index-browser", "./src/index-server", "./src/cli/index"],
|
||||
externals: ["index-browser.cjs", "index-browser.mjs", "zod"],
|
||||
externals: [
|
||||
"index-browser.cjs",
|
||||
"index-browser.mjs",
|
||||
"react",
|
||||
"chalk",
|
||||
"console-table-printer",
|
||||
"tslog",
|
||||
"ora",
|
||||
],
|
||||
declaration: true,
|
||||
rollup: {
|
||||
emitCJS: true,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user