@firebase/functions
Advanced tools
Comparing version 0.11.10 to 0.12.0-20241210130030
@@ -402,2 +402,3 @@ import { _registerComponent, registerVersion, _getProvider, getApp } from '@firebase/app'; | ||
const DEFAULT_REGION = 'us-central1'; | ||
const responseLineRE = /^data: (.*?)(?:\n|$)/; | ||
/** | ||
@@ -436,4 +437,5 @@ * Returns a Promise that will be rejected after the given duration. | ||
*/ | ||
constructor(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain = DEFAULT_REGION) { | ||
constructor(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain = DEFAULT_REGION, fetchImpl = (...args) => fetch(...args)) { | ||
this.app = app; | ||
this.fetchImpl = fetchImpl; | ||
this.emulatorOrigin = null; | ||
@@ -497,5 +499,9 @@ this.contextProvider = new ContextProvider(authProvider, messagingProvider, appCheckProvider); | ||
function httpsCallable$1(functionsInstance, name, options) { | ||
return (data => { | ||
const callable = (data) => { | ||
return call(functionsInstance, name, data, options || {}); | ||
}); | ||
}; | ||
callable.stream = (data, options) => { | ||
return stream(functionsInstance, name, data, options); | ||
}; | ||
return callable; | ||
} | ||
@@ -508,5 +514,9 @@ /** | ||
function httpsCallableFromURL$1(functionsInstance, url, options) { | ||
return (data => { | ||
const callable = (data) => { | ||
return callAtURL(functionsInstance, url, data, options || {}); | ||
}); | ||
}; | ||
callable.stream = (data, options) => { | ||
return streamAtURL(functionsInstance, url, data, options || {}); | ||
}; | ||
return callable; | ||
} | ||
@@ -520,7 +530,7 @@ /** | ||
*/ | ||
async function postJSON(url, body, headers) { | ||
async function postJSON(url, body, headers, fetchImpl) { | ||
headers['Content-Type'] = 'application/json'; | ||
let response; | ||
try { | ||
response = await fetch(url, { | ||
response = await fetchImpl(url, { | ||
method: 'POST', | ||
@@ -554,5 +564,25 @@ body: JSON.stringify(body), | ||
/** | ||
* Creates authorization headers for Firebase Functions requests. | ||
* @param functionsInstance The Firebase Functions service instance. | ||
* @param options Options for the callable function, including AppCheck token settings. | ||
* @return A Promise that resolves a headers map to include in outgoing fetch request. | ||
*/ | ||
async function makeAuthHeaders(functionsInstance, options) { | ||
const headers = {}; | ||
const context = await functionsInstance.contextProvider.getContext(options.limitedUseAppCheckTokens); | ||
if (context.authToken) { | ||
headers['Authorization'] = 'Bearer ' + context.authToken; | ||
} | ||
if (context.messagingToken) { | ||
headers['Firebase-Instance-ID-Token'] = context.messagingToken; | ||
} | ||
if (context.appCheckToken !== null) { | ||
headers['X-Firebase-AppCheck'] = context.appCheckToken; | ||
} | ||
return headers; | ||
} | ||
/** | ||
* Calls a callable function asynchronously and returns the result. | ||
* @param name The name of the callable trigger. | ||
* @param data The data to pass as params to the function.s | ||
* @param data The data to pass as params to the function. | ||
*/ | ||
@@ -566,3 +596,3 @@ function call(functionsInstance, name, data, options) { | ||
* @param url The url of the callable trigger. | ||
* @param data The data to pass as params to the function.s | ||
* @param data The data to pass as params to the function. | ||
*/ | ||
@@ -574,13 +604,3 @@ async function callAtURL(functionsInstance, url, data, options) { | ||
// Add a header for the authToken. | ||
const headers = {}; | ||
const context = await functionsInstance.contextProvider.getContext(options.limitedUseAppCheckTokens); | ||
if (context.authToken) { | ||
headers['Authorization'] = 'Bearer ' + context.authToken; | ||
} | ||
if (context.messagingToken) { | ||
headers['Firebase-Instance-ID-Token'] = context.messagingToken; | ||
} | ||
if (context.appCheckToken !== null) { | ||
headers['X-Firebase-AppCheck'] = context.appCheckToken; | ||
} | ||
const headers = await makeAuthHeaders(functionsInstance, options); | ||
// Default timeout to 70s, but let the options override it. | ||
@@ -590,3 +610,3 @@ const timeout = options.timeout || 70000; | ||
const response = await Promise.race([ | ||
postJSON(url, body, headers), | ||
postJSON(url, body, headers, functionsInstance.fetchImpl), | ||
failAfterHandle.promise, | ||
@@ -623,5 +643,207 @@ functionsInstance.cancelAllRequests | ||
} | ||
/** | ||
* Calls a callable function asynchronously and returns a streaming result. | ||
* @param name The name of the callable trigger. | ||
* @param data The data to pass as params to the function. | ||
* @param options Streaming request options. | ||
*/ | ||
function stream(functionsInstance, name, data, options) { | ||
const url = functionsInstance._url(name); | ||
return streamAtURL(functionsInstance, url, data, options || {}); | ||
} | ||
/** | ||
* Calls a callable function asynchronously and return a streaming result. | ||
* @param url The url of the callable trigger. | ||
* @param data The data to pass as params to the function. | ||
* @param options Streaming request options. | ||
*/ | ||
async function streamAtURL(functionsInstance, url, data, options) { | ||
var _a; | ||
// Encode any special types, such as dates, in the input data. | ||
data = encode(data); | ||
const body = { data }; | ||
// | ||
// Add a header for the authToken. | ||
const headers = await makeAuthHeaders(functionsInstance, options); | ||
headers['Content-Type'] = 'application/json'; | ||
headers['Accept'] = 'text/event-stream'; | ||
let response; | ||
try { | ||
response = await functionsInstance.fetchImpl(url, { | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
headers, | ||
signal: options === null || options === void 0 ? void 0 : options.signal | ||
}); | ||
} | ||
catch (e) { | ||
if (e instanceof Error && e.name === 'AbortError') { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled.'); | ||
return { | ||
data: Promise.reject(error), | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
return { | ||
next() { | ||
return Promise.reject(error); | ||
} | ||
}; | ||
} | ||
} | ||
}; | ||
} | ||
// This could be an unhandled error on the backend, or it could be a | ||
// network error. There's no way to know, since an unhandled error on the | ||
// backend will fail to set the proper CORS header, and thus will be | ||
// treated as a network error by fetch. | ||
const error = _errorForResponse(0, null); | ||
return { | ||
data: Promise.reject(error), | ||
// Return an empty async iterator | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
return { | ||
next() { | ||
return Promise.reject(error); | ||
} | ||
}; | ||
} | ||
} | ||
}; | ||
} | ||
let resultResolver; | ||
let resultRejecter; | ||
const resultPromise = new Promise((resolve, reject) => { | ||
resultResolver = resolve; | ||
resultRejecter = reject; | ||
}); | ||
(_a = options === null || options === void 0 ? void 0 : options.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', () => { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled.'); | ||
resultRejecter(error); | ||
}); | ||
const reader = response.body.getReader(); | ||
const rstream = createResponseStream(reader, resultResolver, resultRejecter, options === null || options === void 0 ? void 0 : options.signal); | ||
return { | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
const rreader = rstream.getReader(); | ||
return { | ||
async next() { | ||
const { value, done } = await rreader.read(); | ||
return { value: value, done }; | ||
}, | ||
async return() { | ||
await rreader.cancel(); | ||
return { done: true, value: undefined }; | ||
} | ||
}; | ||
} | ||
}, | ||
data: resultPromise | ||
}; | ||
} | ||
/** | ||
* Creates a ReadableStream that processes a streaming response from a streaming | ||
* callable function that returns data in server-sent event format. | ||
* | ||
* @param reader The underlying reader providing raw response data | ||
* @param resultResolver Callback to resolve the final result when received | ||
* @param resultRejecter Callback to reject with an error if encountered | ||
* @param signal Optional AbortSignal to cancel the stream processing | ||
* @returns A ReadableStream that emits decoded messages from the response | ||
* | ||
* The returned ReadableStream: | ||
* 1. Emits individual messages when "message" data is received | ||
* 2. Resolves with the final result when a "result" message is received | ||
* 3. Rejects with an error if an "error" message is received | ||
*/ | ||
function createResponseStream(reader, resultResolver, resultRejecter, signal) { | ||
const processLine = (line, controller) => { | ||
const match = line.match(responseLineRE); | ||
// ignore all other lines (newline, comments, etc.) | ||
if (!match) { | ||
return; | ||
} | ||
const data = match[1]; | ||
try { | ||
const jsonData = JSON.parse(data); | ||
if ('result' in jsonData) { | ||
resultResolver(decode(jsonData.result)); | ||
return; | ||
} | ||
if ('message' in jsonData) { | ||
controller.enqueue(decode(jsonData.message)); | ||
return; | ||
} | ||
if ('error' in jsonData) { | ||
const error = _errorForResponse(0, jsonData); | ||
controller.error(error); | ||
resultRejecter(error); | ||
return; | ||
} | ||
} | ||
catch (error) { | ||
if (error instanceof FunctionsError) { | ||
controller.error(error); | ||
resultRejecter(error); | ||
return; | ||
} | ||
// ignore other parsing errors | ||
} | ||
}; | ||
const decoder = new TextDecoder(); | ||
return new ReadableStream({ | ||
start(controller) { | ||
let currentText = ''; | ||
return pump(); | ||
async function pump() { | ||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled'); | ||
controller.error(error); | ||
resultRejecter(error); | ||
return Promise.resolve(); | ||
} | ||
try { | ||
const { value, done } = await reader.read(); | ||
if (done) { | ||
if (currentText.trim()) { | ||
processLine(currentText.trim(), controller); | ||
} | ||
controller.close(); | ||
return; | ||
} | ||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled'); | ||
controller.error(error); | ||
resultRejecter(error); | ||
await reader.cancel(); | ||
return; | ||
} | ||
currentText += decoder.decode(value, { stream: true }); | ||
const lines = currentText.split('\n'); | ||
currentText = lines.pop() || ''; | ||
for (const line of lines) { | ||
if (line.trim()) { | ||
processLine(line.trim(), controller); | ||
} | ||
} | ||
return pump(); | ||
} | ||
catch (error) { | ||
const functionsError = error instanceof FunctionsError | ||
? error | ||
: _errorForResponse(0, null); | ||
controller.error(functionsError); | ||
resultRejecter(functionsError); | ||
} | ||
} | ||
}, | ||
cancel() { | ||
return reader.cancel(); | ||
} | ||
}); | ||
} | ||
const name = "@firebase/functions"; | ||
const version = "0.11.10"; | ||
const version = "0.12.0-20241210130030"; | ||
@@ -628,0 +850,0 @@ /** |
@@ -45,3 +45,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
/** | ||
@@ -52,2 +52,2 @@ * Returns a reference to the callable HTTPS trigger with the specified url. | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; |
@@ -29,7 +29,18 @@ /** | ||
/** | ||
* A reference to a "callable" HTTP trigger in Google Cloud Functions. | ||
* An `HttpsCallableStreamResult` wraps a single streaming result from a function call. | ||
* @public | ||
*/ | ||
export interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown> { | ||
readonly data: Promise<ResponseData>; | ||
readonly stream: AsyncIterable<StreamData>; | ||
} | ||
/** | ||
* A reference to a "callable" HTTP trigger in Cloud Functions. | ||
* @param data - Data to be passed to callable function. | ||
* @public | ||
*/ | ||
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>; | ||
export interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown> { | ||
(data?: RequestData | null): Promise<HttpsCallableResult<ResponseData>>; | ||
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>; | ||
} | ||
/** | ||
@@ -46,3 +57,3 @@ * An interface for metadata about how calls should be executed. | ||
/** | ||
* If set to true, uses limited-use App Check token for callable function requests from this | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
@@ -54,2 +65,19 @@ * replay protection enabled. By default, this is false. | ||
/** | ||
* An interface for metadata about how a stream call should be executed. | ||
* @public | ||
*/ | ||
export interface HttpsCallableStreamOptions { | ||
/** | ||
* An `AbortSignal` that can be used to cancel the streaming response. When the signal is aborted, | ||
* the underlying HTTP connection will be terminated. | ||
*/ | ||
signal?: AbortSignal; | ||
/** | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
* replay protection enabled. By default, this is false. | ||
*/ | ||
limitedUseAppCheckTokens?: boolean; | ||
} | ||
/** | ||
* A `Functions` instance. | ||
@@ -56,0 +84,0 @@ * @public |
@@ -45,2 +45,3 @@ /** | ||
readonly app: FirebaseApp; | ||
readonly fetchImpl: typeof fetch; | ||
readonly contextProvider: ContextProvider; | ||
@@ -56,3 +57,3 @@ emulatorOrigin: string | null; | ||
*/ | ||
constructor(app: FirebaseApp, authProvider: Provider<FirebaseAuthInternalName>, messagingProvider: Provider<MessagingInternalComponentName>, appCheckProvider: Provider<AppCheckInternalComponentName>, regionOrCustomDomain?: string); | ||
constructor(app: FirebaseApp, authProvider: Provider<FirebaseAuthInternalName>, messagingProvider: Provider<MessagingInternalComponentName>, appCheckProvider: Provider<AppCheckInternalComponentName>, regionOrCustomDomain?: string, fetchImpl?: typeof fetch); | ||
_delete(): Promise<void>; | ||
@@ -81,3 +82,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData, ResponseData>(functionsInstance: FunctionsService, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData, ResponseData, StreamData = unknown>(functionsInstance: FunctionsService, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
/** | ||
@@ -88,2 +89,2 @@ * Returns a reference to the callable https trigger with the given url. | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData, ResponseData>(functionsInstance: FunctionsService, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData, ResponseData, StreamData = unknown>(functionsInstance: FunctionsService, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; |
@@ -129,7 +129,10 @@ /** | ||
/** | ||
* A reference to a "callable" HTTP trigger in Google Cloud Functions. | ||
* A reference to a "callable" HTTP trigger in Cloud Functions. | ||
* @param data - Data to be passed to callable function. | ||
* @public | ||
*/ | ||
export declare type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>; | ||
export declare interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown> { | ||
(data?: RequestData | null): Promise<HttpsCallableResult<ResponseData>>; | ||
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>; | ||
} | ||
@@ -141,3 +144,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
@@ -149,3 +152,3 @@ /** | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
@@ -163,3 +166,3 @@ /** | ||
/** | ||
* If set to true, uses limited-use App Check token for callable function requests from this | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
@@ -182,2 +185,29 @@ * replay protection enabled. By default, this is false. | ||
/** | ||
* An interface for metadata about how a stream call should be executed. | ||
* @public | ||
*/ | ||
export declare interface HttpsCallableStreamOptions { | ||
/** | ||
* An `AbortSignal` that can be used to cancel the streaming response. When the signal is aborted, | ||
* the underlying HTTP connection will be terminated. | ||
*/ | ||
signal?: AbortSignal; | ||
/** | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
* replay protection enabled. By default, this is false. | ||
*/ | ||
limitedUseAppCheckTokens?: boolean; | ||
} | ||
/** | ||
* An `HttpsCallableStreamResult` wraps a single streaming result from a function call. | ||
* @public | ||
*/ | ||
export declare interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown> { | ||
readonly data: Promise<ResponseData>; | ||
readonly stream: AsyncIterable<StreamData>; | ||
} | ||
export { } |
@@ -129,7 +129,10 @@ /** | ||
/** | ||
* A reference to a "callable" HTTP trigger in Google Cloud Functions. | ||
* A reference to a "callable" HTTP trigger in Cloud Functions. | ||
* @param data - Data to be passed to callable function. | ||
* @public | ||
*/ | ||
export declare type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>; | ||
export declare interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown> { | ||
(data?: RequestData | null): Promise<HttpsCallableResult<ResponseData>>; | ||
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>; | ||
} | ||
@@ -141,3 +144,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
@@ -149,3 +152,3 @@ /** | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
@@ -163,3 +166,3 @@ /** | ||
/** | ||
* If set to true, uses limited-use App Check token for callable function requests from this | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
@@ -182,2 +185,29 @@ * replay protection enabled. By default, this is false. | ||
/** | ||
* An interface for metadata about how a stream call should be executed. | ||
* @public | ||
*/ | ||
export declare interface HttpsCallableStreamOptions { | ||
/** | ||
* An `AbortSignal` that can be used to cancel the streaming response. When the signal is aborted, | ||
* the underlying HTTP connection will be terminated. | ||
*/ | ||
signal?: AbortSignal; | ||
/** | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
* replay protection enabled. By default, this is false. | ||
*/ | ||
limitedUseAppCheckTokens?: boolean; | ||
} | ||
/** | ||
* An `HttpsCallableStreamResult` wraps a single streaming result from a function call. | ||
* @public | ||
*/ | ||
export declare interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown> { | ||
readonly data: Promise<ResponseData>; | ||
readonly stream: AsyncIterable<StreamData>; | ||
} | ||
export { } |
@@ -406,2 +406,3 @@ 'use strict'; | ||
const DEFAULT_REGION = 'us-central1'; | ||
const responseLineRE = /^data: (.*?)(?:\n|$)/; | ||
/** | ||
@@ -440,4 +441,5 @@ * Returns a Promise that will be rejected after the given duration. | ||
*/ | ||
constructor(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain = DEFAULT_REGION) { | ||
constructor(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain = DEFAULT_REGION, fetchImpl = (...args) => fetch(...args)) { | ||
this.app = app; | ||
this.fetchImpl = fetchImpl; | ||
this.emulatorOrigin = null; | ||
@@ -501,5 +503,9 @@ this.contextProvider = new ContextProvider(authProvider, messagingProvider, appCheckProvider); | ||
function httpsCallable$1(functionsInstance, name, options) { | ||
return (data => { | ||
const callable = (data) => { | ||
return call(functionsInstance, name, data, options || {}); | ||
}); | ||
}; | ||
callable.stream = (data, options) => { | ||
return stream(functionsInstance, name, data, options); | ||
}; | ||
return callable; | ||
} | ||
@@ -512,5 +518,9 @@ /** | ||
function httpsCallableFromURL$1(functionsInstance, url, options) { | ||
return (data => { | ||
const callable = (data) => { | ||
return callAtURL(functionsInstance, url, data, options || {}); | ||
}); | ||
}; | ||
callable.stream = (data, options) => { | ||
return streamAtURL(functionsInstance, url, data, options || {}); | ||
}; | ||
return callable; | ||
} | ||
@@ -524,7 +534,7 @@ /** | ||
*/ | ||
async function postJSON(url, body, headers) { | ||
async function postJSON(url, body, headers, fetchImpl) { | ||
headers['Content-Type'] = 'application/json'; | ||
let response; | ||
try { | ||
response = await fetch(url, { | ||
response = await fetchImpl(url, { | ||
method: 'POST', | ||
@@ -558,5 +568,25 @@ body: JSON.stringify(body), | ||
/** | ||
* Creates authorization headers for Firebase Functions requests. | ||
* @param functionsInstance The Firebase Functions service instance. | ||
* @param options Options for the callable function, including AppCheck token settings. | ||
* @return A Promise that resolves a headers map to include in outgoing fetch request. | ||
*/ | ||
async function makeAuthHeaders(functionsInstance, options) { | ||
const headers = {}; | ||
const context = await functionsInstance.contextProvider.getContext(options.limitedUseAppCheckTokens); | ||
if (context.authToken) { | ||
headers['Authorization'] = 'Bearer ' + context.authToken; | ||
} | ||
if (context.messagingToken) { | ||
headers['Firebase-Instance-ID-Token'] = context.messagingToken; | ||
} | ||
if (context.appCheckToken !== null) { | ||
headers['X-Firebase-AppCheck'] = context.appCheckToken; | ||
} | ||
return headers; | ||
} | ||
/** | ||
* Calls a callable function asynchronously and returns the result. | ||
* @param name The name of the callable trigger. | ||
* @param data The data to pass as params to the function.s | ||
* @param data The data to pass as params to the function. | ||
*/ | ||
@@ -570,3 +600,3 @@ function call(functionsInstance, name, data, options) { | ||
* @param url The url of the callable trigger. | ||
* @param data The data to pass as params to the function.s | ||
* @param data The data to pass as params to the function. | ||
*/ | ||
@@ -578,13 +608,3 @@ async function callAtURL(functionsInstance, url, data, options) { | ||
// Add a header for the authToken. | ||
const headers = {}; | ||
const context = await functionsInstance.contextProvider.getContext(options.limitedUseAppCheckTokens); | ||
if (context.authToken) { | ||
headers['Authorization'] = 'Bearer ' + context.authToken; | ||
} | ||
if (context.messagingToken) { | ||
headers['Firebase-Instance-ID-Token'] = context.messagingToken; | ||
} | ||
if (context.appCheckToken !== null) { | ||
headers['X-Firebase-AppCheck'] = context.appCheckToken; | ||
} | ||
const headers = await makeAuthHeaders(functionsInstance, options); | ||
// Default timeout to 70s, but let the options override it. | ||
@@ -594,3 +614,3 @@ const timeout = options.timeout || 70000; | ||
const response = await Promise.race([ | ||
postJSON(url, body, headers), | ||
postJSON(url, body, headers, functionsInstance.fetchImpl), | ||
failAfterHandle.promise, | ||
@@ -627,5 +647,207 @@ functionsInstance.cancelAllRequests | ||
} | ||
/** | ||
* Calls a callable function asynchronously and returns a streaming result. | ||
* @param name The name of the callable trigger. | ||
* @param data The data to pass as params to the function. | ||
* @param options Streaming request options. | ||
*/ | ||
function stream(functionsInstance, name, data, options) { | ||
const url = functionsInstance._url(name); | ||
return streamAtURL(functionsInstance, url, data, options || {}); | ||
} | ||
/** | ||
* Calls a callable function asynchronously and return a streaming result. | ||
* @param url The url of the callable trigger. | ||
* @param data The data to pass as params to the function. | ||
* @param options Streaming request options. | ||
*/ | ||
async function streamAtURL(functionsInstance, url, data, options) { | ||
var _a; | ||
// Encode any special types, such as dates, in the input data. | ||
data = encode(data); | ||
const body = { data }; | ||
// | ||
// Add a header for the authToken. | ||
const headers = await makeAuthHeaders(functionsInstance, options); | ||
headers['Content-Type'] = 'application/json'; | ||
headers['Accept'] = 'text/event-stream'; | ||
let response; | ||
try { | ||
response = await functionsInstance.fetchImpl(url, { | ||
method: 'POST', | ||
body: JSON.stringify(body), | ||
headers, | ||
signal: options === null || options === void 0 ? void 0 : options.signal | ||
}); | ||
} | ||
catch (e) { | ||
if (e instanceof Error && e.name === 'AbortError') { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled.'); | ||
return { | ||
data: Promise.reject(error), | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
return { | ||
next() { | ||
return Promise.reject(error); | ||
} | ||
}; | ||
} | ||
} | ||
}; | ||
} | ||
// This could be an unhandled error on the backend, or it could be a | ||
// network error. There's no way to know, since an unhandled error on the | ||
// backend will fail to set the proper CORS header, and thus will be | ||
// treated as a network error by fetch. | ||
const error = _errorForResponse(0, null); | ||
return { | ||
data: Promise.reject(error), | ||
// Return an empty async iterator | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
return { | ||
next() { | ||
return Promise.reject(error); | ||
} | ||
}; | ||
} | ||
} | ||
}; | ||
} | ||
let resultResolver; | ||
let resultRejecter; | ||
const resultPromise = new Promise((resolve, reject) => { | ||
resultResolver = resolve; | ||
resultRejecter = reject; | ||
}); | ||
(_a = options === null || options === void 0 ? void 0 : options.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', () => { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled.'); | ||
resultRejecter(error); | ||
}); | ||
const reader = response.body.getReader(); | ||
const rstream = createResponseStream(reader, resultResolver, resultRejecter, options === null || options === void 0 ? void 0 : options.signal); | ||
return { | ||
stream: { | ||
[Symbol.asyncIterator]() { | ||
const rreader = rstream.getReader(); | ||
return { | ||
async next() { | ||
const { value, done } = await rreader.read(); | ||
return { value: value, done }; | ||
}, | ||
async return() { | ||
await rreader.cancel(); | ||
return { done: true, value: undefined }; | ||
} | ||
}; | ||
} | ||
}, | ||
data: resultPromise | ||
}; | ||
} | ||
/** | ||
* Creates a ReadableStream that processes a streaming response from a streaming | ||
* callable function that returns data in server-sent event format. | ||
* | ||
* @param reader The underlying reader providing raw response data | ||
* @param resultResolver Callback to resolve the final result when received | ||
* @param resultRejecter Callback to reject with an error if encountered | ||
* @param signal Optional AbortSignal to cancel the stream processing | ||
* @returns A ReadableStream that emits decoded messages from the response | ||
* | ||
* The returned ReadableStream: | ||
* 1. Emits individual messages when "message" data is received | ||
* 2. Resolves with the final result when a "result" message is received | ||
* 3. Rejects with an error if an "error" message is received | ||
*/ | ||
function createResponseStream(reader, resultResolver, resultRejecter, signal) { | ||
const processLine = (line, controller) => { | ||
const match = line.match(responseLineRE); | ||
// ignore all other lines (newline, comments, etc.) | ||
if (!match) { | ||
return; | ||
} | ||
const data = match[1]; | ||
try { | ||
const jsonData = JSON.parse(data); | ||
if ('result' in jsonData) { | ||
resultResolver(decode(jsonData.result)); | ||
return; | ||
} | ||
if ('message' in jsonData) { | ||
controller.enqueue(decode(jsonData.message)); | ||
return; | ||
} | ||
if ('error' in jsonData) { | ||
const error = _errorForResponse(0, jsonData); | ||
controller.error(error); | ||
resultRejecter(error); | ||
return; | ||
} | ||
} | ||
catch (error) { | ||
if (error instanceof FunctionsError) { | ||
controller.error(error); | ||
resultRejecter(error); | ||
return; | ||
} | ||
// ignore other parsing errors | ||
} | ||
}; | ||
const decoder = new TextDecoder(); | ||
return new ReadableStream({ | ||
start(controller) { | ||
let currentText = ''; | ||
return pump(); | ||
async function pump() { | ||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled'); | ||
controller.error(error); | ||
resultRejecter(error); | ||
return Promise.resolve(); | ||
} | ||
try { | ||
const { value, done } = await reader.read(); | ||
if (done) { | ||
if (currentText.trim()) { | ||
processLine(currentText.trim(), controller); | ||
} | ||
controller.close(); | ||
return; | ||
} | ||
if (signal === null || signal === void 0 ? void 0 : signal.aborted) { | ||
const error = new FunctionsError('cancelled', 'Request was cancelled'); | ||
controller.error(error); | ||
resultRejecter(error); | ||
await reader.cancel(); | ||
return; | ||
} | ||
currentText += decoder.decode(value, { stream: true }); | ||
const lines = currentText.split('\n'); | ||
currentText = lines.pop() || ''; | ||
for (const line of lines) { | ||
if (line.trim()) { | ||
processLine(line.trim(), controller); | ||
} | ||
} | ||
return pump(); | ||
} | ||
catch (error) { | ||
const functionsError = error instanceof FunctionsError | ||
? error | ||
: _errorForResponse(0, null); | ||
controller.error(functionsError); | ||
resultRejecter(functionsError); | ||
} | ||
} | ||
}, | ||
cancel() { | ||
return reader.cancel(); | ||
} | ||
}); | ||
} | ||
const name = "@firebase/functions"; | ||
const version = "0.11.10"; | ||
const version = "0.12.0-20241210130030"; | ||
@@ -632,0 +854,0 @@ /** |
@@ -45,3 +45,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
/** | ||
@@ -52,2 +52,2 @@ * Returns a reference to the callable HTTPS trigger with the specified url. | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData = unknown, ResponseData = unknown, StreamData = unknown>(functionsInstance: Functions, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; |
@@ -29,7 +29,18 @@ /** | ||
/** | ||
* A reference to a "callable" HTTP trigger in Google Cloud Functions. | ||
* An `HttpsCallableStreamResult` wraps a single streaming result from a function call. | ||
* @public | ||
*/ | ||
export interface HttpsCallableStreamResult<ResponseData = unknown, StreamData = unknown> { | ||
readonly data: Promise<ResponseData>; | ||
readonly stream: AsyncIterable<StreamData>; | ||
} | ||
/** | ||
* A reference to a "callable" HTTP trigger in Cloud Functions. | ||
* @param data - Data to be passed to callable function. | ||
* @public | ||
*/ | ||
export type HttpsCallable<RequestData = unknown, ResponseData = unknown> = (data?: RequestData | null) => Promise<HttpsCallableResult<ResponseData>>; | ||
export interface HttpsCallable<RequestData = unknown, ResponseData = unknown, StreamData = unknown> { | ||
(data?: RequestData | null): Promise<HttpsCallableResult<ResponseData>>; | ||
stream: (data?: RequestData | null, options?: HttpsCallableStreamOptions) => Promise<HttpsCallableStreamResult<ResponseData, StreamData>>; | ||
} | ||
/** | ||
@@ -46,3 +57,3 @@ * An interface for metadata about how calls should be executed. | ||
/** | ||
* If set to true, uses limited-use App Check token for callable function requests from this | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
@@ -54,2 +65,19 @@ * replay protection enabled. By default, this is false. | ||
/** | ||
* An interface for metadata about how a stream call should be executed. | ||
* @public | ||
*/ | ||
export interface HttpsCallableStreamOptions { | ||
/** | ||
* An `AbortSignal` that can be used to cancel the streaming response. When the signal is aborted, | ||
* the underlying HTTP connection will be terminated. | ||
*/ | ||
signal?: AbortSignal; | ||
/** | ||
* If set to true, uses a limited-use App Check token for callable function requests from this | ||
* instance of {@link Functions}. You must use limited-use tokens to call functions with | ||
* replay protection enabled. By default, this is false. | ||
*/ | ||
limitedUseAppCheckTokens?: boolean; | ||
} | ||
/** | ||
* A `Functions` instance. | ||
@@ -56,0 +84,0 @@ * @public |
@@ -45,2 +45,3 @@ /** | ||
readonly app: FirebaseApp; | ||
readonly fetchImpl: typeof fetch; | ||
readonly contextProvider: ContextProvider; | ||
@@ -56,3 +57,3 @@ emulatorOrigin: string | null; | ||
*/ | ||
constructor(app: FirebaseApp, authProvider: Provider<FirebaseAuthInternalName>, messagingProvider: Provider<MessagingInternalComponentName>, appCheckProvider: Provider<AppCheckInternalComponentName>, regionOrCustomDomain?: string); | ||
constructor(app: FirebaseApp, authProvider: Provider<FirebaseAuthInternalName>, messagingProvider: Provider<MessagingInternalComponentName>, appCheckProvider: Provider<AppCheckInternalComponentName>, regionOrCustomDomain?: string, fetchImpl?: typeof fetch); | ||
_delete(): Promise<void>; | ||
@@ -81,3 +82,3 @@ /** | ||
*/ | ||
export declare function httpsCallable<RequestData, ResponseData>(functionsInstance: FunctionsService, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallable<RequestData, ResponseData, StreamData = unknown>(functionsInstance: FunctionsService, name: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; | ||
/** | ||
@@ -88,2 +89,2 @@ * Returns a reference to the callable https trigger with the given url. | ||
*/ | ||
export declare function httpsCallableFromURL<RequestData, ResponseData>(functionsInstance: FunctionsService, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData>; | ||
export declare function httpsCallableFromURL<RequestData, ResponseData, StreamData = unknown>(functionsInstance: FunctionsService, url: string, options?: HttpsCallableOptions): HttpsCallable<RequestData, ResponseData, StreamData>; |
{ | ||
"name": "@firebase/functions", | ||
"version": "0.11.10", | ||
"version": "0.12.0-20241210130030", | ||
"description": "", | ||
@@ -40,3 +40,3 @@ "author": "Firebase <firebase-support@google.com> (https://firebase.google.com/)", | ||
"test:node": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'src/{,!(browser)/**/}*.test.ts' --file src/index.ts --config ../../config/mocharc.node.js", | ||
"test:emulator": "env FIREBASE_FUNCTIONS_EMULATOR_ORIGIN=http://localhost:5005 run-p --npm-path npm test:node", | ||
"test:emulator": "env FIREBASE_FUNCTIONS_EMULATOR_ORIGIN=http://127.0.0.1:5005 run-p --npm-path npm test:node", | ||
"trusted-type-check": "tsec -p tsconfig.json --noEmit", | ||
@@ -43,0 +43,0 @@ "api-report": "api-extractor run --local --verbose", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
248435
3205