1
0
mirror of synced 2025-12-19 18:11:23 -05:00

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:
Brandon Bayer
2021-03-04 14:55:07 -05:00
committed by GitHub
parent eaef66ba0f
commit 66cd76643c
98 changed files with 963 additions and 668 deletions

1
.gitignore vendored
View File

@@ -28,3 +28,4 @@ dist
examples/auth2
.idea
.ultra.cache.json
db.sqlite-journal

View File

@@ -1,6 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn manypkg fix
yarn lint-staged
yarn pretty-quick --staged

View File

@@ -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())

View File

@@ -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,

View File

@@ -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)

View File

@@ -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",

View File

@@ -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)
})

View File

@@ -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",

View File

@@ -14,6 +14,7 @@
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"downlevelIteration": true,
"jsx": "preserve"
},
"exclude": ["node_modules", "cypress"],

View File

@@ -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"

View File

@@ -1,4 +1,4 @@
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
const withMonorepoBuildTooling = require("@preconstruct/next")
module.exports = withMonorepoBuildTooling({

View File

@@ -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

View File

@@ -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
}

View File

@@ -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")

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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",

View File

@@ -0,0 +1,4 @@
module.exports = {
preset: '../../jest.config.js',
testEnvironment: 'jest-environment-jsdom-sixteen',
};

View File

View 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",

View File

@@ -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;
}

View 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';
`,
},
],
});

View 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;

View File

@@ -1,3 +0,0 @@
it.skip('works', () => {
expect(true).toBe(true);
});

View 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"
}

View File

@@ -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",

View 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

View File

@@ -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"

View File

@@ -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",

View File

@@ -26,7 +26,6 @@
"gitHead": "d3b9fce0bdd251c2b1890793b0aa1cd77c1c0922",
"dependencies": {
"fs-extra": "^9.1.0",
"module-alias": "2.2.2",
"pkg-dir": "^5.0.0"
}
}

View 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"
}

View 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"
}

View 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"
}

View 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"
}

View 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"
}

View File

@@ -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"
}

View 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"
}

View 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"],
})
})
})

View 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)
}
}

View 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
}

View File

@@ -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(),
}))

View File

@@ -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`

View 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-κόσμε-żółć-平仮名",
})
})
})

View File

@@ -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)

View File

@@ -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 = `

View 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"

View 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"

View 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"

View File

@@ -0,0 +1 @@
export {default as ErrorComponent} from "next/error"

View 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"

View 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"

View File

@@ -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"

View File

@@ -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
}

View 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"

View File

@@ -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"

View 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
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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,

View File

@@ -1,4 +1,4 @@
import {SecurePassword} from "../src/auth-utils"
import {SecurePassword} from "./auth-utils"
describe("SecurePassword", () => {
describe("hash", () => {

View File

@@ -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)

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
import {Middleware, MiddlewareRequest, MiddlewareResponse} from "./types"
import {Middleware, MiddlewareRequest, MiddlewareResponse} from "../../types"
export const secureProxyMiddleware: Middleware = function (
req: MiddlewareRequest,

View File

@@ -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

View File

@@ -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) {

View 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"

View File

@@ -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 () => {

View 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
}

View File

@@ -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 () => {

View File

@@ -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>,

View File

@@ -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 () => {

View File

@@ -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

View File

@@ -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", () => {

View File

@@ -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>,

View File

@@ -1,4 +1,4 @@
import {paginate} from "../src"
import {paginate} from "./server-utils"
describe("paginate", () => {
const dummyPaginationPromises = {

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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>

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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
}

View 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)
}

View File

@@ -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)

View File

@@ -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"],
})
})
})
})

View File

@@ -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", () => {

View File

@@ -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)[/\\]/,

View File

@@ -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)
}

View File

@@ -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", () => {

View 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"
}

View File

@@ -1,4 +1,4 @@
const { sessionMiddleware, simpleRolesIsAuthorized } = require("@blitzjs/server")
const { sessionMiddleware, simpleRolesIsAuthorized } = require("blitz")
module.exports = {
middleware: [

View File

@@ -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

View File

@@ -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": "*",

View File

@@ -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

View File

@@ -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});

View File

@@ -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)

View File

@@ -1,4 +1,4 @@
const {sessionMiddleware, simpleRolesIsAuthorized} = require("@blitzjs/server")
const {sessionMiddleware, simpleRolesIsAuthorized} = require("blitz")
const withMDX = require("@next/mdx")({
extension: /\.mdx?$/,

View File

@@ -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"