graphql-http 1.20.0 → 1.22.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 +13 -1
- package/lib/handler.d.mts +19 -2
- package/lib/handler.d.ts +19 -2
- package/lib/handler.js +145 -115
- package/lib/handler.mjs +137 -108
- 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/use/express.d.mts +41 -0
- package/lib/use/express.d.ts +41 -0
- package/lib/use/express.js +72 -19
- package/lib/use/express.mjs +71 -19
- package/lib/use/fastify.d.mts +42 -1
- package/lib/use/fastify.d.ts +42 -1
- package/lib/use/fastify.js +68 -11
- package/lib/use/fastify.mjs +67 -11
- package/lib/use/fetch.d.mts +50 -0
- package/lib/use/fetch.d.ts +50 -0
- package/lib/use/fetch.js +74 -9
- package/lib/use/fetch.mjs +73 -9
- package/lib/use/http.d.mts +44 -0
- package/lib/use/http.d.ts +44 -0
- package/lib/use/http.js +75 -13
- package/lib/use/http.mjs +74 -13
- package/lib/use/http2.d.mts +56 -0
- package/lib/use/http2.d.ts +56 -0
- package/lib/use/http2.js +93 -13
- package/lib/use/http2.mjs +92 -13
- package/lib/use/koa.d.mts +47 -1
- package/lib/use/koa.d.ts +47 -1
- package/lib/use/koa.js +84 -1
- package/lib/use/koa.mjs +83 -1
- package/lib/use/uWebSockets.d.mts +55 -0
- package/lib/use/uWebSockets.d.ts +55 -0
- package/lib/use/uWebSockets.js +109 -31
- package/lib/use/uWebSockets.mjs +108 -31
- 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 +28 -32
- 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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
[](https://graphql.org/conf/)
|
|
2
|
+
|
|
1
3
|
<div align="center">
|
|
2
4
|
<br />
|
|
3
5
|
|
|
@@ -222,6 +224,16 @@ export default {
|
|
|
222
224
|
};
|
|
223
225
|
```
|
|
224
226
|
|
|
227
|
+
##### With [`Netlify Functions`](https://docs.netlify.com/functions/overview/)
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
import { createHandler } from 'graphql-http/lib/use/@netlify/functions'; // yarn add @netlify/functions
|
|
231
|
+
import { schema } from './previous-step';
|
|
232
|
+
|
|
233
|
+
// Create the GraphQL over HTTP native fetch handler
|
|
234
|
+
export const handler = createHandler({ schema });
|
|
235
|
+
```
|
|
236
|
+
|
|
225
237
|
#### Use the client
|
|
226
238
|
|
|
227
239
|
```js
|
|
@@ -487,7 +499,7 @@ const client = createClient({
|
|
|
487
499
|
<summary><a href="#browser">🔗</a> Client usage in browser</summary>
|
|
488
500
|
|
|
489
501
|
```html
|
|
490
|
-
<!
|
|
502
|
+
<!doctype html>
|
|
491
503
|
<html>
|
|
492
504
|
<head>
|
|
493
505
|
<meta charset="utf-8" />
|
package/lib/handler.d.mts
CHANGED
|
@@ -102,8 +102,10 @@ export type OperationContext = Record<PropertyKey, unknown> | symbol | number |
|
|
|
102
102
|
*/
|
|
103
103
|
export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError | Error;
|
|
104
104
|
/**
|
|
105
|
-
* The request parser for an incoming GraphQL request
|
|
106
|
-
*
|
|
105
|
+
* The request parser for an incoming GraphQL request in the handler.
|
|
106
|
+
*
|
|
107
|
+
* It should parse and validate the request itself, including the request method
|
|
108
|
+
* and the content-type of the body.
|
|
107
109
|
*
|
|
108
110
|
* In case you are extending the server to handle more request types, this is the
|
|
109
111
|
* perfect place to do so.
|
|
@@ -120,6 +122,21 @@ export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError
|
|
|
120
122
|
* @category Server
|
|
121
123
|
*/
|
|
122
124
|
export type ParseRequestParams<RequestRaw = unknown, RequestContext = unknown> = (req: Request<RequestRaw, RequestContext>) => Promise<RequestParams | Response | void> | RequestParams | Response | void;
|
|
125
|
+
/**
|
|
126
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
127
|
+
* It parses and validates the request itself, including the request method and the
|
|
128
|
+
* content-type of the body.
|
|
129
|
+
*
|
|
130
|
+
* If the HTTP request itself is invalid or malformed, the function will return an
|
|
131
|
+
* appropriate {@link Response}.
|
|
132
|
+
*
|
|
133
|
+
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the
|
|
134
|
+
* function will throw an error and it is up to the user to handle and respond as
|
|
135
|
+
* they see fit.
|
|
136
|
+
*
|
|
137
|
+
* @category Server
|
|
138
|
+
*/
|
|
139
|
+
export declare function parseRequestParams<RequestRaw = unknown, RequestContext = unknown>(req: Request<RequestRaw, RequestContext>): Promise<Response | RequestParams>;
|
|
123
140
|
/** @category Server */
|
|
124
141
|
export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & {
|
|
125
142
|
contextValue?: Context;
|
package/lib/handler.d.ts
CHANGED
|
@@ -102,8 +102,10 @@ export type OperationContext = Record<PropertyKey, unknown> | symbol | number |
|
|
|
102
102
|
*/
|
|
103
103
|
export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError | Error;
|
|
104
104
|
/**
|
|
105
|
-
* The request parser for an incoming GraphQL request
|
|
106
|
-
*
|
|
105
|
+
* The request parser for an incoming GraphQL request in the handler.
|
|
106
|
+
*
|
|
107
|
+
* It should parse and validate the request itself, including the request method
|
|
108
|
+
* and the content-type of the body.
|
|
107
109
|
*
|
|
108
110
|
* In case you are extending the server to handle more request types, this is the
|
|
109
111
|
* perfect place to do so.
|
|
@@ -120,6 +122,21 @@ export type FormatError = (err: Readonly<GraphQLError | Error>) => GraphQLError
|
|
|
120
122
|
* @category Server
|
|
121
123
|
*/
|
|
122
124
|
export type ParseRequestParams<RequestRaw = unknown, RequestContext = unknown> = (req: Request<RequestRaw, RequestContext>) => Promise<RequestParams | Response | void> | RequestParams | Response | void;
|
|
125
|
+
/**
|
|
126
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
127
|
+
* It parses and validates the request itself, including the request method and the
|
|
128
|
+
* content-type of the body.
|
|
129
|
+
*
|
|
130
|
+
* If the HTTP request itself is invalid or malformed, the function will return an
|
|
131
|
+
* appropriate {@link Response}.
|
|
132
|
+
*
|
|
133
|
+
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the
|
|
134
|
+
* function will throw an error and it is up to the user to handle and respond as
|
|
135
|
+
* they see fit.
|
|
136
|
+
*
|
|
137
|
+
* @category Server
|
|
138
|
+
*/
|
|
139
|
+
export declare function parseRequestParams<RequestRaw = unknown, RequestContext = unknown>(req: Request<RequestRaw, RequestContext>): Promise<Response | RequestParams>;
|
|
123
140
|
/** @category Server */
|
|
124
141
|
export type OperationArgs<Context extends OperationContext = undefined> = ExecutionArgs & {
|
|
125
142
|
contextValue?: Context;
|
package/lib/handler.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.createHandler = void 0;
|
|
8
|
+
exports.createHandler = exports.parseRequestParams = void 0;
|
|
9
9
|
const graphql_1 = require("graphql");
|
|
10
10
|
const utils_1 = require("./utils");
|
|
11
11
|
/** Checks whether the passed value is the `graphql-http` server agnostic response. */
|
|
@@ -15,6 +15,117 @@ function isResponse(val) {
|
|
|
15
15
|
(typeof val[0] === 'string' || val[0] === null) &&
|
|
16
16
|
(0, utils_1.isObject)(val[1]));
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The GraphQL over HTTP spec compliant request parser for an incoming GraphQL request.
|
|
20
|
+
* It parses and validates the request itself, including the request method and the
|
|
21
|
+
* content-type of the body.
|
|
22
|
+
*
|
|
23
|
+
* If the HTTP request itself is invalid or malformed, the function will return an
|
|
24
|
+
* appropriate {@link Response}.
|
|
25
|
+
*
|
|
26
|
+
* If the HTTP request is valid, but is not a well-formatted GraphQL request, the
|
|
27
|
+
* function will throw an error and it is up to the user to handle and respond as
|
|
28
|
+
* they see fit.
|
|
29
|
+
*
|
|
30
|
+
* @category Server
|
|
31
|
+
*/
|
|
32
|
+
async function parseRequestParams(req) {
|
|
33
|
+
var _a, _b;
|
|
34
|
+
const method = req.method;
|
|
35
|
+
if (method !== 'GET' && method !== 'POST') {
|
|
36
|
+
return [
|
|
37
|
+
null,
|
|
38
|
+
{
|
|
39
|
+
status: 405,
|
|
40
|
+
statusText: 'Method Not Allowed',
|
|
41
|
+
headers: {
|
|
42
|
+
allow: 'GET, POST',
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
const [mediaType, charset = 'charset=utf-8', // utf-8 is assumed when not specified. this parameter is either "charset" or "boundary" (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
|
|
48
|
+
] = (getHeader(req, 'content-type') || '')
|
|
49
|
+
.replace(/\s/g, '')
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.split(';');
|
|
52
|
+
const partParams = {};
|
|
53
|
+
switch (true) {
|
|
54
|
+
case method === 'GET': {
|
|
55
|
+
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
56
|
+
try {
|
|
57
|
+
const [, search] = req.url.split('?');
|
|
58
|
+
const searchParams = new URLSearchParams(search);
|
|
59
|
+
partParams.operationName =
|
|
60
|
+
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
61
|
+
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
62
|
+
const variables = searchParams.get('variables');
|
|
63
|
+
if (variables)
|
|
64
|
+
partParams.variables = JSON.parse(variables);
|
|
65
|
+
const extensions = searchParams.get('extensions');
|
|
66
|
+
if (extensions)
|
|
67
|
+
partParams.extensions = JSON.parse(extensions);
|
|
68
|
+
}
|
|
69
|
+
catch (_c) {
|
|
70
|
+
throw new Error('Unparsable URL');
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case method === 'POST' &&
|
|
75
|
+
mediaType === 'application/json' &&
|
|
76
|
+
charset === 'charset=utf-8':
|
|
77
|
+
{
|
|
78
|
+
if (!req.body) {
|
|
79
|
+
throw new Error('Missing body');
|
|
80
|
+
}
|
|
81
|
+
let data;
|
|
82
|
+
try {
|
|
83
|
+
const body = typeof req.body === 'function' ? await req.body() : req.body;
|
|
84
|
+
data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new Error('Unparsable JSON body');
|
|
88
|
+
}
|
|
89
|
+
if (!(0, utils_1.isObject)(data)) {
|
|
90
|
+
throw new Error('JSON body must be an object');
|
|
91
|
+
}
|
|
92
|
+
partParams.operationName = data.operationName;
|
|
93
|
+
partParams.query = data.query;
|
|
94
|
+
partParams.variables = data.variables;
|
|
95
|
+
partParams.extensions = data.extensions;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
default: // graphql-http doesnt support any other content type
|
|
99
|
+
return [
|
|
100
|
+
null,
|
|
101
|
+
{
|
|
102
|
+
status: 415,
|
|
103
|
+
statusText: 'Unsupported Media Type',
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
if (partParams.query == null)
|
|
108
|
+
throw new Error('Missing query');
|
|
109
|
+
if (typeof partParams.query !== 'string')
|
|
110
|
+
throw new Error('Invalid query');
|
|
111
|
+
if (partParams.variables != null &&
|
|
112
|
+
(typeof partParams.variables !== 'object' ||
|
|
113
|
+
Array.isArray(partParams.variables))) {
|
|
114
|
+
throw new Error('Invalid variables');
|
|
115
|
+
}
|
|
116
|
+
if (partParams.operationName != null &&
|
|
117
|
+
typeof partParams.operationName !== 'string') {
|
|
118
|
+
throw new Error('Invalid operationName');
|
|
119
|
+
}
|
|
120
|
+
if (partParams.extensions != null &&
|
|
121
|
+
(typeof partParams.extensions !== 'object' ||
|
|
122
|
+
Array.isArray(partParams.extensions))) {
|
|
123
|
+
throw new Error('Invalid extensions');
|
|
124
|
+
}
|
|
125
|
+
// request parameters are checked and now complete
|
|
126
|
+
return partParams;
|
|
127
|
+
}
|
|
128
|
+
exports.parseRequestParams = parseRequestParams;
|
|
18
129
|
/**
|
|
19
130
|
* Makes a GraphQL over HTTP spec compliant server handler. The handler can
|
|
20
131
|
* be used with your favorite server library.
|
|
@@ -71,7 +182,7 @@ function isResponse(val) {
|
|
|
71
182
|
* @category Server
|
|
72
183
|
*/
|
|
73
184
|
function createHandler(options) {
|
|
74
|
-
const { schema, context, validate = graphql_1.validate, validationRules = [], execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams =
|
|
185
|
+
const { schema, context, validate = graphql_1.validate, validationRules = [], execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, parseRequestParams: optionsParseRequestParams = parseRequestParams, } = options;
|
|
75
186
|
return async function handler(req) {
|
|
76
187
|
let acceptedMediaType = null;
|
|
77
188
|
const accepts = (getHeader(req, 'accept') || '*/*')
|
|
@@ -111,9 +222,9 @@ function createHandler(options) {
|
|
|
111
222
|
}
|
|
112
223
|
let params;
|
|
113
224
|
try {
|
|
114
|
-
let paramsOrRes = await
|
|
225
|
+
let paramsOrRes = await optionsParseRequestParams(req);
|
|
115
226
|
if (!paramsOrRes)
|
|
116
|
-
paramsOrRes = await
|
|
227
|
+
paramsOrRes = await parseRequestParams(req);
|
|
117
228
|
if (isResponse(paramsOrRes))
|
|
118
229
|
return paramsOrRes;
|
|
119
230
|
params = paramsOrRes;
|
|
@@ -126,7 +237,7 @@ function createHandler(options) {
|
|
|
126
237
|
if (isResponse(maybeResErrsOrArgs))
|
|
127
238
|
return maybeResErrsOrArgs;
|
|
128
239
|
else if ((0, utils_1.isExecutionResult)(maybeResErrsOrArgs) ||
|
|
129
|
-
|
|
240
|
+
areGraphQLErrors(maybeResErrsOrArgs))
|
|
130
241
|
return makeResponse(maybeResErrsOrArgs, acceptedMediaType, formatError);
|
|
131
242
|
else if (maybeResErrsOrArgs)
|
|
132
243
|
args = maybeResErrsOrArgs;
|
|
@@ -222,110 +333,6 @@ function createHandler(options) {
|
|
|
222
333
|
};
|
|
223
334
|
}
|
|
224
335
|
exports.createHandler = createHandler;
|
|
225
|
-
/**
|
|
226
|
-
* The default request params parser. Used when no custom one is provided or if it
|
|
227
|
-
* returns nothing.
|
|
228
|
-
*
|
|
229
|
-
* Read more about it in {@link ParseRequestParams}.
|
|
230
|
-
*
|
|
231
|
-
* TODO: should graphql-http itself care about content-encoding? I'd say unzipping should happen before handler is reached
|
|
232
|
-
*/
|
|
233
|
-
async function defaultParseRequestParams(req) {
|
|
234
|
-
var _a, _b;
|
|
235
|
-
const method = req.method;
|
|
236
|
-
if (method !== 'GET' && method !== 'POST') {
|
|
237
|
-
return [
|
|
238
|
-
null,
|
|
239
|
-
{
|
|
240
|
-
status: 405,
|
|
241
|
-
statusText: 'Method Not Allowed',
|
|
242
|
-
headers: {
|
|
243
|
-
allow: 'GET, POST',
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
];
|
|
247
|
-
}
|
|
248
|
-
const [mediaType, charset = 'charset=utf-8', // utf-8 is assumed when not specified. this parameter is either "charset" or "boundary" (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length)
|
|
249
|
-
] = (getHeader(req, 'content-type') || '')
|
|
250
|
-
.replace(/\s/g, '')
|
|
251
|
-
.toLowerCase()
|
|
252
|
-
.split(';');
|
|
253
|
-
const partParams = {};
|
|
254
|
-
switch (true) {
|
|
255
|
-
case method === 'GET': {
|
|
256
|
-
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
257
|
-
try {
|
|
258
|
-
const [, search] = req.url.split('?');
|
|
259
|
-
const searchParams = new URLSearchParams(search);
|
|
260
|
-
partParams.operationName =
|
|
261
|
-
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
262
|
-
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
263
|
-
const variables = searchParams.get('variables');
|
|
264
|
-
if (variables)
|
|
265
|
-
partParams.variables = JSON.parse(variables);
|
|
266
|
-
const extensions = searchParams.get('extensions');
|
|
267
|
-
if (extensions)
|
|
268
|
-
partParams.extensions = JSON.parse(extensions);
|
|
269
|
-
}
|
|
270
|
-
catch (_c) {
|
|
271
|
-
throw new Error('Unparsable URL');
|
|
272
|
-
}
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
case method === 'POST' &&
|
|
276
|
-
mediaType === 'application/json' &&
|
|
277
|
-
charset === 'charset=utf-8':
|
|
278
|
-
{
|
|
279
|
-
if (!req.body) {
|
|
280
|
-
throw new Error('Missing body');
|
|
281
|
-
}
|
|
282
|
-
let data;
|
|
283
|
-
try {
|
|
284
|
-
const body = typeof req.body === 'function' ? await req.body() : req.body;
|
|
285
|
-
data = typeof body === 'string' ? JSON.parse(body) : body;
|
|
286
|
-
}
|
|
287
|
-
catch (err) {
|
|
288
|
-
throw new Error('Unparsable JSON body');
|
|
289
|
-
}
|
|
290
|
-
if (!(0, utils_1.isObject)(data)) {
|
|
291
|
-
throw new Error('JSON body must be an object');
|
|
292
|
-
}
|
|
293
|
-
partParams.operationName = data.operationName;
|
|
294
|
-
partParams.query = data.query;
|
|
295
|
-
partParams.variables = data.variables;
|
|
296
|
-
partParams.extensions = data.extensions;
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
default: // graphql-http doesnt support any other content type
|
|
300
|
-
return [
|
|
301
|
-
null,
|
|
302
|
-
{
|
|
303
|
-
status: 415,
|
|
304
|
-
statusText: 'Unsupported Media Type',
|
|
305
|
-
},
|
|
306
|
-
];
|
|
307
|
-
}
|
|
308
|
-
if (partParams.query == null)
|
|
309
|
-
throw new Error('Missing query');
|
|
310
|
-
if (typeof partParams.query !== 'string')
|
|
311
|
-
throw new Error('Invalid query');
|
|
312
|
-
if (partParams.variables != null &&
|
|
313
|
-
(typeof partParams.variables !== 'object' ||
|
|
314
|
-
Array.isArray(partParams.variables))) {
|
|
315
|
-
throw new Error('Invalid variables');
|
|
316
|
-
}
|
|
317
|
-
if (partParams.operationName != null &&
|
|
318
|
-
typeof partParams.operationName !== 'string') {
|
|
319
|
-
throw new Error('Invalid operationName');
|
|
320
|
-
}
|
|
321
|
-
if (partParams.extensions != null &&
|
|
322
|
-
(typeof partParams.extensions !== 'object' ||
|
|
323
|
-
Array.isArray(partParams.extensions))) {
|
|
324
|
-
throw new Error('Invalid extensions');
|
|
325
|
-
}
|
|
326
|
-
// request parameters are checked and now complete
|
|
327
|
-
return partParams;
|
|
328
|
-
}
|
|
329
336
|
/**
|
|
330
337
|
* Creates an appropriate GraphQL over HTTP response following the provided arguments.
|
|
331
338
|
*
|
|
@@ -340,9 +347,9 @@ async function defaultParseRequestParams(req) {
|
|
|
340
347
|
function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
341
348
|
if (resultOrErrors instanceof Error &&
|
|
342
349
|
// because GraphQLError extends the Error class
|
|
343
|
-
!
|
|
350
|
+
!isGraphQLError(resultOrErrors)) {
|
|
344
351
|
return [
|
|
345
|
-
JSON.stringify({ errors: [formatError(resultOrErrors)] },
|
|
352
|
+
JSON.stringify({ errors: [formatError(resultOrErrors)] }, jsonErrorReplacer),
|
|
346
353
|
{
|
|
347
354
|
status: 400,
|
|
348
355
|
statusText: 'Bad Request',
|
|
@@ -352,14 +359,14 @@ function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
|
352
359
|
},
|
|
353
360
|
];
|
|
354
361
|
}
|
|
355
|
-
const errors =
|
|
362
|
+
const errors = isGraphQLError(resultOrErrors)
|
|
356
363
|
? [resultOrErrors]
|
|
357
|
-
:
|
|
364
|
+
: areGraphQLErrors(resultOrErrors)
|
|
358
365
|
? resultOrErrors
|
|
359
366
|
: null;
|
|
360
367
|
if (errors) {
|
|
361
368
|
return [
|
|
362
|
-
JSON.stringify({ errors: errors.map(formatError) },
|
|
369
|
+
JSON.stringify({ errors: errors.map(formatError) }, jsonErrorReplacer),
|
|
363
370
|
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
364
371
|
? {
|
|
365
372
|
status: 200,
|
|
@@ -377,7 +384,7 @@ function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
|
|
|
377
384
|
}
|
|
378
385
|
return [
|
|
379
386
|
JSON.stringify('errors' in resultOrErrors && resultOrErrors.errors
|
|
380
|
-
? Object.assign(Object.assign({}, resultOrErrors), { errors: resultOrErrors.errors.map(formatError) }) : resultOrErrors,
|
|
387
|
+
? Object.assign(Object.assign({}, resultOrErrors), { errors: resultOrErrors.errors.map(formatError) }) : resultOrErrors, jsonErrorReplacer),
|
|
381
388
|
{
|
|
382
389
|
status: 200,
|
|
383
390
|
statusText: 'OK',
|
|
@@ -395,3 +402,26 @@ function getHeader(req, key) {
|
|
|
395
402
|
}
|
|
396
403
|
return Object(req.headers)[key];
|
|
397
404
|
}
|
|
405
|
+
function areGraphQLErrors(obj) {
|
|
406
|
+
return (Array.isArray(obj) &&
|
|
407
|
+
obj.length > 0 &&
|
|
408
|
+
// if one item in the array is a GraphQLError, we're good
|
|
409
|
+
obj.some(isGraphQLError));
|
|
410
|
+
}
|
|
411
|
+
function isGraphQLError(obj) {
|
|
412
|
+
return obj instanceof graphql_1.GraphQLError;
|
|
413
|
+
}
|
|
414
|
+
function jsonErrorReplacer(_key,
|
|
415
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
416
|
+
val) {
|
|
417
|
+
if (val instanceof Error &&
|
|
418
|
+
// GraphQL errors implement their own stringer
|
|
419
|
+
!isGraphQLError(val)) {
|
|
420
|
+
return {
|
|
421
|
+
// name: val.name, name is included in message
|
|
422
|
+
message: val.message,
|
|
423
|
+
// stack: val.stack, can leak sensitive details
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
return val;
|
|
427
|
+
}
|