@graphql-tools/url-loader 7.2.1-alpha-0dcd17c0.0 → 7.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/addCancelToResponseStream.d.ts +1 -0
- package/defaultAsyncFetch.d.ts +3 -0
- package/defaultSyncFetch.d.ts +6 -0
- package/event-stream/handleEventStreamResponse.d.ts +2 -0
- package/event-stream/handlePart.d.ts +2 -0
- package/event-stream/handleReadable.d.ts +7 -0
- package/event-stream/handleReadableStream.d.ts +1 -0
- package/handleMultipartMixedResponse.d.ts +6 -0
- package/index.d.ts +3 -13
- package/index.js +146 -97
- package/index.mjs +147 -98
- package/package.json +4 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function addCancelToResponseStream<T>(resultStream: AsyncIterable<T>, controller: AbortController): AsyncIterator<T | undefined, any, undefined>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleReadableStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<ReadableStreamDefaultReadValueResult<Uint8Array>, void, unknown>;
|
package/index.d.ts
CHANGED
|
@@ -6,14 +6,9 @@ import { ClientOptions } from 'graphql-ws';
|
|
|
6
6
|
import { ClientOptions as GraphQLSSEClientOptions } from 'graphql-sse';
|
|
7
7
|
import WebSocket from 'isomorphic-ws';
|
|
8
8
|
import FormData from 'form-data';
|
|
9
|
-
import { FetchEventSourceInit } from '@ardatan/fetch-event-source';
|
|
10
9
|
import { ConnectionParamsOptions } from 'subscriptions-transport-ws';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export declare type SyncResponse = Omit<Response, 'json' | 'text'> & {
|
|
14
|
-
json: () => any;
|
|
15
|
-
text: () => string;
|
|
16
|
-
};
|
|
10
|
+
import { AsyncFetchFn } from './defaultAsyncFetch';
|
|
11
|
+
import { SyncFetchFn } from './defaultSyncFetch';
|
|
17
12
|
export declare type FetchFn = AsyncFetchFn | SyncFetchFn;
|
|
18
13
|
export declare type AsyncImportFn = (moduleName: string) => PromiseLike<any>;
|
|
19
14
|
export declare type SyncImportFn = (moduleName: string) => any;
|
|
@@ -66,10 +61,6 @@ export interface LoadFromUrlOptions extends BaseLoaderOptions, Partial<Introspec
|
|
|
66
61
|
* Use multipart for POST requests
|
|
67
62
|
*/
|
|
68
63
|
multipart?: boolean;
|
|
69
|
-
/**
|
|
70
|
-
* Additional options to pass to the constructor of the underlying EventSource instance.
|
|
71
|
-
*/
|
|
72
|
-
eventSourceOptions?: FetchEventSourceInit;
|
|
73
64
|
/**
|
|
74
65
|
* Handle URL as schema SDL
|
|
75
66
|
*/
|
|
@@ -123,9 +114,8 @@ export declare class UrlLoader implements Loader<LoadFromUrlOptions> {
|
|
|
123
114
|
buildHTTPExecutor(endpoint: string, fetch: AsyncFetchFn, options?: LoadFromUrlOptions): AsyncExecutor<any, ExecutionExtensions>;
|
|
124
115
|
buildWSExecutor(subscriptionsEndpoint: string, webSocketImpl: typeof WebSocket, connectionParams?: ClientOptions['connectionParams']): AsyncExecutor;
|
|
125
116
|
buildWSLegacyExecutor(subscriptionsEndpoint: string, webSocketImpl: typeof WebSocket, connectionParams?: ConnectionParamsOptions): AsyncExecutor;
|
|
126
|
-
buildSSEExecutor(endpoint: string, fetch: AsyncFetchFn, options?: Omit<LoadFromUrlOptions, 'subscriptionEndpoint'>): AsyncExecutor<any, ExecutionExtensions>;
|
|
127
117
|
buildGraphQLSSEExecutor(endpoint: string, fetch: AsyncFetchFn, options?: Omit<LoadFromUrlOptions, 'subscriptionEndpoint'>): AsyncExecutor;
|
|
128
|
-
getFetch(customFetch: LoadFromUrlOptions['customFetch'], importFn: AsyncImportFn): PromiseLike<AsyncFetchFn
|
|
118
|
+
getFetch(customFetch: LoadFromUrlOptions['customFetch'], importFn: AsyncImportFn): PromiseLike<AsyncFetchFn> | AsyncFetchFn;
|
|
129
119
|
getFetch(customFetch: LoadFromUrlOptions['customFetch'], importFn: SyncImportFn): SyncFetchFn;
|
|
130
120
|
private getDefaultMethodFromOptions;
|
|
131
121
|
getWebSocketImpl(importFn: AsyncImportFn, options?: LoadFromUrlOptions): PromiseLike<typeof WebSocket>;
|
package/index.js
CHANGED
|
@@ -26,25 +26,31 @@ function _interopNamespace(e) {
|
|
|
26
26
|
const graphql = require('graphql');
|
|
27
27
|
const utils = require('@graphql-tools/utils');
|
|
28
28
|
const validUrl = require('valid-url');
|
|
29
|
-
const crossFetch = require('cross-fetch');
|
|
30
29
|
const wrap = require('@graphql-tools/wrap');
|
|
31
30
|
const graphqlWs = require('graphql-ws');
|
|
32
31
|
const graphqlSse = require('graphql-sse');
|
|
33
32
|
const WebSocket = _interopDefault(require('isomorphic-ws'));
|
|
34
|
-
const syncFetchImported = _interopDefault(require('sync-fetch'));
|
|
35
33
|
const isPromise = _interopDefault(require('is-promise'));
|
|
36
34
|
const extractFiles = require('extract-files');
|
|
37
35
|
const FormData = _interopDefault(require('form-data'));
|
|
38
|
-
const fetchEventSource = require('@ardatan/fetch-event-source');
|
|
39
36
|
const subscriptionsTransportWs = require('subscriptions-transport-ws');
|
|
40
37
|
const AbortController = _interopDefault(require('abort-controller'));
|
|
41
|
-
const meros = require('meros');
|
|
42
|
-
const _ = _interopDefault(require('lodash'));
|
|
43
38
|
const valueOrPromise = require('value-or-promise');
|
|
44
39
|
const graphqlLiveQuery = require('@n1ru4l/graphql-live-query');
|
|
40
|
+
const crossFetch = require('cross-fetch');
|
|
41
|
+
const syncFetchImported = _interopDefault(require('sync-fetch'));
|
|
42
|
+
const node = require('meros/node');
|
|
43
|
+
const browser = require('meros/browser');
|
|
44
|
+
const _ = _interopDefault(require('lodash'));
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const defaultAsyncFetch = async (input, init) => {
|
|
47
|
+
if (typeof fetch !== 'undefined') {
|
|
48
|
+
return fetch(input, init);
|
|
49
|
+
}
|
|
50
|
+
return crossFetch.fetch(input, init);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const defaultSyncFetch = (input, init) => {
|
|
48
54
|
if (typeof input === 'string') {
|
|
49
55
|
init === null || init === void 0 ? true : delete init.signal;
|
|
50
56
|
}
|
|
@@ -53,6 +59,124 @@ const syncFetch = (input, init) => {
|
|
|
53
59
|
}
|
|
54
60
|
return syncFetchImported(input, init);
|
|
55
61
|
};
|
|
62
|
+
|
|
63
|
+
/* eslint-disable */
|
|
64
|
+
function isIncomingMessage(body) {
|
|
65
|
+
return body != null && typeof body === 'object' && 'pipe' in body;
|
|
66
|
+
}
|
|
67
|
+
async function handleMultipartMixedResponse(response) {
|
|
68
|
+
const body = await response.body;
|
|
69
|
+
const contentType = response.headers.get('content-type') || '';
|
|
70
|
+
let asyncIterator;
|
|
71
|
+
if (isIncomingMessage(body)) {
|
|
72
|
+
// Meros/node expects headers as an object map with the content-type prop
|
|
73
|
+
body.headers = {
|
|
74
|
+
'content-type': contentType,
|
|
75
|
+
};
|
|
76
|
+
// And it expects `IncomingMessage` and `node-fetch` returns `body` as `Promise<PassThrough>`
|
|
77
|
+
asyncIterator = (await node.meros(body));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
// Nothing is needed for regular `Response`.
|
|
81
|
+
asyncIterator = (await browser.meros(response));
|
|
82
|
+
}
|
|
83
|
+
const executionResult = {};
|
|
84
|
+
return utils.mapAsyncIterator(asyncIterator, (part) => {
|
|
85
|
+
if (part.json) {
|
|
86
|
+
const chunk = part.body;
|
|
87
|
+
if (chunk.path) {
|
|
88
|
+
if (chunk.data) {
|
|
89
|
+
const path = ['data'];
|
|
90
|
+
_.set(executionResult, path.concat(chunk.path), chunk.data);
|
|
91
|
+
}
|
|
92
|
+
if (chunk.errors) {
|
|
93
|
+
executionResult.errors = (executionResult.errors || []).concat(chunk.errors);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
if (chunk.data) {
|
|
98
|
+
executionResult.data = chunk.data;
|
|
99
|
+
}
|
|
100
|
+
if (chunk.errors) {
|
|
101
|
+
executionResult.errors = chunk.errors;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return executionResult;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function handlePart(part) {
|
|
110
|
+
const eventStr = part.split('event: ')[1];
|
|
111
|
+
const dataStr = part.split('data: ')[1];
|
|
112
|
+
const data = JSON.parse(dataStr);
|
|
113
|
+
if (data.payload && eventStr) {
|
|
114
|
+
if (eventStr === 'complete') {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
return data.payload;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return data;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function* handleReadable(readable) {
|
|
125
|
+
outer: for await (const chunk of readable) {
|
|
126
|
+
const chunkStr = chunk.toString();
|
|
127
|
+
for (const part of chunkStr.split('\n\n')) {
|
|
128
|
+
if (part) {
|
|
129
|
+
const result = handlePart(part);
|
|
130
|
+
if (result === false) {
|
|
131
|
+
break outer;
|
|
132
|
+
}
|
|
133
|
+
yield result;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* eslint-disable no-labels */
|
|
140
|
+
async function* handleReadableStream(stream) {
|
|
141
|
+
const decoder = new TextDecoder();
|
|
142
|
+
const reader = stream.getReader();
|
|
143
|
+
try {
|
|
144
|
+
let result;
|
|
145
|
+
outer: while (!(result = await reader.read()).done) {
|
|
146
|
+
const chunk = decoder.decode(result.value);
|
|
147
|
+
for (const part of chunk.toString().split('\n\n')) {
|
|
148
|
+
if (part) {
|
|
149
|
+
const executionResult = handlePart(part);
|
|
150
|
+
if (executionResult === false) {
|
|
151
|
+
break outer;
|
|
152
|
+
}
|
|
153
|
+
yield result;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
reader.releaseLock();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* eslint-disable */
|
|
164
|
+
async function handleEventStreamResponse(response) {
|
|
165
|
+
const body = await response.body;
|
|
166
|
+
if (body) {
|
|
167
|
+
if ('pipe' in body) {
|
|
168
|
+
return handleReadable(body);
|
|
169
|
+
}
|
|
170
|
+
return handleReadableStream(body);
|
|
171
|
+
}
|
|
172
|
+
throw new Error('Body is null???');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function addCancelToResponseStream(resultStream, controller) {
|
|
176
|
+
return utils.withCancel(resultStream, () => controller.abort());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* eslint-disable no-case-declarations */
|
|
56
180
|
const asyncImport = (moduleName) => new Promise(function (resolve) { resolve(_interopNamespace(require(moduleName))); });
|
|
57
181
|
const syncImport = (moduleName) => require(moduleName);
|
|
58
182
|
(function (SubscriptionProtocol) {
|
|
@@ -218,7 +342,7 @@ class UrlLoader {
|
|
|
218
342
|
extensions,
|
|
219
343
|
}),
|
|
220
344
|
headers: {
|
|
221
|
-
accept: 'application/json, multipart/mixed',
|
|
345
|
+
accept: 'application/json, multipart/mixed, text/event-stream',
|
|
222
346
|
'content-type': 'application/json',
|
|
223
347
|
...headers,
|
|
224
348
|
},
|
|
@@ -228,41 +352,12 @@ class UrlLoader {
|
|
|
228
352
|
}
|
|
229
353
|
})
|
|
230
354
|
.then((fetchResult) => {
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/mixed')) {
|
|
236
|
-
return
|
|
237
|
-
if (utils.isAsyncIterable(maybeStream)) {
|
|
238
|
-
return utils.withCancel(utils.mapAsyncIterator(maybeStream, part => {
|
|
239
|
-
if (part.json) {
|
|
240
|
-
const chunk = part.body;
|
|
241
|
-
if (chunk.path) {
|
|
242
|
-
if (chunk.data) {
|
|
243
|
-
const path = ['data'];
|
|
244
|
-
_.merge(response, _.set({}, path.concat(chunk.path), chunk.data));
|
|
245
|
-
}
|
|
246
|
-
if (chunk.errors) {
|
|
247
|
-
response.errors = (response.errors || []).concat(chunk.errors);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
if (chunk.data) {
|
|
252
|
-
response.data = chunk.data;
|
|
253
|
-
}
|
|
254
|
-
if (chunk.errors) {
|
|
255
|
-
response.errors = chunk.errors;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return response;
|
|
259
|
-
}
|
|
260
|
-
}), () => controller.abort());
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
return maybeStream.json();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
355
|
+
const contentType = fetchResult.headers.get('content-type');
|
|
356
|
+
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/event-stream')) {
|
|
357
|
+
return handleEventStreamResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
|
|
358
|
+
}
|
|
359
|
+
else if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/mixed')) {
|
|
360
|
+
return handleMultipartMixedResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
|
|
266
361
|
}
|
|
267
362
|
return fetchResult.json();
|
|
268
363
|
})
|
|
@@ -315,52 +410,6 @@ class UrlLoader {
|
|
|
315
410
|
}));
|
|
316
411
|
};
|
|
317
412
|
}
|
|
318
|
-
buildSSEExecutor(endpoint, fetch, options) {
|
|
319
|
-
return async ({ document, variables, extensions, operationName }) => {
|
|
320
|
-
const controller = new AbortController();
|
|
321
|
-
const query = graphql.print(document);
|
|
322
|
-
const finalUrl = this.prepareGETUrl({ baseUrl: endpoint, query, variables, operationName, extensions });
|
|
323
|
-
return utils.observableToAsyncIterable({
|
|
324
|
-
subscribe: observer => {
|
|
325
|
-
const headers = Object.assign({}, (options === null || options === void 0 ? void 0 : options.headers) || {}, (extensions === null || extensions === void 0 ? void 0 : extensions.headers) || {});
|
|
326
|
-
fetchEventSource.fetchEventSource(finalUrl, {
|
|
327
|
-
credentials: 'include',
|
|
328
|
-
headers,
|
|
329
|
-
method: 'GET',
|
|
330
|
-
onerror: error => {
|
|
331
|
-
observer.error(error);
|
|
332
|
-
},
|
|
333
|
-
onmessage: event => {
|
|
334
|
-
observer.next(JSON.parse(event.data || '{}'));
|
|
335
|
-
},
|
|
336
|
-
onopen: async (response) => {
|
|
337
|
-
const contentType = response.headers.get('content-type');
|
|
338
|
-
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('text/event-stream'))) {
|
|
339
|
-
let error;
|
|
340
|
-
try {
|
|
341
|
-
const { errors } = await response.json();
|
|
342
|
-
error = errors[0];
|
|
343
|
-
}
|
|
344
|
-
catch (error) {
|
|
345
|
-
// Failed to parse body
|
|
346
|
-
}
|
|
347
|
-
if (error) {
|
|
348
|
-
throw error;
|
|
349
|
-
}
|
|
350
|
-
throw new Error(`Expected content-type to be ${'text/event-stream'} but got "${contentType}".`);
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
fetch,
|
|
354
|
-
signal: controller.signal,
|
|
355
|
-
...((options === null || options === void 0 ? void 0 : options.eventSourceOptions) || {}),
|
|
356
|
-
});
|
|
357
|
-
return {
|
|
358
|
-
unsubscribe: () => controller.abort(),
|
|
359
|
-
};
|
|
360
|
-
},
|
|
361
|
-
});
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
413
|
buildGraphQLSSEExecutor(endpoint, fetch, options = {}) {
|
|
365
414
|
const { headers } = options;
|
|
366
415
|
const client = graphqlSse.createClient({
|
|
@@ -394,18 +443,15 @@ class UrlLoader {
|
|
|
394
443
|
.then(module => (fetchFnName ? module[fetchFnName] : module))
|
|
395
444
|
.resolve();
|
|
396
445
|
}
|
|
397
|
-
else {
|
|
446
|
+
else if (typeof customFetch === 'function') {
|
|
398
447
|
return customFetch;
|
|
399
448
|
}
|
|
400
449
|
}
|
|
401
450
|
if (importFn === asyncImport) {
|
|
402
|
-
|
|
403
|
-
return crossFetch.fetch;
|
|
404
|
-
}
|
|
405
|
-
return fetch;
|
|
451
|
+
return defaultAsyncFetch;
|
|
406
452
|
}
|
|
407
453
|
else {
|
|
408
|
-
return
|
|
454
|
+
return defaultSyncFetch;
|
|
409
455
|
}
|
|
410
456
|
}
|
|
411
457
|
getDefaultMethodFromOptions(method, defaultMethod) {
|
|
@@ -428,7 +474,10 @@ class UrlLoader {
|
|
|
428
474
|
}
|
|
429
475
|
async buildSubscriptionExecutor(subscriptionsEndpoint, fetch, options) {
|
|
430
476
|
if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === exports.SubscriptionProtocol.SSE) {
|
|
431
|
-
return this.
|
|
477
|
+
return this.buildHTTPExecutor(subscriptionsEndpoint, fetch, {
|
|
478
|
+
...options,
|
|
479
|
+
method: 'GET',
|
|
480
|
+
});
|
|
432
481
|
}
|
|
433
482
|
else if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === exports.SubscriptionProtocol.GRAPHQL_SSE) {
|
|
434
483
|
if (!(options === null || options === void 0 ? void 0 : options.subscriptionsEndpoint)) {
|
|
@@ -459,7 +508,7 @@ class UrlLoader {
|
|
|
459
508
|
if (!operationAst) {
|
|
460
509
|
throw new Error(`No valid operations found: ${params.operationName || ''}`);
|
|
461
510
|
}
|
|
462
|
-
if (
|
|
511
|
+
if (operationAst.operation === 'subscription' ||
|
|
463
512
|
graphqlLiveQuery.isLiveQueryOperationDefinitionNode(operationAst, params.variables)) {
|
|
464
513
|
return subscriptionExecutor(params);
|
|
465
514
|
}
|
package/index.mjs
CHANGED
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { print, getOperationAST, buildASTSchema, buildSchema } from 'graphql';
|
|
2
|
-
import {
|
|
2
|
+
import { mapAsyncIterator, withCancel, isAsyncIterable, observableToAsyncIterable, parseGraphQLSDL } from '@graphql-tools/utils';
|
|
3
3
|
import { isWebUri } from 'valid-url';
|
|
4
|
-
import { fetch as fetch$1 } from 'cross-fetch';
|
|
5
4
|
import { introspectSchema, wrapSchema } from '@graphql-tools/wrap';
|
|
6
5
|
import { createClient } from 'graphql-ws';
|
|
7
6
|
import { createClient as createClient$1 } from 'graphql-sse';
|
|
8
7
|
import WebSocket from 'isomorphic-ws';
|
|
9
|
-
import syncFetchImported from 'sync-fetch';
|
|
10
8
|
import isPromise from 'is-promise';
|
|
11
9
|
import { extractFiles, isExtractableFile } from 'extract-files';
|
|
12
10
|
import FormData from 'form-data';
|
|
13
|
-
import { fetchEventSource } from '@ardatan/fetch-event-source';
|
|
14
11
|
import { SubscriptionClient } from 'subscriptions-transport-ws';
|
|
15
12
|
import AbortController from 'abort-controller';
|
|
16
|
-
import { meros } from 'meros';
|
|
17
|
-
import _ from 'lodash';
|
|
18
13
|
import { ValueOrPromise } from 'value-or-promise';
|
|
19
14
|
import { isLiveQueryOperationDefinitionNode } from '@n1ru4l/graphql-live-query';
|
|
15
|
+
import { fetch as fetch$1 } from 'cross-fetch';
|
|
16
|
+
import syncFetchImported from 'sync-fetch';
|
|
17
|
+
import { meros } from 'meros/node';
|
|
18
|
+
import { meros as meros$1 } from 'meros/browser';
|
|
19
|
+
import _ from 'lodash';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const defaultAsyncFetch = async (input, init) => {
|
|
22
|
+
if (typeof fetch !== 'undefined') {
|
|
23
|
+
return fetch(input, init);
|
|
24
|
+
}
|
|
25
|
+
return fetch$1(input, init);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const defaultSyncFetch = (input, init) => {
|
|
23
29
|
if (typeof input === 'string') {
|
|
24
30
|
init === null || init === void 0 ? true : delete init.signal;
|
|
25
31
|
}
|
|
@@ -28,6 +34,124 @@ const syncFetch = (input, init) => {
|
|
|
28
34
|
}
|
|
29
35
|
return syncFetchImported(input, init);
|
|
30
36
|
};
|
|
37
|
+
|
|
38
|
+
/* eslint-disable */
|
|
39
|
+
function isIncomingMessage(body) {
|
|
40
|
+
return body != null && typeof body === 'object' && 'pipe' in body;
|
|
41
|
+
}
|
|
42
|
+
async function handleMultipartMixedResponse(response) {
|
|
43
|
+
const body = await response.body;
|
|
44
|
+
const contentType = response.headers.get('content-type') || '';
|
|
45
|
+
let asyncIterator;
|
|
46
|
+
if (isIncomingMessage(body)) {
|
|
47
|
+
// Meros/node expects headers as an object map with the content-type prop
|
|
48
|
+
body.headers = {
|
|
49
|
+
'content-type': contentType,
|
|
50
|
+
};
|
|
51
|
+
// And it expects `IncomingMessage` and `node-fetch` returns `body` as `Promise<PassThrough>`
|
|
52
|
+
asyncIterator = (await meros(body));
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Nothing is needed for regular `Response`.
|
|
56
|
+
asyncIterator = (await meros$1(response));
|
|
57
|
+
}
|
|
58
|
+
const executionResult = {};
|
|
59
|
+
return mapAsyncIterator(asyncIterator, (part) => {
|
|
60
|
+
if (part.json) {
|
|
61
|
+
const chunk = part.body;
|
|
62
|
+
if (chunk.path) {
|
|
63
|
+
if (chunk.data) {
|
|
64
|
+
const path = ['data'];
|
|
65
|
+
_.set(executionResult, path.concat(chunk.path), chunk.data);
|
|
66
|
+
}
|
|
67
|
+
if (chunk.errors) {
|
|
68
|
+
executionResult.errors = (executionResult.errors || []).concat(chunk.errors);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
if (chunk.data) {
|
|
73
|
+
executionResult.data = chunk.data;
|
|
74
|
+
}
|
|
75
|
+
if (chunk.errors) {
|
|
76
|
+
executionResult.errors = chunk.errors;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return executionResult;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function handlePart(part) {
|
|
85
|
+
const eventStr = part.split('event: ')[1];
|
|
86
|
+
const dataStr = part.split('data: ')[1];
|
|
87
|
+
const data = JSON.parse(dataStr);
|
|
88
|
+
if (data.payload && eventStr) {
|
|
89
|
+
if (eventStr === 'complete') {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return data.payload;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function* handleReadable(readable) {
|
|
100
|
+
outer: for await (const chunk of readable) {
|
|
101
|
+
const chunkStr = chunk.toString();
|
|
102
|
+
for (const part of chunkStr.split('\n\n')) {
|
|
103
|
+
if (part) {
|
|
104
|
+
const result = handlePart(part);
|
|
105
|
+
if (result === false) {
|
|
106
|
+
break outer;
|
|
107
|
+
}
|
|
108
|
+
yield result;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* eslint-disable no-labels */
|
|
115
|
+
async function* handleReadableStream(stream) {
|
|
116
|
+
const decoder = new TextDecoder();
|
|
117
|
+
const reader = stream.getReader();
|
|
118
|
+
try {
|
|
119
|
+
let result;
|
|
120
|
+
outer: while (!(result = await reader.read()).done) {
|
|
121
|
+
const chunk = decoder.decode(result.value);
|
|
122
|
+
for (const part of chunk.toString().split('\n\n')) {
|
|
123
|
+
if (part) {
|
|
124
|
+
const executionResult = handlePart(part);
|
|
125
|
+
if (executionResult === false) {
|
|
126
|
+
break outer;
|
|
127
|
+
}
|
|
128
|
+
yield result;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
reader.releaseLock();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* eslint-disable */
|
|
139
|
+
async function handleEventStreamResponse(response) {
|
|
140
|
+
const body = await response.body;
|
|
141
|
+
if (body) {
|
|
142
|
+
if ('pipe' in body) {
|
|
143
|
+
return handleReadable(body);
|
|
144
|
+
}
|
|
145
|
+
return handleReadableStream(body);
|
|
146
|
+
}
|
|
147
|
+
throw new Error('Body is null???');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function addCancelToResponseStream(resultStream, controller) {
|
|
151
|
+
return withCancel(resultStream, () => controller.abort());
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* eslint-disable no-case-declarations */
|
|
31
155
|
const asyncImport = (moduleName) => import(moduleName);
|
|
32
156
|
const syncImport = (moduleName) => require(moduleName);
|
|
33
157
|
var SubscriptionProtocol;
|
|
@@ -194,7 +318,7 @@ class UrlLoader {
|
|
|
194
318
|
extensions,
|
|
195
319
|
}),
|
|
196
320
|
headers: {
|
|
197
|
-
accept: 'application/json, multipart/mixed',
|
|
321
|
+
accept: 'application/json, multipart/mixed, text/event-stream',
|
|
198
322
|
'content-type': 'application/json',
|
|
199
323
|
...headers,
|
|
200
324
|
},
|
|
@@ -204,41 +328,12 @@ class UrlLoader {
|
|
|
204
328
|
}
|
|
205
329
|
})
|
|
206
330
|
.then((fetchResult) => {
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/mixed')) {
|
|
212
|
-
return
|
|
213
|
-
if (isAsyncIterable(maybeStream)) {
|
|
214
|
-
return withCancel(mapAsyncIterator(maybeStream, part => {
|
|
215
|
-
if (part.json) {
|
|
216
|
-
const chunk = part.body;
|
|
217
|
-
if (chunk.path) {
|
|
218
|
-
if (chunk.data) {
|
|
219
|
-
const path = ['data'];
|
|
220
|
-
_.merge(response, _.set({}, path.concat(chunk.path), chunk.data));
|
|
221
|
-
}
|
|
222
|
-
if (chunk.errors) {
|
|
223
|
-
response.errors = (response.errors || []).concat(chunk.errors);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
if (chunk.data) {
|
|
228
|
-
response.data = chunk.data;
|
|
229
|
-
}
|
|
230
|
-
if (chunk.errors) {
|
|
231
|
-
response.errors = chunk.errors;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return response;
|
|
235
|
-
}
|
|
236
|
-
}), () => controller.abort());
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
return maybeStream.json();
|
|
240
|
-
}
|
|
241
|
-
});
|
|
331
|
+
const contentType = fetchResult.headers.get('content-type');
|
|
332
|
+
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/event-stream')) {
|
|
333
|
+
return handleEventStreamResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
|
|
334
|
+
}
|
|
335
|
+
else if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/mixed')) {
|
|
336
|
+
return handleMultipartMixedResponse(fetchResult).then(resultStream => addCancelToResponseStream(resultStream, controller));
|
|
242
337
|
}
|
|
243
338
|
return fetchResult.json();
|
|
244
339
|
})
|
|
@@ -291,52 +386,6 @@ class UrlLoader {
|
|
|
291
386
|
}));
|
|
292
387
|
};
|
|
293
388
|
}
|
|
294
|
-
buildSSEExecutor(endpoint, fetch, options) {
|
|
295
|
-
return async ({ document, variables, extensions, operationName }) => {
|
|
296
|
-
const controller = new AbortController();
|
|
297
|
-
const query = print(document);
|
|
298
|
-
const finalUrl = this.prepareGETUrl({ baseUrl: endpoint, query, variables, operationName, extensions });
|
|
299
|
-
return observableToAsyncIterable({
|
|
300
|
-
subscribe: observer => {
|
|
301
|
-
const headers = Object.assign({}, (options === null || options === void 0 ? void 0 : options.headers) || {}, (extensions === null || extensions === void 0 ? void 0 : extensions.headers) || {});
|
|
302
|
-
fetchEventSource(finalUrl, {
|
|
303
|
-
credentials: 'include',
|
|
304
|
-
headers,
|
|
305
|
-
method: 'GET',
|
|
306
|
-
onerror: error => {
|
|
307
|
-
observer.error(error);
|
|
308
|
-
},
|
|
309
|
-
onmessage: event => {
|
|
310
|
-
observer.next(JSON.parse(event.data || '{}'));
|
|
311
|
-
},
|
|
312
|
-
onopen: async (response) => {
|
|
313
|
-
const contentType = response.headers.get('content-type');
|
|
314
|
-
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('text/event-stream'))) {
|
|
315
|
-
let error;
|
|
316
|
-
try {
|
|
317
|
-
const { errors } = await response.json();
|
|
318
|
-
error = errors[0];
|
|
319
|
-
}
|
|
320
|
-
catch (error) {
|
|
321
|
-
// Failed to parse body
|
|
322
|
-
}
|
|
323
|
-
if (error) {
|
|
324
|
-
throw error;
|
|
325
|
-
}
|
|
326
|
-
throw new Error(`Expected content-type to be ${'text/event-stream'} but got "${contentType}".`);
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
fetch,
|
|
330
|
-
signal: controller.signal,
|
|
331
|
-
...((options === null || options === void 0 ? void 0 : options.eventSourceOptions) || {}),
|
|
332
|
-
});
|
|
333
|
-
return {
|
|
334
|
-
unsubscribe: () => controller.abort(),
|
|
335
|
-
};
|
|
336
|
-
},
|
|
337
|
-
});
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
389
|
buildGraphQLSSEExecutor(endpoint, fetch, options = {}) {
|
|
341
390
|
const { headers } = options;
|
|
342
391
|
const client = createClient$1({
|
|
@@ -370,18 +419,15 @@ class UrlLoader {
|
|
|
370
419
|
.then(module => (fetchFnName ? module[fetchFnName] : module))
|
|
371
420
|
.resolve();
|
|
372
421
|
}
|
|
373
|
-
else {
|
|
422
|
+
else if (typeof customFetch === 'function') {
|
|
374
423
|
return customFetch;
|
|
375
424
|
}
|
|
376
425
|
}
|
|
377
426
|
if (importFn === asyncImport) {
|
|
378
|
-
|
|
379
|
-
return fetch$1;
|
|
380
|
-
}
|
|
381
|
-
return fetch;
|
|
427
|
+
return defaultAsyncFetch;
|
|
382
428
|
}
|
|
383
429
|
else {
|
|
384
|
-
return
|
|
430
|
+
return defaultSyncFetch;
|
|
385
431
|
}
|
|
386
432
|
}
|
|
387
433
|
getDefaultMethodFromOptions(method, defaultMethod) {
|
|
@@ -404,7 +450,10 @@ class UrlLoader {
|
|
|
404
450
|
}
|
|
405
451
|
async buildSubscriptionExecutor(subscriptionsEndpoint, fetch, options) {
|
|
406
452
|
if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.SSE) {
|
|
407
|
-
return this.
|
|
453
|
+
return this.buildHTTPExecutor(subscriptionsEndpoint, fetch, {
|
|
454
|
+
...options,
|
|
455
|
+
method: 'GET',
|
|
456
|
+
});
|
|
408
457
|
}
|
|
409
458
|
else if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.GRAPHQL_SSE) {
|
|
410
459
|
if (!(options === null || options === void 0 ? void 0 : options.subscriptionsEndpoint)) {
|
|
@@ -435,7 +484,7 @@ class UrlLoader {
|
|
|
435
484
|
if (!operationAst) {
|
|
436
485
|
throw new Error(`No valid operations found: ${params.operationName || ''}`);
|
|
437
486
|
}
|
|
438
|
-
if (
|
|
487
|
+
if (operationAst.operation === 'subscription' ||
|
|
439
488
|
isLiveQueryOperationDefinitionNode(operationAst, params.variables)) {
|
|
440
489
|
return subscriptionExecutor(params);
|
|
441
490
|
}
|
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-tools/url-loader",
|
|
3
|
-
"version": "7.2.1
|
|
3
|
+
"version": "7.2.1",
|
|
4
4
|
"description": "A set of utils for faster development of GraphQL tools",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@
|
|
11
|
-
"@graphql-tools/
|
|
12
|
-
"@graphql-tools/
|
|
13
|
-
"@graphql-tools/wrap": "8.2.0-alpha-0dcd17c0.0",
|
|
10
|
+
"@graphql-tools/delegate": "^8.2.0",
|
|
11
|
+
"@graphql-tools/utils": "^8.2.0",
|
|
12
|
+
"@graphql-tools/wrap": "^8.1.0",
|
|
14
13
|
"@n1ru4l/graphql-live-query": "0.8.1",
|
|
15
14
|
"@types/websocket": "1.0.4",
|
|
16
15
|
"@types/ws": "^8.0.0",
|