diff --git a/.changeset/metal-maps-dream.md b/.changeset/metal-maps-dream.md new file mode 100644 index 0000000000..091d07840b --- /dev/null +++ b/.changeset/metal-maps-dream.md @@ -0,0 +1,12 @@ +--- +'@graphql-yoga/apollo-link': patch +'@graphql-yoga/urql-exchange': patch +'@graphql-yoga/common': patch +'@graphql-yoga/graphiql': patch +'graphql-yoga': patch +'@graphql-yoga/plugin-apollo-inline-trace': patch +'@graphql-yoga/plugin-apq': patch +'@graphql-yoga/plugin-persisted-operations': patch +--- + +Support older version of GraphQLjs diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d53a5760dd..1fb25b53b0 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -51,15 +51,25 @@ module.exports = { { files: ['packages/**/*'], rules: { + 'import/no-extraneous-dependencies': [ + 'error', + { devDependencies: ['**/*.test.ts', '**/*.spec.ts'] }, + ], 'no-restricted-imports': [ 'error', { paths: [ { name: 'graphql', - importNames: ['execute', 'subscribe'], + importNames: [ + 'execute', + 'subscribe', + 'graphql', + 'executeSync', + 'graphqlSync', + ], message: - 'Please use `execute` and `subscribe` from `@graphql-tools/executor` instead.', + 'Please use `normalizedExecutor` from `@graphql-tools/executor` instead.', }, ], }, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e83b7debcc..09c889b7b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,29 +12,88 @@ on: jobs: unit: - name: unit / nodejs v${{ matrix.node-version }} - uses: the-guild-org/shared-config/.github/workflows/ci-node-matrix.yml@main - with: - script: 'pnpm build && pnpm test' - nodeVersions: '[14,16,18]' - packageManager: pnpm + name: unit / nodejs v${{ matrix.node-version }} / graphql v${{ matrix.graphql-version }} + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14, 16, 18] + graphql-version: ['15.8.0', '16.6.0'] + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Install Node + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.4 + with: + version: 7 + + - name: Get pnpm store path + id: pnpm-store + run: echo "PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Cache pnpm + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-store.outputs.PATH }} + key: ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}- + + - name: Set GraphQL Version + run: node scripts/override-graphql-version.js ${{ matrix.graphql-version }} + + - name: Install Dependencies + run: pnpm i --no-frozen-lockfile # no frozen-lockfile because we change the resolutions + + - name: Build Packages + run: pnpm build + + - name: Run Tests + run: pnpm run test --ci integration: - name: integration / nodejs v${{ matrix.node-version }} + name: integration / nodejs v${{ matrix.node-version }} / graphql v${{ matrix.graphql-version }} runs-on: ubuntu-latest strategy: matrix: node-version: [14, 16, 18] - fail-fast: false + graphql-version: ['15.8.0', '16.6.0'] steps: - name: Checkout Repository uses: actions/checkout@v3 - - uses: the-guild-org/shared-config/setup@main - name: setup env + - name: Install Node + uses: actions/setup-node@v3 with: - nodeVersion: ${{ matrix.node-version }} - packageManager: pnpm + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.4 + with: + version: 7 + + - name: Get pnpm store path + id: pnpm-store + run: echo "PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Cache pnpm + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-store.outputs.PATH }} + key: ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}- + + - name: Set GraphQL Version + run: node scripts/override-graphql-version.js ${{ matrix.graphql-version }} + + - name: Install Dependencies + run: pnpm i --no-frozen-lockfile # no frozen-lockfile because we change the resolutions - name: Build Packages run: pnpm build @@ -43,21 +102,43 @@ jobs: run: pnpm test:integration --ci leaks: - name: leaks / nodejs v${{ matrix.node-version }} + name: leaks / nodejs v${{ matrix.node-version }} / graphql v${{ matrix.graphql-version }} runs-on: ubuntu-latest strategy: matrix: node-version: [14, 16, 18] - fail-fast: false + graphql-version: ['15.8.0', '16.6.0'] steps: - name: Checkout Repository uses: actions/checkout@v3 - - uses: the-guild-org/shared-config/setup@main - name: setup env + - name: Install Node + uses: actions/setup-node@v3 with: - nodeVersion: ${{ matrix.node-version }} - packageManager: pnpm + node-version: ${{ matrix.node-version }} + + - name: Install pnpm + uses: pnpm/action-setup@v2.2.4 + with: + version: 7 + + - name: Get pnpm store path + id: pnpm-store + run: echo "PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - name: Cache pnpm + uses: actions/cache@v3 + with: + path: ${{ steps.pnpm-store.outputs.PATH }} + key: ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store-graphql-v${{ matrix.graphql-version }}- + + - name: Set GraphQL Version + run: node scripts/override-graphql-version.js ${{ matrix.graphql-version }} + + - name: Install Dependencies + run: pnpm i --no-frozen-lockfile # no frozen-lockfile because we change the resolutions - name: Build Packages run: pnpm build diff --git a/examples/error-handling/src/yoga.ts b/examples/error-handling/src/yoga.ts index d11ad0af25..51b78d827d 100644 --- a/examples/error-handling/src/yoga.ts +++ b/examples/error-handling/src/yoga.ts @@ -1,6 +1,5 @@ -import { createYoga, createSchema } from 'graphql-yoga' +import { createYoga, createSchema, createGraphQLError } from 'graphql-yoga' import { fetch } from '@whatwg-node/fetch' -import { GraphQLError } from 'graphql' const users = [ { @@ -43,7 +42,7 @@ export const yoga = createYoga({ user: async (_, args) => { const user = users.find((user) => user.id === args.byId) if (!user) { - throw new GraphQLError(`User with id '${args.byId}' not found.`, { + throw createGraphQLError(`User with id '${args.byId}' not found.`, { extensions: { code: 'USER_NOT_FOUND', someRandomExtensions: { diff --git a/examples/hackernews/src/schema.ts b/examples/hackernews/src/schema.ts index 7c4461c7c4..47249fe0a4 100644 --- a/examples/hackernews/src/schema.ts +++ b/examples/hackernews/src/schema.ts @@ -1,3 +1,4 @@ +import { createGraphQLError } from 'graphql-yoga' import { makeExecutableSchema } from '@graphql-tools/schema' import type { Link } from '@prisma/client' import { PrismaClientKnownRequestError } from '@prisma/client/runtime' @@ -42,7 +43,7 @@ const applyTakeConstraints = (params: { value: number }) => { if (params.value < params.min || params.value > params.max) { - throw new GraphQLError( + throw createGraphQLError( `'take' argument value '${params.value}' is outside the valid range of '${params.min}' to '${params.max}'.`, ) } @@ -122,7 +123,7 @@ const resolvers = { const linkId = parseIntSafe(args.linkId) if (linkId === null) { return Promise.reject( - new GraphQLError( + createGraphQLError( `Cannot post common on non-existing link with id '${args.linkId}'.`, ), ) @@ -141,7 +142,7 @@ const resolvers = { err.code === 'P2003' ) { return Promise.reject( - new GraphQLError( + createGraphQLError( `Cannot post common on non-existing link with id '${args.linkId}'.`, ), ) diff --git a/examples/node-ts/src/yoga.ts b/examples/node-ts/src/yoga.ts index 527356ff48..49e078d973 100644 --- a/examples/node-ts/src/yoga.ts +++ b/examples/node-ts/src/yoga.ts @@ -1,5 +1,9 @@ -import { createYoga, Plugin, createSchema } from 'graphql-yoga' -import { GraphQLError } from 'graphql' +import { + createYoga, + Plugin, + createSchema, + createGraphQLError, +} from 'graphql-yoga' // available when handling requests, needs to be provided by the implementor // eslint-disable-next-line @typescript-eslint/ban-types @@ -52,7 +56,7 @@ function useDisableSubscription(): Plugin< return { onSubscribe({ args }) { if (args.contextValue.disableSubscription) { - throw new GraphQLError('Subscriptions have been disabled', { + throw createGraphQLError('Subscriptions have been disabled', { extensions: { http: { status: 400, // report error with a 400 diff --git a/packages/graphql-yoga/__integration-tests__/browser.spec.ts b/packages/graphql-yoga/__integration-tests__/browser.spec.ts index b4052a4014..d9c4746abe 100644 --- a/packages/graphql-yoga/__integration-tests__/browser.spec.ts +++ b/packages/graphql-yoga/__integration-tests__/browser.spec.ts @@ -1,6 +1,6 @@ import { InMemoryLiveQueryStore } from '@n1ru4l/in-memory-live-query-store' import { GraphQLLiveDirective, useLiveQuery } from '@envelop/live-query' -import { CORSOptions, createYoga } from 'graphql-yoga' +import { CORSOptions, createYoga } from '../src/index.js' import { renderGraphiQL } from '@graphql-yoga/render-graphiql' import puppeteer from 'puppeteer' import { createServer, Server } from 'http' diff --git a/packages/graphql-yoga/__integration-tests__/incremental-delivery.spec.ts b/packages/graphql-yoga/__integration-tests__/incremental-delivery.spec.ts index 17d2b8a5f3..ffd29eb5ea 100644 --- a/packages/graphql-yoga/__integration-tests__/incremental-delivery.spec.ts +++ b/packages/graphql-yoga/__integration-tests__/incremental-delivery.spec.ts @@ -133,7 +133,7 @@ describe('incremental delivery: node-fetch', () => { type: GraphQLFile, }, }, - resolve: async (_, { file }: { file: File }) => file, + resolve: async (_, { file }) => file, }, parseFileStream: { type: GraphQLString, @@ -144,7 +144,7 @@ describe('incremental delivery: node-fetch', () => { type: GraphQLFile, }, }, - resolve: async (_, { file }: { file: File }) => { + resolve: async (_, { file }) => { const chunks = [] for await (const chunk of file.stream()) { chunks.push(Buffer.from(chunk)) @@ -161,7 +161,7 @@ describe('incremental delivery: node-fetch', () => { type: GraphQLFile, }, }, - resolve: async (_, { file }: { file: File }) => { + resolve: async (_, { file }) => { return Buffer.from(await file.arrayBuffer()).toString('utf8') }, }, diff --git a/packages/graphql-yoga/__tests__/500.spec.ts b/packages/graphql-yoga/__tests__/500.spec.ts index e7b45169ae..c71668c5a8 100644 --- a/packages/graphql-yoga/__tests__/500.spec.ts +++ b/packages/graphql-yoga/__tests__/500.spec.ts @@ -1,6 +1,4 @@ -import { Plugin } from '@envelop/core' import { createGraphQLError } from '@graphql-tools/utils' -import { GraphQLError } from 'graphql' import { createSchema } from '../src/schema' import { createYoga } from '../src/server' diff --git a/packages/graphql-yoga/__tests__/error-masking.spec.ts b/packages/graphql-yoga/__tests__/error-masking.spec.ts index 56f0c7b994..9a35d0f887 100644 --- a/packages/graphql-yoga/__tests__/error-masking.spec.ts +++ b/packages/graphql-yoga/__tests__/error-masking.spec.ts @@ -1,5 +1,4 @@ -import { GraphQLError } from 'graphql' -import { createSchema, createYoga } from 'graphql-yoga' +import { createGraphQLError, createSchema, createYoga } from 'graphql-yoga' describe('error masking', () => { function createTestSchema() { @@ -13,7 +12,7 @@ describe('error masking', () => { resolvers: { Query: { hello: () => { - throw new GraphQLError('This error never gets masked.') + throw createGraphQLError('This error never gets masked.') }, hi: () => { throw new Error( @@ -218,7 +217,7 @@ describe('error masking', () => { const yoga = createYoga({ logging: false, context: () => { - throw new GraphQLError('I like turtles') + throw createGraphQLError('I like turtles') }, schema: createTestSchema(), }) @@ -242,7 +241,7 @@ describe('error masking', () => { const yoga = createYoga({ logging: false, context: () => { - throw new GraphQLError('I like turtles', { + throw createGraphQLError('I like turtles', { extensions: { foo: 1, }, diff --git a/packages/graphql-yoga/__tests__/http-extensions.spec.ts b/packages/graphql-yoga/__tests__/http-extensions.spec.ts index 728a47bf94..9802c4b88b 100644 --- a/packages/graphql-yoga/__tests__/http-extensions.spec.ts +++ b/packages/graphql-yoga/__tests__/http-extensions.spec.ts @@ -1,5 +1,4 @@ -import { GraphQLError } from 'graphql' -import { createSchema, createYoga } from 'graphql-yoga' +import { createSchema, createYoga, createGraphQLError } from 'graphql-yoga' describe('GraphQLError.extensions.http', () => { it('sets correct status code and headers for thrown GraphQLError in a resolver', async () => { @@ -13,7 +12,7 @@ describe('GraphQLError.extensions.http', () => { resolvers: { Query: { a() { - throw new GraphQLError('A', { + throw createGraphQLError('A', { extensions: { http: { status: 401, @@ -52,7 +51,7 @@ describe('GraphQLError.extensions.http', () => { resolvers: { Query: { a: () => { - throw new GraphQLError('A', { + throw createGraphQLError('A', { extensions: { http: { status: 401, @@ -61,7 +60,7 @@ describe('GraphQLError.extensions.http', () => { }) }, b: () => { - throw new GraphQLError('B', { + throw createGraphQLError('B', { extensions: { http: { status: 503, @@ -113,7 +112,7 @@ describe('GraphQLError.extensions.http', () => { resolvers: { Query: { a: () => { - throw new GraphQLError('', { + throw createGraphQLError('', { extensions: { http: { headers: { @@ -124,7 +123,7 @@ describe('GraphQLError.extensions.http', () => { }) }, b: () => { - throw new GraphQLError('DB is not available', { + throw createGraphQLError('DB is not available', { extensions: { http: { headers: { @@ -167,7 +166,7 @@ describe('GraphQLError.extensions.http', () => { resolvers: { Query: { a: () => { - throw new GraphQLError('Woah!', { + throw createGraphQLError('Woah!', { extensions: { http: { status: 418, @@ -257,7 +256,7 @@ describe('GraphQLError.extensions.http', () => { `, }), context: () => { - throw new GraphQLError('No http status extension', { + throw createGraphQLError('No http status extension', { extensions: { http: { headers: { 'x-foo': 'bar' } } }, }) }, diff --git a/packages/graphql-yoga/__tests__/introspection.spec.ts b/packages/graphql-yoga/__tests__/introspection.spec.ts index bbcfd300e7..684c608e2a 100644 --- a/packages/graphql-yoga/__tests__/introspection.spec.ts +++ b/packages/graphql-yoga/__tests__/introspection.spec.ts @@ -1,6 +1,6 @@ import { useDisableIntrospection } from '@envelop/disable-introspection' -import { getIntrospectionQuery, GraphQLError } from 'graphql' -import { createSchema, createYoga } from 'graphql-yoga' +import { getIntrospectionQuery } from 'graphql' +import { createSchema, createYoga, createGraphQLError } from 'graphql-yoga' function createTestSchema() { return createSchema({ @@ -13,7 +13,7 @@ function createTestSchema() { resolvers: { Query: { hello: () => { - throw new GraphQLError('This error never gets masked.') + throw createGraphQLError('This error never gets masked.') }, hi: () => { throw new Error('This error will get mask if you enable maskedError.') diff --git a/packages/graphql-yoga/__tests__/logging.spec.ts b/packages/graphql-yoga/__tests__/logging.spec.ts index 64f566df24..4a749ba827 100644 --- a/packages/graphql-yoga/__tests__/logging.spec.ts +++ b/packages/graphql-yoga/__tests__/logging.spec.ts @@ -1,5 +1,5 @@ import { jest } from '@jest/globals' -import { defaultYogaLogger, createYoga } from 'graphql-yoga' +import { createYoga, defaultYogaLogger } from '../src' describe('logging', () => { it('use custom logger', async () => { diff --git a/packages/graphql-yoga/__tests__/schema.spec.ts b/packages/graphql-yoga/__tests__/schema.spec.ts index 821959e1a8..54ea0b1bac 100644 --- a/packages/graphql-yoga/__tests__/schema.spec.ts +++ b/packages/graphql-yoga/__tests__/schema.spec.ts @@ -28,7 +28,7 @@ describe('schema', () => { it('schema factory function', async () => { const schemaFactory = async (ctx: YogaInitialContext) => { const strFromContext = ctx.request.headers.get('str') - return createSchema({ + return createSchema({ typeDefs: /* GraphQL */ ` type Query { foo: String diff --git a/packages/graphql-yoga/__tests__/validations/harness.ts b/packages/graphql-yoga/__tests__/validations/harness.ts index f5b6305d10..3b0730f10e 100644 --- a/packages/graphql-yoga/__tests__/validations/harness.ts +++ b/packages/graphql-yoga/__tests__/validations/harness.ts @@ -8,7 +8,15 @@ import { import { Maybe } from 'graphql/jsutils/Maybe' import { validateSDL } from 'graphql/validation/validate' import { SDLValidationRule } from 'graphql/validation/ValidationContext' -import { isObjectLike } from 'graphql/jsutils/isObjectLike' + +/** + * Checks whether the value is an object. + * + * `import { isObjectLike } from 'graphql/jsutils/isObjectLike'` does not always have TS files. + */ +function isObjectLike(val: unknown): val is Record { + return Boolean(val) && typeof val === 'object' +} /** * Creates an object map with the same keys as `map` and values generated by diff --git a/packages/graphql-yoga/package.json b/packages/graphql-yoga/package.json index c13788fe3e..7462200ecb 100644 --- a/packages/graphql-yoga/package.json +++ b/packages/graphql-yoga/package.json @@ -54,6 +54,7 @@ "@envelop/validation-cache": "5.0.3", "@graphql-tools/executor": "0.0.6", "@graphql-tools/schema": "^9.0.0", + "@graphql-tools/utils": "^9.0.1", "@graphql-typed-document-node/core": "^3.1.1", "@graphql-yoga/subscription": "^3.0.0-next.0", "@whatwg-node/fetch": "0.5.1", @@ -64,7 +65,6 @@ "devDependencies": { "@envelop/disable-introspection": "4.0.3", "@envelop/live-query": "5.0.3", - "@graphql-tools/utils": "^8.12.0", "@jest/globals": "^29.2.1", "@n1ru4l/in-memory-live-query-store": "0.10.0", "@repeaterjs/repeater": "^3.0.4", @@ -73,6 +73,7 @@ "graphql-http": "^1.6.0", "graphql-scalars": "1.20.0", "html-minifier-terser": "7.0.0", + "json-bigint-patch": "0.0.8", "puppeteer": "19.2.0" }, "peerDependencies": { diff --git a/packages/graphql-yoga/src/error.ts b/packages/graphql-yoga/src/error.ts index b86f58e4cd..61c184a788 100644 --- a/packages/graphql-yoga/src/error.ts +++ b/packages/graphql-yoga/src/error.ts @@ -3,6 +3,8 @@ import { GraphQLError } from 'graphql' import type { ResultProcessorInput } from './plugins/types' import type { YogaMaskedErrorOpts } from './types' +export { createGraphQLError } + declare module 'graphql' { interface GraphQLHTTPErrorExtensions { status?: number diff --git a/packages/graphql-yoga/src/index.ts b/packages/graphql-yoga/src/index.ts index b0b058976c..45968da710 100644 --- a/packages/graphql-yoga/src/index.ts +++ b/packages/graphql-yoga/src/index.ts @@ -11,3 +11,4 @@ export { useSchema } from './plugins/useSchema.js' export { useReadinessCheck } from './plugins/useReadinessCheck.js' export * from './schema.js' export * from './subscription.js' +export { createGraphQLError } from './error.js' diff --git a/packages/graphql-yoga/src/plugins/plugins.test.ts b/packages/graphql-yoga/src/plugins/plugins.test.ts index 04e394120a..e23faf74c2 100644 --- a/packages/graphql-yoga/src/plugins/plugins.test.ts +++ b/packages/graphql-yoga/src/plugins/plugins.test.ts @@ -1,8 +1,8 @@ import { AfterValidateHook } from '@envelop/core' -import { GraphQLError } from 'graphql' import { Plugin } from './types' import { createYoga } from '../server' import { createSchema } from '../schema' +import { createGraphQLError } from '../error' const schema = createSchema({ typeDefs: /* GraphQL */ ` @@ -18,7 +18,7 @@ describe('Yoga Plugins', () => { .fn() .mockImplementation(({ setResult }) => { setResult([ - new GraphQLError('My Error', { extensions: { my: 'error' } }), + createGraphQLError('My Error', { extensions: { my: 'error' } }), ]) }) const testPlugin: Plugin = { diff --git a/packages/graphql-yoga/src/plugins/requestValidation/useLimitBatching.ts b/packages/graphql-yoga/src/plugins/requestValidation/useLimitBatching.ts index 8d92edbc69..413ce994b9 100644 --- a/packages/graphql-yoga/src/plugins/requestValidation/useLimitBatching.ts +++ b/packages/graphql-yoga/src/plugins/requestValidation/useLimitBatching.ts @@ -1,4 +1,4 @@ -import { GraphQLError } from 'graphql' +import { createGraphQLError } from '../../error.js' import type { Plugin } from '../types' export function useLimitBatching(limit?: number): Plugin { @@ -8,7 +8,7 @@ export function useLimitBatching(limit?: number): Plugin { onRequestParseDone({ requestParserResult }) { if (Array.isArray(requestParserResult)) { if (!limit) { - throw new GraphQLError(`Batching is not supported.`, { + throw createGraphQLError(`Batching is not supported.`, { extensions: { http: { status: 400, @@ -17,7 +17,7 @@ export function useLimitBatching(limit?: number): Plugin { }) } if (requestParserResult.length > limit) { - throw new GraphQLError( + throw createGraphQLError( `Batching is limited to ${limit} operations per request.`, { extensions: { diff --git a/packages/graphql-yoga/src/plugins/useSchema.ts b/packages/graphql-yoga/src/plugins/useSchema.ts index 4afefa7d2b..d3f74e1cbe 100644 --- a/packages/graphql-yoga/src/plugins/useSchema.ts +++ b/packages/graphql-yoga/src/plugins/useSchema.ts @@ -6,14 +6,15 @@ import type { Plugin } from './types' export type YogaSchemaDefinition = | PromiseOrValue> | (( - context: TContext & { request: Request }, + context: TContext & YogaInitialContext, ) => PromiseOrValue>) export const useSchema = < - TContext extends YogaInitialContext = YogaInitialContext, + // eslint-disable-next-line @typescript-eslint/ban-types + TContext = {}, >( schemaDef?: YogaSchemaDefinition, -): Plugin => { +): Plugin => { if (schemaDef == null) { return {} } @@ -54,7 +55,7 @@ export const useSchema = < const schema = await schemaDef({ ...serverContext, request, - } as TContext & { request: Request }) + } as TContext & YogaInitialContext) schemaByRequest.set(request, schema) }, } diff --git a/packages/graphql-yoga/src/schema.ts b/packages/graphql-yoga/src/schema.ts index d029eb6d74..138ca04918 100644 --- a/packages/graphql-yoga/src/schema.ts +++ b/packages/graphql-yoga/src/schema.ts @@ -4,10 +4,11 @@ import { } from '@graphql-tools/schema' import { GraphQLSchemaWithContext, YogaInitialContext } from './types.js' -export function createSchema( - opts: IExecutableSchemaDefinition, +// eslint-disable-next-line @typescript-eslint/ban-types +export function createSchema( + opts: IExecutableSchemaDefinition, ): GraphQLSchemaWithContext { - return makeExecutableSchema({ + return makeExecutableSchema({ ...opts, typeDefs: [ /* GraphQL */ ` diff --git a/packages/graphql-yoga/src/server.ts b/packages/graphql-yoga/src/server.ts index 21da4063e5..af68222505 100644 --- a/packages/graphql-yoga/src/server.ts +++ b/packages/graphql-yoga/src/server.ts @@ -150,9 +150,7 @@ export type YogaServerOptions< renderGraphiQL?: (options?: GraphiQLOptions) => PromiseOrValue - schema?: YogaSchemaDefinition< - TUserContext & TServerContext & YogaInitialContext - > + schema?: YogaSchemaDefinition /** * Envelop Plugins diff --git a/packages/graphql-yoga/src/validations/defer-stream-directive-label.ts b/packages/graphql-yoga/src/validations/defer-stream-directive-label.ts index 025accc7d9..0e8fa45095 100644 --- a/packages/graphql-yoga/src/validations/defer-stream-directive-label.ts +++ b/packages/graphql-yoga/src/validations/defer-stream-directive-label.ts @@ -1,4 +1,5 @@ -import { GraphQLError, Kind, ASTVisitor, ValidationContext } from 'graphql' +import { Kind, ASTVisitor, ValidationContext } from 'graphql' +import { createGraphQLError } from '../error.js' import { GraphQLDeferDirective } from '../directives/defer.js' import { GraphQLStreamDirective } from '../directives/stream.js' @@ -26,14 +27,14 @@ export function DeferStreamDirectiveLabelRule( } if (labelValue.kind !== Kind.STRING) { context.reportError( - new GraphQLError( + createGraphQLError( `Directive "${node.name.value}"'s label argument must be a static string.`, { nodes: node }, ), ) } else if (knownLabels[labelValue.value]) { context.reportError( - new GraphQLError( + createGraphQLError( 'Defer/Stream directive label argument must be unique.', { nodes: [knownLabels[labelValue.value], node] }, ), diff --git a/packages/graphql-yoga/src/validations/defer-stream-directive-on-root-field.ts b/packages/graphql-yoga/src/validations/defer-stream-directive-on-root-field.ts index f4115c7381..050ba3f4ed 100644 --- a/packages/graphql-yoga/src/validations/defer-stream-directive-on-root-field.ts +++ b/packages/graphql-yoga/src/validations/defer-stream-directive-on-root-field.ts @@ -1,6 +1,7 @@ -import { GraphQLError, ASTVisitor, ValidationContext } from 'graphql' +import { ASTVisitor, ValidationContext } from 'graphql' import { GraphQLDeferDirective } from '../directives/defer.js' import { GraphQLStreamDirective } from '../directives/stream.js' +import { createGraphQLError } from '../error.js' /** * Stream directive on list field @@ -18,7 +19,7 @@ export function DeferStreamDirectiveOnRootFieldRule( if (parentType && node.name.value === GraphQLDeferDirective.name) { if (mutationType && parentType === mutationType) { context.reportError( - new GraphQLError( + createGraphQLError( `Defer directive cannot be used on root mutation type "${parentType.name}".`, { nodes: node }, ), @@ -26,7 +27,7 @@ export function DeferStreamDirectiveOnRootFieldRule( } if (subscriptionType && parentType === subscriptionType) { context.reportError( - new GraphQLError( + createGraphQLError( `Defer directive cannot be used on root subscription type "${parentType.name}".`, { nodes: node }, ), @@ -36,7 +37,7 @@ export function DeferStreamDirectiveOnRootFieldRule( if (parentType && node.name.value === GraphQLStreamDirective.name) { if (mutationType && parentType === mutationType) { context.reportError( - new GraphQLError( + createGraphQLError( `Stream directive cannot be used on root mutation type "${parentType.name}".`, { nodes: node }, ), @@ -44,7 +45,7 @@ export function DeferStreamDirectiveOnRootFieldRule( } if (subscriptionType && parentType === subscriptionType) { context.reportError( - new GraphQLError( + createGraphQLError( `Stream directive cannot be used on root subscription type "${parentType.name}".`, { nodes: node }, ), diff --git a/packages/graphql-yoga/src/validations/overlapping-fields-can-be-merged.ts b/packages/graphql-yoga/src/validations/overlapping-fields-can-be-merged.ts index a06011c48c..095e5d8291 100644 --- a/packages/graphql-yoga/src/validations/overlapping-fields-can-be-merged.ts +++ b/packages/graphql-yoga/src/validations/overlapping-fields-can-be-merged.ts @@ -1,5 +1,4 @@ import { - GraphQLError, DirectiveNode, FieldNode, FragmentDefinitionNode, @@ -22,7 +21,7 @@ import { ObjectFieldNode, ValueNode, } from 'graphql' -import { inspect, Maybe } from '@graphql-tools/utils' +import { inspect, Maybe, createGraphQLError } from '@graphql-tools/utils' interface ObjMap { [key: string]: T @@ -170,7 +169,7 @@ export function OverlappingFieldsCanBeMergedRule( for (const [[responseName, reason], fields1, fields2] of conflicts) { const reasonMsg = reasonMessage(reason) context.reportError( - new GraphQLError( + createGraphQLError( `Fields "${responseName}" conflict because ${reasonMsg}. Use different aliases on the fields to fetch both if this was intentional.`, { nodes: fields1.concat(fields2) }, ), @@ -722,9 +721,9 @@ function findConflict( cachedFieldsAndFragmentNames, comparedFragmentPairs, areMutuallyExclusive, - getNamedType(type1), + getNamedType(type1 as GraphQLOutputType), selectionSet1, - getNamedType(type2), + getNamedType(type2 as GraphQLOutputType), selectionSet2, ) return subfieldConflicts(conflicts, responseName, node1, node2) diff --git a/packages/graphql-yoga/src/validations/stream-directive-on-list-field.ts b/packages/graphql-yoga/src/validations/stream-directive-on-list-field.ts index f3679e32a9..7c93528429 100644 --- a/packages/graphql-yoga/src/validations/stream-directive-on-list-field.ts +++ b/packages/graphql-yoga/src/validations/stream-directive-on-list-field.ts @@ -4,8 +4,8 @@ import { isWrappingType, DirectiveNode, ASTVisitor, - GraphQLError, } from 'graphql' +import { createGraphQLError } from '../error.js' import { GraphQLStreamDirective } from '../directives/stream.js' /** @@ -30,7 +30,7 @@ export function StreamDirectiveOnListFieldRule( ) ) { context.reportError( - new GraphQLError( + createGraphQLError( `Stream directive cannot be used on non-list field "${fieldDef.name}" on type "${parentType.name}".`, { nodes: node }, ), diff --git a/packages/graphql-yoga/type-api-check.ts b/packages/graphql-yoga/type-api-check.ts index f8996051a4..b389df5259 100644 --- a/packages/graphql-yoga/type-api-check.ts +++ b/packages/graphql-yoga/type-api-check.ts @@ -2,7 +2,7 @@ import { IResolvers } from '@graphql-tools/utils' import { ClientRequest } from 'http' -import { createYoga, createSchema, YogaInitialContext } from 'graphql-yoga' +import { createYoga, createSchema, YogaInitialContext } from './src/index.js' import type { GraphQLSchema } from 'graphql' // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -92,11 +92,13 @@ const request: Request = null as any }, }, } + const schema = createSchema({ + typeDefs: ``, + resolvers, + }) + createYoga({ - schema: createSchema({ - typeDefs: ``, - resolvers, - }), + schema, }) } diff --git a/packages/plugins/apollo-inline-trace/__tests__/apollo-inline-trace.spec.ts b/packages/plugins/apollo-inline-trace/__tests__/apollo-inline-trace.spec.ts index 780e35dd94..6d04542595 100644 --- a/packages/plugins/apollo-inline-trace/__tests__/apollo-inline-trace.spec.ts +++ b/packages/plugins/apollo-inline-trace/__tests__/apollo-inline-trace.spec.ts @@ -1,7 +1,6 @@ -import { createYoga, createSchema } from 'graphql-yoga' +import { createYoga, createSchema, createGraphQLError } from 'graphql-yoga' import { useApolloInlineTrace } from '../src/index.js' import { Trace } from 'apollo-reporting-protobuf' -import { GraphQLError } from 'graphql' describe('Inline Trace', () => { const schema = createSchema({ @@ -411,7 +410,7 @@ describe('Inline Trace', () => { plugins: [ useApolloInlineTrace({ rewriteError: () => - new GraphQLError('bim', { extensions: { str: 'ing' } }), + createGraphQLError('bim', { extensions: { str: 'ing' } }), }), ], }) diff --git a/packages/plugins/apollo-inline-trace/package.json b/packages/plugins/apollo-inline-trace/package.json index ffdbb26451..1e8cb2cf48 100644 --- a/packages/plugins/apollo-inline-trace/package.json +++ b/packages/plugins/apollo-inline-trace/package.json @@ -41,14 +41,18 @@ "access": "public" }, "peerDependencies": { - "graphql-yoga": "^3.0.0-next.9" + "@graphql-tools/utils": "^9.0.1", + "@whatwg-node/fetch": "^0.5.1", + "graphql-yoga": "^3.0.0-next.9", + "graphql": "^15.2.0 || ^16.0.0" }, "dependencies": { "@envelop/on-resolve": "^2.0.2", - "@whatwg-node/fetch": "0.5.1", "apollo-reporting-protobuf": "^3.3.2" }, "devDependencies": { + "@envelop/on-resolve": "^2.0.2", + "@whatwg-node/fetch": "^0.5.1", "graphql-yoga": "^3.0.0-next.9" } } diff --git a/packages/plugins/apollo-inline-trace/src/index.ts b/packages/plugins/apollo-inline-trace/src/index.ts index cad8f3fb19..d1578178c0 100644 --- a/packages/plugins/apollo-inline-trace/src/index.ts +++ b/packages/plugins/apollo-inline-trace/src/index.ts @@ -1,4 +1,9 @@ -import { isAsyncIterable, Plugin, YogaInitialContext } from 'graphql-yoga' +import { + isAsyncIterable, + Plugin, + YogaInitialContext, + createGraphQLError, +} from 'graphql-yoga' import { GraphQLError, ResponsePath } from 'graphql' import ApolloReportingProtobuf from 'apollo-reporting-protobuf' import { btoa } from '@whatwg-node/fetch' @@ -112,7 +117,7 @@ export function useApolloInlineTrace( handleErrors( ctx, [ - new GraphQLError(result.message, { + createGraphQLError(result.message, { originalError: result, }), ], @@ -292,7 +297,7 @@ function handleErrors( } // only message and extensions can be rewritten - errToReport = new GraphQLError(errToReport.message, { + errToReport = createGraphQLError(errToReport.message, { extensions: errToReport.extensions || err.extensions, nodes: err.nodes, source: err.source, diff --git a/packages/plugins/apq/package.json b/packages/plugins/apq/package.json index d36fd37a84..826314c2fe 100644 --- a/packages/plugins/apq/package.json +++ b/packages/plugins/apq/package.json @@ -43,7 +43,11 @@ "@whatwg-node/fetch": "0.5.1", "tiny-lru": "^10.0.0" }, + "devDependencies": { + "graphql-yoga": "^3.0.0-next.9" + }, "peerDependencies": { + "@graphql-tools/utils": "^9.0.1", "graphql-yoga": "^3.0.0-next.9" }, "type": "module" diff --git a/packages/plugins/apq/src/index.ts b/packages/plugins/apq/src/index.ts index 7ebc96401b..b6683907ec 100644 --- a/packages/plugins/apq/src/index.ts +++ b/packages/plugins/apq/src/index.ts @@ -1,5 +1,4 @@ -import { Plugin, PromiseOrValue } from 'graphql-yoga' -import { GraphQLError } from 'graphql' +import { Plugin, PromiseOrValue, createGraphQLError } from 'graphql-yoga' import { lru } from 'tiny-lru' export async function hashSHA256( @@ -81,7 +80,7 @@ export function useAPQ>( if (params.query == null) { const persistedQuery = await store.get(persistedQueryData.sha256Hash) if (persistedQuery == null) { - throw new GraphQLError('PersistedQueryNotFound', { + throw createGraphQLError('PersistedQueryNotFound', { extensions: { http: { status: 404, @@ -96,7 +95,7 @@ export function useAPQ>( } else { const expectedHash = await hash(params.query, fetchAPI) if (persistedQueryData.sha256Hash !== expectedHash) { - throw new GraphQLError('PersistedQueryMismatch', { + throw createGraphQLError('PersistedQueryMismatch', { extensions: { http: { status: 400, diff --git a/packages/plugins/persisted-operations/package.json b/packages/plugins/persisted-operations/package.json index 9e10e44cf7..9fc4b09674 100644 --- a/packages/plugins/persisted-operations/package.json +++ b/packages/plugins/persisted-operations/package.json @@ -40,10 +40,12 @@ "access": "public" }, "peerDependencies": { + "@graphql-tools/utils": "^9.0.1", "graphql-yoga": "^3.0.0-next.9" }, "devDependencies": { - "@types/lru-cache": "7.10.9" + "@types/lru-cache": "7.10.9", + "graphql-yoga": "^3.0.0-next.9" }, "type": "module" } diff --git a/packages/plugins/persisted-operations/src/index.ts b/packages/plugins/persisted-operations/src/index.ts index 3ee3ddb669..cf3e90966f 100644 --- a/packages/plugins/persisted-operations/src/index.ts +++ b/packages/plugins/persisted-operations/src/index.ts @@ -1,5 +1,9 @@ -import { GraphQLParams, Plugin, PromiseOrValue } from 'graphql-yoga' -import { GraphQLError } from 'graphql' +import { + GraphQLParams, + Plugin, + PromiseOrValue, + createGraphQLError, +} from 'graphql-yoga' export type ExtractPersistedOperationId = ( params: GraphQLParams, @@ -56,7 +60,7 @@ export function usePersistedOperations< ? allowArbitraryOperations : await allowArbitraryOperations(request)) === false ) { - throw new GraphQLError('PersistedQueryOnly') + throw createGraphQLError('PersistedQueryOnly') } return } @@ -64,12 +68,12 @@ export function usePersistedOperations< const persistedOperationKey = extractPersistedOperationId(params) if (persistedOperationKey == null) { - throw new GraphQLError('PersistedQueryNotFound') + throw createGraphQLError('PersistedQueryNotFound') } const persistedQuery = await getPersistedOperation(persistedOperationKey) if (persistedQuery == null) { - throw new GraphQLError('PersistedQueryNotFound') + throw createGraphQLError('PersistedQueryNotFound') } setParams({ query: persistedQuery, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62635daaec..115fcf63c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -860,7 +860,7 @@ importers: '@envelop/validation-cache': 5.0.3 '@graphql-tools/executor': 0.0.6 '@graphql-tools/schema': ^9.0.0 - '@graphql-tools/utils': ^8.12.0 + '@graphql-tools/utils': ^9.0.1 '@graphql-typed-document-node/core': ^3.1.1 '@graphql-yoga/subscription': ^3.0.0-next.0 '@jest/globals': ^29.2.1 @@ -874,6 +874,7 @@ importers: graphql-http: ^1.6.0 graphql-scalars: 1.20.0 html-minifier-terser: 7.0.0 + json-bigint-patch: 0.0.8 puppeteer: 19.2.0 tslib: ^2.3.1 dependencies: @@ -882,6 +883,7 @@ importers: '@envelop/validation-cache': 5.0.3_pbptz7adnmiijjca652cpfgxr4 '@graphql-tools/executor': 0.0.6_graphql@16.6.0 '@graphql-tools/schema': 9.0.4_graphql@16.6.0 + '@graphql-tools/utils': 9.0.1_graphql@16.6.0 '@graphql-typed-document-node/core': 3.1.1_graphql@16.6.0 '@graphql-yoga/subscription': link:../subscription '@whatwg-node/fetch': 0.5.1 @@ -891,7 +893,6 @@ importers: devDependencies: '@envelop/disable-introspection': 4.0.3_pbptz7adnmiijjca652cpfgxr4 '@envelop/live-query': 5.0.3_pbptz7adnmiijjca652cpfgxr4 - '@graphql-tools/utils': 8.12.0_graphql@16.6.0 '@jest/globals': 29.2.1 '@n1ru4l/in-memory-live-query-store': 0.10.0_graphql@16.6.0 '@repeaterjs/repeater': 3.0.4 @@ -900,6 +901,7 @@ importers: graphql-http: 1.6.1_graphql@16.6.0 graphql-scalars: 1.20.0_graphql@16.6.0 html-minifier-terser: 7.0.0 + json-bigint-patch: 0.0.8 puppeteer: 19.2.0 publishDirectory: dist @@ -915,31 +917,36 @@ importers: packages/plugins/apollo-inline-trace: specifiers: '@envelop/on-resolve': ^2.0.2 - '@whatwg-node/fetch': 0.5.1 + '@whatwg-node/fetch': ^0.5.1 apollo-reporting-protobuf: ^3.3.2 graphql-yoga: ^3.0.0-next.9 dependencies: '@envelop/on-resolve': 2.0.2 - '@whatwg-node/fetch': 0.5.1 apollo-reporting-protobuf: 3.3.3 devDependencies: + '@whatwg-node/fetch': 0.5.1 graphql-yoga: link:../../graphql-yoga publishDirectory: dist packages/plugins/apq: specifiers: '@whatwg-node/fetch': 0.5.1 + graphql-yoga: ^3.0.0-next.9 tiny-lru: ^10.0.0 dependencies: '@whatwg-node/fetch': 0.5.1 tiny-lru: 10.0.0 + devDependencies: + graphql-yoga: link:../../graphql-yoga publishDirectory: dist packages/plugins/persisted-operations: specifiers: '@types/lru-cache': 7.10.9 + graphql-yoga: ^3.0.0-next.9 devDependencies: '@types/lru-cache': 7.10.9 + graphql-yoga: link:../../graphql-yoga publishDirectory: dist packages/plugins/prometheus: @@ -18324,7 +18331,6 @@ packages: /json-bigint-patch/0.0.8: resolution: {integrity: sha512-xa0LTQsyaq8awYyZyuUsporWisZFiyqzxGW8CKM3t7oouf0GFAKYJnqAm6e9NLNBQOCtOLvy614DEiRX/rPbnA==} - dev: false /json-buffer/3.0.0: resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} diff --git a/scripts/override-graphql-version.js b/scripts/override-graphql-version.js new file mode 100644 index 0000000000..6eeb3ca6aa --- /dev/null +++ b/scripts/override-graphql-version.js @@ -0,0 +1,16 @@ +const fs = require('fs') +const path = require('path') + +// supply the wished graphql version as first argument of script +const graphqlVersion = process.argv[2] + +const pkgPath = path.resolve(__dirname, '..', 'package.json') +const pkgFile = fs.readFileSync(pkgPath) + +const pkg = JSON.parse(pkgFile.toString()) +pkg.resolutions = { + ...pkg.resolutions, + graphql: graphqlVersion, +} + +fs.writeFileSync(pkgPath, JSON.stringify(pkg, undefined, ' ') + '\n')