firebase-tools 14.27.0 → 15.1.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 (61) hide show
  1. package/lib/appUtils.js +14 -15
  2. package/lib/archiveDirectory.js +7 -45
  3. package/lib/bin/cli.js +35 -8
  4. package/lib/bin/mcp.js +46 -8
  5. package/lib/command.js +5 -1
  6. package/lib/commands/dataconnect-execute.js +1 -1
  7. package/lib/commands/dataconnect-sdk-generate.js +7 -5
  8. package/lib/commands/dataconnect-sql-diff.js +7 -5
  9. package/lib/commands/dataconnect-sql-grant.js +12 -12
  10. package/lib/commands/dataconnect-sql-migrate.js +6 -4
  11. package/lib/commands/dataconnect-sql-setup.js +6 -4
  12. package/lib/commands/dataconnect-sql-shell.js +6 -4
  13. package/lib/commands/firestore-backups-list.js +1 -1
  14. package/lib/commands/functions-config-clone.js +2 -2
  15. package/lib/commands/functions-config-export.js +137 -92
  16. package/lib/commands/functions-config-get.js +1 -2
  17. package/lib/commands/functions-config-set.js +2 -2
  18. package/lib/commands/functions-config-unset.js +2 -2
  19. package/lib/commands/help.js +1 -1
  20. package/lib/commands/index.js +15 -10
  21. package/lib/commands/init.js +8 -0
  22. package/lib/config.js +1 -5
  23. package/lib/dataconnect/load.js +18 -21
  24. package/lib/deploy/database/prepare.js +1 -3
  25. package/lib/deploy/functions/prepare.js +5 -1
  26. package/lib/deploy/functions/prepareFunctionsUpload.js +1 -2
  27. package/lib/deploy/functions/runtimes/node/index.js +11 -12
  28. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  29. package/lib/deploy/functions/validate.js +49 -2
  30. package/lib/emulator/commandUtils.js +1 -1
  31. package/lib/emulator/controller.js +1 -2
  32. package/lib/emulator/databaseEmulator.js +1 -5
  33. package/lib/emulator/downloadableEmulatorInfo.json +30 -30
  34. package/lib/emulator/functionsEmulator.js +0 -40
  35. package/lib/emulator/functionsEmulatorRuntime.js +1 -118
  36. package/lib/emulator/functionsEmulatorShared.js +1 -6
  37. package/lib/experiments.js +8 -1
  38. package/lib/extensions/extensionsHelper.js +0 -1
  39. package/lib/frameworks/constants.js +1 -1
  40. package/lib/fsAsync.js +11 -3
  41. package/lib/functionsConfig.js +39 -1
  42. package/lib/gcp/cloudsql/cloudsqladmin.js +1 -1
  43. package/lib/index.js +44 -1
  44. package/lib/init/features/dataconnect/resolver.js +111 -0
  45. package/lib/init/features/index.js +4 -1
  46. package/lib/init/features/project.js +1 -0
  47. package/lib/init/index.js +5 -0
  48. package/lib/mcp/index.js +31 -22
  49. package/lib/mcp/tools/core/get_environment.js +4 -1
  50. package/lib/mcp/tools/dataconnect/compile.js +13 -7
  51. package/lib/mcp/tools/dataconnect/execute.js +10 -7
  52. package/lib/mcp/tools/dataconnect/generate_operation.js +7 -3
  53. package/lib/mcp/tools/index.js +53 -44
  54. package/package.json +1 -2
  55. package/lib/deploy/functions/runtimes/node/extractTriggers.js +0 -23
  56. package/lib/deploy/functions/runtimes/node/parseTriggers.js +0 -332
  57. package/lib/deploy/functions/runtimes/node/triggerParser.js +0 -72
  58. package/lib/functions/deprecationWarnings.js +0 -21
  59. package/lib/functions/runtimeConfigExport.js +0 -141
  60. package/lib/handlePreviewToggles.js +0 -38
  61. package/lib/parseBoltRules.js +0 -29
@@ -1,16 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.cpuConfigIsValid = exports.endpointsAreValid = exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = exports.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = void 0;
3
+ exports.secretsAreValid = exports.functionIdsAreValid = exports.functionsDirectoryExists = exports.endpointsAreUnique = exports.validateTimeoutConfig = exports.cpuConfigIsValid = exports.endpointsAreValid = exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = exports.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = void 0;
4
4
  const path = require("path");
5
5
  const clc = require("colorette");
6
6
  const error_1 = require("../../error");
7
7
  const secretManager_1 = require("../../gcp/secretManager");
8
8
  const logger_1 = require("../../logger");
9
+ const functionsDeployHelper_1 = require("./functionsDeployHelper");
10
+ const services_1 = require("./services");
9
11
  const fsutils = require("../../fsutils");
10
12
  const backend = require("./backend");
11
13
  const utils = require("../../utils");
12
14
  const secrets = require("../../functions/secrets");
13
- const services_1 = require("./services");
15
+ const MAX_V1_TIMEOUT_SECONDS = 540;
16
+ const MAX_V2_EVENTS_TIMEOUT_SECONDS = 540;
17
+ const MAX_V2_SCHEDULE_TIMEOUT_SECONDS = 1800;
18
+ const MAX_V2_TASK_QUEUE_TIMEOUT_SECONDS = 1800;
19
+ const MAX_V2_HTTP_TIMEOUT_SECONDS = 3600;
14
20
  exports.DEFAULT_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = 180;
15
21
  exports.MAX_V2_SCHEDULE_ATTEMPT_DEADLINE_SECONDS = 1800;
16
22
  function matchingIds(endpoints, filter) {
@@ -39,6 +45,7 @@ function validateScheduledTimeout(ep) {
39
45
  function endpointsAreValid(wantBackend) {
40
46
  const endpoints = backend.allEndpoints(wantBackend);
41
47
  functionIdsAreValid(endpoints);
48
+ validateTimeoutConfig(endpoints);
42
49
  for (const ep of endpoints) {
43
50
  validateScheduledTimeout(ep);
44
51
  (0, services_1.serviceForEndpoint)(ep).validateTrigger(ep, wantBackend);
@@ -116,6 +123,46 @@ function cpuConfigIsValid(endpoints) {
116
123
  }
117
124
  }
118
125
  exports.cpuConfigIsValid = cpuConfigIsValid;
126
+ function validateTimeoutConfig(endpoints) {
127
+ const invalidEndpoints = [];
128
+ for (const ep of endpoints) {
129
+ const timeout = ep.timeoutSeconds;
130
+ if (!timeout) {
131
+ continue;
132
+ }
133
+ let limit;
134
+ if (ep.platform === "gcfv1") {
135
+ limit = MAX_V1_TIMEOUT_SECONDS;
136
+ }
137
+ else if (backend.isEventTriggered(ep)) {
138
+ limit = MAX_V2_EVENTS_TIMEOUT_SECONDS;
139
+ }
140
+ else if (backend.isScheduleTriggered(ep)) {
141
+ limit = MAX_V2_SCHEDULE_TIMEOUT_SECONDS;
142
+ }
143
+ else if (backend.isTaskQueueTriggered(ep)) {
144
+ limit = MAX_V2_TASK_QUEUE_TIMEOUT_SECONDS;
145
+ }
146
+ else if (backend.isHttpsTriggered(ep) || backend.isCallableTriggered(ep)) {
147
+ limit = MAX_V2_HTTP_TIMEOUT_SECONDS;
148
+ }
149
+ if (limit !== undefined && timeout > limit) {
150
+ invalidEndpoints.push({ ep, limit });
151
+ }
152
+ }
153
+ if (invalidEndpoints.length === 0) {
154
+ return;
155
+ }
156
+ const invalidList = invalidEndpoints
157
+ .sort((a, b) => backend.compareFunctions(a.ep, b.ep))
158
+ .map(({ ep, limit }) => `\t${(0, functionsDeployHelper_1.getFunctionLabel)(ep)}: ${ep.timeoutSeconds}s (limit: ${limit}s)`)
159
+ .join("\n");
160
+ const msg = "The following functions have timeouts that exceed the maximum allowed for their trigger type:\n\n" +
161
+ invalidList +
162
+ "\n\nFor more information, see https://firebase.google.com/docs/functions/quotas#time_limits";
163
+ throw new error_1.FirebaseError(msg);
164
+ }
165
+ exports.validateTimeoutConfig = validateTimeoutConfig;
119
166
  function endpointsAreUnique(backends) {
120
167
  const endpointToCodebases = {};
121
168
  for (const [codebase, b] of Object.entries(backends)) {
@@ -385,5 +385,5 @@ async function checkJavaMajorVersion() {
385
385
  }
386
386
  exports.checkJavaMajorVersion = checkJavaMajorVersion;
387
387
  exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = 21;
388
- exports.JAVA_DEPRECATION_WARNING = "firebase-tools will drop support for Java version < 21 soon in firebase-tools@15. " +
388
+ exports.JAVA_DEPRECATION_WARNING = "firebase-tools no longer supports Java version before 21. " +
389
389
  "Please install a JDK at version 21 or above to get a compatible runtime.";
@@ -180,8 +180,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
180
180
  const deprecationNotices = [];
181
181
  if (targets.some(downloadableEmulators_1.requiresJava)) {
182
182
  if ((await commandUtils.checkJavaMajorVersion()) < commandUtils_1.MIN_SUPPORTED_JAVA_MAJOR_VERSION) {
183
- utils.logLabeledError("emulators", commandUtils_1.JAVA_DEPRECATION_WARNING, "warn");
184
- deprecationNotices.push(commandUtils_1.JAVA_DEPRECATION_WARNING);
183
+ throw new error_1.FirebaseError(commandUtils_1.JAVA_DEPRECATION_WARNING);
185
184
  }
186
185
  }
187
186
  if (options.logVerbosity) {
@@ -12,7 +12,6 @@ const constants_1 = require("./constants");
12
12
  const registry_1 = require("./registry");
13
13
  const emulatorLogger_1 = require("./emulatorLogger");
14
14
  const error_1 = require("../error");
15
- const parseBoltRules_1 = require("../parseBoltRules");
16
15
  const utils_1 = require("../utils");
17
16
  class DatabaseEmulator {
18
17
  constructor(args) {
@@ -123,10 +122,7 @@ class DatabaseEmulator {
123
122
  }
124
123
  async updateRules(instance, rulesPath) {
125
124
  var _a;
126
- const rulesExt = path.extname(rulesPath);
127
- const content = rulesExt === ".bolt"
128
- ? (0, parseBoltRules_1.parseBoltRules)(rulesPath).toString()
129
- : fs.readFileSync(rulesPath, "utf8");
125
+ const content = fs.readFileSync(rulesPath, "utf8");
130
126
  try {
131
127
  await registry_1.EmulatorRegistry.client(types_1.Emulators.DATABASE).put(`/.settings/rules.json`, content, {
132
128
  headers: { Authorization: "Bearer owner" },
@@ -8,12 +8,12 @@
8
8
  "downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
9
9
  },
10
10
  "firestore": {
11
- "version": "1.19.8",
12
- "expectedSize": 63634791,
13
- "expectedChecksum": "9b43a6daa590678de9b7df6d68260395",
14
- "expectedChecksumSHA256": "9d43599ed6151199e8d604dc87fac51218e49e5f3a48519b1ae560bbe5e3382d",
15
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.19.8.jar",
16
- "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.19.8.jar"
11
+ "version": "1.20.2",
12
+ "expectedSize": 123046947,
13
+ "expectedChecksum": "3b04168138ff8ab17025fe7756e05dc9",
14
+ "expectedChecksumSHA256": "4a117fc297b1441eac1b7756e80442e86ef88865b9e3caf6f59eabf83da574f8",
15
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.20.2.jar",
16
+ "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.20.2.jar"
17
17
  },
18
18
  "storage": {
19
19
  "version": "1.1.3",
@@ -54,36 +54,36 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.17.3",
58
- "expectedSize": 30053216,
59
- "expectedChecksum": "b9bc9fefd65143b8ed3a5b0f0e1f26fa",
60
- "expectedChecksumSHA256": "03766907fdbd513e333602f989ebaf486cd53a73d9cdefd221a02988f757a7cc",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.17.3",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
57
+ "version": "3.0.1",
58
+ "expectedSize": 30212960,
59
+ "expectedChecksum": "5a556a6e52d8e4707be9e65555cf6fc0",
60
+ "expectedChecksumSHA256": "04507e034d26a9e1053a246508351881473c82daf86357a28472e112f50e0128",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.0.1",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.0.1"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "2.17.3",
66
- "expectedSize": 29510226,
67
- "expectedChecksum": "a585f578ac6891366059a6c4814b10cc",
68
- "expectedChecksumSHA256": "c1b6ad2976d6399435a11a9079a5e63e19b0af04c792cdb843a7464be793618c",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v2.17.3",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
65
+ "version": "3.0.1",
66
+ "expectedSize": 29680322,
67
+ "expectedChecksum": "31b21517979ce3acc408c432023a97e5",
68
+ "expectedChecksumSHA256": "36e2db3abf7a024436ba045805153a5f7ae2845f34658c187c38aeff329c6626",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.0.1",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.0.1"
71
71
  },
72
72
  "win32": {
73
- "version": "2.17.3",
74
- "expectedSize": 30547968,
75
- "expectedChecksum": "5a9d3490a1de85978441afff8582e7c3",
76
- "expectedChecksumSHA256": "8f2b8d38cbad8138cc33479456c91c5e4b046c689a4209a63fbb0c4dca9c6af8",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.17.3",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3.exe"
73
+ "version": "3.0.1",
74
+ "expectedSize": 30712832,
75
+ "expectedChecksum": "953603294bf7112a5100936db813f928",
76
+ "expectedChecksumSHA256": "4a71d6ce535302a28215e08cc3b645818cf8d8204f4bb2512455d3b7d3c17df1",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.0.1",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.0.1.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "2.17.3",
82
- "expectedSize": 29974712,
83
- "expectedChecksum": "aacf4f08de500df0913ff8ec6261a0ad",
84
- "expectedChecksumSHA256": "2d593500745c4ce0d522d6f5f75c89ed413ad4853891b8afafb5cbfbb45d4abd",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.17.3",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.17.3"
81
+ "version": "3.0.1",
82
+ "expectedSize": 30138552,
83
+ "expectedChecksum": "48d5f03c2edea8d4c6b200dccc9bf8a8",
84
+ "expectedChecksumSHA256": "cdf2d37f69b4372c0757afcc6c6ed4d7c6162fe7d29d9191e20e1411e116a665",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.0.1",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.0.1"
87
87
  }
88
88
  }
89
89
  }
@@ -6,7 +6,6 @@ const path = require("path");
6
6
  const express = require("express");
7
7
  const clc = require("colorette");
8
8
  const http = require("http");
9
- const jwt = require("jsonwebtoken");
10
9
  const cors = require("cors");
11
10
  const semver = require("semver");
12
11
  const url_1 = require("url");
@@ -1098,31 +1097,6 @@ class FunctionsEmulator {
1098
1097
  }
1099
1098
  return registry_1.EmulatorRegistry.getInfo(emulator);
1100
1099
  }
1101
- tokenFromAuthHeader(authHeader) {
1102
- const match = /^Bearer (.*)$/.exec(authHeader);
1103
- if (!match) {
1104
- return;
1105
- }
1106
- let idToken = match[1];
1107
- logger_1.logger.debug(`ID Token: ${idToken}`);
1108
- if (idToken && idToken.includes("=")) {
1109
- idToken = idToken.replace(/[=]+?\./g, ".");
1110
- logger_1.logger.debug(`ID Token contained invalid padding, new value: ${idToken}`);
1111
- }
1112
- try {
1113
- const decoded = jwt.decode(idToken, { complete: true });
1114
- if (!decoded || typeof decoded !== "object") {
1115
- logger_1.logger.debug(`Failed to decode ID Token: ${decoded}`);
1116
- return;
1117
- }
1118
- const claims = decoded.payload;
1119
- claims.uid = claims.sub;
1120
- return claims;
1121
- }
1122
- catch (e) {
1123
- return;
1124
- }
1125
- }
1126
1100
  async handleHttpsTrigger(req, res) {
1127
1101
  var _a;
1128
1102
  const method = req.method;
@@ -1150,20 +1124,6 @@ class FunctionsEmulator {
1150
1124
  req.headers["content-length"] = reqBody.length.toString();
1151
1125
  }
1152
1126
  }
1153
- const isCallable = trigger.labels && trigger.labels["deployment-callable"] === "true";
1154
- const authHeader = req.header("Authorization");
1155
- if (authHeader && isCallable && trigger.platform !== "gcfv2") {
1156
- const token = this.tokenFromAuthHeader(authHeader);
1157
- if (token) {
1158
- const contextAuth = {
1159
- uid: token.uid,
1160
- token: token,
1161
- };
1162
- req.headers[functionsEmulatorShared_1.HttpConstants.ORIGINAL_AUTH_HEADER] = req.headers["authorization"];
1163
- delete req.headers["authorization"];
1164
- req.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER] = encodeURIComponent(JSON.stringify(contextAuth));
1165
- }
1166
- }
1167
1127
  void (0, track_1.trackEmulator)(EVENT_INVOKE_GA4, {
1168
1128
  function_service: (0, functionsEmulatorShared_1.getFunctionService)(trigger),
1169
1129
  });
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const fs = require("fs");
4
3
  const express = require("express");
5
4
  const path = require("path");
6
5
  const bodyParser = require("body-parser");
@@ -138,7 +137,7 @@ async function assertResolveDeveloperNodeModule(name) {
138
137
  async function verifyDeveloperNodeModules() {
139
138
  const modBundles = [
140
139
  { name: "firebase-admin", isDev: false, minVersion: "8.9.0" },
141
- { name: "firebase-functions", isDev: false, minVersion: "3.13.1" },
140
+ { name: "firebase-functions", isDev: false, minVersion: "3.16.0" },
142
141
  ];
143
142
  for (const modBundle of modBundles) {
144
143
  const resolution = await resolveDeveloperNodeModule(modBundle.name);
@@ -264,87 +263,10 @@ async function initializeFirebaseFunctionsStubs() {
264
263
  httpsProvider.onRequest = (handler) => {
265
264
  return httpsProvider[onRequestInnerMethodName](handler, {});
266
265
  };
267
- const onCallInnerMethodName = "_onCallWithOptions";
268
- const onCallMethodOriginal = httpsProvider[onCallInnerMethodName];
269
- if (onCallMethodOriginal.length === 3) {
270
- httpsProvider[onCallInnerMethodName] = (opts, handler, deployOpts) => {
271
- const wrapped = wrapCallableHandler(handler);
272
- const cf = onCallMethodOriginal(opts, wrapped, deployOpts);
273
- return cf;
274
- };
275
- }
276
- else {
277
- httpsProvider[onCallInnerMethodName] = (handler, opts) => {
278
- const wrapped = wrapCallableHandler(handler);
279
- const cf = onCallMethodOriginal(wrapped, opts);
280
- return cf;
281
- };
282
- }
283
- httpsProvider.onCall = function (optsOrHandler, handler) {
284
- if (onCallMethodOriginal.length === 3) {
285
- let opts;
286
- if (arguments.length === 1) {
287
- opts = {};
288
- handler = optsOrHandler;
289
- }
290
- else {
291
- opts = optsOrHandler;
292
- }
293
- return httpsProvider[onCallInnerMethodName](opts, handler, {});
294
- }
295
- else {
296
- return httpsProvider[onCallInnerMethodName](optsOrHandler, {});
297
- }
298
- };
299
- }
300
- function wrapCallableHandler(handler) {
301
- const newHandler = (data, context) => {
302
- if (context.rawRequest) {
303
- const authContext = context.rawRequest.header(functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER);
304
- if (authContext) {
305
- logDebug("Callable functions auth override", {
306
- key: functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER,
307
- value: authContext,
308
- });
309
- context.auth = JSON.parse(decodeURIComponent(authContext));
310
- delete context.rawRequest.headers[functionsEmulatorShared_1.HttpConstants.CALLABLE_AUTH_HEADER];
311
- }
312
- else {
313
- logDebug("No callable functions auth found");
314
- }
315
- const originalAuth = context.rawRequest.header(functionsEmulatorShared_1.HttpConstants.ORIGINAL_AUTH_HEADER);
316
- if (originalAuth) {
317
- context.rawRequest.headers["authorization"] = originalAuth;
318
- delete context.rawRequest.headers[functionsEmulatorShared_1.HttpConstants.ORIGINAL_AUTH_HEADER];
319
- }
320
- }
321
- return handler(data, context);
322
- };
323
- return newHandler;
324
266
  }
325
267
  function getDefaultConfig() {
326
268
  return JSON.parse(process.env.FIREBASE_CONFIG || "{}");
327
269
  }
328
- function initializeRuntimeConfig() {
329
- if (!process.env.CLOUD_RUNTIME_CONFIG) {
330
- const configPath = `${process.cwd()}/.runtimeconfig.json`;
331
- try {
332
- const configContent = fs.readFileSync(configPath, "utf8");
333
- if (configContent) {
334
- try {
335
- JSON.parse(configContent.toString());
336
- logDebug(`Found local functions config: ${configPath}`);
337
- process.env.CLOUD_RUNTIME_CONFIG = configContent.toString();
338
- }
339
- catch (e) {
340
- new types_1.EmulatorLog("SYSTEM", "function-runtimeconfig-json-invalid", "").log();
341
- }
342
- }
343
- }
344
- catch (e) {
345
- }
346
- }
347
- }
348
270
  async function initializeFirebaseAdminStubs() {
349
271
  const adminResolution = await assertResolveDeveloperNodeModule("firebase-admin");
350
272
  const localAdminModule = require(adminResolution.resolution);
@@ -453,43 +375,6 @@ function warnAboutStorageProd() {
453
375
  }
454
376
  new types_1.EmulatorLog("WARN_ONCE", "runtime-status", "The Firebase Storage emulator is not running, so calls to Firebase Storage will affect production.").log();
455
377
  }
456
- async function initializeFunctionsConfigHelper() {
457
- const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
458
- if ((0, functionsEmulatorUtils_1.compareVersionStrings)(functionsResolution.version, "7.0.0") >= 0) {
459
- logDebug("Detected firebase-functions v7+, skipping config helper.");
460
- return;
461
- }
462
- const localFunctionsModule = require(functionsResolution.resolution);
463
- logDebug("Checked functions.config()", {
464
- config: localFunctionsModule.config(),
465
- });
466
- const originalConfig = localFunctionsModule.config();
467
- const proxiedConfig = new Proxied(originalConfig)
468
- .any((parentConfig, parentKey) => {
469
- const isInternal = parentKey.startsWith("Symbol(") || parentKey.startsWith("inspect");
470
- if (!parentConfig[parentKey] && !isInternal) {
471
- new types_1.EmulatorLog("SYSTEM", "functions-config-missing-value", "", {
472
- key: parentKey,
473
- }).log();
474
- }
475
- return parentConfig[parentKey];
476
- })
477
- .finalize();
478
- const functionsModuleProxy = new Proxied(localFunctionsModule);
479
- const proxiedFunctionsModule = functionsModuleProxy
480
- .when("config", () => () => {
481
- return proxiedConfig;
482
- })
483
- .finalize();
484
- const v = require.cache[functionsResolution.resolution];
485
- require.cache[functionsResolution.resolution] = Object.assign(v, {
486
- exports: proxiedFunctionsModule,
487
- path: path.dirname(functionsResolution.resolution),
488
- });
489
- logDebug("firebase-functions has been stubbed.", {
490
- functionsResolution,
491
- });
492
- }
493
378
  function rawBodySaver(req, res, buf) {
494
379
  req.rawBody = buf;
495
380
  }
@@ -580,9 +465,7 @@ async function initializeRuntime() {
580
465
  new types_1.EmulatorLog("INFO", "runtime-status", `Your functions could not be parsed due to an issue with your node_modules (see above)`).log();
581
466
  return;
582
467
  }
583
- initializeRuntimeConfig();
584
468
  initializeNetworkFiltering();
585
- await initializeFunctionsConfigHelper();
586
469
  await initializeFirebaseFunctionsStubs();
587
470
  await initializeFirebaseAdminStubs();
588
471
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.prepareEndpoints = exports.eventServiceImplemented = exports.EmulatedTrigger = exports.HttpConstants = exports.EVENTARC_SOURCE_ENV = void 0;
3
+ exports.toBackendInfo = exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.prepareEndpoints = exports.eventServiceImplemented = exports.EmulatedTrigger = exports.EVENTARC_SOURCE_ENV = void 0;
4
4
  const os = require("os");
5
5
  const path = require("path");
6
6
  const fs = require("fs");
@@ -23,11 +23,6 @@ const V2_EVENTS = [
23
23
  ...events.v2.FIRESTORE_EVENTS,
24
24
  ];
25
25
  exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
26
- class HttpConstants {
27
- }
28
- exports.HttpConstants = HttpConstants;
29
- HttpConstants.CALLABLE_AUTH_HEADER = "x-callable-context-auth";
30
- HttpConstants.ORIGINAL_AUTH_HEADER = "x-original-auth";
31
26
  class EmulatedTrigger {
32
27
  constructor(definition, module) {
33
28
  this.definition = definition;
@@ -26,7 +26,7 @@ exports.ALL_EXPERIMENTS = experiments({
26
26
  "of deploys. This has been made an experiment due to backend bugs that are " +
27
27
  "temporarily causing failures in some regions with this optimization enabled",
28
28
  public: true,
29
- default: true,
29
+ default: false,
30
30
  },
31
31
  deletegcfartifacts: {
32
32
  shortDescription: `Add the ${(0, colorette_1.bold)("functions:deletegcfartifacts")} command to purge docker build images`,
@@ -39,6 +39,13 @@ exports.ALL_EXPERIMENTS = experiments({
39
39
  `Registry. The ${(0, colorette_1.bold)("functions:deletegcfartifacts")} command ` +
40
40
  "will delete all Docker images created by Google Cloud Functions irrespective " +
41
41
  "of how that image was created.",
42
+ public: false,
43
+ },
44
+ legacyRuntimeConfigCommands: {
45
+ shortDescription: "Expose legacy functions.config() CLI commands",
46
+ fullDescription: "The Cloud Runtime Config API is deprecated. Enable this experiment to continue using the " +
47
+ "`functions:config:*` commands while you migrate to the Firebase Functions params APIs.",
48
+ default: true,
42
49
  public: true,
43
50
  },
44
51
  runfunctions: {
@@ -411,7 +411,6 @@ async function ensureExtensionsPublisherApiEnabled(options) {
411
411
  exports.ensureExtensionsPublisherApiEnabled = ensureExtensionsPublisherApiEnabled;
412
412
  async function archiveAndUploadSource(extPath, bucketName) {
413
413
  const zippedSource = await (0, archiveDirectory_1.archiveDirectory)(extPath, {
414
- type: "zip",
415
414
  ignore: ["node_modules", ".git"],
416
415
  });
417
416
  const res = await (0, storage_1.uploadObject)(zippedSource, bucketName);
@@ -22,7 +22,7 @@ exports.FIREBASE_FUNCTIONS_VERSION = "^6.0.1";
22
22
  exports.FIREBASE_ADMIN_VERSION = "^11.11.1";
23
23
  exports.SHARP_VERSION = "^0.32 || ^0.33";
24
24
  exports.NODE_VERSION = parseInt(process.versions.node, 10);
25
- exports.VALID_ENGINES = { node: [16, 18, 20] };
25
+ exports.VALID_ENGINES = { node: [20, 22, 24] };
26
26
  exports.VALID_LOCALE_FORMATS = [/^ALL_[a-z]+$/, /^[a-z]+_ALL$/, /^[a-z]+(_[a-z]+)?$/];
27
27
  exports.DEFAULT_REGION = "us-central1";
28
28
  exports.ALLOWED_SSR_REGIONS = [
package/lib/fsAsync.js CHANGED
@@ -7,8 +7,10 @@ const _ = require("lodash");
7
7
  const minimatch = require("minimatch");
8
8
  const path_1 = require("path");
9
9
  async function readdirRecursiveHelper(options) {
10
- const dirContents = (0, fs_extra_1.readdirSync)(options.path);
11
- const fullPaths = dirContents.map((n) => (0, path_1.join)(options.path, n));
10
+ const dirContents = (0, fs_extra_1.readdirSync)(options.path, { withFileTypes: true });
11
+ const fullPaths = dirContents
12
+ .filter((n) => !options.ignoreSymlinks || !n.isSymbolicLink())
13
+ .map((n) => (0, path_1.join)(options.path, n.name));
12
14
  const filteredPaths = fullPaths.filter((p) => !options.filter(p));
13
15
  const filePromises = [];
14
16
  for (const p of filteredPaths) {
@@ -20,7 +22,12 @@ async function readdirRecursiveHelper(options) {
20
22
  continue;
21
23
  }
22
24
  if (options.maxDepth > 1) {
23
- filePromises.push(readdirRecursiveHelper({ path: p, filter: options.filter, maxDepth: options.maxDepth - 1 }));
25
+ filePromises.push(readdirRecursiveHelper({
26
+ path: p,
27
+ filter: options.filter,
28
+ maxDepth: options.maxDepth - 1,
29
+ ignoreSymlinks: options.ignoreSymlinks,
30
+ }));
24
31
  }
25
32
  }
26
33
  const files = await Promise.all(filePromises);
@@ -49,6 +56,7 @@ async function readdirRecursive(options) {
49
56
  path: options.path,
50
57
  filter: filter,
51
58
  maxDepth,
59
+ ignoreSymlinks: !!options.ignoreSymlinks,
52
60
  });
53
61
  }
54
62
  exports.readdirRecursive = readdirRecursive;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseUnsetArgs = exports.parseSetArgs = exports.materializeAll = exports.materializeConfig = exports.setVariablesRecursive = exports.getFirebaseConfig = exports.getAppEngineLocation = exports.idsToVarName = exports.varNameToIds = exports.ensureApi = exports.RESERVED_NAMESPACES = void 0;
3
+ exports.parseUnsetArgs = exports.parseSetArgs = exports.materializeAll = exports.materializeConfig = exports.setVariablesRecursive = exports.getFirebaseConfig = exports.getAppEngineLocation = exports.idsToVarName = exports.varNameToIds = exports.ensureApi = exports.ensureLegacyRuntimeConfigCommandsEnabled = exports.logFunctionsConfigDeprecationWarning = exports.getFunctionsConfigDeprecationMessage = exports.RESERVED_NAMESPACES = void 0;
4
4
  const _ = require("lodash");
5
5
  const clc = require("colorette");
6
6
  const api_1 = require("./api");
@@ -9,8 +9,46 @@ const ensureApiEnabled_1 = require("./ensureApiEnabled");
9
9
  const error_1 = require("./error");
10
10
  const projectUtils_1 = require("./projectUtils");
11
11
  const runtimeconfig = require("./gcp/runtimeconfig");
12
+ const experiments = require("./experiments");
13
+ const utils_1 = require("./utils");
12
14
  exports.RESERVED_NAMESPACES = ["firebase"];
13
15
  const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.firebaseApiOrigin)() });
16
+ const LEGACY_RUNTIME_CONFIG_EXPERIMENT = "legacyRuntimeConfigCommands";
17
+ const FUNCTIONS_CONFIG_DEPRECATION_MESSAGE = `DEPRECATION NOTICE: Action required before March 2026
18
+
19
+ The functions.config() API and the Cloud Runtime Config service are deprecated. Deploys that rely on functions.config() will fail once Runtime Config shuts down in March 2026.
20
+
21
+ The legacy functions:config:* CLI commands are deprecated and will be removed before March 2026.
22
+
23
+ Learn how to migrate from functions.config() to the params package:
24
+
25
+ https://firebase.google.com/docs/functions/config-env#migrate-config
26
+
27
+ To convert existing functions.config() values to params, try the interactive migration command:
28
+
29
+ firebase functions:config:export
30
+ `;
31
+ const LEGACY_GUIDANCE_MESSAGE = `${FUNCTIONS_CONFIG_DEPRECATION_MESSAGE}
32
+
33
+ To run this legacy command temporarily, run the following command and try again:
34
+
35
+ firebase experiments:enable ${LEGACY_RUNTIME_CONFIG_EXPERIMENT}
36
+ `;
37
+ function getFunctionsConfigDeprecationMessage() {
38
+ return FUNCTIONS_CONFIG_DEPRECATION_MESSAGE;
39
+ }
40
+ exports.getFunctionsConfigDeprecationMessage = getFunctionsConfigDeprecationMessage;
41
+ function logFunctionsConfigDeprecationWarning() {
42
+ (0, utils_1.logWarningToStderr)(FUNCTIONS_CONFIG_DEPRECATION_MESSAGE);
43
+ }
44
+ exports.logFunctionsConfigDeprecationWarning = logFunctionsConfigDeprecationWarning;
45
+ function ensureLegacyRuntimeConfigCommandsEnabled() {
46
+ if (experiments.isEnabled(LEGACY_RUNTIME_CONFIG_EXPERIMENT)) {
47
+ return;
48
+ }
49
+ throw new error_1.FirebaseError(LEGACY_GUIDANCE_MESSAGE, { exit: 1 });
50
+ }
51
+ exports.ensureLegacyRuntimeConfigCommandsEnabled = ensureLegacyRuntimeConfigCommandsEnabled;
14
52
  function keyToIds(key) {
15
53
  const keyParts = key.split(".");
16
54
  const variable = keyParts.slice(1).join("/");
@@ -51,7 +51,7 @@ function instanceConsoleLink(projectId, instanceId) {
51
51
  return `https://console.cloud.google.com/sql/instances/${instanceId}/overview?project=${projectId}`;
52
52
  }
53
53
  exports.instanceConsoleLink = instanceConsoleLink;
54
- exports.DEFAULT_DATABASE_VERSION = "POSTGRES_15";
54
+ exports.DEFAULT_DATABASE_VERSION = "POSTGRES_17";
55
55
  async function createInstance(args) {
56
56
  const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
57
57
  if (args.enableGoogleMlIntegration) {
package/lib/index.js CHANGED
@@ -3,6 +3,7 @@ const program = require("commander");
3
3
  const clc = require("colorette");
4
4
  const leven = require("leven");
5
5
  const logger_1 = require("./logger");
6
+ const command_1 = require("./command");
6
7
  const pkg = require("../package.json");
7
8
  program.version(pkg.version);
8
9
  program.option("-P, --project <alias_or_project_id>", "the Firebase project to use for this command");
@@ -13,6 +14,7 @@ program.option("--non-interactive", "error out of the command instead of waiting
13
14
  program.option("-i, --interactive", "force prompts to be displayed");
14
15
  program.option("--debug", "print verbose debug output and keep a debug log file");
15
16
  program.option("-c, --config <path>", "path to the firebase.json file to use for configuration");
17
+ program.allowUnknownOption();
16
18
  const client = {
17
19
  cli: program,
18
20
  logger: require("./logger"),
@@ -23,6 +25,26 @@ const client = {
23
25
  return client.cli.commands[i];
24
26
  }
25
27
  }
28
+ const keys = name.split(":");
29
+ let obj = client;
30
+ for (const key of keys) {
31
+ if (!obj || (typeof obj !== "object" && typeof obj !== "function")) {
32
+ return;
33
+ }
34
+ const nextKey = Object.keys(obj).find((k) => k.toLowerCase() === key.toLowerCase());
35
+ if (!nextKey) {
36
+ return;
37
+ }
38
+ obj = obj[nextKey];
39
+ }
40
+ if ((0, command_1.isCommandModule)(obj)) {
41
+ obj.load();
42
+ for (let i = 0; i < client.cli.commands.length; i++) {
43
+ if (client.cli.commands[i]._name === name) {
44
+ return client.cli.commands[i];
45
+ }
46
+ }
47
+ }
26
48
  return;
27
49
  },
28
50
  };
@@ -53,8 +75,29 @@ const RENAMED_COMMANDS = {
53
75
  "prefs:token": "login:ci",
54
76
  };
55
77
  program.action((_, args) => {
56
- (0, logger_1.useConsoleLoggers)();
57
78
  const cmd = args[0];
79
+ const keys = cmd.split(":");
80
+ let obj = client;
81
+ let hit = true;
82
+ for (const key of keys) {
83
+ if (!obj || (typeof obj !== "object" && typeof obj !== "function")) {
84
+ hit = false;
85
+ break;
86
+ }
87
+ const nextKey = Object.keys(obj).find((k) => k.toLowerCase() === key.toLowerCase());
88
+ if (!nextKey) {
89
+ hit = false;
90
+ break;
91
+ }
92
+ obj = obj[nextKey];
93
+ }
94
+ if (hit && (0, command_1.isCommandModule)(obj)) {
95
+ obj.load();
96
+ client.cli.allowUnknownOption(false);
97
+ client.cli.parse(process.argv);
98
+ return;
99
+ }
100
+ (0, logger_1.useConsoleLoggers)();
58
101
  logger_1.logger.error(clc.bold(clc.red("Error:")), clc.bold(cmd), "is not a Firebase command");
59
102
  if (RENAMED_COMMANDS[cmd]) {
60
103
  logger_1.logger.error();