@@ -20,7 +20,8 @@ import {
20
20
HttpsCallable ,
21
21
HttpsCallableResult ,
22
22
HttpsCallableStreamResult ,
23
- HttpsCallableOptions
23
+ HttpsCallableOptions ,
24
+ HttpsCallableStreamOptions
24
25
} from './public-types' ;
25
26
import { _errorForResponse , FunctionsError } from './error' ;
26
27
import { ContextProvider } from './context' ;
@@ -186,8 +187,8 @@ export function httpsCallable<RequestData, ResponseData, StreamData = unknown>(
186
187
return call ( functionsInstance , name , data , options || { } ) ;
187
188
} ;
188
189
189
- callable . stream = ( data ?: RequestData | null ) => {
190
- return stream ( functionsInstance , name , data ) ;
190
+ callable . stream = ( data ?: RequestData | null , options ?: HttpsCallableStreamOptions ) => {
191
+ return stream ( functionsInstance , name , data , options ) ;
191
192
} ;
192
193
193
194
return callable as HttpsCallable < RequestData , ResponseData , StreamData > ;
@@ -207,8 +208,8 @@ export function httpsCallableFromURL<RequestData, ResponseData, StreamData = unk
207
208
return callAtURL ( functionsInstance , url , data , options || { } ) ;
208
209
} ;
209
210
210
- callable . stream = ( data ?: RequestData | null ) => {
211
- return streamAtURL ( functionsInstance , url , options || { } ) ;
211
+ callable . stream = ( data ?: RequestData | null , options ?: HttpsCallableStreamOptions ) => {
212
+ return streamAtURL ( functionsInstance , url , options ) ;
212
213
} ;
213
214
return callable as HttpsCallable < RequestData , ResponseData , StreamData > ;
214
215
}
@@ -354,25 +355,29 @@ async function callAtURL(
354
355
* Calls a callable function asynchronously and returns a streaming result.
355
356
* @param name The name of the callable trigger.
356
357
* @param data The data to pass as params to the function.
358
+ * @param options Streaming request options.
357
359
*/
358
360
function stream (
359
361
functionsInstance : FunctionsService ,
360
362
name : string ,
361
363
data : unknown ,
364
+ options ?: HttpsCallableStreamOptions
362
365
) : Promise < HttpsCallableStreamResult > {
363
366
const url = functionsInstance . _url ( name ) ;
364
- return streamAtURL ( functionsInstance , url , data ) ;
367
+ return streamAtURL ( functionsInstance , url , options ) ;
365
368
}
366
369
367
370
/**
368
371
* Calls a callable function asynchronously and return a streaming result.
369
372
* @param url The url of the callable trigger.
370
373
* @param data The data to pass as params to the function.
374
+ * @param options Streaming request options.
371
375
*/
372
376
async function streamAtURL (
373
377
functionsInstance : FunctionsService ,
374
378
url : string ,
375
379
data : unknown ,
380
+ options ?: HttpsCallableStreamOptions
376
381
) : Promise < HttpsCallableStreamResult > {
377
382
// Encode any special types, such as dates, in the input data.
378
383
data = encode ( data ) ;
@@ -396,12 +401,31 @@ async function streamAtURL(
396
401
397
402
let response : Response ;
398
403
try {
399
- response = await functionsInstance . fetchImpl ( url , {
404
+ response = await fetch ( url , {
400
405
method : 'POST' ,
401
406
body : JSON . stringify ( body ) ,
402
- headers
407
+ headers,
408
+ signal : options ?. signal
403
409
} ) ;
404
410
} catch ( e ) {
411
+ if ( e instanceof Error && e . name === 'AbortError' ) {
412
+ const error = new FunctionsError (
413
+ 'cancelled' ,
414
+ 'Request was cancelled.'
415
+ ) ;
416
+ return {
417
+ data : Promise . reject ( error ) ,
418
+ stream : {
419
+ [ Symbol . asyncIterator ] ( ) {
420
+ return {
421
+ next ( ) {
422
+ return Promise . reject ( error ) ;
423
+ }
424
+ } ;
425
+ }
426
+ }
427
+ } ;
428
+ }
405
429
// This could be an unhandled error on the backend, or it could be a
406
430
// network error. There's no way to know, since an unhandled error on the
407
431
// backend will fail to set the proper CORS header, and thus will be
@@ -434,10 +458,29 @@ async function streamAtURL(
434
458
resultRejecter = reject ;
435
459
} ) ;
436
460
461
+ // Set up abort handler for the stream
462
+ options ?. signal ?. addEventListener ( 'abort' , ( ) => {
463
+ reader . cancel ( ) ;
464
+ const error = new FunctionsError (
465
+ 'cancelled' ,
466
+ 'Request was cancelled.'
467
+ ) ;
468
+ resultRejecter ( error ) ;
469
+ } ) ;
470
+
437
471
const stream = {
438
472
[ Symbol . asyncIterator ] ( ) {
439
473
return {
440
474
async next ( ) {
475
+ if ( options ?. signal ?. aborted ) {
476
+ const error = new FunctionsError (
477
+ 'cancelled' ,
478
+ 'Request was cancelled.'
479
+ ) ;
480
+ resultRejecter ( error )
481
+ throw error ;
482
+ }
483
+
441
484
while ( true ) {
442
485
const { value, done } = await reader . read ( ) ;
443
486
if ( done ) return { done : true , value : undefined } ;
0 commit comments