msw 2.10.4 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/lib/core/{HttpResponse-C7FhBLaS.d.mts → HttpResponse-DiuKTgC7.d.mts} +21 -7
  2. package/lib/core/{HttpResponse-DWu36LsY.d.ts → HttpResponse-DlQEvD4q.d.ts} +21 -7
  3. package/lib/core/HttpResponse.d.mts +1 -1
  4. package/lib/core/HttpResponse.d.ts +1 -1
  5. package/lib/core/SetupApi.d.mts +1 -1
  6. package/lib/core/SetupApi.d.ts +1 -1
  7. package/lib/core/getResponse.d.mts +1 -1
  8. package/lib/core/getResponse.d.ts +1 -1
  9. package/lib/core/graphql.d.mts +2 -2
  10. package/lib/core/graphql.d.ts +2 -2
  11. package/lib/core/graphql.js +2 -8
  12. package/lib/core/graphql.js.map +1 -1
  13. package/lib/core/graphql.mjs +2 -8
  14. package/lib/core/graphql.mjs.map +1 -1
  15. package/lib/core/handlers/GraphQLHandler.d.mts +1 -1
  16. package/lib/core/handlers/GraphQLHandler.d.ts +1 -1
  17. package/lib/core/handlers/GraphQLHandler.js +36 -9
  18. package/lib/core/handlers/GraphQLHandler.js.map +1 -1
  19. package/lib/core/handlers/GraphQLHandler.mjs +36 -9
  20. package/lib/core/handlers/GraphQLHandler.mjs.map +1 -1
  21. package/lib/core/handlers/HttpHandler.d.mts +15 -5
  22. package/lib/core/handlers/HttpHandler.d.ts +15 -5
  23. package/lib/core/handlers/HttpHandler.js +21 -10
  24. package/lib/core/handlers/HttpHandler.js.map +1 -1
  25. package/lib/core/handlers/HttpHandler.mjs +21 -10
  26. package/lib/core/handlers/HttpHandler.mjs.map +1 -1
  27. package/lib/core/handlers/RequestHandler.d.mts +1 -1
  28. package/lib/core/handlers/RequestHandler.d.ts +1 -1
  29. package/lib/core/handlers/RequestHandler.js +1 -1
  30. package/lib/core/handlers/RequestHandler.js.map +1 -1
  31. package/lib/core/handlers/RequestHandler.mjs +1 -1
  32. package/lib/core/handlers/RequestHandler.mjs.map +1 -1
  33. package/lib/core/http.d.mts +4 -4
  34. package/lib/core/http.d.ts +4 -4
  35. package/lib/core/http.js +2 -2
  36. package/lib/core/http.js.map +1 -1
  37. package/lib/core/http.mjs +2 -2
  38. package/lib/core/http.mjs.map +1 -1
  39. package/lib/core/index.d.mts +2 -2
  40. package/lib/core/index.d.ts +2 -2
  41. package/lib/core/index.js.map +1 -1
  42. package/lib/core/index.mjs.map +1 -1
  43. package/lib/core/passthrough.d.mts +1 -1
  44. package/lib/core/passthrough.d.ts +1 -1
  45. package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
  46. package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
  47. package/lib/core/utils/executeHandlers.d.mts +1 -1
  48. package/lib/core/utils/executeHandlers.d.ts +1 -1
  49. package/lib/core/utils/handleRequest.d.mts +1 -1
  50. package/lib/core/utils/handleRequest.d.ts +1 -1
  51. package/lib/core/utils/internal/isHandlerKind.d.mts +1 -1
  52. package/lib/core/utils/internal/isHandlerKind.d.ts +1 -1
  53. package/lib/core/utils/internal/parseGraphQLRequest.d.mts +1 -1
  54. package/lib/core/utils/internal/parseGraphQLRequest.d.ts +1 -1
  55. package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
  56. package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
  57. package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
  58. package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
  59. package/lib/core/ws/handleWebSocketEvent.d.mts +1 -1
  60. package/lib/core/ws/handleWebSocketEvent.d.ts +1 -1
  61. package/lib/iife/index.js +62 -30
  62. package/lib/iife/index.js.map +1 -1
  63. package/lib/mockServiceWorker.js +1 -1
  64. package/package.json +2 -2
  65. package/src/core/graphql.ts +8 -12
  66. package/src/core/handlers/GraphQLHandler.test.ts +86 -51
  67. package/src/core/handlers/GraphQLHandler.ts +81 -17
  68. package/src/core/handlers/HttpHandler.test.ts +60 -30
  69. package/src/core/handlers/HttpHandler.ts +61 -12
  70. package/src/core/handlers/RequestHandler.ts +2 -2
  71. package/src/core/http.ts +5 -5
  72. package/src/core/index.ts +4 -0
  73. package/src/core/utils/handleRequest.test.ts +84 -0
@@ -7,7 +7,7 @@
7
7
  * - Please do NOT modify this file.
8
8
  */
9
9
 
10
- const PACKAGE_VERSION = '2.10.4'
10
+ const PACKAGE_VERSION = '2.11.0'
11
11
  const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
12
12
  const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
13
13
  const activeClientIds = new Set()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "msw",
3
- "version": "2.10.4",
3
+ "version": "2.11.0",
4
4
  "description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
5
5
  "type": "commonjs",
6
6
  "main": "./lib/core/index.js",
@@ -280,7 +280,7 @@
280
280
  "vitest-environment-miniflare": "^2.14.4",
281
281
  "webpack": "^5.95.0",
282
282
  "webpack-http-server": "^0.5.0",
283
- "msw": "2.10.4"
283
+ "msw": "2.11.0"
284
284
  },
285
285
  "peerDependencies": {
286
286
  "typescript": ">= 4.8.x"
@@ -6,11 +6,12 @@ import {
6
6
  import {
7
7
  GraphQLHandler,
8
8
  GraphQLVariables,
9
- ExpectedOperationTypeNode,
9
+ GraphQLOperationType,
10
10
  GraphQLHandlerNameSelector,
11
11
  GraphQLResolverExtras,
12
12
  GraphQLResponseBody,
13
13
  GraphQLQuery,
14
+ GraphQLCustomPredicate,
14
15
  } from './handlers/GraphQLHandler'
15
16
  import type { Path } from './utils/matching/matchRequestUrl'
16
17
 
@@ -27,10 +28,11 @@ export type GraphQLRequestHandler = <
27
28
  Query extends GraphQLQuery = GraphQLQuery,
28
29
  Variables extends GraphQLVariables = GraphQLVariables,
29
30
  >(
30
- operationName:
31
+ predicate:
31
32
  | GraphQLHandlerNameSelector
32
33
  | DocumentNode
33
- | TypedDocumentNode<Query, Variables>,
34
+ | TypedDocumentNode<Query, Variables>
35
+ | GraphQLCustomPredicate,
34
36
  resolver: GraphQLResponseResolver<
35
37
  [Query] extends [never] ? GraphQLQuery : Query,
36
38
  Variables
@@ -48,17 +50,11 @@ export type GraphQLResponseResolver<
48
50
  >
49
51
 
50
52
  function createScopedGraphQLHandler(
51
- operationType: ExpectedOperationTypeNode,
53
+ operationType: GraphQLOperationType,
52
54
  url: Path,
53
55
  ): GraphQLRequestHandler {
54
- return (operationName, resolver, options = {}) => {
55
- return new GraphQLHandler(
56
- operationType,
57
- operationName,
58
- url,
59
- resolver,
60
- options,
61
- )
56
+ return (predicate, resolver, options = {}) => {
57
+ return new GraphQLHandler(operationType, predicate, url, resolver, options)
62
58
  }
63
59
  }
64
60
 
@@ -1,6 +1,4 @@
1
- /**
2
- * @vitest-environment jsdom
3
- */
1
+ // @vitest-environment jsdom
4
2
  import { createRequestId, encodeBuffer } from '@mswjs/interceptors'
5
3
  import { OperationTypeNode, parse } from 'graphql'
6
4
  import {
@@ -62,7 +60,7 @@ const LOGIN = `
62
60
  `
63
61
 
64
62
  describe('info', () => {
65
- test('exposes request handler information for query', () => {
63
+ it('exposes request handler information for query', () => {
66
64
  const handler = new GraphQLHandler(
67
65
  OperationTypeNode.QUERY,
68
66
  'GetUser',
@@ -75,7 +73,7 @@ describe('info', () => {
75
73
  expect(handler.info.operationName).toEqual('GetUser')
76
74
  })
77
75
 
78
- test('exposes request handler information for mutation', () => {
76
+ it('exposes request handler information for mutation', () => {
79
77
  const handler = new GraphQLHandler(
80
78
  OperationTypeNode.MUTATION,
81
79
  'Login',
@@ -88,7 +86,7 @@ describe('info', () => {
88
86
  expect(handler.info.operationName).toEqual('Login')
89
87
  })
90
88
 
91
- test('parses a query operation name from a given DocumentNode', () => {
89
+ it('parses a query operation name from a given DocumentNode', () => {
92
90
  const node = parse(`
93
91
  query GetUser {
94
92
  user {
@@ -109,7 +107,7 @@ describe('info', () => {
109
107
  expect(handler.info).toHaveProperty('operationName', 'GetUser')
110
108
  })
111
109
 
112
- test('parses a mutation operation name from a given DocumentNode', () => {
110
+ it('parses a mutation operation name from a given DocumentNode', () => {
113
111
  const node = parse(`
114
112
  mutation Login {
115
113
  user {
@@ -129,7 +127,7 @@ describe('info', () => {
129
127
  expect(handler.info).toHaveProperty('operationName', 'Login')
130
128
  })
131
129
 
132
- test('throws an exception given a DocumentNode with a mismatched operation type', () => {
130
+ it('throws an exception given a DocumentNode with a mismatched operation type', () => {
133
131
  const node = parse(`
134
132
  mutation CreateUser {
135
133
  user {
@@ -148,7 +146,7 @@ describe('info', () => {
148
146
 
149
147
  describe('parse', () => {
150
148
  describe('query', () => {
151
- test('parses a query without variables (GET)', async () => {
149
+ it('parses a query without variables (GET)', async () => {
152
150
  const handler = new GraphQLHandler(
153
151
  OperationTypeNode.QUERY,
154
152
  'GetUser',
@@ -174,7 +172,7 @@ describe('parse', () => {
174
172
  })
175
173
  })
176
174
 
177
- test('parses a query with variables (GET)', async () => {
175
+ it('parses a query with variables (GET)', async () => {
178
176
  const handler = new GraphQLHandler(
179
177
  OperationTypeNode.QUERY,
180
178
  'GetUser',
@@ -205,7 +203,7 @@ describe('parse', () => {
205
203
  })
206
204
  })
207
205
 
208
- test('parses a query without variables (POST)', async () => {
206
+ it('parses a query without variables (POST)', async () => {
209
207
  const handler = new GraphQLHandler(
210
208
  OperationTypeNode.QUERY,
211
209
  'GetUser',
@@ -231,7 +229,7 @@ describe('parse', () => {
231
229
  })
232
230
  })
233
231
 
234
- test('parses a query with variables (POST)', async () => {
232
+ it('parses a query with variables (POST)', async () => {
235
233
  const handler = new GraphQLHandler(
236
234
  OperationTypeNode.QUERY,
237
235
  'GetUser',
@@ -264,7 +262,7 @@ describe('parse', () => {
264
262
  })
265
263
 
266
264
  describe('mutation', () => {
267
- test('parses a mutation without variables (GET)', async () => {
265
+ it('parses a mutation without variables (GET)', async () => {
268
266
  const handler = new GraphQLHandler(
269
267
  OperationTypeNode.MUTATION,
270
268
  'GetUser',
@@ -290,7 +288,7 @@ describe('parse', () => {
290
288
  })
291
289
  })
292
290
 
293
- test('parses a mutation with variables (GET)', async () => {
291
+ it('parses a mutation with variables (GET)', async () => {
294
292
  const handler = new GraphQLHandler(
295
293
  OperationTypeNode.MUTATION,
296
294
  'GetUser',
@@ -321,7 +319,7 @@ describe('parse', () => {
321
319
  })
322
320
  })
323
321
 
324
- test('parses a mutation without variables (POST)', async () => {
322
+ it('parses a mutation without variables (POST)', async () => {
325
323
  const handler = new GraphQLHandler(
326
324
  OperationTypeNode.MUTATION,
327
325
  'GetUser',
@@ -347,7 +345,7 @@ describe('parse', () => {
347
345
  })
348
346
  })
349
347
 
350
- test('parses a mutation with variables (POST)', async () => {
348
+ it('parses a mutation with variables (POST)', async () => {
351
349
  const handler = new GraphQLHandler(
352
350
  OperationTypeNode.MUTATION,
353
351
  'GetUser',
@@ -380,7 +378,7 @@ describe('parse', () => {
380
378
  })
381
379
 
382
380
  describe('with endpoint configuration', () => {
383
- test('parses the request and parses grapqhl properties from it when the graphql.link endpoint matches', async () => {
381
+ it('parses the request and parses grapqhl properties from it when the graphql.link endpoint matches', async () => {
384
382
  const handler = new GraphQLHandler(
385
383
  OperationTypeNode.QUERY,
386
384
  'GetUser',
@@ -441,7 +439,7 @@ describe('parse', () => {
441
439
  })
442
440
  })
443
441
 
444
- test('parses a request but does not parse graphql properties from it graphql.link hostname does not match', async () => {
442
+ it('parses a request but does not parse graphql properties from it graphql.link hostname does not match', async () => {
445
443
  const handler = new GraphQLHandler(
446
444
  OperationTypeNode.QUERY,
447
445
  'GetUser',
@@ -490,7 +488,7 @@ describe('parse', () => {
490
488
  })
491
489
  })
492
490
 
493
- test('parses a request but does not parse graphql properties from it graphql.link pathname does not match', async () => {
491
+ it('parses a request but does not parse graphql properties from it graphql.link pathname does not match', async () => {
494
492
  const handler = new GraphQLHandler(
495
493
  OperationTypeNode.QUERY,
496
494
  'GetUser',
@@ -542,7 +540,7 @@ describe('parse', () => {
542
540
  })
543
541
 
544
542
  describe('predicate', () => {
545
- test('respects operation type', async () => {
543
+ it('respects operation type', async () => {
546
544
  const handler = new GraphQLHandler(
547
545
  OperationTypeNode.QUERY,
548
546
  'GetUser',
@@ -557,20 +555,20 @@ describe('predicate', () => {
557
555
  })
558
556
 
559
557
  expect(
560
- handler.predicate({
558
+ await handler.predicate({
561
559
  request,
562
560
  parsedResult: await handler.parse({ request }),
563
561
  }),
564
562
  ).toBe(true)
565
563
  expect(
566
- handler.predicate({
564
+ await handler.predicate({
567
565
  request: alienRequest,
568
566
  parsedResult: await handler.parse({ request: alienRequest }),
569
567
  }),
570
568
  ).toBe(false)
571
569
  })
572
570
 
573
- test('respects operation name', async () => {
571
+ it('respects operation name', async () => {
574
572
  const handler = new GraphQLHandler(
575
573
  OperationTypeNode.QUERY,
576
574
  'GetUser',
@@ -590,21 +588,21 @@ describe('predicate', () => {
590
588
  `,
591
589
  })
592
590
 
593
- expect(
591
+ await expect(
594
592
  handler.predicate({
595
593
  request,
596
594
  parsedResult: await handler.parse({ request }),
597
595
  }),
598
- ).toBe(true)
599
- expect(
596
+ ).resolves.toBe(true)
597
+ await expect(
600
598
  handler.predicate({
601
599
  request: alienRequest,
602
600
  parsedResult: await handler.parse({ request: alienRequest }),
603
601
  }),
604
- ).toBe(false)
602
+ ).resolves.toBe(false)
605
603
  })
606
604
 
607
- test('allows anonymous GraphQL opertaions when using "all" expected operation type', async () => {
605
+ it('allows anonymous GraphQL opertaions when using "all" expected operation type', async () => {
608
606
  const handler = new GraphQLHandler('all', new RegExp('.*'), '*', resolver)
609
607
  const request = createPostGraphQLRequest({
610
608
  query: `
@@ -617,15 +615,15 @@ describe('predicate', () => {
617
615
  `,
618
616
  })
619
617
 
620
- expect(
618
+ await expect(
621
619
  handler.predicate({
622
620
  request,
623
621
  parsedResult: await handler.parse({ request }),
624
622
  }),
625
- ).toBe(true)
623
+ ).resolves.toBe(true)
626
624
  })
627
625
 
628
- test('respects custom endpoint', async () => {
626
+ it('respects custom endpoint', async () => {
629
627
  const handler = new GraphQLHandler(
630
628
  OperationTypeNode.QUERY,
631
629
  'GetUser',
@@ -642,23 +640,60 @@ describe('predicate', () => {
642
640
  query: GET_USER,
643
641
  })
644
642
 
645
- expect(
643
+ await expect(
646
644
  handler.predicate({
647
645
  request,
648
646
  parsedResult: await handler.parse({ request }),
649
647
  }),
650
- ).toBe(true)
651
- expect(
648
+ ).resolves.toBe(true)
649
+ await expect(
652
650
  handler.predicate({
653
651
  request: alienRequest,
654
652
  parsedResult: await handler.parse({ request: alienRequest }),
655
653
  }),
656
- ).toBe(false)
654
+ ).resolves.toBe(false)
655
+ })
656
+
657
+ it('supports custom predicate function', async () => {
658
+ const handler = new GraphQLHandler(
659
+ OperationTypeNode.QUERY,
660
+ ({ query }) => {
661
+ return query.includes('password')
662
+ },
663
+ /.+/,
664
+ resolver,
665
+ )
666
+
667
+ {
668
+ const request = createPostGraphQLRequest({
669
+ query: `query GetUser { user { password } }`,
670
+ })
671
+
672
+ await expect(
673
+ handler.predicate({
674
+ request,
675
+ parsedResult: await handler.parse({ request }),
676
+ }),
677
+ ).resolves.toBe(true)
678
+ }
679
+
680
+ {
681
+ const request = createPostGraphQLRequest({
682
+ query: `query GetUser { user { nonMatching } }`,
683
+ })
684
+
685
+ await expect(
686
+ handler.predicate({
687
+ request,
688
+ parsedResult: await handler.parse({ request }),
689
+ }),
690
+ ).resolves.toBe(false)
691
+ }
657
692
  })
658
693
  })
659
694
 
660
695
  describe('test', () => {
661
- test('respects operation type', async () => {
696
+ it('respects operation type', async () => {
662
697
  const handler = new GraphQLHandler(
663
698
  OperationTypeNode.QUERY,
664
699
  'GetUser',
@@ -676,7 +711,7 @@ describe('test', () => {
676
711
  expect(await handler.test({ request: alienRequest })).toBe(false)
677
712
  })
678
713
 
679
- test('respects operation name', async () => {
714
+ it('respects operation name', async () => {
680
715
  const handler = new GraphQLHandler(
681
716
  OperationTypeNode.QUERY,
682
717
  'GetUser',
@@ -696,11 +731,11 @@ describe('test', () => {
696
731
  `,
697
732
  })
698
733
 
699
- expect(await handler.test({ request })).toBe(true)
700
- expect(await handler.test({ request: alienRequest })).toBe(false)
734
+ await expect(handler.test({ request })).resolves.toBe(true)
735
+ await expect(handler.test({ request: alienRequest })).resolves.toBe(false)
701
736
  })
702
737
 
703
- test('respects custom endpoint', async () => {
738
+ it('respects custom endpoint', async () => {
704
739
  const handler = new GraphQLHandler(
705
740
  OperationTypeNode.QUERY,
706
741
  'GetUser',
@@ -717,13 +752,13 @@ describe('test', () => {
717
752
  query: GET_USER,
718
753
  })
719
754
 
720
- expect(await handler.test({ request })).toBe(true)
721
- expect(await handler.test({ request: alienRequest })).toBe(false)
755
+ await expect(handler.test({ request })).resolves.toBe(true)
756
+ await expect(handler.test({ request: alienRequest })).resolves.toBe(false)
722
757
  })
723
758
  })
724
759
 
725
760
  describe('run', () => {
726
- test('returns a mocked response given a matching query', async () => {
761
+ it('returns a mocked response given a matching query', async () => {
727
762
  const handler = new GraphQLHandler(
728
763
  OperationTypeNode.QUERY,
729
764
  'GetUser',
@@ -757,18 +792,18 @@ describe('run', () => {
757
792
  })
758
793
  expect(result!.request.method).toBe('POST')
759
794
  expect(result!.request.url).toBe('https://example.com/')
760
- expect(await result!.request.json()).toEqual({
795
+ await expect(result!.request.json()).resolves.toEqual({
761
796
  query: GET_USER,
762
797
  variables: { userId: 'abc-123' },
763
798
  })
764
799
  expect(result!.response?.status).toBe(200)
765
800
  expect(result!.response?.statusText).toBe('OK')
766
- expect(await result!.response?.json()).toEqual({
801
+ await expect(result!.response?.json()).resolves.toEqual({
767
802
  data: { user: { id: 'abc-123' } },
768
803
  })
769
804
  })
770
805
 
771
- test('returns null given a non-matching query', async () => {
806
+ it('returns null given a non-matching query', async () => {
772
807
  const handler = new GraphQLHandler(
773
808
  OperationTypeNode.QUERY,
774
809
  'GetUser',
@@ -818,12 +853,12 @@ describe('request', () => {
818
853
  )
819
854
  const request = createPostGraphQLRequest({
820
855
  query: `
821
- query GetAllUsers {
822
- user {
823
- id
824
- }
856
+ query GetAllUsers {
857
+ user {
858
+ id
825
859
  }
826
- `,
860
+ }
861
+ `,
827
862
  })
828
863
 
829
864
  const requestId = createRequestId()
@@ -21,15 +21,15 @@ import { toPublicUrl } from '../utils/request/toPublicUrl'
21
21
  import { devUtils } from '../utils/internal/devUtils'
22
22
  import { getAllRequestCookies } from '../utils/request/getRequestCookies'
23
23
 
24
- export type ExpectedOperationTypeNode = OperationTypeNode | 'all'
24
+ export type GraphQLOperationType = OperationTypeNode | 'all'
25
25
  export type GraphQLHandlerNameSelector = DocumentNode | RegExp | string
26
26
 
27
27
  export type GraphQLQuery = Record<string, any> | null
28
28
  export type GraphQLVariables = Record<string, any>
29
29
 
30
30
  export interface GraphQLHandlerInfo extends RequestHandlerDefaultInfo {
31
- operationType: ExpectedOperationTypeNode
32
- operationName: GraphQLHandlerNameSelector
31
+ operationType: GraphQLOperationType
32
+ operationName: GraphQLHandlerNameSelector | GraphQLCustomPredicate
33
33
  }
34
34
 
35
35
  export type GraphQLRequestParsedResult = {
@@ -77,6 +77,21 @@ export type GraphQLResponseBody<BodyType extends DefaultBodyType> =
77
77
  | null
78
78
  | undefined
79
79
 
80
+ export type GraphQLCustomPredicate = (args: {
81
+ request: Request
82
+ query: string
83
+ operationType: GraphQLOperationType
84
+ operationName: string
85
+ variables: GraphQLVariables
86
+ cookies: Record<string, string>
87
+ }) => GraphQLCustomPredicateResult | Promise<GraphQLCustomPredicateResult>
88
+
89
+ export type GraphQLCustomPredicateResult = boolean | { matches: boolean }
90
+
91
+ export type GraphQLPredicate =
92
+ | GraphQLHandlerNameSelector
93
+ | GraphQLCustomPredicate
94
+
80
95
  export function isDocumentNode(
81
96
  value: DocumentNode | any,
82
97
  ): value is DocumentNode {
@@ -100,16 +115,16 @@ export class GraphQLHandler extends RequestHandler<
100
115
  >()
101
116
 
102
117
  constructor(
103
- operationType: ExpectedOperationTypeNode,
104
- operationName: GraphQLHandlerNameSelector,
118
+ operationType: GraphQLOperationType,
119
+ predicate: GraphQLPredicate,
105
120
  endpoint: Path,
106
121
  resolver: ResponseResolver<GraphQLResolverExtras<any>, any, any>,
107
122
  options?: RequestHandlerOptions,
108
123
  ) {
109
- let resolvedOperationName = operationName
124
+ let resolvedOperationName = predicate
110
125
 
111
- if (isDocumentNode(operationName)) {
112
- const parsedNode = parseDocumentNode(operationName)
126
+ if (isDocumentNode(resolvedOperationName)) {
127
+ const parsedNode = parseDocumentNode(resolvedOperationName)
113
128
 
114
129
  if (parsedNode.operationType !== operationType) {
115
130
  throw new Error(
@@ -126,10 +141,15 @@ export class GraphQLHandler extends RequestHandler<
126
141
  resolvedOperationName = parsedNode.operationName
127
142
  }
128
143
 
144
+ const displayOperationName =
145
+ typeof resolvedOperationName === 'function'
146
+ ? '[custom predicate]'
147
+ : resolvedOperationName
148
+
129
149
  const header =
130
150
  operationType === 'all'
131
151
  ? `${operationType} (origin: ${endpoint.toString()})`
132
- : `${operationType} ${resolvedOperationName} (origin: ${endpoint.toString()})`
152
+ : `${operationType}${displayOperationName ? ` ${displayOperationName}` : ''} (origin: ${endpoint.toString()})`
133
153
 
134
154
  super({
135
155
  info: {
@@ -175,7 +195,10 @@ export class GraphQLHandler extends RequestHandler<
175
195
  const cookies = getAllRequestCookies(args.request)
176
196
 
177
197
  if (!match.matches) {
178
- return { match, cookies }
198
+ return {
199
+ match,
200
+ cookies,
201
+ }
179
202
  }
180
203
 
181
204
  const parsedResult = await this.parseGraphQLRequestOrGetFromCache(
@@ -183,7 +206,10 @@ export class GraphQLHandler extends RequestHandler<
183
206
  )
184
207
 
185
208
  if (typeof parsedResult === 'undefined') {
186
- return { match, cookies }
209
+ return {
210
+ match,
211
+ cookies,
212
+ }
187
213
  }
188
214
 
189
215
  return {
@@ -196,10 +222,10 @@ export class GraphQLHandler extends RequestHandler<
196
222
  }
197
223
  }
198
224
 
199
- predicate(args: {
225
+ async predicate(args: {
200
226
  request: Request
201
227
  parsedResult: GraphQLRequestParsedResult
202
- }) {
228
+ }): Promise<boolean> {
203
229
  if (args.parsedResult.operationType === undefined) {
204
230
  return false
205
231
  }
@@ -218,10 +244,16 @@ Consider naming this operation or using "graphql.operation()" request handler to
218
244
  this.info.operationType === 'all' ||
219
245
  args.parsedResult.operationType === this.info.operationType
220
246
 
221
- const hasMatchingOperationName =
222
- this.info.operationName instanceof RegExp
223
- ? this.info.operationName.test(args.parsedResult.operationName || '')
224
- : args.parsedResult.operationName === this.info.operationName
247
+ /**
248
+ * Check if the operation name matches the outgoing GraphQL request.
249
+ * @note Unlike the HTTP handler, the custom predicate functions are invoked
250
+ * during predicate, not parsing, because GraphQL request parsing happens first,
251
+ * and non-GraphQL requests are filtered out automatically.
252
+ */
253
+ const hasMatchingOperationName = await this.matchOperationName({
254
+ request: args.request,
255
+ parsedResult: args.parsedResult,
256
+ })
225
257
 
226
258
  return (
227
259
  args.parsedResult.match.matches &&
@@ -230,12 +262,44 @@ Consider naming this operation or using "graphql.operation()" request handler to
230
262
  )
231
263
  }
232
264
 
265
+ private async matchOperationName(args: {
266
+ request: Request
267
+ parsedResult: GraphQLRequestParsedResult
268
+ }): Promise<boolean> {
269
+ if (typeof this.info.operationName === 'function') {
270
+ const customPredicateResult = await this.info.operationName({
271
+ request: args.request,
272
+ ...this.extendResolverArgs({
273
+ request: args.request,
274
+ parsedResult: args.parsedResult,
275
+ }),
276
+ })
277
+
278
+ /**
279
+ * @note Keep the { matches } signature in case we decide to support path parameters
280
+ * in GraphQL handlers. If that happens, the custom predicate would have to be moved
281
+ * to the parsing phase, the same as we have for the HttpHandler, and the user will
282
+ * have a possibility to return parsed path parameters from the custom predicate.
283
+ */
284
+ return typeof customPredicateResult === 'boolean'
285
+ ? customPredicateResult
286
+ : customPredicateResult.matches
287
+ }
288
+
289
+ if (this.info.operationName instanceof RegExp) {
290
+ return this.info.operationName.test(args.parsedResult.operationName || '')
291
+ }
292
+
293
+ return args.parsedResult.operationName === this.info.operationName
294
+ }
295
+
233
296
  protected extendResolverArgs(args: {
234
297
  request: Request
235
298
  parsedResult: GraphQLRequestParsedResult
236
299
  }) {
237
300
  return {
238
301
  query: args.parsedResult.query || '',
302
+ operationType: args.parsedResult.operationType!,
239
303
  operationName: args.parsedResult.operationName || '',
240
304
  variables: args.parsedResult.variables || {},
241
305
  cookies: args.parsedResult.cookies,