Major internal refactor + add multiple entry points + change all imports to be from blitz + rename getSessionContext to getSession + other fixes (#2039)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -28,3 +28,4 @@ dist
|
||||
examples/auth2
|
||||
.idea
|
||||
.ultra.cache.json
|
||||
db.sqlite-journal
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn manypkg fix
|
||||
yarn lint-staged
|
||||
yarn pretty-quick --staged
|
||||
|
||||
@@ -8,8 +8,8 @@ beforeEach(async () => {
|
||||
})
|
||||
|
||||
const generatedToken = "plain-token"
|
||||
jest.mock("blitz", () => ({
|
||||
...jest.requireActual("blitz")!,
|
||||
jest.mock("@blitzjs/core/server", () => ({
|
||||
...jest.requireActual("@blitzjs/core/server")!,
|
||||
generateToken: () => generatedToken,
|
||||
}))
|
||||
jest.mock("preview-email", () => jest.fn())
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import {render} from "test/utils"
|
||||
import Home from "./index"
|
||||
|
||||
jest.mock("blitz", () => ({
|
||||
...jest.requireActual("blitz")!,
|
||||
jest.mock("@blitzjs/core", () => ({
|
||||
...jest.requireActual("@blitzjs/core")!,
|
||||
useQuery: () => [
|
||||
{
|
||||
id: 1,
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
import {FC} from "react"
|
||||
import {getSessionContext} from "@blitzjs/server"
|
||||
import {
|
||||
getSession,
|
||||
invokeWithMiddleware,
|
||||
useRouter,
|
||||
GetServerSideProps,
|
||||
PromiseReturnType,
|
||||
ErrorComponent as ErrorPage,
|
||||
useMutation,
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
GetServerSideProps,
|
||||
InferGetServerSidePropsType,
|
||||
BlitzPage,
|
||||
} from "blitz"
|
||||
import getUser from "app/users/queries/getUser"
|
||||
import logout from "app/auth/mutations/logout"
|
||||
import path from "path"
|
||||
|
||||
type PageProps = {
|
||||
user?: PromiseReturnType<typeof getUser>
|
||||
error?: {
|
||||
statusCode: number
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, res}) => {
|
||||
export const getServerSideProps: GetServerSideProps = async ({req, res}) => {
|
||||
// Ensure these files are not eliminated by trace-based tree-shaking (like Vercel)
|
||||
// https://github.com/blitz-js/blitz/issues/794
|
||||
path.resolve("next.config.js")
|
||||
@@ -30,7 +22,7 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, re
|
||||
path.resolve(".next/blitz/db.js")
|
||||
// End anti-tree-shaking
|
||||
|
||||
const session = await getSessionContext(req, res)
|
||||
const session = await getSession(req, res)
|
||||
console.log("Session id:", session.userId)
|
||||
try {
|
||||
const user = await invokeWithMiddleware(
|
||||
@@ -62,7 +54,7 @@ export const getServerSideProps: GetServerSideProps<PageProps> = async ({req, re
|
||||
}
|
||||
}
|
||||
|
||||
const Test: FC<PageProps> = ({user, error}: PageProps) => {
|
||||
const Test: BlitzPage<InferGetServerSidePropsType<typeof getServerSideProps>> = ({user, error}) => {
|
||||
const router = useRouter()
|
||||
const [logoutMutation] = useMutation(logout)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
||||
enabled: process.env.ANALYZE === "true",
|
||||
|
||||
@@ -53,6 +53,7 @@ describe("index page", () => {
|
||||
cy.contains("button", "Logout").click()
|
||||
|
||||
cy.location("pathname").should("equal", "/")
|
||||
cy.wait(1000)
|
||||
cy.contains("a", /login/i)
|
||||
})
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx .",
|
||||
"analyze": "cross-env ANALYZE=true blitz build",
|
||||
"cy:open": "cypress open",
|
||||
"cy:run": "cypress run || cypress run",
|
||||
"cy:run": "cypress run --browser chrome",
|
||||
"test": "prisma generate && yarn test:jest && yarn test:e2e",
|
||||
"test:jest": "jest",
|
||||
"test:server": "cross-env NODE_ENV=test blitz prisma migrate deploy --preview-feature && blitz build && cross-env NODE_ENV=test blitz start -p 3099",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"downlevelIteration": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"exclude": ["node_modules", "cypress"],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {DefaultCtx, SessionContext} from "blitz"
|
||||
import {SimpleRolesIsAuthorized} from "@blitzjs/server"
|
||||
import {DefaultCtx, SessionContext, SimpleRolesIsAuthorized} from "blitz"
|
||||
import {User} from "db"
|
||||
|
||||
export type Role = "ADMIN" | "USER"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
|
||||
module.exports = withMonorepoBuildTooling({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const {createServer} = require("http")
|
||||
const {parse} = require("url")
|
||||
const blitz = require("@blitzjs/server")
|
||||
const blitz = require("blitz/custom-server")
|
||||
const {log} = require("@blitzjs/display")
|
||||
|
||||
const {PORT = "3000"} = process.env
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {DefaultCtx, SessionContext} from "blitz"
|
||||
import {simpleRolesIsAuthorized} from "@blitzjs/server"
|
||||
import {DefaultCtx, SessionContext, SimpleRolesIsAuthorized} from "blitz"
|
||||
import React from "react"
|
||||
|
||||
declare module "blitz" {
|
||||
@@ -7,7 +6,7 @@ declare module "blitz" {
|
||||
session: SessionContext
|
||||
}
|
||||
export interface Session {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
isAuthorized: SimpleRolesIsAuthorized
|
||||
PublicData: {
|
||||
userId: number
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("@blitzjs/server")
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz")
|
||||
const withMonorepoBuildTooling = require("@preconstruct/next")
|
||||
const { GraphQLClient, gql } = require("graphql-request")
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import { DefaultCtx, SessionContext } from "blitz"
|
||||
import { simpleRolesIsAuthorized } from "@blitzjs/server"
|
||||
import { DefaultCtx, SessionContext, SimpleRolesIsAuthorized } from "blitz"
|
||||
|
||||
declare module "blitz" {
|
||||
export interface Ctx extends DefaultCtx {
|
||||
session: SessionContext
|
||||
}
|
||||
export interface Session {
|
||||
isAuthorized: typeof simpleRolesIsAuthorized
|
||||
isAuthorized: SimpleRolesIsAuthorized
|
||||
PublicData: {
|
||||
userId: string
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Document, Html, DocumentHead, Main, BlitzScript, DocumentContext} from "@blitzjs/core"
|
||||
import {Document, Html, DocumentHead, Main, BlitzScript, DocumentContext} from "blitz"
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
|
||||
@@ -51,6 +51,9 @@
|
||||
"lint-staged": {
|
||||
"*.{js,ts,tsx}": [
|
||||
"eslint --fix"
|
||||
],
|
||||
"package.json": [
|
||||
"manypkg fix"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -80,6 +83,7 @@
|
||||
"@types/fs-extra": "9.0.6",
|
||||
"@types/fs-readdir-recursive": "1.0.0",
|
||||
"@types/gulp-if": "0.0.33",
|
||||
"@types/htmlescape": "^1.1.1",
|
||||
"@types/ink-spinner": "3.0.0",
|
||||
"@types/jest": "^26.0.20",
|
||||
"@types/jsonwebtoken": "8.5.0",
|
||||
@@ -88,7 +92,6 @@
|
||||
"@types/mem-fs-editor": "7.0.0",
|
||||
"@types/merge-stream": "1.1.2",
|
||||
"@types/mock-fs": "4.13.0",
|
||||
"@types/module-alias": "2.0.0",
|
||||
"@types/node": "14.14.22",
|
||||
"@types/node-fetch": "2.5.8",
|
||||
"@types/parallel-transform": "1.1.0",
|
||||
@@ -116,6 +119,7 @@
|
||||
"babel-plugin-annotate-pure-calls": "0.4.0",
|
||||
"babel-plugin-dev-expression": "0.2.2",
|
||||
"babel-plugin-macros": "3.0.1",
|
||||
"babel-plugin-tester": "10.0.0",
|
||||
"babel-plugin-transform-inline-environment-variables": "0.4.3",
|
||||
"concurrently": "6.0.0",
|
||||
"cpx": "1.5.0",
|
||||
|
||||
4
packages/babel-preset/jest.config.js
Normal file
4
packages/babel-preset/jest.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
preset: '../../jest.config.js',
|
||||
testEnvironment: 'jest-environment-jsdom-sixteen',
|
||||
};
|
||||
0
packages/babel-preset/jest.setup.js
Normal file
0
packages/babel-preset/jest.setup.js
Normal file
@@ -2,7 +2,10 @@
|
||||
"name": "@blitzjs/babel-preset",
|
||||
"version": "0.30.7",
|
||||
"license": "MIT",
|
||||
"scripts": {},
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"module": "dist/blitzjs-babel-preset.esm.js",
|
||||
"main": "dist/blitzjs-babel-preset.cjs.js",
|
||||
"typings": "dist/blitzjs-babel-preset.cjs.d.ts",
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import AddBlitzAppRoot from './add-blitz-app-root';
|
||||
import RewriteImports from './rewrite-imports';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function preset(_api: any, options = {}) {
|
||||
return {
|
||||
// const isTest = _api.env('test');
|
||||
const isRunningInJest = Boolean(process.env.JEST_WORKER_ID);
|
||||
|
||||
const config = {
|
||||
presets: [[require('next/babel'), options]],
|
||||
plugins: [require('babel-plugin-superjson-next'), AddBlitzAppRoot],
|
||||
};
|
||||
|
||||
if (!isRunningInJest) {
|
||||
config.plugins.push(RewriteImports);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
41
packages/babel-preset/src/rewrite-imports.test.ts
Normal file
41
packages/babel-preset/src/rewrite-imports.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import pluginTester from 'babel-plugin-tester';
|
||||
import RewriteImports from './rewrite-imports';
|
||||
|
||||
pluginTester({
|
||||
pluginName: RewriteImports.name,
|
||||
plugin: RewriteImports,
|
||||
tests: [
|
||||
{
|
||||
code: `import { useQuery } from 'blitz';`,
|
||||
output: `import { useQuery } from '@blitzjs/core';`,
|
||||
},
|
||||
{
|
||||
code: `import { Image } from 'blitz';`,
|
||||
output: `import { Image } from '@blitzjs/core/image';`,
|
||||
},
|
||||
{
|
||||
code: `import {Image, Link} from 'blitz';`,
|
||||
output: `
|
||||
import { Link } from '@blitzjs/core';
|
||||
import { Image } from '@blitzjs/core/image';
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `import {Image as BlitzImage, Link} from 'blitz';`,
|
||||
output: `
|
||||
import { Link } from '@blitzjs/core';
|
||||
import { Image as BlitzImage } from '@blitzjs/core/image';
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `import {Document, Html, DocumentHead, Main, BlitzScript} from "blitz";`,
|
||||
output: `
|
||||
import { BlitzScript } from '@blitzjs/core/document';
|
||||
import { Main } from '@blitzjs/core/document';
|
||||
import { DocumentHead } from '@blitzjs/core/document';
|
||||
import { Html } from '@blitzjs/core/document';
|
||||
import { Document } from '@blitzjs/core/document';
|
||||
`,
|
||||
},
|
||||
],
|
||||
});
|
||||
110
packages/babel-preset/src/rewrite-imports.ts
Normal file
110
packages/babel-preset/src/rewrite-imports.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { PluginObj } from '@babel/core';
|
||||
import { BabelType } from 'babel-plugin-tester';
|
||||
|
||||
/*
|
||||
* https://astexplorer.net/#/gist/dd0cdbd56a701d8c9e078d20505b3980/latest
|
||||
*/
|
||||
|
||||
const defaultImportSource = '@blitzjs/core';
|
||||
|
||||
const specialImports: Record<string, string> = {
|
||||
Image: '@blitzjs/core/image',
|
||||
|
||||
Head: '@blitzjs/core/head',
|
||||
|
||||
dynamic: '@blitzjs/core/dynamic',
|
||||
noSSR: '@blitzjs/core/dynamic',
|
||||
|
||||
getConfig: '@blitzjs/core/config',
|
||||
setConfig: '@blitzjs/core/config',
|
||||
|
||||
Document: '@blitzjs/core/document',
|
||||
DocumentHead: '@blitzjs/core/document',
|
||||
Html: '@blitzjs/core/document',
|
||||
Main: '@blitzjs/core/document',
|
||||
BlitzScript: '@blitzjs/core/document',
|
||||
|
||||
getAllMiddlewareForModule: '@blitzjs/core/server',
|
||||
handleRequestWithMiddleware: '@blitzjs/core/server',
|
||||
connectMiddleware: '@blitzjs/core/server',
|
||||
invokeWithMiddleware: '@blitzjs/core/server',
|
||||
paginate: '@blitzjs/core/server',
|
||||
resolver: '@blitzjs/core/server',
|
||||
isLocalhost: '@blitzjs/core/server',
|
||||
passportAuth: '@blitzjs/core/server',
|
||||
sessionMiddleware: '@blitzjs/core/server',
|
||||
simpleRolesIsAuthorized: '@blitzjs/core/server',
|
||||
getSession: '@blitzjs/core/server',
|
||||
SecurePassword: '@blitzjs/core/server',
|
||||
hash256: '@blitzjs/core/server',
|
||||
generateToken: '@blitzjs/core/server',
|
||||
rpcApiHandler: '@blitzjs/core/server',
|
||||
};
|
||||
|
||||
function RewriteImports(babel: BabelType): PluginObj {
|
||||
const { types: t } = babel;
|
||||
|
||||
return {
|
||||
name: 'RewriteImports',
|
||||
visitor: {
|
||||
ImportDeclaration(path) {
|
||||
if (
|
||||
!looksLike(path, {
|
||||
node: {
|
||||
source: { value: 'blitz' },
|
||||
},
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
path.node.source = t.stringLiteral(defaultImportSource);
|
||||
|
||||
const specifierIndexesToRemove: number[] = [];
|
||||
path.node.specifiers.slice().forEach((specifier, index) => {
|
||||
if (!t.isImportSpecifier(specifier)) return;
|
||||
const importedName = t.isStringLiteral(specifier.imported)
|
||||
? specifier.imported.value
|
||||
: specifier.imported.name;
|
||||
if (importedName in specialImports) {
|
||||
path.insertAfter(
|
||||
t.importDeclaration(
|
||||
[specifier],
|
||||
t.stringLiteral(specialImports[importedName])
|
||||
)
|
||||
);
|
||||
|
||||
specifierIndexesToRemove.push(index);
|
||||
}
|
||||
});
|
||||
specifierIndexesToRemove.reverse().forEach((index) => {
|
||||
path.node.specifiers.splice(index, 1);
|
||||
});
|
||||
if (!path.node.specifiers.length) {
|
||||
path.remove();
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function looksLike(a: any, b: any): boolean {
|
||||
return (
|
||||
a &&
|
||||
b &&
|
||||
Object.keys(b).every((bKey) => {
|
||||
const bVal = b[bKey];
|
||||
const aVal = a[bKey];
|
||||
if (typeof bVal === 'function') {
|
||||
return bVal(aVal);
|
||||
}
|
||||
return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal);
|
||||
})
|
||||
);
|
||||
}
|
||||
function isPrimitive(val: any) {
|
||||
return val == null || /^[sbn]/.test(typeof val);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default RewriteImports;
|
||||
@@ -1,3 +0,0 @@
|
||||
it.skip('works', () => {
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
5
packages/blitz/custom-server/package.json
Normal file
5
packages/blitz/custom-server/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitz-custom-server.cjs.js",
|
||||
"module": "dist/blitz-custom-server.esm.js",
|
||||
"types": "dist/blitz-custom-server.cjs.d.ts"
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"index.ts",
|
||||
"cli.ts"
|
||||
"cli.ts",
|
||||
"custom-server.ts"
|
||||
]
|
||||
},
|
||||
"bin": {
|
||||
@@ -27,6 +28,7 @@
|
||||
"files": [
|
||||
"dist",
|
||||
"cli",
|
||||
"custom-server",
|
||||
"jest-preset.js",
|
||||
"jest-preset",
|
||||
"babel.js"
|
||||
@@ -71,6 +73,12 @@
|
||||
"resolve-from": "^5.0.0",
|
||||
"ts-jest": "26.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"next": "10.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"next": "*"
|
||||
},
|
||||
"keywords": [
|
||||
"blitz",
|
||||
"blitzjs",
|
||||
|
||||
10
packages/blitz/src/custom-server.ts
Normal file
10
packages/blitz/src/custom-server.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import next from "next"
|
||||
|
||||
// Support commonjs `require('blitz')`
|
||||
if (process.env.BLITZ_PROD_BUILD) {
|
||||
module.exports = next
|
||||
exports = module.exports
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default next
|
||||
@@ -1 +1,7 @@
|
||||
export * from "@blitzjs/core/config"
|
||||
export * from "@blitzjs/core/document"
|
||||
export * from "@blitzjs/core/dynamic"
|
||||
export * from "@blitzjs/core/head"
|
||||
export * from "@blitzjs/core/image"
|
||||
export * from "@blitzjs/core"
|
||||
export * from "@blitzjs/core/server"
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
"hasbin": "1.2.3",
|
||||
"import-cwd": "3.0.0",
|
||||
"minimist": "1.2.5",
|
||||
"module-alias": "2.2.2",
|
||||
"p-event": "4.2.0",
|
||||
"pkg-dir": "^5.0.0",
|
||||
"pluralize": "^8.0.0",
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922",
|
||||
"dependencies": {
|
||||
"fs-extra": "^9.1.0",
|
||||
"module-alias": "2.2.2",
|
||||
"pkg-dir": "^5.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
5
packages/core/config/package.json
Normal file
5
packages/core/config/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-config.cjs.js",
|
||||
"module": "dist/blitzjs-core-config.esm.js",
|
||||
"types": "dist/blitzjs-core-config.cjs.d.ts"
|
||||
}
|
||||
5
packages/core/document/package.json
Normal file
5
packages/core/document/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-document.cjs.js",
|
||||
"module": "dist/blitzjs-core-document.esm.js",
|
||||
"types": "dist/blitzjs-core-document.cjs.d.ts"
|
||||
}
|
||||
5
packages/core/dynamic/package.json
Normal file
5
packages/core/dynamic/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-dynamic.cjs.js",
|
||||
"module": "dist/blitzjs-core-dynamic.esm.js",
|
||||
"types": "dist/blitzjs-core-dynamic.cjs.d.ts"
|
||||
}
|
||||
5
packages/core/head/package.json
Normal file
5
packages/core/head/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-head.cjs.js",
|
||||
"module": "dist/blitzjs-core-head.esm.js",
|
||||
"types": "dist/blitzjs-core-head.cjs.d.ts"
|
||||
}
|
||||
5
packages/core/image/package.json
Normal file
5
packages/core/image/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-image.cjs.js",
|
||||
"module": "dist/blitzjs-core-image.esm.js",
|
||||
"types": "dist/blitzjs-core-image.cjs.d.ts"
|
||||
}
|
||||
@@ -7,47 +7,68 @@
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch"
|
||||
},
|
||||
"author": {
|
||||
"name": "Brandon Bayer",
|
||||
"email": "b@bayer.ws",
|
||||
"url": "https://twitter.com/flybayer"
|
||||
"preconstruct": {
|
||||
"entrypoints": [
|
||||
"config.ts",
|
||||
"document.ts",
|
||||
"dynamic.ts",
|
||||
"head.ts",
|
||||
"image.ts",
|
||||
"index.ts",
|
||||
"server/index.ts",
|
||||
"with-blitz.ts"
|
||||
]
|
||||
},
|
||||
"main": "dist/blitzjs-core.cjs.js",
|
||||
"module": "dist/blitzjs-core.esm.js",
|
||||
"types": "dist/blitzjs-core.cjs.d.ts",
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"config",
|
||||
"document",
|
||||
"dynamic",
|
||||
"head",
|
||||
"image",
|
||||
"server",
|
||||
"with-blitz"
|
||||
],
|
||||
"repository": "https://github.com/blitz-js/blitz",
|
||||
"dependencies": {
|
||||
"@types/htmlescape": "^1.1.1",
|
||||
"@types/secure-password": "3.1.0",
|
||||
"b64-lite": "^1.4.0",
|
||||
"bad-behavior": "^1.0.1",
|
||||
"chalk": "^4.1.0",
|
||||
"cookie": "0.4.1",
|
||||
"cookie-session": "^1.4.0",
|
||||
"cross-spawn": "7.0.3",
|
||||
"htmlescape": "^1.1.1",
|
||||
"jsonwebtoken": "8.5.1",
|
||||
"lodash.frompairs": "4.0.1",
|
||||
"nanoid": "^3.1.20",
|
||||
"next": "10.0.7",
|
||||
"npm-which": "^3.0.1",
|
||||
"null-loader": "4.0.1",
|
||||
"passport": "0.4.1",
|
||||
"react-query": "2.5.12",
|
||||
"secure-password": "4.0.0",
|
||||
"superjson": "1.7.2",
|
||||
"zod": "1.11.11"
|
||||
"superjson": "1.7.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@blitzjs/config": "*",
|
||||
"@blitzjs/display": "*",
|
||||
"nanoid": "*",
|
||||
"next": "*",
|
||||
"react": ">=0.0.0 || >=0.0.0-experimental"
|
||||
"react": ">=0.0.0 || >=0.0.0-experimental",
|
||||
"zod": "*"
|
||||
},
|
||||
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922",
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "0.30.7",
|
||||
"@blitzjs/display": "0.30.7",
|
||||
"nanoid": "^3.1.20",
|
||||
"next": "10.0.7",
|
||||
"react": "0.0.0-experimental-3310209d0"
|
||||
}
|
||||
"@blitzjs/config": "*",
|
||||
"@blitzjs/display": "*",
|
||||
"react": "0.0.0-experimental-3310209d0",
|
||||
"zod": "1.11.11"
|
||||
},
|
||||
"repository": "https://github.com/blitz-js/blitz",
|
||||
"author": {
|
||||
"name": "Brandon Bayer",
|
||||
"email": "b@bayer.ws",
|
||||
"url": "https://twitter.com/flybayer"
|
||||
},
|
||||
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922"
|
||||
}
|
||||
|
||||
5
packages/core/server/package.json
Normal file
5
packages/core/server/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-server.cjs.js",
|
||||
"module": "dist/blitzjs-core-server.esm.js",
|
||||
"types": "dist/blitzjs-core-server.cjs.d.ts"
|
||||
}
|
||||
62
packages/core/src/auth/auth-client.test.ts
Normal file
62
packages/core/src/auth/auth-client.test.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/* eslint @typescript-eslint/no-floating-promises: off */
|
||||
import {act} from "@testing-library/react-hooks"
|
||||
import {renderHook} from "../../test/test-utils"
|
||||
import {useSession} from "./auth-client"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
|
||||
describe("useSession", () => {
|
||||
it("returns empty at when no value is set", () => {
|
||||
const {result} = renderHook(() => useSession())
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
...publicDataStore.emptyPublicData,
|
||||
})
|
||||
})
|
||||
|
||||
it("subscribes to the public data store", () => {
|
||||
const {result} = renderHook(() => useSession())
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["foo"], userId: "bar"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["baz"], userId: "boo"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "boo",
|
||||
roles: ["baz"],
|
||||
})
|
||||
})
|
||||
|
||||
it("un-subscribes from the public data store on unmount", () => {
|
||||
const {result, unmount} = renderHook(() => useSession())
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["foo"], userId: "bar"} as any)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
unmount()
|
||||
})
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["baz"], userId: "boo"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
})
|
||||
})
|
||||
73
packages/core/src/auth/auth-client.ts
Normal file
73
packages/core/src/auth/auth-client.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import {useEffect, useState} from "react"
|
||||
import {getBlitzRuntimeData} from "../blitz-data"
|
||||
import {COOKIE_CSRF_TOKEN, COOKIE_LEGACY_CSRF_TOKEN} from "../constants"
|
||||
import {AuthenticationError} from "../errors"
|
||||
import {isServer} from "../utils"
|
||||
import {readCookie} from "../utils/cookie"
|
||||
import {AuthenticatedClientSession, ClientSession, PublicData} from "./auth-types"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
|
||||
export const getAntiCSRFToken = () =>
|
||||
readCookie(COOKIE_CSRF_TOKEN()) || readCookie(COOKIE_LEGACY_CSRF_TOKEN())
|
||||
|
||||
interface UseSessionOptions {
|
||||
initialPublicData?: PublicData
|
||||
suspense?: boolean | null
|
||||
}
|
||||
|
||||
export const useSession = (options: UseSessionOptions = {}): ClientSession => {
|
||||
const suspense = options?.suspense ?? getBlitzRuntimeData().suspenseEnabled
|
||||
|
||||
let initialState: ClientSession
|
||||
if (options.initialPublicData) {
|
||||
initialState = {...options.initialPublicData, isLoading: false}
|
||||
} else if (suspense) {
|
||||
if (isServer) {
|
||||
throw new Promise((_) => {})
|
||||
} else {
|
||||
initialState = {...publicDataStore.getData(), isLoading: false}
|
||||
}
|
||||
} else {
|
||||
initialState = {...publicDataStore.emptyPublicData, isLoading: true}
|
||||
}
|
||||
|
||||
const [session, setSession] = useState(initialState)
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize on mount
|
||||
setSession({...publicDataStore.getData(), isLoading: false})
|
||||
const subscription = publicDataStore.observable.subscribe((data) =>
|
||||
setSession({...data, isLoading: false}),
|
||||
)
|
||||
return subscription.unsubscribe
|
||||
}, [])
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
export const useAuthenticatedSession = (
|
||||
options: UseSessionOptions = {},
|
||||
): AuthenticatedClientSession => {
|
||||
useAuthorize()
|
||||
return useSession(options) as AuthenticatedClientSession
|
||||
}
|
||||
|
||||
export const useAuthorize = () => {
|
||||
useAuthorizeIf(true)
|
||||
}
|
||||
|
||||
export const useAuthorizeIf = (condition?: boolean) => {
|
||||
useEffect(() => {
|
||||
if (condition && !publicDataStore.getData().userId) {
|
||||
const error = new AuthenticationError()
|
||||
delete error.stack
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useRedirectAuthenticated = (to: string) => {
|
||||
if (typeof window !== "undefined" && publicDataStore.getData().userId) {
|
||||
window.location.replace(to)
|
||||
}
|
||||
}
|
||||
83
packages/core/src/auth/auth-types.ts
Normal file
83
packages/core/src/auth/auth-types.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import {Ctx} from "@blitzjs/core"
|
||||
|
||||
export interface Session {
|
||||
// isAuthorize can be injected here
|
||||
// PublicData can be injected here
|
||||
}
|
||||
|
||||
export type PublicData = "PublicData" extends keyof Session
|
||||
? Session["PublicData"]
|
||||
: {userId: unknown}
|
||||
|
||||
export interface EmptyPublicData extends Partial<Omit<PublicData, "userId">> {
|
||||
userId: PublicData["userId"] | null
|
||||
}
|
||||
|
||||
export type IsAuthorizedArgs = "isAuthorized" extends keyof Session
|
||||
? "args" extends keyof Parameters<Session["isAuthorized"]>[0]
|
||||
? Parameters<Session["isAuthorized"]>[0]["args"]
|
||||
: unknown[]
|
||||
: unknown[]
|
||||
|
||||
export interface SessionModel extends Record<any, any> {
|
||||
handle: string
|
||||
userId?: PublicData["userId"]
|
||||
expiresAt?: Date
|
||||
hashedSessionToken?: string
|
||||
antiCSRFToken?: string
|
||||
publicData?: string
|
||||
privateData?: string
|
||||
}
|
||||
|
||||
export type SessionConfig = {
|
||||
sessionExpiryMinutes?: number
|
||||
method?: "essential" | "advanced"
|
||||
sameSite?: "none" | "lax" | "strict"
|
||||
domain?: string
|
||||
publicDataKeysToSyncAcrossSessions?: string[]
|
||||
getSession: (handle: string) => Promise<SessionModel | null>
|
||||
getSessions: (userId: PublicData["userId"]) => Promise<SessionModel[]>
|
||||
createSession: (session: SessionModel) => Promise<SessionModel>
|
||||
updateSession: (handle: string, session: Partial<SessionModel>) => Promise<SessionModel>
|
||||
deleteSession: (handle: string) => Promise<SessionModel>
|
||||
isAuthorized: (data: {ctx: Ctx; args: any[]}) => boolean
|
||||
}
|
||||
|
||||
export interface SessionContextBase {
|
||||
$handle: string | null
|
||||
$publicData: unknown
|
||||
$authorize(...args: IsAuthorizedArgs): asserts this is AuthenticatedSessionContext
|
||||
// $isAuthorized cannot have assertion return type because it breaks advanced use cases
|
||||
// with multiple isAuthorized calls
|
||||
$isAuthorized: (...args: IsAuthorizedArgs) => boolean
|
||||
$create: (publicData: PublicData, privateData?: Record<any, any>) => Promise<void>
|
||||
$revoke: () => Promise<void>
|
||||
$revokeAll: () => Promise<void>
|
||||
$getPrivateData: () => Promise<Record<any, any>>
|
||||
$setPrivateData: (data: Record<any, any>) => Promise<void>
|
||||
$setPublicData: (data: Partial<Omit<PublicData, "userId">>) => Promise<void>
|
||||
}
|
||||
|
||||
// Could be anonymous
|
||||
export interface SessionContext extends SessionContextBase, EmptyPublicData {
|
||||
$publicData: Partial<PublicData> | EmptyPublicData
|
||||
}
|
||||
|
||||
export interface AuthenticatedSessionContext extends SessionContextBase, PublicData {
|
||||
userId: PublicData["userId"]
|
||||
$publicData: PublicData
|
||||
}
|
||||
|
||||
export interface ClientSession extends EmptyPublicData {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export interface AuthenticatedClientSession extends PublicData {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export type VerifyCallbackResult = {
|
||||
publicData: PublicData
|
||||
privateData?: Record<string, any>
|
||||
redirectUrl?: string
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN} from "./constants"
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN} from "../constants"
|
||||
import {deleteCookie, readCookie} from "../utils/cookie"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {deleteCookie, readCookie} from "./utils/cookie"
|
||||
import {parsePublicDataToken} from "./utils/tokens"
|
||||
import {parsePublicDataToken} from "./public-data-token"
|
||||
|
||||
jest.mock("./utils/tokens", () => ({
|
||||
jest.mock("./public-data-token", () => ({
|
||||
parsePublicDataToken: jest.fn(),
|
||||
}))
|
||||
jest.mock("./utils/cookie", () => ({
|
||||
jest.mock("../utils/cookie", () => ({
|
||||
readCookie: jest.fn(),
|
||||
deleteCookie: jest.fn(),
|
||||
}))
|
||||
@@ -1,8 +1,8 @@
|
||||
import BadBehavior from "bad-behavior"
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN, LOCALSTORAGE_PREFIX} from "./constants"
|
||||
import {EmptyPublicData, PublicData} from "./types"
|
||||
import {deleteCookie, readCookie} from "./utils/cookie"
|
||||
import {parsePublicDataToken} from "./utils/tokens"
|
||||
import {COOKIE_PUBLIC_DATA_TOKEN, LOCALSTORAGE_PREFIX} from "../constants"
|
||||
import {deleteCookie, readCookie} from "../utils/cookie"
|
||||
import {EmptyPublicData, PublicData} from "./auth-types"
|
||||
import {parsePublicDataToken} from "./public-data-token"
|
||||
|
||||
class PublicDataStore {
|
||||
private eventKey = `${LOCALSTORAGE_PREFIX}publicDataUpdated`
|
||||
30
packages/core/src/auth/public-data-token.test.ts
Normal file
30
packages/core/src/auth/public-data-token.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import {toBase64} from "b64-lite"
|
||||
import {parsePublicDataToken} from "./public-data-token"
|
||||
|
||||
describe("parsePublicDataToken", () => {
|
||||
it("throws if token is empty", () => {
|
||||
const ret = () => parsePublicDataToken("")
|
||||
expect(ret).toThrow("[parsePublicDataToken] Failed: token is empty")
|
||||
})
|
||||
|
||||
it("throws if the token cannot be parsed", () => {
|
||||
const invalidJSON = "{"
|
||||
const ret = () => parsePublicDataToken(toBase64(invalidJSON))
|
||||
|
||||
expect(ret).toThrowError("[parsePublicDataToken] Failed to parse publicDataStr: {")
|
||||
})
|
||||
|
||||
it("parses the public data", () => {
|
||||
const validJSON = '{"foo": "bar"}'
|
||||
expect(parsePublicDataToken(toBase64(validJSON))).toEqual({
|
||||
publicData: {foo: "bar"},
|
||||
})
|
||||
})
|
||||
|
||||
it("parses the public data containing unicode chars", () => {
|
||||
const data = '"foo-κόσμε-żółć-平仮名"'
|
||||
expect(parsePublicDataToken(toBase64(data))).toEqual({
|
||||
publicData: "foo-κόσμε-żółć-平仮名",
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
import {fromBase64} from "b64-lite"
|
||||
import {PublicData} from "../types"
|
||||
import {PublicData} from "./auth-types"
|
||||
|
||||
function assert(condition: any, message: string): asserts condition {
|
||||
if (!condition) throw new Error(message)
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, {ComponentPropsWithoutRef, useEffect} from "react"
|
||||
import {Head} from "./nextjs"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {useAuthorizeIf} from "./supertokens"
|
||||
import {useAuthorizeIf} from "./auth/auth-client"
|
||||
import {publicDataStore} from "./auth/public-data-store"
|
||||
import {Head} from "./head"
|
||||
import {AppProps, BlitzPage} from "./types"
|
||||
|
||||
const customCSS = `
|
||||
|
||||
6
packages/core/src/config.ts
Normal file
6
packages/core/src/config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as getConfig, setConfig} from "next/config"
|
||||
8
packages/core/src/document.ts
Normal file
8
packages/core/src/document.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as Document, Head as DocumentHead, Html, Main} from "next/document"
|
||||
export type {DocumentProps, DocumentContext, DocumentInitialProps} from "next/document"
|
||||
export {BlitzScript} from "./blitz-script"
|
||||
6
packages/core/src/dynamic.ts
Normal file
6
packages/core/src/dynamic.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as dynamic, noSSR} from "next/dynamic"
|
||||
1
packages/core/src/error.ts
Normal file
1
packages/core/src/error.ts
Normal file
@@ -0,0 +1 @@
|
||||
export {default as ErrorComponent} from "next/error"
|
||||
6
packages/core/src/head.ts
Normal file
6
packages/core/src/head.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as Head} from "next/head"
|
||||
7
packages/core/src/image.ts
Normal file
7
packages/core/src/image.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as Image} from "next/image"
|
||||
export type {ImageProps, ImageLoader, ImageLoaderProps} from "next/image"
|
||||
@@ -1,49 +1,24 @@
|
||||
export * from "./nextjs"
|
||||
export * from "./types"
|
||||
export * from "./router"
|
||||
export * from "./link"
|
||||
export * from "./error"
|
||||
export * from "./errors"
|
||||
export * from "./constants"
|
||||
export {BlitzScript} from "./blitz-script"
|
||||
export {withBlitzAppRoot} from "./blitz-app-root"
|
||||
export {useQuery, usePaginatedQuery, useInfiniteQuery} from "./use-query-hooks"
|
||||
export {getQueryKey, invalidateQuery, setQueryData} from "./utils/react-query-utils"
|
||||
export {useParam, useParams} from "./use-params"
|
||||
export {withRouter, RouterContext} from "./with-router"
|
||||
export type {BlitzRouter} from "./with-router"
|
||||
export {useRouter} from "./use-router"
|
||||
export {useRouterQuery} from "./use-router-query"
|
||||
export {passportAuth} from "./passport-adapter"
|
||||
export {getIsomorphicEnhancedResolver} from "./rpc"
|
||||
export {getIsomorphicEnhancedResolver} from "./rpc-client"
|
||||
export {useMutation} from "./use-mutation"
|
||||
export {invoke, invokeWithMiddleware} from "./invoke"
|
||||
export {invoke} from "./invoke"
|
||||
export {getBlitzRuntimeData} from "./blitz-data"
|
||||
export {resolver} from "./resolver"
|
||||
export type {AuthenticatedMiddlewareCtx} from "./resolver"
|
||||
export {paginate} from "./server-utils"
|
||||
|
||||
export {
|
||||
getAllMiddlewareForModule,
|
||||
handleRequestWithMiddleware,
|
||||
connectMiddleware,
|
||||
} from "./middleware"
|
||||
export type {Ctx, DefaultCtx} from "./middleware"
|
||||
export {
|
||||
getAntiCSRFToken,
|
||||
useSession,
|
||||
useAuthenticatedSession,
|
||||
useAuthorize,
|
||||
useRedirectAuthenticated,
|
||||
} from "./supertokens"
|
||||
export type {
|
||||
SessionConfig,
|
||||
SessionContext,
|
||||
AuthenticatedSessionContext,
|
||||
ClientSession,
|
||||
AuthenticatedClientSession,
|
||||
} from "./supertokens"
|
||||
|
||||
export {SecurePassword, hash256, generateToken} from "./auth-utils"
|
||||
|
||||
export {isLocalhost} from "./utils/index"
|
||||
export {prettyMs} from "./utils/pretty-ms"
|
||||
} from "./auth/auth-client"
|
||||
export * from "./auth/auth-types"
|
||||
|
||||
export {enhancePrisma} from "./prisma-utils"
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import {baseLogger, chalk, log as displayLog} from "@blitzjs/display"
|
||||
import {getAllMiddlewareForModule, handleRequestWithMiddleware} from "./middleware"
|
||||
import {
|
||||
CancellablePromise,
|
||||
EnhancedResolver,
|
||||
EnhancedResolverRpcClient,
|
||||
FirstParam,
|
||||
InvokeWithMiddlewareConfig,
|
||||
MiddlewareResponse,
|
||||
PromiseReturnType,
|
||||
QueryFn,
|
||||
Resolver,
|
||||
} from "./types"
|
||||
import {isClient} from "./utils"
|
||||
import {prettyMs} from "./utils/pretty-ms"
|
||||
|
||||
export function invoke<T extends QueryFn, TInput = FirstParam<T>>(
|
||||
queryFn: T,
|
||||
@@ -32,47 +26,3 @@ export function invoke<T extends QueryFn, TInput = FirstParam<T>>(
|
||||
return fn(params) as ReturnType<T>
|
||||
}
|
||||
}
|
||||
|
||||
export async function invokeWithMiddleware<TInput, TResult>(
|
||||
resolver: Resolver<TInput, TResult>,
|
||||
params: TInput,
|
||||
ctx: InvokeWithMiddlewareConfig,
|
||||
): Promise<TResult> {
|
||||
if (!ctx.req) {
|
||||
throw new Error("You must provide `req` in third argument of invokeWithMiddleware()")
|
||||
}
|
||||
if (!ctx.res) {
|
||||
throw new Error("You must provide `res` in third argument of invokeWithMiddleware()")
|
||||
}
|
||||
const enhancedResolver = (resolver as unknown) as EnhancedResolver<TInput, TResult>
|
||||
|
||||
const middleware = getAllMiddlewareForModule(enhancedResolver)
|
||||
|
||||
if (ctx.middleware) {
|
||||
middleware.push(...ctx.middleware)
|
||||
}
|
||||
|
||||
middleware.push(async (_req, res, next) => {
|
||||
const log = baseLogger().getChildLogger({prefix: [enhancedResolver._meta.name + "()"]})
|
||||
displayLog.newline()
|
||||
try {
|
||||
log.info(chalk.dim("Starting with input:"), params)
|
||||
const startTime = Date.now()
|
||||
|
||||
const result = await enhancedResolver(params, res.blitzCtx)
|
||||
|
||||
const duration = Date.now() - startTime
|
||||
log.info(chalk.dim(`Finished in ${prettyMs(duration)}`))
|
||||
displayLog.newline()
|
||||
|
||||
res.blitzResult = result
|
||||
return next()
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
await handleRequestWithMiddleware(ctx.req, ctx.res, middleware)
|
||||
|
||||
return (ctx.res as MiddlewareResponse).blitzResult as TResult
|
||||
}
|
||||
|
||||
7
packages/core/src/link.ts
Normal file
7
packages/core/src/link.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {default as Link} from "next/link"
|
||||
export type {LinkProps} from "next/link"
|
||||
@@ -1,22 +0,0 @@
|
||||
// --------------------
|
||||
// Exports from Next.js
|
||||
// --------------------
|
||||
export {default as Head} from "next/head"
|
||||
|
||||
export {default as Link} from "next/link"
|
||||
export type {LinkProps} from "next/link"
|
||||
|
||||
export {default as Router} from "next/router"
|
||||
|
||||
export {default as Image} from "next/image"
|
||||
export type {ImageProps} from "next/image"
|
||||
|
||||
export {default as Document, Html, Head as DocumentHead, Main} from "next/document"
|
||||
export type {DocumentContext, DocumentProps, DocumentInitialProps} from "next/document"
|
||||
|
||||
export {default as dynamic} from "next/dynamic"
|
||||
|
||||
export {default as ErrorComponent} from "next/error"
|
||||
export type {ErrorProps} from "next/error"
|
||||
|
||||
export {default as getConfig} from "next/config"
|
||||
45
packages/core/src/router/index.tsx
Normal file
45
packages/core/src/router/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import {WithRouterProps as WithNextRouterProps} from "next/dist/client/with-router"
|
||||
import {
|
||||
default as NextRouter,
|
||||
NextRouter as NextRouterType,
|
||||
useRouter as useNextRouter,
|
||||
withRouter as withNextRouter,
|
||||
} from "next/router"
|
||||
import React from "react"
|
||||
import {extractRouterParams, useParams, useRouterQuery} from "./router-hooks"
|
||||
|
||||
export const Router = NextRouter
|
||||
export {createRouter, makePublicRouterInstance} from "next/router"
|
||||
export {RouterContext} from "next/dist/next-server/lib/router-context"
|
||||
|
||||
export {useParam, useParams, useRouterQuery} from "./router-hooks"
|
||||
|
||||
export interface BlitzRouter extends NextRouterType {
|
||||
params: ReturnType<typeof extractRouterParams>
|
||||
query: ReturnType<typeof useRouterQuery>
|
||||
}
|
||||
|
||||
export interface WithRouterProps {
|
||||
router: BlitzRouter
|
||||
}
|
||||
|
||||
export function withRouter(WrappedComponent: React.ComponentType<WithRouterProps>) {
|
||||
const Wrapper: React.FC<WithNextRouterProps> = ({router}) => {
|
||||
const query = useRouterQuery()
|
||||
const params = useParams()
|
||||
return <WrappedComponent router={{...router, query, params}} />
|
||||
}
|
||||
return withNextRouter(Wrapper)
|
||||
}
|
||||
|
||||
export function useRouter() {
|
||||
const router = useNextRouter()
|
||||
const query = useRouterQuery()
|
||||
const params = useParams()
|
||||
|
||||
// TODO - we have to explicitly define the return type otherwise TS complains about
|
||||
// NextHistoryState and TransitionOptions not being exported from Next.js code
|
||||
return React.useMemo(() => {
|
||||
return {...router, query, params}
|
||||
}, [params, query, router]) as BlitzRouter
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
import fromPairs from "lodash.frompairs"
|
||||
import {useRouter} from "next/router"
|
||||
import {useMemo} from "react"
|
||||
import {Dict, ParsedUrlQuery, ParsedUrlQueryValue} from "./types"
|
||||
import {useRouterQuery} from "./use-router-query"
|
||||
import {Dict, ParsedUrlQuery, ParsedUrlQueryValue} from "../types"
|
||||
|
||||
export function useRouterQuery() {
|
||||
const router = useRouter()
|
||||
|
||||
const query = useMemo(() => {
|
||||
const query = decode(router.asPath.split("?")[1])
|
||||
return query
|
||||
}, [router.asPath])
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
function areQueryValuesEqual(value1: ParsedUrlQueryValue, value2: ParsedUrlQueryValue) {
|
||||
// Check if their type match
|
||||
@@ -104,3 +114,34 @@ export function useParam(
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/*
|
||||
* Copied from https://github.com/lukeed/qss
|
||||
*/
|
||||
function toValue(mix: any) {
|
||||
if (!mix) return ""
|
||||
var str = decodeURIComponent(mix)
|
||||
if (str === "false") return false
|
||||
if (str === "true") return true
|
||||
return +str * 0 === 0 ? +str : str
|
||||
}
|
||||
function decode(str: string) {
|
||||
if (!str) return {}
|
||||
let tmp: any
|
||||
let k
|
||||
const out: Record<string, any> = {}
|
||||
const arr = str.split("&")
|
||||
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
while ((tmp = arr.shift())) {
|
||||
tmp = tmp.split("=")
|
||||
k = tmp.shift()
|
||||
if (out[k] !== void 0) {
|
||||
out[k] = [].concat(out[k], toValue(tmp.shift()) as any)
|
||||
} else {
|
||||
out[k] = toValue(tmp.shift())
|
||||
}
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import {serialize} from "superjson"
|
||||
import {getBlitzRuntimeData} from "../src/blitz-data"
|
||||
import {executeRpcCall, getIsomorphicEnhancedResolver} from "../src/rpc"
|
||||
import {executeRpcCall, getIsomorphicEnhancedResolver} from "./rpc-client"
|
||||
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
@@ -1,6 +1,8 @@
|
||||
import {queryCache} from "react-query"
|
||||
import {deserialize, serialize} from "superjson"
|
||||
import {SuperJSONResult} from "superjson/dist/types"
|
||||
import {getAntiCSRFToken} from "./auth/auth-client"
|
||||
import {publicDataStore} from "./auth/public-data-store"
|
||||
import {
|
||||
HEADER_CSRF,
|
||||
HEADER_CSRF_ERROR,
|
||||
@@ -9,8 +11,6 @@ import {
|
||||
HEADER_SESSION_REVOKED,
|
||||
} from "./constants"
|
||||
import {CSRFTokenMismatchError} from "./errors"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {getAntiCSRFToken} from "./supertokens"
|
||||
import {
|
||||
CancellablePromise,
|
||||
EnhancedResolver,
|
||||
@@ -1,4 +1,4 @@
|
||||
import {SecurePassword} from "../src/auth-utils"
|
||||
import {SecurePassword} from "./auth-utils"
|
||||
|
||||
describe("SecurePassword", () => {
|
||||
describe("hash", () => {
|
||||
@@ -1,10 +1,11 @@
|
||||
import * as crypto from "crypto"
|
||||
import {nanoid} from "nanoid"
|
||||
import SecurePasswordLib from "secure-password"
|
||||
import {AuthenticationError} from "./errors"
|
||||
import {AuthenticationError} from "../../errors"
|
||||
|
||||
export const hash256 = (input: string = "") =>
|
||||
crypto.createHash("sha256").update(input).digest("hex")
|
||||
export const hash256 = (input: string = "") => {
|
||||
return crypto.createHash("sha256").update(input).digest("hex")
|
||||
}
|
||||
|
||||
export const generateToken = (numberOfCharacters: number = 32) => nanoid(numberOfCharacters)
|
||||
|
||||
@@ -3,16 +3,21 @@
|
||||
import {log} from "@blitzjs/display"
|
||||
import cookieSession from "cookie-session"
|
||||
import passport from "passport"
|
||||
import {BlitzApiRequest, BlitzApiResponse, ConnectMiddleware} from "."
|
||||
import {SessionContext, VerifyCallbackResult} from "../../auth/auth-types"
|
||||
import {
|
||||
BlitzApiRequest,
|
||||
BlitzApiResponse,
|
||||
BlitzPassportConfig,
|
||||
ConnectMiddleware,
|
||||
Middleware,
|
||||
} from "../../types"
|
||||
import {
|
||||
connectMiddleware,
|
||||
getAllMiddlewareForModule,
|
||||
handleRequestWithMiddleware,
|
||||
} from "./middleware"
|
||||
} from "../middleware"
|
||||
import {isLocalhost} from "../server-utils"
|
||||
import {secureProxyMiddleware} from "./secure-proxy-middleware"
|
||||
import {SessionContext} from "./supertokens"
|
||||
import {BlitzPassportConfig, Middleware, VerifyCallbackResult} from "./types"
|
||||
import {isLocalhost} from "./utils/index"
|
||||
|
||||
function assert(condition: any, message: string): asserts condition {
|
||||
if (!condition) throw new Error(message)
|
||||
@@ -1,4 +1,4 @@
|
||||
import {Middleware, MiddlewareRequest, MiddlewareResponse} from "./types"
|
||||
import {Middleware, MiddlewareRequest, MiddlewareResponse} from "../../types"
|
||||
|
||||
export const secureProxyMiddleware: Middleware = function (
|
||||
req: MiddlewareRequest,
|
||||
@@ -14,8 +14,8 @@ import http from "http"
|
||||
import {apiResolver} from "next/dist/next-server/server/api-utils"
|
||||
import fetch from "node-fetch"
|
||||
import listen from "test-listen"
|
||||
import {rpcApiHandler} from "./rpc"
|
||||
import {sessionMiddleware, simpleRolesIsAuthorized} from "./supertokens"
|
||||
import {rpcApiHandler} from "../rpc-server"
|
||||
import {sessionMiddleware, simpleRolesIsAuthorized} from "./sessions"
|
||||
|
||||
const isIsoDate = (str: string) => {
|
||||
if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(str)) return false
|
||||
@@ -38,7 +38,7 @@ type CtxWithSession = {
|
||||
session: SessionContext
|
||||
}
|
||||
|
||||
describe("supertokens", () => {
|
||||
describe("sessions", () => {
|
||||
it("anonymous", async () => {
|
||||
const resolverModule = ((() => {
|
||||
return
|
||||
@@ -1,47 +1,43 @@
|
||||
/* eslint-disable es5/no-es6-methods -- file only used on the server */
|
||||
import {getConfig} from "@blitzjs/config"
|
||||
import {getProjectRoot} from "@blitzjs/config"
|
||||
// Must import this type from 'blitz'
|
||||
import {log} from "@blitzjs/display"
|
||||
import {fromBase64, toBase64} from "b64-lite"
|
||||
import cookie from "cookie"
|
||||
import {IncomingMessage, ServerResponse} from "http"
|
||||
import {sign as jwtSign, verify as jwtVerify} from "jsonwebtoken"
|
||||
import {getCookieParser} from "next/dist/next-server/server/api-utils"
|
||||
import {join} from "path"
|
||||
import {
|
||||
EmptyPublicData,
|
||||
IsAuthorizedArgs,
|
||||
PublicData,
|
||||
SessionConfig,
|
||||
SessionContext,
|
||||
} from "../../auth/auth-types"
|
||||
import {
|
||||
AuthenticationError,
|
||||
AuthorizationError,
|
||||
BlitzApiRequest,
|
||||
BlitzApiResponse,
|
||||
COOKIE_ANONYMOUS_SESSION_TOKEN,
|
||||
COOKIE_CSRF_TOKEN,
|
||||
COOKIE_PUBLIC_DATA_TOKEN,
|
||||
COOKIE_REFRESH_TOKEN,
|
||||
COOKIE_SESSION_TOKEN,
|
||||
CSRFTokenMismatchError,
|
||||
Ctx,
|
||||
EmptyPublicData,
|
||||
generateToken,
|
||||
HANDLE_SEPARATOR,
|
||||
hash256,
|
||||
HEADER_CSRF,
|
||||
HEADER_CSRF_ERROR,
|
||||
HEADER_PUBLIC_DATA_TOKEN,
|
||||
HEADER_SESSION_CREATED,
|
||||
HEADER_SESSION_REVOKED,
|
||||
IsAuthorizedArgs,
|
||||
isLocalhost,
|
||||
Middleware,
|
||||
MiddlewareResponse,
|
||||
PublicData,
|
||||
SESSION_TOKEN_VERSION_0,
|
||||
SESSION_TYPE_ANONYMOUS_JWT,
|
||||
SESSION_TYPE_OPAQUE_TOKEN_SIMPLE,
|
||||
SessionConfig,
|
||||
SessionContext,
|
||||
TOKEN_SEPARATOR,
|
||||
} from "@blitzjs/core"
|
||||
// Must import this type from 'blitz'
|
||||
import {log} from "@blitzjs/display"
|
||||
import {fromBase64, toBase64} from "b64-lite"
|
||||
import cookie from "cookie"
|
||||
import {addMinutes, addYears, differenceInMinutes, isPast} from "date-fns"
|
||||
import {IncomingMessage, ServerResponse} from "http"
|
||||
import {sign as jwtSign, verify as jwtVerify} from "jsonwebtoken"
|
||||
import {getCookieParser} from "next/dist/next-server/server/api-utils"
|
||||
import {join} from "path"
|
||||
} from "../../constants"
|
||||
import {AuthenticationError, AuthorizationError, CSRFTokenMismatchError} from "../../errors"
|
||||
import {BlitzApiRequest, BlitzApiResponse, Ctx, Middleware, MiddlewareResponse} from "../../types"
|
||||
import {addMinutes, addYears, differenceInMinutes, isPast} from "../../utils/date-utils"
|
||||
import {isLocalhost} from "../server-utils"
|
||||
import {generateToken, hash256} from "./auth-utils"
|
||||
const debug = require("debug")("blitz:session")
|
||||
|
||||
function assert(condition: any, message: string): asserts condition {
|
||||
@@ -157,7 +153,7 @@ export const sessionMiddleware = (sessionConfig: Partial<SessionConfig> = {}): M
|
||||
return async (req, res, next) => {
|
||||
if (req.method !== "HEAD" && !(res.blitzCtx as any).session) {
|
||||
// This function also saves session to res.blitzCtx
|
||||
await getSessionContext(req, res)
|
||||
await getSession(req, res)
|
||||
}
|
||||
return next()
|
||||
}
|
||||
@@ -204,7 +200,7 @@ function ensureMiddlewareResponse(
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSessionContext(
|
||||
export async function getSession(
|
||||
req: BlitzApiRequest | IncomingMessage,
|
||||
res: BlitzApiResponse | ServerResponse,
|
||||
): Promise<SessionContext> {
|
||||
@@ -217,7 +213,7 @@ export async function getSessionContext(
|
||||
return response.blitzCtx.session
|
||||
}
|
||||
|
||||
let sessionKernel = await getSession(req, res)
|
||||
let sessionKernel = await getSessionKernel(req, res)
|
||||
|
||||
if (sessionKernel) {
|
||||
debug("Got existing session", sessionKernel)
|
||||
@@ -544,7 +540,7 @@ export const setPublicDataCookie = (
|
||||
// --------------------------------
|
||||
// Get Session
|
||||
// --------------------------------
|
||||
export async function getSession(
|
||||
export async function getSessionKernel(
|
||||
req: BlitzApiRequest,
|
||||
res: ServerResponse,
|
||||
): Promise<SessionKernel | null> {
|
||||
@@ -556,7 +552,7 @@ export async function getSession(
|
||||
const antiCSRFToken = req.headers[HEADER_CSRF] as string
|
||||
|
||||
if (sessionToken) {
|
||||
debug("[getSession] Request has sessionToken")
|
||||
debug("[getSessionKernel] Request has sessionToken")
|
||||
const {handle, version, hashedPublicData} = parseSessionToken(sessionToken)
|
||||
|
||||
if (!handle) {
|
||||
25
packages/core/src/server/index.ts
Normal file
25
packages/core/src/server/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* IF YOU CHANGE THIS FILE
|
||||
* You also need to update the rewrite map in
|
||||
* packages/babel-preset/src/rewrite-imports.ts
|
||||
*/
|
||||
export {
|
||||
getAllMiddlewareForModule,
|
||||
handleRequestWithMiddleware,
|
||||
connectMiddleware,
|
||||
} from "./middleware"
|
||||
|
||||
export {invokeWithMiddleware} from "./invoke-with-middleware"
|
||||
|
||||
export {paginate, isLocalhost} from "./server-utils"
|
||||
export type {PaginateArgs} from "./server-utils"
|
||||
|
||||
export {resolver} from "./resolver"
|
||||
export type {AuthenticatedMiddlewareCtx} from "./resolver"
|
||||
|
||||
export {sessionMiddleware, simpleRolesIsAuthorized, getSession} from "./auth/sessions"
|
||||
export type {SimpleRolesIsAuthorized} from "./auth/sessions"
|
||||
export {passportAuth} from "./auth/passport-adapter"
|
||||
export {SecurePassword, hash256, generateToken} from "./auth/auth-utils"
|
||||
|
||||
export {rpcApiHandler} from "./rpc-server"
|
||||
@@ -2,8 +2,8 @@ import delay from "delay"
|
||||
import http, {IncomingMessage, ServerResponse} from "http"
|
||||
import fetch from "node-fetch"
|
||||
import listen from "test-listen"
|
||||
import {invokeWithMiddleware} from "./invoke"
|
||||
import {EnhancedResolver} from "./types"
|
||||
import {EnhancedResolver} from "../types"
|
||||
import {invokeWithMiddleware} from "./invoke-with-middleware"
|
||||
|
||||
describe("invokeWithMiddleware", () => {
|
||||
it("works without middleware", async () => {
|
||||
48
packages/core/src/server/invoke-with-middleware.ts
Normal file
48
packages/core/src/server/invoke-with-middleware.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import {baseLogger, chalk, log as displayLog} from "@blitzjs/display"
|
||||
import {EnhancedResolver, InvokeWithMiddlewareConfig, MiddlewareResponse, Resolver} from "../types"
|
||||
import {prettyMs} from "../utils/pretty-ms"
|
||||
import {getAllMiddlewareForModule, handleRequestWithMiddleware} from "./middleware"
|
||||
|
||||
export async function invokeWithMiddleware<TInput, TResult>(
|
||||
resolver: Resolver<TInput, TResult>,
|
||||
params: TInput,
|
||||
ctx: InvokeWithMiddlewareConfig,
|
||||
): Promise<TResult> {
|
||||
if (!ctx.req) {
|
||||
throw new Error("You must provide `req` in third argument of invokeWithMiddleware()")
|
||||
}
|
||||
if (!ctx.res) {
|
||||
throw new Error("You must provide `res` in third argument of invokeWithMiddleware()")
|
||||
}
|
||||
const enhancedResolver = (resolver as unknown) as EnhancedResolver<TInput, TResult>
|
||||
|
||||
const middleware = getAllMiddlewareForModule(enhancedResolver)
|
||||
|
||||
if (ctx.middleware) {
|
||||
middleware.push(...ctx.middleware)
|
||||
}
|
||||
|
||||
middleware.push(async (_req, res, next) => {
|
||||
const log = baseLogger().getChildLogger({prefix: [enhancedResolver._meta.name + "()"]})
|
||||
displayLog.newline()
|
||||
try {
|
||||
log.info(chalk.dim("Starting with input:"), params)
|
||||
const startTime = Date.now()
|
||||
|
||||
const result = await enhancedResolver(params, res.blitzCtx)
|
||||
|
||||
const duration = Date.now() - startTime
|
||||
log.info(chalk.dim(`Finished in ${prettyMs(duration)}`))
|
||||
displayLog.newline()
|
||||
|
||||
res.blitzResult = result
|
||||
return next()
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
|
||||
await handleRequestWithMiddleware(ctx.req, ctx.res, middleware)
|
||||
|
||||
return (ctx.res as MiddlewareResponse).blitzResult as TResult
|
||||
}
|
||||
@@ -2,9 +2,8 @@ import http from "http"
|
||||
import {apiResolver} from "next/dist/next-server/server/api-utils"
|
||||
import fetch from "node-fetch"
|
||||
import listen from "test-listen"
|
||||
import {BlitzApiRequest, BlitzApiResponse} from "."
|
||||
import {BlitzApiRequest, BlitzApiResponse, Middleware} from "../types"
|
||||
import {handleRequestWithMiddleware} from "./middleware"
|
||||
import {Middleware} from "./types"
|
||||
|
||||
describe("handleRequestWithMiddleware", () => {
|
||||
it("works without await", async () => {
|
||||
@@ -2,18 +2,17 @@
|
||||
import {getConfig} from "@blitzjs/config"
|
||||
import {baseLogger, log} from "@blitzjs/display"
|
||||
import {IncomingMessage, ServerResponse} from "http"
|
||||
import {BlitzApiRequest, BlitzApiResponse} from "."
|
||||
import {
|
||||
BlitzApiRequest,
|
||||
BlitzApiResponse,
|
||||
ConnectMiddleware,
|
||||
Ctx,
|
||||
EnhancedResolver,
|
||||
Middleware,
|
||||
MiddlewareNext,
|
||||
MiddlewareRequest,
|
||||
MiddlewareResponse,
|
||||
} from "./types"
|
||||
|
||||
export interface DefaultCtx {}
|
||||
export interface Ctx extends DefaultCtx {}
|
||||
} from "../types"
|
||||
|
||||
export function getAllMiddlewareForModule<TInput, TResult>(
|
||||
resolverModule: EnhancedResolver<TInput, TResult>,
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as z from "zod"
|
||||
import {Ctx, resolver} from "../src"
|
||||
import {Ctx} from "../types"
|
||||
import {resolver} from "./resolver"
|
||||
|
||||
describe("resolver", () => {
|
||||
it("should typecheck and pass along value", async () => {
|
||||
@@ -1,7 +1,6 @@
|
||||
import {infer as zInfer, ZodSchema} from "zod"
|
||||
import {Ctx} from "./middleware"
|
||||
import {AuthenticatedSessionContext, SessionContext, SessionContextBase} from "./supertokens"
|
||||
import {Await, EnsurePromise} from "./types"
|
||||
import {AuthenticatedSessionContext, SessionContext, SessionContextBase} from "../auth/auth-types"
|
||||
import {Await, Ctx, EnsurePromise} from "../types"
|
||||
|
||||
interface ResultWithContext<Result = unknown, Context = unknown> {
|
||||
__blitz: true
|
||||
@@ -1,10 +1,11 @@
|
||||
import {connectMiddleware, EnhancedResolver} from "@blitzjs/core"
|
||||
import delay from "delay"
|
||||
import http from "http"
|
||||
import {apiResolver} from "next/dist/next-server/server/api-utils"
|
||||
import fetch from "node-fetch"
|
||||
import listen from "test-listen"
|
||||
import {rpcApiHandler} from "../src/rpc"
|
||||
import {EnhancedResolver} from "../types"
|
||||
import {connectMiddleware} from "./middleware"
|
||||
import {rpcApiHandler} from "./rpc-server"
|
||||
|
||||
describe("rpcMiddleware", () => {
|
||||
describe("HEAD", () => {
|
||||
@@ -1,14 +1,9 @@
|
||||
import {
|
||||
BlitzApiRequest,
|
||||
BlitzApiResponse,
|
||||
EnhancedResolver,
|
||||
handleRequestWithMiddleware,
|
||||
Middleware,
|
||||
prettyMs,
|
||||
} from "@blitzjs/core"
|
||||
import {baseLogger, log as displayLog} from "@blitzjs/display"
|
||||
import chalk from "chalk"
|
||||
import {deserialize, serialize} from "superjson"
|
||||
import {BlitzApiRequest, BlitzApiResponse, EnhancedResolver, Middleware} from "../types"
|
||||
import {prettyMs} from "../utils/pretty-ms"
|
||||
import {handleRequestWithMiddleware} from "./middleware"
|
||||
|
||||
const rpcMiddleware = <TInput, TResult>(
|
||||
resolver: EnhancedResolver<TInput, TResult>,
|
||||
@@ -1,4 +1,4 @@
|
||||
import {paginate} from "../src"
|
||||
import {paginate} from "./server-utils"
|
||||
|
||||
describe("paginate", () => {
|
||||
const dummyPaginationPromises = {
|
||||
@@ -1,6 +1,18 @@
|
||||
import {PaginationArgumentError} from "./errors"
|
||||
import {IncomingMessage} from "http"
|
||||
import {PaginationArgumentError} from "../errors"
|
||||
import {BlitzApiRequest} from "../types"
|
||||
|
||||
type PaginateArgs<QueryResult> = {
|
||||
export function isLocalhost(req: BlitzApiRequest | IncomingMessage): boolean {
|
||||
let {host} = req.headers
|
||||
let localhost = false
|
||||
if (host) {
|
||||
host = host.split(":")[0]
|
||||
localhost = host === "localhost"
|
||||
}
|
||||
return localhost
|
||||
}
|
||||
|
||||
export type PaginateArgs<QueryResult> = {
|
||||
skip?: number
|
||||
take?: number
|
||||
maxTake?: number
|
||||
@@ -1,131 +0,0 @@
|
||||
import {useEffect, useState} from "react"
|
||||
import {getBlitzRuntimeData} from "./blitz-data"
|
||||
import {COOKIE_CSRF_TOKEN, COOKIE_LEGACY_CSRF_TOKEN} from "./constants"
|
||||
import {AuthenticationError} from "./errors"
|
||||
import {Ctx} from "./middleware"
|
||||
import {publicDataStore} from "./public-data-store"
|
||||
import {EmptyPublicData, IsAuthorizedArgs, PublicData} from "./types"
|
||||
import {isServer} from "./utils"
|
||||
import {readCookie} from "./utils/cookie"
|
||||
|
||||
export interface SessionModel extends Record<any, any> {
|
||||
handle: string
|
||||
userId?: PublicData["userId"]
|
||||
expiresAt?: Date
|
||||
hashedSessionToken?: string
|
||||
antiCSRFToken?: string
|
||||
publicData?: string
|
||||
privateData?: string
|
||||
}
|
||||
|
||||
export type SessionConfig = {
|
||||
sessionExpiryMinutes?: number
|
||||
method?: "essential" | "advanced"
|
||||
sameSite?: "none" | "lax" | "strict"
|
||||
domain?: string
|
||||
publicDataKeysToSyncAcrossSessions?: string[]
|
||||
getSession: (handle: string) => Promise<SessionModel | null>
|
||||
getSessions: (userId: PublicData["userId"]) => Promise<SessionModel[]>
|
||||
createSession: (session: SessionModel) => Promise<SessionModel>
|
||||
updateSession: (handle: string, session: Partial<SessionModel>) => Promise<SessionModel>
|
||||
deleteSession: (handle: string) => Promise<SessionModel>
|
||||
isAuthorized: (data: {ctx: Ctx; args: any[]}) => boolean
|
||||
}
|
||||
|
||||
export interface SessionContextBase {
|
||||
$handle: string | null
|
||||
$publicData: unknown
|
||||
$authorize(...args: IsAuthorizedArgs): asserts this is AuthenticatedSessionContext
|
||||
// $isAuthorized cannot have assertion return type because it breaks advanced use cases
|
||||
// with multiple isAuthorized calls
|
||||
$isAuthorized: (...args: IsAuthorizedArgs) => boolean
|
||||
$create: (publicData: PublicData, privateData?: Record<any, any>) => Promise<void>
|
||||
$revoke: () => Promise<void>
|
||||
$revokeAll: () => Promise<void>
|
||||
$getPrivateData: () => Promise<Record<any, any>>
|
||||
$setPrivateData: (data: Record<any, any>) => Promise<void>
|
||||
$setPublicData: (data: Partial<Omit<PublicData, "userId">>) => Promise<void>
|
||||
}
|
||||
|
||||
// Could be anonymous
|
||||
export interface SessionContext extends SessionContextBase, EmptyPublicData {
|
||||
$publicData: Partial<PublicData> | EmptyPublicData
|
||||
}
|
||||
|
||||
export interface AuthenticatedSessionContext extends SessionContextBase, PublicData {
|
||||
userId: PublicData["userId"]
|
||||
$publicData: PublicData
|
||||
}
|
||||
|
||||
export const getAntiCSRFToken = () =>
|
||||
readCookie(COOKIE_CSRF_TOKEN()) || readCookie(COOKIE_LEGACY_CSRF_TOKEN())
|
||||
|
||||
export interface ClientSession extends EmptyPublicData {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
export interface AuthenticatedClientSession extends PublicData {
|
||||
isLoading: boolean
|
||||
}
|
||||
|
||||
interface UseSessionOptions {
|
||||
initialPublicData?: PublicData
|
||||
suspense?: boolean | null
|
||||
}
|
||||
|
||||
export const useSession = (options: UseSessionOptions = {}): ClientSession => {
|
||||
const suspense = options?.suspense ?? getBlitzRuntimeData().suspenseEnabled
|
||||
|
||||
let initialState: ClientSession
|
||||
if (options.initialPublicData) {
|
||||
initialState = {...options.initialPublicData, isLoading: false}
|
||||
} else if (suspense) {
|
||||
if (isServer) {
|
||||
throw new Promise((_) => {})
|
||||
} else {
|
||||
initialState = {...publicDataStore.getData(), isLoading: false}
|
||||
}
|
||||
} else {
|
||||
initialState = {...publicDataStore.emptyPublicData, isLoading: true}
|
||||
}
|
||||
|
||||
const [session, setSession] = useState(initialState)
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize on mount
|
||||
setSession({...publicDataStore.getData(), isLoading: false})
|
||||
const subscription = publicDataStore.observable.subscribe((data) =>
|
||||
setSession({...data, isLoading: false}),
|
||||
)
|
||||
return subscription.unsubscribe
|
||||
}, [])
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
export const useAuthenticatedSession = (
|
||||
options: UseSessionOptions = {},
|
||||
): AuthenticatedClientSession => {
|
||||
useAuthorize()
|
||||
return useSession(options) as AuthenticatedClientSession
|
||||
}
|
||||
|
||||
export const useAuthorize = () => {
|
||||
useAuthorizeIf(true)
|
||||
}
|
||||
|
||||
export const useAuthorizeIf = (condition?: boolean) => {
|
||||
useEffect(() => {
|
||||
if (condition && !publicDataStore.getData().userId) {
|
||||
const error = new AuthenticationError()
|
||||
delete error.stack
|
||||
throw error
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const useRedirectAuthenticated = (to: string) => {
|
||||
if (typeof window !== "undefined" && publicDataStore.getData().userId) {
|
||||
window.location.replace(to)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import {IncomingMessage, ServerResponse} from "http"
|
||||
import {AppProps as NextAppProps} from "next/app"
|
||||
import {NextRouter} from "next/router"
|
||||
import {
|
||||
NextApiRequest,
|
||||
NextApiResponse,
|
||||
@@ -11,12 +10,10 @@ import {
|
||||
import {AuthenticateOptions, Strategy} from "passport"
|
||||
import {MutateOptions, MutationResult} from "react-query"
|
||||
import {BlitzRuntimeData} from "./blitz-data"
|
||||
import {Ctx} from "./middleware"
|
||||
import {useParams} from "./use-params"
|
||||
import {useRouterQuery} from "./use-router-query"
|
||||
|
||||
export type {
|
||||
GetServerSideProps,
|
||||
GetServerSidePropsContext,
|
||||
GetServerSidePropsResult,
|
||||
GetStaticPaths,
|
||||
GetStaticPathsContext,
|
||||
@@ -45,28 +42,8 @@ export type BlitzPage<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
redirectAuthenticatedTo?: string
|
||||
}
|
||||
|
||||
export interface BlitzRouter extends NextRouter {
|
||||
query: ReturnType<typeof useRouterQuery>
|
||||
params: ReturnType<typeof useParams>
|
||||
}
|
||||
|
||||
export interface Session {
|
||||
// isAuthorize can be injected here (see supertokens.ts)
|
||||
// PublicData can be injected here (see supertokens.ts)
|
||||
}
|
||||
|
||||
export type PublicData = "PublicData" extends keyof Session
|
||||
? Session["PublicData"]
|
||||
: {userId: unknown}
|
||||
export interface EmptyPublicData extends Partial<Omit<PublicData, "userId">> {
|
||||
userId: PublicData["userId"] | null
|
||||
}
|
||||
|
||||
export type IsAuthorizedArgs = "isAuthorized" extends keyof Session
|
||||
? "args" extends keyof Parameters<Session["isAuthorized"]>[0]
|
||||
? Parameters<Session["isAuthorized"]>[0]["args"]
|
||||
: unknown[]
|
||||
: unknown[]
|
||||
export interface DefaultCtx {}
|
||||
export interface Ctx extends DefaultCtx {}
|
||||
|
||||
export interface MiddlewareRequest extends BlitzApiRequest {
|
||||
protocol?: string
|
||||
@@ -148,12 +125,6 @@ export type BlitzPassportConfig = {
|
||||
secureProxy?: boolean
|
||||
}
|
||||
|
||||
export type VerifyCallbackResult = {
|
||||
publicData: PublicData
|
||||
privateData?: Record<string, any>
|
||||
redirectUrl?: string
|
||||
}
|
||||
|
||||
// The actual resolver source definition
|
||||
export type Resolver<TInput, TResult> = (input: TInput, ctx?: any) => Promise<TResult>
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
usePaginatedQuery as usePaginatedReactQuery,
|
||||
useQuery as useReactQuery,
|
||||
} from "react-query"
|
||||
import {useSession} from "./supertokens"
|
||||
import {useSession} from "./auth/auth-client"
|
||||
import {useRouter} from "./router"
|
||||
import {FirstParam, PromiseReturnType, QueryFn} from "./types"
|
||||
import {useRouter} from "./use-router"
|
||||
import {isClient} from "./utils"
|
||||
import {
|
||||
emptyQueryFn,
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import {useRouter} from "next/router"
|
||||
import {useMemo} from "react"
|
||||
import {parse} from "url"
|
||||
|
||||
export function useRouterQuery() {
|
||||
const router = useRouter()
|
||||
|
||||
const query = useMemo(() => {
|
||||
const {query} = parse(router.asPath, true)
|
||||
|
||||
return query
|
||||
}, [router.asPath])
|
||||
|
||||
return query
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import {useRouter as useNextRouter} from "next/router"
|
||||
import {useMemo} from "react"
|
||||
import {BlitzRouter} from "./types"
|
||||
import {useParams} from "./use-params"
|
||||
import {useRouterQuery} from "./use-router-query"
|
||||
|
||||
// TODO - we have to explicitly define the return type otherwise TS complains about
|
||||
// NextHistoryState and TransitionOptions not being exported from Next.js code
|
||||
export function useRouter() {
|
||||
const router = useNextRouter()
|
||||
const query = useRouterQuery()
|
||||
const params = useParams()
|
||||
|
||||
return useMemo(() => {
|
||||
return {...router, query, params}
|
||||
}, [params, query, router]) as BlitzRouter
|
||||
}
|
||||
45
packages/core/src/utils/date-utils.ts
Normal file
45
packages/core/src/utils/date-utils.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
const MILLISECONDS_IN_MINUTE = 60000
|
||||
const MINUTES_IN_YEAR = 525960
|
||||
|
||||
export const isPast = (date: Date) => {
|
||||
return date.getTime() < Date.now()
|
||||
}
|
||||
|
||||
export function differenceInMilliseconds(dateLeft: Date, dateRight: Date) {
|
||||
return dateLeft.getTime() - dateRight.getTime()
|
||||
}
|
||||
|
||||
export function differenceInMinutes(dateLeft: Date, dateRight: Date) {
|
||||
const diff = differenceInMilliseconds(dateLeft, dateRight) / MILLISECONDS_IN_MINUTE
|
||||
return diff > 0 ? Math.floor(diff) : Math.ceil(diff)
|
||||
}
|
||||
|
||||
export function addMilliseconds(date: Date, amount: number): Date {
|
||||
const timestamp = date.getTime()
|
||||
const cleanAmount = toInteger(amount)
|
||||
return new Date(timestamp + cleanAmount)
|
||||
}
|
||||
|
||||
export function addMinutes(date: Date, amount: number): Date {
|
||||
const cleanAmount = toInteger(amount)
|
||||
return addMilliseconds(date, cleanAmount * MILLISECONDS_IN_MINUTE)
|
||||
}
|
||||
|
||||
export function addYears(date: Date, amount: number): Date {
|
||||
const cleanAmount = toInteger(amount)
|
||||
return addMinutes(date, cleanAmount * MINUTES_IN_YEAR)
|
||||
}
|
||||
|
||||
export function toInteger(dirtyNumber: unknown) {
|
||||
if (dirtyNumber === null || dirtyNumber === true || dirtyNumber === false) {
|
||||
return NaN
|
||||
}
|
||||
|
||||
const number = Number(dirtyNumber)
|
||||
|
||||
if (isNaN(number)) {
|
||||
return number
|
||||
}
|
||||
|
||||
return number < 0 ? Math.ceil(number) : Math.floor(number)
|
||||
}
|
||||
@@ -1,19 +1,6 @@
|
||||
import {IncomingMessage} from "http"
|
||||
import {BlitzApiRequest} from "../"
|
||||
|
||||
export const isServer = typeof window === "undefined"
|
||||
export const isClient = typeof window !== "undefined"
|
||||
|
||||
export function isLocalhost(req: BlitzApiRequest | IncomingMessage): boolean {
|
||||
let {host} = req.headers
|
||||
let localhost = false
|
||||
if (host) {
|
||||
host = host.split(":")[0]
|
||||
localhost = host === "localhost"
|
||||
}
|
||||
return localhost
|
||||
}
|
||||
|
||||
export function clientDebug(...args: any) {
|
||||
if (typeof window !== "undefined" && (window as any)["DEBUG_BLITZ"]) {
|
||||
console.log("[BLITZ]", ...args)
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/* eslint @typescript-eslint/no-floating-promises: off */
|
||||
import {act} from "@testing-library/react-hooks"
|
||||
import {toBase64} from "b64-lite"
|
||||
import {renderHook} from "../../test/test-utils"
|
||||
import {publicDataStore} from "../public-data-store"
|
||||
import {useSession} from "../supertokens"
|
||||
import {parsePublicDataToken} from "./tokens"
|
||||
|
||||
describe("supertokens", () => {
|
||||
describe("parsePublicDataToken", () => {
|
||||
it("throws if token is empty", () => {
|
||||
const ret = () => parsePublicDataToken("")
|
||||
expect(ret).toThrow("[parsePublicDataToken] Failed: token is empty")
|
||||
})
|
||||
|
||||
it("throws if the token cannot be parsed", () => {
|
||||
const invalidJSON = "{"
|
||||
const ret = () => parsePublicDataToken(toBase64(invalidJSON))
|
||||
|
||||
expect(ret).toThrowError("[parsePublicDataToken] Failed to parse publicDataStr: {")
|
||||
})
|
||||
|
||||
it("parses the public data", () => {
|
||||
const validJSON = '{"foo": "bar"}'
|
||||
expect(parsePublicDataToken(toBase64(validJSON))).toEqual({
|
||||
publicData: {foo: "bar"},
|
||||
})
|
||||
})
|
||||
|
||||
it("parses the public data containing unicode chars", () => {
|
||||
const data = '"foo-κόσμε-żółć-平仮名"'
|
||||
expect(parsePublicDataToken(toBase64(data))).toEqual({
|
||||
publicData: "foo-κόσμε-żółć-平仮名",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("useSession", () => {
|
||||
it("returns empty at when no value is set", () => {
|
||||
const {result} = renderHook(() => useSession())
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
...publicDataStore.emptyPublicData,
|
||||
})
|
||||
})
|
||||
|
||||
it("subscribes to the public data store", () => {
|
||||
const {result} = renderHook(() => useSession())
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["foo"], userId: "bar"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["baz"], userId: "boo"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "boo",
|
||||
roles: ["baz"],
|
||||
})
|
||||
})
|
||||
|
||||
it("un-subscribes from the public data store on unmount", () => {
|
||||
const {result, unmount} = renderHook(() => useSession())
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["foo"], userId: "bar"} as any)
|
||||
})
|
||||
|
||||
act(() => {
|
||||
unmount()
|
||||
})
|
||||
|
||||
act(() => {
|
||||
publicDataStore.updateState({roles: ["baz"], userId: "boo"} as any)
|
||||
})
|
||||
|
||||
expect(result.current).toEqual({
|
||||
isLoading: false,
|
||||
userId: "bar",
|
||||
roles: ["foo"],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,7 @@ jest.mock("@blitzjs/config", () => {
|
||||
}
|
||||
})
|
||||
|
||||
import {withBlitz} from "../src/with-blitz"
|
||||
import {withBlitz} from "./with-blitz"
|
||||
|
||||
describe("withBlitz", () => {
|
||||
it("alters the webpack config as expected", () => {
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable es5/no-es6-methods -- file only used on the server */
|
||||
import {getProjectRoot} from "@blitzjs/config"
|
||||
import fs from "fs"
|
||||
import path from "path"
|
||||
@@ -34,23 +35,25 @@ export function withBlitz(nextConfig: any) {
|
||||
config.module = config.module ?? {}
|
||||
config.module.rules = config.module.rules ?? []
|
||||
const excluded = [
|
||||
/node_modules[\\/]passport/,
|
||||
/node_modules[\\/]cookie-session/,
|
||||
/node_modules[\\/]secure-password/,
|
||||
/node_modules[\\/]npm-which/,
|
||||
/node_modules[\\/]cross-spawn/,
|
||||
/node_modules[\\/]node-libs-browser/,
|
||||
/node_modules[\\/]crypto-browserify/,
|
||||
/@blitzjs[\\/]display/,
|
||||
/[\\/]npm-which[\\/]/,
|
||||
/[\\/]cross-spawn[\\/]/,
|
||||
/@blitzjs[\\/]config/,
|
||||
/blitz[\\/]packages[\\/]config/,
|
||||
/blitz[\\/]packages[\\/]display/,
|
||||
]
|
||||
excluded.forEach((excluded) => {
|
||||
config.module.rules.push({test: excluded, use: {loader: "null-loader"}})
|
||||
})
|
||||
|
||||
if (normalizedConfig.experimental?.isomorphicResolverImports) {
|
||||
config.module.rules.push({
|
||||
test: /@blitzjs[\\/]core[\\/]server/,
|
||||
use: {loader: "null-loader"},
|
||||
})
|
||||
config.module.rules.push({
|
||||
test: /blitz[\\/]packages[\\/]core[\\/]server/,
|
||||
use: {loader: "null-loader"},
|
||||
})
|
||||
|
||||
config.plugins.push(
|
||||
new options.webpack.NormalModuleReplacementPlugin(
|
||||
/[/\\]?(mutations|queries)[/\\]/,
|
||||
@@ -1,24 +0,0 @@
|
||||
import {WithRouterProps as WithNextRouterProps} from "next/dist/client/with-router"
|
||||
import {NextRouter, withRouter as withNextRouter} from "next/router"
|
||||
import React from "react"
|
||||
import {extractRouterParams, useParams} from "./use-params"
|
||||
import {useRouterQuery} from "./use-router-query"
|
||||
|
||||
export {RouterContext} from "next/dist/next-server/lib/router-context"
|
||||
|
||||
export interface BlitzRouter extends NextRouter {
|
||||
params: ReturnType<typeof extractRouterParams>
|
||||
}
|
||||
|
||||
export interface WithRouterProps {
|
||||
router: BlitzRouter
|
||||
}
|
||||
|
||||
export function withRouter(WrappedComponent: React.ComponentType<WithRouterProps>) {
|
||||
const Wrapper: React.FC<WithNextRouterProps> = ({router}) => {
|
||||
const query = useRouterQuery()
|
||||
const params = useParams()
|
||||
return <WrappedComponent router={{...router, query, params}} />
|
||||
}
|
||||
return withNextRouter(Wrapper)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {extractRouterParams, useParam, useParams} from "../src/use-params"
|
||||
import {extractRouterParams, useParam, useParams} from "../src/router/router-hooks"
|
||||
import {renderHook} from "./test-utils"
|
||||
|
||||
describe("extractRouterParams", () => {
|
||||
5
packages/core/with-blitz/package.json
Normal file
5
packages/core/with-blitz/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"main": "dist/blitzjs-core-with-blitz.cjs.js",
|
||||
"module": "dist/blitzjs-core-with-blitz.esm.js",
|
||||
"types": "dist/blitzjs-core-with-blitz.d.ts"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("@blitzjs/server")
|
||||
const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz")
|
||||
|
||||
module.exports = {
|
||||
middleware: [
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { DefaultCtx, SessionContext } from "blitz"
|
||||
import { SimpleRolesIsAuthorized } from "@blitzjs/server"
|
||||
import { DefaultCtx, SessionContext, SimpleRolesIsAuthorized } from "blitz"
|
||||
import { User } from "db"
|
||||
|
||||
// Note: You should switch to Postgres and then use a DB enum for role type
|
||||
|
||||
@@ -24,11 +24,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@blitzjs/file-pipeline": "0.30.7",
|
||||
"b64-lite": "^1.4.0",
|
||||
"chalk": "^4.1.0",
|
||||
"cookie": "^0.4.1",
|
||||
"cross-spawn": "7.0.3",
|
||||
"date-fns": "^2.16.1",
|
||||
"detect-port": "1.3.0",
|
||||
"expand-tilde": "2.0.2",
|
||||
"fast-glob": "3.2.5",
|
||||
@@ -36,13 +32,7 @@
|
||||
"from2": "2.3.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"gulp-if": "3.0.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"lodash": "^4.0.0",
|
||||
"module-alias": "2.2.2",
|
||||
"nanoid": "^3.1.20",
|
||||
"next": "10.0.7",
|
||||
"null-loader": "4.0.1",
|
||||
"ora": "^5.3.0",
|
||||
"parallel-transform": "1.2.0",
|
||||
"parse-gitignore": "1.0.1",
|
||||
"pirates": "4.0.1",
|
||||
@@ -51,15 +41,14 @@
|
||||
"readable-stream": "3.6.0",
|
||||
"resolve-cwd": "3.0.0",
|
||||
"slash": "^3.0.0",
|
||||
"superjson": "1.7.2",
|
||||
"through2": "4.0.2",
|
||||
"vinyl": "2.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@blitzjs/config": "0.30.7",
|
||||
"@blitzjs/core": "0.30.7",
|
||||
"@blitzjs/display": "0.30.7",
|
||||
"next-transpile-modules": "6.1.0"
|
||||
"@blitzjs/config": "*",
|
||||
"@blitzjs/core": "*",
|
||||
"@blitzjs/display": "*",
|
||||
"next": "10.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@blitzjs/config": "*",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export {withBlitz} from "./with-blitz"
|
||||
export {build} from "./build"
|
||||
export {dev} from "./dev"
|
||||
export {prod} from "./prod"
|
||||
@@ -7,19 +6,3 @@ export {normalize} from "./config"
|
||||
export type {ServerConfig} from "./config"
|
||||
export {resolveBinAsync} from "./resolve-bin-async"
|
||||
export {ManifestLoader} from "./stages/manifest"
|
||||
export * from "./rpc"
|
||||
export * from "./supertokens"
|
||||
|
||||
// -----------------
|
||||
// For custom server
|
||||
// -----------------
|
||||
import next from "next"
|
||||
|
||||
// Support commonjs `require('blitz')`
|
||||
if (process.env.BLITZ_PROD_BUILD) {
|
||||
module.exports = next
|
||||
exports = module.exports
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default next
|
||||
|
||||
@@ -40,7 +40,7 @@ export const createStageConfig: Stage = ({config, processNewFile, processNewChil
|
||||
cwd: config.src,
|
||||
path: resolve(config.src, "next.config.js"),
|
||||
contents: Buffer.from(`
|
||||
const {withBlitz} = require('@blitzjs/server');
|
||||
const {withBlitz} = require('@blitzjs/core/with-blitz');
|
||||
const config = require('./blitz.config.js');
|
||||
module.exports = withBlitz(config);
|
||||
`),
|
||||
@@ -72,7 +72,7 @@ module.exports = withBlitz(config);
|
||||
})
|
||||
|
||||
file.contents = Buffer.from(`
|
||||
const {withBlitz} = require('@blitzjs/server');
|
||||
const {withBlitz} = require('@blitzjs/core/with-blitz');
|
||||
const vercelConfig = require('./next-vercel.config.js');
|
||||
const config = require('./blitz.config.js');
|
||||
module.exports = withBlitz({...config, ...vercelConfig});
|
||||
|
||||
@@ -60,8 +60,8 @@ export default getIsomorphicEnhancedResolver(
|
||||
const apiHandlerTemplate = (originalPath: string, useTypes: boolean) => `
|
||||
// This imports the output of getIsomorphicEnhancedResolver()
|
||||
import enhancedResolver from '${originalPath}'
|
||||
import {getAllMiddlewareForModule} from '@blitzjs/core'
|
||||
import {rpcApiHandler} from '@blitzjs/server'
|
||||
import {getAllMiddlewareForModule} from '@blitzjs/core/server'
|
||||
import {rpcApiHandler} from '@blitzjs/core/server'
|
||||
import path from 'path'
|
||||
|
||||
// Ensure these files are not eliminated by trace-based tree-shaking (like Vercel)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
|
||||
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
|
||||
|
||||
const withMDX = require("@next/mdx")({
|
||||
extension: /\.mdx?$/,
|
||||
|
||||
67
yarn.lock
67
yarn.lock
@@ -3726,7 +3726,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/b64-lite/-/b64-lite-1.3.0.tgz#0c0351b7d91e0c37c9376a2882b3e794fc69c5b3"
|
||||
integrity sha512-xrTKDOdOCLtdWAn+XnFWVoVNdump98dtZEpzQcw+BCtMdrGNVdbg6i6D1b9IU7HWzQm3ypi7hoLuXhrLiJn3bw==
|
||||
|
||||
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.12", "@types/babel__core@^7.1.7":
|
||||
"@types/babel-plugin-tester@^9.0.0":
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel-plugin-tester/-/babel-plugin-tester-9.0.1.tgz#5f1dc3b9da821b119f74544cda308d1e040c4cba"
|
||||
integrity sha512-RGZzADCDXd9MIxQzOM2I6guMFRtZw+XwVx+vmPliPAvGeh228ZYA3hoTEF9u3jNR2Nf/gyb55HI3D9pMl3m1LA==
|
||||
dependencies:
|
||||
"@types/babel__core" "*"
|
||||
"@types/prettier" "*"
|
||||
|
||||
"@types/babel__core@*", "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.12", "@types/babel__core@^7.1.7":
|
||||
version "7.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.12.tgz#4d8e9e51eb265552a7e4f1ff2219ab6133bdfb2d"
|
||||
integrity sha512-wMTHiiTiBAAPebqaPiPDLFA4LYPKr6Ph0Xq/6rq1Ur3v66HXyG+clfR9CNETkD7MQS8ZHvpQOtA53DLws5WAEQ==
|
||||
@@ -4101,11 +4109,6 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/module-alias@2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/module-alias/-/module-alias-2.0.0.tgz#882668f8b8cdbda44812c3b592c590909e18849e"
|
||||
integrity sha512-e3sW4oEH0qS1QxSfX7PT6xIi5qk/YSMsrB9Lq8EtkhQBZB+bKyfkP+jpLJRySanvBhAQPSv2PEBe81M8Iy/7yg==
|
||||
|
||||
"@types/node-fetch@2.5.8":
|
||||
version "2.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
|
||||
@@ -4211,6 +4214,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/pluralize/-/pluralize-0.0.29.tgz#6ffa33ed1fc8813c469b859681d09707eb40d03c"
|
||||
integrity sha512-BYOID+l2Aco2nBik+iYS4SZX0Lf20KPILP5RGmM1IgzdwNdTs0eebiFriOPcej1sX9mLnSoiNte5zcFxssgpGA==
|
||||
|
||||
"@types/prettier@*":
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.2.1.tgz#374e31645d58cb18a07b3ecd8e9dede4deb2cccd"
|
||||
integrity sha512-DxZZbyMAM9GWEzXL+BMZROWz9oo6A9EilwwOMET2UVu2uZTqMWS5S69KVtuVKaRjCUpcrOXRalet86/OpG4kqw==
|
||||
|
||||
"@types/prettier@2.1.6", "@types/prettier@^2.0.0":
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.1.6.tgz#f4b1efa784e8db479cdb8b14403e2144b1e9ff03"
|
||||
@@ -5536,6 +5544,16 @@ babel-plugin-syntax-jsx@6.18.0:
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946"
|
||||
integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=
|
||||
|
||||
babel-plugin-tester@10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-tester/-/babel-plugin-tester-10.0.0.tgz#7ece63c50ee76cc4b486f99d6a2a4cf78c836fe5"
|
||||
integrity sha512-RHlDIUtfdlFvUXHTWroyL3iz9OZnPYgcaDIUrp4ejXrzSClRx3ldJlhaOauvblJqZEPb/7HR32gLZT45lgOXFg==
|
||||
dependencies:
|
||||
"@types/babel-plugin-tester" "^9.0.0"
|
||||
lodash.mergewith "^4.6.2"
|
||||
prettier "^2.0.1"
|
||||
strip-indent "^3.0.0"
|
||||
|
||||
babel-plugin-transform-inline-environment-variables@0.4.3:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-environment-variables/-/babel-plugin-transform-inline-environment-variables-0.4.3.tgz#a3b09883353be8b5e2336e3ff1ef8a5d93f9c489"
|
||||
@@ -7039,7 +7057,7 @@ cookie-session@^1.4.0:
|
||||
debug "2.6.9"
|
||||
on-headers "~1.0.2"
|
||||
|
||||
cookie@^0.4.1:
|
||||
cookie@0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
@@ -8204,14 +8222,6 @@ enhanced-resolve@^4.5.0:
|
||||
memory-fs "^0.5.0"
|
||||
tapable "^1.0.0"
|
||||
|
||||
enhanced-resolve@^5.3.2:
|
||||
version "5.4.1"
|
||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.4.1.tgz#c89b0c34f17f931902ef2913a125d4b825b49b6f"
|
||||
integrity sha512-4GbyIMzYktTFoRSmkbgZ1LU+RXwf4AQ8Z+rSuuh1dC8plp0PPeaWvx6+G4hh4KnUJ48VoxKbNyA1QQQIUpXjYA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
tapable "^2.2.0"
|
||||
|
||||
enquirer@2.3.6, enquirer@^2.3.5, enquirer@^2.3.6:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
|
||||
@@ -12157,7 +12167,7 @@ jsonparse@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
|
||||
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=
|
||||
|
||||
jsonwebtoken@^8.5.1:
|
||||
jsonwebtoken@8.5.1:
|
||||
version "8.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d"
|
||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==
|
||||
@@ -12739,6 +12749,11 @@ lodash.memoize@^4.1.2:
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.mergewith@^4.6.2:
|
||||
version "4.6.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
|
||||
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
|
||||
|
||||
lodash.once@^4.0.0, lodash.once@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||
@@ -13434,11 +13449,6 @@ modify-values@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
|
||||
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==
|
||||
|
||||
module-alias@2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0"
|
||||
integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==
|
||||
|
||||
moment@^2.22.1, moment@^2.27.0:
|
||||
version "2.29.1"
|
||||
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
|
||||
@@ -13620,14 +13630,6 @@ new-github-issue-url@^0.2.1:
|
||||
resolved "https://registry.yarnpkg.com/new-github-issue-url/-/new-github-issue-url-0.2.1.tgz#e17be1f665a92de465926603e44b9f8685630c1d"
|
||||
integrity sha512-md4cGoxuT4T4d/HDOXbrUHkTKrp/vp+m3aOA7XXVYwNsUNMK49g3SQicTSeV5GIz/5QVGAeYRAOlyp9OvlgsYA==
|
||||
|
||||
next-transpile-modules@6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-6.1.0.tgz#f5673044a9a955855733b15ab48ff12a05e5c5f4"
|
||||
integrity sha512-IVjXHXnvr/n7liIe7SZcDbq3Gab4zltmKk7sC8zqn/L3TJt1V49W0OnUdtD/LRUdET3i34EfH/4p45SjgyQbNw==
|
||||
dependencies:
|
||||
enhanced-resolve "^5.3.2"
|
||||
escalade "^3.1.1"
|
||||
|
||||
next@10.0.7:
|
||||
version "10.0.7"
|
||||
resolved "https://registry.yarnpkg.com/next/-/next-10.0.7.tgz#442f8e1da7454de33b0bbcc1ce5684b923597ee6"
|
||||
@@ -15497,7 +15499,7 @@ prettier-plugin-prisma@0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-prisma/-/prettier-plugin-prisma-0.2.0.tgz#8ca14224bbc16570cdda3fa774e675a27614f4ba"
|
||||
integrity sha512-A9D8tUpicPFQ5+wDGAjBowBqXnkoJublmlh4tlOo89f4HSOp+H1ETyZhfvjKkrJSn0k7s3TS7PYkL1Z00o5W4A==
|
||||
|
||||
prettier@2.2.1:
|
||||
prettier@2.2.1, prettier@^2.0.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
|
||||
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
|
||||
@@ -18031,11 +18033,6 @@ tapable@^1.0.0, tapable@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
|
||||
|
||||
tapable@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
|
||||
integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
|
||||
|
||||
tar-fs@^2.0.0, tar-fs@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
|
||||
Reference in New Issue
Block a user