graphql-http 1.16.0 → 1.17.1

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
@@ -736,6 +736,36 @@ for (const audit of serverAudits({
736
736
 
737
737
  </details>
738
738
 
739
+ <details id="audit-deno">
740
+ <summary><a href="#audit-deno">🔗</a> Audit for servers usage in <a href="https://deno.land">Deno</a> environment</summary>
741
+
742
+ ```ts
743
+ import { serverAudits } from 'npm:graphql-http';
744
+
745
+ for (const audit of serverAudits({
746
+ url: 'http://localhost:4000/graphql',
747
+ fetchFn: fetch,
748
+ })) {
749
+ Deno.test(audit.name, async () => {
750
+ const result = await audit.fn();
751
+ if (result.status === 'error') {
752
+ throw result.reason;
753
+ }
754
+ if (result.status === 'warn') {
755
+ console.warn(result.reason); // or throw if you want full compliance (warnings are not requirements)
756
+ }
757
+ // Avoid leaking resources
758
+ if ('body' in result && result.body instanceof ReadableStream) {
759
+ await result.body.cancel();
760
+ }
761
+ });
762
+ }
763
+ ```
764
+
765
+ Put the above contents in a file and run it with `deno test --allow-net`.
766
+
767
+ </details>
768
+
739
769
  ## Only [GraphQL over HTTP](https://graphql.github.io/graphql-over-http/)
740
770
 
741
771
  This is the official [GraphQL over HTTP spec](https://graphql.github.io/graphql-over-http/) reference implementation and as such follows the specification strictly without any additional features (like file uploads, @stream/@defer directives and subscriptions).
@@ -744,7 +774,9 @@ Having said this, graphql-http is mostly aimed for library authors and simple se
744
774
 
745
775
  ## [Servers](/implementations)
746
776
 
747
- If you want a feature-full server with bleeding edge technologies, you're recommended to use one of the following.
777
+ If you want a feature-full server with bleeding edge technologies, you're recommended to use one of the following servers.
778
+
779
+ Their compliance with the [GraphQL over HTTP spec](https://graphql.github.io/graphql-over-http) is checked automatically and updated regularly.
748
780
 
749
781
  <!-- <ServersTable> -->
750
782
  <!-- prettier-ignore-start -->
@@ -768,16 +800,11 @@ Check the [docs folder](docs/) out for [TypeDoc](https://typedoc.org) generated
768
800
 
769
801
  ## [Audits](implementations/)
770
802
 
771
- Inspect audits of other implementations in the [implementations folder](implementations/). Adding your implementation is very welcome!
803
+ Inspect audits of other implementations in the [implementations folder](implementations).
804
+ Adding your implementation is very welcome, [see how](CONTRIBUTING.md#adding-implementations)!
772
805
 
773
806
  ## Want to help?
774
807
 
775
- File a bug, contribute with code, or improve documentation? Read up on our guidelines below and drive development with `yarn test --watch` away!
776
-
777
- This repository is managed by EasyCLA. Project participants must sign the free [GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](http://individual-spec-membership.graphql.org/) or their [employers](http://corporate-spec-membership.graphql.org/).
778
-
779
- To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you.
780
-
781
- You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org).
808
+ File a bug, contribute with code, or improve documentation? [Read more in CONTRIBUTING.md](CONTRIBUTING.md).
782
809
 
783
810
  If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join).
@@ -1,3 +1,3 @@
1
- export * from './common';
2
- export * from './server';
3
- export * from './render';
1
+ export * from './common.mjs';
2
+ export * from './server.mjs';
3
+ export * from './render.mjs';
@@ -1,4 +1,4 @@
1
- import { AuditResult } from './common';
1
+ import { AuditResult } from './common.mjs';
2
2
  /**
3
3
  * Renders the provided audit results to well-formatted and valid HTML.
4
4
  *
@@ -3,7 +3,7 @@
3
3
  * audit/server
4
4
  *
5
5
  */
6
- import { Audit, AuditResult } from './common';
6
+ import { Audit, AuditResult } from './common.mjs';
7
7
  /**
8
8
  * Options for server audits required to check GraphQL over HTTP spec conformance.
9
9
  *
@@ -86,7 +86,9 @@ function serverAudits(opts) {
86
86
  headers: {
87
87
  'content-type': 'application/json; charset=utf-8',
88
88
  },
89
- body: JSON.stringify({ query: '{ __typename }' }),
89
+ body: JSON.stringify({
90
+ query: '{ __type(name: "Run🏃Swim🏊") { name } }',
91
+ }),
90
92
  });
91
93
  (0, utils_1.ressert)(res).status.toBe(200);
92
94
  }),
@@ -534,6 +536,20 @@ function serverAudits(opts) {
534
536
  });
535
537
  (0, utils_1.ressert)(res).status.toBe(200);
536
538
  }),
539
+ (0, utils_1.audit)('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
540
+ const res = await fetchFn(await getUrl(opts.url), {
541
+ method: 'POST',
542
+ headers: {
543
+ 'content-type': 'application/json',
544
+ accept: 'application/json',
545
+ },
546
+ body: JSON.stringify({
547
+ query: 'query CoerceFailure($id: ID!){ __typename }',
548
+ variables: { id: null },
549
+ }),
550
+ });
551
+ (0, utils_1.ressert)(res).status.toBe(200);
552
+ }),
537
553
  // Response application/graphql-response+json
538
554
  (0, utils_1.audit)(
539
555
  // TODO: convert to MUST after watershed
@@ -682,6 +698,20 @@ function serverAudits(opts) {
682
698
  });
683
699
  await (0, utils_1.ressert)(res).bodyAsExecutionResult.data.toBe(undefined);
684
700
  }),
701
+ (0, utils_1.audit)('86EE', 'SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json', async () => {
702
+ const res = await fetchFn(await getUrl(opts.url), {
703
+ method: 'POST',
704
+ headers: {
705
+ 'content-type': 'application/json',
706
+ accept: 'application/graphql-response+json',
707
+ },
708
+ body: JSON.stringify({
709
+ query: 'query CoerceFailure($id: ID!){ __typename }',
710
+ variables: { id: null },
711
+ }),
712
+ });
713
+ (0, utils_1.ressert)(res).status.toBe(400);
714
+ }),
685
715
  // TODO: how to fail and have the data entry?
686
716
  // audit('EE52', 'MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),
687
717
  // TODO: how to make an unauthorized request?
@@ -83,7 +83,9 @@ export function serverAudits(opts) {
83
83
  headers: {
84
84
  'content-type': 'application/json; charset=utf-8',
85
85
  },
86
- body: JSON.stringify({ query: '{ __typename }' }),
86
+ body: JSON.stringify({
87
+ query: '{ __type(name: "Run🏃Swim🏊") { name } }',
88
+ }),
87
89
  });
88
90
  ressert(res).status.toBe(200);
89
91
  }),
@@ -531,6 +533,20 @@ export function serverAudits(opts) {
531
533
  });
532
534
  ressert(res).status.toBe(200);
533
535
  }),
536
+ audit('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
537
+ const res = await fetchFn(await getUrl(opts.url), {
538
+ method: 'POST',
539
+ headers: {
540
+ 'content-type': 'application/json',
541
+ accept: 'application/json',
542
+ },
543
+ body: JSON.stringify({
544
+ query: 'query CoerceFailure($id: ID!){ __typename }',
545
+ variables: { id: null },
546
+ }),
547
+ });
548
+ ressert(res).status.toBe(200);
549
+ }),
534
550
  // Response application/graphql-response+json
535
551
  audit(
536
552
  // TODO: convert to MUST after watershed
@@ -679,6 +695,20 @@ export function serverAudits(opts) {
679
695
  });
680
696
  await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
681
697
  }),
698
+ audit('86EE', 'SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json', async () => {
699
+ const res = await fetchFn(await getUrl(opts.url), {
700
+ method: 'POST',
701
+ headers: {
702
+ 'content-type': 'application/json',
703
+ accept: 'application/graphql-response+json',
704
+ },
705
+ body: JSON.stringify({
706
+ query: 'query CoerceFailure($id: ID!){ __typename }',
707
+ variables: { id: null },
708
+ }),
709
+ });
710
+ ressert(res).status.toBe(400);
711
+ }),
682
712
  // TODO: how to fail and have the data entry?
683
713
  // audit('EE52', 'MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),
684
714
  // TODO: how to make an unauthorized request?
@@ -4,8 +4,8 @@
4
4
  *
5
5
  */
6
6
  import type { ExecutionResult } from 'graphql';
7
- import { Audit, AuditName } from './common';
8
- export * from '../utils';
7
+ import { Audit, AuditName } from './common.mjs';
8
+ export * from '../utils.mjs';
9
9
  /**
10
10
  * Wrap and prepare an audit for testing.
11
11
  *
package/lib/client.d.mts CHANGED
@@ -4,9 +4,9 @@
4
4
  *
5
5
  */
6
6
  import type { ExecutionResult } from 'graphql';
7
- import { RequestParams, Sink } from './common';
7
+ import { RequestParams, Sink } from './common.mjs';
8
8
  /** This file is the entry point for browsers, re-export common elements. */
9
- export * from './common';
9
+ export * from './common.mjs';
10
10
  /** @category Client */
11
11
  export interface ClientOptions {
12
12
  /**
package/lib/handler.d.mts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  */
6
6
  import { ExecutionArgs, ExecutionResult, GraphQLSchema, validate as graphqlValidate, ValidationRule, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError } from 'graphql';
7
- import { RequestParams } from './common';
7
+ import { RequestParams } from './common.mjs';
8
8
  /**
9
9
  * The incoming request headers the implementing server should provide.
10
10
  *
@@ -246,7 +246,7 @@ export type Handler<RequestRaw = unknown, RequestContext = unknown> = (req: Requ
246
246
  * ```js
247
247
  * import http from 'http';
248
248
  * import { createHandler } from 'graphql-http';
249
- * import { schema } from './my-graphql-schema';
249
+ * import { schema } from './my-graphql-schema/index.mjs';
250
250
  *
251
251
  * // Create the GraphQL over HTTP handler
252
252
  * const handler = createHandler({ schema });
package/lib/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- export * from './common';
2
- export * from './handler';
3
- export * from './client';
4
- export * from './audits';
1
+ export * from './common.mjs';
2
+ export * from './handler.mjs';
3
+ export * from './client.mjs';
4
+ export * from './audits/index.mjs';
@@ -1,11 +1,19 @@
1
- import type { Request, Handler } from 'express';
2
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
1
+ import type { Request, Response, Handler } from 'express';
2
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
3
+ /**
4
+ * The context in the request for the handler.
5
+ *
6
+ * @category Server/express
7
+ */
8
+ export interface RequestContext {
9
+ res: Response;
10
+ }
3
11
  /**
4
12
  * Handler options when using the express adapter.
5
13
  *
6
14
  * @category Server/express
7
15
  */
8
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Request, undefined, Context>;
16
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Request, RequestContext, Context>;
9
17
  /**
10
18
  * Create a GraphQL over HTTP spec compliant request handler for
11
19
  * the express framework.
@@ -13,7 +21,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
13
21
  * ```js
14
22
  * import express from 'express'; // yarn add express
15
23
  * import { createHandler } from 'graphql-http/lib/use/express';
16
- * import { schema } from './my-graphql-schema';
24
+ * import { schema } from './my-graphql-schema/index.mjs';
17
25
  *
18
26
  * const app = express();
19
27
  * app.all('/graphql', createHandler({ schema }));
@@ -1,11 +1,19 @@
1
- import type { Request, Handler } from 'express';
1
+ import type { Request, Response, Handler } from 'express';
2
2
  import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
3
+ /**
4
+ * The context in the request for the handler.
5
+ *
6
+ * @category Server/express
7
+ */
8
+ export interface RequestContext {
9
+ res: Response;
10
+ }
3
11
  /**
4
12
  * Handler options when using the express adapter.
5
13
  *
6
14
  * @category Server/express
7
15
  */
8
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Request, undefined, Context>;
16
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Request, RequestContext, Context>;
9
17
  /**
10
18
  * Create a GraphQL over HTTP spec compliant request handler for
11
19
  * the express framework.
@@ -41,7 +41,7 @@ function createHandler(options) {
41
41
  });
42
42
  },
43
43
  raw: req,
44
- context: undefined,
44
+ context: { res },
45
45
  });
46
46
  res.writeHead(init.status, init.statusText, init.headers).end(body);
47
47
  }
@@ -38,7 +38,7 @@ export function createHandler(options) {
38
38
  });
39
39
  },
40
40
  raw: req,
41
- context: undefined,
41
+ context: { res },
42
42
  });
43
43
  res.writeHead(init.status, init.statusText, init.headers).end(body);
44
44
  }
@@ -1,11 +1,19 @@
1
- import type { FastifyRequest, RouteHandler } from 'fastify';
2
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
1
+ import type { FastifyRequest, FastifyReply, RouteHandler } from 'fastify';
2
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
3
+ /**
4
+ * The context in the request for the handler.
5
+ *
6
+ * @category Server/fastify
7
+ */
8
+ export interface RequestContext {
9
+ reply: FastifyReply;
10
+ }
3
11
  /**
4
12
  * Handler options when using the fastify adapter.
5
13
  *
6
14
  * @category Server/fastify
7
15
  */
8
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<FastifyRequest, undefined, Context>;
16
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<FastifyRequest, RequestContext, Context>;
9
17
  /**
10
18
  * Create a GraphQL over HTTP spec compliant request handler for
11
19
  * the fastify framework.
@@ -13,7 +21,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
13
21
  * ```js
14
22
  * import Fastify from 'fastify'; // yarn add fastify
15
23
  * import { createHandler } from 'graphql-http/lib/use/express';
16
- * import { schema } from './my-graphql-schema';
24
+ * import { schema } from './my-graphql-schema/index.mjs';
17
25
  *
18
26
  * const fastify = Fastify();
19
27
  * fastify.all('/graphql', createHandler({ schema }));
@@ -1,11 +1,19 @@
1
- import type { FastifyRequest, RouteHandler } from 'fastify';
1
+ import type { FastifyRequest, FastifyReply, RouteHandler } from 'fastify';
2
2
  import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
3
+ /**
4
+ * The context in the request for the handler.
5
+ *
6
+ * @category Server/fastify
7
+ */
8
+ export interface RequestContext {
9
+ reply: FastifyReply;
10
+ }
3
11
  /**
4
12
  * Handler options when using the fastify adapter.
5
13
  *
6
14
  * @category Server/fastify
7
15
  */
8
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<FastifyRequest, undefined, Context>;
16
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<FastifyRequest, RequestContext, Context>;
9
17
  /**
10
18
  * Create a GraphQL over HTTP spec compliant request handler for
11
19
  * the fastify framework.
@@ -32,7 +32,7 @@ function createHandler(options) {
32
32
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
33
  body: req.body,
34
34
  raw: req,
35
- context: undefined,
35
+ context: { reply },
36
36
  });
37
37
  reply
38
38
  .status(init.status)
@@ -29,7 +29,7 @@ export function createHandler(options) {
29
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
30
  body: req.body,
31
31
  raw: req,
32
- context: undefined,
32
+ context: { reply },
33
33
  });
34
34
  reply
35
35
  .status(init.status)
@@ -1,4 +1,4 @@
1
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
1
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
2
2
  /**
3
3
  * The necessary API from the fetch environment for the handler.
4
4
  *
@@ -26,7 +26,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
26
26
  * import http from 'http';
27
27
  * import { createServerAdapter } from '@whatwg-node/server'; // yarn add @whatwg-node/server
28
28
  * import { createHandler } from 'graphql-http/lib/use/fetch';
29
- * import { schema } from './my-graphql-step';
29
+ * import { schema } from './my-graphql-step/index.mjs';
30
30
  *
31
31
  * // Use this adapter in _any_ environment.
32
32
  * const adapter = createServerAdapter({
@@ -1,12 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import type { IncomingMessage, ServerResponse } from 'http';
3
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
3
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
4
+ /**
5
+ * The context in the request for the handler.
6
+ *
7
+ * @category Server/http
8
+ */
9
+ export interface RequestContext {
10
+ res: ServerResponse;
11
+ }
4
12
  /**
5
13
  * Handler options when using the http adapter.
6
14
  *
7
15
  * @category Server/http
8
16
  */
9
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, undefined, Context>;
17
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, RequestContext, Context>;
10
18
  /**
11
19
  * Create a GraphQL over HTTP spec compliant request handler for
12
20
  * the Node environment http module.
@@ -14,7 +22,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
14
22
  * ```js
15
23
  * import http from 'http';
16
24
  * import { createHandler } from 'graphql-http/lib/use/http';
17
- * import { schema } from './my-graphql-step';
25
+ * import { schema } from './my-graphql-step/index.mjs';
18
26
  *
19
27
  * const server = http.createServer(createHandler({ schema }));
20
28
  *
package/lib/use/http.d.ts CHANGED
@@ -1,12 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import type { IncomingMessage, ServerResponse } from 'http';
3
3
  import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
4
+ /**
5
+ * The context in the request for the handler.
6
+ *
7
+ * @category Server/http
8
+ */
9
+ export interface RequestContext {
10
+ res: ServerResponse;
11
+ }
4
12
  /**
5
13
  * Handler options when using the http adapter.
6
14
  *
7
15
  * @category Server/http
8
16
  */
9
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, undefined, Context>;
17
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, RequestContext, Context>;
10
18
  /**
11
19
  * Create a GraphQL over HTTP spec compliant request handler for
12
20
  * the Node environment http module.
package/lib/use/http.js CHANGED
@@ -40,7 +40,7 @@ function createHandler(options) {
40
40
  req.on('end', () => resolve(body));
41
41
  }),
42
42
  raw: req,
43
- context: undefined,
43
+ context: { res },
44
44
  });
45
45
  res.writeHead(init.status, init.statusText, init.headers).end(body);
46
46
  }
package/lib/use/http.mjs CHANGED
@@ -37,7 +37,7 @@ export function createHandler(options) {
37
37
  req.on('end', () => resolve(body));
38
38
  }),
39
39
  raw: req,
40
- context: undefined,
40
+ context: { res },
41
41
  });
42
42
  res.writeHead(init.status, init.statusText, init.headers).end(body);
43
43
  }
@@ -1,12 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import type { Http2ServerRequest, Http2ServerResponse } from 'http2';
3
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
3
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
4
+ /**
5
+ * The context in the request for the handler.
6
+ *
7
+ * @category Server/http2
8
+ */
9
+ export interface RequestContext {
10
+ res: Http2ServerResponse;
11
+ }
4
12
  /**
5
13
  * Handler options when using the http adapter.
6
14
  *
7
15
  * @category Server/http2
8
16
  */
9
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Http2ServerRequest, undefined, Context>;
17
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Http2ServerRequest, RequestContext, Context>;
10
18
  /**
11
19
  * Create a GraphQL over HTTP spec compliant request handler for
12
20
  * the Node environment http2 module.
@@ -20,7 +28,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
20
28
  * import fs from 'fs';
21
29
  * import http2 from 'http2';
22
30
  * import { createHandler } from 'graphql-http/lib/use/http2';
23
- * import { schema } from './my-graphql-step';
31
+ * import { schema } from './my-graphql-step/index.mjs';
24
32
  *
25
33
  * const server = http2.createSecureServer(
26
34
  * {
@@ -1,12 +1,20 @@
1
1
  /// <reference types="node" />
2
2
  import type { Http2ServerRequest, Http2ServerResponse } from 'http2';
3
3
  import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
4
+ /**
5
+ * The context in the request for the handler.
6
+ *
7
+ * @category Server/http2
8
+ */
9
+ export interface RequestContext {
10
+ res: Http2ServerResponse;
11
+ }
4
12
  /**
5
13
  * Handler options when using the http adapter.
6
14
  *
7
15
  * @category Server/http2
8
16
  */
9
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Http2ServerRequest, undefined, Context>;
17
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<Http2ServerRequest, RequestContext, Context>;
10
18
  /**
11
19
  * Create a GraphQL over HTTP spec compliant request handler for
12
20
  * the Node environment http2 module.
package/lib/use/http2.js CHANGED
@@ -52,7 +52,7 @@ function createHandler(options) {
52
52
  req.on('end', () => resolve(body));
53
53
  }),
54
54
  raw: req,
55
- context: undefined,
55
+ context: { res },
56
56
  });
57
57
  res.writeHead(init.status, init.statusText, init.headers);
58
58
  if (body) {
package/lib/use/http2.mjs CHANGED
@@ -49,7 +49,7 @@ export function createHandler(options) {
49
49
  req.on('end', () => resolve(body));
50
50
  }),
51
51
  raw: req,
52
- context: undefined,
52
+ context: { res },
53
53
  });
54
54
  res.writeHead(init.status, init.statusText, init.headers);
55
55
  if (body) {
package/lib/use/koa.d.mts CHANGED
@@ -1,13 +1,21 @@
1
1
  /// <reference types="node" />
2
- import type { Middleware } from 'koa';
2
+ import type { Middleware, Response } from 'koa';
3
3
  import type { IncomingMessage } from 'http';
4
- import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
4
+ import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler.mjs';
5
+ /**
6
+ * The context in the request for the handler.
7
+ *
8
+ * @category Server/koa
9
+ */
10
+ export interface RequestContext {
11
+ res: Response;
12
+ }
5
13
  /**
6
14
  * Handler options when using the koa adapter.
7
15
  *
8
16
  * @category Server/koa
9
17
  */
10
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, undefined, Context>;
18
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, RequestContext, Context>;
11
19
  /**
12
20
  * Create a GraphQL over HTTP spec compliant request handler for
13
21
  * the Koa framework.
@@ -16,7 +24,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = RawHa
16
24
  * import Koa from 'koa'; // yarn add koa
17
25
  * import mount from 'koa-mount'; // yarn add koa-mount
18
26
  * import { createHandler } from 'graphql-http/lib/use/koa';
19
- * import { schema } from './my-graphql-schema';
27
+ * import { schema } from './my-graphql-schema/index.mjs';
20
28
  *
21
29
  * const app = new Koa();
22
30
  * app.use(mount('/', createHandler({ schema })));
package/lib/use/koa.d.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  /// <reference types="node" />
2
- import type { Middleware } from 'koa';
2
+ import type { Middleware, Response } from 'koa';
3
3
  import type { IncomingMessage } from 'http';
4
4
  import { HandlerOptions as RawHandlerOptions, OperationContext } from '../handler';
5
+ /**
6
+ * The context in the request for the handler.
7
+ *
8
+ * @category Server/koa
9
+ */
10
+ export interface RequestContext {
11
+ res: Response;
12
+ }
5
13
  /**
6
14
  * Handler options when using the koa adapter.
7
15
  *
8
16
  * @category Server/koa
9
17
  */
10
- export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, undefined, Context>;
18
+ export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<IncomingMessage, RequestContext, Context>;
11
19
  /**
12
20
  * Create a GraphQL over HTTP spec compliant request handler for
13
21
  * the Koa framework.
package/lib/use/koa.js CHANGED
@@ -42,7 +42,7 @@ function createHandler(options) {
42
42
  });
43
43
  },
44
44
  raw: ctx.req,
45
- context: undefined,
45
+ context: { res: ctx.response },
46
46
  });
47
47
  ctx.body = body;
48
48
  ctx.response.status = init.status;
package/lib/use/koa.mjs CHANGED
@@ -39,7 +39,7 @@ export function createHandler(options) {
39
39
  });
40
40
  },
41
41
  raw: ctx.req,
42
- context: undefined,
42
+ context: { res: ctx.response },
43
43
  });
44
44
  ctx.body = body;
45
45
  ctx.response.status = init.status;
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
- import { HandlerOptions as HttpHandlerOptions } from './http';
3
- import { OperationContext } from '../handler';
2
+ import { HandlerOptions as HttpHandlerOptions } from './http.mjs';
3
+ import { OperationContext } from '../handler.mjs';
4
4
  /**
5
5
  * Handler options when using the node adapter.
6
6
  *
@@ -16,7 +16,7 @@ export type HandlerOptions<Context extends OperationContext = undefined> = HttpH
16
16
  * ```js
17
17
  * import http from 'http';
18
18
  * import { createHandler } from 'graphql-http/lib/use/node';
19
- * import { schema } from './my-graphql-step';
19
+ * import { schema } from './my-graphql-step/index.mjs';
20
20
  *
21
21
  * const server = http.createServer(createHandler({ schema }));
22
22
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-http",
3
- "version": "1.16.0",
3
+ "version": "1.17.1",
4
4
  "description": "Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.",
5
5
  "keywords": [
6
6
  "graphql",
@@ -24,7 +24,7 @@
24
24
  "engines": {
25
25
  "node": ">=12"
26
26
  },
27
- "packageManager": "yarn@3.4.1",
27
+ "packageManager": "yarn@3.5.0",
28
28
  "main": "lib/index.js",
29
29
  "module": "lib/index.mjs",
30
30
  "browser": "umd/graphql-http.js",
@@ -88,7 +88,8 @@
88
88
  "scripts": {
89
89
  "gendocs": "typedoc --options typedoc.js src/",
90
90
  "lint:eslint": "eslint 'src'",
91
- "lint:prettier": "prettier -c .",
91
+ "lint:prettier": "prettier --check .",
92
+ "lint:fix": "yarn lint:eslint --fix && yarn lint:prettier --write",
92
93
  "lint": "yarn lint:eslint && yarn lint:prettier",
93
94
  "type-check": "tsc --noEmit",
94
95
  "test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest",
@@ -105,47 +106,47 @@
105
106
  "graphql": ">=0.11 <=16"
106
107
  },
107
108
  "devDependencies": {
108
- "@babel/core": "^7.20.12",
109
+ "@babel/core": "^7.21.4",
109
110
  "@babel/plugin-proposal-class-properties": "^7.18.6",
110
111
  "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
111
112
  "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
112
- "@babel/plugin-proposal-optional-chaining": "^7.20.7",
113
- "@babel/preset-env": "^7.20.2",
114
- "@babel/preset-typescript": "^7.18.6",
113
+ "@babel/plugin-proposal-optional-chaining": "^7.21.0",
114
+ "@babel/preset-env": "^7.21.4",
115
+ "@babel/preset-typescript": "^7.21.4",
115
116
  "@rollup/plugin-terser": "^0.4.0",
116
117
  "@rollup/plugin-typescript": "^11.0.0",
117
- "@semantic-release/changelog": "^6.0.2",
118
+ "@semantic-release/changelog": "^6.0.3",
118
119
  "@semantic-release/git": "^10.0.1",
119
- "@types/eslint": "^8.21.0",
120
+ "@types/eslint": "^8.37.0",
120
121
  "@types/express": "^4.17.17",
121
- "@types/glob": "^8.0.1",
122
+ "@types/glob": "^8.1.0",
122
123
  "@types/html-validator": "^5.0.3",
123
- "@types/jest": "^29.4.0",
124
- "@types/koa": "^2.13.5",
124
+ "@types/jest": "^29.5.0",
125
+ "@types/koa": "^2.13.6",
125
126
  "@types/koa-mount": "^4.0.2",
126
- "@typescript-eslint/eslint-plugin": "^5.51.0",
127
- "@typescript-eslint/parser": "^5.51.0",
128
- "@whatwg-node/fetch": "^0.8.1",
129
- "babel-jest": "^29.4.2",
130
- "eslint": "^8.34.0",
131
- "eslint-config-prettier": "^8.6.0",
127
+ "@typescript-eslint/eslint-plugin": "^5.57.0",
128
+ "@typescript-eslint/parser": "^5.57.0",
129
+ "@whatwg-node/fetch": "^0.8.4",
130
+ "babel-jest": "^29.5.0",
131
+ "eslint": "^8.37.0",
132
+ "eslint-config-prettier": "^8.8.0",
132
133
  "express": "^4.18.2",
133
- "fastify": "^4.13.0",
134
+ "fastify": "^4.15.0",
134
135
  "graphql": "^16.6.0",
135
136
  "html-validator": "^6.0.1",
136
- "jest": "^29.4.2",
137
- "jest-jasmine2": "^29.4.2",
137
+ "jest": "^29.5.0",
138
+ "jest-jasmine2": "^29.5.0",
138
139
  "koa": "^2.14.1",
139
140
  "koa-mount": "^4.0.0",
140
- "node-fetch": "^3.3.0",
141
- "prettier": "^2.8.4",
142
- "rollup": "^3.15.0",
141
+ "node-fetch": "^3.3.1",
142
+ "prettier": "^2.8.7",
143
+ "rollup": "^3.20.2",
143
144
  "rollup-plugin-gzip": "^3.1.0",
144
- "semantic-release": "^20.1.0",
145
+ "semantic-release": "^21.0.0",
145
146
  "tslib": "^2.5.0",
146
- "typedoc": "^0.23.25",
147
+ "typedoc": "^0.23.28",
147
148
  "typedoc-plugin-markdown": "^3.14.0",
148
- "typescript": "^4.9.5"
149
+ "typescript": "^5.0.3"
149
150
  },
150
151
  "resolutions": {
151
152
  "npm/libnpmversion": "^3.0.6"
@@ -245,7 +245,9 @@
245
245
  headers: {
246
246
  'content-type': 'application/json; charset=utf-8',
247
247
  },
248
- body: JSON.stringify({ query: '{ __typename }' }),
248
+ body: JSON.stringify({
249
+ query: '{ __type(name: "Run🏃Swim🏊") { name } }',
250
+ }),
249
251
  });
250
252
  ressert(res).status.toBe(200);
251
253
  }),
@@ -693,6 +695,20 @@
693
695
  });
694
696
  ressert(res).status.toBe(200);
695
697
  }),
698
+ audit('7B9B', 'SHOULD use a status code of 200 on variable coercion failure when accepting application/json', async () => {
699
+ const res = await fetchFn(await getUrl(opts.url), {
700
+ method: 'POST',
701
+ headers: {
702
+ 'content-type': 'application/json',
703
+ accept: 'application/json',
704
+ },
705
+ body: JSON.stringify({
706
+ query: 'query CoerceFailure($id: ID!){ __typename }',
707
+ variables: { id: null },
708
+ }),
709
+ });
710
+ ressert(res).status.toBe(200);
711
+ }),
696
712
  // Response application/graphql-response+json
697
713
  audit(
698
714
  // TODO: convert to MUST after watershed
@@ -841,6 +857,20 @@
841
857
  });
842
858
  await ressert(res).bodyAsExecutionResult.data.toBe(undefined);
843
859
  }),
860
+ audit('86EE', 'SHOULD use a status code of 400 on variable coercion failure when accepting application/graphql-response+json', async () => {
861
+ const res = await fetchFn(await getUrl(opts.url), {
862
+ method: 'POST',
863
+ headers: {
864
+ 'content-type': 'application/json',
865
+ accept: 'application/graphql-response+json',
866
+ },
867
+ body: JSON.stringify({
868
+ query: 'query CoerceFailure($id: ID!){ __typename }',
869
+ variables: { id: null },
870
+ }),
871
+ });
872
+ ressert(res).status.toBe(400);
873
+ }),
844
874
  // TODO: how to fail and have the data entry?
845
875
  // audit('EE52', 'MUST use 2xx status code if response contains the data entry and it is not null when accepting application/graphql-response+json'),
846
876
  // TODO: how to make an unauthorized request?
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("graphql")):"function"==typeof define&&define.amd?define(["exports","graphql"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).graphqlHttpAudits={})}(this,(function(t){"use strict";function e(t){return null===t?"null":Array.isArray(t)?"array":typeof t}function a(t,e,a){return{id:t,name:e,fn:async()=>{try{return await a(),{id:t,name:e,status:"ok"}}catch(a){if(!(a instanceof n))throw a;return{id:t,name:e,status:e.startsWith("MUST")?"error":"warn",reason:a.reason,response:a.response}}}}}class n{constructor(t,e){this.response=t,this.reason=e}}function o(t){return{status:{toBe(e){if(t.status!==e)throw new n(t,`Response status code is not ${e}`)},toBeBetween:(e,a)=>{if(!(e<=t.status&&t.status<=a))throw new n(t,`Response status is not between ${e} and ${a}`)}},header:e=>({toContain(a){var o;if(!(null===(o=t.headers.get(e))||void 0===o?void 0:o.includes(a)))throw new n(t,`Response header ${e} does not contain ${a}`)},notToContain(a){var o;if(null===(o=t.headers.get(e))||void 0===o?void 0:o.includes(a))throw new n(t,`Response header ${e} contains ${a}`)}}),bodyAsExecutionResult:{data:{async toBe(e){const a=t.clone();if((await s(t)).data!==e)throw new n(a,`Response body execution result data is not "${e}"`)}},async toHaveProperty(e){const a=t.clone();if(!(e in await s(t)))throw new n(a,`Response body execution result does not have a property "${e}"`)},async notToHaveProperty(e){const a=t.clone();if(e in await s(t))throw new n(a,`Response body execution result has a property "${e}"`)}}}}async function s(t){let e,a;try{const a=new TextDecoder("utf-8"),n=await t.arrayBuffer();e=a.decode(n)}catch(e){throw new n(t,"Response body is not UTF-8 encoded")}try{a=JSON.parse(e)}catch(e){throw new n(t,"Response body is not valid JSON")}return a}function i(t){const s=t.fetchFn||fetch;return[a("22EB","SHOULD accept application/graphql-response+json and match the content-type",(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }"})});o(e).status.toBe(200),o(e).header("content-type").toContain("application/graphql-response+json")})),a("4655","MUST accept application/json and match the content-type",(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(e).status.toBe(200),o(e).header("content-type").toContain("application/json")})),a("47DE","SHOULD accept */* and use application/json for the content-type",(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"*/*"},body:JSON.stringify({query:"{ __typename }"})});o(e).status.toBe(200),o(e).header("content-type").toContain("application/json")})),a("80D8","SHOULD assume application/json content-type when accept is missing",(async()=>{const e=new URL(await r(t.url));e.searchParams.set("query","{ __typename }");const a=await s(e.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(e).status.toBe(200);try{new TextDecoder("utf-8").decode(await e.arrayBuffer())}catch(t){throw new n(e,"Response body is not UTF-8 encoded")}})),a("BF61","MUST accept utf-8 encoded request",(async()=>{o(await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json; charset=utf-8"},body:JSON.stringify({query:"{ __typename }"})})).status.toBe(200)})),a("78D5","MUST assume utf-8 in request if encoding is unspecified",(async()=>{o(await s(await r(t.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(t.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 e=new URL(await r(t.url));e.searchParams.set("query","{ __typename }");o(await s(e.toString())).status.toBe(200)})),a("9C48","MAY NOT allow executing mutations on GET requests",(async()=>{const e=new URL(await r(t.url));e.searchParams.set("query","mutation { __typename }");o(await s(e.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(t.url),{method:"POST"})).status.toBeBetween(400,499)})),a("03D4","MUST accept application/json POST requests",(async()=>{o(await s(await r(t.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 e;const a=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json"}});(null===(e=a.headers.get("content-type"))||void 0===e?void 0:e.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(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({notquery:"{ __typename }"})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.toHaveProperty("errors")})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`4F5${i}`,`SHOULD use 400 status code on ${e(n)} {query} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(t.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 ${e(n)} {query} parameter when accepting application/json`,(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:n})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.toHaveProperty("errors")})))),a("34A2","SHOULD allow string {query} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }"})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.notToHaveProperty("errors")})),...[{obj:"ect"},0,!1,["array"]].map(((n,i)=>a(`E3E${i}`,`SHOULD use 400 status code on ${e(n)} {operationName} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(t.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 ${e(n)} {operationName} parameter when accepting application/json`,(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({operationName:n,query:"{ __typename }"})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.toHaveProperty("errors")})))),a("8161","SHOULD allow string {operationName} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({operationName:"Query",query:"query Query { __typename }"})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.notToHaveProperty("errors")})),...["variables","operationName","extensions"].flatMap(((e,n)=>[a(`94B${n}`,`SHOULD allow null {${e}} parameter when accepting application/graphql-response+json`,(async()=>{const a=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ __typename }",[e]:null})});o(a).status.toBe(200),await o(a).bodyAsExecutionResult.notToHaveProperty("errors")})),a(`022${n}`,`MUST allow null {${e}} parameter when accepting application/json`,(async()=>{const a=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",[e]: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 ${e(n)} {variables} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(t.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 ${e(n)} {variables} parameter when accepting application/json`,(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",variables:n})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.toHaveProperty("errors")})))),a("2EA1","SHOULD allow map {variables} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(t.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 e=await s(await r(t.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(e).status.toBe(200),await o(e).bodyAsExecutionResult.notToHaveProperty("errors")})),a("D6D5","MAY allow URL-encoded JSON string {variables} parameter in GETs when accepting application/graphql-response+json",(async()=>{const e=new URL(await r(t.url));e.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),e.searchParams.set("variables",JSON.stringify({name:"sometype"}));o(await s(e.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 e=new URL(await r(t.url));e.searchParams.set("query","query Type($name: String!) { __type(name: $name) { name } }"),e.searchParams.set("variables",JSON.stringify({name:"sometype"}));const a=await s(e.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 ${e(n)} {extensions} parameter when accepting application/graphql-response+json`,(async()=>{o(await s(await r(t.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 ${e(n)} {extensions} parameter when accepting application/json`,(async()=>{const e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:n})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.toHaveProperty("errors")})))),a("428F","SHOULD allow map {extensions} parameter when accepting application/graphql-response+json",(async()=>{o(await s(await r(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ __typename }",extensions:{some:"value"}})});o(e).status.toBe(200),await o(e).bodyAsExecutionResult.notToHaveProperty("errors")})),a("D477","SHOULD use 200 status code on JSON parsing failure when accepting application/json",(async()=>{o(await s(await r(t.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(t.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(t.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(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})})).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(t.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(t.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(t.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(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({qeury:"{ __typename }"})});await o(e).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(t.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(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{"})});await o(e).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(t.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(t.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 e=await s(await r(t.url),{method:"POST",headers:{"content-type":"application/json",accept:"application/graphql-response+json"},body:JSON.stringify({query:"{ 8f31403dfe404bccbb0e835f2629c6a7 }"})});await o(e).bodyAsExecutionResult.data.toBe(void 0)}))]}async function r(t){return"function"==typeof t?await t():t}async function p(t){let e="";e+=`<li><code>${t.id}</code> ${t.name}\n`,e+="<details>\n",e+=`<summary>${function(t,e=1024){if(t.length>e)return t.substring(0,e)+"...";return t}(t.reason)}</summary>\n`,e+='<pre><code class="lang-json">';const a=t.response,n={};for(const[t,e]of a.headers.entries())"date"===t?n[t]="<timestamp>":["cf-ray","server-timing"].includes(t)?n[t]="<omitted>":n[t]=e;let o,s="";try{s=await a.text(),o=JSON.parse(s)}catch(t){}return e+=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},((t,e)=>{if(null!=e&&"object"==typeof e&&!Array.isArray(e)){const t={};return Object.keys(e).sort().reverse().reduce(((t,a)=>(t[a]=e[a],t)),t)}return e}),2)+"\n",e+="</code></pre>\n",e+="</details>\n",e+="</li>\n",e}t.auditServer=async function(t){const e=i(t);return await Promise.all(e.map((({fn:t})=>t())))},t.renderAuditResultsToHTML=async function(t){const e={total:0,ok:[],warn:[],error:[]};for(const a of t)e.total++,a.status,e[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>${e.total}</b> audits in total</li>\n`,e.ok.length&&(a+=`<li><span style="font-family: monospace">✅</span> <b>${e.ok.length}</b> pass</li>\n`),e.warn.length&&(a+=`<li><span style="font-family: monospace">⚠️</span> <b>${e.warn.length}</b> warnings (optional)</li>\n`),e.error.length&&(a+=`<li><span style="font-family: monospace">❌</span> <b>${e.error.length}</b> errors (required)</li>\n`),a+="</ul>\n",a+="\n",e.ok.length){a+="<h2>Passing</h2>\n",a+="<ol>\n";for(const[,t]of e.ok.entries())a+=`<li><code>${t.id}</code> ${t.name}</li>\n`;a+="</ol>\n",a+="\n"}if(e.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[,t]of e.warn.entries())a+=await p(t);a+="</ol>\n",a+="\n"}if(e.error.length){a+="<h2>Errors</h2>\n",a+="The server <b>MUST</b> support these.\n",a+="<ol>\n";for(const[,t]of e.error.entries())a+=await p(t);a+="</ol>\n"}return a},t.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":"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}));
Binary file