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.
Files changed (76) hide show
  1. package/lib/common/api.d.ts +7 -0
  2. package/lib/common/api.js +34 -0
  3. package/lib/common/providers/https.d.ts +7 -0
  4. package/lib/common/providers/https.js +40 -1
  5. package/lib/esm/common/api.mjs +31 -0
  6. package/lib/esm/common/providers/https.mjs +40 -2
  7. package/lib/esm/lifecycle/index.mjs +55 -0
  8. package/lib/esm/runtime/loader.mjs +12 -2
  9. package/lib/esm/runtime/manifest.mjs +0 -1
  10. package/lib/esm/security/roles.mjs +17 -0
  11. package/lib/esm/v1/cloud-functions.mjs +2 -1
  12. package/lib/esm/v1/function-builder.mjs +15 -1
  13. package/lib/esm/v1/index.mjs +2 -1
  14. package/lib/esm/v1/providers/https.mjs +28 -4
  15. package/lib/esm/v2/index.doc.mjs +4 -1
  16. package/lib/esm/v2/index.mjs +4 -1
  17. package/lib/esm/v2/options.mjs +79 -3
  18. package/lib/esm/v2/providers/ai/index.mjs +2 -2
  19. package/lib/esm/v2/providers/alerts/alerts.mjs +1 -1
  20. package/lib/esm/v2/providers/database.mjs +19 -6
  21. package/lib/esm/v2/providers/dataconnect/graphql.mjs +1 -1
  22. package/lib/esm/v2/providers/dataconnect/index.mjs +1 -1
  23. package/lib/esm/v2/providers/eventarc.mjs +1 -1
  24. package/lib/esm/v2/providers/firestore.mjs +1 -1
  25. package/lib/esm/v2/providers/https.mjs +9 -23
  26. package/lib/esm/v2/providers/identity.mjs +89 -4
  27. package/lib/esm/v2/providers/pubsub.mjs +4 -4
  28. package/lib/esm/v2/providers/remoteConfig.mjs +1 -1
  29. package/lib/esm/v2/providers/scheduler.mjs +1 -1
  30. package/lib/esm/v2/providers/storage.mjs +2 -2
  31. package/lib/esm/v2/providers/tasks.mjs +2 -2
  32. package/lib/esm/v2/providers/testLab.mjs +1 -1
  33. package/lib/esm/v2/security.mjs +19 -0
  34. package/lib/lifecycle/index.d.ts +44 -0
  35. package/lib/lifecycle/index.js +59 -0
  36. package/lib/logger/common.d.ts +20 -5
  37. package/lib/params/types.d.ts +9 -3
  38. package/lib/runtime/loader.js +12 -2
  39. package/lib/runtime/manifest.d.ts +24 -3
  40. package/lib/runtime/manifest.js +0 -1
  41. package/lib/security/roles.d.ts +5 -0
  42. package/lib/security/roles.js +19 -0
  43. package/lib/v1/cloud-functions.d.ts +8 -5
  44. package/lib/v1/cloud-functions.js +2 -1
  45. package/lib/v1/function-builder.d.ts +2 -2
  46. package/lib/v1/function-builder.js +15 -1
  47. package/lib/v1/index.d.ts +1 -0
  48. package/lib/v1/index.js +2 -0
  49. package/lib/v1/providers/https.d.ts +11 -3
  50. package/lib/v1/providers/https.js +28 -3
  51. package/lib/v2/index.d.ts +4 -2
  52. package/lib/v2/index.doc.js +7 -0
  53. package/lib/v2/index.js +7 -0
  54. package/lib/v2/options.d.ts +6 -2
  55. package/lib/v2/options.js +84 -2
  56. package/lib/v2/providers/ai/index.js +2 -2
  57. package/lib/v2/providers/alerts/alerts.js +1 -1
  58. package/lib/v2/providers/database.d.ts +1 -1
  59. package/lib/v2/providers/database.js +19 -6
  60. package/lib/v2/providers/dataconnect/graphql.js +1 -1
  61. package/lib/v2/providers/dataconnect/index.js +1 -1
  62. package/lib/v2/providers/eventarc.js +1 -1
  63. package/lib/v2/providers/firestore.js +1 -1
  64. package/lib/v2/providers/https.js +8 -22
  65. package/lib/v2/providers/identity.d.ts +71 -1
  66. package/lib/v2/providers/identity.js +91 -3
  67. package/lib/v2/providers/pubsub.d.ts +3 -3
  68. package/lib/v2/providers/pubsub.js +4 -4
  69. package/lib/v2/providers/remoteConfig.js +1 -1
  70. package/lib/v2/providers/scheduler.js +1 -1
  71. package/lib/v2/providers/storage.js +2 -2
  72. package/lib/v2/providers/tasks.js +2 -2
  73. package/lib/v2/providers/testLab.js +1 -1
  74. package/lib/v2/security.d.ts +11 -0
  75. package/lib/v2/security.js +19 -0
  76. 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
- (0, cors.default)(options.cors)(req, res, () => {
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
- cors(options.cors)(req, res, () => {
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 = contextOnlyHandler(context);
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) => _onRequestWithOptions(handler, this.options),
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
  }
@@ -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 handler A function that takes a request and response object,
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
- return _onRequestWithOptions(handler, {});
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
  };
@@ -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 };
@@ -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 };
@@ -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",