graphql-http 1.17.1 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 { areGraphQLErrors, isAsyncIterable, isExecutionResult, isGraphQLError, isObject, } from './utils.mjs';
7
+ import { areGraphQLErrors, isAsyncIterable, isExecutionResult, isGraphQLError, isObject, jsonErrorReplacer, } from './utils.mjs';
8
8
  /**
9
9
  * Checks whether the passed value is the `graphql-http` server agnostic response.
10
10
  *
@@ -72,7 +72,7 @@ export function isResponse(val) {
72
72
  * @category Server
73
73
  */
74
74
  export function createHandler(options) {
75
- const { schema, context, validate = graphqlValidate, validationRules = [], execute = graphqlExecute, parse = graphqlParse, getOperationAST = graphqlGetOperationAST, rootValue, onSubscribe, onOperation, } = options;
75
+ const { schema, context, validate = graphqlValidate, validationRules = [], execute = graphqlExecute, parse = graphqlParse, getOperationAST = graphqlGetOperationAST, rootValue, onSubscribe, onOperation, formatError = (err) => err, } = options;
76
76
  return async function handler(req) {
77
77
  var _a, _b;
78
78
  const method = req.method;
@@ -173,6 +173,10 @@ export function createHandler(options) {
173
173
  Array.isArray(partParams.variables))) {
174
174
  throw new Error('Invalid variables');
175
175
  }
176
+ if (partParams.operationName != null &&
177
+ typeof partParams.operationName !== 'string') {
178
+ throw new Error('Invalid operationName');
179
+ }
176
180
  if (partParams.extensions != null &&
177
181
  (typeof partParams.extensions !== 'object' ||
178
182
  Array.isArray(partParams.extensions))) {
@@ -182,7 +186,7 @@ export function createHandler(options) {
182
186
  params = partParams;
183
187
  }
184
188
  catch (err) {
185
- return makeResponse(new GraphQLError(err.message), acceptedMediaType);
189
+ return makeResponse(err, acceptedMediaType, formatError);
186
190
  }
187
191
  let args;
188
192
  const maybeResErrsOrArgs = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, params));
@@ -190,7 +194,7 @@ export function createHandler(options) {
190
194
  return maybeResErrsOrArgs;
191
195
  else if (isExecutionResult(maybeResErrsOrArgs) ||
192
196
  areGraphQLErrors(maybeResErrsOrArgs))
193
- return makeResponse(maybeResErrsOrArgs, acceptedMediaType);
197
+ return makeResponse(maybeResErrsOrArgs, acceptedMediaType, formatError);
194
198
  else if (maybeResErrsOrArgs)
195
199
  args = maybeResErrsOrArgs;
196
200
  else {
@@ -202,7 +206,7 @@ export function createHandler(options) {
202
206
  document = parse(query);
203
207
  }
204
208
  catch (err) {
205
- return makeResponse(err, acceptedMediaType);
209
+ return makeResponse(err, acceptedMediaType, formatError);
206
210
  }
207
211
  const resOrContext = typeof context === 'function' ? await context(req, params) : context;
208
212
  if (isResponse(resOrContext))
@@ -231,7 +235,7 @@ export function createHandler(options) {
231
235
  }
232
236
  const validationErrs = validate(args.schema, args.document, rules);
233
237
  if (validationErrs.length) {
234
- return makeResponse(validationErrs, acceptedMediaType);
238
+ return makeResponse(validationErrs, acceptedMediaType, formatError);
235
239
  }
236
240
  }
237
241
  let operation;
@@ -242,10 +246,10 @@ export function createHandler(options) {
242
246
  operation = ast.operation;
243
247
  }
244
248
  catch (_d) {
245
- return makeResponse(new GraphQLError('Unable to detect operation AST'), acceptedMediaType);
249
+ return makeResponse(new GraphQLError('Unable to detect operation AST'), acceptedMediaType, formatError);
246
250
  }
247
251
  if (operation === 'subscription') {
248
- return makeResponse(new GraphQLError('Subscriptions are not supported'), acceptedMediaType);
252
+ return makeResponse(new GraphQLError('Subscriptions are not supported'), acceptedMediaType, formatError);
249
253
  }
250
254
  // mutations cannot happen over GETs
251
255
  // https://graphql.github.io/graphql-over-http/draft/#sel-CALFJRPAAELBAAxwP
@@ -279,9 +283,9 @@ export function createHandler(options) {
279
283
  else if (maybeResponseOrResult)
280
284
  result = maybeResponseOrResult;
281
285
  if (isAsyncIterable(result)) {
282
- return makeResponse(new GraphQLError('Subscriptions are not supported'), acceptedMediaType);
286
+ return makeResponse(new GraphQLError('Subscriptions are not supported'), acceptedMediaType, formatError);
283
287
  }
284
- return makeResponse(result, acceptedMediaType);
288
+ return makeResponse(result, acceptedMediaType, formatError);
285
289
  };
286
290
  }
287
291
  /**
@@ -321,12 +325,29 @@ export function getAcceptableMediaType(acceptHeader) {
321
325
  *
322
326
  * If the first argument is an `ExecutionResult`, the operation will be treated as "successful".
323
327
  *
324
- * If the first argument is _any_ object without the `data` field, it will be treated as an error (as per the spec)
325
- * and the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
328
+ * If the first argument is (an array of) `GraphQLError`, or an `ExecutionResult` without the `data` field, it will be treated
329
+ * the response will be constructed with the help of `acceptedMediaType` complying with the GraphQL over HTTP spec.
330
+ *
331
+ * If the first argument is an `Error`, the operation will be treated as a bad request responding with `400: Bad Request` and the
332
+ * error will be present in the `ExecutionResult` style.
326
333
  *
327
334
  * @category Server
328
335
  */
329
- export function makeResponse(resultOrErrors, acceptedMediaType) {
336
+ export function makeResponse(resultOrErrors, acceptedMediaType, formatError) {
337
+ if (resultOrErrors instanceof Error &&
338
+ // because GraphQLError extends the Error class
339
+ !isGraphQLError(resultOrErrors)) {
340
+ return [
341
+ JSON.stringify({ errors: [formatError(resultOrErrors)] }, jsonErrorReplacer),
342
+ {
343
+ status: 400,
344
+ statusText: 'Bad Request',
345
+ headers: {
346
+ 'content-type': 'application/json; charset=utf-8',
347
+ },
348
+ },
349
+ ];
350
+ }
330
351
  const errors = isGraphQLError(resultOrErrors)
331
352
  ? [resultOrErrors]
332
353
  : areGraphQLErrors(resultOrErrors)
@@ -334,7 +355,7 @@ export function makeResponse(resultOrErrors, acceptedMediaType) {
334
355
  : null;
335
356
  if (errors) {
336
357
  return [
337
- JSON.stringify({ errors }),
358
+ JSON.stringify({ errors: errors.map(formatError) }, jsonErrorReplacer),
338
359
  Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
339
360
  ? {
340
361
  status: 200,
@@ -351,7 +372,8 @@ export function makeResponse(resultOrErrors, acceptedMediaType) {
351
372
  ];
352
373
  }
353
374
  return [
354
- JSON.stringify(resultOrErrors),
375
+ JSON.stringify('errors' in resultOrErrors && resultOrErrors.errors
376
+ ? Object.assign(Object.assign({}, resultOrErrors), { errors: resultOrErrors.errors.map(formatError) }) : resultOrErrors, jsonErrorReplacer),
355
377
  {
356
378
  status: 200,
357
379
  statusText: 'OK',
package/lib/utils.d.mts CHANGED
@@ -17,3 +17,5 @@ export declare function isGraphQLError(obj: unknown): obj is GraphQLError;
17
17
  export declare function isExecutionResult(val: unknown): val is ExecutionResult;
18
18
  /** @private */
19
19
  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
@@ -17,3 +17,5 @@ export declare function isGraphQLError(obj: unknown): obj is GraphQLError;
17
17
  export declare function isExecutionResult(val: unknown): val is ExecutionResult;
18
18
  /** @private */
19
19
  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,7 +5,7 @@
5
5
  *
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.isAsyncIterable = exports.isExecutionResult = exports.isGraphQLError = exports.areGraphQLErrors = exports.isObject = exports.extendedTypeof = void 0;
8
+ exports.jsonErrorReplacer = exports.isAsyncIterable = exports.isExecutionResult = exports.isGraphQLError = exports.areGraphQLErrors = exports.isObject = exports.extendedTypeof = void 0;
9
9
  const graphql_1 = require("graphql");
10
10
  /** @private */
11
11
  function extendedTypeof(val) {
@@ -47,3 +47,17 @@ function isAsyncIterable(val) {
47
47
  return typeof Object(val)[Symbol.asyncIterator] === 'function';
48
48
  }
49
49
  exports.isAsyncIterable = isAsyncIterable;
50
+ /** @private */
51
+ function jsonErrorReplacer(_key, val) {
52
+ if (val instanceof Error &&
53
+ // GraphQL errors implement their own stringer
54
+ !isGraphQLError(val)) {
55
+ return {
56
+ // name: val.name, name is included in message
57
+ message: val.message,
58
+ // stack: val.stack, can leak sensitive details
59
+ };
60
+ }
61
+ return val;
62
+ }
63
+ exports.jsonErrorReplacer = jsonErrorReplacer;
package/lib/utils.mjs CHANGED
@@ -38,3 +38,16 @@ export function isExecutionResult(val) {
38
38
  export function isAsyncIterable(val) {
39
39
  return typeof Object(val)[Symbol.asyncIterator] === 'function';
40
40
  }
41
+ /** @private */
42
+ export function jsonErrorReplacer(_key, val) {
43
+ if (val instanceof Error &&
44
+ // GraphQL errors implement their own stringer
45
+ !isGraphQLError(val)) {
46
+ return {
47
+ // name: val.name, name is included in message
48
+ message: val.message,
49
+ // stack: val.stack, can leak sensitive details
50
+ };
51
+ }
52
+ return val;
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphql-http",
3
- "version": "1.17.1",
3
+ "version": "1.18.0",
4
4
  "description": "Simple, pluggable, zero-dependency, GraphQL over HTTP spec compliant server, client and audit suite.",
5
5
  "keywords": [
6
6
  "graphql",
@@ -113,8 +113,8 @@
113
113
  "@babel/plugin-proposal-optional-chaining": "^7.21.0",
114
114
  "@babel/preset-env": "^7.21.4",
115
115
  "@babel/preset-typescript": "^7.21.4",
116
- "@rollup/plugin-terser": "^0.4.0",
117
- "@rollup/plugin-typescript": "^11.0.0",
116
+ "@rollup/plugin-terser": "^0.4.1",
117
+ "@rollup/plugin-typescript": "^11.1.0",
118
118
  "@semantic-release/changelog": "^6.0.3",
119
119
  "@semantic-release/git": "^10.0.1",
120
120
  "@types/eslint": "^8.37.0",
@@ -124,29 +124,30 @@
124
124
  "@types/jest": "^29.5.0",
125
125
  "@types/koa": "^2.13.6",
126
126
  "@types/koa-mount": "^4.0.2",
127
- "@typescript-eslint/eslint-plugin": "^5.57.0",
128
- "@typescript-eslint/parser": "^5.57.0",
127
+ "@typescript-eslint/eslint-plugin": "^5.58.0",
128
+ "@typescript-eslint/parser": "^5.58.0",
129
129
  "@whatwg-node/fetch": "^0.8.4",
130
130
  "babel-jest": "^29.5.0",
131
- "eslint": "^8.37.0",
131
+ "eslint": "^8.38.0",
132
132
  "eslint-config-prettier": "^8.8.0",
133
133
  "express": "^4.18.2",
134
134
  "fastify": "^4.15.0",
135
+ "glob": "^10.0.0",
135
136
  "graphql": "^16.6.0",
136
137
  "html-validator": "^6.0.1",
137
138
  "jest": "^29.5.0",
138
139
  "jest-jasmine2": "^29.5.0",
139
- "koa": "^2.14.1",
140
+ "koa": "^2.14.2",
140
141
  "koa-mount": "^4.0.0",
141
142
  "node-fetch": "^3.3.1",
142
143
  "prettier": "^2.8.7",
143
144
  "rollup": "^3.20.2",
144
145
  "rollup-plugin-gzip": "^3.1.0",
145
- "semantic-release": "^21.0.0",
146
+ "semantic-release": "^21.0.1",
146
147
  "tslib": "^2.5.0",
147
- "typedoc": "^0.23.28",
148
- "typedoc-plugin-markdown": "^3.14.0",
149
- "typescript": "^5.0.3"
148
+ "typedoc": "^0.24.1",
149
+ "typedoc-plugin-markdown": "^3.15.1",
150
+ "typescript": "^5.0.4"
150
151
  },
151
152
  "resolutions": {
152
153
  "npm/libnpmversion": "^3.0.6"