graphql-http 1.7.2 → 1.9.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.
package/README.md CHANGED
@@ -735,7 +735,6 @@ If you want a feature-full server with bleeding edge technologies, you're recomm
735
735
  | [hotchocolate](https://chillicream.com/docs/hotchocolate) | [✅ Compliant](/implementations/hotchocolate/README.md) |
736
736
  | [postgraphile](https://www.graphile.org/postgraphile/) | [✅ Compliant](/implementations/postgraphile/README.md) |
737
737
  | [apollo-server](https://www.apollographql.com/docs/apollo-server/) | [✅ Compliant](/implementations/apollo-server/README.md) |
738
- | [mercurius](https://mercurius.dev) | [✅ Compliant](/implementations/mercurius/README.md) |
739
738
 
740
739
  ## [Documentation](docs/)
741
740
 
@@ -12,8 +12,11 @@ import { Audit, AuditResult } from './common';
12
12
  export interface ServerAuditOptions {
13
13
  /**
14
14
  * The URL of the GraphQL server for the audit.
15
+ *
16
+ * A function can be also supplied, in this case -
17
+ * every audit will invoke the function to get the URL.
15
18
  */
16
- url: string;
19
+ url: string | Promise<string> | (() => string | Promise<string>);
17
20
  /**
18
21
  * The Fetch function to use.
19
22
  *
@@ -12,8 +12,11 @@ import { Audit, AuditResult } from './common';
12
12
  export interface ServerAuditOptions {
13
13
  /**
14
14
  * The URL of the GraphQL server for the audit.
15
+ *
16
+ * A function can be also supplied, in this case -
17
+ * every audit will invoke the function to get the URL.
15
18
  */
16
- url: string;
19
+ url: string | Promise<string> | (() => string | Promise<string>);
17
20
  /**
18
21
  * The Fetch function to use.
19
22
  *
@@ -19,7 +19,7 @@ function serverAudits(opts) {
19
19
  (0, utils_1.audit)(
20
20
  // TODO: convert to MUST after watershed
21
21
  'SHOULD accept application/graphql-response+json and match the content-type', async () => {
22
- const res = await fetchFn(opts.url, {
22
+ const res = await fetchFn(await getUrl(opts.url), {
23
23
  method: 'POST',
24
24
  headers: {
25
25
  'content-type': 'application/json',
@@ -31,7 +31,7 @@ function serverAudits(opts) {
31
31
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
32
32
  }),
33
33
  (0, utils_1.audit)('MUST accept application/json and match the content-type', async () => {
34
- const res = await fetchFn(opts.url, {
34
+ const res = await fetchFn(await getUrl(opts.url), {
35
35
  method: 'POST',
36
36
  headers: {
37
37
  'content-type': 'application/json',
@@ -43,7 +43,7 @@ function serverAudits(opts) {
43
43
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
44
44
  }),
45
45
  (0, utils_1.audit)('SHOULD accept */* and use application/json for the content-type', async () => {
46
- const res = await fetchFn(opts.url, {
46
+ const res = await fetchFn(await getUrl(opts.url), {
47
47
  method: 'POST',
48
48
  headers: {
49
49
  'content-type': 'application/json',
@@ -55,14 +55,14 @@ function serverAudits(opts) {
55
55
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
56
56
  }),
57
57
  (0, utils_1.audit)('SHOULD assume application/json content-type when accept is missing', async () => {
58
- const url = new URL(opts.url);
58
+ const url = new URL(await getUrl(opts.url));
59
59
  url.searchParams.set('query', '{ __typename }');
60
60
  const res = await fetchFn(url.toString());
61
61
  (0, utils_1.assert)('Status code', res.status).toBe(200);
62
62
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('application/json');
63
63
  }),
64
64
  (0, utils_1.audit)('MUST use utf-8 encoding when responding', async () => {
65
- const res = await fetchFn(opts.url, {
65
+ const res = await fetchFn(await getUrl(opts.url), {
66
66
  method: 'POST',
67
67
  headers: {
68
68
  'content-type': 'application/json',
@@ -91,7 +91,7 @@ function serverAudits(opts) {
91
91
  }
92
92
  }),
93
93
  (0, utils_1.audit)('MUST accept utf-8 encoding', async () => {
94
- const res = await fetchFn(opts.url, {
94
+ const res = await fetchFn(await getUrl(opts.url), {
95
95
  method: 'POST',
96
96
  headers: {
97
97
  'content-type': 'application/json; charset=utf-8',
@@ -102,7 +102,7 @@ function serverAudits(opts) {
102
102
  (0, utils_1.assert)('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
103
103
  }),
104
104
  (0, utils_1.audit)('MUST assume utf-8 if encoding is unspecified', async () => {
105
- const res = await fetchFn(opts.url, {
105
+ const res = await fetchFn(await getUrl(opts.url), {
106
106
  method: 'POST',
107
107
  headers: {
108
108
  'content-type': 'application/json',
@@ -114,7 +114,7 @@ function serverAudits(opts) {
114
114
  }),
115
115
  // Request
116
116
  (0, utils_1.audit)('MUST accept POST requests', async () => {
117
- const res = await fetchFn(opts.url, {
117
+ const res = await fetchFn(await getUrl(opts.url), {
118
118
  method: 'POST',
119
119
  headers: { 'content-type': 'application/json' },
120
120
  body: JSON.stringify({ query: '{ __typename }' }),
@@ -122,7 +122,7 @@ function serverAudits(opts) {
122
122
  (0, utils_1.assert)('Status code', res.status).toBe(200);
123
123
  }),
124
124
  (0, utils_1.audit)('MAY accept application/x-www-form-urlencoded formatted GET requests', async () => {
125
- const url = new URL(opts.url);
125
+ const url = new URL(await getUrl(opts.url));
126
126
  url.searchParams.set('query', '{ __typename }');
127
127
  const res = await fetchFn(url.toString());
128
128
  (0, utils_1.assert)('Status code', res.status).toBe(200);
@@ -130,7 +130,7 @@ function serverAudits(opts) {
130
130
  // Request GET
131
131
  // TODO: this is a MUST if the server supports GET requests
132
132
  (0, utils_1.audit)('MAY NOT allow executing mutations on GET requests', async () => {
133
- const url = new URL(opts.url);
133
+ const url = new URL(await getUrl(opts.url));
134
134
  url.searchParams.set('query', 'mutation { __typename }');
135
135
  const res = await fetchFn(url.toString(), {
136
136
  headers: {
@@ -141,13 +141,13 @@ function serverAudits(opts) {
141
141
  }),
142
142
  // Request POST
143
143
  (0, utils_1.audit)('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
144
- const res = await fetchFn(opts.url, {
144
+ const res = await fetchFn(await getUrl(opts.url), {
145
145
  method: 'POST',
146
146
  });
147
147
  (0, utils_1.assert)('Status code', res.status).toBeBetween(400, 499);
148
148
  }),
149
149
  (0, utils_1.audit)('MUST accept application/json POST requests', async () => {
150
- const res = await fetchFn(opts.url, {
150
+ const res = await fetchFn(await getUrl(opts.url), {
151
151
  method: 'POST',
152
152
  headers: { 'content-type': 'application/json' },
153
153
  body: JSON.stringify({ query: '{ __typename }' }),
@@ -156,7 +156,7 @@ function serverAudits(opts) {
156
156
  }),
157
157
  (0, utils_1.audit)('MUST require a request body on POST', async () => {
158
158
  var _a;
159
- const res = await fetchFn(opts.url, {
159
+ const res = await fetchFn(await getUrl(opts.url), {
160
160
  method: 'POST',
161
161
  headers: { 'content-type': 'application/json' },
162
162
  });
@@ -171,7 +171,7 @@ function serverAudits(opts) {
171
171
  (0, utils_1.audit)(
172
172
  // TODO: convert to MUST after watershed
173
173
  'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
174
- const res = await fetchFn(opts.url, {
174
+ const res = await fetchFn(await getUrl(opts.url), {
175
175
  method: 'POST',
176
176
  headers: {
177
177
  'content-type': 'application/json',
@@ -182,7 +182,7 @@ function serverAudits(opts) {
182
182
  (0, utils_1.assert)('Status code', res.status).toBe(400);
183
183
  }),
184
184
  (0, utils_1.audit)('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
185
- const res = await fetchFn(opts.url, {
185
+ const res = await fetchFn(await getUrl(opts.url), {
186
186
  method: 'POST',
187
187
  headers: {
188
188
  'content-type': 'application/json',
@@ -196,7 +196,7 @@ function serverAudits(opts) {
196
196
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
197
197
  // TODO: convert to MUST after watershed
198
198
  `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
199
- const res = await fetchFn(opts.url, {
199
+ const res = await fetchFn(await getUrl(opts.url), {
200
200
  method: 'POST',
201
201
  headers: {
202
202
  'content-type': 'application/json',
@@ -209,7 +209,7 @@ function serverAudits(opts) {
209
209
  (0, utils_1.assert)('Status code', res.status).toBe(400);
210
210
  })),
211
211
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {query} parameter when accepting application/json`, async () => {
212
- const res = await fetchFn(opts.url, {
212
+ const res = await fetchFn(await getUrl(opts.url), {
213
213
  method: 'POST',
214
214
  headers: {
215
215
  'content-type': 'application/json',
@@ -225,7 +225,7 @@ function serverAudits(opts) {
225
225
  (0, utils_1.audit)(
226
226
  // TODO: convert to MUST after watershed
227
227
  'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
228
- const res = await fetchFn(opts.url, {
228
+ const res = await fetchFn(await getUrl(opts.url), {
229
229
  method: 'POST',
230
230
  headers: {
231
231
  'content-type': 'application/json',
@@ -238,7 +238,7 @@ function serverAudits(opts) {
238
238
  (0, utils_1.assert)('Status code', res.status).toBe(200);
239
239
  }),
240
240
  (0, utils_1.audit)('MUST allow string {query} parameter when accepting application/json', async () => {
241
- const res = await fetchFn(opts.url, {
241
+ const res = await fetchFn(await getUrl(opts.url), {
242
242
  method: 'POST',
243
243
  headers: {
244
244
  'content-type': 'application/json',
@@ -254,7 +254,7 @@ function serverAudits(opts) {
254
254
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
255
255
  // TODO: convert to MUST after watershed
256
256
  `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
257
- const res = await fetchFn(opts.url, {
257
+ const res = await fetchFn(await getUrl(opts.url), {
258
258
  method: 'POST',
259
259
  headers: {
260
260
  'content-type': 'application/json',
@@ -268,7 +268,7 @@ function serverAudits(opts) {
268
268
  (0, utils_1.assert)('Status code', res.status).toBe(400);
269
269
  })),
270
270
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {operationName} parameter when accepting application/json`, async () => {
271
- const res = await fetchFn(opts.url, {
271
+ const res = await fetchFn(await getUrl(opts.url), {
272
272
  method: 'POST',
273
273
  headers: {
274
274
  'content-type': 'application/json',
@@ -285,7 +285,7 @@ function serverAudits(opts) {
285
285
  (0, utils_1.audit)(
286
286
  // TODO: convert to MUST after watershed
287
287
  'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
288
- const res = await fetchFn(opts.url, {
288
+ const res = await fetchFn(await getUrl(opts.url), {
289
289
  method: 'POST',
290
290
  headers: {
291
291
  'content-type': 'application/json',
@@ -299,7 +299,7 @@ function serverAudits(opts) {
299
299
  (0, utils_1.assert)('Status code', res.status).toBe(200);
300
300
  }),
301
301
  (0, utils_1.audit)('MUST allow string {operationName} parameter when accepting application/json', async () => {
302
- const res = await fetchFn(opts.url, {
302
+ const res = await fetchFn(await getUrl(opts.url), {
303
303
  method: 'POST',
304
304
  headers: {
305
305
  'content-type': 'application/json',
@@ -313,10 +313,44 @@ function serverAudits(opts) {
313
313
  (0, utils_1.assert)('Status code', res.status).toBe(200);
314
314
  (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
315
315
  }),
316
+ ...['variables', 'operationName', 'extensions'].flatMap((parameter) => [
317
+ (0, utils_1.audit)(
318
+ // TODO: convert to MUST after watershed
319
+ `SHOULD allow null {${parameter}} parameter when accepting application/graphql-response+json`, async () => {
320
+ const res = await fetchFn(await getUrl(opts.url), {
321
+ method: 'POST',
322
+ headers: {
323
+ 'content-type': 'application/json',
324
+ accept: 'application/graphql-response+json',
325
+ },
326
+ body: JSON.stringify({
327
+ query: '{ __typename }',
328
+ [parameter]: null,
329
+ }),
330
+ });
331
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
332
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
333
+ }),
334
+ (0, utils_1.audit)(`MUST allow null {${parameter}} parameter when accepting application/json`, async () => {
335
+ const res = await fetchFn(await getUrl(opts.url), {
336
+ method: 'POST',
337
+ headers: {
338
+ 'content-type': 'application/json',
339
+ accept: 'application/json',
340
+ },
341
+ body: JSON.stringify({
342
+ query: '{ __typename }',
343
+ [parameter]: null,
344
+ }),
345
+ });
346
+ (0, utils_1.assert)('Status code', res.status).toBe(200);
347
+ (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
348
+ }),
349
+ ]),
316
350
  ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
317
351
  // TODO: convert to MUST after watershed
318
352
  `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
319
- const res = await fetchFn(opts.url, {
353
+ const res = await fetchFn(await getUrl(opts.url), {
320
354
  method: 'POST',
321
355
  headers: {
322
356
  'content-type': 'application/json',
@@ -330,7 +364,7 @@ function serverAudits(opts) {
330
364
  (0, utils_1.assert)('Status code', res.status).toBe(400);
331
365
  })),
332
366
  ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {variables} parameter when accepting application/json`, async () => {
333
- const res = await fetchFn(opts.url, {
367
+ const res = await fetchFn(await getUrl(opts.url), {
334
368
  method: 'POST',
335
369
  headers: {
336
370
  'content-type': 'application/json',
@@ -347,7 +381,7 @@ function serverAudits(opts) {
347
381
  (0, utils_1.audit)(
348
382
  // TODO: convert to MUST after watershed
349
383
  'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
350
- const res = await fetchFn(opts.url, {
384
+ const res = await fetchFn(await getUrl(opts.url), {
351
385
  method: 'POST',
352
386
  headers: {
353
387
  'content-type': 'application/json',
@@ -361,7 +395,7 @@ function serverAudits(opts) {
361
395
  (0, utils_1.assert)('Status code', res.status).toBe(200);
362
396
  }),
363
397
  (0, utils_1.audit)('MUST allow map {variables} parameter when accepting application/json', async () => {
364
- const res = await fetchFn(opts.url, {
398
+ const res = await fetchFn(await getUrl(opts.url), {
365
399
  method: 'POST',
366
400
  headers: {
367
401
  'content-type': 'application/json',
@@ -376,7 +410,7 @@ function serverAudits(opts) {
376
410
  (0, utils_1.assert)('Execution result', await (0, utils_1.assertBodyAsExecutionResult)(res)).notToHaveProperty('errors');
377
411
  }),
378
412
  (0, utils_1.audit)('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
379
- const url = new URL(opts.url);
413
+ const url = new URL(await getUrl(opts.url));
380
414
  url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
381
415
  url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
382
416
  const res = await fetchFn(url.toString(), {
@@ -388,7 +422,7 @@ function serverAudits(opts) {
388
422
  (0, utils_1.assert)('Status code', res.status).toBe(200);
389
423
  }),
390
424
  (0, utils_1.audit)('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
391
- const url = new URL(opts.url);
425
+ const url = new URL(await getUrl(opts.url));
392
426
  url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
393
427
  url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
394
428
  const res = await fetchFn(url.toString(), {
@@ -403,7 +437,7 @@ function serverAudits(opts) {
403
437
  ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(
404
438
  // TODO: convert to MUST after watershed
405
439
  `SHOULD use 400 status code on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
406
- const res = await fetchFn(opts.url, {
440
+ const res = await fetchFn(await getUrl(opts.url), {
407
441
  method: 'POST',
408
442
  headers: {
409
443
  'content-type': 'application/json',
@@ -417,7 +451,7 @@ function serverAudits(opts) {
417
451
  (0, utils_1.assert)('Status code', res.status).toBe(400);
418
452
  })),
419
453
  ...['string', 0, false, ['array']].map((invalid) => (0, utils_1.audit)(`SHOULD use 200 status code with errors field on ${(0, utils_1.extendedTypeof)(invalid)} {extensions} parameter when accepting application/json`, async () => {
420
- const res = await fetchFn(opts.url, {
454
+ const res = await fetchFn(await getUrl(opts.url), {
421
455
  method: 'POST',
422
456
  headers: {
423
457
  'content-type': 'application/json',
@@ -434,7 +468,7 @@ function serverAudits(opts) {
434
468
  (0, utils_1.audit)(
435
469
  // TODO: convert to MUST after watershed
436
470
  'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
437
- const res = await fetchFn(opts.url, {
471
+ const res = await fetchFn(await getUrl(opts.url), {
438
472
  method: 'POST',
439
473
  headers: {
440
474
  'content-type': 'application/json',
@@ -448,7 +482,7 @@ function serverAudits(opts) {
448
482
  (0, utils_1.assert)('Status code', res.status).toBe(200);
449
483
  }),
450
484
  (0, utils_1.audit)('MUST allow map {extensions} parameter when accepting application/json', async () => {
451
- const res = await fetchFn(opts.url, {
485
+ const res = await fetchFn(await getUrl(opts.url), {
452
486
  method: 'POST',
453
487
  headers: {
454
488
  'content-type': 'application/json',
@@ -465,7 +499,7 @@ function serverAudits(opts) {
465
499
  // TODO: audit('MUST accept a map for the {extensions} parameter'),
466
500
  // Response application/json
467
501
  (0, utils_1.audit)('SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
468
- const res = await fetchFn(opts.url, {
502
+ const res = await fetchFn(await getUrl(opts.url), {
469
503
  method: 'POST',
470
504
  headers: {
471
505
  'content-type': 'application/json',
@@ -476,7 +510,7 @@ function serverAudits(opts) {
476
510
  (0, utils_1.assert)('Status code', res.status).toBe(200);
477
511
  }),
478
512
  (0, utils_1.audit)('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
479
- const res = await fetchFn(opts.url, {
513
+ const res = await fetchFn(await getUrl(opts.url), {
480
514
  method: 'POST',
481
515
  headers: {
482
516
  'content-type': 'application/json',
@@ -489,7 +523,7 @@ function serverAudits(opts) {
489
523
  (0, utils_1.assert)('Status code', res.status).toBe(200);
490
524
  }),
491
525
  (0, utils_1.audit)('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
492
- const res = await fetchFn(opts.url, {
526
+ const res = await fetchFn(await getUrl(opts.url), {
493
527
  method: 'POST',
494
528
  headers: {
495
529
  'content-type': 'application/json',
@@ -500,7 +534,7 @@ function serverAudits(opts) {
500
534
  (0, utils_1.assert)('Status code', res.status).toBe(200);
501
535
  }),
502
536
  (0, utils_1.audit)('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
503
- const res = await fetchFn(opts.url, {
537
+ const res = await fetchFn(await getUrl(opts.url), {
504
538
  method: 'POST',
505
539
  headers: {
506
540
  'content-type': 'application/json',
@@ -516,7 +550,7 @@ function serverAudits(opts) {
516
550
  (0, utils_1.audit)(
517
551
  // TODO: convert to MUST after watershed
518
552
  'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
519
- const res = await fetchFn(opts.url, {
553
+ const res = await fetchFn(await getUrl(opts.url), {
520
554
  method: 'POST',
521
555
  headers: {
522
556
  'content-type': 'application/json',
@@ -527,7 +561,7 @@ function serverAudits(opts) {
527
561
  (0, utils_1.assert)('Status code', res.status).toBeBetween(400, 499);
528
562
  }),
529
563
  (0, utils_1.audit)('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
530
- const res = await fetchFn(opts.url, {
564
+ const res = await fetchFn(await getUrl(opts.url), {
531
565
  method: 'POST',
532
566
  headers: {
533
567
  'content-type': 'application/json',
@@ -538,7 +572,7 @@ function serverAudits(opts) {
538
572
  (0, utils_1.assert)('Status code', res.status).toBe(400);
539
573
  }),
540
574
  (0, utils_1.audit)('SHOULD not contain the data entry on JSON parsing failure when accepting application/graphql-response+json', async () => {
541
- const res = await fetchFn(opts.url, {
575
+ const res = await fetchFn(await getUrl(opts.url), {
542
576
  method: 'POST',
543
577
  headers: {
544
578
  'content-type': 'application/json',
@@ -551,7 +585,7 @@ function serverAudits(opts) {
551
585
  (0, utils_1.audit)(
552
586
  // TODO: convert to MUST after watershed
553
587
  'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
554
- const res = await fetchFn(opts.url, {
588
+ const res = await fetchFn(await getUrl(opts.url), {
555
589
  method: 'POST',
556
590
  headers: {
557
591
  'content-type': 'application/json',
@@ -564,7 +598,7 @@ function serverAudits(opts) {
564
598
  (0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
565
599
  }),
566
600
  (0, utils_1.audit)('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
567
- const res = await fetchFn(opts.url, {
601
+ const res = await fetchFn(await getUrl(opts.url), {
568
602
  method: 'POST',
569
603
  headers: {
570
604
  'content-type': 'application/json',
@@ -577,7 +611,7 @@ function serverAudits(opts) {
577
611
  (0, utils_1.assert)('Status code', res.status).toBe(400);
578
612
  }),
579
613
  (0, utils_1.audit)('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
580
- const res = await fetchFn(opts.url, {
614
+ const res = await fetchFn(await getUrl(opts.url), {
581
615
  method: 'POST',
582
616
  headers: {
583
617
  'content-type': 'application/json',
@@ -592,7 +626,7 @@ function serverAudits(opts) {
592
626
  (0, utils_1.audit)(
593
627
  // TODO: convert to MUST after watershed
594
628
  'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
595
- const res = await fetchFn(opts.url, {
629
+ const res = await fetchFn(await getUrl(opts.url), {
596
630
  method: 'POST',
597
631
  headers: {
598
632
  'content-type': 'application/json',
@@ -605,7 +639,7 @@ function serverAudits(opts) {
605
639
  (0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
606
640
  }),
607
641
  (0, utils_1.audit)('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
608
- const res = await fetchFn(opts.url, {
642
+ const res = await fetchFn(await getUrl(opts.url), {
609
643
  method: 'POST',
610
644
  headers: {
611
645
  'content-type': 'application/json',
@@ -618,7 +652,7 @@ function serverAudits(opts) {
618
652
  (0, utils_1.assert)('Status code', res.status).toBe(400);
619
653
  }),
620
654
  (0, utils_1.audit)('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
621
- const res = await fetchFn(opts.url, {
655
+ const res = await fetchFn(await getUrl(opts.url), {
622
656
  method: 'POST',
623
657
  headers: {
624
658
  'content-type': 'application/json',
@@ -633,7 +667,7 @@ function serverAudits(opts) {
633
667
  (0, utils_1.audit)(
634
668
  // TODO: convert to MUST after watershed
635
669
  'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
636
- const res = await fetchFn(opts.url, {
670
+ const res = await fetchFn(await getUrl(opts.url), {
637
671
  method: 'POST',
638
672
  headers: {
639
673
  'content-type': 'application/json',
@@ -646,7 +680,7 @@ function serverAudits(opts) {
646
680
  (0, utils_1.assert)('Status code', res.status).toBeBetween(400, 599);
647
681
  }),
648
682
  (0, utils_1.audit)('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
649
- const res = await fetchFn(opts.url, {
683
+ const res = await fetchFn(await getUrl(opts.url), {
650
684
  method: 'POST',
651
685
  headers: {
652
686
  'content-type': 'application/json',
@@ -659,7 +693,7 @@ function serverAudits(opts) {
659
693
  (0, utils_1.assert)('Status code', res.status).toBe(400);
660
694
  }),
661
695
  (0, utils_1.audit)('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
662
- const res = await fetchFn(opts.url, {
696
+ const res = await fetchFn(await getUrl(opts.url), {
663
697
  method: 'POST',
664
698
  headers: {
665
699
  'content-type': 'application/json',
@@ -692,3 +726,10 @@ async function auditServer(opts) {
692
726
  return await Promise.all(audits.map(({ fn }) => fn()));
693
727
  }
694
728
  exports.auditServer = auditServer;
729
+ /** @private */
730
+ async function getUrl(url) {
731
+ if (typeof url === 'function') {
732
+ return await url();
733
+ }
734
+ return url;
735
+ }
@@ -16,7 +16,7 @@ export function serverAudits(opts) {
16
16
  audit(
17
17
  // TODO: convert to MUST after watershed
18
18
  'SHOULD accept application/graphql-response+json and match the content-type', async () => {
19
- const res = await fetchFn(opts.url, {
19
+ const res = await fetchFn(await getUrl(opts.url), {
20
20
  method: 'POST',
21
21
  headers: {
22
22
  'content-type': 'application/json',
@@ -28,7 +28,7 @@ export function serverAudits(opts) {
28
28
  assert('Content-Type header', res.headers.get('content-type')).toContain('application/graphql-response+json');
29
29
  }),
30
30
  audit('MUST accept application/json and match the content-type', async () => {
31
- const res = await fetchFn(opts.url, {
31
+ const res = await fetchFn(await getUrl(opts.url), {
32
32
  method: 'POST',
33
33
  headers: {
34
34
  'content-type': 'application/json',
@@ -40,7 +40,7 @@ export function serverAudits(opts) {
40
40
  assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
41
41
  }),
42
42
  audit('SHOULD accept */* and use application/json for the content-type', async () => {
43
- const res = await fetchFn(opts.url, {
43
+ const res = await fetchFn(await getUrl(opts.url), {
44
44
  method: 'POST',
45
45
  headers: {
46
46
  'content-type': 'application/json',
@@ -52,14 +52,14 @@ export function serverAudits(opts) {
52
52
  assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
53
53
  }),
54
54
  audit('SHOULD assume application/json content-type when accept is missing', async () => {
55
- const url = new URL(opts.url);
55
+ const url = new URL(await getUrl(opts.url));
56
56
  url.searchParams.set('query', '{ __typename }');
57
57
  const res = await fetchFn(url.toString());
58
58
  assert('Status code', res.status).toBe(200);
59
59
  assert('Content-Type header', res.headers.get('content-type')).toContain('application/json');
60
60
  }),
61
61
  audit('MUST use utf-8 encoding when responding', async () => {
62
- const res = await fetchFn(opts.url, {
62
+ const res = await fetchFn(await getUrl(opts.url), {
63
63
  method: 'POST',
64
64
  headers: {
65
65
  'content-type': 'application/json',
@@ -88,7 +88,7 @@ export function serverAudits(opts) {
88
88
  }
89
89
  }),
90
90
  audit('MUST accept utf-8 encoding', async () => {
91
- const res = await fetchFn(opts.url, {
91
+ const res = await fetchFn(await getUrl(opts.url), {
92
92
  method: 'POST',
93
93
  headers: {
94
94
  'content-type': 'application/json; charset=utf-8',
@@ -99,7 +99,7 @@ export function serverAudits(opts) {
99
99
  assert('Content-Type header', res.headers.get('content-type')).toContain('utf-8');
100
100
  }),
101
101
  audit('MUST assume utf-8 if encoding is unspecified', async () => {
102
- const res = await fetchFn(opts.url, {
102
+ const res = await fetchFn(await getUrl(opts.url), {
103
103
  method: 'POST',
104
104
  headers: {
105
105
  'content-type': 'application/json',
@@ -111,7 +111,7 @@ export function serverAudits(opts) {
111
111
  }),
112
112
  // Request
113
113
  audit('MUST accept POST requests', async () => {
114
- const res = await fetchFn(opts.url, {
114
+ const res = await fetchFn(await getUrl(opts.url), {
115
115
  method: 'POST',
116
116
  headers: { 'content-type': 'application/json' },
117
117
  body: JSON.stringify({ query: '{ __typename }' }),
@@ -119,7 +119,7 @@ export function serverAudits(opts) {
119
119
  assert('Status code', res.status).toBe(200);
120
120
  }),
121
121
  audit('MAY accept application/x-www-form-urlencoded formatted GET requests', async () => {
122
- const url = new URL(opts.url);
122
+ const url = new URL(await getUrl(opts.url));
123
123
  url.searchParams.set('query', '{ __typename }');
124
124
  const res = await fetchFn(url.toString());
125
125
  assert('Status code', res.status).toBe(200);
@@ -127,7 +127,7 @@ export function serverAudits(opts) {
127
127
  // Request GET
128
128
  // TODO: this is a MUST if the server supports GET requests
129
129
  audit('MAY NOT allow executing mutations on GET requests', async () => {
130
- const url = new URL(opts.url);
130
+ const url = new URL(await getUrl(opts.url));
131
131
  url.searchParams.set('query', 'mutation { __typename }');
132
132
  const res = await fetchFn(url.toString(), {
133
133
  headers: {
@@ -138,13 +138,13 @@ export function serverAudits(opts) {
138
138
  }),
139
139
  // Request POST
140
140
  audit('SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
141
- const res = await fetchFn(opts.url, {
141
+ const res = await fetchFn(await getUrl(opts.url), {
142
142
  method: 'POST',
143
143
  });
144
144
  assert('Status code', res.status).toBeBetween(400, 499);
145
145
  }),
146
146
  audit('MUST accept application/json POST requests', async () => {
147
- const res = await fetchFn(opts.url, {
147
+ const res = await fetchFn(await getUrl(opts.url), {
148
148
  method: 'POST',
149
149
  headers: { 'content-type': 'application/json' },
150
150
  body: JSON.stringify({ query: '{ __typename }' }),
@@ -153,7 +153,7 @@ export function serverAudits(opts) {
153
153
  }),
154
154
  audit('MUST require a request body on POST', async () => {
155
155
  var _a;
156
- const res = await fetchFn(opts.url, {
156
+ const res = await fetchFn(await getUrl(opts.url), {
157
157
  method: 'POST',
158
158
  headers: { 'content-type': 'application/json' },
159
159
  });
@@ -168,7 +168,7 @@ export function serverAudits(opts) {
168
168
  audit(
169
169
  // TODO: convert to MUST after watershed
170
170
  'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
171
- const res = await fetchFn(opts.url, {
171
+ const res = await fetchFn(await getUrl(opts.url), {
172
172
  method: 'POST',
173
173
  headers: {
174
174
  'content-type': 'application/json',
@@ -179,7 +179,7 @@ export function serverAudits(opts) {
179
179
  assert('Status code', res.status).toBe(400);
180
180
  }),
181
181
  audit('SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
182
- const res = await fetchFn(opts.url, {
182
+ const res = await fetchFn(await getUrl(opts.url), {
183
183
  method: 'POST',
184
184
  headers: {
185
185
  'content-type': 'application/json',
@@ -193,7 +193,7 @@ export function serverAudits(opts) {
193
193
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
194
194
  // TODO: convert to MUST after watershed
195
195
  `SHOULD use 400 status code on ${extendedTypeof(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
196
- const res = await fetchFn(opts.url, {
196
+ const res = await fetchFn(await getUrl(opts.url), {
197
197
  method: 'POST',
198
198
  headers: {
199
199
  'content-type': 'application/json',
@@ -206,7 +206,7 @@ export function serverAudits(opts) {
206
206
  assert('Status code', res.status).toBe(400);
207
207
  })),
208
208
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {query} parameter when accepting application/json`, async () => {
209
- const res = await fetchFn(opts.url, {
209
+ const res = await fetchFn(await getUrl(opts.url), {
210
210
  method: 'POST',
211
211
  headers: {
212
212
  'content-type': 'application/json',
@@ -222,7 +222,7 @@ export function serverAudits(opts) {
222
222
  audit(
223
223
  // TODO: convert to MUST after watershed
224
224
  'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
225
- const res = await fetchFn(opts.url, {
225
+ const res = await fetchFn(await getUrl(opts.url), {
226
226
  method: 'POST',
227
227
  headers: {
228
228
  'content-type': 'application/json',
@@ -235,7 +235,7 @@ export function serverAudits(opts) {
235
235
  assert('Status code', res.status).toBe(200);
236
236
  }),
237
237
  audit('MUST allow string {query} parameter when accepting application/json', async () => {
238
- const res = await fetchFn(opts.url, {
238
+ const res = await fetchFn(await getUrl(opts.url), {
239
239
  method: 'POST',
240
240
  headers: {
241
241
  'content-type': 'application/json',
@@ -251,7 +251,7 @@ export function serverAudits(opts) {
251
251
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(
252
252
  // TODO: convert to MUST after watershed
253
253
  `SHOULD use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
254
- const res = await fetchFn(opts.url, {
254
+ const res = await fetchFn(await getUrl(opts.url), {
255
255
  method: 'POST',
256
256
  headers: {
257
257
  'content-type': 'application/json',
@@ -265,7 +265,7 @@ export function serverAudits(opts) {
265
265
  assert('Status code', res.status).toBe(400);
266
266
  })),
267
267
  ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/json`, async () => {
268
- const res = await fetchFn(opts.url, {
268
+ const res = await fetchFn(await getUrl(opts.url), {
269
269
  method: 'POST',
270
270
  headers: {
271
271
  'content-type': 'application/json',
@@ -282,7 +282,7 @@ export function serverAudits(opts) {
282
282
  audit(
283
283
  // TODO: convert to MUST after watershed
284
284
  'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
285
- const res = await fetchFn(opts.url, {
285
+ const res = await fetchFn(await getUrl(opts.url), {
286
286
  method: 'POST',
287
287
  headers: {
288
288
  'content-type': 'application/json',
@@ -296,7 +296,7 @@ export function serverAudits(opts) {
296
296
  assert('Status code', res.status).toBe(200);
297
297
  }),
298
298
  audit('MUST allow string {operationName} parameter when accepting application/json', async () => {
299
- const res = await fetchFn(opts.url, {
299
+ const res = await fetchFn(await getUrl(opts.url), {
300
300
  method: 'POST',
301
301
  headers: {
302
302
  'content-type': 'application/json',
@@ -310,10 +310,44 @@ export function serverAudits(opts) {
310
310
  assert('Status code', res.status).toBe(200);
311
311
  assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
312
312
  }),
313
+ ...['variables', 'operationName', 'extensions'].flatMap((parameter) => [
314
+ audit(
315
+ // TODO: convert to MUST after watershed
316
+ `SHOULD allow null {${parameter}} parameter when accepting application/graphql-response+json`, async () => {
317
+ const res = await fetchFn(await getUrl(opts.url), {
318
+ method: 'POST',
319
+ headers: {
320
+ 'content-type': 'application/json',
321
+ accept: 'application/graphql-response+json',
322
+ },
323
+ body: JSON.stringify({
324
+ query: '{ __typename }',
325
+ [parameter]: null,
326
+ }),
327
+ });
328
+ assert('Status code', res.status).toBe(200);
329
+ assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
330
+ }),
331
+ audit(`MUST allow null {${parameter}} parameter when accepting application/json`, async () => {
332
+ const res = await fetchFn(await getUrl(opts.url), {
333
+ method: 'POST',
334
+ headers: {
335
+ 'content-type': 'application/json',
336
+ accept: 'application/json',
337
+ },
338
+ body: JSON.stringify({
339
+ query: '{ __typename }',
340
+ [parameter]: null,
341
+ }),
342
+ });
343
+ assert('Status code', res.status).toBe(200);
344
+ assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
345
+ }),
346
+ ]),
313
347
  ...['string', 0, false, ['array']].map((invalid) => audit(
314
348
  // TODO: convert to MUST after watershed
315
349
  `SHOULD use 400 status code on ${extendedTypeof(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
316
- const res = await fetchFn(opts.url, {
350
+ const res = await fetchFn(await getUrl(opts.url), {
317
351
  method: 'POST',
318
352
  headers: {
319
353
  'content-type': 'application/json',
@@ -327,7 +361,7 @@ export function serverAudits(opts) {
327
361
  assert('Status code', res.status).toBe(400);
328
362
  })),
329
363
  ...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {variables} parameter when accepting application/json`, async () => {
330
- const res = await fetchFn(opts.url, {
364
+ const res = await fetchFn(await getUrl(opts.url), {
331
365
  method: 'POST',
332
366
  headers: {
333
367
  'content-type': 'application/json',
@@ -344,7 +378,7 @@ export function serverAudits(opts) {
344
378
  audit(
345
379
  // TODO: convert to MUST after watershed
346
380
  'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
347
- const res = await fetchFn(opts.url, {
381
+ const res = await fetchFn(await getUrl(opts.url), {
348
382
  method: 'POST',
349
383
  headers: {
350
384
  'content-type': 'application/json',
@@ -358,7 +392,7 @@ export function serverAudits(opts) {
358
392
  assert('Status code', res.status).toBe(200);
359
393
  }),
360
394
  audit('MUST allow map {variables} parameter when accepting application/json', async () => {
361
- const res = await fetchFn(opts.url, {
395
+ const res = await fetchFn(await getUrl(opts.url), {
362
396
  method: 'POST',
363
397
  headers: {
364
398
  'content-type': 'application/json',
@@ -373,7 +407,7 @@ export function serverAudits(opts) {
373
407
  assert('Execution result', await assertBodyAsExecutionResult(res)).notToHaveProperty('errors');
374
408
  }),
375
409
  audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json', async () => {
376
- const url = new URL(opts.url);
410
+ const url = new URL(await getUrl(opts.url));
377
411
  url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
378
412
  url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
379
413
  const res = await fetchFn(url.toString(), {
@@ -385,7 +419,7 @@ export function serverAudits(opts) {
385
419
  assert('Status code', res.status).toBe(200);
386
420
  }),
387
421
  audit('MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json', async () => {
388
- const url = new URL(opts.url);
422
+ const url = new URL(await getUrl(opts.url));
389
423
  url.searchParams.set('query', 'query Type($name: String!) { __type(name: $name) { name } }');
390
424
  url.searchParams.set('variables', JSON.stringify({ name: 'sometype' }));
391
425
  const res = await fetchFn(url.toString(), {
@@ -400,7 +434,7 @@ export function serverAudits(opts) {
400
434
  ...['string', 0, false, ['array']].map((invalid) => audit(
401
435
  // TODO: convert to MUST after watershed
402
436
  `SHOULD use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
403
- const res = await fetchFn(opts.url, {
437
+ const res = await fetchFn(await getUrl(opts.url), {
404
438
  method: 'POST',
405
439
  headers: {
406
440
  'content-type': 'application/json',
@@ -414,7 +448,7 @@ export function serverAudits(opts) {
414
448
  assert('Status code', res.status).toBe(400);
415
449
  })),
416
450
  ...['string', 0, false, ['array']].map((invalid) => audit(`SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/json`, async () => {
417
- const res = await fetchFn(opts.url, {
451
+ const res = await fetchFn(await getUrl(opts.url), {
418
452
  method: 'POST',
419
453
  headers: {
420
454
  'content-type': 'application/json',
@@ -431,7 +465,7 @@ export function serverAudits(opts) {
431
465
  audit(
432
466
  // TODO: convert to MUST after watershed
433
467
  'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
434
- const res = await fetchFn(opts.url, {
468
+ const res = await fetchFn(await getUrl(opts.url), {
435
469
  method: 'POST',
436
470
  headers: {
437
471
  'content-type': 'application/json',
@@ -445,7 +479,7 @@ export function serverAudits(opts) {
445
479
  assert('Status code', res.status).toBe(200);
446
480
  }),
447
481
  audit('MUST allow map {extensions} parameter when accepting application/json', async () => {
448
- const res = await fetchFn(opts.url, {
482
+ const res = await fetchFn(await getUrl(opts.url), {
449
483
  method: 'POST',
450
484
  headers: {
451
485
  'content-type': 'application/json',
@@ -462,7 +496,7 @@ export function serverAudits(opts) {
462
496
  // TODO: audit('MUST accept a map for the {extensions} parameter'),
463
497
  // Response application/json
464
498
  audit('SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
465
- const res = await fetchFn(opts.url, {
499
+ const res = await fetchFn(await getUrl(opts.url), {
466
500
  method: 'POST',
467
501
  headers: {
468
502
  'content-type': 'application/json',
@@ -473,7 +507,7 @@ export function serverAudits(opts) {
473
507
  assert('Status code', res.status).toBe(200);
474
508
  }),
475
509
  audit('SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
476
- const res = await fetchFn(opts.url, {
510
+ const res = await fetchFn(await getUrl(opts.url), {
477
511
  method: 'POST',
478
512
  headers: {
479
513
  'content-type': 'application/json',
@@ -486,7 +520,7 @@ export function serverAudits(opts) {
486
520
  assert('Status code', res.status).toBe(200);
487
521
  }),
488
522
  audit('SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
489
- const res = await fetchFn(opts.url, {
523
+ const res = await fetchFn(await getUrl(opts.url), {
490
524
  method: 'POST',
491
525
  headers: {
492
526
  'content-type': 'application/json',
@@ -497,7 +531,7 @@ export function serverAudits(opts) {
497
531
  assert('Status code', res.status).toBe(200);
498
532
  }),
499
533
  audit('SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
500
- const res = await fetchFn(opts.url, {
534
+ const res = await fetchFn(await getUrl(opts.url), {
501
535
  method: 'POST',
502
536
  headers: {
503
537
  'content-type': 'application/json',
@@ -513,7 +547,7 @@ export function serverAudits(opts) {
513
547
  audit(
514
548
  // TODO: convert to MUST after watershed
515
549
  'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
516
- const res = await fetchFn(opts.url, {
550
+ const res = await fetchFn(await getUrl(opts.url), {
517
551
  method: 'POST',
518
552
  headers: {
519
553
  'content-type': 'application/json',
@@ -524,7 +558,7 @@ export function serverAudits(opts) {
524
558
  assert('Status code', res.status).toBeBetween(400, 499);
525
559
  }),
526
560
  audit('SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
527
- const res = await fetchFn(opts.url, {
561
+ const res = await fetchFn(await getUrl(opts.url), {
528
562
  method: 'POST',
529
563
  headers: {
530
564
  'content-type': 'application/json',
@@ -535,7 +569,7 @@ export function serverAudits(opts) {
535
569
  assert('Status code', res.status).toBe(400);
536
570
  }),
537
571
  audit('SHOULD not contain the data entry on JSON parsing failure when accepting application/graphql-response+json', async () => {
538
- const res = await fetchFn(opts.url, {
572
+ const res = await fetchFn(await getUrl(opts.url), {
539
573
  method: 'POST',
540
574
  headers: {
541
575
  'content-type': 'application/json',
@@ -548,7 +582,7 @@ export function serverAudits(opts) {
548
582
  audit(
549
583
  // TODO: convert to MUST after watershed
550
584
  'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
551
- const res = await fetchFn(opts.url, {
585
+ const res = await fetchFn(await getUrl(opts.url), {
552
586
  method: 'POST',
553
587
  headers: {
554
588
  'content-type': 'application/json',
@@ -561,7 +595,7 @@ export function serverAudits(opts) {
561
595
  assert('Status code', res.status).toBeBetween(400, 599);
562
596
  }),
563
597
  audit('SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
564
- const res = await fetchFn(opts.url, {
598
+ const res = await fetchFn(await getUrl(opts.url), {
565
599
  method: 'POST',
566
600
  headers: {
567
601
  'content-type': 'application/json',
@@ -574,7 +608,7 @@ export function serverAudits(opts) {
574
608
  assert('Status code', res.status).toBe(400);
575
609
  }),
576
610
  audit('SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
577
- const res = await fetchFn(opts.url, {
611
+ const res = await fetchFn(await getUrl(opts.url), {
578
612
  method: 'POST',
579
613
  headers: {
580
614
  'content-type': 'application/json',
@@ -589,7 +623,7 @@ export function serverAudits(opts) {
589
623
  audit(
590
624
  // TODO: convert to MUST after watershed
591
625
  'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
592
- const res = await fetchFn(opts.url, {
626
+ const res = await fetchFn(await getUrl(opts.url), {
593
627
  method: 'POST',
594
628
  headers: {
595
629
  'content-type': 'application/json',
@@ -602,7 +636,7 @@ export function serverAudits(opts) {
602
636
  assert('Status code', res.status).toBeBetween(400, 599);
603
637
  }),
604
638
  audit('SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json', async () => {
605
- const res = await fetchFn(opts.url, {
639
+ const res = await fetchFn(await getUrl(opts.url), {
606
640
  method: 'POST',
607
641
  headers: {
608
642
  'content-type': 'application/json',
@@ -615,7 +649,7 @@ export function serverAudits(opts) {
615
649
  assert('Status code', res.status).toBe(400);
616
650
  }),
617
651
  audit('SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json', async () => {
618
- const res = await fetchFn(opts.url, {
652
+ const res = await fetchFn(await getUrl(opts.url), {
619
653
  method: 'POST',
620
654
  headers: {
621
655
  'content-type': 'application/json',
@@ -630,7 +664,7 @@ export function serverAudits(opts) {
630
664
  audit(
631
665
  // TODO: convert to MUST after watershed
632
666
  'SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json', async () => {
633
- const res = await fetchFn(opts.url, {
667
+ const res = await fetchFn(await getUrl(opts.url), {
634
668
  method: 'POST',
635
669
  headers: {
636
670
  'content-type': 'application/json',
@@ -643,7 +677,7 @@ export function serverAudits(opts) {
643
677
  assert('Status code', res.status).toBeBetween(400, 599);
644
678
  }),
645
679
  audit('SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json', async () => {
646
- const res = await fetchFn(opts.url, {
680
+ const res = await fetchFn(await getUrl(opts.url), {
647
681
  method: 'POST',
648
682
  headers: {
649
683
  'content-type': 'application/json',
@@ -656,7 +690,7 @@ export function serverAudits(opts) {
656
690
  assert('Status code', res.status).toBe(400);
657
691
  }),
658
692
  audit('SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json', async () => {
659
- const res = await fetchFn(opts.url, {
693
+ const res = await fetchFn(await getUrl(opts.url), {
660
694
  method: 'POST',
661
695
  headers: {
662
696
  'content-type': 'application/json',
@@ -687,3 +721,10 @@ export async function auditServer(opts) {
687
721
  // audit tests will throw only on fatal errors, tests are contained within the AuditResult
688
722
  return await Promise.all(audits.map(({ fn }) => fn()));
689
723
  }
724
+ /** @private */
725
+ async function getUrl(url) {
726
+ if (typeof url === 'function') {
727
+ return await url();
728
+ }
729
+ return url;
730
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-http",
3
- "version": "1.7.2",
3
+ "version": "1.9.0",
4
4
  "description": "Simple, pluggable, zero-dependency, GraphQL over HTTP Protocol compliant server and client",
5
5
  "keywords": [
6
6
  "graphql",
Binary file