graphql-http 1.0.0 → 1.1.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 +51 -1
- package/lib/client.d.ts +11 -3
- package/lib/client.js +1 -1
- package/lib/client.mjs +1 -1
- package/lib/common.d.ts +15 -5
- package/lib/common.js +3 -1
- package/lib/common.mjs +3 -1
- package/lib/handler.d.ts +13 -2
- package/lib/handler.js +80 -36
- package/lib/handler.mjs +79 -35
- package/lib/utils.d.ts +3 -0
- package/lib/utils.js +10 -1
- package/lib/utils.mjs +8 -0
- package/package.json +2 -2
- package/umd/graphql-http.js +4 -2
- package/umd/graphql-http.min.js +1 -1
- package/umd/graphql-http.min.js.gz +0 -0
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
<h3>graphql-http</h3>
|
|
5
5
|
|
|
6
|
-
<h6>Simple,
|
|
6
|
+
<h6>Simple, pluggable, zero-dependency, <a href="https://graphql.github.io/graphql-over-http">GraphQL over HTTP Protocol</a> compliant server and client.</h6>
|
|
7
7
|
|
|
8
8
|
[](https://github.com/enisdenjo/graphql-http/actions?query=workflow%3A%22Continuous+integration%22) [](https://www.npmjs.com/package/graphql-http)
|
|
9
9
|
|
|
@@ -207,6 +207,41 @@ fastify.listen(4000);
|
|
|
207
207
|
console.log('Listening to port 4000');
|
|
208
208
|
```
|
|
209
209
|
|
|
210
|
+
##### With [`Deno`](https://deno.land/)
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
import { serve } from 'https://deno.land/std@0.151.0/http/server.ts';
|
|
214
|
+
import { createHandler } from 'https://esm.sh/graphql-http';
|
|
215
|
+
import { schema } from './previous-step';
|
|
216
|
+
|
|
217
|
+
const handler = createHandler<Request>({ schema });
|
|
218
|
+
|
|
219
|
+
await serve(
|
|
220
|
+
async (req: Request) => {
|
|
221
|
+
const [path, _search] = req.url.split('?');
|
|
222
|
+
if (!path.endsWith('/graphql')) {
|
|
223
|
+
return new Response(null, { status: 404, statusText: 'Not Found' });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const headers: Record<string, string> = {};
|
|
227
|
+
req.headers.forEach((value, key) => (headers[key] = value));
|
|
228
|
+
const [body, init] = await handler({
|
|
229
|
+
url: req.url,
|
|
230
|
+
method: req.method,
|
|
231
|
+
headers,
|
|
232
|
+
body: await req.text(),
|
|
233
|
+
raw: req,
|
|
234
|
+
});
|
|
235
|
+
return new Response(body, init);
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
port: 4000,
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// Listening to port 4000
|
|
243
|
+
```
|
|
244
|
+
|
|
210
245
|
#### Use the client
|
|
211
246
|
|
|
212
247
|
```js
|
|
@@ -515,6 +550,21 @@ const client = createClient({
|
|
|
515
550
|
|
|
516
551
|
</details>
|
|
517
552
|
|
|
553
|
+
<details id="deno-client">
|
|
554
|
+
<summary><a href="#deno-client">🔗</a> Client usage in Deno</summary>
|
|
555
|
+
|
|
556
|
+
```js
|
|
557
|
+
import { createClient } from 'graphql-http';
|
|
558
|
+
|
|
559
|
+
const client = createClient({
|
|
560
|
+
url: 'http://deno.earth:4000/graphql',
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// consider other recipes for usage inspiration
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
</details>
|
|
567
|
+
|
|
518
568
|
<details id="auth">
|
|
519
569
|
<summary><a href="#auth">🔗</a> Server handler usage with authentication</summary>
|
|
520
570
|
|
package/lib/client.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* client
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ExecutionResult } from 'graphql';
|
|
6
|
+
import type { ExecutionResult } from 'graphql';
|
|
7
7
|
import { RequestParams, Sink } from './common';
|
|
8
8
|
/** This file is the entry point for browsers, re-export common elements. */
|
|
9
9
|
export * from './common';
|
|
@@ -18,8 +18,11 @@ export interface ClientOptions {
|
|
|
18
18
|
*
|
|
19
19
|
* A good use-case for having a function is when using the URL for authentication,
|
|
20
20
|
* where subsequent requests (due to auth) may have a refreshed identity token.
|
|
21
|
+
*
|
|
22
|
+
* Function receives the request params. Useful for example, to ease up debugging and DevTools
|
|
23
|
+
* navigation you might want to use the operation name in the URL's search params (`/graphql?MyQuery`).
|
|
21
24
|
*/
|
|
22
|
-
url: string | (() => Promise<string> | string);
|
|
25
|
+
url: string | ((request: RequestParams) => Promise<string> | string);
|
|
23
26
|
/**
|
|
24
27
|
* Indicates whether the user agent should send cookies from the other domain in the case
|
|
25
28
|
* of cross-origin requests.
|
|
@@ -140,7 +143,12 @@ export declare class NetworkError<Response extends ResponseLike = ResponseLike>
|
|
|
140
143
|
response: Response | undefined;
|
|
141
144
|
constructor(msgOrErrOrResponse: string | Error | Response);
|
|
142
145
|
}
|
|
143
|
-
|
|
146
|
+
/**
|
|
147
|
+
* Concrete interface a response needs to implement for the client.
|
|
148
|
+
*
|
|
149
|
+
* @category Client
|
|
150
|
+
*/
|
|
151
|
+
export interface ResponseLike {
|
|
144
152
|
readonly ok: boolean;
|
|
145
153
|
readonly status: number;
|
|
146
154
|
readonly statusText: string;
|
package/lib/client.js
CHANGED
package/lib/client.mjs
CHANGED
package/lib/common.d.ts
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* The incoming request headers the implementing server should provide.
|
|
8
8
|
*
|
|
9
9
|
* @category Common
|
|
10
10
|
*/
|
|
11
|
-
export interface
|
|
11
|
+
export interface RequestHeaders {
|
|
12
12
|
accept?: string | undefined;
|
|
13
13
|
allow?: string | undefined;
|
|
14
14
|
'content-type'?: string | undefined;
|
|
@@ -29,7 +29,7 @@ export interface Headers {
|
|
|
29
29
|
export interface Request<RawRequest> {
|
|
30
30
|
readonly method: string;
|
|
31
31
|
readonly url: string;
|
|
32
|
-
readonly headers:
|
|
32
|
+
readonly headers: RequestHeaders;
|
|
33
33
|
readonly body: string | Record<string, unknown> | null;
|
|
34
34
|
/**
|
|
35
35
|
* The raw request itself from the implementing server.
|
|
@@ -52,6 +52,16 @@ export interface RequestParams {
|
|
|
52
52
|
variables?: Record<string, unknown> | undefined;
|
|
53
53
|
extensions?: Record<string, unknown> | undefined;
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The response headers that get returned from graphql-http.
|
|
57
|
+
*
|
|
58
|
+
* @category Common
|
|
59
|
+
*/
|
|
60
|
+
export declare type ResponseHeaders = {
|
|
61
|
+
accept?: string;
|
|
62
|
+
allow?: string;
|
|
63
|
+
'content-type'?: string;
|
|
64
|
+
} & Record<string, string>;
|
|
55
65
|
/**
|
|
56
66
|
* Server agnostic response body returned from `graphql-http` needing
|
|
57
67
|
* to be coerced to the server implementation in use.
|
|
@@ -67,8 +77,8 @@ export declare type ResponseBody = string;
|
|
|
67
77
|
*/
|
|
68
78
|
export interface ResponseInit {
|
|
69
79
|
readonly status: number;
|
|
70
|
-
readonly statusText
|
|
71
|
-
readonly headers?:
|
|
80
|
+
readonly statusText: string;
|
|
81
|
+
readonly headers?: ResponseHeaders;
|
|
72
82
|
}
|
|
73
83
|
/**
|
|
74
84
|
* Server agnostic response returned from `graphql-http` containing the
|
package/lib/common.js
CHANGED
|
@@ -14,6 +14,8 @@ const utils_1 = require("./utils");
|
|
|
14
14
|
*/
|
|
15
15
|
function isResponse(val) {
|
|
16
16
|
// TODO: make sure the contents of init match ResponseInit
|
|
17
|
-
return Array.isArray(val) &&
|
|
17
|
+
return (Array.isArray(val) &&
|
|
18
|
+
(typeof val[0] === 'string' || val[0] === null) &&
|
|
19
|
+
(0, utils_1.isObject)(val[1]));
|
|
18
20
|
}
|
|
19
21
|
exports.isResponse = isResponse;
|
package/lib/common.mjs
CHANGED
|
@@ -11,5 +11,7 @@ import { isObject } from './utils.mjs';
|
|
|
11
11
|
*/
|
|
12
12
|
export function isResponse(val) {
|
|
13
13
|
// TODO: make sure the contents of init match ResponseInit
|
|
14
|
-
return Array.isArray(val) &&
|
|
14
|
+
return (Array.isArray(val) &&
|
|
15
|
+
(typeof val[0] === 'string' || val[0] === null) &&
|
|
16
|
+
isObject(val[1]));
|
|
15
17
|
}
|
package/lib/handler.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* handler
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { ExecutionArgs, ExecutionResult, GraphQLSchema, validate as graphqlValidate, execute as graphqlExecute } from 'graphql';
|
|
6
|
+
import { ExecutionArgs, ExecutionResult, GraphQLSchema, validate as graphqlValidate, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, GraphQLError } from 'graphql';
|
|
7
7
|
import { Request, RequestParams, Response } from './common';
|
|
8
8
|
/**
|
|
9
9
|
* A concrete GraphQL execution context value type.
|
|
@@ -51,6 +51,14 @@ export interface HandlerOptions<RawRequest = unknown> {
|
|
|
51
51
|
* used to execute the query and mutation operations.
|
|
52
52
|
*/
|
|
53
53
|
execute?: typeof graphqlExecute;
|
|
54
|
+
/**
|
|
55
|
+
* GraphQL parse function allowing you to apply a custom parser.
|
|
56
|
+
*/
|
|
57
|
+
parse?: typeof graphqlParse;
|
|
58
|
+
/**
|
|
59
|
+
* GraphQL operation AST getter used for detecting the operation type.
|
|
60
|
+
*/
|
|
61
|
+
getOperationAST?: typeof graphqlGetOperationAST;
|
|
54
62
|
/**
|
|
55
63
|
* The subscribe callback executed right after processing the request
|
|
56
64
|
* before proceeding with the GraphQL operation execution.
|
|
@@ -59,6 +67,9 @@ export interface HandlerOptions<RawRequest = unknown> {
|
|
|
59
67
|
* trying to build one internally. In this case, you are responsible for providing
|
|
60
68
|
* a ready set of arguments which will be directly plugged in the operation execution.
|
|
61
69
|
*
|
|
70
|
+
* If you return an array of `GraphQLError` from the callback, they will be reported
|
|
71
|
+
* to the client while complying with the spec.
|
|
72
|
+
*
|
|
62
73
|
* Omitting the fields `contextValue` from the returned `ExecutionArgs` will use the
|
|
63
74
|
* provided `context` option, if available.
|
|
64
75
|
*
|
|
@@ -70,7 +81,7 @@ export interface HandlerOptions<RawRequest = unknown> {
|
|
|
70
81
|
* you should do by returning a `Request` argument which will stop
|
|
71
82
|
* further execution.
|
|
72
83
|
*/
|
|
73
|
-
onSubscribe?: (req: Request<RawRequest>, params: RequestParams) => Promise<ExecutionArgs | Response | void> | ExecutionArgs | Response | void;
|
|
84
|
+
onSubscribe?: (req: Request<RawRequest>, params: RequestParams) => Promise<ExecutionArgs | GraphQLError[] | Response | void> | ExecutionArgs | GraphQLError[] | Response | void;
|
|
74
85
|
/**
|
|
75
86
|
* Executed after the operation call resolves.
|
|
76
87
|
*
|
package/lib/handler.js
CHANGED
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
exports.createHandler = void 0;
|
|
9
9
|
const graphql_1 = require("graphql");
|
|
10
10
|
const common_1 = require("./common");
|
|
11
|
+
const utils_1 = require("./utils");
|
|
11
12
|
/**
|
|
12
13
|
* Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
|
|
13
14
|
* be used with your favourite server library.
|
|
@@ -64,7 +65,7 @@ const common_1 = require("./common");
|
|
|
64
65
|
* @category Server
|
|
65
66
|
*/
|
|
66
67
|
function createHandler(options) {
|
|
67
|
-
const { schema, context, validate = graphql_1.validate, execute = graphql_1.execute, onSubscribe, onOperation, } = options;
|
|
68
|
+
const { schema, context, validate = graphql_1.validate, execute = graphql_1.execute, parse = graphql_1.parse, getOperationAST = graphql_1.getOperationAST, onSubscribe, onOperation, } = options;
|
|
68
69
|
return async function handler(req) {
|
|
69
70
|
var _a, _b;
|
|
70
71
|
const method = req.method;
|
|
@@ -127,14 +128,15 @@ function createHandler(options) {
|
|
|
127
128
|
case method === 'GET': {
|
|
128
129
|
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
129
130
|
try {
|
|
130
|
-
const
|
|
131
|
+
const [, search] = req.url.split('?');
|
|
132
|
+
const searchParams = new URLSearchParams(search);
|
|
131
133
|
partParams.operationName =
|
|
132
|
-
(_a =
|
|
133
|
-
partParams.query = (_b =
|
|
134
|
-
const variables =
|
|
134
|
+
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
135
|
+
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
136
|
+
const variables = searchParams.get('variables');
|
|
135
137
|
if (variables)
|
|
136
138
|
partParams.variables = JSON.parse(variables);
|
|
137
|
-
const extensions =
|
|
139
|
+
const extensions = searchParams.get('extensions');
|
|
138
140
|
if (extensions)
|
|
139
141
|
partParams.extensions = JSON.parse(extensions);
|
|
140
142
|
}
|
|
@@ -189,33 +191,64 @@ function createHandler(options) {
|
|
|
189
191
|
catch (err) {
|
|
190
192
|
return [
|
|
191
193
|
err.message,
|
|
192
|
-
{
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
194
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
195
|
+
? {
|
|
196
|
+
status: 200,
|
|
197
|
+
statusText: 'OK',
|
|
198
|
+
}
|
|
199
|
+
: {
|
|
200
|
+
status: 400,
|
|
201
|
+
statusText: 'Bad Request',
|
|
202
|
+
})), { statusText: 'Bad Request' }),
|
|
196
203
|
];
|
|
197
204
|
}
|
|
198
205
|
let args;
|
|
199
|
-
const
|
|
200
|
-
if ((0, common_1.isResponse)(
|
|
201
|
-
return
|
|
202
|
-
else if (
|
|
203
|
-
|
|
206
|
+
const maybeResErrsOrArgs = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, params));
|
|
207
|
+
if ((0, common_1.isResponse)(maybeResErrsOrArgs))
|
|
208
|
+
return maybeResErrsOrArgs;
|
|
209
|
+
else if ((0, utils_1.areGraphQLErrors)(maybeResErrsOrArgs))
|
|
210
|
+
return [
|
|
211
|
+
JSON.stringify({ errors: maybeResErrsOrArgs }),
|
|
212
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
213
|
+
? {
|
|
214
|
+
status: 200,
|
|
215
|
+
statusText: 'OK',
|
|
216
|
+
}
|
|
217
|
+
: {
|
|
218
|
+
status: 400,
|
|
219
|
+
statusText: 'Bad Request',
|
|
220
|
+
})), { headers: {
|
|
221
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
222
|
+
? 'application/json; charset=utf-8'
|
|
223
|
+
: 'application/graphql+json; charset=utf-8',
|
|
224
|
+
} }),
|
|
225
|
+
];
|
|
226
|
+
else if (maybeResErrsOrArgs)
|
|
227
|
+
args = maybeResErrsOrArgs;
|
|
204
228
|
else {
|
|
205
229
|
if (!schema)
|
|
206
230
|
throw new Error('The GraphQL schema is not provided');
|
|
207
231
|
const { operationName, query, variables } = params;
|
|
208
232
|
let document;
|
|
209
233
|
try {
|
|
210
|
-
document =
|
|
234
|
+
document = parse(query);
|
|
211
235
|
}
|
|
212
|
-
catch (
|
|
236
|
+
catch (err) {
|
|
213
237
|
return [
|
|
214
|
-
|
|
215
|
-
{
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
238
|
+
JSON.stringify({ errors: [err] }),
|
|
239
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
240
|
+
? {
|
|
241
|
+
status: 200,
|
|
242
|
+
statusText: 'OK',
|
|
243
|
+
}
|
|
244
|
+
: {
|
|
245
|
+
status: 400,
|
|
246
|
+
statusText: 'Bad Request',
|
|
247
|
+
})), { statusText: 'Bad Request', headers: {
|
|
248
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
249
|
+
? 'application/json; charset=utf-8'
|
|
250
|
+
: 'application/graphql+json; charset=utf-8',
|
|
251
|
+
} }),
|
|
219
252
|
];
|
|
220
253
|
}
|
|
221
254
|
const argsWithoutSchema = {
|
|
@@ -235,18 +268,23 @@ function createHandler(options) {
|
|
|
235
268
|
}
|
|
236
269
|
let operation;
|
|
237
270
|
try {
|
|
238
|
-
const ast =
|
|
271
|
+
const ast = getOperationAST(args.document, args.operationName);
|
|
239
272
|
if (!ast)
|
|
240
273
|
throw null;
|
|
241
274
|
operation = ast.operation;
|
|
242
275
|
}
|
|
243
|
-
catch (
|
|
276
|
+
catch (_e) {
|
|
244
277
|
return [
|
|
245
278
|
'Unable to detect operation AST',
|
|
246
|
-
{
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
279
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
280
|
+
? {
|
|
281
|
+
status: 200,
|
|
282
|
+
statusText: 'OK',
|
|
283
|
+
}
|
|
284
|
+
: {
|
|
285
|
+
status: 400,
|
|
286
|
+
statusText: 'Bad Request',
|
|
287
|
+
})), { statusText: 'Bad Request' }),
|
|
250
288
|
];
|
|
251
289
|
}
|
|
252
290
|
if (operation === 'subscription') {
|
|
@@ -273,22 +311,28 @@ function createHandler(options) {
|
|
|
273
311
|
];
|
|
274
312
|
}
|
|
275
313
|
if (!('contextValue' in args)) {
|
|
276
|
-
args
|
|
277
|
-
|
|
314
|
+
const maybeResOrContext = typeof context === 'function' ? await context(req, args) : context;
|
|
315
|
+
if ((0, common_1.isResponse)(maybeResOrContext))
|
|
316
|
+
return maybeResOrContext;
|
|
317
|
+
args.contextValue = maybeResOrContext;
|
|
278
318
|
}
|
|
279
319
|
const validationErrs = validate(args.schema, args.document);
|
|
280
320
|
if (validationErrs.length) {
|
|
281
321
|
return [
|
|
282
322
|
JSON.stringify({ errors: validationErrs }),
|
|
283
|
-
{
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
323
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
324
|
+
? {
|
|
325
|
+
status: 200,
|
|
326
|
+
statusText: 'OK',
|
|
327
|
+
}
|
|
328
|
+
: {
|
|
329
|
+
status: 400,
|
|
330
|
+
statusText: 'Bad Request',
|
|
331
|
+
})), { headers: {
|
|
287
332
|
'content-type': acceptedMediaType === 'application/json'
|
|
288
333
|
? 'application/json; charset=utf-8'
|
|
289
334
|
: 'application/graphql+json; charset=utf-8',
|
|
290
|
-
},
|
|
291
|
-
},
|
|
335
|
+
} }),
|
|
292
336
|
];
|
|
293
337
|
}
|
|
294
338
|
let result = await execute(args);
|
package/lib/handler.mjs
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* handler
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
-
import { validate as graphqlValidate, execute as graphqlExecute, parse, getOperationAST, } from 'graphql';
|
|
6
|
+
import { validate as graphqlValidate, execute as graphqlExecute, parse as graphqlParse, getOperationAST as graphqlGetOperationAST, } from 'graphql';
|
|
7
7
|
import { isResponse } from './common.mjs';
|
|
8
|
+
import { areGraphQLErrors } from './utils.mjs';
|
|
8
9
|
/**
|
|
9
10
|
* Makes a GraphQL over HTTP Protocol compliant server handler. The handler can
|
|
10
11
|
* be used with your favourite server library.
|
|
@@ -61,7 +62,7 @@ import { isResponse } from './common.mjs';
|
|
|
61
62
|
* @category Server
|
|
62
63
|
*/
|
|
63
64
|
export function createHandler(options) {
|
|
64
|
-
const { schema, context, validate = graphqlValidate, execute = graphqlExecute, onSubscribe, onOperation, } = options;
|
|
65
|
+
const { schema, context, validate = graphqlValidate, execute = graphqlExecute, parse = graphqlParse, getOperationAST = graphqlGetOperationAST, onSubscribe, onOperation, } = options;
|
|
65
66
|
return async function handler(req) {
|
|
66
67
|
var _a, _b;
|
|
67
68
|
const method = req.method;
|
|
@@ -124,14 +125,15 @@ export function createHandler(options) {
|
|
|
124
125
|
case method === 'GET': {
|
|
125
126
|
// TODO: what if content-type is specified and is not application/x-www-form-urlencoded?
|
|
126
127
|
try {
|
|
127
|
-
const
|
|
128
|
+
const [, search] = req.url.split('?');
|
|
129
|
+
const searchParams = new URLSearchParams(search);
|
|
128
130
|
partParams.operationName =
|
|
129
|
-
(_a =
|
|
130
|
-
partParams.query = (_b =
|
|
131
|
-
const variables =
|
|
131
|
+
(_a = searchParams.get('operationName')) !== null && _a !== void 0 ? _a : undefined;
|
|
132
|
+
partParams.query = (_b = searchParams.get('query')) !== null && _b !== void 0 ? _b : undefined;
|
|
133
|
+
const variables = searchParams.get('variables');
|
|
132
134
|
if (variables)
|
|
133
135
|
partParams.variables = JSON.parse(variables);
|
|
134
|
-
const extensions =
|
|
136
|
+
const extensions = searchParams.get('extensions');
|
|
135
137
|
if (extensions)
|
|
136
138
|
partParams.extensions = JSON.parse(extensions);
|
|
137
139
|
}
|
|
@@ -186,18 +188,40 @@ export function createHandler(options) {
|
|
|
186
188
|
catch (err) {
|
|
187
189
|
return [
|
|
188
190
|
err.message,
|
|
189
|
-
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
192
|
+
? {
|
|
193
|
+
status: 200,
|
|
194
|
+
statusText: 'OK',
|
|
195
|
+
}
|
|
196
|
+
: {
|
|
197
|
+
status: 400,
|
|
198
|
+
statusText: 'Bad Request',
|
|
199
|
+
})), { statusText: 'Bad Request' }),
|
|
193
200
|
];
|
|
194
201
|
}
|
|
195
202
|
let args;
|
|
196
|
-
const
|
|
197
|
-
if (isResponse(
|
|
198
|
-
return
|
|
199
|
-
else if (
|
|
200
|
-
|
|
203
|
+
const maybeResErrsOrArgs = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(req, params));
|
|
204
|
+
if (isResponse(maybeResErrsOrArgs))
|
|
205
|
+
return maybeResErrsOrArgs;
|
|
206
|
+
else if (areGraphQLErrors(maybeResErrsOrArgs))
|
|
207
|
+
return [
|
|
208
|
+
JSON.stringify({ errors: maybeResErrsOrArgs }),
|
|
209
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
210
|
+
? {
|
|
211
|
+
status: 200,
|
|
212
|
+
statusText: 'OK',
|
|
213
|
+
}
|
|
214
|
+
: {
|
|
215
|
+
status: 400,
|
|
216
|
+
statusText: 'Bad Request',
|
|
217
|
+
})), { headers: {
|
|
218
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
219
|
+
? 'application/json; charset=utf-8'
|
|
220
|
+
: 'application/graphql+json; charset=utf-8',
|
|
221
|
+
} }),
|
|
222
|
+
];
|
|
223
|
+
else if (maybeResErrsOrArgs)
|
|
224
|
+
args = maybeResErrsOrArgs;
|
|
201
225
|
else {
|
|
202
226
|
if (!schema)
|
|
203
227
|
throw new Error('The GraphQL schema is not provided');
|
|
@@ -206,13 +230,22 @@ export function createHandler(options) {
|
|
|
206
230
|
try {
|
|
207
231
|
document = parse(query);
|
|
208
232
|
}
|
|
209
|
-
catch (
|
|
233
|
+
catch (err) {
|
|
210
234
|
return [
|
|
211
|
-
|
|
212
|
-
{
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
235
|
+
JSON.stringify({ errors: [err] }),
|
|
236
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
237
|
+
? {
|
|
238
|
+
status: 200,
|
|
239
|
+
statusText: 'OK',
|
|
240
|
+
}
|
|
241
|
+
: {
|
|
242
|
+
status: 400,
|
|
243
|
+
statusText: 'Bad Request',
|
|
244
|
+
})), { statusText: 'Bad Request', headers: {
|
|
245
|
+
'content-type': acceptedMediaType === 'application/json'
|
|
246
|
+
? 'application/json; charset=utf-8'
|
|
247
|
+
: 'application/graphql+json; charset=utf-8',
|
|
248
|
+
} }),
|
|
216
249
|
];
|
|
217
250
|
}
|
|
218
251
|
const argsWithoutSchema = {
|
|
@@ -237,13 +270,18 @@ export function createHandler(options) {
|
|
|
237
270
|
throw null;
|
|
238
271
|
operation = ast.operation;
|
|
239
272
|
}
|
|
240
|
-
catch (
|
|
273
|
+
catch (_e) {
|
|
241
274
|
return [
|
|
242
275
|
'Unable to detect operation AST',
|
|
243
|
-
{
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
276
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
277
|
+
? {
|
|
278
|
+
status: 200,
|
|
279
|
+
statusText: 'OK',
|
|
280
|
+
}
|
|
281
|
+
: {
|
|
282
|
+
status: 400,
|
|
283
|
+
statusText: 'Bad Request',
|
|
284
|
+
})), { statusText: 'Bad Request' }),
|
|
247
285
|
];
|
|
248
286
|
}
|
|
249
287
|
if (operation === 'subscription') {
|
|
@@ -270,22 +308,28 @@ export function createHandler(options) {
|
|
|
270
308
|
];
|
|
271
309
|
}
|
|
272
310
|
if (!('contextValue' in args)) {
|
|
273
|
-
args
|
|
274
|
-
|
|
311
|
+
const maybeResOrContext = typeof context === 'function' ? await context(req, args) : context;
|
|
312
|
+
if (isResponse(maybeResOrContext))
|
|
313
|
+
return maybeResOrContext;
|
|
314
|
+
args.contextValue = maybeResOrContext;
|
|
275
315
|
}
|
|
276
316
|
const validationErrs = validate(args.schema, args.document);
|
|
277
317
|
if (validationErrs.length) {
|
|
278
318
|
return [
|
|
279
319
|
JSON.stringify({ errors: validationErrs }),
|
|
280
|
-
{
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
320
|
+
Object.assign(Object.assign({}, (acceptedMediaType === 'application/json'
|
|
321
|
+
? {
|
|
322
|
+
status: 200,
|
|
323
|
+
statusText: 'OK',
|
|
324
|
+
}
|
|
325
|
+
: {
|
|
326
|
+
status: 400,
|
|
327
|
+
statusText: 'Bad Request',
|
|
328
|
+
})), { headers: {
|
|
284
329
|
'content-type': acceptedMediaType === 'application/json'
|
|
285
330
|
? 'application/json; charset=utf-8'
|
|
286
331
|
: 'application/graphql+json; charset=utf-8',
|
|
287
|
-
},
|
|
288
|
-
},
|
|
332
|
+
} }),
|
|
289
333
|
];
|
|
290
334
|
}
|
|
291
335
|
let result = await execute(args);
|
package/lib/utils.d.ts
CHANGED
|
@@ -3,5 +3,8 @@
|
|
|
3
3
|
* utils
|
|
4
4
|
*
|
|
5
5
|
*/
|
|
6
|
+
import type { GraphQLError } from 'graphql';
|
|
6
7
|
/** @private */
|
|
7
8
|
export declare function isObject(val: unknown): val is Record<PropertyKey, unknown>;
|
|
9
|
+
/** @private */
|
|
10
|
+
export declare function areGraphQLErrors(obj: unknown): obj is GraphQLError[];
|
package/lib/utils.js
CHANGED
|
@@ -5,9 +5,18 @@
|
|
|
5
5
|
*
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.isObject = void 0;
|
|
8
|
+
exports.areGraphQLErrors = exports.isObject = void 0;
|
|
9
9
|
/** @private */
|
|
10
10
|
function isObject(val) {
|
|
11
11
|
return typeof val === 'object' && val !== null;
|
|
12
12
|
}
|
|
13
13
|
exports.isObject = isObject;
|
|
14
|
+
/** @private */
|
|
15
|
+
function areGraphQLErrors(obj) {
|
|
16
|
+
return (Array.isArray(obj) &&
|
|
17
|
+
// must be at least one error
|
|
18
|
+
obj.length > 0 &&
|
|
19
|
+
// error has at least a message
|
|
20
|
+
obj.every((ob) => 'message' in ob));
|
|
21
|
+
}
|
|
22
|
+
exports.areGraphQLErrors = areGraphQLErrors;
|
package/lib/utils.mjs
CHANGED
|
@@ -7,3 +7,11 @@
|
|
|
7
7
|
export function isObject(val) {
|
|
8
8
|
return typeof val === 'object' && val !== null;
|
|
9
9
|
}
|
|
10
|
+
/** @private */
|
|
11
|
+
export function areGraphQLErrors(obj) {
|
|
12
|
+
return (Array.isArray(obj) &&
|
|
13
|
+
// must be at least one error
|
|
14
|
+
obj.length > 0 &&
|
|
15
|
+
// error has at least a message
|
|
16
|
+
obj.every((ob) => 'message' in ob));
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "graphql-http",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Simple,
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Simple, pluggable, zero-dependency, GraphQL over HTTP Protocol compliant server and client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
7
7
|
"client",
|
package/umd/graphql-http.js
CHANGED
|
@@ -26,7 +26,9 @@
|
|
|
26
26
|
*/
|
|
27
27
|
function isResponse(val) {
|
|
28
28
|
// TODO: make sure the contents of init match ResponseInit
|
|
29
|
-
return Array.isArray(val) &&
|
|
29
|
+
return (Array.isArray(val) &&
|
|
30
|
+
(typeof val[0] === 'string' || val[0] === null) &&
|
|
31
|
+
isObject(val[1]));
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -102,7 +104,7 @@
|
|
|
102
104
|
}
|
|
103
105
|
try {
|
|
104
106
|
const url = typeof options.url === 'function'
|
|
105
|
-
? await options.url()
|
|
107
|
+
? await options.url(request)
|
|
106
108
|
: options.url;
|
|
107
109
|
if (control.signal.aborted)
|
|
108
110
|
return;
|
package/umd/graphql-http.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
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";function t(e){return"object"==typeof e&&null!==e}class r extends Error{constructor(e){let r,o;var n;t(n=e)&&"boolean"==typeof n.ok&&"number"==typeof n.status&&"string"==typeof n.statusText?(o=e,r="Server responded with "+e.status+": "+e.statusText):r=e instanceof Error?e.message:String(e),super(r),this.name=this.constructor.name,this.response=o}}e.NetworkError=r,e.createClient=function(e){const{credentials:t="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
|
|
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";function t(e){return"object"==typeof e&&null!==e}class r extends Error{constructor(e){let r,o;var n;t(n=e)&&"boolean"==typeof n.ok&&"number"==typeof n.status&&"string"==typeof n.statusText?(o=e,r="Server responded with "+e.status+": "+e.statusText):r=e instanceof Error?e.message:String(e),super(r),this.name=this.constructor.name,this.response=o}}e.NetworkError=r,e.createClient=function(e){const{credentials:t="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(l,p){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(l):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+json, application/json"}),credentials:t,referrer:o,referrerPolicy:n,body:JSON.stringify(l)})}catch(e){throw new r(e)}if(!u.ok)throw new r(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+json")&&!d.includes("application/json"))throw new Error(`Unsupported response content-type ${d}`);const h=await u.json();return p.next(h),f.abort()}catch(e){if(f.signal.aborted)return;if(!(e instanceof r))throw e;c=e}}})().then((()=>p.complete())).catch((e=>p.error(e))),()=>f.abort()},dispose(){c.dispose()}}},e.isResponse=function(e){return Array.isArray(e)&&("string"==typeof e[0]||null===e[0])&&t(e[1])},Object.defineProperty(e,"__esModule",{value:!0})}));
|
|
Binary file
|