@graphql-tools/url-loader 7.12.4 → 7.13.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.
@@ -1,17 +1,17 @@
1
1
  "use strict";
2
2
  /* eslint-disable no-labels */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.handleReadable = void 0;
4
+ exports.handleAsyncIterable = void 0;
5
5
  let decodeUint8Array;
6
6
  if (globalThis.Buffer) {
7
7
  decodeUint8Array = uint8Array => globalThis.Buffer.from(uint8Array).toString('utf-8');
8
8
  }
9
9
  else {
10
10
  const textDecoder = new TextDecoder();
11
- decodeUint8Array = uint8Array => textDecoder.decode(uint8Array);
11
+ decodeUint8Array = uint8Array => textDecoder.decode(uint8Array, { stream: true });
12
12
  }
13
- async function* handleReadable(readable) {
14
- outer: for await (const chunk of readable) {
13
+ async function* handleAsyncIterable(asyncIterable) {
14
+ outer: for await (const chunk of asyncIterable) {
15
15
  const chunkStr = typeof chunk === 'string' ? chunk : decodeUint8Array(chunk);
16
16
  for (const part of chunkStr.split('\n\n')) {
17
17
  if (part) {
@@ -28,4 +28,4 @@ async function* handleReadable(readable) {
28
28
  }
29
29
  }
30
30
  }
31
- exports.handleReadable = handleReadable;
31
+ exports.handleAsyncIterable = handleAsyncIterable;
@@ -2,14 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleEventStreamResponse = void 0;
4
4
  const utils_1 = require("@graphql-tools/utils");
5
- const handleReadable_js_1 = require("./handleReadable.js");
5
+ const handleAsyncIterable_js_1 = require("./handleAsyncIterable.js");
6
6
  const handleReadableStream_js_1 = require("./handleReadableStream.js");
7
7
  async function handleEventStreamResponse(response) {
8
8
  // node-fetch returns body as a promise so we need to resolve it
9
- const body = await response.body;
9
+ const body = response.body;
10
10
  if (body) {
11
11
  if ((0, utils_1.isAsyncIterable)(body)) {
12
- return (0, handleReadable_js_1.handleReadable)(body);
12
+ return (0, handleAsyncIterable_js_1.handleAsyncIterable)(body);
13
13
  }
14
14
  return (0, handleReadableStream_js_1.handleReadableStream)(body);
15
15
  }
@@ -1,131 +1,31 @@
1
1
  "use strict";
2
- // Based on https://github.com/Azure/fetch-event-source/blob/main/src/parse.ts
2
+ /* eslint-disable no-labels */
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.handleReadableStream = void 0;
5
- async function* handleReadableStream(stream) {
6
- const decoder = new TextDecoder();
7
- const reader = stream.getReader();
8
- let buffer;
9
- let position = 0; // current read position
10
- let fieldLength = -1; // length of the `field` portion of the line
11
- let discardTrailingNewline = false;
12
- try {
13
- let result;
14
- let message = {
15
- data: '',
16
- event: '',
17
- id: '',
18
- retry: undefined,
19
- };
20
- while (!(result = await reader.read()).done) {
21
- const arr = result.value;
22
- if (buffer === undefined) {
23
- buffer = arr;
24
- position = 0;
25
- fieldLength = -1;
26
- }
27
- else {
28
- // we're still parsing the old line. Append the new bytes into buffer:
29
- buffer = concat(buffer, arr);
30
- }
31
- const bufLength = buffer.length;
32
- let lineStart = 0; // index where the current line starts
33
- while (position < bufLength) {
34
- if (discardTrailingNewline) {
35
- if (buffer[position] === 10 /* ControlChars.NewLine */) {
36
- lineStart = ++position; // skip to next char
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;
37
18
  }
38
- discardTrailingNewline = false;
39
- }
40
- // start looking forward till the end of line:
41
- let lineEnd = -1; // index of the \r or \n char
42
- for (; position < bufLength && lineEnd === -1; ++position) {
43
- switch (buffer[position]) {
44
- case 58 /* ControlChars.Colon */: {
45
- if (fieldLength === -1) {
46
- // first colon in line
47
- fieldLength = position - lineStart;
48
- }
49
- break;
50
- }
51
- case 13 /* ControlChars.CarriageReturn */: {
52
- discardTrailingNewline = true;
53
- break;
54
- }
55
- case 10 /* ControlChars.NewLine */: {
56
- lineEnd = position;
57
- break;
58
- }
19
+ if (dataStr) {
20
+ const data = JSON.parse(dataStr);
21
+ yield data.payload || data;
59
22
  }
60
23
  }
61
- if (lineEnd === -1) {
62
- // We reached the end of the buffer but the line hasn't ended.
63
- // Wait for the next arr and then continue parsing:
64
- break;
65
- }
66
- // we've reached the line end, send it out:
67
- const line = buffer.subarray(lineStart, lineEnd);
68
- if (line.length === 0) {
69
- // empty line denotes end of message. Trigger the callback and start a new message:
70
- if (message.event || message.data) {
71
- // NOT a server ping (":\n\n")
72
- yield JSON.parse(message.data);
73
- message = {
74
- data: '',
75
- event: '',
76
- id: '',
77
- retry: undefined,
78
- };
79
- }
80
- }
81
- else if (fieldLength > 0) {
82
- // exclude comments and lines with no values
83
- // line is of format "<field>:<value>" or "<field>: <value>"
84
- // https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation
85
- const field = decoder.decode(line.subarray(0, fieldLength));
86
- const valueOffset = fieldLength + (line[fieldLength + 1] === 32 /* ControlChars.Space */ ? 2 : 1);
87
- const value = decoder.decode(line.subarray(valueOffset));
88
- switch (field) {
89
- case 'data':
90
- // if this message already has data, append the new value to the old.
91
- // otherwise, just set to the new value:
92
- message.data = message.data ? message.data + '\n' + value : value; // otherwise,
93
- break;
94
- case 'event':
95
- message.event = value;
96
- break;
97
- case 'id':
98
- message.id = value;
99
- break;
100
- case 'retry': {
101
- const retry = parseInt(value, 10);
102
- message.retry = retry;
103
- break;
104
- }
105
- }
106
- }
107
- lineStart = position; // we're now on the next line
108
- fieldLength = -1;
109
- }
110
- if (lineStart === bufLength) {
111
- buffer = undefined; // we've finished reading it
112
- }
113
- else if (lineStart !== 0) {
114
- // Create a new view into buffer beginning at lineStart so we don't
115
- // need to copy over the previous lines when we get the new arr:
116
- buffer = buffer.subarray(lineStart);
117
- position -= lineStart;
118
24
  }
119
25
  }
120
- }
121
- finally {
122
- reader.releaseLock();
26
+ if (done) {
27
+ break;
28
+ }
123
29
  }
124
30
  }
125
31
  exports.handleReadableStream = handleReadableStream;
126
- function concat(a, b) {
127
- const res = new Uint8Array(a.length + b.length);
128
- res.set(a);
129
- res.set(b, a.length);
130
- return res;
131
- }
@@ -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* handleReadable(readable) {
11
- outer: for await (const chunk of readable) {
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 { handleReadable } from './handleReadable.js';
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 = await response.body;
6
+ const body = response.body;
7
7
  if (body) {
8
8
  if (isAsyncIterable(body)) {
9
- return handleReadable(body);
9
+ return handleAsyncIterable(body);
10
10
  }
11
11
  return handleReadableStream(body);
12
12
  }
@@ -1,127 +1,27 @@
1
- // Based on https://github.com/Azure/fetch-event-source/blob/main/src/parse.ts
2
- export async function* handleReadableStream(stream) {
3
- const decoder = new TextDecoder();
4
- const reader = stream.getReader();
5
- let buffer;
6
- let position = 0; // current read position
7
- let fieldLength = -1; // length of the `field` portion of the line
8
- let discardTrailingNewline = false;
9
- try {
10
- let result;
11
- let message = {
12
- data: '',
13
- event: '',
14
- id: '',
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
- discardTrailingNewline = false;
36
- }
37
- // start looking forward till the end of line:
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphql-tools/url-loader",
3
- "version": "7.12.4",
3
+ "version": "7.13.0",
4
4
  "description": "A set of utils for faster development of GraphQL tools",
5
5
  "sideEffects": false,
6
6
  "peerDependencies": {
@@ -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>;