graphql-http 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/lib/handler.js +30 -7
- package/lib/handler.mjs +24 -1
- package/lib/use/@netlify/functions.d.mts +14 -0
- package/lib/use/@netlify/functions.d.ts +14 -0
- package/lib/use/@netlify/functions.js +37 -0
- package/lib/use/@netlify/functions.mjs +33 -0
- package/lib/utils.d.mts +0 -7
- package/lib/utils.d.ts +0 -7
- package/lib/utils.js +1 -31
- package/lib/utils.mjs +0 -27
- package/package.json +10 -14
- package/umd/graphql-http-audits.js +2 -2
- package/umd/graphql-http-audits.min.js +1 -1
- package/umd/graphql-http-audits.min.js.gz +0 -0
- package/umd/graphql-http.js +3 -2
- package/umd/graphql-http.min.js +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/README.md
CHANGED
|
@@ -222,6 +222,16 @@ export default {
|
|
|
222
222
|
};
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
##### With [`Netlify Functions`](https://docs.netlify.com/functions/overview/)
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
import { createHandler } from 'graphql-http/lib/use/@netlify/functions'; // yarn add @netlify/functions
|
|
229
|
+
import { schema } from './previous-step';
|
|
230
|
+
|
|
231
|
+
// Create the GraphQL over HTTP native fetch handler
|
|
232
|
+
export const handler = createHandler({ schema });
|
|
233
|
+
```
|
|
234
|
+
|
|
225
235
|
#### Use the client
|
|
226
236
|
|
|
227
237
|
```js
|
package/lib/handler.js
CHANGED
|
@@ -126,7 +126,7 @@ function createHandler(options) {
|
|
|
126
126
|
if (isResponse(maybeResErrsOrArgs))
|
|
127
127
|
return maybeResErrsOrArgs;
|
|
128
128
|
else if ((0, utils_1.isExecutionResult)(maybeResErrsOrArgs) ||
|
|
129
|
-
|
|
129
|
+
areGraphQLErrors(maybeResErrsOrArgs))
|
|
130
130
|
return makeResponse(maybeResErrsOrArgs, acceptedMediaType, formatError);
|
|
131
131
|
else if (maybeResErrsOrArgs)
|
|
132
132
|
args = maybeResErrsOrArgs;
|
|
@@ -340,9 +340,9 @@ async function defaultParseRequestParams(req) {
|
|
|
340
340
|
function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
341
341
|
if (resultOrErrors instanceof Error &&
|
|
342
342
|
// because GraphQLError extends the Error class
|
|
343
|
-
!
|
|
343
|
+
!isGraphQLError(resultOrErrors)) {
|
|
344
344
|
return [
|
|
345
|
-
JSON.stringify({ errors: [formatError(resultOrErrors)] },
|
|
345
|
+
JSON.stringify({ errors: [formatError(resultOrErrors)] }, jsonErrorReplacer),
|
|
346
346
|
{
|
|
347
347
|
status: 400,
|
|
348
348
|
statusText: 'Bad Request',
|
|
@@ -352,14 +352,14 @@ function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
|
352
352
|
},
|
|
353
353
|
];
|
|
354
354
|
}
|
|
355
|
-
const errors =
|
|
355
|
+
const errors = isGraphQLError(resultOrErrors)
|
|
356
356
|
? [resultOrErrors]
|
|
357
|
-
:
|
|
357
|
+
: areGraphQLErrors(resultOrErrors)
|
|
358
358
|
? resultOrErrors
|
|
359
359
|
: null;
|
|
360
360
|
if (errors) {
|
|
361
361
|
return [
|
|
362
|
-
JSON.stringify({ errors: errors.map(formatError) },
|
|
362
|
+
JSON.stringify({ errors: errors.map(formatError) }, jsonErrorReplacer),
|
|
363
363
|
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
364
364
|
? {
|
|
365
365
|
status: 200,
|
|
@@ -377,7 +377,7 @@ function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
|
377
377
|
}
|
|
378
378
|
return [
|
|
379
379
|
JSON.stringify('errors' in resultOrErrors && resultOrErrors.errors
|
|
380
|
-
? Object.assign(Object.assign({}, resultOrErrors), { errors: resultOrErrors.errors.map(formatError) }) : resultOrErrors,
|
|
380
|
+
? Object.assign(Object.assign({}, resultOrErrors), { errors: resultOrErrors.errors.map(formatError) }) : resultOrErrors, jsonErrorReplacer),
|
|
381
381
|
{
|
|
382
382
|
status: 200,
|
|
383
383
|
statusText: 'OK',
|
|
@@ -395,3 +395,26 @@ function getHeader(req, key) {
|
|
|
395
395
|
}
|
|
396
396
|
return Object(req.headers)[key];
|
|
397
397
|
}
|
|
398
|
+
function areGraphQLErrors(obj) {
|
|
399
|
+
return (Array.isArray(obj) &&
|
|
400
|
+
obj.length > 0 &&
|
|
401
|
+
// if one item in the array is a GraphQLError, we're good
|
|
402
|
+
obj.some(isGraphQLError));
|
|
403
|
+
}
|
|
404
|
+
function isGraphQLError(obj) {
|
|
405
|
+
return obj instanceof graphql_1.GraphQLError;
|
|
406
|
+
}
|
|
407
|
+
function jsonErrorReplacer(_key,
|
|
408
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
409
|
+
val) {
|
|
410
|
+
if (val instanceof Error &&
|
|
411
|
+
// GraphQL errors implement their own stringer
|
|
412
|
+
!isGraphQLError(val)) {
|
|
413
|
+
return {
|
|
414
|
+
// name: val.name, name is included in message
|
|
415
|
+
message: val.message,
|
|
416
|
+
// stack: val.stack, can leak sensitive details
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return val;
|
|
420
|
+
}
|
package/lib/handler.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
import { validate as graphqlValidate, specifiedRules, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError, } from 'graphql';
|
|
7
|
-
import {
|
|
7
|
+
import { isAsyncIterable, isExecutionResult, isObject } from './utils.mjs';
|
|
8
8
|
/** Checks whether the passed value is the `graphql-http` server agnostic response. */
|
|
9
9
|
function isResponse(val) {
|
|
10
10
|
// TODO: make sure the contents of init match ResponseInit
|
|
@@ -391,3 +391,26 @@ function getHeader(req, key) {
|
|
|
391
391
|
}
|
|
392
392
|
return Object(req.headers)[key];
|
|
393
393
|
}
|
|
394
|
+
function areGraphQLErrors(obj) {
|
|
395
|
+
return (Array.isArray(obj) &&
|
|
396
|
+
obj.length > 0 &&
|
|
397
|
+
// if one item in the array is a GraphQLError, we're good
|
|
398
|
+
obj.some(isGraphQLError));
|
|
399
|
+
}
|
|
400
|
+
function isGraphQLError(obj) {
|
|
401
|
+
return obj instanceof GraphQLError;
|
|
402
|
+
}
|
|
403
|
+
function jsonErrorReplacer(_key,
|
|
404
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
405
|
+
val) {
|
|
406
|
+
if (val instanceof Error &&
|
|
407
|
+
// GraphQL errors implement their own stringer
|
|
408
|
+
!isGraphQLError(val)) {
|
|
409
|
+
return {
|
|
410
|
+
// name: val.name, name is included in message
|
|
411
|
+
message: val.message,
|
|
412
|
+
// stack: val.stack, can leak sensitive details
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return val;
|
|
416
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../../handler.mjs';
|
|
2
|
+
import type { Handler as NetlifyHandler, HandlerEvent as NetlifyHandlerEvent, HandlerContext as NetlifyHandlerContext } from '@netlify/functions';
|
|
3
|
+
/**
|
|
4
|
+
* Handler options when using the netlify adapter
|
|
5
|
+
*
|
|
6
|
+
* @category Server/@netlify/functions
|
|
7
|
+
*/
|
|
8
|
+
export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<NetlifyHandlerEvent, NetlifyHandlerContext, Context>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
11
|
+
*
|
|
12
|
+
* @category Server/@netlify/functions
|
|
13
|
+
*/
|
|
14
|
+
export declare function createHandler<Context extends OperationContext = undefined>(options: HandlerOptions<Context>): NetlifyHandler;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HandlerOptions as RawHandlerOptions, OperationContext } from '../../handler';
|
|
2
|
+
import type { Handler as NetlifyHandler, HandlerEvent as NetlifyHandlerEvent, HandlerContext as NetlifyHandlerContext } from '@netlify/functions';
|
|
3
|
+
/**
|
|
4
|
+
* Handler options when using the netlify adapter
|
|
5
|
+
*
|
|
6
|
+
* @category Server/@netlify/functions
|
|
7
|
+
*/
|
|
8
|
+
export type HandlerOptions<Context extends OperationContext = undefined> = RawHandlerOptions<NetlifyHandlerEvent, NetlifyHandlerContext, Context>;
|
|
9
|
+
/**
|
|
10
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
11
|
+
*
|
|
12
|
+
* @category Server/@netlify/functions
|
|
13
|
+
*/
|
|
14
|
+
export declare function createHandler<Context extends OperationContext = undefined>(options: HandlerOptions<Context>): NetlifyHandler;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createHandler = void 0;
|
|
4
|
+
const handler_1 = require("../../handler");
|
|
5
|
+
/**
|
|
6
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
7
|
+
*
|
|
8
|
+
* @category Server/@netlify/functions
|
|
9
|
+
*/
|
|
10
|
+
function createHandler(options) {
|
|
11
|
+
const handler = (0, handler_1.createHandler)(options);
|
|
12
|
+
return async function handleRequest(req, ctx) {
|
|
13
|
+
try {
|
|
14
|
+
const [body, init] = await handler({
|
|
15
|
+
method: req.httpMethod,
|
|
16
|
+
url: req.rawUrl,
|
|
17
|
+
headers: req.headers,
|
|
18
|
+
body: req.body,
|
|
19
|
+
raw: req,
|
|
20
|
+
context: ctx,
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
// if body is null, return undefined
|
|
24
|
+
body: body !== null && body !== void 0 ? body : undefined,
|
|
25
|
+
statusCode: init.status,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
// The handler shouldnt throw errors.
|
|
30
|
+
// If you wish to handle them differently, consider implementing your own request handler.
|
|
31
|
+
console.error('Internal error occurred during request handling. ' +
|
|
32
|
+
'Please check your implementation.', err);
|
|
33
|
+
return { statusCode: 500 };
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
exports.createHandler = createHandler;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHandler as createRawHandler, } from '../../handler.mjs';
|
|
2
|
+
/**
|
|
3
|
+
* Create a GraphQL over HTTP spec compliant request handler for netlify functions
|
|
4
|
+
*
|
|
5
|
+
* @category Server/@netlify/functions
|
|
6
|
+
*/
|
|
7
|
+
export function createHandler(options) {
|
|
8
|
+
const handler = createRawHandler(options);
|
|
9
|
+
return async function handleRequest(req, ctx) {
|
|
10
|
+
try {
|
|
11
|
+
const [body, init] = await handler({
|
|
12
|
+
method: req.httpMethod,
|
|
13
|
+
url: req.rawUrl,
|
|
14
|
+
headers: req.headers,
|
|
15
|
+
body: req.body,
|
|
16
|
+
raw: req,
|
|
17
|
+
context: ctx,
|
|
18
|
+
});
|
|
19
|
+
return {
|
|
20
|
+
// if body is null, return undefined
|
|
21
|
+
body: body !== null && body !== void 0 ? body : undefined,
|
|
22
|
+
statusCode: init.status,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
// The handler shouldnt throw errors.
|
|
27
|
+
// If you wish to handle them differently, consider implementing your own request handler.
|
|
28
|
+
console.error('Internal error occurred during request handling. ' +
|
|
29
|
+
'Please check your implementation.', err);
|
|
30
|
+
return { statusCode: 500 };
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
package/lib/utils.d.mts
CHANGED
|
@@ -4,18 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
import type { ExecutionResult } from 'graphql';
|
|
7
|
-
import { GraphQLError } from 'graphql';
|
|
8
7
|
/** @private */
|
|
9
8
|
export declare function extendedTypeof(val: unknown): 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'null';
|
|
10
9
|
/** @private */
|
|
11
10
|
export declare function isObject(val: unknown): val is Record<PropertyKey, any>;
|
|
12
11
|
/** @private */
|
|
13
|
-
export declare function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];
|
|
14
|
-
/** @private */
|
|
15
|
-
export declare function isGraphQLError(obj: unknown): obj is GraphQLError;
|
|
16
|
-
/** @private */
|
|
17
12
|
export declare function isExecutionResult(val: unknown): val is ExecutionResult;
|
|
18
13
|
/** @private */
|
|
19
14
|
export declare function isAsyncIterable<T = unknown>(val: unknown): val is AsyncIterable<T>;
|
|
20
|
-
/** @private */
|
|
21
|
-
export declare function jsonErrorReplacer(_key: string, val: any): any;
|
package/lib/utils.d.ts
CHANGED
|
@@ -4,18 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
import type { ExecutionResult } from 'graphql';
|
|
7
|
-
import { GraphQLError } from 'graphql';
|
|
8
7
|
/** @private */
|
|
9
8
|
export declare function extendedTypeof(val: unknown): 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'null';
|
|
10
9
|
/** @private */
|
|
11
10
|
export declare function isObject(val: unknown): val is Record<PropertyKey, any>;
|
|
12
11
|
/** @private */
|
|
13
|
-
export declare function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];
|
|
14
|
-
/** @private */
|
|
15
|
-
export declare function isGraphQLError(obj: unknown): obj is GraphQLError;
|
|
16
|
-
/** @private */
|
|
17
12
|
export declare function isExecutionResult(val: unknown): val is ExecutionResult;
|
|
18
13
|
/** @private */
|
|
19
14
|
export declare function isAsyncIterable<T = unknown>(val: unknown): val is AsyncIterable<T>;
|
|
20
|
-
/** @private */
|
|
21
|
-
export declare function jsonErrorReplacer(_key: string, val: any): any;
|
package/lib/utils.js
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.
|
|
9
|
-
const graphql_1 = require("graphql");
|
|
8
|
+
exports.isAsyncIterable = exports.isExecutionResult = exports.isObject = exports.extendedTypeof = void 0;
|
|
10
9
|
/** @private */
|
|
11
10
|
function extendedTypeof(val) {
|
|
12
11
|
if (val === null) {
|
|
@@ -24,19 +23,6 @@ function isObject(val) {
|
|
|
24
23
|
}
|
|
25
24
|
exports.isObject = isObject;
|
|
26
25
|
/** @private */
|
|
27
|
-
function areGraphQLErrors(obj) {
|
|
28
|
-
return (Array.isArray(obj) &&
|
|
29
|
-
obj.length > 0 &&
|
|
30
|
-
// if one item in the array is a GraphQLError, we're good
|
|
31
|
-
obj.some(isGraphQLError));
|
|
32
|
-
}
|
|
33
|
-
exports.areGraphQLErrors = areGraphQLErrors;
|
|
34
|
-
/** @private */
|
|
35
|
-
function isGraphQLError(obj) {
|
|
36
|
-
return obj instanceof graphql_1.GraphQLError;
|
|
37
|
-
}
|
|
38
|
-
exports.isGraphQLError = isGraphQLError;
|
|
39
|
-
/** @private */
|
|
40
26
|
function isExecutionResult(val) {
|
|
41
27
|
return (isObject(val) &&
|
|
42
28
|
('data' in val || ('data' in val && val.data == null && 'errors' in val)));
|
|
@@ -47,19 +33,3 @@ function isAsyncIterable(val) {
|
|
|
47
33
|
return typeof Object(val)[Symbol.asyncIterator] === 'function';
|
|
48
34
|
}
|
|
49
35
|
exports.isAsyncIterable = isAsyncIterable;
|
|
50
|
-
/** @private */
|
|
51
|
-
function jsonErrorReplacer(_key,
|
|
52
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
53
|
-
val) {
|
|
54
|
-
if (val instanceof Error &&
|
|
55
|
-
// GraphQL errors implement their own stringer
|
|
56
|
-
!isGraphQLError(val)) {
|
|
57
|
-
return {
|
|
58
|
-
// name: val.name, name is included in message
|
|
59
|
-
message: val.message,
|
|
60
|
-
// stack: val.stack, can leak sensitive details
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
return val;
|
|
64
|
-
}
|
|
65
|
-
exports.jsonErrorReplacer = jsonErrorReplacer;
|
package/lib/utils.mjs
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* utils
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { GraphQLError } from 'graphql';
|
|
7
6
|
/** @private */
|
|
8
7
|
export function extendedTypeof(val) {
|
|
9
8
|
if (val === null) {
|
|
@@ -19,17 +18,6 @@ export function isObject(val) {
|
|
|
19
18
|
return typeof val === 'object' && val !== null;
|
|
20
19
|
}
|
|
21
20
|
/** @private */
|
|
22
|
-
export function areGraphQLErrors(obj) {
|
|
23
|
-
return (Array.isArray(obj) &&
|
|
24
|
-
obj.length > 0 &&
|
|
25
|
-
// if one item in the array is a GraphQLError, we're good
|
|
26
|
-
obj.some(isGraphQLError));
|
|
27
|
-
}
|
|
28
|
-
/** @private */
|
|
29
|
-
export function isGraphQLError(obj) {
|
|
30
|
-
return obj instanceof GraphQLError;
|
|
31
|
-
}
|
|
32
|
-
/** @private */
|
|
33
21
|
export function isExecutionResult(val) {
|
|
34
22
|
return (isObject(val) &&
|
|
35
23
|
('data' in val || ('data' in val && val.data == null && 'errors' in val)));
|
|
@@ -38,18 +26,3 @@ export function isExecutionResult(val) {
|
|
|
38
26
|
export function isAsyncIterable(val) {
|
|
39
27
|
return typeof Object(val)[Symbol.asyncIterator] === 'function';
|
|
40
28
|
}
|
|
41
|
-
/** @private */
|
|
42
|
-
export function jsonErrorReplacer(_key,
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
val) {
|
|
45
|
-
if (val instanceof Error &&
|
|
46
|
-
// GraphQL errors implement their own stringer
|
|
47
|
-
!isGraphQLError(val)) {
|
|
48
|
-
return {
|
|
49
|
-
// name: val.name, name is included in message
|
|
50
|
-
message: val.message,
|
|
51
|
-
// stack: val.stack, can leak sensitive details
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return val;
|
|
55
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-http",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -65,6 +65,11 @@
|
|
|
65
65
|
"require": "./lib/use/fastify.js",
|
|
66
66
|
"import": "./lib/use/fastify.mjs"
|
|
67
67
|
},
|
|
68
|
+
"./lib/use/@netlify/functions": {
|
|
69
|
+
"types": "./lib/use/@netlify/functions.d.ts",
|
|
70
|
+
"require": "./lib/use/@netlify/functions.js",
|
|
71
|
+
"import": "./lib/use/@netlify/functions.mjs"
|
|
72
|
+
},
|
|
68
73
|
"./lib/use/koa": {
|
|
69
74
|
"types": "./lib/use/koa.d.ts",
|
|
70
75
|
"require": "./lib/use/koa.js",
|
|
@@ -96,7 +101,7 @@
|
|
|
96
101
|
"check:lint": "eslint 'src'",
|
|
97
102
|
"check:spell": "cspell --gitignore **/*.md",
|
|
98
103
|
"check:type": "tsc --noEmit",
|
|
99
|
-
"test": "
|
|
104
|
+
"test": "vitest",
|
|
100
105
|
"build:esm": "tsc -b tsconfig.esm.json && tsx scripts/esm-post-process.ts",
|
|
101
106
|
"build:cjs": "tsc -b tsconfig.cjs.json",
|
|
102
107
|
"build:umd": "rollup --configPlugin typescript --config rollup.config.ts",
|
|
@@ -111,14 +116,8 @@
|
|
|
111
116
|
"graphql": ">=0.11 <=16"
|
|
112
117
|
},
|
|
113
118
|
"devDependencies": {
|
|
114
|
-
"@babel/core": "^7.22.8",
|
|
115
|
-
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
|
116
|
-
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
|
|
117
|
-
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
|
118
|
-
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
|
119
|
-
"@babel/preset-env": "^7.22.7",
|
|
120
|
-
"@babel/preset-typescript": "^7.22.5",
|
|
121
119
|
"@cspell/cspell-types": "^6.31.1",
|
|
120
|
+
"@netlify/functions": "^1.6.0",
|
|
122
121
|
"@rollup/plugin-terser": "^0.4.3",
|
|
123
122
|
"@rollup/plugin-typescript": "^11.1.2",
|
|
124
123
|
"@semantic-release/changelog": "^6.0.3",
|
|
@@ -127,14 +126,12 @@
|
|
|
127
126
|
"@types/express": "^4.17.17",
|
|
128
127
|
"@types/glob": "^8.1.0",
|
|
129
128
|
"@types/html-validator": "^5.0.3",
|
|
130
|
-
"@types/jest": "^29.5.2",
|
|
131
129
|
"@types/k6": "^0.45.0",
|
|
132
130
|
"@types/koa": "^2.13.6",
|
|
133
131
|
"@types/koa-mount": "^4.0.2",
|
|
134
132
|
"@typescript-eslint/eslint-plugin": "^5.61.0",
|
|
135
133
|
"@typescript-eslint/parser": "^5.61.0",
|
|
136
134
|
"@whatwg-node/fetch": "^0.9.7",
|
|
137
|
-
"babel-jest": "^29.6.1",
|
|
138
135
|
"cspell": "^6.31.1",
|
|
139
136
|
"eslint": "^8.44.0",
|
|
140
137
|
"eslint-config-prettier": "^8.8.0",
|
|
@@ -143,8 +140,6 @@
|
|
|
143
140
|
"glob": "^10.3.2",
|
|
144
141
|
"graphql": "^16.7.1",
|
|
145
142
|
"html-validator": "^6.0.1",
|
|
146
|
-
"jest": "^29.6.1",
|
|
147
|
-
"jest-jasmine2": "^29.6.1",
|
|
148
143
|
"koa": "^2.14.2",
|
|
149
144
|
"koa-mount": "^4.0.0",
|
|
150
145
|
"node-fetch": "^3.3.1",
|
|
@@ -157,6 +152,7 @@
|
|
|
157
152
|
"typedoc": "^0.24.8",
|
|
158
153
|
"typedoc-plugin-markdown": "^3.15.3",
|
|
159
154
|
"typescript": "^5.1.6",
|
|
160
|
-
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0"
|
|
155
|
+
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0",
|
|
156
|
+
"vitest": "^0.33.0"
|
|
161
157
|
}
|
|
162
158
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports'
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.graphqlHttpAudits = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
@@ -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":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}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],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
|
package/umd/graphql-http.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports'
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.graphqlHttp = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
/** @private */
|
|
13
|
+
/** @private */
|
|
13
14
|
function isObject(val) {
|
|
14
15
|
return typeof val === 'object' && val !== null;
|
|
15
16
|
}
|
package/umd/graphql-http.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).graphqlHttp={})}(this,(function(e){"use strict";class t extends Error{constructor(e){let t,r;var o;(function(e){return"object"==typeof e&&null!==e})(o=e)&&"boolean"==typeof o.ok&&"number"==typeof o.status&&"string"==typeof o.statusText?(r=e,t="Server responded with "+e.status+": "+e.statusText):t=e instanceof Error?e.message:String(e),super(t),this.name=this.constructor.name,this.response=r}}e.NetworkError=t,e.createClient=function(e){const{credentials:r="same-origin",referrer:o,referrerPolicy:n,shouldRetry:s=(()=>!1)}=e,i=e.fetchFn||fetch,a=e.abortControllerImpl||AbortController,c=(()=>{let e=!1;const t=[];return{get disposed(){return e},onDispose:r=>e?(setTimeout((()=>r()),0),()=>{}):(t.push(r),()=>{t.splice(t.indexOf(r),1)}),dispose(){if(!e){e=!0;for(const e of[...t])e()}}}})();return{subscribe(p,l){if(c.disposed)throw new Error("Client has been disposed");const f=new a,u=c.onDispose((()=>{u(),f.abort()}));return(async()=>{var a;let c=null,u=0;for(;;){if(c){const e=await s(c,u);if(f.signal.aborted)return;if(!e)throw c;u++}try{const s="function"==typeof e.url?await e.url(p):e.url;if(f.signal.aborted)return;const c="function"==typeof e.headers?await e.headers():null!==(a=e.headers)&&void 0!==a?a:{};if(f.signal.aborted)return;let u;try{u=await i(s,{signal:f.signal,method:"POST",headers:Object.assign(Object.assign({},c),{"content-type":"application/json; charset=utf-8",accept:"application/graphql-response+json, application/json"}),credentials:r,referrer:o,referrerPolicy:n,body:JSON.stringify(p)})}catch(e){throw new t(e)}if(!u.ok)throw new t(u);if(!u.body)throw new Error("Missing response body");const d=u.headers.get("content-type");if(!d)throw new Error("Missing response content-type");if(!d.includes("application/graphql-response+json")&&!d.includes("application/json"))throw new Error(`Unsupported response content-type ${d}`);const h=await u.json();return l.next(h),f.abort()}catch(e){if(f.signal.aborted)return;if(!(e instanceof t))throw e;c=e}}})().then((()=>l.complete())).catch((e=>l.error(e))),()=>f.abort()},dispose(){c.dispose()}}}}));
|
|
Binary file
|