firebase-functions 7.2.6-rc.0 → 7.3.0-rc.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.
- package/lib/common/api.d.ts +7 -0
- package/lib/common/api.js +34 -0
- package/lib/common/providers/https.d.ts +7 -0
- package/lib/common/providers/https.js +40 -1
- package/lib/esm/common/api.mjs +31 -0
- package/lib/esm/common/providers/https.mjs +40 -2
- package/lib/esm/lifecycle/index.mjs +55 -0
- package/lib/esm/runtime/loader.mjs +12 -2
- package/lib/esm/runtime/manifest.mjs +0 -1
- package/lib/esm/security/roles.mjs +17 -0
- package/lib/esm/v1/cloud-functions.mjs +2 -1
- package/lib/esm/v1/function-builder.mjs +15 -1
- package/lib/esm/v1/index.mjs +2 -1
- package/lib/esm/v1/providers/https.mjs +28 -4
- package/lib/esm/v2/index.doc.mjs +4 -1
- package/lib/esm/v2/index.mjs +4 -1
- package/lib/esm/v2/options.mjs +79 -3
- package/lib/esm/v2/providers/ai/index.mjs +2 -2
- package/lib/esm/v2/providers/alerts/alerts.mjs +1 -1
- package/lib/esm/v2/providers/database.mjs +19 -6
- package/lib/esm/v2/providers/dataconnect/graphql.mjs +1 -1
- package/lib/esm/v2/providers/dataconnect/index.mjs +1 -1
- package/lib/esm/v2/providers/eventarc.mjs +1 -1
- package/lib/esm/v2/providers/firestore.mjs +1 -1
- package/lib/esm/v2/providers/https.mjs +9 -23
- package/lib/esm/v2/providers/identity.mjs +89 -4
- package/lib/esm/v2/providers/pubsub.mjs +4 -4
- package/lib/esm/v2/providers/remoteConfig.mjs +1 -1
- package/lib/esm/v2/providers/scheduler.mjs +1 -1
- package/lib/esm/v2/providers/storage.mjs +2 -2
- package/lib/esm/v2/providers/tasks.mjs +2 -2
- package/lib/esm/v2/providers/testLab.mjs +1 -1
- package/lib/esm/v2/security.mjs +19 -0
- package/lib/lifecycle/index.d.ts +44 -0
- package/lib/lifecycle/index.js +59 -0
- package/lib/logger/common.d.ts +20 -5
- package/lib/params/types.d.ts +9 -3
- package/lib/runtime/loader.js +12 -2
- package/lib/runtime/manifest.d.ts +24 -3
- package/lib/runtime/manifest.js +0 -1
- package/lib/security/roles.d.ts +5 -0
- package/lib/security/roles.js +19 -0
- package/lib/v1/cloud-functions.d.ts +8 -5
- package/lib/v1/cloud-functions.js +2 -1
- package/lib/v1/function-builder.d.ts +2 -2
- package/lib/v1/function-builder.js +15 -1
- package/lib/v1/index.d.ts +1 -0
- package/lib/v1/index.js +2 -0
- package/lib/v1/providers/https.d.ts +11 -3
- package/lib/v1/providers/https.js +28 -3
- package/lib/v2/index.d.ts +4 -2
- package/lib/v2/index.doc.js +7 -0
- package/lib/v2/index.js +7 -0
- package/lib/v2/options.d.ts +6 -2
- package/lib/v2/options.js +84 -2
- package/lib/v2/providers/ai/index.js +2 -2
- package/lib/v2/providers/alerts/alerts.js +1 -1
- package/lib/v2/providers/database.d.ts +1 -1
- package/lib/v2/providers/database.js +19 -6
- package/lib/v2/providers/dataconnect/graphql.js +1 -1
- package/lib/v2/providers/dataconnect/index.js +1 -1
- package/lib/v2/providers/eventarc.js +1 -1
- package/lib/v2/providers/firestore.js +1 -1
- package/lib/v2/providers/https.js +8 -22
- package/lib/v2/providers/identity.d.ts +71 -1
- package/lib/v2/providers/identity.js +91 -3
- package/lib/v2/providers/pubsub.d.ts +3 -3
- package/lib/v2/providers/pubsub.js +4 -4
- package/lib/v2/providers/remoteConfig.js +1 -1
- package/lib/v2/providers/scheduler.js +1 -1
- package/lib/v2/providers/storage.js +2 -2
- package/lib/v2/providers/tasks.js +2 -2
- package/lib/v2/providers/testLab.js +1 -1
- package/lib/v2/security.d.ts +11 -0
- package/lib/v2/security.js +19 -0
- package/package.json +17 -13
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type GoogleCloudApi = `${string}.googleapis.com`;
|
|
2
|
+
/**
|
|
3
|
+
* Declare that this project requires a specific Google Cloud API to be enabled.
|
|
4
|
+
* @param api The API name, e.g. "secretmanager.googleapis.com"
|
|
5
|
+
* @param reason Optional reason why the API is needed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function requiresAPI(api: GoogleCloudApi, reason?: string): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/common/api.ts
|
|
3
|
+
let globalRequiredAPIs = [];
|
|
4
|
+
/**
|
|
5
|
+
* Declare that this project requires a specific Google Cloud API to be enabled.
|
|
6
|
+
* @param api The API name, e.g. "secretmanager.googleapis.com"
|
|
7
|
+
* @param reason Optional reason why the API is needed.
|
|
8
|
+
*/
|
|
9
|
+
function requiresAPI(api, reason = "") {
|
|
10
|
+
if (!api || typeof api !== "string" || !api.endsWith(".googleapis.com")) {
|
|
11
|
+
throw new Error("requiresAPI: 'api' must be a non-empty string ending with '.googleapis.com'.");
|
|
12
|
+
}
|
|
13
|
+
globalRequiredAPIs.push({
|
|
14
|
+
api,
|
|
15
|
+
reason
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
function getGlobalRequiredAPIs() {
|
|
22
|
+
return globalRequiredAPIs;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
function clearGlobalRequiredAPIs() {
|
|
28
|
+
globalRequiredAPIs = [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
exports.clearGlobalRequiredAPIs = clearGlobalRequiredAPIs;
|
|
33
|
+
exports.getGlobalRequiredAPIs = getGlobalRequiredAPIs;
|
|
34
|
+
exports.requiresAPI = requiresAPI;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import cors from "cors";
|
|
1
2
|
import * as express from "express";
|
|
2
3
|
import { DecodedAppCheckToken } from "firebase-admin/app-check";
|
|
3
4
|
import { DecodedIdToken } from "firebase-admin/auth";
|
|
4
5
|
import { TaskContext } from "./tasks";
|
|
6
|
+
import { Expression } from "../../params";
|
|
5
7
|
/** An express request with the wire format representation of the request body. */
|
|
6
8
|
export interface Request extends express.Request {
|
|
7
9
|
/** The wire format representation of the request body. */
|
|
@@ -240,4 +242,9 @@ export declare function decode(data: any): any;
|
|
|
240
242
|
type TokenStatus = "MISSING" | "VALID" | "INVALID";
|
|
241
243
|
/** @interanl */
|
|
242
244
|
export declare function checkAuthToken(req: Request, ctx: CallableContext | TaskContext): Promise<TokenStatus>;
|
|
245
|
+
export type CorsOption = string | Expression<string> | Expression<string[]> | boolean | RegExp | Array<string | RegExp>;
|
|
246
|
+
export declare function resolveCorsOrigin(corsOption?: CorsOption): cors.CorsOptions["origin"];
|
|
247
|
+
export type CorsInfo = Omit<cors.CorsOptions, "origin"> & {
|
|
248
|
+
origin?: CorsOption;
|
|
249
|
+
};
|
|
243
250
|
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.js');
|
|
2
2
|
const require_logger_index = require('../../logger/index.js');
|
|
3
|
+
const require_params_types = require('../../params/types.js');
|
|
4
|
+
require('../../params/index.js');
|
|
3
5
|
const require_common_app = require('../app.js');
|
|
4
6
|
const require_common_debug = require('../debug.js');
|
|
5
7
|
let firebase_admin_auth = require("firebase-admin/auth");
|
|
@@ -373,13 +375,49 @@ async function checkAppCheckToken(req, ctx, options) {
|
|
|
373
375
|
return "INVALID";
|
|
374
376
|
}
|
|
375
377
|
}
|
|
378
|
+
function resolveCorsOrigin(corsOption) {
|
|
379
|
+
let origin;
|
|
380
|
+
if (corsOption instanceof require_params_types.Expression) {
|
|
381
|
+
try {
|
|
382
|
+
origin = corsOption.runtimeValue();
|
|
383
|
+
} catch (e) {
|
|
384
|
+
require_logger_index.warn(`Failed to resolve CORS parameter: ${e}`);
|
|
385
|
+
origin = false;
|
|
386
|
+
}
|
|
387
|
+
} else {
|
|
388
|
+
origin = corsOption;
|
|
389
|
+
}
|
|
390
|
+
if (Array.isArray(origin) && origin.length === 0) {
|
|
391
|
+
require_logger_index.warn("CORS parameter resolved to an empty array. Disabling CORS.");
|
|
392
|
+
origin = false;
|
|
393
|
+
}
|
|
394
|
+
if (origin === "") {
|
|
395
|
+
require_logger_index.warn("CORS parameter resolved to an empty string. Disabling CORS.");
|
|
396
|
+
origin = false;
|
|
397
|
+
}
|
|
398
|
+
if (require_common_debug.isDebugFeatureEnabled("enableCors")) {
|
|
399
|
+
origin = origin === false ? false : true;
|
|
400
|
+
}
|
|
401
|
+
if (origin === undefined) {
|
|
402
|
+
origin = true;
|
|
403
|
+
}
|
|
404
|
+
if (Array.isArray(origin) && origin.length === 1) {
|
|
405
|
+
origin = origin[0];
|
|
406
|
+
}
|
|
407
|
+
return origin;
|
|
408
|
+
}
|
|
376
409
|
/** @internal */
|
|
377
410
|
function onCallHandler(options, handler, version) {
|
|
378
411
|
const wrapped = wrapOnCallHandler(options, handler, version);
|
|
379
412
|
return (req, res) => {
|
|
380
413
|
return new Promise((resolve) => {
|
|
381
414
|
res.on("finish", resolve);
|
|
382
|
-
|
|
415
|
+
const origin = resolveCorsOrigin(options.cors.origin);
|
|
416
|
+
const corsOptions = {
|
|
417
|
+
...options.cors,
|
|
418
|
+
origin
|
|
419
|
+
};
|
|
420
|
+
(0, cors.default)(corsOptions)(req, res, () => {
|
|
383
421
|
resolve(wrapped(req, res));
|
|
384
422
|
});
|
|
385
423
|
});
|
|
@@ -583,6 +621,7 @@ exports.decode = decode;
|
|
|
583
621
|
exports.encode = encode;
|
|
584
622
|
exports.isValidRequest = isValidRequest;
|
|
585
623
|
exports.onCallHandler = onCallHandler;
|
|
624
|
+
exports.resolveCorsOrigin = resolveCorsOrigin;
|
|
586
625
|
exports.unsafeDecodeAppCheckToken = unsafeDecodeAppCheckToken;
|
|
587
626
|
exports.unsafeDecodeIdToken = unsafeDecodeIdToken;
|
|
588
627
|
exports.unsafeDecodeToken = unsafeDecodeToken;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/common/api.ts
|
|
2
|
+
let globalRequiredAPIs = [];
|
|
3
|
+
/**
|
|
4
|
+
* Declare that this project requires a specific Google Cloud API to be enabled.
|
|
5
|
+
* @param api The API name, e.g. "secretmanager.googleapis.com"
|
|
6
|
+
* @param reason Optional reason why the API is needed.
|
|
7
|
+
*/
|
|
8
|
+
function requiresAPI(api, reason = "") {
|
|
9
|
+
if (!api || typeof api !== "string" || !api.endsWith(".googleapis.com")) {
|
|
10
|
+
throw new Error("requiresAPI: 'api' must be a non-empty string ending with '.googleapis.com'.");
|
|
11
|
+
}
|
|
12
|
+
globalRequiredAPIs.push({
|
|
13
|
+
api,
|
|
14
|
+
reason
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
20
|
+
function getGlobalRequiredAPIs() {
|
|
21
|
+
return globalRequiredAPIs;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
function clearGlobalRequiredAPIs() {
|
|
27
|
+
globalRequiredAPIs = [];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { clearGlobalRequiredAPIs, getGlobalRequiredAPIs, requiresAPI };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { debug, error, warn } from "../../logger/index.mjs";
|
|
2
|
+
import { Expression } from "../../params/types.mjs";
|
|
3
|
+
import "../../params/index.mjs";
|
|
2
4
|
import { getApp } from "../app.mjs";
|
|
3
5
|
import { isDebugFeatureEnabled } from "../debug.mjs";
|
|
4
6
|
import { getAuth } from "firebase-admin/auth";
|
|
@@ -369,13 +371,49 @@ async function checkAppCheckToken(req, ctx, options) {
|
|
|
369
371
|
return "INVALID";
|
|
370
372
|
}
|
|
371
373
|
}
|
|
374
|
+
function resolveCorsOrigin(corsOption) {
|
|
375
|
+
let origin;
|
|
376
|
+
if (corsOption instanceof Expression) {
|
|
377
|
+
try {
|
|
378
|
+
origin = corsOption.runtimeValue();
|
|
379
|
+
} catch (e) {
|
|
380
|
+
warn(`Failed to resolve CORS parameter: ${e}`);
|
|
381
|
+
origin = false;
|
|
382
|
+
}
|
|
383
|
+
} else {
|
|
384
|
+
origin = corsOption;
|
|
385
|
+
}
|
|
386
|
+
if (Array.isArray(origin) && origin.length === 0) {
|
|
387
|
+
warn("CORS parameter resolved to an empty array. Disabling CORS.");
|
|
388
|
+
origin = false;
|
|
389
|
+
}
|
|
390
|
+
if (origin === "") {
|
|
391
|
+
warn("CORS parameter resolved to an empty string. Disabling CORS.");
|
|
392
|
+
origin = false;
|
|
393
|
+
}
|
|
394
|
+
if (isDebugFeatureEnabled("enableCors")) {
|
|
395
|
+
origin = origin === false ? false : true;
|
|
396
|
+
}
|
|
397
|
+
if (origin === undefined) {
|
|
398
|
+
origin = true;
|
|
399
|
+
}
|
|
400
|
+
if (Array.isArray(origin) && origin.length === 1) {
|
|
401
|
+
origin = origin[0];
|
|
402
|
+
}
|
|
403
|
+
return origin;
|
|
404
|
+
}
|
|
372
405
|
/** @internal */
|
|
373
406
|
function onCallHandler(options, handler, version) {
|
|
374
407
|
const wrapped = wrapOnCallHandler(options, handler, version);
|
|
375
408
|
return (req, res) => {
|
|
376
409
|
return new Promise((resolve) => {
|
|
377
410
|
res.on("finish", resolve);
|
|
378
|
-
|
|
411
|
+
const origin = resolveCorsOrigin(options.cors.origin);
|
|
412
|
+
const corsOptions = {
|
|
413
|
+
...options.cors,
|
|
414
|
+
origin
|
|
415
|
+
};
|
|
416
|
+
cors(corsOptions)(req, res, () => {
|
|
379
417
|
resolve(wrapped(req, res));
|
|
380
418
|
});
|
|
381
419
|
});
|
|
@@ -570,4 +608,4 @@ function withErrorHandler(handler) {
|
|
|
570
608
|
}
|
|
571
609
|
|
|
572
610
|
//#endregion
|
|
573
|
-
export { CALLABLE_AUTH_HEADER, DEFAULT_HEARTBEAT_SECONDS, HttpsError, ORIGINAL_AUTH_HEADER, checkAuthToken, decode, encode, isValidRequest, onCallHandler, unsafeDecodeAppCheckToken, unsafeDecodeIdToken, unsafeDecodeToken, withErrorHandler };
|
|
611
|
+
export { CALLABLE_AUTH_HEADER, DEFAULT_HEARTBEAT_SECONDS, HttpsError, ORIGINAL_AUTH_HEADER, checkAuthToken, decode, encode, isValidRequest, onCallHandler, resolveCorsOrigin, unsafeDecodeAppCheckToken, unsafeDecodeIdToken, unsafeDecodeToken, withErrorHandler };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//#region src/lifecycle/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Use a global singleton to manage the list of declared lifecycle hooks.
|
|
4
|
+
*
|
|
5
|
+
* This ensures that lifecycle hooks are shared between CJS and ESM builds,
|
|
6
|
+
* avoiding the "dual-package hazard" where the src/bin/firebase-functions.ts (CJS) sees
|
|
7
|
+
* an empty list while the user's code (ESM) populates a different list.
|
|
8
|
+
*/
|
|
9
|
+
const majorVersion = typeof "7" !== "undefined" ? "7" : "0";
|
|
10
|
+
const GLOBAL_SYMBOL = Symbol.for(`firebase-functions:lifecycle:declaredHooks:v${majorVersion}`);
|
|
11
|
+
const globalSymbols = globalThis;
|
|
12
|
+
if (!globalSymbols[GLOBAL_SYMBOL]) {
|
|
13
|
+
globalSymbols[GLOBAL_SYMBOL] = {};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Singleton dictionary of declared lifecycle hooks.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
const declaredLifecycleHooks = globalSymbols[GLOBAL_SYMBOL];
|
|
20
|
+
/**
|
|
21
|
+
* Registers an action to be executed automatically post-deployment when resources in this codebase
|
|
22
|
+
* are deployed for the first time.
|
|
23
|
+
*
|
|
24
|
+
* @param action The lifecycle action to execute.
|
|
25
|
+
*/
|
|
26
|
+
function afterFirstDeploy(action) {
|
|
27
|
+
if (declaredLifecycleHooks.afterFirstDeploy) {
|
|
28
|
+
throw new Error("Only one afterFirstDeploy lifecycle hook is allowed per codebase.");
|
|
29
|
+
}
|
|
30
|
+
declaredLifecycleHooks.afterFirstDeploy = action;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registers an action to be executed automatically post-deployment when resources in this codebase
|
|
34
|
+
* are updated.
|
|
35
|
+
*
|
|
36
|
+
* @param action The lifecycle action to execute.
|
|
37
|
+
*/
|
|
38
|
+
function afterRedeploy(action) {
|
|
39
|
+
if (declaredLifecycleHooks.afterRedeploy) {
|
|
40
|
+
throw new Error("Only one afterRedeploy lifecycle hook is allowed per codebase.");
|
|
41
|
+
}
|
|
42
|
+
declaredLifecycleHooks.afterRedeploy = action;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Helper to clear declared lifecycle hooks.
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
function clearDeclaredLifecycleHooks() {
|
|
49
|
+
for (const key of Object.keys(declaredLifecycleHooks)) {
|
|
50
|
+
delete declaredLifecycleHooks[key];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { afterFirstDeploy, afterRedeploy, clearDeclaredLifecycleHooks, declaredLifecycleHooks };
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { __require } from "../_virtual/rolldown_runtime.mjs";
|
|
2
2
|
import { declaredParams } from "../params/index.mjs";
|
|
3
|
+
import { declaredRoles } from "../security/roles.mjs";
|
|
4
|
+
import { clearGlobalRequiredAPIs, getGlobalRequiredAPIs } from "../common/api.mjs";
|
|
5
|
+
import { clearDeclaredLifecycleHooks, declaredLifecycleHooks } from "../lifecycle/index.mjs";
|
|
3
6
|
import * as path from "path";
|
|
4
7
|
import * as url from "url";
|
|
5
8
|
|
|
@@ -7,9 +10,7 @@ import * as url from "url";
|
|
|
7
10
|
/**
|
|
8
11
|
* Dynamically load import function to prevent TypeScript from
|
|
9
12
|
* transpiling into a require.
|
|
10
|
-
*
|
|
11
13
|
* See https://github.com/microsoft/TypeScript/issues/43329.
|
|
12
|
-
*
|
|
13
14
|
*/
|
|
14
15
|
const dynamicImport = new Function("modulePath", "return import(modulePath)");
|
|
15
16
|
async function loadModule(functionsDir) {
|
|
@@ -116,6 +117,8 @@ async function loadStack(functionsDir) {
|
|
|
116
117
|
const extensions = {};
|
|
117
118
|
const mod = await loadModule(functionsDir);
|
|
118
119
|
extractStack(mod, endpoints, requiredAPIs, extensions);
|
|
120
|
+
requiredAPIs.push(...getGlobalRequiredAPIs());
|
|
121
|
+
clearGlobalRequiredAPIs();
|
|
119
122
|
const stack = {
|
|
120
123
|
endpoints,
|
|
121
124
|
specVersion: "v1alpha1",
|
|
@@ -125,6 +128,13 @@ async function loadStack(functionsDir) {
|
|
|
125
128
|
if (declaredParams.length > 0) {
|
|
126
129
|
stack.params = declaredParams.map((p) => p.toSpec());
|
|
127
130
|
}
|
|
131
|
+
if (declaredRoles.size > 0) {
|
|
132
|
+
stack.requiredRoles = Array.from(declaredRoles);
|
|
133
|
+
}
|
|
134
|
+
if (Object.keys(declaredLifecycleHooks).length > 0) {
|
|
135
|
+
stack.lifecycleHooks = { ...declaredLifecycleHooks };
|
|
136
|
+
}
|
|
137
|
+
clearDeclaredLifecycleHooks();
|
|
128
138
|
return stack;
|
|
129
139
|
}
|
|
130
140
|
|
|
@@ -7,7 +7,6 @@ import { RESET_VALUE, ResetValue } from "../common/options.mjs";
|
|
|
7
7
|
* Returns the JSON representation of a ManifestStack, which has CEL
|
|
8
8
|
* expressions in its options as object types, with its expressions
|
|
9
9
|
* transformed into the actual CEL strings.
|
|
10
|
-
*
|
|
11
10
|
* @alpha
|
|
12
11
|
*/
|
|
13
12
|
function stackToWire(stack) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/security/roles.ts
|
|
2
|
+
/**
|
|
3
|
+
* Global set of declared IAM roles required by this codebase.
|
|
4
|
+
* Accumulated in-memory during manifest generation.
|
|
5
|
+
*/
|
|
6
|
+
const declaredRoles = new Set();
|
|
7
|
+
/**
|
|
8
|
+
* Registers a role to be required by this codebase.
|
|
9
|
+
* Automatically deduplicates using the Set.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function registerRole(role) {
|
|
13
|
+
declaredRoles.add(role);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
export { declaredRoles, registerRole };
|
|
@@ -13,6 +13,7 @@ const WILDCARD_REGEX = new RegExp("{[^/{}]*}", "g");
|
|
|
13
13
|
/** @internal */
|
|
14
14
|
function makeCloudFunction({ contextOnlyHandler, dataConstructor = (raw) => raw.data, eventType, handler, labels = {}, legacyEventType, options = {}, provider, service, triggerResource }) {
|
|
15
15
|
handler = withInit(handler ?? contextOnlyHandler);
|
|
16
|
+
const contextOnlyHandlerWithInit = contextOnlyHandler && withInit(contextOnlyHandler);
|
|
16
17
|
const cloudFunction = (data, context) => {
|
|
17
18
|
if (legacyEventType && context.eventType === legacyEventType) {
|
|
18
19
|
context.eventType = provider + "." + eventType;
|
|
@@ -42,7 +43,7 @@ function makeCloudFunction({ contextOnlyHandler, dataConstructor = (raw) => raw.
|
|
|
42
43
|
}
|
|
43
44
|
let promise;
|
|
44
45
|
if (labels && labels["deployment-scheduled"]) {
|
|
45
|
-
promise =
|
|
46
|
+
promise = contextOnlyHandlerWithInit(context);
|
|
46
47
|
} else {
|
|
47
48
|
const dataOrChange = dataConstructor(event);
|
|
48
49
|
promise = handler(dataOrChange, context);
|
|
@@ -200,7 +200,21 @@ var FunctionBuilder = class {
|
|
|
200
200
|
console.warn("RuntimeOptions.failurePolicy is not supported in https functions.");
|
|
201
201
|
}
|
|
202
202
|
return {
|
|
203
|
-
onRequest: (handler) =>
|
|
203
|
+
onRequest: (optsOrHandler, handler) => {
|
|
204
|
+
let opts;
|
|
205
|
+
let userHandler;
|
|
206
|
+
if (typeof optsOrHandler === "function") {
|
|
207
|
+
opts = {};
|
|
208
|
+
userHandler = optsOrHandler;
|
|
209
|
+
} else {
|
|
210
|
+
opts = optsOrHandler;
|
|
211
|
+
userHandler = handler;
|
|
212
|
+
}
|
|
213
|
+
return _onRequestWithOptions(userHandler, {
|
|
214
|
+
...this.options,
|
|
215
|
+
...opts
|
|
216
|
+
});
|
|
217
|
+
},
|
|
204
218
|
onCall: (handler) => _onCallWithOptions(handler, this.options)
|
|
205
219
|
};
|
|
206
220
|
}
|
package/lib/esm/v1/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger_exports } from "../logger/index.mjs";
|
|
2
2
|
import { params_exports } from "../params/index.mjs";
|
|
3
|
+
import { requiresAPI } from "../common/api.mjs";
|
|
3
4
|
import { RESET_VALUE } from "../common/options.mjs";
|
|
4
5
|
import { firebaseConfig } from "../common/config.mjs";
|
|
5
6
|
import { setApp } from "../common/app.mjs";
|
|
@@ -24,4 +25,4 @@ import { FunctionBuilder, region, runWith } from "./function-builder.mjs";
|
|
|
24
25
|
const app = { setEmulatedAdminApp: setApp };
|
|
25
26
|
|
|
26
27
|
//#endregion
|
|
27
|
-
export { Change, DEFAULT_FAILURE_POLICY, FunctionBuilder, INGRESS_SETTINGS_OPTIONS, MAX_NUMBER_USER_LABELS, MAX_TIMEOUT_SECONDS, MIN_TIMEOUT_SECONDS, RESET_VALUE, SUPPORTED_REGIONS, VALID_MEMORY_OPTIONS, VPC_EGRESS_SETTINGS_OPTIONS, analytics_exports as analytics, app, auth_exports as auth, config, database_exports as database, firebaseConfig, firestore_exports as firestore, https_exports as https, logger_exports as logger, makeCloudFunction, onInit, optionsToEndpoint, optionsToTrigger, params_exports as params, pubsub_exports as pubsub, region, remoteConfig_exports as remoteConfig, runWith, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab };
|
|
28
|
+
export { Change, DEFAULT_FAILURE_POLICY, FunctionBuilder, INGRESS_SETTINGS_OPTIONS, MAX_NUMBER_USER_LABELS, MAX_TIMEOUT_SECONDS, MIN_TIMEOUT_SECONDS, RESET_VALUE, SUPPORTED_REGIONS, VALID_MEMORY_OPTIONS, VPC_EGRESS_SETTINGS_OPTIONS, analytics_exports as analytics, app, auth_exports as auth, config, database_exports as database, firebaseConfig, firestore_exports as firestore, https_exports as https, logger_exports as logger, makeCloudFunction, onInit, optionsToEndpoint, optionsToTrigger, params_exports as params, pubsub_exports as pubsub, region, remoteConfig_exports as remoteConfig, requiresAPI, runWith, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab };
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { __export } from "../../_virtual/rolldown_runtime.mjs";
|
|
2
2
|
import { initV1Endpoint } from "../../runtime/manifest.mjs";
|
|
3
|
+
import { isDebugFeatureEnabled } from "../../common/debug.mjs";
|
|
3
4
|
import { convertIfPresent, convertInvoker } from "../../common/encoding.mjs";
|
|
4
5
|
import { withInit } from "../../common/onInit.mjs";
|
|
5
6
|
import { optionsToEndpoint, optionsToTrigger } from "../cloud-functions.mjs";
|
|
6
|
-
import { HttpsError, onCallHandler, withErrorHandler } from "../../common/providers/https.mjs";
|
|
7
|
+
import { HttpsError, onCallHandler, resolveCorsOrigin, withErrorHandler } from "../../common/providers/https.mjs";
|
|
7
8
|
import { wrapTraceContext } from "../../v2/trace.mjs";
|
|
9
|
+
import cors from "cors";
|
|
8
10
|
|
|
9
11
|
//#region src/v1/providers/https.ts
|
|
10
12
|
var https_exports = /* @__PURE__ */ __export({
|
|
@@ -16,11 +18,20 @@ var https_exports = /* @__PURE__ */ __export({
|
|
|
16
18
|
});
|
|
17
19
|
/**
|
|
18
20
|
* Handle HTTP requests.
|
|
19
|
-
* @param
|
|
21
|
+
* @param optsOrHandler Options or a function that takes a request and response object,
|
|
20
22
|
* same signature as an Express app.
|
|
21
23
|
*/
|
|
22
|
-
function onRequest(handler) {
|
|
23
|
-
|
|
24
|
+
function onRequest(optsOrHandler, handler) {
|
|
25
|
+
let opts;
|
|
26
|
+
let userHandler;
|
|
27
|
+
if (typeof optsOrHandler === "function") {
|
|
28
|
+
opts = {};
|
|
29
|
+
userHandler = optsOrHandler;
|
|
30
|
+
} else {
|
|
31
|
+
opts = optsOrHandler;
|
|
32
|
+
userHandler = handler;
|
|
33
|
+
}
|
|
34
|
+
return _onRequestWithOptions(userHandler, opts);
|
|
24
35
|
}
|
|
25
36
|
/**
|
|
26
37
|
* Declares a callable method for clients to call using a Firebase SDK.
|
|
@@ -31,6 +42,19 @@ function onCall(handler) {
|
|
|
31
42
|
}
|
|
32
43
|
/** @internal */
|
|
33
44
|
function _onRequestWithOptions(handler, options) {
|
|
45
|
+
if (isDebugFeatureEnabled("enableCors") || "cors" in options) {
|
|
46
|
+
const userProvidedHandler = handler;
|
|
47
|
+
handler = (req, res) => {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
res.on("finish", resolve);
|
|
50
|
+
const origin = resolveCorsOrigin(options.cors);
|
|
51
|
+
const middleware = cors({ origin });
|
|
52
|
+
middleware(req, res, () => {
|
|
53
|
+
resolve(userProvidedHandler(req, res));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
34
58
|
const cloudFunction = (req, res) => {
|
|
35
59
|
return wrapTraceContext(withInit(withErrorHandler(handler)))(req, res);
|
|
36
60
|
};
|
package/lib/esm/v2/index.doc.mjs
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { traceContext } from "../common/trace.mjs";
|
|
2
2
|
import { logger } from "../logger/index.mjs";
|
|
3
3
|
import { params_exports } from "../params/index.mjs";
|
|
4
|
+
import { requiresAPI } from "../common/api.mjs";
|
|
5
|
+
import { afterFirstDeploy, afterRedeploy } from "../lifecycle/index.mjs";
|
|
4
6
|
import { Change } from "../common/change.mjs";
|
|
5
7
|
import { onInit } from "../common/onInit.mjs";
|
|
6
8
|
import { config } from "../v1/config.mjs";
|
|
9
|
+
import { requiresRole } from "./security.mjs";
|
|
7
10
|
import { setGlobalOptions } from "./options.mjs";
|
|
8
11
|
import { dataconnect_doc_exports } from "./dataconnect.doc.mjs";
|
|
9
12
|
import { alerts_exports } from "./providers/alerts/index.mjs";
|
|
@@ -20,4 +23,4 @@ import { testLab_exports } from "./providers/testLab.mjs";
|
|
|
20
23
|
import { firestore_exports } from "./providers/firestore.mjs";
|
|
21
24
|
import { app } from "./index.mjs";
|
|
22
25
|
|
|
23
|
-
export { Change, alerts_exports as alerts, app, config, database_exports as database, dataconnect_doc_exports as dataconnect, eventarc_exports as eventarc, firestore_exports as firestore, https_exports as https, identity_exports as identity, logger, onInit, params_exports as params, pubsub_exports as pubsub, remoteConfig_exports as remoteConfig, scheduler_exports as scheduler, setGlobalOptions, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab, traceContext };
|
|
26
|
+
export { Change, afterFirstDeploy, afterRedeploy, alerts_exports as alerts, app, config, database_exports as database, dataconnect_doc_exports as dataconnect, eventarc_exports as eventarc, firestore_exports as firestore, https_exports as https, identity_exports as identity, logger, onInit, params_exports as params, pubsub_exports as pubsub, remoteConfig_exports as remoteConfig, requiresAPI, requiresRole, scheduler_exports as scheduler, setGlobalOptions, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab, traceContext };
|
package/lib/esm/v2/index.mjs
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { traceContext } from "../common/trace.mjs";
|
|
2
2
|
import { logger } from "../logger/index.mjs";
|
|
3
3
|
import { params_exports } from "../params/index.mjs";
|
|
4
|
+
import { requiresAPI } from "../common/api.mjs";
|
|
5
|
+
import { afterFirstDeploy, afterRedeploy } from "../lifecycle/index.mjs";
|
|
4
6
|
import { setApp } from "../common/app.mjs";
|
|
5
7
|
import { Change } from "../common/change.mjs";
|
|
6
8
|
import { onInit } from "../common/onInit.mjs";
|
|
7
9
|
import { config } from "../v1/config.mjs";
|
|
8
10
|
import "./core.mjs";
|
|
11
|
+
import { requiresRole } from "./security.mjs";
|
|
9
12
|
import { setGlobalOptions } from "./options.mjs";
|
|
10
13
|
import { dataconnect_exports } from "./providers/dataconnect/index.mjs";
|
|
11
14
|
import { alerts_exports } from "./providers/alerts/index.mjs";
|
|
@@ -25,4 +28,4 @@ import { firestore_exports } from "./providers/firestore.mjs";
|
|
|
25
28
|
const app = { setEmulatedAdminApp: setApp };
|
|
26
29
|
|
|
27
30
|
//#endregion
|
|
28
|
-
export { Change, alerts_exports as alerts, app, config, database_exports as database, dataconnect_exports as dataconnect, eventarc_exports as eventarc, firestore_exports as firestore, https_exports as https, identity_exports as identity, logger, onInit, params_exports as params, pubsub_exports as pubsub, remoteConfig_exports as remoteConfig, scheduler_exports as scheduler, setGlobalOptions, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab, traceContext };
|
|
31
|
+
export { Change, afterFirstDeploy, afterRedeploy, alerts_exports as alerts, app, config, database_exports as database, dataconnect_exports as dataconnect, eventarc_exports as eventarc, firestore_exports as firestore, https_exports as https, identity_exports as identity, logger, onInit, params_exports as params, pubsub_exports as pubsub, remoteConfig_exports as remoteConfig, requiresAPI, requiresRole, scheduler_exports as scheduler, setGlobalOptions, storage_exports as storage, tasks_exports as tasks, testLab_exports as testLab, traceContext };
|
package/lib/esm/v2/options.mjs
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
import { warn } from "../logger/index.mjs";
|
|
2
|
+
import { Expression } from "../params/types.mjs";
|
|
2
3
|
import { declaredParams } from "../params/index.mjs";
|
|
3
4
|
import { RESET_VALUE, ResetValue } from "../common/options.mjs";
|
|
4
5
|
import { convertIfPresent, copyIfPresent, durationFromSeconds, serviceAccountFromShorthand } from "../common/encoding.mjs";
|
|
6
|
+
import { assertNever } from "../common/utilities/assertions.mjs";
|
|
7
|
+
import { requiresRole } from "./security.mjs";
|
|
5
8
|
|
|
6
9
|
//#region src/v2/options.ts
|
|
10
|
+
/**
|
|
11
|
+
* Maximum timeout in seconds for event-handling functions (e.g. Firestore,
|
|
12
|
+
* Realtime Database, Pub/Sub, Storage, Eventarc, alerts, scheduler).
|
|
13
|
+
* See https://firebase.google.com/docs/functions/manage-functions
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
const MAX_EVENT_TIMEOUT_SECONDS = 540;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum timeout in seconds for HTTPS and callable functions.
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
const MAX_HTTPS_TIMEOUT_SECONDS = 3600;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum timeout in seconds for Task Queue functions.
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
const MAX_TASK_TIMEOUT_SECONDS = 1800;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum timeout in seconds for Identity blocking functions.
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
31
|
+
const MAX_IDENTITY_TIMEOUT_SECONDS = 7;
|
|
7
32
|
const MemoryOptionToMB = {
|
|
8
33
|
"128MiB": 128,
|
|
9
34
|
"256MiB": 256,
|
|
@@ -35,10 +60,58 @@ function getGlobalOptions() {
|
|
|
35
60
|
return globalOptions || {};
|
|
36
61
|
}
|
|
37
62
|
/**
|
|
63
|
+
* Validate that `timeoutSeconds` (resolved with global option fallback) is
|
|
64
|
+
* finite and within the accepted range for the given function kind. Throws a
|
|
65
|
+
* plain `Error` matching the v1 `assertRuntimeOptionsValid` shape so the
|
|
66
|
+
* problem surfaces at function-definition time instead of at deploy time.
|
|
67
|
+
* `Expression`, `RESET_VALUE`, and `undefined` are skipped. Literal numbers
|
|
68
|
+
* are range checked, and other types are rejected.
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
function assertTimeoutSecondsValid(opts, kind) {
|
|
72
|
+
const timeoutSeconds = opts.timeoutSeconds === undefined ? getGlobalOptions().timeoutSeconds : opts.timeoutSeconds;
|
|
73
|
+
if (typeof timeoutSeconds !== "number") {
|
|
74
|
+
if (timeoutSeconds === undefined || timeoutSeconds instanceof Expression || timeoutSeconds instanceof ResetValue) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`timeoutSeconds must be a number, Expression, or RESET_VALUE. Got ${typeof timeoutSeconds}.`);
|
|
78
|
+
}
|
|
79
|
+
if (!Number.isFinite(timeoutSeconds)) {
|
|
80
|
+
throw new Error(`timeoutSeconds must be a finite number. Got ${timeoutSeconds}.`);
|
|
81
|
+
}
|
|
82
|
+
let max;
|
|
83
|
+
let label;
|
|
84
|
+
switch (kind) {
|
|
85
|
+
case "event":
|
|
86
|
+
max = MAX_EVENT_TIMEOUT_SECONDS;
|
|
87
|
+
label = "event-handling";
|
|
88
|
+
break;
|
|
89
|
+
case "https":
|
|
90
|
+
max = MAX_HTTPS_TIMEOUT_SECONDS;
|
|
91
|
+
label = "HTTPS and callable";
|
|
92
|
+
break;
|
|
93
|
+
case "task":
|
|
94
|
+
max = MAX_TASK_TIMEOUT_SECONDS;
|
|
95
|
+
label = "task queue";
|
|
96
|
+
break;
|
|
97
|
+
case "identity":
|
|
98
|
+
max = MAX_IDENTITY_TIMEOUT_SECONDS;
|
|
99
|
+
label = "blocking";
|
|
100
|
+
break;
|
|
101
|
+
default: assertNever(kind);
|
|
102
|
+
}
|
|
103
|
+
if (timeoutSeconds < 0 || timeoutSeconds > max) {
|
|
104
|
+
throw new Error(`timeoutSeconds must be between 0 and ${max} for ${label} functions. Got ${timeoutSeconds}.`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
38
108
|
* Apply GlobalOptions to trigger definitions.
|
|
39
109
|
* @internal
|
|
40
110
|
*/
|
|
41
|
-
function optionsToTriggerAnnotations(opts) {
|
|
111
|
+
function optionsToTriggerAnnotations(opts, kind) {
|
|
112
|
+
if (kind !== undefined) {
|
|
113
|
+
assertTimeoutSecondsValid(opts, kind);
|
|
114
|
+
}
|
|
42
115
|
const annotation = {};
|
|
43
116
|
copyIfPresent(annotation, opts, "concurrency", "minInstances", "maxInstances", "ingressSettings", "labels", "vpcConnector", "secrets");
|
|
44
117
|
const vpcEgress = opts.vpcEgress ?? opts.vpcConnectorEgressSettings;
|
|
@@ -69,7 +142,10 @@ function optionsToTriggerAnnotations(opts) {
|
|
|
69
142
|
* Apply GlobalOptions to endpoint manifest.
|
|
70
143
|
* @internal
|
|
71
144
|
*/
|
|
72
|
-
function optionsToEndpoint(opts) {
|
|
145
|
+
function optionsToEndpoint(opts, kind) {
|
|
146
|
+
if (kind !== undefined) {
|
|
147
|
+
assertTimeoutSecondsValid(opts, kind);
|
|
148
|
+
}
|
|
73
149
|
const endpoint = {};
|
|
74
150
|
copyIfPresent(endpoint, opts, "omit", "concurrency", "minInstances", "maxInstances", "ingressSettings", "labels", "timeoutSeconds", "cpu");
|
|
75
151
|
convertIfPresent(endpoint, opts, "serviceAccountEmail", "serviceAccount");
|
|
@@ -127,4 +203,4 @@ function __getSpec() {
|
|
|
127
203
|
}
|
|
128
204
|
|
|
129
205
|
//#endregion
|
|
130
|
-
export { RESET_VALUE, __getSpec, getGlobalOptions, optionsToEndpoint, optionsToTriggerAnnotations, setGlobalOptions };
|
|
206
|
+
export { MAX_EVENT_TIMEOUT_SECONDS, MAX_HTTPS_TIMEOUT_SECONDS, MAX_IDENTITY_TIMEOUT_SECONDS, MAX_TASK_TIMEOUT_SECONDS, RESET_VALUE, __getSpec, assertTimeoutSecondsValid, getGlobalOptions, optionsToEndpoint, optionsToTriggerAnnotations, requiresRole, setGlobalOptions };
|
|
@@ -90,7 +90,7 @@ function beforeGenerateContent(optsOrCb, cb) {
|
|
|
90
90
|
func.run = handler;
|
|
91
91
|
func = wrapTraceContext(withInit(func));
|
|
92
92
|
const baseOpts = optionsToEndpoint(getGlobalOptions());
|
|
93
|
-
const specificOpts = optionsToEndpoint(opts);
|
|
93
|
+
const specificOpts = optionsToEndpoint(opts, "https");
|
|
94
94
|
func.__endpoint = {
|
|
95
95
|
...initV2Endpoint(getGlobalOptions(), opts),
|
|
96
96
|
platform: "gcfv2",
|
|
@@ -165,7 +165,7 @@ function afterGenerateContent(optsOrCb, cb) {
|
|
|
165
165
|
func.run = handler;
|
|
166
166
|
func = wrapTraceContext(withInit(func));
|
|
167
167
|
const baseOpts = optionsToEndpoint(getGlobalOptions());
|
|
168
|
-
const specificOpts = optionsToEndpoint(opts);
|
|
168
|
+
const specificOpts = optionsToEndpoint(opts, "https");
|
|
169
169
|
func.__endpoint = {
|
|
170
170
|
...initV2Endpoint(getGlobalOptions(), opts),
|
|
171
171
|
platform: "gcfv2",
|
|
@@ -21,7 +21,7 @@ function onAlertPublished(alertTypeOrOpts, handler) {
|
|
|
21
21
|
*/
|
|
22
22
|
function getEndpointAnnotation(opts, alertType, appId) {
|
|
23
23
|
const baseOpts = optionsToEndpoint(getGlobalOptions());
|
|
24
|
-
const specificOpts = optionsToEndpoint(opts);
|
|
24
|
+
const specificOpts = optionsToEndpoint(opts, "event");
|
|
25
25
|
const endpoint = {
|
|
26
26
|
...initV2Endpoint(getGlobalOptions(), opts),
|
|
27
27
|
platform: "gcfv2",
|