···
* The implementation in this file needs to make certain accommodations for:
* - Non-browser or polyfill Fetch APIs
-
* - Node.js-like Fetch implementations (see `toString` below)
* GraphQL over SSE has a reference implementation, which supports non-HTTP/2
* modes and is a faithful implementation of the spec.
···
import type { Operation, OperationResult, ExecutionResult } from '../types';
import { makeResult, makeErrorResult, mergeResultPatch } from '../utils';
-
const decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder() : null;
const boundaryHeaderRe = /boundary="?([^=";]+)"?/i;
const eventStreamRe = /data: ?([^\n]+)/;
type ChunkData = Buffer | Uint8Array;
-
// NOTE: We're avoiding referencing the `Buffer` global here to prevent
-
// auto-polyfilling in Webpack
-
const toString = (input: Buffer | ArrayBuffer): string =>
-
input.constructor.name === 'Buffer'
-
? (input as Buffer).toString()
-
: decoder!.decode(input as ArrayBuffer);
-
async function* streamBody(response: Response): AsyncIterableIterator<string> {
if (response.body![Symbol.asyncIterator]) {
-
for await (const chunk of response.body! as any)
-
yield toString(chunk as ChunkData);
const reader = response.body!.getReader();
let result: ReadableStreamReadResult<ChunkData>;
-
while (!(result = await reader.read()).done) yield toString(result.value);
-
chunks: AsyncIterableIterator<string>,
): AsyncIterableIterator<string> {
let boundaryIndex: number;
for await (const chunk of chunks) {
while ((boundaryIndex = buffer.indexOf(boundary)) > -1) {
yield buffer.slice(0, boundaryIndex);
buffer = buffer.slice(boundaryIndex + boundary.length);
···
): AsyncIterableIterator<ExecutionResult> {
-
for await (const chunk of split(streamBody(response), '\n\n')) {
const match = chunk.match(eventStreamRe);
···
const boundary = '--' + (boundaryHeader ? boundaryHeader[1] : '-');
-
for await (let chunk of split(streamBody(response), '\r\n' + boundary)) {
const preambleIndex = chunk.indexOf(boundary);