Initial Commit
This commit is contained in:
970
node_modules/@firebase/functions/dist/esm/index.esm.js
generated
vendored
Normal file
970
node_modules/@firebase/functions/dist/esm/index.esm.js
generated
vendored
Normal file
@@ -0,0 +1,970 @@
|
||||
import { _isFirebaseServerApp, _registerComponent, registerVersion, _getProvider, getApp } from '@firebase/app';
|
||||
import { FirebaseError, isCloudWorkstation, pingServer, updateEmulatorBanner, getModularInstance, getDefaultEmulatorHostnameAndPort } from '@firebase/util';
|
||||
import { Component } from '@firebase/component';
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const LONG_TYPE = 'type.googleapis.com/google.protobuf.Int64Value';
|
||||
const UNSIGNED_LONG_TYPE = 'type.googleapis.com/google.protobuf.UInt64Value';
|
||||
function mapValues(
|
||||
// { [k: string]: unknown } is no longer a wildcard assignment target after typescript 3.5
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
o, f) {
|
||||
const result = {};
|
||||
for (const key in o) {
|
||||
if (o.hasOwnProperty(key)) {
|
||||
result[key] = f(o[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Takes data and encodes it in a JSON-friendly way, such that types such as
|
||||
* Date are preserved.
|
||||
* @internal
|
||||
* @param data - Data to encode.
|
||||
*/
|
||||
function encode(data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
if (data instanceof Number) {
|
||||
data = data.valueOf();
|
||||
}
|
||||
if (typeof data === 'number' && isFinite(data)) {
|
||||
// Any number in JS is safe to put directly in JSON and parse as a double
|
||||
// without any loss of precision.
|
||||
return data;
|
||||
}
|
||||
if (data === true || data === false) {
|
||||
return data;
|
||||
}
|
||||
if (Object.prototype.toString.call(data) === '[object String]') {
|
||||
return data;
|
||||
}
|
||||
if (data instanceof Date) {
|
||||
return data.toISOString();
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(x => encode(x));
|
||||
}
|
||||
if (typeof data === 'function' || typeof data === 'object') {
|
||||
return mapValues(data, x => encode(x));
|
||||
}
|
||||
// If we got this far, the data is not encodable.
|
||||
throw new Error('Data cannot be encoded in JSON: ' + data);
|
||||
}
|
||||
/**
|
||||
* Takes data that's been encoded in a JSON-friendly form and returns a form
|
||||
* with richer datatypes, such as Dates, etc.
|
||||
* @internal
|
||||
* @param json - JSON to convert.
|
||||
*/
|
||||
function decode(json) {
|
||||
if (json == null) {
|
||||
return json;
|
||||
}
|
||||
if (json['@type']) {
|
||||
switch (json['@type']) {
|
||||
case LONG_TYPE:
|
||||
// Fall through and handle this the same as unsigned.
|
||||
case UNSIGNED_LONG_TYPE: {
|
||||
// Technically, this could work return a valid number for malformed
|
||||
// data if there was a number followed by garbage. But it's just not
|
||||
// worth all the extra code to detect that case.
|
||||
const value = Number(json['value']);
|
||||
if (isNaN(value)) {
|
||||
throw new Error('Data cannot be decoded from JSON: ' + json);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
default: {
|
||||
throw new Error('Data cannot be decoded from JSON: ' + json);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Array.isArray(json)) {
|
||||
return json.map(x => decode(x));
|
||||
}
|
||||
if (typeof json === 'function' || typeof json === 'object') {
|
||||
return mapValues(json, x => decode(x));
|
||||
}
|
||||
// Anything else is safe to return.
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Type constant for Firebase Functions.
|
||||
*/
|
||||
const FUNCTIONS_TYPE = 'functions';
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Standard error codes for different ways a request can fail, as defined by:
|
||||
* https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
|
||||
*
|
||||
* This map is used primarily to convert from a backend error code string to
|
||||
* a client SDK error code string, and make sure it's in the supported set.
|
||||
*/
|
||||
const errorCodeMap = {
|
||||
OK: 'ok',
|
||||
CANCELLED: 'cancelled',
|
||||
UNKNOWN: 'unknown',
|
||||
INVALID_ARGUMENT: 'invalid-argument',
|
||||
DEADLINE_EXCEEDED: 'deadline-exceeded',
|
||||
NOT_FOUND: 'not-found',
|
||||
ALREADY_EXISTS: 'already-exists',
|
||||
PERMISSION_DENIED: 'permission-denied',
|
||||
UNAUTHENTICATED: 'unauthenticated',
|
||||
RESOURCE_EXHAUSTED: 'resource-exhausted',
|
||||
FAILED_PRECONDITION: 'failed-precondition',
|
||||
ABORTED: 'aborted',
|
||||
OUT_OF_RANGE: 'out-of-range',
|
||||
UNIMPLEMENTED: 'unimplemented',
|
||||
INTERNAL: 'internal',
|
||||
UNAVAILABLE: 'unavailable',
|
||||
DATA_LOSS: 'data-loss'
|
||||
};
|
||||
/**
|
||||
* An error returned by the Firebase Functions client SDK.
|
||||
*
|
||||
* See {@link FunctionsErrorCode} for full documentation of codes.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
class FunctionsError extends FirebaseError {
|
||||
/**
|
||||
* Constructs a new instance of the `FunctionsError` class.
|
||||
*/
|
||||
constructor(
|
||||
/**
|
||||
* A standard error code that will be returned to the client. This also
|
||||
* determines the HTTP status code of the response, as defined in code.proto.
|
||||
*/
|
||||
code, message,
|
||||
/**
|
||||
* Additional details to be converted to JSON and included in the error response.
|
||||
*/
|
||||
details) {
|
||||
super(`${FUNCTIONS_TYPE}/${code}`, message || '');
|
||||
this.details = details;
|
||||
// Since the FirebaseError constructor sets the prototype of `this` to FirebaseError.prototype,
|
||||
// we also have to do it in all subclasses to allow for correct `instanceof` checks.
|
||||
Object.setPrototypeOf(this, FunctionsError.prototype);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes an HTTP status code and returns the corresponding ErrorCode.
|
||||
* This is the standard HTTP status code -> error mapping defined in:
|
||||
* https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
|
||||
*
|
||||
* @param status An HTTP status code.
|
||||
* @return The corresponding ErrorCode, or ErrorCode.UNKNOWN if none.
|
||||
*/
|
||||
function codeForHTTPStatus(status) {
|
||||
// Make sure any successful status is OK.
|
||||
if (status >= 200 && status < 300) {
|
||||
return 'ok';
|
||||
}
|
||||
switch (status) {
|
||||
case 0:
|
||||
// This can happen if the server returns 500.
|
||||
return 'internal';
|
||||
case 400:
|
||||
return 'invalid-argument';
|
||||
case 401:
|
||||
return 'unauthenticated';
|
||||
case 403:
|
||||
return 'permission-denied';
|
||||
case 404:
|
||||
return 'not-found';
|
||||
case 409:
|
||||
return 'aborted';
|
||||
case 429:
|
||||
return 'resource-exhausted';
|
||||
case 499:
|
||||
return 'cancelled';
|
||||
case 500:
|
||||
return 'internal';
|
||||
case 501:
|
||||
return 'unimplemented';
|
||||
case 503:
|
||||
return 'unavailable';
|
||||
case 504:
|
||||
return 'deadline-exceeded';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
/**
|
||||
* Takes an HTTP response and returns the corresponding Error, if any.
|
||||
*/
|
||||
function _errorForResponse(status, bodyJSON) {
|
||||
let code = codeForHTTPStatus(status);
|
||||
// Start with reasonable defaults from the status code.
|
||||
let description = code;
|
||||
let details = undefined;
|
||||
// Then look through the body for explicit details.
|
||||
try {
|
||||
const errorJSON = bodyJSON && bodyJSON.error;
|
||||
if (errorJSON) {
|
||||
const status = errorJSON.status;
|
||||
if (typeof status === 'string') {
|
||||
if (!errorCodeMap[status]) {
|
||||
// They must've included an unknown error code in the body.
|
||||
return new FunctionsError('internal', 'internal');
|
||||
}
|
||||
code = errorCodeMap[status];
|
||||
// TODO(klimt): Add better default descriptions for error enums.
|
||||
// The default description needs to be updated for the new code.
|
||||
description = status;
|
||||
}
|
||||
const message = errorJSON.message;
|
||||
if (typeof message === 'string') {
|
||||
description = message;
|
||||
}
|
||||
details = errorJSON.details;
|
||||
if (details !== undefined) {
|
||||
details = decode(details);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
// If we couldn't parse explicit error data, that's fine.
|
||||
}
|
||||
if (code === 'ok') {
|
||||
// Technically, there's an edge case where a developer could explicitly
|
||||
// return an error code of OK, and we will treat it as success, but that
|
||||
// seems reasonable.
|
||||
return null;
|
||||
}
|
||||
return new FunctionsError(code, description, details);
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Helper class to get metadata that should be included with a function call.
|
||||
* @internal
|
||||
*/
|
||||
class ContextProvider {
|
||||
constructor(app, authProvider, messagingProvider, appCheckProvider) {
|
||||
this.app = app;
|
||||
this.auth = null;
|
||||
this.messaging = null;
|
||||
this.appCheck = null;
|
||||
this.serverAppAppCheckToken = null;
|
||||
if (_isFirebaseServerApp(app) && app.settings.appCheckToken) {
|
||||
this.serverAppAppCheckToken = app.settings.appCheckToken;
|
||||
}
|
||||
this.auth = authProvider.getImmediate({ optional: true });
|
||||
this.messaging = messagingProvider.getImmediate({
|
||||
optional: true
|
||||
});
|
||||
if (!this.auth) {
|
||||
authProvider.get().then(auth => (this.auth = auth), () => {
|
||||
/* get() never rejects */
|
||||
});
|
||||
}
|
||||
if (!this.messaging) {
|
||||
messagingProvider.get().then(messaging => (this.messaging = messaging), () => {
|
||||
/* get() never rejects */
|
||||
});
|
||||
}
|
||||
if (!this.appCheck) {
|
||||
appCheckProvider?.get().then(appCheck => (this.appCheck = appCheck), () => {
|
||||
/* get() never rejects */
|
||||
});
|
||||
}
|
||||
}
|
||||
async getAuthToken() {
|
||||
if (!this.auth) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
const token = await this.auth.getToken();
|
||||
return token?.accessToken;
|
||||
}
|
||||
catch (e) {
|
||||
// If there's any error when trying to get the auth token, leave it off.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
async getMessagingToken() {
|
||||
if (!this.messaging ||
|
||||
!('Notification' in self) ||
|
||||
Notification.permission !== 'granted') {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return await this.messaging.getToken();
|
||||
}
|
||||
catch (e) {
|
||||
// We don't warn on this, because it usually means messaging isn't set up.
|
||||
// console.warn('Failed to retrieve instance id token.', e);
|
||||
// If there's any error when trying to get the token, leave it off.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
async getAppCheckToken(limitedUseAppCheckTokens) {
|
||||
if (this.serverAppAppCheckToken) {
|
||||
return this.serverAppAppCheckToken;
|
||||
}
|
||||
if (this.appCheck) {
|
||||
const result = limitedUseAppCheckTokens
|
||||
? await this.appCheck.getLimitedUseToken()
|
||||
: await this.appCheck.getToken();
|
||||
if (result.error) {
|
||||
// Do not send the App Check header to the functions endpoint if
|
||||
// there was an error from the App Check exchange endpoint. The App
|
||||
// Check SDK will already have logged the error to console.
|
||||
return null;
|
||||
}
|
||||
return result.token;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
async getContext(limitedUseAppCheckTokens) {
|
||||
const authToken = await this.getAuthToken();
|
||||
const messagingToken = await this.getMessagingToken();
|
||||
const appCheckToken = await this.getAppCheckToken(limitedUseAppCheckTokens);
|
||||
return { authToken, messagingToken, appCheckToken };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const DEFAULT_REGION = 'us-central1';
|
||||
const responseLineRE = /^data: (.*?)(?:\n|$)/;
|
||||
/**
|
||||
* Returns a Promise that will be rejected after the given duration.
|
||||
* The error will be of type FunctionsError.
|
||||
*
|
||||
* @param millis Number of milliseconds to wait before rejecting.
|
||||
*/
|
||||
function failAfter(millis) {
|
||||
// Node timers and browser timers are fundamentally incompatible, but we
|
||||
// don't care about the value here
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let timer = null;
|
||||
return {
|
||||
promise: new Promise((_, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
reject(new FunctionsError('deadline-exceeded', 'deadline-exceeded'));
|
||||
}, millis);
|
||||
}),
|
||||
cancel: () => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* The main class for the Firebase Functions SDK.
|
||||
* @internal
|
||||
*/
|
||||
class FunctionsService {
|
||||
/**
|
||||
* Creates a new Functions service for the given app.
|
||||
* @param app - The FirebaseApp to use.
|
||||
*/
|
||||
constructor(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain = DEFAULT_REGION, fetchImpl = (...args) => fetch(...args)) {
|
||||
this.app = app;
|
||||
this.fetchImpl = fetchImpl;
|
||||
this.emulatorOrigin = null;
|
||||
this.contextProvider = new ContextProvider(app, authProvider, messagingProvider, appCheckProvider);
|
||||
// Cancels all ongoing requests when resolved.
|
||||
this.cancelAllRequests = new Promise(resolve => {
|
||||
this.deleteService = () => {
|
||||
return Promise.resolve(resolve());
|
||||
};
|
||||
});
|
||||
// Resolve the region or custom domain overload by attempting to parse it.
|
||||
try {
|
||||
const url = new URL(regionOrCustomDomain);
|
||||
this.customDomain =
|
||||
url.origin + (url.pathname === '/' ? '' : url.pathname);
|
||||
this.region = DEFAULT_REGION;
|
||||
}
|
||||
catch (e) {
|
||||
this.customDomain = null;
|
||||
this.region = regionOrCustomDomain;
|
||||
}
|
||||
}
|
||||
_delete() {
|
||||
return this.deleteService();
|
||||
}
|
||||
/**
|
||||
* Returns the URL for a callable with the given name.
|
||||
* @param name - The name of the callable.
|
||||
* @internal
|
||||
*/
|
||||
_url(name) {
|
||||
const projectId = this.app.options.projectId;
|
||||
if (this.emulatorOrigin !== null) {
|
||||
const origin = this.emulatorOrigin;
|
||||
return `${origin}/${projectId}/${this.region}/${name}`;
|
||||
}
|
||||
if (this.customDomain !== null) {
|
||||
return `${this.customDomain}/${name}`;
|
||||
}
|
||||
return `https://${this.region}-${projectId}.cloudfunctions.net/${name}`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Modify this instance to communicate with the Cloud Functions emulator.
|
||||
*
|
||||
* Note: this must be called before this instance has been used to do any operations.
|
||||
*
|
||||
* @param host The emulator host (ex: localhost)
|
||||
* @param port The emulator port (ex: 5001)
|
||||
* @public
|
||||
*/
|
||||
function connectFunctionsEmulator$1(functionsInstance, host, port) {
|
||||
const useSsl = isCloudWorkstation(host);
|
||||
functionsInstance.emulatorOrigin = `http${useSsl ? 's' : ''}://${host}:${port}`;
|
||||
// Workaround to get cookies in Firebase Studio
|
||||
if (useSsl) {
|
||||
void pingServer(functionsInstance.emulatorOrigin + '/backends');
|
||||
updateEmulatorBanner('Functions', true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns a reference to the callable https trigger with the given name.
|
||||
* @param name - The name of the trigger.
|
||||
* @public
|
||||
*/
|
||||
function httpsCallable$1(functionsInstance, name, options) {
|
||||
const callable = (data) => {
|
||||
return call(functionsInstance, name, data, options || {});
|
||||
};
|
||||
callable.stream = (data, options) => {
|
||||
return stream(functionsInstance, name, data, options);
|
||||
};
|
||||
return callable;
|
||||
}
|
||||
/**
|
||||
* Returns a reference to the callable https trigger with the given url.
|
||||
* @param url - The url of the trigger.
|
||||
* @public
|
||||
*/
|
||||
function httpsCallableFromURL$1(functionsInstance, url, options) {
|
||||
const callable = (data) => {
|
||||
return callAtURL(functionsInstance, url, data, options || {});
|
||||
};
|
||||
callable.stream = (data, options) => {
|
||||
return streamAtURL(functionsInstance, url, data, options || {});
|
||||
};
|
||||
return callable;
|
||||
}
|
||||
function getCredentials(functionsInstance) {
|
||||
return functionsInstance.emulatorOrigin &&
|
||||
isCloudWorkstation(functionsInstance.emulatorOrigin)
|
||||
? 'include'
|
||||
: undefined;
|
||||
}
|
||||
/**
|
||||
* Does an HTTP POST and returns the completed response.
|
||||
* @param url The url to post to.
|
||||
* @param body The JSON body of the post.
|
||||
* @param headers The HTTP headers to include in the request.
|
||||
* @param functionsInstance functions instance that is calling postJSON
|
||||
* @return A Promise that will succeed when the request finishes.
|
||||
*/
|
||||
async function postJSON(url, body, headers, fetchImpl, functionsInstance) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
let response;
|
||||
try {
|
||||
response = await fetchImpl(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(body),
|
||||
headers,
|
||||
credentials: getCredentials(functionsInstance)
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
// 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.
|
||||
return {
|
||||
status: 0,
|
||||
json: null
|
||||
};
|
||||
}
|
||||
let json = null;
|
||||
try {
|
||||
json = await response.json();
|
||||
}
|
||||
catch (e) {
|
||||
// If we fail to parse JSON, it will fail the same as an empty body.
|
||||
}
|
||||
return {
|
||||
status: response.status,
|
||||
json
|
||||
};
|
||||
}
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function call(functionsInstance, name, data, options) {
|
||||
const url = functionsInstance._url(name);
|
||||
return callAtURL(functionsInstance, url, data, options);
|
||||
}
|
||||
/**
|
||||
* Calls a callable function asynchronously and returns the result.
|
||||
* @param url The url of the callable trigger.
|
||||
* @param data The data to pass as params to the function.
|
||||
*/
|
||||
async function callAtURL(functionsInstance, url, data, options) {
|
||||
// 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);
|
||||
// Default timeout to 70s, but let the options override it.
|
||||
const timeout = options.timeout || 70000;
|
||||
const failAfterHandle = failAfter(timeout);
|
||||
const response = await Promise.race([
|
||||
postJSON(url, body, headers, functionsInstance.fetchImpl, functionsInstance),
|
||||
failAfterHandle.promise,
|
||||
functionsInstance.cancelAllRequests
|
||||
]);
|
||||
// Always clear the failAfter timeout
|
||||
failAfterHandle.cancel();
|
||||
// If service was deleted, interrupted response throws an error.
|
||||
if (!response) {
|
||||
throw new FunctionsError('cancelled', 'Firebase Functions instance was deleted.');
|
||||
}
|
||||
// Check for an error status, regardless of http status.
|
||||
const error = _errorForResponse(response.status, response.json);
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
if (!response.json) {
|
||||
throw new FunctionsError('internal', 'Response is not valid JSON object.');
|
||||
}
|
||||
let responseData = response.json.data;
|
||||
// TODO(klimt): For right now, allow "result" instead of "data", for
|
||||
// backwards compatibility.
|
||||
if (typeof responseData === 'undefined') {
|
||||
responseData = response.json.result;
|
||||
}
|
||||
if (typeof responseData === 'undefined') {
|
||||
// Consider the response malformed.
|
||||
throw new FunctionsError('internal', 'Response is missing data field.');
|
||||
}
|
||||
// Decode any special types, such as dates, in the returned data.
|
||||
const decodedData = decode(responseData);
|
||||
return { data: decodedData };
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
// 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?.signal,
|
||||
credentials: getCredentials(functionsInstance)
|
||||
});
|
||||
}
|
||||
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;
|
||||
});
|
||||
options?.signal?.addEventListener('abort', () => {
|
||||
const error = new FunctionsError('cancelled', 'Request was cancelled.');
|
||||
resultRejecter(error);
|
||||
});
|
||||
const reader = response.body.getReader();
|
||||
const rstream = createResponseStream(reader, resultResolver, resultRejecter, 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?.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?.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.13.2";
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const AUTH_INTERNAL_NAME = 'auth-internal';
|
||||
const APP_CHECK_INTERNAL_NAME = 'app-check-internal';
|
||||
const MESSAGING_INTERNAL_NAME = 'messaging-internal';
|
||||
function registerFunctions(variant) {
|
||||
const factory = (container, { instanceIdentifier: regionOrCustomDomain }) => {
|
||||
// Dependencies
|
||||
const app = container.getProvider('app').getImmediate();
|
||||
const authProvider = container.getProvider(AUTH_INTERNAL_NAME);
|
||||
const messagingProvider = container.getProvider(MESSAGING_INTERNAL_NAME);
|
||||
const appCheckProvider = container.getProvider(APP_CHECK_INTERNAL_NAME);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return new FunctionsService(app, authProvider, messagingProvider, appCheckProvider, regionOrCustomDomain);
|
||||
};
|
||||
_registerComponent(new Component(FUNCTIONS_TYPE, factory, "PUBLIC" /* ComponentType.PUBLIC */).setMultipleInstances(true));
|
||||
registerVersion(name, version, variant);
|
||||
// BUILD_TARGET will be replaced by values like esm, cjs, etc during the compilation
|
||||
registerVersion(name, version, 'esm2020');
|
||||
}
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Returns a {@link Functions} instance for the given app.
|
||||
* @param app - The {@link @firebase/app#FirebaseApp} to use.
|
||||
* @param regionOrCustomDomain - one of:
|
||||
* a) The region the callable functions are located in (ex: us-central1)
|
||||
* b) A custom domain hosting the callable functions (ex: https://mydomain.com)
|
||||
* @public
|
||||
*/
|
||||
function getFunctions(app = getApp(), regionOrCustomDomain = DEFAULT_REGION) {
|
||||
// Dependencies
|
||||
const functionsProvider = _getProvider(getModularInstance(app), FUNCTIONS_TYPE);
|
||||
const functionsInstance = functionsProvider.getImmediate({
|
||||
identifier: regionOrCustomDomain
|
||||
});
|
||||
const emulator = getDefaultEmulatorHostnameAndPort('functions');
|
||||
if (emulator) {
|
||||
connectFunctionsEmulator(functionsInstance, ...emulator);
|
||||
}
|
||||
return functionsInstance;
|
||||
}
|
||||
/**
|
||||
* Modify this instance to communicate with the Cloud Functions emulator.
|
||||
*
|
||||
* Note: this must be called before this instance has been used to do any operations.
|
||||
*
|
||||
* @param host - The emulator host (ex: localhost)
|
||||
* @param port - The emulator port (ex: 5001)
|
||||
* @public
|
||||
*/
|
||||
function connectFunctionsEmulator(functionsInstance, host, port) {
|
||||
connectFunctionsEmulator$1(getModularInstance(functionsInstance), host, port);
|
||||
}
|
||||
/**
|
||||
* Returns a reference to the callable HTTPS trigger with the given name.
|
||||
* @param name - The name of the trigger.
|
||||
* @public
|
||||
*/
|
||||
function httpsCallable(functionsInstance, name, options) {
|
||||
return httpsCallable$1(getModularInstance(functionsInstance), name, options);
|
||||
}
|
||||
/**
|
||||
* Returns a reference to the callable HTTPS trigger with the specified url.
|
||||
* @param url - The url of the trigger.
|
||||
* @public
|
||||
*/
|
||||
function httpsCallableFromURL(functionsInstance, url, options) {
|
||||
return httpsCallableFromURL$1(getModularInstance(functionsInstance), url, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloud Functions for Firebase
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
registerFunctions();
|
||||
|
||||
export { FunctionsError, connectFunctionsEmulator, getFunctions, httpsCallable, httpsCallableFromURL };
|
||||
//# sourceMappingURL=index.esm.js.map
|
||||
Reference in New Issue
Block a user