feat(graphql): graphQL custom field complexity and validationRules (#9955)
### What?
Adds the ability to set custom validation rules on the root `graphQL`
config property and the ability to define custom complexity on
relationship, join and upload type fields.
### Why?
**Validation Rules**
These give you the option to add your own validation rules. For example,
you may want to prevent introspection queries in production. You can now
do that with the following:
```ts
import { GraphQL } from '@payloadcms/graphql/types'
import { buildConfig } from 'payload'
export default buildConfig({
// ...
graphQL: {
validationRules: (args) => [
NoProductionIntrospection
]
},
// ...
})
const NoProductionIntrospection: GraphQL.ValidationRule = (context) => ({
Field(node) {
if (process.env.NODE_ENV === 'production') {
if (node.name.value === '__schema' || node.name.value === '__type') {
context.reportError(
new GraphQL.GraphQLError(
'GraphQL introspection is not allowed, but the query contained __schema or __type',
{ nodes: [node] }
)
);
}
}
}
})
```
**Custom field complexity**
You can now increase the complexity of a field, this will help users
from running queries that are too expensive. A higher number will make
the `maxComplexity` trigger sooner.
```ts
const fieldWithComplexity = {
name: 'authors',
type: 'relationship',
relationship: 'authors',
graphQL: {
complexity: 100, // highlight-line
}
}
```
This commit is contained in:
@@ -136,6 +136,7 @@ powerful Admin UI.
|
||||
| **`admin`** | Admin-specific configuration. [More details](#admin-config-options). |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins). |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema. |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ export const MyRelationshipField: Field = {
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ export const MyUploadField: Field = {
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
| **`typescriptSchema`** | Override field type generation with providing a JSON schema |
|
||||
| **`virtual`** | Provide `true` to disable field in the database. See [Virtual Fields](https://payloadcms.com/blog/learn-how-virtual-fields-can-help-solve-common-cms-challenges) |
|
||||
| **`graphQL`** | Custom graphQL configuration for the field. [More details](/docs/graphql/overview#field-complexity) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ At the top of your Payload Config you can define all the options to manage Graph
|
||||
| `maxComplexity` | A number used to set the maximum allowed complexity allowed by requests [More](/docs/graphql/overview#query-complexity-limits) |
|
||||
| `disablePlaygroundInProduction` | A boolean that if false will enable the GraphQL playground, defaults to true. [More](/docs/graphql/overview#graphql-playground) |
|
||||
| `disable` | A boolean that if true will disable the GraphQL entirely, defaults to false. |
|
||||
| `validationRules` | A function that takes the ExecutionArgs and returns an array of ValidationRules. |
|
||||
|
||||
## Collections
|
||||
|
||||
@@ -124,6 +125,55 @@ You can even log in using the `login[collection-singular-label-here]` mutation t
|
||||
see a ton of detail about how GraphQL operates within Payload.
|
||||
</Banner>
|
||||
|
||||
## Custom Validation Rules
|
||||
|
||||
You can add custom validation rules to your GraphQL API by defining a `validationRules` function in your Payload Config. This function should return an array of [Validation Rules](https://graphql.org/graphql-js/validation/#validation-rules) that will be applied to all incoming queries and mutations.
|
||||
|
||||
```ts
|
||||
import { GraphQL } from '@payloadcms/graphql/types'
|
||||
import { buildConfig } from 'payload'
|
||||
|
||||
export default buildConfig({
|
||||
// ...
|
||||
graphQL: {
|
||||
validationRules: (args) => [
|
||||
NoProductionIntrospection
|
||||
]
|
||||
},
|
||||
// ...
|
||||
})
|
||||
|
||||
const NoProductionIntrospection: GraphQL.ValidationRule = (context) => ({
|
||||
Field(node) {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (node.name.value === '__schema' || node.name.value === '__type') {
|
||||
context.reportError(
|
||||
new GraphQL.GraphQLError(
|
||||
'GraphQL introspection is not allowed, but the query contained __schema or __type',
|
||||
{ nodes: [node] }
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## Query complexity limits
|
||||
|
||||
Payload comes with a built-in query complexity limiter to prevent bad people from trying to slow down your server by running massive queries. To learn more, [click here](/docs/production/preventing-abuse#limiting-graphql-complexity).
|
||||
|
||||
## Field complexity
|
||||
|
||||
You can define custom complexity for `relationship`, `upload` and `join` type fields. This is useful if you want to assign a higher complexity to a field that is more expensive to resolve. This can help prevent users from running queries that are too complex.
|
||||
|
||||
```ts
|
||||
const fieldWithComplexity = {
|
||||
name: 'authors',
|
||||
type: 'relationship',
|
||||
relationship: 'authors',
|
||||
graphQL: {
|
||||
complexity: 100, // highlight-line
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -98,14 +98,12 @@ export function configToSchema(config: SanitizedConfig): {
|
||||
const query = new GraphQL.GraphQLObjectType(graphqlResult.Query)
|
||||
const mutation = new GraphQL.GraphQLObjectType(graphqlResult.Mutation)
|
||||
|
||||
const schemaToCreate = {
|
||||
const schema = new GraphQL.GraphQLSchema({
|
||||
mutation,
|
||||
query,
|
||||
}
|
||||
})
|
||||
|
||||
const schema = new GraphQL.GraphQLSchema(schemaToCreate)
|
||||
|
||||
const validationRules = (args) => [
|
||||
const validationRules = (args): GraphQL.ValidationRule[] => [
|
||||
createComplexityRule({
|
||||
estimators: [
|
||||
fieldExtensionsEstimator(),
|
||||
@@ -115,6 +113,9 @@ export function configToSchema(config: SanitizedConfig): {
|
||||
variables: args.variableValues,
|
||||
// onComplete: (complexity) => { console.log('Query Complexity:', complexity); },
|
||||
}),
|
||||
...(typeof config?.graphQL?.validationRules === 'function'
|
||||
? config.graphQL.validationRules(args)
|
||||
: []),
|
||||
]
|
||||
|
||||
return {
|
||||
|
||||
@@ -245,7 +245,10 @@ export function buildObjectType({
|
||||
type: graphqlResult.collections[field.collection].graphQL.whereInputType,
|
||||
},
|
||||
},
|
||||
extensions: { complexity: 10 },
|
||||
extensions: {
|
||||
complexity:
|
||||
typeof field?.graphQL?.complexity === 'number' ? field.graphQL.complexity : 10,
|
||||
},
|
||||
async resolve(parent, args, context: Context) {
|
||||
const { collection } = field
|
||||
const { limit, sort, where } = args
|
||||
@@ -416,7 +419,10 @@ export function buildObjectType({
|
||||
forceNullable,
|
||||
),
|
||||
args: relationshipArgs,
|
||||
extensions: { complexity: 10 },
|
||||
extensions: {
|
||||
complexity:
|
||||
typeof field?.graphQL?.complexity === 'number' ? field.graphQL.complexity : 10,
|
||||
},
|
||||
async resolve(parent, args, context: Context) {
|
||||
const value = parent[field.name]
|
||||
const locale = args.locale || context.req.locale
|
||||
@@ -768,7 +774,10 @@ export function buildObjectType({
|
||||
forceNullable,
|
||||
),
|
||||
args: relationshipArgs,
|
||||
extensions: { complexity: 10 },
|
||||
extensions: {
|
||||
complexity:
|
||||
typeof field?.graphQL?.complexity === 'number' ? field.graphQL.complexity : 10,
|
||||
},
|
||||
async resolve(parent, args, context: Context) {
|
||||
const value = parent[field.name]
|
||||
const locale = args.locale || context.req.locale
|
||||
|
||||
@@ -950,6 +950,12 @@ export type Config = {
|
||||
* Filepath to write the generated schema to
|
||||
*/
|
||||
schemaOutputFile?: string
|
||||
/**
|
||||
* Function that returns an array of validation rules to apply to the GraphQL schema
|
||||
*
|
||||
* @see https://payloadcms.com/docs/graphql/overview#custom-validation-rules
|
||||
*/
|
||||
validationRules?: (args: GraphQL.ExecutionArgs) => GraphQL.ValidationRule[]
|
||||
}
|
||||
/**
|
||||
* Tap into Payload-wide hooks.
|
||||
|
||||
@@ -32,6 +32,7 @@ export type ServerOnlyFieldProperties =
|
||||
| 'editor' // This is a `richText` only property
|
||||
| 'enumName' // can be a function
|
||||
| 'filterOptions' // This is a `relationship` and `upload` only property
|
||||
| 'graphQL'
|
||||
| 'label'
|
||||
| 'typescriptSchema'
|
||||
| 'validate'
|
||||
@@ -53,6 +54,7 @@ const serverOnlyFieldProperties: Partial<ServerOnlyFieldProperties>[] = [
|
||||
'typescriptSchema',
|
||||
'dbName', // can be a function
|
||||
'enumName', // can be a function
|
||||
'graphQL', // client does not need graphQL
|
||||
// the following props are handled separately (see below):
|
||||
// `label`
|
||||
// `fields`
|
||||
|
||||
@@ -370,6 +370,17 @@ export type OptionObject = {
|
||||
|
||||
export type Option = OptionObject | string
|
||||
|
||||
export type FieldGraphQLType = {
|
||||
graphQL?: {
|
||||
/**
|
||||
* Complexity for the query. This is used to limit the complexity of the join query.
|
||||
*
|
||||
* @default 10
|
||||
*/
|
||||
complexity?: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface FieldBase {
|
||||
/**
|
||||
* Do not set this property manually. This is set to true during sanitization, to avoid
|
||||
@@ -844,6 +855,7 @@ type SharedUploadProperties = {
|
||||
validate?: UploadFieldSingleValidation
|
||||
}
|
||||
) &
|
||||
FieldGraphQLType &
|
||||
Omit<FieldBase, 'validate'>
|
||||
|
||||
type SharedUploadPropertiesClient = FieldBaseClient &
|
||||
@@ -1023,6 +1035,7 @@ type SharedRelationshipProperties = {
|
||||
validate?: RelationshipFieldSingleValidation
|
||||
}
|
||||
) &
|
||||
FieldGraphQLType &
|
||||
Omit<FieldBase, 'validate'>
|
||||
|
||||
type SharedRelationshipPropertiesClient = FieldBaseClient &
|
||||
@@ -1405,7 +1418,8 @@ export type JoinField = {
|
||||
type: 'join'
|
||||
validate?: never
|
||||
where?: Where
|
||||
} & FieldBase
|
||||
} & FieldBase &
|
||||
FieldGraphQLType
|
||||
|
||||
export type JoinFieldClient = {
|
||||
admin?: AdminClient & Pick<JoinField['admin'], 'allowCreate' | 'disableBulkEdit' | 'readOnly'>
|
||||
|
||||
67
test/graphql/config.ts
Normal file
67
test/graphql/config.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { GraphQL } from '@payloadcms/graphql/types'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import path from 'path'
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
// ...extend config here
|
||||
collections: [
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
name: 'relationToSelf',
|
||||
graphQL: {
|
||||
complexity: 801,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
admin: {
|
||||
importMap: {
|
||||
baseDir: path.resolve(dirname),
|
||||
},
|
||||
},
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: devUser.email,
|
||||
password: devUser.password,
|
||||
},
|
||||
})
|
||||
},
|
||||
typescript: {
|
||||
outputFile: path.resolve(dirname, 'payload-types.ts'),
|
||||
},
|
||||
graphQL: {
|
||||
maxComplexity: 800,
|
||||
validationRules: () => [NoIntrospection],
|
||||
},
|
||||
})
|
||||
|
||||
const NoIntrospection: GraphQL.ValidationRule = (context) => ({
|
||||
Field(node) {
|
||||
if (node.name.value === '__schema' || node.name.value === '__type') {
|
||||
context.reportError(
|
||||
new GraphQL.GraphQLError(
|
||||
'GraphQL introspection is not allowed, but the query contained __schema or __type',
|
||||
{ nodes: [node] },
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
})
|
||||
19
test/graphql/eslint.config.js
Normal file
19
test/graphql/eslint.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { rootParserOptions } from '../../eslint.config.js'
|
||||
import testEslintConfig from '../eslint.config.js'
|
||||
|
||||
/** @typedef {import('eslint').Linter.Config} Config */
|
||||
|
||||
/** @type {Config[]} */
|
||||
export const index = [
|
||||
...testEslintConfig,
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
...rootParserOptions,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default index
|
||||
84
test/graphql/int.spec.ts
Normal file
84
test/graphql/int.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { Payload } from 'payload'
|
||||
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
|
||||
|
||||
import { idToString } from '../helpers/idToString.js'
|
||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||
|
||||
let payload: Payload
|
||||
let restClient: NextRESTClient
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
describe('graphql', () => {
|
||||
beforeAll(async () => {
|
||||
;({ payload, restClient } = await initPayloadInt(dirname))
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
if (typeof payload.db.destroy === 'function') {
|
||||
await payload.db.destroy()
|
||||
}
|
||||
})
|
||||
|
||||
describe('graphql', () => {
|
||||
it('should not be able to query introspection', async () => {
|
||||
const query = `query {
|
||||
__schema {
|
||||
queryType {
|
||||
name
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const response = await restClient
|
||||
.GRAPHQL_POST({
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
expect(response.errors[0].message).toMatch(
|
||||
'GraphQL introspection is not allowed, but the query contained __schema or __type',
|
||||
)
|
||||
})
|
||||
|
||||
it('should respect maxComplexity', async () => {
|
||||
const post = await payload.create({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
title: 'example post',
|
||||
},
|
||||
})
|
||||
await payload.update({
|
||||
collection: 'posts',
|
||||
id: post.id,
|
||||
data: {
|
||||
relatedToSelf: post.id,
|
||||
},
|
||||
})
|
||||
|
||||
const query = `query {
|
||||
Post(id: ${idToString(post.id, payload)}) {
|
||||
title
|
||||
relationToSelf {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const response = await restClient
|
||||
.GRAPHQL_POST({
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
|
||||
expect(response.errors[0].message).toMatch(
|
||||
'The query exceeds the maximum complexity of 800. Actual complexity is 804',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
214
test/graphql/payload-types.ts
Normal file
214
test/graphql/payload-types.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
collections: {
|
||||
posts: Post;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {};
|
||||
globalsSelect: {};
|
||||
locale: null;
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
jobs: {
|
||||
tasks: unknown;
|
||||
workflows: unknown;
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
login: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
relationToSelf?: (string | null) | Post;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: string;
|
||||
name?: string | null;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
relationToSelf?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
resetPasswordToken?: T;
|
||||
resetPasswordExpiration?: T;
|
||||
salt?: T;
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-locked-documents_select".
|
||||
*/
|
||||
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
|
||||
document?: T;
|
||||
globalSlug?: T;
|
||||
user?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences_select".
|
||||
*/
|
||||
export interface PayloadPreferencesSelect<T extends boolean = true> {
|
||||
user?: T;
|
||||
key?: T;
|
||||
value?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations_select".
|
||||
*/
|
||||
export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
batch?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "auth".
|
||||
*/
|
||||
export interface Auth {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
declare module 'payload' {
|
||||
// @ts-ignore
|
||||
export interface GeneratedTypes extends Config {}
|
||||
}
|
||||
1799
test/graphql/schema.graphql
Normal file
1799
test/graphql/schema.graphql
Normal file
File diff suppressed because it is too large
Load Diff
13
test/graphql/tsconfig.eslint.json
Normal file
13
test/graphql/tsconfig.eslint.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// extend your base config to share compilerOptions, etc
|
||||
//"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
// ensure that nobody can accidentally use this config for a build
|
||||
"noEmit": true
|
||||
},
|
||||
"include": [
|
||||
// whatever paths you intend to lint
|
||||
"./**/*.ts",
|
||||
"./**/*.tsx"
|
||||
]
|
||||
}
|
||||
3
test/graphql/tsconfig.json
Normal file
3
test/graphql/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user