firebase-tools 10.5.0 → 10.7.1

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 (105) hide show
  1. package/lib/command.js +4 -4
  2. package/lib/commands/deploy.js +1 -1
  3. package/lib/commands/emulators-start.js +7 -2
  4. package/lib/commands/ext-configure.js +15 -5
  5. package/lib/commands/ext-export.js +6 -5
  6. package/lib/commands/ext-install.js +28 -44
  7. package/lib/commands/ext-update.js +9 -1
  8. package/lib/commands/functions-delete.js +7 -3
  9. package/lib/commands/functions-secrets-destroy.js +23 -3
  10. package/lib/commands/functions-secrets-prune.js +15 -12
  11. package/lib/commands/functions-secrets-set.js +51 -4
  12. package/lib/commands/hosting-channel-deploy.js +2 -2
  13. package/lib/deploy/database/deploy.js +4 -0
  14. package/lib/deploy/database/index.js +1 -0
  15. package/lib/deploy/extensions/deploy.js +4 -4
  16. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  17. package/lib/deploy/extensions/planner.js +36 -9
  18. package/lib/deploy/extensions/prepare.js +1 -1
  19. package/lib/deploy/extensions/secrets.js +2 -2
  20. package/lib/deploy/extensions/tasks.js +60 -21
  21. package/lib/deploy/functions/backend.js +37 -6
  22. package/lib/deploy/functions/build.js +162 -0
  23. package/lib/deploy/functions/checkIam.js +10 -6
  24. package/lib/deploy/functions/deploy.js +49 -28
  25. package/lib/deploy/functions/ensure.js +4 -4
  26. package/lib/deploy/functions/functionsDeployHelper.js +99 -24
  27. package/lib/deploy/functions/prepare.js +130 -62
  28. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  29. package/lib/deploy/functions/pricing.js +6 -3
  30. package/lib/deploy/functions/prompts.js +1 -7
  31. package/lib/deploy/functions/release/fabricator.js +70 -28
  32. package/lib/deploy/functions/release/index.js +41 -6
  33. package/lib/deploy/functions/release/planner.js +19 -12
  34. package/lib/deploy/functions/release/reporter.js +14 -11
  35. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  36. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  37. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/auth.js +95 -0
  42. package/lib/deploy/functions/services/index.js +41 -21
  43. package/lib/deploy/functions/services/storage.js +1 -6
  44. package/lib/deploy/functions/validate.js +32 -6
  45. package/lib/deploy/hosting/args.js +2 -0
  46. package/lib/deploy/hosting/convertConfig.js +39 -8
  47. package/lib/deploy/hosting/deploy.js +3 -3
  48. package/lib/deploy/hosting/prepare.js +2 -2
  49. package/lib/deploy/hosting/release.js +6 -2
  50. package/lib/deploy/index.js +82 -93
  51. package/lib/deploy/remoteconfig/deploy.js +4 -0
  52. package/lib/deploy/remoteconfig/index.js +3 -1
  53. package/lib/emulator/auth/operations.js +5 -0
  54. package/lib/emulator/auth/utils.js +3 -25
  55. package/lib/emulator/controller.js +17 -14
  56. package/lib/emulator/downloadableEmulators.js +39 -23
  57. package/lib/emulator/extensions/postinstall.js +41 -0
  58. package/lib/emulator/extensions/validation.js +2 -2
  59. package/lib/emulator/extensionsEmulator.js +85 -21
  60. package/lib/emulator/functionsEmulator.js +88 -10
  61. package/lib/emulator/functionsEmulatorShared.js +37 -21
  62. package/lib/emulator/functionsEmulatorShell.js +2 -3
  63. package/lib/emulator/pubsubEmulator.js +13 -9
  64. package/lib/emulator/registry.js +34 -12
  65. package/lib/emulator/storage/apis/firebase.js +13 -8
  66. package/lib/emulator/storage/apis/gcloud.js +15 -9
  67. package/lib/emulator/storage/files.js +14 -3
  68. package/lib/emulator/storage/index.js +9 -1
  69. package/lib/emulator/storage/metadata.js +18 -8
  70. package/lib/emulator/storage/rules/manager.js +7 -17
  71. package/lib/emulator/storage/server.js +38 -12
  72. package/lib/ensureApiEnabled.js +8 -4
  73. package/lib/extensions/askUserForParam.js +14 -11
  74. package/lib/extensions/changelog.js +1 -1
  75. package/lib/extensions/emulator/optionsHelper.js +9 -10
  76. package/lib/extensions/emulator/specHelper.js +7 -1
  77. package/lib/extensions/emulator/triggerHelper.js +11 -14
  78. package/lib/extensions/extensionsApi.js +2 -1
  79. package/lib/extensions/extensionsHelper.js +30 -24
  80. package/lib/extensions/manifest.js +28 -8
  81. package/lib/extensions/paramHelper.js +19 -13
  82. package/lib/extensions/provisioningHelper.js +2 -2
  83. package/lib/extensions/warnings.js +3 -3
  84. package/lib/functions/env.js +10 -2
  85. package/lib/functions/events/index.js +7 -0
  86. package/lib/functions/events/v1.js +6 -0
  87. package/lib/functions/projectConfig.js +32 -6
  88. package/lib/functions/runtimeConfigExport.js +10 -6
  89. package/lib/functions/secrets.js +99 -6
  90. package/lib/functionsShellCommandAction.js +1 -1
  91. package/lib/gcp/cloudfunctions.js +44 -18
  92. package/lib/gcp/cloudfunctionsv2.js +48 -25
  93. package/lib/gcp/cloudtasks.js +5 -3
  94. package/lib/gcp/identityPlatform.js +44 -0
  95. package/lib/gcp/secretManager.js +2 -2
  96. package/lib/metaprogramming.js +2 -0
  97. package/lib/previews.js +1 -1
  98. package/lib/serve/functions.js +16 -19
  99. package/lib/serve/hosting.js +25 -12
  100. package/lib/serve/index.js +6 -0
  101. package/lib/track.js +15 -21
  102. package/lib/utils.js +30 -1
  103. package/npm-shrinkwrap.json +44 -2
  104. package/package.json +4 -1
  105. package/schema/firebase-config.json +6 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
3
+ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.BLOCKING_LABEL = exports.CODEBASE_LABEL = exports.API_VERSION = void 0;
4
4
  const clc = require("cli-color");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
@@ -9,10 +9,21 @@ const backend = require("../deploy/functions/backend");
9
9
  const utils = require("../utils");
10
10
  const proto = require("./proto");
11
11
  const runtimes = require("../deploy/functions/runtimes");
12
+ const projectConfig = require("../functions/projectConfig");
12
13
  const apiv2_1 = require("../apiv2");
13
14
  const api_1 = require("../api");
14
15
  exports.API_VERSION = "v1";
16
+ exports.CODEBASE_LABEL = "firebase-functions-codebase";
15
17
  const client = new apiv2_1.Client({ urlPrefix: api_1.functionsOrigin, apiVersion: exports.API_VERSION });
18
+ exports.BLOCKING_LABEL = "deployment-blocking";
19
+ const BLOCKING_LABEL_KEY_TO_EVENT = {
20
+ "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
21
+ "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
22
+ };
23
+ const BLOCKING_EVENT_TO_LABEL_KEY = {
24
+ "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
25
+ "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
26
+ };
16
27
  function validateFunction(func) {
17
28
  proto.assertOneOf("Cloud Function", func, "sourceCode", "sourceArchiveUrl", "sourceRepository", "sourceUploadUrl");
18
29
  proto.assertOneOf("Cloud Function", func, "trigger", "httpsTrigger", "eventTrigger");
@@ -202,7 +213,7 @@ async function listAllFunctions(projectId) {
202
213
  }
203
214
  exports.listAllFunctions = listAllFunctions;
204
215
  function endpointFromFunction(gcfFunction) {
205
- var _a, _b, _c, _d, _e;
216
+ var _a, _b, _c, _d, _e, _f, _g;
206
217
  const [, project, , region, , id] = gcfFunction.name.split("/");
207
218
  let trigger;
208
219
  let uri;
@@ -223,25 +234,29 @@ function endpointFromFunction(gcfFunction) {
223
234
  callableTrigger: {},
224
235
  };
225
236
  }
237
+ else if ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[exports.BLOCKING_LABEL]) {
238
+ trigger = {
239
+ blockingTrigger: {
240
+ eventType: BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[exports.BLOCKING_LABEL]],
241
+ },
242
+ };
243
+ }
226
244
  else if (gcfFunction.httpsTrigger) {
227
245
  trigger = { httpsTrigger: {} };
228
- uri = gcfFunction.httpsTrigger.url;
229
- securityLevel = gcfFunction.httpsTrigger.securityLevel;
230
246
  }
231
247
  else {
232
248
  trigger = {
233
249
  eventTrigger: {
234
250
  eventType: gcfFunction.eventTrigger.eventType,
235
- eventFilters: [
236
- {
237
- attribute: "resource",
238
- value: gcfFunction.eventTrigger.resource,
239
- },
240
- ],
241
- retry: !!((_e = gcfFunction.eventTrigger.failurePolicy) === null || _e === void 0 ? void 0 : _e.retry),
251
+ eventFilters: { resource: gcfFunction.eventTrigger.resource },
252
+ retry: !!((_f = gcfFunction.eventTrigger.failurePolicy) === null || _f === void 0 ? void 0 : _f.retry),
242
253
  },
243
254
  };
244
255
  }
256
+ if (gcfFunction.httpsTrigger) {
257
+ uri = gcfFunction.httpsTrigger.url;
258
+ securityLevel = gcfFunction.httpsTrigger.securityLevel;
259
+ }
245
260
  if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
246
261
  logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
247
262
  }
@@ -254,15 +269,18 @@ function endpointFromFunction(gcfFunction) {
254
269
  if (securityLevel) {
255
270
  endpoint.securityLevel = securityLevel;
256
271
  }
257
- proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
272
+ proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
273
+ proto.renameIfPresent(endpoint, gcfFunction, "timeoutSeconds", "timeout", proto.secondsFromDuration);
258
274
  if (gcfFunction.vpcConnector) {
259
275
  endpoint.vpc = { connector: gcfFunction.vpcConnector };
260
276
  proto.renameIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings");
261
277
  }
278
+ endpoint.codebase = ((_g = gcfFunction.labels) === null || _g === void 0 ? void 0 : _g[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
262
279
  return endpoint;
263
280
  }
264
281
  exports.endpointFromFunction = endpointFromFunction;
265
282
  function functionFromEndpoint(endpoint, sourceUploadUrl) {
283
+ var _a;
266
284
  if (endpoint.platform !== "gcfv1") {
267
285
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
268
286
  }
@@ -278,13 +296,9 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
278
296
  };
279
297
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
280
298
  if (backend.isEventTriggered(endpoint)) {
281
- const resourceFilter = backend.findEventFilter(endpoint, "resource");
282
- if (!resourceFilter) {
283
- throw new error_1.FirebaseError("Invalid event trigger definition. Expected event filter with 'resource' attribute.");
284
- }
285
299
  gcfFunction.eventTrigger = {
286
300
  eventType: endpoint.eventTrigger.eventType,
287
- resource: resourceFilter.value,
301
+ resource: endpoint.eventTrigger.eventFilters.resource,
288
302
  };
289
303
  gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
290
304
  ? { retry: {} }
@@ -302,6 +316,10 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
302
316
  gcfFunction.httpsTrigger = {};
303
317
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
304
318
  }
319
+ else if (backend.isBlockingTriggered(endpoint)) {
320
+ gcfFunction.httpsTrigger = {};
321
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
322
+ }
305
323
  else {
306
324
  gcfFunction.httpsTrigger = {};
307
325
  if (backend.isCallableTriggered(endpoint)) {
@@ -311,11 +329,19 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
311
329
  gcfFunction.httpsTrigger.securityLevel = endpoint.securityLevel;
312
330
  }
313
331
  }
314
- proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
332
+ proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
333
+ proto.renameIfPresent(gcfFunction, endpoint, "timeout", "timeoutSeconds", proto.durationFromSeconds);
315
334
  if (endpoint.vpc) {
316
335
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
317
336
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
318
337
  }
338
+ const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
339
+ if (codebase !== projectConfig.DEFAULT_CODEBASE) {
340
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: codebase });
341
+ }
342
+ else {
343
+ (_a = gcfFunction.labels) === null || _a === void 0 ? true : delete _a[exports.CODEBASE_LABEL];
344
+ }
319
345
  return gcfFunction;
320
346
  }
321
347
  exports.functionFromEndpoint = functionFromEndpoint;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.API_VERSION = void 0;
3
+ exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.BLOCKING_LABEL = exports.CODEBASE_LABEL = exports.API_VERSION = void 0;
4
4
  const clc = require("cli-color");
5
5
  const apiv2_1 = require("../apiv2");
6
6
  const error_1 = require("../error");
@@ -11,12 +11,23 @@ const backend = require("../deploy/functions/backend");
11
11
  const runtimes = require("../deploy/functions/runtimes");
12
12
  const proto = require("./proto");
13
13
  const utils = require("../utils");
14
+ const projectConfig = require("../functions/projectConfig");
14
15
  exports.API_VERSION = "v2alpha";
16
+ exports.CODEBASE_LABEL = "firebase-functions-codebase";
15
17
  const client = new apiv2_1.Client({
16
18
  urlPrefix: api_1.functionsV2Origin,
17
19
  auth: true,
18
20
  apiVersion: exports.API_VERSION,
19
21
  });
22
+ exports.BLOCKING_LABEL = "deployment-blocking";
23
+ const BLOCKING_LABEL_KEY_TO_EVENT = {
24
+ "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
25
+ "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
26
+ };
27
+ const BLOCKING_EVENT_TO_LABEL_KEY = {
28
+ "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
29
+ "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
30
+ };
20
31
  const BYTES_PER_UNIT = {
21
32
  "": 1,
22
33
  k: 1e3,
@@ -146,6 +157,7 @@ async function deleteFunction(cloudFunction) {
146
157
  }
147
158
  exports.deleteFunction = deleteFunction;
148
159
  function functionFromEndpoint(endpoint, source) {
160
+ var _a;
149
161
  if (endpoint.platform !== "gcfv2") {
150
162
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
151
163
  }
@@ -166,9 +178,8 @@ function functionFromEndpoint(endpoint, source) {
166
178
  serviceConfig: {},
167
179
  };
168
180
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
169
- proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "serviceAccountEmail", "ingressSettings");
181
+ proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "serviceAccountEmail", "ingressSettings", "timeoutSeconds");
170
182
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemory", "availableMemoryMb", (mb) => `${mb}M`);
171
- proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "timeoutSeconds", "timeout", proto.secondsFromDuration);
172
183
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "minInstanceCount", "minInstances");
173
184
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "maxInstanceCount", "maxInstances");
174
185
  if (endpoint.vpc) {
@@ -180,25 +191,22 @@ function functionFromEndpoint(endpoint, source) {
180
191
  eventType: endpoint.eventTrigger.eventType,
181
192
  };
182
193
  if (gcfFunction.eventTrigger.eventType === v2_1.PUBSUB_PUBLISH_EVENT) {
183
- const pubsubFilter = backend.findEventFilter(endpoint, "topic");
184
- if (!pubsubFilter) {
185
- throw new error_1.FirebaseError("Invalid pubsub endpoint. Expected eventFilter with 'topic' attribute but found none.");
186
- }
187
- gcfFunction.eventTrigger.pubsubTopic = pubsubFilter.value;
188
- for (const filter of endpoint.eventTrigger.eventFilters) {
189
- if (filter.attribute === "topic") {
194
+ gcfFunction.eventTrigger.pubsubTopic = endpoint.eventTrigger.eventFilters.topic;
195
+ gcfFunction.eventTrigger.eventFilters = [];
196
+ for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
197
+ if (attribute === "topic")
190
198
  continue;
191
- }
192
- if (!gcfFunction.eventTrigger.eventFilters) {
193
- gcfFunction.eventTrigger.eventFilters = [];
194
- }
195
- gcfFunction.eventTrigger.eventFilters.push(filter);
199
+ gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
196
200
  }
197
201
  }
198
202
  else {
199
- gcfFunction.eventTrigger.eventFilters = endpoint.eventTrigger.eventFilters;
203
+ gcfFunction.eventTrigger.eventFilters = [];
204
+ for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
205
+ gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
206
+ }
200
207
  }
201
208
  proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
209
+ proto.copyIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "channel");
202
210
  if (endpoint.eventTrigger.retry) {
203
211
  logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
204
212
  }
@@ -213,11 +221,21 @@ function functionFromEndpoint(endpoint, source) {
213
221
  else if (backend.isCallableTriggered(endpoint)) {
214
222
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
215
223
  }
224
+ else if (backend.isBlockingTriggered(endpoint)) {
225
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
226
+ }
227
+ const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
228
+ if (codebase !== projectConfig.DEFAULT_CODEBASE) {
229
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: codebase });
230
+ }
231
+ else {
232
+ (_a = gcfFunction.labels) === null || _a === void 0 ? true : delete _a[exports.CODEBASE_LABEL];
233
+ }
216
234
  return gcfFunction;
217
235
  }
218
236
  exports.functionFromEndpoint = functionFromEndpoint;
219
237
  function endpointFromFunction(gcfFunction) {
220
- var _a, _b, _c;
238
+ var _a, _b, _c, _d, _e;
221
239
  const [, project, , region, , id] = gcfFunction.name.split("/");
222
240
  let trigger;
223
241
  if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
@@ -235,25 +253,30 @@ function endpointFromFunction(gcfFunction) {
235
253
  callableTrigger: {},
236
254
  };
237
255
  }
256
+ else if ((_d = gcfFunction.labels) === null || _d === void 0 ? void 0 : _d[exports.BLOCKING_LABEL]) {
257
+ trigger = {
258
+ blockingTrigger: {
259
+ eventType: BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[exports.BLOCKING_LABEL]],
260
+ },
261
+ };
262
+ }
238
263
  else if (gcfFunction.eventTrigger) {
239
264
  trigger = {
240
265
  eventTrigger: {
241
266
  eventType: gcfFunction.eventTrigger.eventType,
242
- eventFilters: [],
267
+ eventFilters: {},
243
268
  retry: false,
244
269
  },
245
270
  };
246
271
  if (gcfFunction.eventTrigger.pubsubTopic) {
247
- trigger.eventTrigger.eventFilters.push({
248
- attribute: "topic",
249
- value: gcfFunction.eventTrigger.pubsubTopic,
250
- });
272
+ trigger.eventTrigger.eventFilters.topic = gcfFunction.eventTrigger.pubsubTopic;
251
273
  }
252
274
  else {
253
275
  for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
254
- trigger.eventTrigger.eventFilters.push({ attribute, value });
276
+ trigger.eventTrigger.eventFilters[attribute] = value;
255
277
  }
256
278
  }
279
+ proto.copyIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "channel");
257
280
  proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
258
281
  }
259
282
  else {
@@ -265,9 +288,8 @@ function endpointFromFunction(gcfFunction) {
265
288
  const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
266
289
  project,
267
290
  region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, uri: gcfFunction.serviceConfig.uri });
268
- proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "ingressSettings", "environmentVariables");
291
+ proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "ingressSettings", "environmentVariables", "timeoutSeconds");
269
292
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "availableMemoryMb", "availableMemory", megabytes);
270
- proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
271
293
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
272
294
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
273
295
  proto.copyIfPresent(endpoint, gcfFunction, "labels");
@@ -275,6 +297,7 @@ function endpointFromFunction(gcfFunction) {
275
297
  endpoint.vpc = { connector: gcfFunction.serviceConfig.vpcConnector };
276
298
  proto.renameIfPresent(endpoint.vpc, gcfFunction.serviceConfig, "egressSettings", "vpcConnectorEgressSettings");
277
299
  }
300
+ endpoint.codebase = ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
278
301
  return endpoint;
279
302
  }
280
303
  exports.endpointFromFunction = endpointFromFunction;
@@ -13,7 +13,6 @@ const client = new apiv2_1.Client({
13
13
  exports.DEFAULT_SETTINGS = {
14
14
  rateLimits: {
15
15
  maxConcurrentDispatches: 1000,
16
- maxBurstSize: 100,
17
16
  maxDispatchesPerSecond: 500,
18
17
  },
19
18
  state: "RUNNING",
@@ -133,10 +132,13 @@ exports.queueNameForEndpoint = queueNameForEndpoint;
133
132
  function queueFromEndpoint(endpoint) {
134
133
  const queue = Object.assign(Object.assign({}, JSON.parse(JSON.stringify(exports.DEFAULT_SETTINGS))), { name: queueNameForEndpoint(endpoint) });
135
134
  if (endpoint.taskQueueTrigger.rateLimits) {
136
- proto.copyIfPresent(queue.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxBurstSize", "maxConcurrentDispatches", "maxDispatchesPerSecond");
135
+ proto.copyIfPresent(queue.rateLimits, endpoint.taskQueueTrigger.rateLimits, "maxConcurrentDispatches", "maxDispatchesPerSecond");
137
136
  }
138
137
  if (endpoint.taskQueueTrigger.retryConfig) {
139
- proto.copyIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxBackoff", "maxDoublings", "maxRetryDuration", "minBackoff");
138
+ proto.copyIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxAttempts", "maxDoublings");
139
+ proto.renameIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxRetryDuration", "maxRetrySeconds", proto.durationFromSeconds);
140
+ proto.renameIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "maxBackoff", "maxBackoffSeconds", proto.durationFromSeconds);
141
+ proto.renameIfPresent(queue.retryConfig, endpoint.taskQueueTrigger.retryConfig, "minBackoff", "minBackoffSeconds", proto.durationFromSeconds);
140
142
  }
141
143
  return queue;
142
144
  }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateConfig = exports.setBlockingFunctionsConfig = exports.getConfig = exports.getBlockingFunctionsConfig = void 0;
4
+ const proto = require("./proto");
5
+ const api_1 = require("../api");
6
+ const apiv2_1 = require("../apiv2");
7
+ const API_VERSION = "v2";
8
+ const adminApiClient = new apiv2_1.Client({
9
+ urlPrefix: api_1.identityOrigin + "/admin",
10
+ apiVersion: API_VERSION,
11
+ });
12
+ async function getBlockingFunctionsConfig(project) {
13
+ const config = (await getConfig(project)) || {};
14
+ if (!config.blockingFunctions) {
15
+ config.blockingFunctions = {};
16
+ }
17
+ return config.blockingFunctions;
18
+ }
19
+ exports.getBlockingFunctionsConfig = getBlockingFunctionsConfig;
20
+ async function getConfig(project) {
21
+ const response = await adminApiClient.get(`projects/${project}/config`);
22
+ return response.body;
23
+ }
24
+ exports.getConfig = getConfig;
25
+ async function setBlockingFunctionsConfig(project, blockingConfig) {
26
+ const config = (await updateConfig(project, { blockingFunctions: blockingConfig }, "blockingFunctions")) || {};
27
+ if (!config.blockingFunctions) {
28
+ config.blockingFunctions = {};
29
+ }
30
+ return config.blockingFunctions;
31
+ }
32
+ exports.setBlockingFunctionsConfig = setBlockingFunctionsConfig;
33
+ async function updateConfig(project, config, updateMask) {
34
+ if (!updateMask) {
35
+ updateMask = proto.fieldMasks(config).join(",");
36
+ }
37
+ const response = await adminApiClient.patch(`projects/${project}/config`, config, {
38
+ queryParams: {
39
+ updateMask,
40
+ },
41
+ });
42
+ return response.body;
43
+ }
44
+ exports.updateConfig = updateConfig;
@@ -33,7 +33,7 @@ async function listSecrets(projectId, filter) {
33
33
  ? baseOpts
34
34
  : Object.assign(Object.assign({}, baseOpts), { queryParams: Object.assign(Object.assign({}, baseOpts === null || baseOpts === void 0 ? void 0 : baseOpts.queryParams), { pageToken }) });
35
35
  const res = await client.get(path, opts);
36
- for (const s of res.body.secrets) {
36
+ for (const s of res.body.secrets || []) {
37
37
  secrets.push(Object.assign(Object.assign({}, parseSecretResourceName(s.name)), { labels: (_a = s.labels) !== null && _a !== void 0 ? _a : {} }));
38
38
  }
39
39
  if (!res.body.nextPageToken) {
@@ -133,7 +133,7 @@ async function createSecret(projectId, name, labels) {
133
133
  },
134
134
  labels,
135
135
  }, { queryParams: { secretId: name } });
136
- return parseSecretResourceName(createRes.body.name);
136
+ return Object.assign(Object.assign({}, parseSecretResourceName(createRes.body.name)), { labels });
137
137
  }
138
138
  exports.createSecret = createSecret;
139
139
  async function patchSecret(projectId, name, labels) {
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/previews.js CHANGED
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.previews = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  const configstore_1 = require("./configstore");
6
- exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, extensionsemulator: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, artifactregistry: false, emulatoruisnapshot: false }, configstore_1.configstore.get("previews"));
6
+ exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, extensionsemulator: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, artifactregistry: false, emulatoruisnapshot: false, frameworkawareness: false }, configstore_1.configstore.get("previews"));
7
7
  if (process.env.FIREBASE_CLI_PREVIEWS) {
8
8
  process.env.FIREBASE_CLI_PREVIEWS.split(",").forEach((feature) => {
9
9
  if ((0, lodash_1.has)(exports.previews, feature)) {
@@ -10,28 +10,29 @@ const auth_1 = require("../auth");
10
10
  const projectConfig = require("../functions/projectConfig");
11
11
  const utils = require("../utils");
12
12
  class FunctionsServer {
13
- constructor() {
14
- this.emulatorServer = undefined;
15
- this.backend = undefined;
16
- }
17
13
  assertServer() {
18
- if (!this.emulatorServer || !this.backend) {
14
+ if (!this.emulatorServer || !this.backends) {
19
15
  throw new Error("Must call start() before calling any other operation!");
20
16
  }
21
17
  }
22
18
  async start(options, partialArgs) {
23
19
  const projectId = (0, projectUtils_1.needProjectId)(options);
24
- const config = projectConfig.normalizeAndValidate(options.config.src.functions)[0];
25
- const functionsDir = path.join(options.config.projectDir, config.source);
20
+ const config = projectConfig.normalizeAndValidate(options.config.src.functions);
21
+ const backends = [];
22
+ for (const cfg of config) {
23
+ const functionsDir = path.join(options.config.projectDir, cfg.source);
24
+ const nodeMajorVersion = (0, functionsEmulatorUtils_1.parseRuntimeVersion)(cfg.runtime);
25
+ backends.push({
26
+ functionsDir,
27
+ codebase: cfg.codebase,
28
+ nodeMajorVersion,
29
+ env: {},
30
+ secretEnv: [],
31
+ });
32
+ }
33
+ this.backends = backends;
26
34
  const account = (0, auth_1.getProjectDefaultAccount)(options.config.projectDir);
27
- const nodeMajorVersion = (0, functionsEmulatorUtils_1.parseRuntimeVersion)(config.runtime);
28
- this.backend = {
29
- functionsDir,
30
- nodeMajorVersion,
31
- env: {},
32
- secretEnv: [],
33
- };
34
- const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: [this.backend], projectAlias: options.projectAlias, account }, partialArgs);
35
+ const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: this.backends, projectAlias: options.projectAlias, account }, partialArgs);
35
36
  if (options.host) {
36
37
  utils.assertIsStringOrUndefined(options.host);
37
38
  args.host = options.host;
@@ -59,10 +60,6 @@ class FunctionsServer {
59
60
  this.assertServer();
60
61
  await this.emulatorServer.stop();
61
62
  }
62
- getBackend() {
63
- this.assertServer();
64
- return this.backend;
65
- }
66
63
  get() {
67
64
  this.assertServer();
68
65
  return this.emulatorServer.get();
@@ -15,6 +15,7 @@ const stream_1 = require("stream");
15
15
  const emulatorLogger_1 = require("../emulator/emulatorLogger");
16
16
  const types_1 = require("../emulator/types");
17
17
  const utils_1 = require("../utils");
18
+ const child_process_1 = require("child_process");
18
19
  const MAX_PORT_ATTEMPTS = 10;
19
20
  let attempts = 0;
20
21
  let destroyServer = undefined;
@@ -31,6 +32,29 @@ function startServer(options, config, port, init) {
31
32
  const morganMiddleware = morgan("combined", {
32
33
  stream: morganStream,
33
34
  });
35
+ const portInUse = () => {
36
+ const message = "Port " + options.port + " is not available.";
37
+ logger.log("WARN", clc.yellow("hosting: ") + message + " Trying another port...");
38
+ if (attempts < MAX_PORT_ATTEMPTS) {
39
+ attempts++;
40
+ startServer(options, config, port + 5, init);
41
+ }
42
+ else {
43
+ logger.log("WARN", message);
44
+ throw new error_1.FirebaseError("Could not find an open port for hosting development server.", {
45
+ exit: 1,
46
+ });
47
+ }
48
+ };
49
+ if (process.platform === "darwin") {
50
+ try {
51
+ (0, child_process_1.execSync)(`lsof -i :${port}`);
52
+ portInUse();
53
+ return;
54
+ }
55
+ catch (e) {
56
+ }
57
+ }
34
58
  const server = superstatic({
35
59
  debug: false,
36
60
  port: port,
@@ -60,18 +84,7 @@ function startServer(options, config, port, init) {
60
84
  destroyServer = (0, utils_1.createDestroyer)(server);
61
85
  server.on("error", (err) => {
62
86
  if (err.code === "EADDRINUSE") {
63
- const message = "Port " + options.port + " is not available.";
64
- logger.log("WARN", clc.yellow("hosting: ") + message + " Trying another port...");
65
- if (attempts < MAX_PORT_ATTEMPTS) {
66
- attempts++;
67
- startServer(options, config, port + 5, init);
68
- }
69
- else {
70
- logger.log("WARN", message);
71
- throw new error_1.FirebaseError("Could not find an open port for hosting development server.", {
72
- exit: 1,
73
- });
74
- }
87
+ portInUse();
75
88
  }
76
89
  else {
77
90
  throw new error_1.FirebaseError("An error occurred while starting the hosting development server:\n\n" + err.toString(), { exit: 1 });
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serve = void 0;
4
4
  const _ = require("lodash");
5
5
  const logger_1 = require("../logger");
6
+ const previews_1 = require("../previews");
6
7
  const { FunctionsServer } = require("./functions");
7
8
  const TARGETS = {
8
9
  hosting: require("./hosting"),
@@ -11,6 +12,11 @@ const TARGETS = {
11
12
  async function serve(options) {
12
13
  const targetNames = options.targets;
13
14
  options.port = parseInt(options.port, 10);
15
+ if (previews_1.previews.frameworkawareness &&
16
+ targetNames.includes("hosting") &&
17
+ [].concat(options.config.get("hosting")).some((it) => it.source)) {
18
+ await require("firebase-frameworks").prepare(targetNames, { project: options.projectId }, options);
19
+ }
14
20
  await Promise.all(_.map(targetNames, (targetName) => {
15
21
  return TARGETS[targetName].start(options);
16
22
  }));
package/lib/track.js CHANGED
@@ -1,31 +1,26 @@
1
1
  "use strict";
2
- var ua = require("universal-analytics");
3
- var _ = require("lodash");
4
- var { configstore } = require("./configstore");
5
- var pkg = require("../package.json");
6
- var uuid = require("uuid");
7
- const { logger } = require("./logger");
8
- var anonId = configstore.get("analytics-uuid");
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.track = void 0;
4
+ const ua = require("universal-analytics");
5
+ const uuid_1 = require("uuid");
6
+ const configstore_1 = require("./configstore");
7
+ const pkg = require("../package.json");
8
+ let anonId = configstore_1.configstore.get("analytics-uuid");
9
9
  if (!anonId) {
10
- anonId = uuid.v4();
11
- configstore.set("analytics-uuid", anonId);
10
+ anonId = (0, uuid_1.v4)();
11
+ configstore_1.configstore.set("analytics-uuid", anonId);
12
12
  }
13
- var visitor = ua(process.env.FIREBASE_ANALYTICS_UA || "UA-29174744-3", anonId, {
13
+ const visitor = ua(process.env.FIREBASE_ANALYTICS_UA || "UA-29174744-3", anonId, {
14
14
  strictCidFormat: false,
15
15
  https: true,
16
16
  });
17
17
  visitor.set("cd1", process.platform);
18
18
  visitor.set("cd2", process.version);
19
19
  visitor.set("cd3", process.env.FIREPIT_VERSION || "none");
20
- function track(action, label, duration) {
21
- return new Promise(function (resolve) {
22
- if (!_.isString(action) || !_.isString(label)) {
23
- logger.debug("track received non-string arguments:", action, label);
24
- resolve();
25
- }
26
- duration = duration || 0;
27
- if (configstore.get("tokens") && configstore.get("usage")) {
28
- visitor.event("Firebase CLI " + pkg.version, action, label, duration).send(function () {
20
+ function track(action, label, duration = 0) {
21
+ return new Promise((resolve) => {
22
+ if (configstore_1.configstore.get("tokens") && configstore_1.configstore.get("usage")) {
23
+ visitor.event("Firebase CLI " + pkg.version, action, label, duration).send(() => {
29
24
  resolve();
30
25
  });
31
26
  }
@@ -34,5 +29,4 @@ function track(action, label, duration) {
34
29
  }
35
30
  });
36
31
  }
37
- track.track = track;
38
- module.exports = track;
32
+ exports.track = track;
package/lib/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.assertDefined = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
3
+ exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.assertDefined = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.promiseWithSpinner = exports.setupLoggers = exports.tryParse = exports.tryStringify = exports.promiseProps = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.getInheritedOption = exports.consoleUrl = exports.envOverrides = void 0;
4
4
  const _ = require("lodash");
5
5
  const url = require("url");
6
6
  const clc = require("cli-color");
@@ -406,3 +406,32 @@ function groupBy(arr, f) {
406
406
  }, {});
407
407
  }
408
408
  exports.groupBy = groupBy;
409
+ function cloneArray(arr) {
410
+ return arr.map((e) => cloneDeep(e));
411
+ }
412
+ function cloneObject(obj) {
413
+ const clone = {};
414
+ for (const [k, v] of Object.entries(obj)) {
415
+ clone[k] = cloneDeep(v);
416
+ }
417
+ return clone;
418
+ }
419
+ function cloneDeep(obj) {
420
+ if (typeof obj !== "object" || !obj) {
421
+ return obj;
422
+ }
423
+ if (obj instanceof RegExp) {
424
+ return RegExp(obj, obj.flags);
425
+ }
426
+ if (obj instanceof Date) {
427
+ return new Date(obj);
428
+ }
429
+ if (Array.isArray(obj)) {
430
+ return cloneArray(obj);
431
+ }
432
+ if (obj instanceof Map) {
433
+ return new Map(obj.entries());
434
+ }
435
+ return cloneObject(obj);
436
+ }
437
+ exports.cloneDeep = cloneDeep;