chore: separates graphql package out for schema generation

This commit is contained in:
Jarrod Flesch
2024-02-07 16:38:01 -05:00
parent 166fce793f
commit 86bfc0a7f9
75 changed files with 220 additions and 108 deletions

15
packages/graphql/.swcrc Normal file
View File

@@ -0,0 +1,15 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"sourceMaps": "inline",
"jsc": {
"target": "esnext",
"parser": {
"syntax": "typescript",
"tsx": true,
"dts": true
}
},
"module": {
"type": "commonjs"
}
}

View File

@@ -0,0 +1,51 @@
{
"name": "@payloadcms/graphql",
"version": "0.0.1",
"private": true,
"main": "./src/index.ts",
"types": "./src/index.d.ts",
"scripts": {
"build": "pnpm build:swc && pnpm build:types",
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
"build:types": "tsc --emitDeclarationOnly --outDir dist"
},
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts",
"types": "./src/index.ts"
}
},
"devDependencies": {
"@payloadcms/eslint-config": "workspace:*",
"payload": "workspace:*",
"ts-essentials": "7.0.3"
},
"dependencies": {
"graphql": "16.8.1",
"graphql-http": "^1.22.0",
"graphql-playground-html": "1.6.30",
"graphql-query-complexity": "0.12.0",
"graphql-scalars": "1.22.2",
"graphql-type-json": "0.3.2",
"pluralize": "8.0.0"
},
"peerDependencies": {
"payload": "^2.0.0"
},
"publishConfig": {
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index/*.js",
"require": "./dist/index/*.js",
"types": "./dist/index/*.d.ts"
}
},
"registry": "https://registry.npmjs.org/"
},
"files": [
"dist"
]
}

View File

@@ -1,25 +1,25 @@
/* eslint-disable no-param-reassign */
import * as GraphQL from 'graphql'
import { GraphQLObjectType, GraphQLSchema } from 'graphql'
import { OperationArgs } from 'graphql-http'
import queryComplexity, {
import {
fieldExtensionsEstimator,
simpleEstimator,
createComplexityRule,
} from 'graphql-query-complexity'
import type { GraphQLInfo } from 'payload/config'
import type { SanitizedConfig } from 'payload/types'
import accessResolver from '../resolvers/auth/access'
import initCollections from '../initCollections'
import initGlobals from '../initGlobals'
import buildFallbackLocaleInputType from './buildFallbackLocaleInputType'
import buildLocaleInputType from './buildLocaleInputType'
import buildPoliciesType from './buildPoliciesType'
import { wrapCustomFields } from '../utilities/wrapCustomResolver'
import accessResolver from './resolvers/auth/access'
import initCollections from './schema/initCollections'
import initGlobals from './schema/initGlobals'
import buildFallbackLocaleInputType from './schema/buildFallbackLocaleInputType'
import buildLocaleInputType from './schema/buildLocaleInputType'
import buildPoliciesType from './schema/buildPoliciesType'
import { wrapCustomFields } from './utilities/wrapCustomResolver'
export async function configToSchema(config: SanitizedConfig): Promise<{
schema: GraphQLSchema
schema: GraphQL.GraphQLSchema
validationRules: (args: OperationArgs<any>) => GraphQL.ValidationRule[]
}> {
const collections = config.collections.reduce((acc, collection) => {
@@ -97,24 +97,24 @@ export async function configToSchema(config: SanitizedConfig): Promise<{
}
}
const query = new GraphQLObjectType(graphqlResult.Query)
const mutation = new GraphQLObjectType(graphqlResult.Mutation)
const query = new GraphQL.GraphQLObjectType(graphqlResult.Query)
const mutation = new GraphQL.GraphQLObjectType(graphqlResult.Mutation)
const schemaToCreate = {
mutation,
query,
}
const schema = new GraphQLSchema(schemaToCreate)
const schema = new GraphQL.GraphQLSchema(schemaToCreate)
const validationRules = ({ variableValues }) => [
queryComplexity({
const validationRules = (args) => [
createComplexityRule({
estimators: [
fieldExtensionsEstimator(),
simpleEstimator({ defaultComplexity: 1 }), // Fallback if complexity not set
],
maximumComplexity: config.graphQL.maxComplexity,
variables: variableValues,
variables: args.variableValues,
// onComplete: (complexity) => { console.log('Query Complexity:', complexity); },
}),
]

View File

@@ -1,8 +1,8 @@
import { loginOperation } from 'payload/operations'
import { generatePayloadCookie } from 'payload/auth'
import type { Collection } from 'payload/types'
import isolateTransactionID from '../../utilities/isolateTransactionID'
import { generatePayloadCookie } from '../../../utilities/cookies'
import { Context } from '../types'
function loginResolver(collection: Collection) {

View File

@@ -1,8 +1,8 @@
import { logoutOperation } from 'payload/operations'
import { generateExpiredPayloadCookie } from 'payload/auth'
import type { Collection } from 'payload/types'
import isolateTransactionID from '../../utilities/isolateTransactionID'
import { generateExpiredPayloadCookie } from '../../../utilities/cookies'
import { Context } from '../types'
function logoutResolver(collection: Collection): any {

View File

@@ -1,9 +1,8 @@
import { refreshOperation } from 'payload/operations'
import { generatePayloadCookie, extractJWT } from 'payload/auth'
import type { Collection } from 'payload/types'
import isolateTransactionID from '../../utilities/isolateTransactionID'
import { extractJWT } from '../../../utilities/jwt'
import { generatePayloadCookie } from '../../../utilities/cookies'
import { Context } from '../types'
function refreshResolver(collection: Collection) {

View File

@@ -1,8 +1,8 @@
import { resetPasswordOperation } from 'payload/operations'
import { generatePayloadCookie } from 'payload/auth'
import type { Collection } from 'payload/types'
import isolateTransactionID from '../../utilities/isolateTransactionID'
import { generatePayloadCookie } from '../../../utilities/cookies'
import { Context } from '../types'
function resetPasswordResolver(collection: Collection) {

View File

@@ -1,9 +1,9 @@
import { updateOperationGlobal } from 'payload/operations'
import type { DeepPartial } from 'ts-essentials'
import type { GeneratedTypes } from 'payload'
import type { PayloadRequest, SanitizedGlobalConfig } from 'payload/types'
import isolateTransactionID from '../../utilities/isolateTransactionID'
import type { GeneratedTypes } from 'payload'
import { Context } from '../types'
type Resolver<TSlug extends keyof GeneratedTypes['globals']> = (

View File

@@ -13,31 +13,31 @@ import { buildVersionCollectionFields } from 'payload/versions'
import type { GraphQLInfo } from 'payload/config'
import type { Field, Collection, SanitizedCollectionConfig, SanitizedConfig } from 'payload/types'
import type { ObjectTypeConfig } from './schema/buildObjectType'
import forgotPassword from './resolvers/auth/forgotPassword'
import init from './resolvers/auth/init'
import login from './resolvers/auth/login'
import logout from './resolvers/auth/logout'
import me from './resolvers/auth/me'
import refresh from './resolvers/auth/refresh'
import resetPassword from './resolvers/auth/resetPassword'
import unlock from './resolvers/auth/unlock'
import verifyEmail from './resolvers/auth/verifyEmail'
import { buildMutationInputType, getCollectionIDType } from './schema/buildMutationInputType'
import buildObjectType from './schema/buildObjectType'
import buildPaginatedListType from './schema/buildPaginatedListType'
import { buildPolicyType } from './schema/buildPoliciesType'
import buildWhereInputType from './schema/buildWhereInputType'
import formatName from './utilities/formatName'
import createResolver from './resolvers/collections/create'
import getDeleteResolver from './resolvers/collections/delete'
import { docAccessResolver } from './resolvers/collections/docAccess'
import findResolver from './resolvers/collections/find'
import findByIDResolver from './resolvers/collections/findByID'
import findVersionByIDResolver from './resolvers/collections/findVersionByID'
import findVersionsResolver from './resolvers/collections/findVersions'
import restoreVersionResolver from './resolvers/collections/restoreVersion'
import updateResolver from './resolvers/collections/update'
import type { ObjectTypeConfig } from './buildObjectType'
import forgotPassword from '../resolvers/auth/forgotPassword'
import init from '../resolvers/auth/init'
import login from '../resolvers/auth/login'
import logout from '../resolvers/auth/logout'
import me from '../resolvers/auth/me'
import refresh from '../resolvers/auth/refresh'
import resetPassword from '../resolvers/auth/resetPassword'
import unlock from '../resolvers/auth/unlock'
import verifyEmail from '../resolvers/auth/verifyEmail'
import { buildMutationInputType, getCollectionIDType } from './buildMutationInputType'
import buildObjectType from './buildObjectType'
import buildPaginatedListType from './buildPaginatedListType'
import { buildPolicyType } from './buildPoliciesType'
import buildWhereInputType from './buildWhereInputType'
import formatName from '../utilities/formatName'
import createResolver from '../resolvers/collections/create'
import getDeleteResolver from '../resolvers/collections/delete'
import { docAccessResolver } from '../resolvers/collections/docAccess'
import findResolver from '../resolvers/collections/find'
import findByIDResolver from '../resolvers/collections/findByID'
import findVersionByIDResolver from '../resolvers/collections/findVersionByID'
import findVersionsResolver from '../resolvers/collections/findVersions'
import restoreVersionResolver from '../resolvers/collections/restoreVersion'
import updateResolver from '../resolvers/collections/update'
type InitCollectionsGraphQLArgs = {
config: SanitizedConfig

View File

@@ -7,18 +7,18 @@ import { buildVersionGlobalFields } from 'payload/versions'
import type { GraphQLInfo } from 'payload/config'
import type { Field, SanitizedConfig, SanitizedGlobalConfig } from 'payload/types'
import { buildMutationInputType } from './schema/buildMutationInputType'
import buildObjectType from './schema/buildObjectType'
import buildPaginatedListType from './schema/buildPaginatedListType'
import { buildPolicyType } from './schema/buildPoliciesType'
import buildWhereInputType from './schema/buildWhereInputType'
import formatName from './utilities/formatName'
import { docAccessResolver } from './resolvers/globals/docAccess'
import findOneResolver from './resolvers/globals/findOne'
import findVersionByIDResolver from './resolvers/globals/findVersionByID'
import findVersionsResolver from './resolvers/globals/findVersions'
import restoreVersionResolver from './resolvers/globals/restoreVersion'
import updateResolver from './resolvers/globals/update'
import { buildMutationInputType } from './buildMutationInputType'
import buildObjectType from './buildObjectType'
import buildPaginatedListType from './buildPaginatedListType'
import { buildPolicyType } from './buildPoliciesType'
import buildWhereInputType from './buildWhereInputType'
import formatName from '../utilities/formatName'
import { docAccessResolver } from '../resolvers/globals/docAccess'
import findOneResolver from '../resolvers/globals/findOne'
import findVersionByIDResolver from '../resolvers/globals/findVersionByID'
import findVersionsResolver from '../resolvers/globals/findVersions'
import restoreVersionResolver from '../resolvers/globals/restoreVersion'
import updateResolver from '../resolvers/globals/update'
type InitGlobalsGraphQLArgs = {
config: SanitizedConfig

View File

@@ -0,0 +1,25 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true, // Make sure typescript knows that this module depends on their references
"noEmit": false /* Do not emit outputs. */,
"emitDeclarationOnly": true,
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"rootDir": "./src" /* Specify the root folder within your source files. */,
"allowImportingTsExtensions": true
},
"exclude": [
"dist",
"build",
"tests",
"test",
"node_modules",
".eslintrc.js",
"src/**/*.spec.js",
"src/**/*.spec.jsx",
"src/**/*.spec.ts",
"src/**/*.spec.tsx"
],
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
"references": [{ "path": "../payload" }]
}

View File

@@ -51,6 +51,7 @@
"@faceless-ui/modal": "2.0.1",
"@payloadcms/translations": "workspace:^",
"@payloadcms/ui": "workspace:*",
"@payloadcms/graphql": "workspace:*",
"@types/busboy": "^1.5.3",
"busboy": "^1.6.0",
"deep-equal": "2.2.2",

View File

@@ -1,11 +1,11 @@
import config from 'payload-config'
import { GraphQLError } from 'graphql'
import httpStatus from 'http-status'
import { configToSchema } from '@payloadcms/graphql'
import type { Payload, CollectionAfterErrorHook } from 'payload/types'
import type { GraphQLFormattedError } from 'graphql'
import { createPayloadRequest } from '../../utilities/createPayloadRequest'
import { createHandler } from 'graphql-http/lib/use/fetch'
import { configToSchema } from '../schema/configToSchema'
const handleError = async (
payload: Payload,

View File

@@ -1,7 +1,7 @@
import httpStatus from 'http-status'
import { loginOperation } from 'payload/operations'
import { isNumber } from 'payload/utilities'
import { generatePayloadCookie } from '../../utilities/cookies'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const login: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -1,6 +1,6 @@
import httpStatus from 'http-status'
import { logoutOperation } from 'payload/operations'
import { generateExpiredPayloadCookie } from '../../utilities/cookies'
import { generateExpiredPayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const logout: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -1,6 +1,6 @@
import httpStatus from 'http-status'
import { meOperation } from 'payload/operations'
import { extractJWT } from '../../utilities/jwt'
import { extractJWT } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const me: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -1,7 +1,7 @@
import { extractJWT } from '../../utilities/jwt'
import { extractJWT } from 'payload/auth'
import { refreshOperation } from 'payload/operations'
import httpStatus from 'http-status'
import { generatePayloadCookie } from '../../utilities/cookies'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const refresh: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -1,6 +1,6 @@
import httpStatus from 'http-status'
import { registerFirstUserOperation } from 'payload/operations'
import { generatePayloadCookie } from '../../utilities/cookies'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const registerFirstUser: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -1,7 +1,7 @@
import httpStatus from 'http-status'
import { resetPasswordOperation } from 'payload/operations'
import { generatePayloadCookie } from '../../utilities/cookies'
import { generatePayloadCookie } from 'payload/auth'
import { CollectionRouteHandler } from '../types'
export const resetPassword: CollectionRouteHandler = async ({ req, collection }) => {

View File

@@ -7,12 +7,11 @@ import type {
import { getAuthenticatedUser } from 'payload/auth'
import { getPayload } from 'payload'
import { URL } from 'url'
import { parseCookies } from './cookies'
import { parseCookies } from 'payload/auth'
import { getRequestLanguage } from './getRequestLanguage'
import { getRequestLocales } from './getRequestLocales'
import { getNextI18n } from './getNextI18n'
import { getDataAndFile } from './getDataAndFile'
import { registerGraphQLSchema } from '../graphql/registerSchema'
type Args = {
request: Request

View File

@@ -9,7 +9,7 @@ import type {
SanitizedGlobalConfig,
} from 'payload/types'
import { redirect } from 'next/navigation'
import { parseCookies } from './cookies'
import { parseCookies } from 'payload/auth'
import { getNextI18n } from './getNextI18n'
import { getRequestLanguage } from './getRequestLanguage'
import { findLocaleFromCode } from '../../../ui/src/utilities/findLocaleFromCode'

View File

@@ -1,32 +0,0 @@
import { AuthStrategyFunctionArgs } from 'payload/auth'
export const extractJWT = (
args: Pick<AuthStrategyFunctionArgs, 'headers' | 'payload' | 'cookies'>,
): null | string => {
const { headers, payload, cookies } = args
const jwtFromHeader = headers.get('Authorization')
const origin = headers.get('Origin')
if (jwtFromHeader?.startsWith('JWT ')) {
return jwtFromHeader.replace('JWT ', '')
}
// allow RFC6750 OAuth 2.0 compliant Bearer tokens
// in addition to the payload default JWT format
if (jwtFromHeader?.startsWith('Bearer ')) {
return jwtFromHeader.replace('Bearer ', '')
}
const tokenCookieName = `${payload.config.cookiePrefix}-token`
const cookieToken = cookies?.get(tokenCookieName)
if (!cookieToken) {
return null
}
if (!origin || payload.config.csrf.length === 0 || payload.config.csrf.indexOf(origin) > -1) {
return cookieToken
}
return null
}

View File

@@ -8,6 +8,7 @@
"rootDir": "./src" /* Specify the root folder within your source files. */,
"paths": {
"payload-config": ["./src/config.ts"],
"@payloadcms/graphql": ["../graphql/dist/index.ts"],
"@payloadcms/ui": ["../ui/src/exports/index.ts"],
"@payloadcms/translations/*": ["../translations/dist/*"]
},
@@ -32,7 +33,13 @@
"src/**/*.d.ts",
"src/**/*.json",
"../ui/src/createClientConfig.ts",
"../dev/src/app/(payload)/admin/login/action.ts"
"../dev/src/app/(payload)/admin/login/action.ts",
"../payload/src/auth/cookies.ts"
],
"references": [{ "path": "../payload" }, { "path": "../ui" }, { "path": "../translations" }]
"references": [
{ "path": "../payload" },
{ "path": "../ui" },
{ "path": "../translations" },
{ "path": "../graphql" }
]
}

View File

@@ -54,6 +54,7 @@
"deepmerge": "4.3.1",
"file-type": "16.5.4",
"find-up": "4.1.0",
"get-tsconfig": "^4.7.2",
"http-status": "1.6.2",
"is-plain-object": "5.0.0",
"json-schema-to-typescript": "11.0.3",

View File

@@ -1,5 +1,5 @@
import type { Payload, SanitizedCollectionConfig } from 'payload/types'
import type { Payload } from '..'
import type { SanitizedCollectionConfig } from './../collections/config/types'
type CookieOptions = {
domain?: string
expires?: Date

View File

@@ -3,7 +3,7 @@ import type { AuthStrategyFunctionArgs } from '.'
import { parseCookies } from '../utilities/parseCookies'
export const extractJWT = (
args: Pick<AuthStrategyFunctionArgs, 'headers' | 'payload'>,
args: Pick<AuthStrategyFunctionArgs, 'cookies' | 'headers' | 'payload'>,
): null | string => {
const { headers, payload } = args

View File

@@ -1 +1,4 @@
export * from './types'
export { extractJWT } from './extractJWT'
export * from './cookies'

View File

@@ -2,7 +2,7 @@ import jwt from 'jsonwebtoken'
import type { AuthStrategyFunction, User } from '..'
import { extractJWT } from '../getExtractJWT'
import { extractJWT } from '../extractJWT'
type JWTToken = {
collection: string

View File

@@ -1,5 +1,7 @@
export * from '../auth'
export { getAccessResults } from '../auth/getAccessResults'
export { getAuthenticatedUser } from '../auth/getAuthenticatedUser'
export type {
AuthStrategyFunction,
AuthStrategyFunctionArgs,

40
pnpm-lock.yaml generated
View File

@@ -449,6 +449,40 @@ importers:
specifier: 1.15.0
version: 1.15.0(eslint@8.48.0)
packages/graphql:
dependencies:
graphql:
specifier: ^16.8.1
version: 16.8.1
graphql-http:
specifier: ^1.22.0
version: 1.22.0(graphql@16.8.1)
graphql-playground-html:
specifier: 1.6.30
version: 1.6.30
graphql-query-complexity:
specifier: 0.12.0
version: 0.12.0(graphql@16.8.1)
graphql-scalars:
specifier: 1.22.2
version: 1.22.2(graphql@16.8.1)
graphql-type-json:
specifier: 0.3.2
version: 0.3.2(graphql@16.8.1)
pluralize:
specifier: 8.0.0
version: 8.0.0
devDependencies:
'@payloadcms/eslint-config':
specifier: workspace:*
version: link:../eslint-config-payload
payload:
specifier: workspace:*
version: link:../payload
ts-essentials:
specifier: 7.0.3
version: 7.0.3(typescript@5.2.2)
packages/live-preview:
devDependencies:
'@payloadcms/eslint-config':
@@ -482,6 +516,9 @@ importers:
'@faceless-ui/modal':
specifier: 2.0.1
version: 2.0.1(react-dom@18.2.0)(react@18.2.0)
'@payloadcms/graphql':
specifier: workspace:*
version: link:../graphql
'@payloadcms/translations':
specifier: workspace:^
version: link:../translations
@@ -576,6 +613,9 @@ importers:
find-up:
specifier: 4.1.0
version: 4.1.0
get-tsconfig:
specifier: ^4.7.2
version: 4.7.2
http-status:
specifier: 1.6.2
version: 1.6.2

View File

@@ -34,6 +34,7 @@
// if your tsconfig is something different
{ "path": "./packages/db-mongodb" },
{ "path": "./packages/db-postgres" },
{ "path": "./packages/graphql" },
{ "path": "./packages/live-preview" },
{ "path": "./packages/live-preview-react" },
{ "path": "./packages/next" },