Skip to content

Commit 785e906

Browse files
committed
Allow reading multiple data chunks from single read() chunk.
1 parent 9a430cc commit 785e906

File tree

1 file changed

+43
-17
lines changed

1 file changed

+43
-17
lines changed

packages/functions/src/service.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class FunctionsService implements _FirebaseService {
107107
messagingProvider: Provider<MessagingInternalComponentName>,
108108
appCheckProvider: Provider<AppCheckInternalComponentName>,
109109
regionOrCustomDomain: string = DEFAULT_REGION,
110-
readonly fetchImpl: typeof fetch = fetch,
110+
readonly fetchImpl: typeof fetch = (...args) => fetch(...args),
111111
) {
112112
this.contextProvider = new ContextProvider(
113113
authProvider,
@@ -450,6 +450,7 @@ async function streamAtURL(
450450
const reader = response.body!.getReader();
451451
const decoder = new TextDecoder();
452452

453+
let pendingLines: string[] = [];
453454
let buffer = '';
454455
let resultResolver: (value: unknown) => void;
455456
let resultRejecter: (reason: any) => void;
@@ -470,6 +471,30 @@ async function streamAtURL(
470471

471472
const stream = {
472473
[Symbol.asyncIterator]() {
474+
475+
const processLine = (line: string | undefined) => {
476+
// ignore all other lines (newline, comments, etc.)
477+
if (!line?.startsWith('data: ')) return null;
478+
479+
try {
480+
const jsonData = JSON.parse(line.slice(6));
481+
if ('result' in jsonData) {
482+
resultResolver(decode(jsonData.result));
483+
return { done: true, value: undefined };
484+
}
485+
if ('message' in jsonData) {
486+
return { done: false, value: decode(jsonData.message) };
487+
}
488+
if ('error' in jsonData) {
489+
const error = _errorForResponse(0, jsonData);
490+
resultRejecter(error);
491+
throw error;
492+
}
493+
return null; // Unrecognize keys. Skip this line.
494+
} catch (error) {
495+
// Not json. Skip this line.
496+
}
497+
};
473498
return {
474499
async next() {
475500
if (options?.signal?.aborted) {
@@ -481,29 +506,30 @@ async function streamAtURL(
481506
throw error;
482507
}
483508

509+
while (pendingLines.length > 0) {
510+
const result = processLine(pendingLines.shift());
511+
if (result) return result;
512+
}
513+
484514
while (true) {
485515
const { value, done } = await reader.read();
486-
if (done) return { done: true, value: undefined };
516+
517+
if (done) {
518+
if (buffer.trim()) {
519+
const result = processLine(buffer);
520+
if (result) return result;
521+
}
522+
return { done: true, value: undefined };
523+
}
487524

488525
buffer += decoder.decode(value, { stream: true });
489526
const lines = buffer.split('\n');
490527
buffer = lines.pop() || '';
528+
pendingLines.push(...lines.filter(line => line.trim()));
491529

492-
for (const line of lines) {
493-
if (line.startsWith('data: ')) {
494-
const jsonData = JSON.parse(line.slice(6));
495-
if ('result' in jsonData) {
496-
resultResolver(decode(jsonData.result));
497-
return { done: true, value: undefined };
498-
} else if ('message' in jsonData) {
499-
return { done: false, value: decode(jsonData.message) };
500-
} else if ('error' in jsonData) {
501-
const error = _errorForResponse(0, jsonData);
502-
resultRejecter(error)
503-
throw error;
504-
}
505-
}
506-
// ignore all other lines (newline, comments, etc.)
530+
if (pendingLines.length > 0) {
531+
const result = processLine(pendingLines.shift());
532+
if (result) return result;
507533
}
508534
}
509535
}

0 commit comments

Comments
 (0)