diff --git a/.changeset/short-zebras-call.md b/.changeset/short-zebras-call.md new file mode 100644 index 0000000000..2f0f7c553d --- /dev/null +++ b/.changeset/short-zebras-call.md @@ -0,0 +1,5 @@ +--- +'graphql-yoga': patch +--- + +Correct Mask Error Factory signature diff --git a/packages/graphql-yoga/__tests__/error-masking.spec.ts b/packages/graphql-yoga/__tests__/error-masking.spec.ts index fff3f22351..2a5b3f5342 100644 --- a/packages/graphql-yoga/__tests__/error-masking.spec.ts +++ b/packages/graphql-yoga/__tests__/error-masking.spec.ts @@ -1,3 +1,4 @@ +import { inspect } from '@graphql-tools/utils' import { createGraphQLError, createSchema, createYoga } from 'graphql-yoga' describe('error masking', () => { @@ -422,4 +423,49 @@ describe('error masking', () => { ], }) }) + + it('call the custom maskError function with correct parameters', async () => { + const yoga = createYoga({ + logging: false, + context: () => { + throw new Error('I like turtles') + }, + maskedErrors: { + errorMessage: 'My message', + maskError: (error, message, isDev) => { + return createGraphQLError( + inspect({ + errorStr: String(error), + message, + isDev, + }), + ) + }, + isDev: true, + }, + schema: createSchema({ + typeDefs: /* GraphQL */ ` + type Query { + a: String! + } + `, + }), + }) + + const response = await yoga.fetch('http://yoga/graphql', { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ query: '{ __typename }' }), + }) + expect(await response.json()).toMatchObject({ + errors: [ + { + message: + '{ errorStr: "Error: I like turtles", message: "My message", isDev: true }', + }, + ], + }) + }) }) diff --git a/packages/graphql-yoga/src/error.ts b/packages/graphql-yoga/src/error.ts index 5293ef0abb..3d3a3bbe43 100644 --- a/packages/graphql-yoga/src/error.ts +++ b/packages/graphql-yoga/src/error.ts @@ -59,6 +59,7 @@ export function handleError( const maskedError = maskedErrorsOpts.maskError( error, maskedErrorsOpts.errorMessage, + maskedErrorsOpts.isDev, ) if (maskedError !== error) { diff --git a/packages/graphql-yoga/src/schema.ts b/packages/graphql-yoga/src/schema.ts index 503c589bd9..ce75f7953e 100644 --- a/packages/graphql-yoga/src/schema.ts +++ b/packages/graphql-yoga/src/schema.ts @@ -8,7 +8,5 @@ import { GraphQLSchemaWithContext, YogaInitialContext } from './types.js' export function createSchema( opts: IExecutableSchemaDefinition, ): GraphQLSchemaWithContext { - return makeExecutableSchema({ - ...opts, - }) + return makeExecutableSchema(opts) } diff --git a/packages/graphql-yoga/src/server.ts b/packages/graphql-yoga/src/server.ts index 7b81eefbab..217224fcfd 100644 --- a/packages/graphql-yoga/src/server.ts +++ b/packages/graphql-yoga/src/server.ts @@ -15,6 +15,7 @@ import { FetchAPI, GraphQLParams, YogaMaskedErrorOpts, + MaskError, } from './types.js' import { OnParamsHook, @@ -221,11 +222,13 @@ export class YogaServer< ? createLogger(logger) : logger - const maskErrorFn = + const maskErrorFn: MaskError = (typeof options?.maskedErrors === 'object' && options.maskedErrors.maskError) || yogaDefaultFormatError + const maskedErrorSet = new WeakSet() + this.maskedErrorsOpts = options?.maskedErrors === false ? null @@ -235,19 +238,21 @@ export class YogaServer< ? options.maskedErrors : {}), maskError: (error, message) => { + if (maskedErrorSet.has(error as Error)) { + return error as Error + } const newError = maskErrorFn( - { - error, - message, - isDev: this.maskedErrorsOpts?.isDev ?? false, - }, + error, message, + this.maskedErrorsOpts?.isDev, ) if (newError !== error) { this.logger.error(error) } + maskedErrorSet.add(newError) + return newError }, } diff --git a/packages/graphql-yoga/src/types.ts b/packages/graphql-yoga/src/types.ts index 000554c459..0a80d91ed4 100644 --- a/packages/graphql-yoga/src/types.ts +++ b/packages/graphql-yoga/src/types.ts @@ -1,4 +1,4 @@ -import type { MaskError, PromiseOrValue } from '@envelop/core' +import type { PromiseOrValue } from '@envelop/core' import type { createFetch } from '@whatwg-node/fetch' import type { GraphQLSchema } from 'graphql' @@ -57,4 +57,10 @@ export type YogaMaskedErrorOpts = { isDev?: boolean } +export type MaskError = ( + error: unknown, + message: string, + isDev?: boolean, +) => Error + export type MaybeArray = T | T[] diff --git a/packages/graphql-yoga/src/utils/yoga-default-format-error.ts b/packages/graphql-yoga/src/utils/yoga-default-format-error.ts index 3be5b77e88..9deb93217d 100644 --- a/packages/graphql-yoga/src/utils/yoga-default-format-error.ts +++ b/packages/graphql-yoga/src/utils/yoga-default-format-error.ts @@ -1,18 +1,13 @@ import { createGraphQLError } from '@graphql-tools/utils' import { GraphQLErrorExtensions } from 'graphql' import { isGraphQLError } from '../error.js' +import { MaskError } from '../types.js' -export const yogaDefaultFormatError = ({ - error, - message, - isDev, -}: { - error: unknown - message: string - isDev?: boolean -}) => { - const dev = isDev || globalThis.process?.env?.NODE_ENV === 'development' - +export const yogaDefaultFormatError: MaskError = ( + error: unknown, + message: string, + isDev = globalThis.process?.env?.NODE_ENV === 'development', +) => { if (isGraphQLError(error)) { if (error.originalError) { if (error.originalError.name === 'GraphQLError') { @@ -23,7 +18,7 @@ export const yogaDefaultFormatError = ({ ...error.extensions, unexpected: true, } - if (dev) { + if (isDev) { extensions.originalError = { message: error.originalError.message, stack: error.originalError.stack, @@ -43,7 +38,7 @@ export const yogaDefaultFormatError = ({ return createGraphQLError(message, { extensions: { unexpected: true, - originalError: dev + originalError: isDev ? error instanceof Error ? { message: error.message,