graphql-http 1.17.1 → 1.18.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.
@@ -52,10 +52,13 @@
52
52
  id,
53
53
  name,
54
54
  status: name.startsWith('MUST')
55
- ? // only failing MUSTs are considered errors
55
+ ? // failing MUSTs are considered errors
56
56
  'error'
57
- : // everything else is optional and considered a warning
58
- 'warn',
57
+ : name.startsWith('SHOULD')
58
+ ? // recommendations are warnings
59
+ 'warn'
60
+ : // everything else is truly optional
61
+ 'notice',
59
62
  reason: err.reason,
60
63
  response: err.response,
61
64
  };
@@ -216,9 +219,13 @@
216
219
  ressert(res).header('content-type').toContain('application/json');
217
220
  }),
218
221
  audit('80D8', 'SHOULD assume application/json content-type when accept is missing', async () => {
219
- const url = new URL(await getUrl(opts.url));
220
- url.searchParams.set('query', '{ __typename }');
221
- const res = await fetchFn(url.toString());
222
+ const res = await fetchFn(await getUrl(opts.url), {
223
+ method: 'POST',
224
+ headers: {
225
+ 'content-type': 'application/json',
226
+ },
227
+ body: JSON.stringify({ query: '{ __typename }' }),
228
+ });
222
229
  ressert(res).status.toBe(200);
223
230
  ressert(res).header('content-type').toContain('application/json');
224
231
  }),
@@ -289,7 +296,7 @@
289
296
  ressert(res).status.toBeBetween(400, 499);
290
297
  }),
291
298
  // Request POST
292
- audit('9ABE', 'SHOULD respond with 4xx status code if content-type is not supplied on POST requests', async () => {
299
+ audit('9ABE', 'MAY respond with 4xx status code if content-type is not supplied on POST requests', async () => {
293
300
  const res = await fetchFn(await getUrl(opts.url), {
294
301
  method: 'POST',
295
302
  });
@@ -303,23 +310,15 @@
303
310
  });
304
311
  ressert(res).status.toBe(200);
305
312
  }),
306
- audit('7267', 'MUST require a request body on POST', async () => {
307
- var _a;
313
+ audit('A5BF', 'MAY use 400 status code when request body is missing on POST', async () => {
308
314
  const res = await fetchFn(await getUrl(opts.url), {
309
315
  method: 'POST',
310
316
  headers: { 'content-type': 'application/json' },
311
317
  });
312
- if ((_a = res.headers.get('content-type')) === null || _a === void 0 ? void 0 : _a.includes('application/json')) {
313
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
314
- }
315
- else {
316
- ressert(res).status.toBe(400);
317
- }
318
+ ressert(res).status.toBe(400);
318
319
  }),
319
320
  // Request Parameters
320
- audit(
321
- // TODO: convert to MUST after watershed
322
- '6610', 'SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json', async () => {
321
+ audit('423L', 'MAY use 400 status code on missing {query} parameter', async () => {
323
322
  const res = await fetchFn(await getUrl(opts.url), {
324
323
  method: 'POST',
325
324
  headers: {
@@ -330,26 +329,11 @@
330
329
  });
331
330
  ressert(res).status.toBe(400);
332
331
  }),
333
- audit('3715', 'SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json', async () => {
334
- const res = await fetchFn(await getUrl(opts.url), {
335
- method: 'POST',
336
- headers: {
337
- 'content-type': 'application/json',
338
- accept: 'application/json',
339
- },
340
- body: JSON.stringify({ notquery: '{ __typename }' }),
341
- });
342
- ressert(res).status.toBe(200);
343
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
344
- }),
345
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`4F5${index}`,
346
- // TODO: convert to MUST after watershed
347
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {query} parameter when accepting application/graphql-response+json`, async () => {
332
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`LKJ${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {query} parameter`, async () => {
348
333
  const res = await fetchFn(await getUrl(opts.url), {
349
334
  method: 'POST',
350
335
  headers: {
351
336
  'content-type': 'application/json',
352
- accept: 'application/graphql-response+json',
353
337
  },
354
338
  body: JSON.stringify({
355
339
  query: invalid,
@@ -357,20 +341,6 @@
357
341
  });
358
342
  ressert(res).status.toBe(400);
359
343
  })),
360
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`9FE${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {query} parameter when accepting application/json`, async () => {
361
- const res = await fetchFn(await getUrl(opts.url), {
362
- method: 'POST',
363
- headers: {
364
- 'content-type': 'application/json',
365
- accept: 'application/json',
366
- },
367
- body: JSON.stringify({
368
- query: invalid,
369
- }),
370
- });
371
- ressert(res).status.toBe(200);
372
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
373
- })),
374
344
  audit(
375
345
  // TODO: convert to MUST after watershed
376
346
  '34A2', 'SHOULD allow string {query} parameter when accepting application/graphql-response+json', async () => {
@@ -400,14 +370,11 @@
400
370
  ressert(res).status.toBe(200);
401
371
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
402
372
  }),
403
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`E3E${index}`,
404
- // TODO: convert to MUST after watershed
405
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/graphql-response+json`, async () => {
373
+ ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`6C0${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {operationName} parameter`, async () => {
406
374
  const res = await fetchFn(await getUrl(opts.url), {
407
375
  method: 'POST',
408
376
  headers: {
409
377
  'content-type': 'application/json',
410
- accept: 'application/graphql-response+json',
411
378
  },
412
379
  body: JSON.stringify({
413
380
  operationName: invalid,
@@ -416,21 +383,6 @@
416
383
  });
417
384
  ressert(res).status.toBe(400);
418
385
  })),
419
- ...[{ obj: 'ect' }, 0, false, ['array']].map((invalid, index) => audit(`FB9${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {operationName} parameter when accepting application/json`, async () => {
420
- const res = await fetchFn(await getUrl(opts.url), {
421
- method: 'POST',
422
- headers: {
423
- 'content-type': 'application/json',
424
- accept: 'application/json',
425
- },
426
- body: JSON.stringify({
427
- operationName: invalid,
428
- query: '{ __typename }',
429
- }),
430
- });
431
- ressert(res).status.toBe(200);
432
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
433
- })),
434
386
  audit(
435
387
  // TODO: convert to MUST after watershed
436
388
  '8161', 'SHOULD allow string {operationName} parameter when accepting application/graphql-response+json', async () => {
@@ -496,14 +448,11 @@
496
448
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
497
449
  }),
498
450
  ]),
499
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`69B${index}`,
500
- // TODO: convert to MUST after watershed
501
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {variables} parameter when accepting application/graphql-response+json`, async () => {
451
+ ...['string', 0, false, ['array']].map((invalid, index) => audit(`476${index}`, `MAY use 400 status code on ${extendedTypeof(invalid)} {variables} parameter`, async () => {
502
452
  const res = await fetchFn(await getUrl(opts.url), {
503
453
  method: 'POST',
504
454
  headers: {
505
455
  'content-type': 'application/json',
506
- accept: 'application/graphql-response+json',
507
456
  },
508
457
  body: JSON.stringify({
509
458
  query: '{ __typename }',
@@ -512,21 +461,6 @@
512
461
  });
513
462
  ressert(res).status.toBe(400);
514
463
  })),
515
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`F05${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {variables} parameter when accepting application/json`, async () => {
516
- const res = await fetchFn(await getUrl(opts.url), {
517
- method: 'POST',
518
- headers: {
519
- 'content-type': 'application/json',
520
- accept: 'application/json',
521
- },
522
- body: JSON.stringify({
523
- query: '{ __typename }',
524
- variables: invalid,
525
- }),
526
- });
527
- ressert(res).status.toBe(200);
528
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
529
- })),
530
464
  audit(
531
465
  // TODO: convert to MUST after watershed
532
466
  '2EA1', 'SHOULD allow map {variables} parameter when accepting application/graphql-response+json', async () => {
@@ -583,14 +517,13 @@
583
517
  ressert(res).status.toBe(200);
584
518
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
585
519
  }),
586
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`904${index}`,
520
+ ...['string', 0, false, ['array']].map((invalid, index) => audit(`58B${index}`,
587
521
  // TODO: convert to MUST after watershed
588
- `SHOULD use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/graphql-response+json`, async () => {
522
+ `MAY use 400 status code on ${extendedTypeof(invalid)} {extensions} parameter`, async () => {
589
523
  const res = await fetchFn(await getUrl(opts.url), {
590
524
  method: 'POST',
591
525
  headers: {
592
526
  'content-type': 'application/json',
593
- accept: 'application/graphql-response+json',
594
527
  },
595
528
  body: JSON.stringify({
596
529
  query: '{ __typename }',
@@ -599,21 +532,6 @@
599
532
  });
600
533
  ressert(res).status.toBe(400);
601
534
  })),
602
- ...['string', 0, false, ['array']].map((invalid, index) => audit(`368${index}`, `SHOULD use 200 status code with errors field on ${extendedTypeof(invalid)} {extensions} parameter when accepting application/json`, async () => {
603
- const res = await fetchFn(await getUrl(opts.url), {
604
- method: 'POST',
605
- headers: {
606
- 'content-type': 'application/json',
607
- accept: 'application/json',
608
- },
609
- body: JSON.stringify({
610
- query: '{ __typename }',
611
- extensions: invalid,
612
- }),
613
- });
614
- ressert(res).status.toBe(200);
615
- await ressert(res).bodyAsExecutionResult.toHaveProperty('errors');
616
- })),
617
535
  audit(
618
536
  // TODO: convert to MUST after watershed
619
537
  '428F', 'SHOULD allow map {extensions} parameter when accepting application/graphql-response+json', async () => {
@@ -645,136 +563,91 @@
645
563
  ressert(res).status.toBe(200);
646
564
  await ressert(res).bodyAsExecutionResult.notToHaveProperty('errors');
647
565
  }),
648
- // TODO: audit('39AA', 'MUST accept a map for the {extensions} parameter'),
649
- // Response application/json
650
- audit('D477', 'SHOULD use 200 status code on JSON parsing failure when accepting application/json', async () => {
566
+ audit('B6DC', 'MAY use 4xx or 5xx status codes on JSON parsing failure', async () => {
651
567
  const res = await fetchFn(await getUrl(opts.url), {
652
568
  method: 'POST',
653
569
  headers: {
654
570
  'content-type': 'application/json',
655
- accept: 'application/json',
656
571
  },
657
572
  body: '{ "not a JSON',
658
573
  });
659
- ressert(res).status.toBe(200);
660
- }),
661
- audit('F5AF', 'SHOULD use 200 status code if parameters are invalid when accepting application/json', async () => {
662
- const res = await fetchFn(await getUrl(opts.url), {
663
- method: 'POST',
664
- headers: {
665
- 'content-type': 'application/json',
666
- accept: 'application/json',
667
- },
668
- body: JSON.stringify({
669
- qeury: /* typo */ '{ __typename }',
670
- }),
671
- });
672
- ressert(res).status.toBe(200);
574
+ ressert(res).status.toBeBetween(400, 499);
673
575
  }),
674
- audit('572B', 'SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
576
+ audit('BCF8', 'MAY use 400 status code on JSON parsing failure', async () => {
675
577
  const res = await fetchFn(await getUrl(opts.url), {
676
578
  method: 'POST',
677
579
  headers: {
678
580
  'content-type': 'application/json',
679
- accept: 'application/json',
680
581
  },
681
- body: JSON.stringify({ query: '{' }),
582
+ body: '{ "not a JSON',
682
583
  });
683
- ressert(res).status.toBe(200);
584
+ ressert(res).status.toBe(400);
684
585
  }),
685
- audit('FDE2', 'SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
586
+ audit('8764', 'MAY use 4xx or 5xx status codes if parameters are invalid', async () => {
686
587
  const res = await fetchFn(await getUrl(opts.url), {
687
588
  method: 'POST',
688
589
  headers: {
689
590
  'content-type': 'application/json',
690
- accept: 'application/json',
691
591
  },
692
592
  body: JSON.stringify({
693
- query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
593
+ qeury /* typo */: '{ __typename }',
694
594
  }),
695
595
  });
696
- ressert(res).status.toBe(200);
596
+ ressert(res).status.toBeBetween(400, 599);
697
597
  }),
698
- audit('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
598
+ audit('3E3A', 'MAY use 400 status code if parameters are invalid', async () => {
699
599
  const res = await fetchFn(await getUrl(opts.url), {
700
600
  method: 'POST',
701
601
  headers: {
702
602
  'content-type': 'application/json',
703
- accept: 'application/json',
704
603
  },
705
604
  body: JSON.stringify({
706
- query: 'query CoerceFailure($id: ID!){ __typename }',
707
- variables: { id: null },
605
+ qeury: /* typo */ '{ __typename }',
708
606
  }),
709
607
  });
710
- ressert(res).status.toBe(200);
711
- }),
712
- // Response application/graphql-response+json
713
- audit(
714
- // TODO: convert to MUST after watershed
715
- '60AA', 'SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json', async () => {
716
- const res = await fetchFn(await getUrl(opts.url), {
717
- method: 'POST',
718
- headers: {
719
- 'content-type': 'application/json',
720
- accept: 'application/graphql-response+json',
721
- },
722
- body: '{ "not a JSON',
723
- });
724
- ressert(res).status.toBeBetween(400, 499);
725
- }),
726
- audit('2163', 'SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json', async () => {
727
- const res = await fetchFn(await getUrl(opts.url), {
728
- method: 'POST',
729
- headers: {
730
- 'content-type': 'application/json',
731
- accept: 'application/graphql-response+json',
732
- },
733
- body: '{ "not a JSON',
734
- });
735
608
  ressert(res).status.toBe(400);
736
609
  }),
737
- audit(
738
- // TODO: convert to MUST after watershed
739
- '3E36', 'SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json', async () => {
610
+ // TODO: audit('39AA', 'MUST accept a map for the {extensions} parameter'),
611
+ // Response application/json
612
+ audit('572B', 'SHOULD use 200 status code on document parsing failure when accepting application/json', async () => {
740
613
  const res = await fetchFn(await getUrl(opts.url), {
741
614
  method: 'POST',
742
615
  headers: {
743
616
  'content-type': 'application/json',
744
- accept: 'application/graphql-response+json',
617
+ accept: 'application/json',
745
618
  },
746
- body: JSON.stringify({
747
- qeury /* typo */: '{ __typename }',
748
- }),
619
+ body: JSON.stringify({ query: '{' }),
749
620
  });
750
- ressert(res).status.toBeBetween(400, 599);
621
+ ressert(res).status.toBe(200);
751
622
  }),
752
- audit('17C5', 'SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json', async () => {
623
+ audit('FDE2', 'SHOULD use 200 status code on document validation failure when accepting application/json', async () => {
753
624
  const res = await fetchFn(await getUrl(opts.url), {
754
625
  method: 'POST',
755
626
  headers: {
756
627
  'content-type': 'application/json',
757
- accept: 'application/graphql-response+json',
628
+ accept: 'application/json',
758
629
  },
759
630
  body: JSON.stringify({
760
- qeury: /* typo */ '{ __typename }',
631
+ query: '{ 8f31403dfe404bccbb0e835f2629c6a7 }', // making sure the field doesnt exist
761
632
  }),
762
633
  });
763
- ressert(res).status.toBe(400);
634
+ ressert(res).status.toBe(200);
764
635
  }),
765
- audit('34D6', 'SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json', async () => {
636
+ audit('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
766
637
  const res = await fetchFn(await getUrl(opts.url), {
767
638
  method: 'POST',
768
639
  headers: {
769
640
  'content-type': 'application/json',
770
- accept: 'application/graphql-response+json',
641
+ accept: 'application/json',
771
642
  },
772
643
  body: JSON.stringify({
773
- qeury: /* typo */ '{ __typename }',
644
+ query: 'query CoerceFailure($id: ID!){ __typename }',
645
+ variables: { id: null },
774
646
  }),
775
647
  });
776
- await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
648
+ ressert(res).status.toBe(200);
777
649
  }),
650
+ // Response application/graphql-response+json
778
651
  audit(
779
652
  // TODO: convert to MUST after watershed
780
653
  '865D', 'SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json', async () => {
@@ -908,6 +781,7 @@
908
781
  const grouped = {
909
782
  total: 0,
910
783
  ok: [],
784
+ notice: [],
911
785
  warn: [],
912
786
  error: [],
913
787
  };
@@ -930,6 +804,9 @@
930
804
  if (grouped.ok.length) {
931
805
  report += `<li><span style="font-family: monospace">✅</span> <b>${grouped.ok.length}</b> pass</li>\n`;
932
806
  }
807
+ if (grouped.notice.length) {
808
+ report += `<li><span style="font-family: monospace">💡</span> <b>${grouped.notice.length}</b> notices (suggestions)</li>\n`;
809
+ }
933
810
  if (grouped.warn.length) {
934
811
  // TODO: warning sign is rendered as "⚠️" in markdown instead of the emoji
935
812
  report += `<li><span style="font-family: monospace">⚠️</span> <b>${grouped.warn.length}</b> warnings (optional)</li>\n`;
@@ -948,6 +825,17 @@
948
825
  report += '</ol>\n';
949
826
  report += '\n';
950
827
  }
828
+ if (grouped.notice.length) {
829
+ report += `<h2>Notices</h2>\n`;
830
+ report +=
831
+ 'The server <i>MAY</i> support these, but are truly optional. These are suggestions following recommended conventions.\n';
832
+ report += '<ol>\n';
833
+ for (const [, result] of grouped.notice.entries()) {
834
+ report += await printAuditFail(result);
835
+ }
836
+ report += '</ol>\n';
837
+ report += '\n';
838
+ }
951
839
  if (grouped.warn.length) {
952
840
  report += `<h2>Warnings</h2>\n`;
953
841
  report += 'The server <i>SHOULD</i> support these, but is not required.\n';
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("graphql")):"function"==typeof define&&define.amd?define(["exports","graphql"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlHttpAudits={})}(this,(function(e){"use strict";function t(e){return null===e?"null":Array.isArray(e)?"array":typeof e}function a(e,t,a){return{id:e,name:t,fn:async()=>{try{return await a(),{id:e,name:t,status:"ok"}}catch(a){if(!(a instanceof n))throw a;return{id:e,name:t,status:t.startsWith("MUST")?"error":"warn",reason:a.reason,response:a.response}}}}}class n{constructor(e,t){this.response=e,this.reason=t}}function o(e){return{status:{toBe(t){if(e.status!==t)throw new n(e,`Response status code is not ${t}`)},toBeBetween:(t,a)=>{if(!(t<=e.status&&e.status<=a))throw new n(e,`Response status is not between ${t} and ${a}`)}},header:t=>({toContain(a){var o;if(!(null===(o=e.headers.get(t))||void 0===o?void 0:o.includes(a)))throw new n(e,`Response header ${t} does not contain ${a}`)},notToContain(a){var o;if(null===(o=e.headers.get(t))||void 0===o?void 0:o.includes(a))throw new n(e,`Response header ${t} contains ${a}`)}}),bodyAsExecutionResult:{data:{async toBe(t){const a=e.clone();if((await s(e)).data!==t)throw new n(a,`Response body execution result data is not "${t}"`)}},async toHaveProperty(t){const a=e.clone();if(!(t in await s(e)))throw new n(a,`Response body execution result does not have a property "${t}"`)},async notToHaveProperty(t){const a=e.clone();if(t in await s(e))throw new n(a,`Response body execution result has a property "${t}"`)}}}}async function s(e){let t,a;try{const a=new TextDecoder("utf-8"),n=await e.arrayBuffer();t=a.decode(n)}catch(t){throw new n(e,"Response body is not UTF-8 encoded")}try{a=JSON.parse(t)}catch(t){throw new n(e,"Response body is not valid JSON")}return a}function i(e){const s=e.fetchFn||fetch;return[a("22EB","SHOULD accept application/graphql-response+json and match the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/graphql-response+json")})),a("4655","MUST accept application/json and match the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/json")})),a("47DE","SHOULD accept */* and use application/json for the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"*/*"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/json")})),a("80D8","SHOULD assume application/json content-type when accept is missing",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","{ __typename }");const a=await s(t.toString());o(a).status.toBe(200),o(a).header("content-type").toContain("application/json")})),a("82A3","MUST use utf-8 encoding when responding",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200);try{new TextDecoder("utf-8").decode(await t.arrayBuffer())}catch(e){throw new n(t,"Response body is not UTF-8 encoded")}})),a("BF61","MUST accept utf-8 encoded request",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify({query:'{ __type(name: "Run🏃Swim🏊") { name } }'})})).status.toBe(200)})),a("78D5","MUST assume utf-8 in request if encoding is unspecified",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("2C94","MUST accept POST requests",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("5A70","MAY accept application/x-www-form-urlencoded formatted GET requests",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","{ __typename }");o(await s(t.toString())).status.toBe(200)})),a("9C48","MAY NOT allow executing mutations on GET requests",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","mutation { __typename }");o(await s(t.toString(),{headers:{accept:"application/graphql-response+json"}})).status.toBeBetween(400,499)})),a("9ABE","SHOULD respond with 4xx status code if content-type is not supplied on POST requests",(async()=>{o(await s(await r(e.url),{method:"POST"})).status.toBeBetween(400,499)})),a("03D4","MUST accept application/json POST requests",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("7267","MUST require a request body on POST",(async()=>{var t;const a=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"}});(null===(t=a.headers.get("content-type"))||void 0===t?void 0:t.includes("application/json"))?await o(a).bodyAsExecutionResult.toHaveProperty("errors"):o(a).status.toBe(400)})),a("6610","SHOULD use 400 status code on missing {query} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({notquery:"{ __typename }"})})).status.toBe(400)})),a("3715","SHOULD use 200 status code with errors field on missing {query} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({notquery:"{ __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.toHaveProperty("errors")})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`4F5${i}`,`SHOULD use 400 status code on ${t(n)} {query} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:n})})).status.toBe(400)})))),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`9FE${i}`,`SHOULD use 200 status code with errors field on ${t(n)} {query} parameter when accepting application/json`,(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:n})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.toHaveProperty("errors")})))),a("34A2","SHOULD allow string {query} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("13EE","MUST allow string {query} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`E3E${i}`,`SHOULD use 400 status code on ${t(n)} {operationName} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({operationName:n,query:"{ __typename }"})})).status.toBe(400)})))),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`FB9${i}`,`SHOULD use 200 status code with errors field on ${t(n)} {operationName} parameter when accepting application/json`,(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({operationName:n,query:"{ __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.toHaveProperty("errors")})))),a("8161","SHOULD allow string {operationName} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({operationName:"Query",query:"query Query { __typename }"})})).status.toBe(200)})),a("B8B3","MUST allow string {operationName} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({operationName:"Query",query:"query Query { __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),...["variables","operationName","extensions"].flatMap(((t,n)=>[a(`94B${n}`,`SHOULD allow null {${t}} parameter when accepting application/graphql-response+json`,(async()=>{const a=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",[t]:null})});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")})),a(`022${n}`,`MUST allow null {${t}} parameter when accepting application/json`,(async()=>{const a=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",[t]:null})});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")}))])),...["string",0,!1,["array"]].map(((n,i)=>a(`69B${i}`,`SHOULD use 400 status code on ${t(n)} {variables} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",variables:n})})).status.toBe(400)})))),...["string",0,!1,["array"]].map(((n,i)=>a(`F05${i}`,`SHOULD use 200 status code with errors field on ${t(n)} {variables} parameter when accepting application/json`,(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",variables:n})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.toHaveProperty("errors")})))),a("2EA1","SHOULD allow map {variables} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"query Type($name: String!) { __type(name: $name) { name } }",variables:{name:"sometype"}})})).status.toBe(200)})),a("28B9","MUST allow map {variables} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"query Type($name: String!) { __type(name: $name) { name } }",variables:{name:"sometype"}})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),a("D6D5","MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),t.searchParams.set("variables",JSON.stringify({name:"sometype"}));o(await s(t.toString(),{method:"GET",headers:{accept:"application/graphql-response+json"}})).status.toBe(200)})),a("6A70","MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),t.searchParams.set("variables",JSON.stringify({name:"sometype"}));const a=await s(t.toString(),{method:"GET",headers:{accept:"application/json"}});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")})),...["string",0,!1,["array"]].map(((n,i)=>a(`904${i}`,`SHOULD use 400 status code on ${t(n)} {extensions} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",extensions:n})})).status.toBe(400)})))),...["string",0,!1,["array"]].map(((n,i)=>a(`368${i}`,`SHOULD use 200 status code with errors field on ${t(n)} {extensions} parameter when accepting application/json`,(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:n})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.toHaveProperty("errors")})))),a("428F","SHOULD allow map {extensions} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",extensions:{some:"value"}})})).status.toBe(200)})),a("1B7A","MUST allow map {extensions} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:{some:"value"}})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),a("D477","SHOULD use 200 status code on JSON parsing failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:'{ "not a JSON'})).status.toBe(200)})),a("F5AF","SHOULD use 200 status code if parameters are invalid when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({qeury:"{ __typename }"})})).status.toBe(200)})),a("572B","SHOULD use 200 status code on document parsing failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{"})})).status.toBe(200)})),a("FDE2","SHOULD use 200 status code on document validation failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBe(200)})),a("7B9B","SHOULD use a status code of 200 on variable coercion failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"query CoerceFailure($id: ID!){ __typename }",variables:{id:null}})})).status.toBe(200)})),a("60AA","SHOULD use 4xx or 5xx status codes on JSON parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:'{ "not a JSON'})).status.toBeBetween(400,499)})),a("2163","SHOULD use 400 status code on JSON parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:'{ "not a JSON'})).status.toBe(400)})),a("3E36","SHOULD use 4xx or 5xx status codes if parameters are invalid when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({qeury:"{ __typename }"})})).status.toBeBetween(400,599)})),a("17C5","SHOULD use 400 status code if parameters are invalid when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({qeury:"{ __typename }"})})).status.toBe(400)})),a("34D6","SHOULD not contain the data entry if parameters are invalid when accepting application/graphql-response+json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({qeury:"{ __typename }"})});await o(t).bodyAsExecutionResult.data.toBe(void 0)})),a("865D","SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})})).status.toBeBetween(400,599)})),a("556A","SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})})).status.toBe(400)})),a("D586","SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})});await o(t).bodyAsExecutionResult.data.toBe(void 0)})),a("51FE","SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBeBetween(400,599)})),a("74FF","SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBe(400)})),a("5E5B","SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})});await o(t).bodyAsExecutionResult.data.toBe(void 0)})),a("86EE","SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"query CoerceFailure($id: ID!){ __typename }",variables:{id:null}})})).status.toBe(400)}))]}async function r(e){return"function"==typeof e?await e():e}async function p(e){let t="";t+=`<li><code>${e.id}</code> ${e.name}\n`,t+="<details>\n",t+=`<summary>${function(e,t=1024){if(e.length>t)return e.substring(0,t)+"...";return e}(e.reason)}</summary>\n`,t+='<pre><code class="lang-json">';const a=e.response,n={};for(const[e,t]of a.headers.entries())"date"===e?n[e]="<timestamp>":["cf-ray","server-timing"].includes(e)?n[e]="<omitted>":n[e]=t;let o,s="";try{s=await a.text(),o=JSON.parse(s)}catch(e){}return t+=JSON.stringify({status:a.status,statusText:a.statusText,headers:n,body:o||((null==s?void 0:s.length)>5120?"<body is too long>":s)||null},((e,t)=>{if(null!=t&&"object"==typeof t&&!Array.isArray(t)){const e={};return Object.keys(t).sort().reverse().reduce(((e,a)=>(e[a]=t[a],e)),e)}return t}),2)+"\n",t+="</code></pre>\n",t+="</details>\n",t+="</li>\n",t}e.auditServer=async function(e){const t=i(e);return await Promise.all(t.map((({fn:e})=>e())))},e.renderAuditResultsToHTML=async function(e){const t={total:0,ok:[],warn:[],error:[]};for(const a of e)t.total++,a.status,t[a.status].push(a);let a="<i>* This report was auto-generated by graphql-http</i>\n";if(a+="\n",a+="<h1>GraphQL over HTTP audit report</h1>\n",a+="\n",a+="<ul>\n",a+=`<li><b>${t.total}</b> audits in total</li>\n`,t.ok.length&&(a+=`<li><span style="font-family: monospace">✅</span> <b>${t.ok.length}</b> pass</li>\n`),t.warn.length&&(a+=`<li><span style="font-family: monospace">⚠️</span> <b>${t.warn.length}</b> warnings (optional)</li>\n`),t.error.length&&(a+=`<li><span style="font-family: monospace">❌</span> <b>${t.error.length}</b> errors (required)</li>\n`),a+="</ul>\n",a+="\n",t.ok.length){a+="<h2>Passing</h2>\n",a+="<ol>\n";for(const[,e]of t.ok.entries())a+=`<li><code>${e.id}</code> ${e.name}</li>\n`;a+="</ol>\n",a+="\n"}if(t.warn.length){a+="<h2>Warnings</h2>\n",a+="The server <i>SHOULD</i> support these, but is not required.\n",a+="<ol>\n";for(const[,e]of t.warn.entries())a+=await p(e);a+="</ol>\n",a+="\n"}if(t.error.length){a+="<h2>Errors</h2>\n",a+="The server <b>MUST</b> support these.\n",a+="<ol>\n";for(const[,e]of t.error.entries())a+=await p(e);a+="</ol>\n"}return a},e.serverAudits=i}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("graphql")):"function"==typeof define&&define.amd?define(["exports","graphql"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlHttpAudits={})}(this,(function(e){"use strict";function t(e){return null===e?"null":Array.isArray(e)?"array":typeof e}function a(e,t,a){return{id:e,name:t,fn:async()=>{try{return await a(),{id:e,name:t,status:"ok"}}catch(a){if(!(a instanceof n))throw a;return{id:e,name:t,status:t.startsWith("MUST")?"error":t.startsWith("SHOULD")?"warn":"notice",reason:a.reason,response:a.response}}}}}class n{constructor(e,t){this.response=e,this.reason=t}}function o(e){return{status:{toBe(t){if(e.status!==t)throw new n(e,`Response status code is not ${t}`)},toBeBetween:(t,a)=>{if(!(t<=e.status&&e.status<=a))throw new n(e,`Response status is not between ${t} and ${a}`)}},header:t=>({toContain(a){var o;if(!(null===(o=e.headers.get(t))||void 0===o?void 0:o.includes(a)))throw new n(e,`Response header ${t} does not contain ${a}`)},notToContain(a){var o;if(null===(o=e.headers.get(t))||void 0===o?void 0:o.includes(a))throw new n(e,`Response header ${t} contains ${a}`)}}),bodyAsExecutionResult:{data:{async toBe(t){const a=e.clone();if((await s(e)).data!==t)throw new n(a,`Response body execution result data is not "${t}"`)}},async toHaveProperty(t){const a=e.clone();if(!(t in await s(e)))throw new n(a,`Response body execution result does not have a property "${t}"`)},async notToHaveProperty(t){const a=e.clone();if(t in await s(e))throw new n(a,`Response body execution result has a property "${t}"`)}}}}async function s(e){let t,a;try{const a=new TextDecoder("utf-8"),n=await e.arrayBuffer();t=a.decode(n)}catch(t){throw new n(e,"Response body is not UTF-8 encoded")}try{a=JSON.parse(t)}catch(t){throw new n(e,"Response body is not valid JSON")}return a}function i(e){const s=e.fetchFn||fetch;return[a("22EB","SHOULD accept application/graphql-response+json and match the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/graphql-response+json")})),a("4655","MUST accept application/json and match the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/json")})),a("47DE","SHOULD accept */* and use application/json for the content-type",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"*/*"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/json")})),a("80D8","SHOULD assume application/json content-type when accept is missing",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),o(t).header("content-type").toContain("application/json")})),a("82A3","MUST use utf-8 encoding when responding",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200);try{new TextDecoder("utf-8").decode(await t.arrayBuffer())}catch(e){throw new n(t,"Response body is not UTF-8 encoded")}})),a("BF61","MUST accept utf-8 encoded request",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify({query:'{ __type(name: "Run🏃Swim🏊") { name } }'})})).status.toBe(200)})),a("78D5","MUST assume utf-8 in request if encoding is unspecified",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("2C94","MUST accept POST requests",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("5A70","MAY accept application/x-www-form-urlencoded formatted GET requests",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","{ __typename }");o(await s(t.toString())).status.toBe(200)})),a("9C48","MAY NOT allow executing mutations on GET requests",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","mutation { __typename }");o(await s(t.toString(),{headers:{accept:"application/graphql-response+json"}})).status.toBeBetween(400,499)})),a("9ABE","MAY respond with 4xx status code if content-type is not supplied on POST requests",(async()=>{o(await s(await r(e.url),{method:"POST"})).status.toBeBetween(400,499)})),a("03D4","MUST accept application/json POST requests",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("A5BF","MAY use 400 status code when request body is missing on POST",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"}})).status.toBe(400)})),a("423L","MAY use 400 status code on missing {query} parameter",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({notquery:"{ __typename }"})})).status.toBe(400)})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`LKJ${i}`,`MAY use 400 status code on ${t(n)} {query} parameter`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:n})})).status.toBe(400)})))),a("34A2","SHOULD allow string {query} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("13EE","MUST allow string {query} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`6C0${i}`,`MAY use 400 status code on ${t(n)} {operationName} parameter`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({operationName:n,query:"{ __typename }"})})).status.toBe(400)})))),a("8161","SHOULD allow string {operationName} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({operationName:"Query",query:"query Query { __typename }"})})).status.toBe(200)})),a("B8B3","MUST allow string {operationName} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({operationName:"Query",query:"query Query { __typename }"})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),...["variables","operationName","extensions"].flatMap(((t,n)=>[a(`94B${n}`,`SHOULD allow null {${t}} parameter when accepting application/graphql-response+json`,(async()=>{const a=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",[t]:null})});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")})),a(`022${n}`,`MUST allow null {${t}} parameter when accepting application/json`,(async()=>{const a=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",[t]:null})});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")}))])),...["string",0,!1,["array"]].map(((n,i)=>a(`476${i}`,`MAY use 400 status code on ${t(n)} {variables} parameter`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }",variables:n})})).status.toBe(400)})))),a("2EA1","SHOULD allow map {variables} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"query Type($name: String!) { __type(name: $name) { name } }",variables:{name:"sometype"}})})).status.toBe(200)})),a("28B9","MUST allow map {variables} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"query Type($name: String!) { __type(name: $name) { name } }",variables:{name:"sometype"}})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),a("D6D5","MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),t.searchParams.set("variables",JSON.stringify({name:"sometype"}));o(await s(t.toString(),{method:"GET",headers:{accept:"application/graphql-response+json"}})).status.toBe(200)})),a("6A70","MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/json",(async()=>{const t=new URL(await r(e.url));t.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),t.searchParams.set("variables",JSON.stringify({name:"sometype"}));const a=await s(t.toString(),{method:"GET",headers:{accept:"application/json"}});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")})),...["string",0,!1,["array"]].map(((n,i)=>a(`58B${i}`,`MAY use 400 status code on ${t(n)} {extensions} parameter`,(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:n})})).status.toBe(400)})))),a("428F","SHOULD allow map {extensions} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",extensions:{some:"value"}})})).status.toBe(200)})),a("1B7A","MUST allow map {extensions} parameter when accepting application/json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:{some:"value"}})});o(t).status.toBe(200),await o(t).bodyAsExecutionResult.notToHaveProperty("errors")})),a("B6DC","MAY use 4xx or 5xx status codes on JSON parsing failure",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:'{ "not a JSON'})).status.toBeBetween(400,499)})),a("BCF8","MAY use 400 status code on JSON parsing failure",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:'{ "not a JSON'})).status.toBe(400)})),a("8764","MAY use 4xx or 5xx status codes if parameters are invalid",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({qeury:"{ __typename }"})})).status.toBeBetween(400,599)})),a("3E3A","MAY use 400 status code if parameters are invalid",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({qeury:"{ __typename }"})})).status.toBe(400)})),a("572B","SHOULD use 200 status code on document parsing failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{"})})).status.toBe(200)})),a("FDE2","SHOULD use 200 status code on document validation failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBe(200)})),a("7B9B","SHOULD use a status code of 200 on variable coercion failure when accepting application/json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"query CoerceFailure($id: ID!){ __typename }",variables:{id:null}})})).status.toBe(200)})),a("865D","SHOULD use 4xx or 5xx status codes on document parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})})).status.toBeBetween(400,599)})),a("556A","SHOULD use 400 status code on document parsing failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})})).status.toBe(400)})),a("D586","SHOULD not contain the data entry on document parsing failure when accepting application/graphql-response+json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})});await o(t).bodyAsExecutionResult.data.toBe(void 0)})),a("51FE","SHOULD use 4xx or 5xx status codes on document validation failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBeBetween(400,599)})),a("74FF","SHOULD use 400 status code on document validation failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).status.toBe(400)})),a("5E5B","SHOULD not contain the data entry on document validation failure when accepting application/graphql-response+json",(async()=>{const t=await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})});await o(t).bodyAsExecutionResult.data.toBe(void 0)})),a("86EE","SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json",(async()=>{o(await s(await r(e.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"query CoerceFailure($id: ID!){ __typename }",variables:{id:null}})})).status.toBe(400)}))]}async function r(e){return"function"==typeof e?await e():e}async function p(e){let t="";t+=`<li><code>${e.id}</code> ${e.name}\n`,t+="<details>\n",t+=`<summary>${function(e,t=1024){if(e.length>t)return e.substring(0,t)+"...";return e}(e.reason)}</summary>\n`,t+='<pre><code class="lang-json">';const a=e.response,n={};for(const[e,t]of a.headers.entries())"date"===e?n[e]="<timestamp>":["cf-ray","server-timing"].includes(e)?n[e]="<omitted>":n[e]=t;let o,s="";try{s=await a.text(),o=JSON.parse(s)}catch(e){}return t+=JSON.stringify({status:a.status,statusText:a.statusText,headers:n,body:o||((null==s?void 0:s.length)>5120?"<body is too long>":s)||null},((e,t)=>{if(null!=t&&"object"==typeof t&&!Array.isArray(t)){const e={};return Object.keys(t).sort().reverse().reduce(((e,a)=>(e[a]=t[a],e)),e)}return t}),2)+"\n",t+="</code></pre>\n",t+="</details>\n",t+="</li>\n",t}e.auditServer=async function(e){const t=i(e);return await Promise.all(t.map((({fn:e})=>e())))},e.renderAuditResultsToHTML=async function(e){const t={total:0,ok:[],notice:[],warn:[],error:[]};for(const a of e)t.total++,a.status,t[a.status].push(a);let a="<i>* This report was auto-generated by graphql-http</i>\n";if(a+="\n",a+="<h1>GraphQL over HTTP audit report</h1>\n",a+="\n",a+="<ul>\n",a+=`<li><b>${t.total}</b> audits in total</li>\n`,t.ok.length&&(a+=`<li><span style="font-family: monospace">✅</span> <b>${t.ok.length}</b> pass</li>\n`),t.notice.length&&(a+=`<li><span style="font-family: monospace">💡</span> <b>${t.notice.length}</b> notices (suggestions)</li>\n`),t.warn.length&&(a+=`<li><span style="font-family: monospace">⚠️</span> <b>${t.warn.length}</b> warnings (optional)</li>\n`),t.error.length&&(a+=`<li><span style="font-family: monospace">❌</span> <b>${t.error.length}</b> errors (required)</li>\n`),a+="</ul>\n",a+="\n",t.ok.length){a+="<h2>Passing</h2>\n",a+="<ol>\n";for(const[,e]of t.ok.entries())a+=`<li><code>${e.id}</code> ${e.name}</li>\n`;a+="</ol>\n",a+="\n"}if(t.notice.length){a+="<h2>Notices</h2>\n",a+="The server <i>MAY</i> support these, but are truly optional. These are suggestions following recommended conventions.\n",a+="<ol>\n";for(const[,e]of t.notice.entries())a+=await p(e);a+="</ol>\n",a+="\n"}if(t.warn.length){a+="<h2>Warnings</h2>\n",a+="The server <i>SHOULD</i> support these, but is not required.\n",a+="<ol>\n";for(const[,e]of t.warn.entries())a+=await p(e);a+="</ol>\n",a+="\n"}if(t.error.length){a+="<h2>Errors</h2>\n",a+="The server <b>MUST</b> support these.\n",a+="<ol>\n";for(const[,e]of t.error.entries())a+=await p(e);a+="</ol>\n"}return a},e.serverAudits=i}));
Binary file