@graphql-tools/url-loader 7.12.2 → 7.13.0-alpha-a42fa2f1.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/cjs/addCancelToResponseStream.js +12 -0
- package/cjs/defaultAsyncFetch.js +8 -0
- package/cjs/defaultSyncFetch.js +15 -0
- package/cjs/event-stream/handleAsyncIterable.js +31 -0
- package/cjs/event-stream/handleEventStreamResponse.js +18 -0
- package/cjs/event-stream/handleReadableStream.js +31 -0
- package/cjs/handleMultipartMixedResponse.js +52 -0
- package/cjs/index.js +612 -0
- package/cjs/utils.js +28 -0
- package/esm/event-stream/{handleReadable.js → handleAsyncIterable.js} +3 -3
- package/esm/event-stream/handleEventStreamResponse.js +3 -3
- package/esm/event-stream/handleReadableStream.js +20 -120
- package/package.json +1 -1
- package/typings/event-stream/handleAsyncIterable.d.ts +1 -0
- package/typings/event-stream/handleReadableStream.d.ts +1 -15
- package/typings/event-stream/handleReadable.d.ts +0 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addCancelToResponseStream = void 0;
|
|
4
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
5
|
+
function addCancelToResponseStream(resultStream, controller) {
|
|
6
|
+
return (0, utils_1.withCancel)(resultStream, () => {
|
|
7
|
+
if (!controller.signal.aborted) {
|
|
8
|
+
controller.abort();
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
exports.addCancelToResponseStream = addCancelToResponseStream;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultAsyncFetch = void 0;
|
|
4
|
+
const fetch_1 = require("@whatwg-node/fetch");
|
|
5
|
+
const defaultAsyncFetch = async (input, init) => {
|
|
6
|
+
return (0, fetch_1.fetch)(input, init);
|
|
7
|
+
};
|
|
8
|
+
exports.defaultAsyncFetch = defaultAsyncFetch;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultSyncFetch = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const sync_fetch_1 = tslib_1.__importDefault(require("sync-fetch"));
|
|
6
|
+
const defaultSyncFetch = (input, init) => {
|
|
7
|
+
if (typeof input === 'string') {
|
|
8
|
+
init === null || init === void 0 ? true : delete init.signal;
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
delete input.signal;
|
|
12
|
+
}
|
|
13
|
+
return (0, sync_fetch_1.default)(input, init);
|
|
14
|
+
};
|
|
15
|
+
exports.defaultSyncFetch = defaultSyncFetch;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-labels */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.handleAsyncIterable = void 0;
|
|
5
|
+
let decodeUint8Array;
|
|
6
|
+
if (globalThis.Buffer) {
|
|
7
|
+
decodeUint8Array = uint8Array => globalThis.Buffer.from(uint8Array).toString('utf-8');
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
const textDecoder = new TextDecoder();
|
|
11
|
+
decodeUint8Array = uint8Array => textDecoder.decode(uint8Array, { stream: true });
|
|
12
|
+
}
|
|
13
|
+
async function* handleAsyncIterable(asyncIterable) {
|
|
14
|
+
outer: for await (const chunk of asyncIterable) {
|
|
15
|
+
const chunkStr = typeof chunk === 'string' ? chunk : decodeUint8Array(chunk);
|
|
16
|
+
for (const part of chunkStr.split('\n\n')) {
|
|
17
|
+
if (part) {
|
|
18
|
+
const eventStr = part.split('event: ')[1];
|
|
19
|
+
const dataStr = part.split('data: ')[1];
|
|
20
|
+
if (eventStr === 'complete') {
|
|
21
|
+
break outer;
|
|
22
|
+
}
|
|
23
|
+
if (dataStr) {
|
|
24
|
+
const data = JSON.parse(dataStr);
|
|
25
|
+
yield data.payload || data;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.handleAsyncIterable = handleAsyncIterable;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleEventStreamResponse = void 0;
|
|
4
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
5
|
+
const handleAsyncIterable_js_1 = require("./handleAsyncIterable.js");
|
|
6
|
+
const handleReadableStream_js_1 = require("./handleReadableStream.js");
|
|
7
|
+
async function handleEventStreamResponse(response) {
|
|
8
|
+
// node-fetch returns body as a promise so we need to resolve it
|
|
9
|
+
const body = response.body;
|
|
10
|
+
if (body) {
|
|
11
|
+
if ((0, utils_1.isAsyncIterable)(body)) {
|
|
12
|
+
return (0, handleAsyncIterable_js_1.handleAsyncIterable)(body);
|
|
13
|
+
}
|
|
14
|
+
return (0, handleReadableStream_js_1.handleReadableStream)(body);
|
|
15
|
+
}
|
|
16
|
+
throw new Error('Response body is expected to be a readable stream but got; ' + (0, utils_1.inspect)(body));
|
|
17
|
+
}
|
|
18
|
+
exports.handleEventStreamResponse = handleEventStreamResponse;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable no-labels */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.handleReadableStream = void 0;
|
|
5
|
+
async function* handleReadableStream(readableStream) {
|
|
6
|
+
const textDecoderStream = new TextDecoderStream();
|
|
7
|
+
const decodedStream = readableStream.pipeThrough(textDecoderStream);
|
|
8
|
+
const reader = decodedStream.getReader();
|
|
9
|
+
outer: while (true) {
|
|
10
|
+
const { value, done } = await reader.read();
|
|
11
|
+
if (value) {
|
|
12
|
+
for (const part of value.split('\n\n')) {
|
|
13
|
+
if (part) {
|
|
14
|
+
const eventStr = part.split('event: ')[1];
|
|
15
|
+
const dataStr = part.split('data: ')[1];
|
|
16
|
+
if (eventStr === 'complete') {
|
|
17
|
+
break outer;
|
|
18
|
+
}
|
|
19
|
+
if (dataStr) {
|
|
20
|
+
const data = JSON.parse(dataStr);
|
|
21
|
+
yield data.payload || data;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (done) {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.handleReadableStream = handleReadableStream;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleMultipartMixedResponse = void 0;
|
|
4
|
+
const node_1 = require("meros/node");
|
|
5
|
+
const browser_1 = require("meros/browser");
|
|
6
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
7
|
+
const merge_1 = require("dset/merge");
|
|
8
|
+
function isIncomingMessage(body) {
|
|
9
|
+
return body != null && typeof body === 'object' && 'pipe' in body;
|
|
10
|
+
}
|
|
11
|
+
async function handleMultipartMixedResponse(response) {
|
|
12
|
+
const body = await response.body;
|
|
13
|
+
const contentType = response.headers.get('content-type') || '';
|
|
14
|
+
let asyncIterator;
|
|
15
|
+
if (isIncomingMessage(body)) {
|
|
16
|
+
// Meros/node expects headers as an object map with the content-type prop
|
|
17
|
+
body.headers = {
|
|
18
|
+
'content-type': contentType,
|
|
19
|
+
};
|
|
20
|
+
// And it expects `IncomingMessage` and `node-fetch` returns `body` as `Promise<PassThrough>`
|
|
21
|
+
asyncIterator = (await (0, node_1.meros)(body));
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Nothing is needed for regular `Response`.
|
|
25
|
+
asyncIterator = (await (0, browser_1.meros)(response));
|
|
26
|
+
}
|
|
27
|
+
const executionResult = {};
|
|
28
|
+
return (0, utils_1.mapAsyncIterator)(asyncIterator, (part) => {
|
|
29
|
+
if (part.json) {
|
|
30
|
+
const chunk = part.body;
|
|
31
|
+
if (chunk.path) {
|
|
32
|
+
if (chunk.data) {
|
|
33
|
+
const path = ['data'];
|
|
34
|
+
(0, merge_1.dset)(executionResult, path.concat(chunk.path), chunk.data);
|
|
35
|
+
}
|
|
36
|
+
if (chunk.errors) {
|
|
37
|
+
executionResult.errors = (executionResult.errors || []).concat(chunk.errors);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
if (chunk.data) {
|
|
42
|
+
executionResult.data = chunk.data;
|
|
43
|
+
}
|
|
44
|
+
if (chunk.errors) {
|
|
45
|
+
executionResult.errors = chunk.errors;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return executionResult;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
exports.handleMultipartMixedResponse = handleMultipartMixedResponse;
|
package/cjs/index.js
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UrlLoader = exports.SubscriptionProtocol = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/* eslint-disable no-case-declarations */
|
|
6
|
+
/// <reference lib="dom" />
|
|
7
|
+
const graphql_1 = require("graphql");
|
|
8
|
+
const utils_1 = require("@graphql-tools/utils");
|
|
9
|
+
const wrap_1 = require("@graphql-tools/wrap");
|
|
10
|
+
const graphql_ws_1 = require("graphql-ws");
|
|
11
|
+
const isomorphic_ws_1 = tslib_1.__importDefault(require("isomorphic-ws"));
|
|
12
|
+
const extract_files_1 = require("extract-files");
|
|
13
|
+
const value_or_promise_1 = require("value-or-promise");
|
|
14
|
+
const graphql_live_query_1 = require("@n1ru4l/graphql-live-query");
|
|
15
|
+
const defaultAsyncFetch_js_1 = require("./defaultAsyncFetch.js");
|
|
16
|
+
const defaultSyncFetch_js_1 = require("./defaultSyncFetch.js");
|
|
17
|
+
const handleMultipartMixedResponse_js_1 = require("./handleMultipartMixedResponse.js");
|
|
18
|
+
const handleEventStreamResponse_js_1 = require("./event-stream/handleEventStreamResponse.js");
|
|
19
|
+
const addCancelToResponseStream_js_1 = require("./addCancelToResponseStream.js");
|
|
20
|
+
const fetch_1 = require("@whatwg-node/fetch");
|
|
21
|
+
const utils_js_1 = require("./utils.js");
|
|
22
|
+
const asyncImport = (moduleName) => Promise.resolve().then(() => tslib_1.__importStar(require(moduleName)));
|
|
23
|
+
const syncImport = (moduleName) => require(moduleName);
|
|
24
|
+
var SubscriptionProtocol;
|
|
25
|
+
(function (SubscriptionProtocol) {
|
|
26
|
+
SubscriptionProtocol["WS"] = "WS";
|
|
27
|
+
/**
|
|
28
|
+
* Use legacy web socket protocol `graphql-ws` instead of the more current standard `graphql-transport-ws`
|
|
29
|
+
*/
|
|
30
|
+
SubscriptionProtocol["LEGACY_WS"] = "LEGACY_WS";
|
|
31
|
+
/**
|
|
32
|
+
* Use SSE for subscription instead of WebSocket
|
|
33
|
+
*/
|
|
34
|
+
SubscriptionProtocol["SSE"] = "SSE";
|
|
35
|
+
/**
|
|
36
|
+
* Use `graphql-sse` for subscriptions
|
|
37
|
+
*/
|
|
38
|
+
SubscriptionProtocol["GRAPHQL_SSE"] = "GRAPHQL_SSE";
|
|
39
|
+
})(SubscriptionProtocol = exports.SubscriptionProtocol || (exports.SubscriptionProtocol = {}));
|
|
40
|
+
function isCompatibleUri(uri) {
|
|
41
|
+
try {
|
|
42
|
+
// eslint-disable-next-line no-new
|
|
43
|
+
new URL(uri);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* This loader loads a schema from a URL. The loaded schema is a fully-executable,
|
|
52
|
+
* remote schema since it's created using [@graphql-tools/wrap](/docs/remote-schemas).
|
|
53
|
+
*
|
|
54
|
+
* ```
|
|
55
|
+
* const schema = await loadSchema('http://localhost:3000/graphql', {
|
|
56
|
+
* loaders: [
|
|
57
|
+
* new UrlLoader(),
|
|
58
|
+
* ]
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
class UrlLoader {
|
|
63
|
+
createFormDataFromVariables({ query, variables, operationName, extensions, }) {
|
|
64
|
+
const vars = Object.assign({}, variables);
|
|
65
|
+
const { clone, files } = (0, extract_files_1.extractFiles)(vars, 'variables', ((v) => (0, extract_files_1.isExtractableFile)(v) ||
|
|
66
|
+
(v === null || v === void 0 ? void 0 : v.promise) ||
|
|
67
|
+
(0, utils_1.isAsyncIterable)(v) ||
|
|
68
|
+
(v === null || v === void 0 ? void 0 : v.then) ||
|
|
69
|
+
typeof (v === null || v === void 0 ? void 0 : v.arrayBuffer) === 'function'));
|
|
70
|
+
const map = {};
|
|
71
|
+
const uploads = [];
|
|
72
|
+
let currIndex = 0;
|
|
73
|
+
for (const [file, curr] of files) {
|
|
74
|
+
map[currIndex] = curr;
|
|
75
|
+
uploads[currIndex] = file;
|
|
76
|
+
currIndex++;
|
|
77
|
+
}
|
|
78
|
+
const form = new fetch_1.FormData();
|
|
79
|
+
form.append('operations', JSON.stringify({
|
|
80
|
+
query,
|
|
81
|
+
variables: clone,
|
|
82
|
+
operationName,
|
|
83
|
+
extensions,
|
|
84
|
+
}));
|
|
85
|
+
form.append('map', JSON.stringify(map));
|
|
86
|
+
function handleUpload(upload, i) {
|
|
87
|
+
const indexStr = i.toString();
|
|
88
|
+
if (upload != null) {
|
|
89
|
+
const filename = upload.filename || upload.name || upload.path || `blob-${indexStr}`;
|
|
90
|
+
if ((0, utils_js_1.isPromiseLike)(upload)) {
|
|
91
|
+
return upload.then((resolvedUpload) => handleUpload(resolvedUpload, i));
|
|
92
|
+
// If Blob
|
|
93
|
+
}
|
|
94
|
+
else if ((0, utils_js_1.isBlob)(upload)) {
|
|
95
|
+
return upload.arrayBuffer().then((arrayBuffer) => {
|
|
96
|
+
form.append(indexStr, new fetch_1.File([arrayBuffer], filename, { type: upload.type }), filename);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if ((0, utils_js_1.isGraphQLUpload)(upload)) {
|
|
100
|
+
const stream = upload.createReadStream();
|
|
101
|
+
const chunks = [];
|
|
102
|
+
return Promise.resolve().then(async () => {
|
|
103
|
+
for await (const chunk of stream) {
|
|
104
|
+
if (chunk) {
|
|
105
|
+
chunks.push(...chunk);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const blobPart = new Uint8Array(chunks);
|
|
109
|
+
form.append(indexStr, new fetch_1.File([blobPart], filename, { type: upload.mimetype }), filename);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
form.append(indexStr, new fetch_1.File([upload], filename), filename);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return value_or_promise_1.ValueOrPromise.all(uploads.map((upload, i) => new value_or_promise_1.ValueOrPromise(() => handleUpload(upload, i))))
|
|
118
|
+
.then(() => form)
|
|
119
|
+
.resolve();
|
|
120
|
+
}
|
|
121
|
+
prepareGETUrl({ baseUrl, query, variables, operationName, extensions, }) {
|
|
122
|
+
const HTTP_URL = switchProtocols(baseUrl, {
|
|
123
|
+
wss: 'https',
|
|
124
|
+
ws: 'http',
|
|
125
|
+
});
|
|
126
|
+
const dummyHostname = 'https://dummyhostname.com';
|
|
127
|
+
const validUrl = HTTP_URL.startsWith('http')
|
|
128
|
+
? HTTP_URL
|
|
129
|
+
: HTTP_URL.startsWith('/')
|
|
130
|
+
? `${dummyHostname}${HTTP_URL}`
|
|
131
|
+
: `${dummyHostname}/${HTTP_URL}`;
|
|
132
|
+
const urlObj = new URL(validUrl);
|
|
133
|
+
urlObj.searchParams.set('query', query);
|
|
134
|
+
if (variables && Object.keys(variables).length > 0) {
|
|
135
|
+
urlObj.searchParams.set('variables', JSON.stringify(variables));
|
|
136
|
+
}
|
|
137
|
+
if (operationName) {
|
|
138
|
+
urlObj.searchParams.set('operationName', operationName);
|
|
139
|
+
}
|
|
140
|
+
if (extensions) {
|
|
141
|
+
urlObj.searchParams.set('extensions', JSON.stringify(extensions));
|
|
142
|
+
}
|
|
143
|
+
const finalUrl = urlObj.toString().replace(dummyHostname, '');
|
|
144
|
+
return finalUrl;
|
|
145
|
+
}
|
|
146
|
+
buildHTTPExecutor(initialEndpoint, fetch, options) {
|
|
147
|
+
const defaultMethod = this.getDefaultMethodFromOptions(options === null || options === void 0 ? void 0 : options.method, 'POST');
|
|
148
|
+
const HTTP_URL = switchProtocols(initialEndpoint, {
|
|
149
|
+
wss: 'https',
|
|
150
|
+
ws: 'http',
|
|
151
|
+
});
|
|
152
|
+
const executor = (request) => {
|
|
153
|
+
var _a, _b;
|
|
154
|
+
const controller = new fetch_1.AbortController();
|
|
155
|
+
let method = defaultMethod;
|
|
156
|
+
const operationAst = (0, utils_1.getOperationASTFromRequest)(request);
|
|
157
|
+
const operationType = operationAst.operation;
|
|
158
|
+
if ((options === null || options === void 0 ? void 0 : options.useGETForQueries) && operationType === 'query') {
|
|
159
|
+
method = 'GET';
|
|
160
|
+
}
|
|
161
|
+
let accept = 'application/json, multipart/mixed';
|
|
162
|
+
if (operationType === 'subscription' || (0, graphql_live_query_1.isLiveQueryOperationDefinitionNode)(operationAst)) {
|
|
163
|
+
method = 'GET';
|
|
164
|
+
accept = 'text/event-stream';
|
|
165
|
+
}
|
|
166
|
+
const endpoint = ((_a = request.extensions) === null || _a === void 0 ? void 0 : _a.endpoint) || HTTP_URL;
|
|
167
|
+
const headers = Object.assign({
|
|
168
|
+
accept,
|
|
169
|
+
}, options === null || options === void 0 ? void 0 : options.headers, ((_b = request.extensions) === null || _b === void 0 ? void 0 : _b.headers) || {});
|
|
170
|
+
const query = (0, graphql_1.print)(request.document);
|
|
171
|
+
const requestBody = {
|
|
172
|
+
query,
|
|
173
|
+
variables: request.variables,
|
|
174
|
+
operationName: request.operationName,
|
|
175
|
+
extensions: request.extensions,
|
|
176
|
+
};
|
|
177
|
+
let timeoutId;
|
|
178
|
+
if (options === null || options === void 0 ? void 0 : options.timeout) {
|
|
179
|
+
timeoutId = setTimeout(() => {
|
|
180
|
+
if (!controller.signal.aborted) {
|
|
181
|
+
controller.abort();
|
|
182
|
+
}
|
|
183
|
+
}, options.timeout);
|
|
184
|
+
}
|
|
185
|
+
const credentials = (options === null || options === void 0 ? void 0 : options.credentials) !== 'disable' ? (options === null || options === void 0 ? void 0 : options.credentials) || 'same-origin' : null;
|
|
186
|
+
return new value_or_promise_1.ValueOrPromise(() => {
|
|
187
|
+
switch (method) {
|
|
188
|
+
case 'GET':
|
|
189
|
+
const finalUrl = this.prepareGETUrl({
|
|
190
|
+
baseUrl: endpoint,
|
|
191
|
+
...requestBody,
|
|
192
|
+
});
|
|
193
|
+
return fetch(finalUrl, {
|
|
194
|
+
method: 'GET',
|
|
195
|
+
...(credentials != null ? { credentials } : {}),
|
|
196
|
+
headers,
|
|
197
|
+
signal: controller.signal,
|
|
198
|
+
});
|
|
199
|
+
case 'POST':
|
|
200
|
+
if (options === null || options === void 0 ? void 0 : options.multipart) {
|
|
201
|
+
return new value_or_promise_1.ValueOrPromise(() => this.createFormDataFromVariables(requestBody))
|
|
202
|
+
.then(form => fetch(endpoint, {
|
|
203
|
+
method: 'POST',
|
|
204
|
+
...(credentials != null ? { credentials } : {}),
|
|
205
|
+
body: form,
|
|
206
|
+
headers,
|
|
207
|
+
signal: controller.signal,
|
|
208
|
+
}))
|
|
209
|
+
.resolve();
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
return fetch(endpoint, {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
...(credentials != null ? { credentials } : {}),
|
|
215
|
+
body: JSON.stringify(requestBody),
|
|
216
|
+
headers: {
|
|
217
|
+
'content-type': 'application/json',
|
|
218
|
+
...headers,
|
|
219
|
+
},
|
|
220
|
+
signal: controller.signal,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
.then((fetchResult) => {
|
|
226
|
+
if (timeoutId != null) {
|
|
227
|
+
clearTimeout(timeoutId);
|
|
228
|
+
}
|
|
229
|
+
// Retry should respect HTTP Errors
|
|
230
|
+
if ((options === null || options === void 0 ? void 0 : options.retry) != null && !fetchResult.status.toString().startsWith('2')) {
|
|
231
|
+
throw new Error(fetchResult.statusText || `HTTP Error: ${fetchResult.status}`);
|
|
232
|
+
}
|
|
233
|
+
const contentType = fetchResult.headers.get('content-type');
|
|
234
|
+
if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('text/event-stream')) {
|
|
235
|
+
return (0, handleEventStreamResponse_js_1.handleEventStreamResponse)(fetchResult).then(resultStream => (0, addCancelToResponseStream_js_1.addCancelToResponseStream)(resultStream, controller));
|
|
236
|
+
}
|
|
237
|
+
else if (contentType === null || contentType === void 0 ? void 0 : contentType.includes('multipart/mixed')) {
|
|
238
|
+
return (0, handleMultipartMixedResponse_js_1.handleMultipartMixedResponse)(fetchResult).then(resultStream => (0, addCancelToResponseStream_js_1.addCancelToResponseStream)(resultStream, controller));
|
|
239
|
+
}
|
|
240
|
+
return fetchResult.text();
|
|
241
|
+
})
|
|
242
|
+
.then(result => {
|
|
243
|
+
if (typeof result === 'string') {
|
|
244
|
+
if (result) {
|
|
245
|
+
return JSON.parse(result);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
.resolve();
|
|
253
|
+
};
|
|
254
|
+
if ((options === null || options === void 0 ? void 0 : options.retry) != null) {
|
|
255
|
+
return function retryExecutor(request) {
|
|
256
|
+
let result;
|
|
257
|
+
let error;
|
|
258
|
+
let attempt = 0;
|
|
259
|
+
function retryAttempt() {
|
|
260
|
+
attempt++;
|
|
261
|
+
if (attempt > options.retry) {
|
|
262
|
+
if (result != null) {
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
if (error != null) {
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
throw new Error('No result');
|
|
269
|
+
}
|
|
270
|
+
return new value_or_promise_1.ValueOrPromise(() => executor(request))
|
|
271
|
+
.then(res => {
|
|
272
|
+
var _a;
|
|
273
|
+
result = res;
|
|
274
|
+
if ((_a = result === null || result === void 0 ? void 0 : result.errors) === null || _a === void 0 ? void 0 : _a.length) {
|
|
275
|
+
return retryAttempt();
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
})
|
|
279
|
+
.catch((e) => {
|
|
280
|
+
error = e;
|
|
281
|
+
return retryAttempt();
|
|
282
|
+
})
|
|
283
|
+
.resolve();
|
|
284
|
+
}
|
|
285
|
+
return retryAttempt();
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return executor;
|
|
289
|
+
}
|
|
290
|
+
buildWSExecutor(subscriptionsEndpoint, webSocketImpl, connectionParams) {
|
|
291
|
+
const WS_URL = switchProtocols(subscriptionsEndpoint, {
|
|
292
|
+
https: 'wss',
|
|
293
|
+
http: 'ws',
|
|
294
|
+
});
|
|
295
|
+
const subscriptionClient = (0, graphql_ws_1.createClient)({
|
|
296
|
+
url: WS_URL,
|
|
297
|
+
webSocketImpl,
|
|
298
|
+
connectionParams,
|
|
299
|
+
lazy: true,
|
|
300
|
+
});
|
|
301
|
+
return ({ document, variables, operationName, extensions }) => {
|
|
302
|
+
const query = (0, graphql_1.print)(document);
|
|
303
|
+
return (0, utils_1.observableToAsyncIterable)({
|
|
304
|
+
subscribe: observer => {
|
|
305
|
+
const unsubscribe = subscriptionClient.subscribe({
|
|
306
|
+
query,
|
|
307
|
+
variables: variables,
|
|
308
|
+
operationName,
|
|
309
|
+
extensions,
|
|
310
|
+
}, observer);
|
|
311
|
+
return {
|
|
312
|
+
unsubscribe,
|
|
313
|
+
};
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
buildWSLegacyExecutor(subscriptionsEndpoint, WebSocketImpl, options) {
|
|
319
|
+
const WS_URL = switchProtocols(subscriptionsEndpoint, {
|
|
320
|
+
https: 'wss',
|
|
321
|
+
http: 'ws',
|
|
322
|
+
});
|
|
323
|
+
const observerById = new Map();
|
|
324
|
+
let websocket = null;
|
|
325
|
+
const ensureWebsocket = () => {
|
|
326
|
+
websocket = new WebSocketImpl(WS_URL, 'graphql-ws', {
|
|
327
|
+
followRedirects: true,
|
|
328
|
+
headers: options === null || options === void 0 ? void 0 : options.headers,
|
|
329
|
+
rejectUnauthorized: false,
|
|
330
|
+
skipUTF8Validation: true,
|
|
331
|
+
});
|
|
332
|
+
websocket.onopen = () => {
|
|
333
|
+
let payload = {};
|
|
334
|
+
switch (typeof (options === null || options === void 0 ? void 0 : options.connectionParams)) {
|
|
335
|
+
case 'function':
|
|
336
|
+
payload = options === null || options === void 0 ? void 0 : options.connectionParams();
|
|
337
|
+
break;
|
|
338
|
+
case 'object':
|
|
339
|
+
payload = options === null || options === void 0 ? void 0 : options.connectionParams;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
websocket.send(JSON.stringify({
|
|
343
|
+
type: utils_js_1.LEGACY_WS.CONNECTION_INIT,
|
|
344
|
+
payload,
|
|
345
|
+
}));
|
|
346
|
+
};
|
|
347
|
+
};
|
|
348
|
+
const cleanupWebsocket = () => {
|
|
349
|
+
if (websocket != null && observerById.size === 0) {
|
|
350
|
+
websocket.send(JSON.stringify({
|
|
351
|
+
type: utils_js_1.LEGACY_WS.CONNECTION_TERMINATE,
|
|
352
|
+
}));
|
|
353
|
+
websocket.terminate();
|
|
354
|
+
websocket = null;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
return function legacyExecutor(request) {
|
|
358
|
+
const id = Date.now().toString();
|
|
359
|
+
return (0, utils_1.observableToAsyncIterable)({
|
|
360
|
+
subscribe(observer) {
|
|
361
|
+
ensureWebsocket();
|
|
362
|
+
if (websocket == null) {
|
|
363
|
+
throw new Error(`WebSocket connection is not found!`);
|
|
364
|
+
}
|
|
365
|
+
websocket.onmessage = event => {
|
|
366
|
+
const data = JSON.parse(event.data.toString('utf-8'));
|
|
367
|
+
switch (data.type) {
|
|
368
|
+
case utils_js_1.LEGACY_WS.CONNECTION_ACK: {
|
|
369
|
+
if (websocket == null) {
|
|
370
|
+
throw new Error(`WebSocket connection is not found!`);
|
|
371
|
+
}
|
|
372
|
+
websocket.send(JSON.stringify({
|
|
373
|
+
type: utils_js_1.LEGACY_WS.START,
|
|
374
|
+
id,
|
|
375
|
+
payload: {
|
|
376
|
+
query: (0, graphql_1.print)(request.document),
|
|
377
|
+
variables: request.variables,
|
|
378
|
+
operationName: request.operationName,
|
|
379
|
+
},
|
|
380
|
+
}));
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
383
|
+
case utils_js_1.LEGACY_WS.CONNECTION_ERROR: {
|
|
384
|
+
observer.error(data.payload);
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
case utils_js_1.LEGACY_WS.CONNECTION_KEEP_ALIVE: {
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
case utils_js_1.LEGACY_WS.DATA: {
|
|
391
|
+
observer.next(data.payload);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case utils_js_1.LEGACY_WS.COMPLETE: {
|
|
395
|
+
if (websocket == null) {
|
|
396
|
+
throw new Error(`WebSocket connection is not found!`);
|
|
397
|
+
}
|
|
398
|
+
websocket.send(JSON.stringify({
|
|
399
|
+
type: utils_js_1.LEGACY_WS.CONNECTION_TERMINATE,
|
|
400
|
+
}));
|
|
401
|
+
observer.complete();
|
|
402
|
+
cleanupWebsocket();
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
return {
|
|
408
|
+
unsubscribe: () => {
|
|
409
|
+
websocket === null || websocket === void 0 ? void 0 : websocket.send(JSON.stringify({
|
|
410
|
+
type: utils_js_1.LEGACY_WS.STOP,
|
|
411
|
+
id,
|
|
412
|
+
}));
|
|
413
|
+
cleanupWebsocket();
|
|
414
|
+
},
|
|
415
|
+
};
|
|
416
|
+
},
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
getFetch(customFetch, importFn) {
|
|
421
|
+
if (customFetch) {
|
|
422
|
+
if (typeof customFetch === 'string') {
|
|
423
|
+
const [moduleName, fetchFnName] = customFetch.split('#');
|
|
424
|
+
return new value_or_promise_1.ValueOrPromise(() => importFn(moduleName))
|
|
425
|
+
.then(module => (fetchFnName ? module[fetchFnName] : module))
|
|
426
|
+
.resolve();
|
|
427
|
+
}
|
|
428
|
+
else if (typeof customFetch === 'function') {
|
|
429
|
+
return customFetch;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (importFn === asyncImport) {
|
|
433
|
+
return defaultAsyncFetch_js_1.defaultAsyncFetch;
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
return defaultSyncFetch_js_1.defaultSyncFetch;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
getDefaultMethodFromOptions(method, defaultMethod) {
|
|
440
|
+
if (method) {
|
|
441
|
+
defaultMethod = method;
|
|
442
|
+
}
|
|
443
|
+
return defaultMethod;
|
|
444
|
+
}
|
|
445
|
+
getWebSocketImpl(importFn, options) {
|
|
446
|
+
if (typeof (options === null || options === void 0 ? void 0 : options.webSocketImpl) === 'string') {
|
|
447
|
+
const [moduleName, webSocketImplName] = options.webSocketImpl.split('#');
|
|
448
|
+
return new value_or_promise_1.ValueOrPromise(() => importFn(moduleName))
|
|
449
|
+
.then(importedModule => (webSocketImplName ? importedModule[webSocketImplName] : importedModule))
|
|
450
|
+
.resolve();
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
const websocketImpl = (options === null || options === void 0 ? void 0 : options.webSocketImpl) || isomorphic_ws_1.default;
|
|
454
|
+
return websocketImpl;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
buildSubscriptionExecutor(subscriptionsEndpoint, fetch, importFn, options) {
|
|
458
|
+
if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.SSE) {
|
|
459
|
+
return this.buildHTTPExecutor(subscriptionsEndpoint, fetch, options);
|
|
460
|
+
}
|
|
461
|
+
else if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.GRAPHQL_SSE) {
|
|
462
|
+
if (!(options === null || options === void 0 ? void 0 : options.subscriptionsEndpoint)) {
|
|
463
|
+
// when no custom subscriptions endpoint is specified,
|
|
464
|
+
// graphql-sse is recommended to be used on `/graphql/stream`
|
|
465
|
+
subscriptionsEndpoint += '/stream';
|
|
466
|
+
}
|
|
467
|
+
return this.buildHTTPExecutor(subscriptionsEndpoint, fetch, options);
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
const webSocketImpl$ = new value_or_promise_1.ValueOrPromise(() => this.getWebSocketImpl(importFn, options));
|
|
471
|
+
const executor$ = webSocketImpl$.then(webSocketImpl => {
|
|
472
|
+
if ((options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) === SubscriptionProtocol.LEGACY_WS) {
|
|
473
|
+
return this.buildWSLegacyExecutor(subscriptionsEndpoint, webSocketImpl, options);
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
return this.buildWSExecutor(subscriptionsEndpoint, webSocketImpl, options === null || options === void 0 ? void 0 : options.connectionParams);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
return request => executor$.then(executor => executor(request)).resolve();
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
getExecutor(endpoint, importFn, options) {
|
|
483
|
+
const fetch$ = new value_or_promise_1.ValueOrPromise(() => this.getFetch(options === null || options === void 0 ? void 0 : options.customFetch, importFn));
|
|
484
|
+
const httpExecutor$ = fetch$.then(fetch => {
|
|
485
|
+
return this.buildHTTPExecutor(endpoint, fetch, options);
|
|
486
|
+
});
|
|
487
|
+
if ((options === null || options === void 0 ? void 0 : options.subscriptionsEndpoint) != null || (options === null || options === void 0 ? void 0 : options.subscriptionsProtocol) !== SubscriptionProtocol.SSE) {
|
|
488
|
+
const subscriptionExecutor$ = fetch$.then(fetch => {
|
|
489
|
+
const subscriptionsEndpoint = (options === null || options === void 0 ? void 0 : options.subscriptionsEndpoint) || endpoint;
|
|
490
|
+
return this.buildSubscriptionExecutor(subscriptionsEndpoint, fetch, importFn, options);
|
|
491
|
+
});
|
|
492
|
+
// eslint-disable-next-line no-inner-declarations
|
|
493
|
+
function getExecutorByRequest(request) {
|
|
494
|
+
const operationAst = (0, utils_1.getOperationASTFromRequest)(request);
|
|
495
|
+
if (operationAst.operation === 'subscription' ||
|
|
496
|
+
(0, graphql_live_query_1.isLiveQueryOperationDefinitionNode)(operationAst, request.variables)) {
|
|
497
|
+
return subscriptionExecutor$;
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
return httpExecutor$;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return request => getExecutorByRequest(request)
|
|
504
|
+
.then(executor => executor(request))
|
|
505
|
+
.resolve();
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
return request => httpExecutor$.then(executor => executor(request)).resolve();
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
getExecutorAsync(endpoint, options) {
|
|
512
|
+
return this.getExecutor(endpoint, asyncImport, options);
|
|
513
|
+
}
|
|
514
|
+
getExecutorSync(endpoint, options) {
|
|
515
|
+
return this.getExecutor(endpoint, syncImport, options);
|
|
516
|
+
}
|
|
517
|
+
handleSDL(pointer, fetch, options) {
|
|
518
|
+
const defaultMethod = this.getDefaultMethodFromOptions(options === null || options === void 0 ? void 0 : options.method, 'GET');
|
|
519
|
+
return new value_or_promise_1.ValueOrPromise(() => fetch(pointer, {
|
|
520
|
+
method: defaultMethod,
|
|
521
|
+
headers: options.headers,
|
|
522
|
+
}))
|
|
523
|
+
.then(response => response.text())
|
|
524
|
+
.then(schemaString => (0, utils_1.parseGraphQLSDL)(pointer, schemaString, options))
|
|
525
|
+
.resolve();
|
|
526
|
+
}
|
|
527
|
+
async load(pointer, options) {
|
|
528
|
+
if (!isCompatibleUri(pointer)) {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
let source = {
|
|
532
|
+
location: pointer,
|
|
533
|
+
};
|
|
534
|
+
let executor;
|
|
535
|
+
if ((options === null || options === void 0 ? void 0 : options.handleAsSDL) || pointer.endsWith('.graphql') || pointer.endsWith('.graphqls')) {
|
|
536
|
+
const fetch = await this.getFetch(options === null || options === void 0 ? void 0 : options.customFetch, asyncImport);
|
|
537
|
+
source = await this.handleSDL(pointer, fetch, options);
|
|
538
|
+
if (!source.schema && !source.document && !source.rawSDL) {
|
|
539
|
+
throw new Error(`Invalid SDL response`);
|
|
540
|
+
}
|
|
541
|
+
source.schema =
|
|
542
|
+
source.schema ||
|
|
543
|
+
(source.document
|
|
544
|
+
? (0, graphql_1.buildASTSchema)(source.document, options)
|
|
545
|
+
: source.rawSDL
|
|
546
|
+
? (0, graphql_1.buildSchema)(source.rawSDL, options)
|
|
547
|
+
: undefined);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
executor = this.getExecutorAsync(pointer, options);
|
|
551
|
+
source.schema = await (0, wrap_1.introspectSchema)(executor, {}, options);
|
|
552
|
+
}
|
|
553
|
+
if (!source.schema) {
|
|
554
|
+
throw new Error(`Invalid introspected schema`);
|
|
555
|
+
}
|
|
556
|
+
if (options === null || options === void 0 ? void 0 : options.endpoint) {
|
|
557
|
+
executor = this.getExecutorAsync(options.endpoint, options);
|
|
558
|
+
}
|
|
559
|
+
if (executor) {
|
|
560
|
+
source.schema = (0, wrap_1.wrapSchema)({
|
|
561
|
+
schema: source.schema,
|
|
562
|
+
executor,
|
|
563
|
+
batch: options === null || options === void 0 ? void 0 : options.batch,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return [source];
|
|
567
|
+
}
|
|
568
|
+
loadSync(pointer, options) {
|
|
569
|
+
if (!isCompatibleUri(pointer)) {
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
let source = {
|
|
573
|
+
location: pointer,
|
|
574
|
+
};
|
|
575
|
+
let executor;
|
|
576
|
+
if ((options === null || options === void 0 ? void 0 : options.handleAsSDL) || pointer.endsWith('.graphql') || pointer.endsWith('.graphqls')) {
|
|
577
|
+
const fetch = this.getFetch(options === null || options === void 0 ? void 0 : options.customFetch, syncImport);
|
|
578
|
+
source = this.handleSDL(pointer, fetch, options);
|
|
579
|
+
if (!source.schema && !source.document && !source.rawSDL) {
|
|
580
|
+
throw new Error(`Invalid SDL response`);
|
|
581
|
+
}
|
|
582
|
+
source.schema =
|
|
583
|
+
source.schema ||
|
|
584
|
+
(source.document
|
|
585
|
+
? (0, graphql_1.buildASTSchema)(source.document, options)
|
|
586
|
+
: source.rawSDL
|
|
587
|
+
? (0, graphql_1.buildSchema)(source.rawSDL, options)
|
|
588
|
+
: undefined);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
executor = this.getExecutorSync(pointer, options);
|
|
592
|
+
source.schema = (0, wrap_1.introspectSchema)(executor, {}, options);
|
|
593
|
+
}
|
|
594
|
+
if (!source.schema) {
|
|
595
|
+
throw new Error(`Invalid introspected schema`);
|
|
596
|
+
}
|
|
597
|
+
if (options === null || options === void 0 ? void 0 : options.endpoint) {
|
|
598
|
+
executor = this.getExecutorSync(options.endpoint, options);
|
|
599
|
+
}
|
|
600
|
+
if (executor) {
|
|
601
|
+
source.schema = (0, wrap_1.wrapSchema)({
|
|
602
|
+
schema: source.schema,
|
|
603
|
+
executor,
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return [source];
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
exports.UrlLoader = UrlLoader;
|
|
610
|
+
function switchProtocols(pointer, protocolMap) {
|
|
611
|
+
return Object.entries(protocolMap).reduce((prev, [source, target]) => prev.replace(`${source}://`, `${target}://`).replace(`${source}:\\`, `${target}:\\`), pointer);
|
|
612
|
+
}
|
package/cjs/utils.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LEGACY_WS = exports.isPromiseLike = exports.isGraphQLUpload = exports.isBlob = void 0;
|
|
4
|
+
function isBlob(obj) {
|
|
5
|
+
return typeof obj.arrayBuffer === 'function';
|
|
6
|
+
}
|
|
7
|
+
exports.isBlob = isBlob;
|
|
8
|
+
function isGraphQLUpload(upload) {
|
|
9
|
+
return typeof upload.createReadStream === 'function';
|
|
10
|
+
}
|
|
11
|
+
exports.isGraphQLUpload = isGraphQLUpload;
|
|
12
|
+
function isPromiseLike(obj) {
|
|
13
|
+
return typeof obj.then === 'function';
|
|
14
|
+
}
|
|
15
|
+
exports.isPromiseLike = isPromiseLike;
|
|
16
|
+
var LEGACY_WS;
|
|
17
|
+
(function (LEGACY_WS) {
|
|
18
|
+
LEGACY_WS["CONNECTION_INIT"] = "connection_init";
|
|
19
|
+
LEGACY_WS["CONNECTION_ACK"] = "connection_ack";
|
|
20
|
+
LEGACY_WS["CONNECTION_ERROR"] = "connection_error";
|
|
21
|
+
LEGACY_WS["CONNECTION_KEEP_ALIVE"] = "ka";
|
|
22
|
+
LEGACY_WS["START"] = "start";
|
|
23
|
+
LEGACY_WS["STOP"] = "stop";
|
|
24
|
+
LEGACY_WS["CONNECTION_TERMINATE"] = "connection_terminate";
|
|
25
|
+
LEGACY_WS["DATA"] = "data";
|
|
26
|
+
LEGACY_WS["ERROR"] = "error";
|
|
27
|
+
LEGACY_WS["COMPLETE"] = "complete";
|
|
28
|
+
})(LEGACY_WS = exports.LEGACY_WS || (exports.LEGACY_WS = {}));
|
|
@@ -5,10 +5,10 @@ if (globalThis.Buffer) {
|
|
|
5
5
|
}
|
|
6
6
|
else {
|
|
7
7
|
const textDecoder = new TextDecoder();
|
|
8
|
-
decodeUint8Array = uint8Array => textDecoder.decode(uint8Array);
|
|
8
|
+
decodeUint8Array = uint8Array => textDecoder.decode(uint8Array, { stream: true });
|
|
9
9
|
}
|
|
10
|
-
export async function*
|
|
11
|
-
outer: for await (const chunk of
|
|
10
|
+
export async function* handleAsyncIterable(asyncIterable) {
|
|
11
|
+
outer: for await (const chunk of asyncIterable) {
|
|
12
12
|
const chunkStr = typeof chunk === 'string' ? chunk : decodeUint8Array(chunk);
|
|
13
13
|
for (const part of chunkStr.split('\n\n')) {
|
|
14
14
|
if (part) {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { inspect, isAsyncIterable } from '@graphql-tools/utils';
|
|
2
|
-
import {
|
|
2
|
+
import { handleAsyncIterable } from './handleAsyncIterable.js';
|
|
3
3
|
import { handleReadableStream } from './handleReadableStream.js';
|
|
4
4
|
export async function handleEventStreamResponse(response) {
|
|
5
5
|
// node-fetch returns body as a promise so we need to resolve it
|
|
6
|
-
const body =
|
|
6
|
+
const body = response.body;
|
|
7
7
|
if (body) {
|
|
8
8
|
if (isAsyncIterable(body)) {
|
|
9
|
-
return
|
|
9
|
+
return handleAsyncIterable(body);
|
|
10
10
|
}
|
|
11
11
|
return handleReadableStream(body);
|
|
12
12
|
}
|
|
@@ -1,127 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
export async function* handleReadableStream(
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
retry: undefined,
|
|
16
|
-
};
|
|
17
|
-
while (!(result = await reader.read()).done) {
|
|
18
|
-
const arr = result.value;
|
|
19
|
-
if (buffer === undefined) {
|
|
20
|
-
buffer = arr;
|
|
21
|
-
position = 0;
|
|
22
|
-
fieldLength = -1;
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
// we're still parsing the old line. Append the new bytes into buffer:
|
|
26
|
-
buffer = concat(buffer, arr);
|
|
27
|
-
}
|
|
28
|
-
const bufLength = buffer.length;
|
|
29
|
-
let lineStart = 0; // index where the current line starts
|
|
30
|
-
while (position < bufLength) {
|
|
31
|
-
if (discardTrailingNewline) {
|
|
32
|
-
if (buffer[position] === 10 /* ControlChars.NewLine */) {
|
|
33
|
-
lineStart = ++position; // skip to next char
|
|
1
|
+
/* eslint-disable no-labels */
|
|
2
|
+
export async function* handleReadableStream(readableStream) {
|
|
3
|
+
const textDecoderStream = new TextDecoderStream();
|
|
4
|
+
const decodedStream = readableStream.pipeThrough(textDecoderStream);
|
|
5
|
+
const reader = decodedStream.getReader();
|
|
6
|
+
outer: while (true) {
|
|
7
|
+
const { value, done } = await reader.read();
|
|
8
|
+
if (value) {
|
|
9
|
+
for (const part of value.split('\n\n')) {
|
|
10
|
+
if (part) {
|
|
11
|
+
const eventStr = part.split('event: ')[1];
|
|
12
|
+
const dataStr = part.split('data: ')[1];
|
|
13
|
+
if (eventStr === 'complete') {
|
|
14
|
+
break outer;
|
|
34
15
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let lineEnd = -1; // index of the \r or \n char
|
|
39
|
-
for (; position < bufLength && lineEnd === -1; ++position) {
|
|
40
|
-
switch (buffer[position]) {
|
|
41
|
-
case 58 /* ControlChars.Colon */: {
|
|
42
|
-
if (fieldLength === -1) {
|
|
43
|
-
// first colon in line
|
|
44
|
-
fieldLength = position - lineStart;
|
|
45
|
-
}
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
case 13 /* ControlChars.CarriageReturn */: {
|
|
49
|
-
discardTrailingNewline = true;
|
|
50
|
-
break;
|
|
51
|
-
}
|
|
52
|
-
case 10 /* ControlChars.NewLine */: {
|
|
53
|
-
lineEnd = position;
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
16
|
+
if (dataStr) {
|
|
17
|
+
const data = JSON.parse(dataStr);
|
|
18
|
+
yield data.payload || data;
|
|
56
19
|
}
|
|
57
20
|
}
|
|
58
|
-
if (lineEnd === -1) {
|
|
59
|
-
// We reached the end of the buffer but the line hasn't ended.
|
|
60
|
-
// Wait for the next arr and then continue parsing:
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
// we've reached the line end, send it out:
|
|
64
|
-
const line = buffer.subarray(lineStart, lineEnd);
|
|
65
|
-
if (line.length === 0) {
|
|
66
|
-
// empty line denotes end of message. Trigger the callback and start a new message:
|
|
67
|
-
if (message.event || message.data) {
|
|
68
|
-
// NOT a server ping (":\n\n")
|
|
69
|
-
yield JSON.parse(message.data);
|
|
70
|
-
message = {
|
|
71
|
-
data: '',
|
|
72
|
-
event: '',
|
|
73
|
-
id: '',
|
|
74
|
-
retry: undefined,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
else if (fieldLength > 0) {
|
|
79
|
-
// exclude comments and lines with no values
|
|
80
|
-
// line is of format "<field>:<value>" or "<field>: <value>"
|
|
81
|
-
// https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
|
|
82
|
-
const field = decoder.decode(line.subarray(0, fieldLength));
|
|
83
|
-
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 /* ControlChars.Space */ ? 2 : 1);
|
|
84
|
-
const value = decoder.decode(line.subarray(valueOffset));
|
|
85
|
-
switch (field) {
|
|
86
|
-
case 'data':
|
|
87
|
-
// if this message already has data, append the new value to the old.
|
|
88
|
-
// otherwise, just set to the new value:
|
|
89
|
-
message.data = message.data ? message.data + '\n' + value : value; // otherwise,
|
|
90
|
-
break;
|
|
91
|
-
case 'event':
|
|
92
|
-
message.event = value;
|
|
93
|
-
break;
|
|
94
|
-
case 'id':
|
|
95
|
-
message.id = value;
|
|
96
|
-
break;
|
|
97
|
-
case 'retry': {
|
|
98
|
-
const retry = parseInt(value, 10);
|
|
99
|
-
message.retry = retry;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
lineStart = position; // we're now on the next line
|
|
105
|
-
fieldLength = -1;
|
|
106
|
-
}
|
|
107
|
-
if (lineStart === bufLength) {
|
|
108
|
-
buffer = undefined; // we've finished reading it
|
|
109
|
-
}
|
|
110
|
-
else if (lineStart !== 0) {
|
|
111
|
-
// Create a new view into buffer beginning at lineStart so we don't
|
|
112
|
-
// need to copy over the previous lines when we get the new arr:
|
|
113
|
-
buffer = buffer.subarray(lineStart);
|
|
114
|
-
position -= lineStart;
|
|
115
21
|
}
|
|
116
22
|
}
|
|
23
|
+
if (done) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
117
26
|
}
|
|
118
|
-
finally {
|
|
119
|
-
reader.releaseLock();
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
function concat(a, b) {
|
|
123
|
-
const res = new Uint8Array(a.length + b.length);
|
|
124
|
-
res.set(a);
|
|
125
|
-
res.set(b, a.length);
|
|
126
|
-
return res;
|
|
127
27
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function handleAsyncIterable(asyncIterable: AsyncIterable<Uint8Array | string>): AsyncGenerator<any, void, unknown>;
|
|
@@ -1,15 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Represents a message sent in an event stream
|
|
3
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format
|
|
4
|
-
*/
|
|
5
|
-
export interface EventSourceMessage {
|
|
6
|
-
/** The event ID to set the EventSource object's last event ID value. */
|
|
7
|
-
id: string;
|
|
8
|
-
/** A string identifying the type of event described. */
|
|
9
|
-
event: string;
|
|
10
|
-
/** The event data */
|
|
11
|
-
data: string;
|
|
12
|
-
/** The reconnection interval (in milliseconds) to wait before retrying the connection */
|
|
13
|
-
retry?: number;
|
|
14
|
-
}
|
|
15
|
-
export declare function handleReadableStream(stream: ReadableStream<Uint8Array>): AsyncGenerator<any, void, unknown>;
|
|
1
|
+
export declare function handleReadableStream(readableStream: ReadableStream<Uint8Array>): AsyncGenerator<any, void, unknown>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function handleReadable(readable: AsyncIterable<Uint8Array | string>): AsyncGenerator<any, void, unknown>;
|