firebase-tools 10.7.0 → 10.8.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 (64) hide show
  1. package/lib/commands/ext-configure.js +26 -15
  2. package/lib/commands/ext-export.js +14 -5
  3. package/lib/commands/ext-install.js +31 -2
  4. package/lib/commands/ext-update.js +17 -10
  5. package/lib/commands/functions-delete.js +9 -2
  6. package/lib/commands/functions-secrets-set.js +1 -13
  7. package/lib/deploy/extensions/planner.js +12 -0
  8. package/lib/deploy/extensions/tasks.js +13 -0
  9. package/lib/deploy/functions/backend.js +67 -10
  10. package/lib/deploy/functions/build.js +28 -9
  11. package/lib/deploy/functions/checkIam.js +71 -56
  12. package/lib/deploy/functions/containerCleaner.js +8 -7
  13. package/lib/deploy/functions/deploy.js +49 -27
  14. package/lib/deploy/functions/functionsDeployHelper.js +48 -4
  15. package/lib/deploy/functions/prepare.js +125 -74
  16. package/lib/deploy/functions/pricing.js +2 -2
  17. package/lib/deploy/functions/release/executor.js +1 -1
  18. package/lib/deploy/functions/release/fabricator.js +94 -36
  19. package/lib/deploy/functions/release/index.js +16 -27
  20. package/lib/deploy/functions/release/planner.js +12 -7
  21. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +13 -1
  22. package/lib/deploy/functions/runtimes/golang/index.js +3 -0
  23. package/lib/deploy/functions/runtimes/node/index.js +7 -0
  24. package/lib/deploy/functions/runtimes/node/parseTriggers.js +108 -1
  25. package/lib/deploy/functions/services/storage.js +6 -12
  26. package/lib/deploy/functions/validate.js +58 -8
  27. package/lib/deploy/hosting/convertConfig.js +6 -4
  28. package/lib/emulator/auth/cloudFunctions.js +6 -2
  29. package/lib/emulator/auth/operations.js +0 -1
  30. package/lib/emulator/auth/server.js +8 -1
  31. package/lib/emulator/auth/state.js +27 -24
  32. package/lib/emulator/controller.js +12 -9
  33. package/lib/emulator/databaseEmulator.js +36 -3
  34. package/lib/emulator/downloadableEmulators.js +7 -7
  35. package/lib/emulator/extensionsEmulator.js +3 -0
  36. package/lib/emulator/functionsEmulator.js +11 -9
  37. package/lib/emulator/functionsEmulatorRuntime.js +1 -1
  38. package/lib/emulator/functionsEmulatorShared.js +5 -1
  39. package/lib/emulator/functionsEmulatorShell.js +2 -3
  40. package/lib/emulator/functionsEmulatorUtils.js +5 -1
  41. package/lib/emulator/pubsubEmulator.js +13 -9
  42. package/lib/emulator/storage/apis/firebase.js +26 -4
  43. package/lib/ensureApiEnabled.js +1 -1
  44. package/lib/extensions/askUserForEventsConfig.js +97 -0
  45. package/lib/extensions/export.js +7 -0
  46. package/lib/extensions/extensionsApi.js +47 -7
  47. package/lib/extensions/manifest.js +1 -1
  48. package/lib/extensions/paramHelper.js +2 -0
  49. package/lib/extensions/updateHelper.js +7 -1
  50. package/lib/extensions/warnings.js +11 -4
  51. package/lib/functions/projectConfig.js +13 -8
  52. package/lib/functionsShellCommandAction.js +1 -1
  53. package/lib/gcp/cloudfunctions.js +9 -2
  54. package/lib/gcp/cloudfunctionsv2.js +28 -10
  55. package/lib/gcp/serviceusage.js +24 -0
  56. package/lib/previews.js +1 -1
  57. package/lib/serve/functions.js +16 -19
  58. package/lib/throttler/throttler.js +2 -1
  59. package/npm-shrinkwrap.json +214 -527
  60. package/package.json +3 -3
  61. package/templates/extensions/typescript/package.lint.json +2 -1
  62. package/templates/extensions/typescript/package.nolint.json +2 -1
  63. package/templates/init/functions/typescript/package.lint.json +1 -0
  64. package/templates/init/functions/typescript/package.nolint.json +1 -0
@@ -19,6 +19,7 @@ const refs = require("../extensions/refs");
19
19
  const manifest = require("../extensions/manifest");
20
20
  const functional_1 = require("../functional");
21
21
  const paramHelper_1 = require("../extensions/paramHelper");
22
+ const askUserForEventsConfig = require("../extensions/askUserForEventsConfig");
22
23
  marked.setOptions({
23
24
  renderer: new TerminalRenderer(),
24
25
  });
@@ -67,6 +68,15 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
67
68
  instanceId,
68
69
  reconfiguring: true,
69
70
  });
71
+ const eventsConfig = spec.events
72
+ ? await askUserForEventsConfig.askForEventsConfig(spec.events, "${param:PROJECT_ID}", instanceId)
73
+ : undefined;
74
+ if (eventsConfig) {
75
+ mutableParamsBindingOptions.EVENTARC_CHANNEL = { baseValue: eventsConfig.channel };
76
+ mutableParamsBindingOptions.ALLOWED_EVENT_TYPES = {
77
+ baseValue: eventsConfig.allowedEventTypes.join(","),
78
+ };
79
+ }
70
80
  const newParamOptions = Object.assign(Object.assign({}, (0, paramHelper_1.buildBindingOptionsWithBaseValue)(oldParamValues)), mutableParamsBindingOptions);
71
81
  await manifest.writeToManifest([
72
82
  {
@@ -83,20 +93,12 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
83
93
  manifest.showPreviewWarning();
84
94
  return;
85
95
  }
96
+ if (!projectId) {
97
+ throw new error_1.FirebaseError(`Project ID must be provided when re-configuring an instance outside of local mode.`);
98
+ }
86
99
  const spinner = ora(`Configuring ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
87
100
  try {
88
- let existingInstance;
89
- try {
90
- existingInstance = await extensionsApi.getInstance((0, projectUtils_1.needProjectId)({ projectId }), instanceId);
91
- }
92
- catch (err) {
93
- if (err.status === 404) {
94
- return utils.reject(`No extension instance ${instanceId} found in project ${projectId}.`, {
95
- exit: 1,
96
- });
97
- }
98
- throw err;
99
- }
101
+ const existingInstance = await extensionsApi.getInstance((0, projectUtils_1.needProjectId)({ projectId }), instanceId);
100
102
  const paramSpecWithNewDefaults = paramHelper.getParamsWithCurrentValuesAsDefaults(existingInstance);
101
103
  const immutableParams = _.remove(paramSpecWithNewDefaults, (param) => param.immutable);
102
104
  const paramBindingOptions = await paramHelper.getParams({
@@ -121,12 +123,21 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
121
123
  : "To set a different value for this param") +
122
124
  ", uninstall the extension, then install a new instance of this extension.");
123
125
  }
126
+ const pId = (0, projectUtils_1.needProjectId)({ projectId });
127
+ const spec = existingInstance ? existingInstance.config.source.spec : undefined;
128
+ const eventsConfig = spec.events
129
+ ? await askUserForEventsConfig.askForEventsConfig(spec.events, pId, instanceId)
130
+ : undefined;
124
131
  spinner.start();
125
- const res = await extensionsApi.configureInstance({
126
- projectId: (0, projectUtils_1.needProjectId)({ projectId }),
132
+ const configureOptions = {
133
+ projectId: pId,
127
134
  instanceId,
128
135
  params: paramBindings,
129
- });
136
+ canEmitEvents: eventsConfig ? true : false,
137
+ eventarcChannel: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.channel,
138
+ allowedEventTypes: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.allowedEventTypes,
139
+ };
140
+ const res = await extensionsApi.configureInstance(configureOptions);
130
141
  spinner.stop();
131
142
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `successfully configured ${clc.bold(instanceId)}.`);
132
143
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`You can view your reconfigured instance in the Firebase console: ${utils.consoleUrl((0, projectUtils_1.needProjectId)({ projectId }), `/extensions/instances/${instanceId}?tab=config`)}`));
@@ -43,11 +43,20 @@ module.exports = new command_1.Command("ext:export")
43
43
  logger_1.logger.info("Exiting. No changes made.");
44
44
  return;
45
45
  }
46
- const manifestSpecs = withRef.map((spec) => ({
47
- instanceId: spec.instanceId,
48
- ref: spec.ref,
49
- params: (0, paramHelper_1.buildBindingOptionsWithBaseValue)(spec.params),
50
- }));
46
+ const manifestSpecs = withRefSubbed.map((spec) => {
47
+ const paramCopy = Object.assign({}, spec.params);
48
+ if (spec.eventarcChannel) {
49
+ paramCopy.EVENTARC_CHANNEL = spec.eventarcChannel;
50
+ }
51
+ if (spec.allowedEventTypes) {
52
+ paramCopy.ALLOWED_EVENT_TYPES = spec.allowedEventTypes.join(",");
53
+ }
54
+ return {
55
+ instanceId: spec.instanceId,
56
+ ref: spec.ref,
57
+ params: (0, paramHelper_1.buildBindingOptionsWithBaseValue)(paramCopy),
58
+ };
59
+ });
51
60
  const existingConfig = manifest.loadConfig(options);
52
61
  await manifest.writeToManifest(manifestSpecs, existingConfig, {
53
62
  nonInteractive: options.nonInteractive,
@@ -6,6 +6,7 @@ const ora = require("ora");
6
6
  const TerminalRenderer = require("marked-terminal");
7
7
  const askUserForConsent = require("../extensions/askUserForConsent");
8
8
  const displayExtensionInfo_1 = require("../extensions/displayExtensionInfo");
9
+ const askUserForEventsConfig = require("../extensions/askUserForEventsConfig");
9
10
  const billingMigrationHelper_1 = require("../extensions/billingMigrationHelper");
10
11
  const checkProjectBilling_1 = require("../extensions/checkProjectBilling");
11
12
  const cloudbilling_1 = require("../gcp/cloudbilling");
@@ -166,6 +167,15 @@ async function installToManifest(options) {
166
167
  paramsEnvPath,
167
168
  instanceId,
168
169
  });
170
+ const eventsConfig = spec.events
171
+ ? await askUserForEventsConfig.askForEventsConfig(spec.events, "${param:PROJECT_ID}", instanceId)
172
+ : undefined;
173
+ if (eventsConfig) {
174
+ paramBindingOptions.EVENTARC_CHANNEL = { baseValue: eventsConfig.channel };
175
+ paramBindingOptions.ALLOWED_EVENT_TYPES = {
176
+ baseValue: eventsConfig.allowedEventTypes.join(","),
177
+ };
178
+ }
169
179
  const ref = extVersion ? refs.parse(extVersion.ref) : undefined;
170
180
  await manifest.writeToManifest([
171
181
  {
@@ -210,6 +220,12 @@ async function installExtension(options) {
210
220
  reason: `To access and manage secrets which are used by this extension. By using this product you agree to the terms and conditions of the following license: https://console.cloud.google.com/tos?id=cloud&project=${projectId}`,
211
221
  });
212
222
  }
223
+ if (spec.events && spec.events.length > 0) {
224
+ apis.push({
225
+ apiName: "eventarc.googleapis.com",
226
+ reason: `When events are enabled, the Eventarc API is required to provision an event channel and publish events.`,
227
+ });
228
+ }
213
229
  if (apis.length) {
214
230
  askUserForConsent.displayApis(spec.displayName || spec.name, projectId, apis);
215
231
  const consented = await (0, extensionsHelper_1.confirm)({ nonInteractive, force, default: true });
@@ -248,6 +264,7 @@ async function installExtension(options) {
248
264
  }
249
265
  let paramBindingOptions;
250
266
  let paramBindings;
267
+ let eventsConfig;
251
268
  switch (choice) {
252
269
  case "installNew":
253
270
  instanceId = await (0, extensionsHelper_1.promptForValidInstanceId)(`${instanceId}-${(0, utils_1.getRandomString)(4)}`);
@@ -258,6 +275,9 @@ async function installExtension(options) {
258
275
  paramsEnvPath,
259
276
  instanceId,
260
277
  });
278
+ eventsConfig = spec.events
279
+ ? await askUserForEventsConfig.askForEventsConfig(spec.events, projectId, instanceId)
280
+ : undefined;
261
281
  paramBindings = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
262
282
  spinner.text = "Installing your extension instance. This usually takes 3 to 5 minutes...";
263
283
  spinner.start();
@@ -267,6 +287,8 @@ async function installExtension(options) {
267
287
  extensionSource: source,
268
288
  extensionVersionRef: extVersion === null || extVersion === void 0 ? void 0 : extVersion.ref,
269
289
  params: paramBindings,
290
+ allowedEventTypes: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.allowedEventTypes,
291
+ eventarcChannel: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.channel,
270
292
  });
271
293
  spinner.stop();
272
294
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `Successfully installed your instance of ${clc.bold(spec.displayName || spec.name)}! ` +
@@ -280,16 +302,23 @@ async function installExtension(options) {
280
302
  paramsEnvPath,
281
303
  instanceId,
282
304
  });
305
+ eventsConfig = spec.events
306
+ ? await askUserForEventsConfig.askForEventsConfig(spec.events, projectId, instanceId)
307
+ : undefined;
283
308
  paramBindings = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
284
309
  spinner.text = "Updating your extension instance. This usually takes 3 to 5 minutes...";
285
310
  spinner.start();
286
- await (0, updateHelper_1.update)({
311
+ const updateOptions = {
287
312
  projectId,
288
313
  instanceId,
289
314
  source,
315
+ canEmitEvents: eventsConfig ? true : false,
316
+ eventarcChannel: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.channel,
317
+ allowedEventTypes: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.allowedEventTypes,
290
318
  extRef: extVersion === null || extVersion === void 0 ? void 0 : extVersion.ref,
291
319
  params: paramBindings,
292
- });
320
+ };
321
+ await (0, updateHelper_1.update)(updateOptions);
293
322
  spinner.stop();
294
323
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `Successfully updated your instance of ${clc.bold(spec.displayName || spec.name)}! ` +
295
324
  `Its Instance ID is ${clc.bold(instanceId)}.`);
@@ -23,6 +23,7 @@ const requirePermissions_1 = require("../requirePermissions");
23
23
  const utils = require("../utils");
24
24
  const previews_1 = require("../previews");
25
25
  const manifest = require("../extensions/manifest");
26
+ const askUserForEventsConfig = require("../extensions/askUserForEventsConfig");
26
27
  marked.setOptions({
27
28
  renderer: new TerminalRenderer(),
28
29
  });
@@ -86,6 +87,15 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
86
87
  nonInteractive: options.nonInteractive,
87
88
  instanceId,
88
89
  });
90
+ const eventsConfig = newExtensionVersion.spec.events
91
+ ? await askUserForEventsConfig.askForEventsConfig(newExtensionVersion.spec.events, "${param:PROJECT_ID}", instanceId)
92
+ : undefined;
93
+ if (eventsConfig) {
94
+ newParamBindingOptions.EVENTARC_CHANNEL = { baseValue: eventsConfig.channel };
95
+ newParamBindingOptions.ALLOWED_EVENT_TYPES = {
96
+ baseValue: eventsConfig.allowedEventTypes.join(","),
97
+ };
98
+ }
89
99
  await manifest.writeToManifest([
90
100
  {
91
101
  instanceId,
@@ -104,16 +114,7 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
104
114
  const spinner = ora(`Updating ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
105
115
  try {
106
116
  const projectId = (0, projectUtils_1.needProjectId)(options);
107
- let existingInstance;
108
- try {
109
- existingInstance = await extensionsApi.getInstance(projectId, instanceId);
110
- }
111
- catch (err) {
112
- if (err.status === 404) {
113
- throw new error_1.FirebaseError(`Extension instance '${clc.bold(instanceId)}' not found in project '${clc.bold(projectId)}'.`);
114
- }
115
- throw err;
116
- }
117
+ const existingInstance = await extensionsApi.getInstance(projectId, instanceId);
117
118
  const existingSpec = existingInstance.config.source.spec;
118
119
  if (existingInstance.config.source.state === "DELETED") {
119
120
  throw new error_1.FirebaseError(`Instance '${clc.bold(instanceId)}' cannot be updated anymore because the underlying extension was unpublished from Firebase's registry of extensions. Going forward, you will only be able to re-configure or uninstall this instance.`);
@@ -214,11 +215,17 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
214
215
  nonInteractive: options.nonInteractive,
215
216
  instanceId,
216
217
  });
218
+ const eventsConfig = newSpec.events
219
+ ? await askUserForEventsConfig.askForEventsConfig(newSpec.events, projectId, instanceId)
220
+ : undefined;
217
221
  const newParams = paramHelper.getBaseParamBindings(newParamBindings);
218
222
  spinner.start();
219
223
  const updateOptions = {
220
224
  projectId,
221
225
  instanceId,
226
+ canEmitEvents: eventsConfig ? true : false,
227
+ eventarcChannel: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.channel,
228
+ allowedEventTypes: eventsConfig === null || eventsConfig === void 0 ? void 0 : eventsConfig.allowedEventTypes,
222
229
  };
223
230
  if (newSourceName.includes("publisher")) {
224
231
  updateOptions.extRef = refs.toExtensionVersionRef(refs.parse(newSourceName));
@@ -40,7 +40,13 @@ exports.default = new command_1.Command("functions:delete [filters...]")
40
40
  if (options.region) {
41
41
  existingBackend.endpoints = { [options.region]: existingBackend.endpoints[options.region] };
42
42
  }
43
- const plan = planner.createDeploymentPlan(backend.empty(), existingBackend, context.filters, true);
43
+ const plan = planner.createDeploymentPlan({
44
+ wantBackend: backend.empty(),
45
+ haveBackend: existingBackend,
46
+ codebase: "",
47
+ filters: context.filters,
48
+ deleteAll: true,
49
+ });
44
50
  const allEpToDelete = Object.values(plan)
45
51
  .map((changes) => changes.endpointsToDelete)
46
52
  .reduce(functional_1.reduceFlat, [])
@@ -69,8 +75,9 @@ exports.default = new command_1.Command("functions:delete [filters...]")
69
75
  try {
70
76
  const fab = new fabricator.Fabricator({
71
77
  functionExecutor,
72
- executor: new executor.QueueExecutor({}),
73
78
  appEngineLocation,
79
+ executor: new executor.QueueExecutor({}),
80
+ sources: {},
74
81
  });
75
82
  const summary = await fab.applyPlan(plan);
76
83
  await reporter.logAndTrackDeployStats(summary);
@@ -77,17 +77,5 @@ exports.default = new command_1.Command("functions:secrets:set <KEY>")
77
77
  (0, utils_1.logBullet)(`Updated function ${e.id}(${e.region}).`);
78
78
  return updated;
79
79
  });
80
- const updatedEndpoints = await Promise.all(updateOps);
81
- (0, utils_1.logBullet)(`Pruning stale secrets...`);
82
- const prunedResult = await (0, secrets_1.pruneAndDestroySecrets)({ projectId, projectNumber }, updatedEndpoints);
83
- if (prunedResult.destroyed.length > 0) {
84
- (0, utils_1.logBullet)(`Detroyed unused secret versions: ${prunedResult.destroyed
85
- .map((s) => `${s.secret}@${s.version}`)
86
- .join(", ")}`);
87
- }
88
- if (prunedResult.erred.length > 0) {
89
- (0, utils_1.logWarning)(`Failed to destroy unused secret versions:\n\t${prunedResult.erred
90
- .map((err) => err.message)
91
- .join("\n\t")}`);
92
- }
80
+ await Promise.all(updateOps);
93
81
  });
@@ -52,6 +52,8 @@ async function have(projectId) {
52
52
  const dep = {
53
53
  instanceId: i.name.split("/").pop(),
54
54
  params: i.config.params,
55
+ allowedEventTypes: i.config.allowedEventTypes,
56
+ eventarcChannel: i.config.eventarcChannel,
55
57
  };
56
58
  if (i.config.extensionRef) {
57
59
  const ref = refs.parse(i.config.extensionRef);
@@ -78,11 +80,19 @@ async function want(args) {
78
80
  });
79
81
  const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
80
82
  const subbedParams = (0, extensionsHelper_1.substituteParams)(params, autoPopulatedParams);
83
+ const allowedEventTypes = subbedParams.ALLOWED_EVENT_TYPES !== undefined
84
+ ? subbedParams.ALLOWED_EVENT_TYPES.split(",").filter((e) => e !== "")
85
+ : undefined;
86
+ const eventarcChannel = subbedParams.EVENTARC_CHANNEL;
87
+ delete subbedParams["EVENTARC_CHANNEL"];
88
+ delete subbedParams["ALLOWED_EVENT_TYPES"];
81
89
  if ((0, extensionsHelper_1.isLocalPath)(e[1])) {
82
90
  instanceSpecs.push({
83
91
  instanceId,
84
92
  localPath: e[1],
85
93
  params: subbedParams,
94
+ allowedEventTypes: allowedEventTypes,
95
+ eventarcChannel: eventarcChannel,
86
96
  });
87
97
  }
88
98
  else {
@@ -92,6 +102,8 @@ async function want(args) {
92
102
  instanceId,
93
103
  ref,
94
104
  params: subbedParams,
105
+ allowedEventTypes: allowedEventTypes,
106
+ eventarcChannel: eventarcChannel,
95
107
  });
96
108
  }
97
109
  }
@@ -33,6 +33,8 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
33
33
  instanceId: instanceSpec.instanceId,
34
34
  params: instanceSpec.params,
35
35
  extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
36
+ allowedEventTypes: instanceSpec.allowedEventTypes,
37
+ eventarcChannel: instanceSpec.eventarcChannel,
36
38
  validateOnly,
37
39
  });
38
40
  }
@@ -43,6 +45,8 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
43
45
  instanceId: instanceSpec.instanceId,
44
46
  params: instanceSpec.params,
45
47
  extensionSource,
48
+ allowedEventTypes: instanceSpec.allowedEventTypes,
49
+ eventarcChannel: instanceSpec.eventarcChannel,
46
50
  validateOnly,
47
51
  });
48
52
  }
@@ -67,6 +71,9 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
67
71
  instanceId: instanceSpec.instanceId,
68
72
  extRef: refs.toExtensionVersionRef(instanceSpec.ref),
69
73
  params: instanceSpec.params,
74
+ canEmitEvents: !!instanceSpec.allowedEventTypes,
75
+ allowedEventTypes: instanceSpec.allowedEventTypes,
76
+ eventarcChannel: instanceSpec.eventarcChannel,
70
77
  validateOnly,
71
78
  });
72
79
  }
@@ -77,6 +84,9 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
77
84
  instanceId: instanceSpec.instanceId,
78
85
  extensionSource,
79
86
  validateOnly,
87
+ canEmitEvents: !!instanceSpec.allowedEventTypes,
88
+ allowedEventTypes: instanceSpec.allowedEventTypes,
89
+ eventarcChannel: instanceSpec.eventarcChannel,
80
90
  });
81
91
  }
82
92
  else {
@@ -99,6 +109,9 @@ function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly =
99
109
  projectId,
100
110
  instanceId: instanceSpec.instanceId,
101
111
  params: instanceSpec.params,
112
+ canEmitEvents: !!instanceSpec.allowedEventTypes,
113
+ allowedEventTypes: instanceSpec.allowedEventTypes,
114
+ eventarcChannel: instanceSpec.eventarcChannel,
102
115
  validateOnly,
103
116
  });
104
117
  }
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_MEMORY_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.memoryOptionDisplayName = exports.AllMemoryOptions = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
3
+ exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.AllMemoryOptions = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
4
4
  const gcf = require("../../gcp/cloudfunctions");
5
5
  const gcfV2 = require("../../gcp/cloudfunctionsv2");
6
+ const run = require("../../gcp/run");
6
7
  const utils = require("../../utils");
7
8
  const error_1 = require("../../error");
8
9
  const previews_1 = require("../../previews");
10
+ const functional_1 = require("../../functional");
9
11
  function endpointTriggerType(endpoint) {
10
12
  if (isScheduleTriggered(endpoint)) {
11
13
  return "scheduled";
@@ -36,7 +38,9 @@ exports.AllIngressSettings = [
36
38
  "ALLOW_INTERNAL_ONLY",
37
39
  "ALLOW_INTERNAL_AND_GCLB",
38
40
  ];
39
- exports.AllMemoryOptions = [128, 256, 512, 1024, 2048, 4096, 8192];
41
+ exports.AllMemoryOptions = [
42
+ 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
43
+ ];
40
44
  function memoryOptionDisplayName(option) {
41
45
  return {
42
46
  128: "128MB",
@@ -46,11 +50,42 @@ function memoryOptionDisplayName(option) {
46
50
  2048: "2GB",
47
51
  4096: "4GB",
48
52
  8192: "8GB",
53
+ 16384: "16GB",
54
+ 32768: "32GB",
49
55
  }[option];
50
56
  }
51
57
  exports.memoryOptionDisplayName = memoryOptionDisplayName;
58
+ function memoryToGen1Cpu(memory) {
59
+ return {
60
+ 128: 0.0833,
61
+ 256: 0.1666,
62
+ 512: 0.3333,
63
+ 1024: 0.5833,
64
+ 2048: 1,
65
+ 4096: 2,
66
+ 8192: 2,
67
+ 16384: 3,
68
+ 32768: 4,
69
+ }[memory];
70
+ }
71
+ exports.memoryToGen1Cpu = memoryToGen1Cpu;
72
+ function memoryToGen2Cpu(memory) {
73
+ return {
74
+ 128: 1,
75
+ 256: 1,
76
+ 512: 1,
77
+ 1024: 1,
78
+ 2048: 1,
79
+ 4096: 2,
80
+ 8192: 2,
81
+ 16384: 3,
82
+ 32768: 4,
83
+ }[memory];
84
+ }
85
+ exports.memoryToGen2Cpu = memoryToGen2Cpu;
86
+ exports.DEFAULT_CONCURRENCY = 80;
52
87
  exports.DEFAULT_MEMORY = 256;
53
- exports.MIN_MEMORY_FOR_CONCURRENCY = 2048;
88
+ exports.MIN_CPU_FOR_CONCURRENCY = 1;
54
89
  exports.SCHEDULED_FUNCTION_LABEL = Object.freeze({ deployment: "firebase-schedule" });
55
90
  function secretVersionName(s) {
56
91
  var _a;
@@ -102,6 +137,25 @@ function of(...endpoints) {
102
137
  return bkend;
103
138
  }
104
139
  exports.of = of;
140
+ function merge(...backends) {
141
+ const merged = of(...(0, functional_1.flattenArray)(backends.map((b) => allEndpoints(b))));
142
+ const apiToReasons = {};
143
+ for (const b of backends) {
144
+ for (const { api, reason } of b.requiredAPIs) {
145
+ const reasons = apiToReasons[api] || new Set();
146
+ if (reason) {
147
+ reasons.add(reason);
148
+ }
149
+ apiToReasons[api] = reasons;
150
+ }
151
+ merged.environmentVariables = Object.assign(Object.assign({}, merged.environmentVariables), b.environmentVariables);
152
+ }
153
+ for (const [api, reasons] of Object.entries(apiToReasons)) {
154
+ merged.requiredAPIs.push({ api, reason: Array.from(reasons).join(" ") });
155
+ }
156
+ return merged;
157
+ }
158
+ exports.merge = merge;
105
159
  function isEmptyBackend(backend) {
106
160
  return (Object.keys(backend.requiredAPIs).length === 0 && Object.keys(backend.endpoints).length === 0);
107
161
  }
@@ -144,6 +198,16 @@ async function loadExistingBackend(ctx) {
144
198
  let gcfV2Results;
145
199
  try {
146
200
  gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId);
201
+ const runResults = await Promise.all(gcfV2Results.functions.map((fn) => run.getService(fn.serviceConfig.service)));
202
+ for (const [apiFunction, runService] of (0, functional_1.zip)(gcfV2Results.functions, runResults)) {
203
+ const endpoint = gcfV2.endpointFromFunction(apiFunction);
204
+ endpoint.concurrency = runService.spec.template.spec.containerConcurrency || 1;
205
+ endpoint.cpu = +runService.spec.template.spec.containers[0].resources.limits.cpu;
206
+ ctx.existingBackend.endpoints[endpoint.region] =
207
+ ctx.existingBackend.endpoints[endpoint.region] || {};
208
+ ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
209
+ }
210
+ ctx.unreachableRegions.gcfV2 = gcfV2Results.unreachable;
147
211
  }
148
212
  catch (err) {
149
213
  if (err.status === 404 && ((_a = err.message) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes("method not found"))) {
@@ -151,13 +215,6 @@ async function loadExistingBackend(ctx) {
151
215
  }
152
216
  throw err;
153
217
  }
154
- for (const apiFunction of gcfV2Results.functions) {
155
- const endpoint = gcfV2.endpointFromFunction(apiFunction);
156
- ctx.existingBackend.endpoints[endpoint.region] =
157
- ctx.existingBackend.endpoints[endpoint.region] || {};
158
- ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
159
- }
160
- ctx.unreachableRegions.gcfV2 = gcfV2Results.unreachable;
161
218
  }
162
219
  async function checkAvailability(context, want) {
163
220
  const ctx = context;
@@ -1,11 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveBackend = void 0;
3
+ exports.resolveBackend = exports.of = exports.empty = void 0;
4
4
  const backend = require("./backend");
5
5
  const proto = require("../../gcp/proto");
6
6
  const api = require("../../.../../api");
7
7
  const error_1 = require("../../error");
8
8
  const functional_1 = require("../../functional");
9
+ function empty() {
10
+ return {
11
+ requiredAPIs: [],
12
+ endpoints: {},
13
+ params: [],
14
+ };
15
+ }
16
+ exports.empty = empty;
17
+ function of(endpoints) {
18
+ const build = empty();
19
+ build.endpoints = endpoints;
20
+ return build;
21
+ }
22
+ exports.of = of;
9
23
  function resolveInt(from) {
10
24
  if (from == null) {
11
25
  return 0;
@@ -36,7 +50,15 @@ function resolveBoolean(from) {
36
50
  function isMemoryOption(value) {
37
51
  return value == null || [128, 256, 512, 1024, 2048, 4096, 8192].includes(value);
38
52
  }
39
- function resolveBackend(build) {
53
+ function resolveBackend(build, userEnvs) {
54
+ for (const param of build.params) {
55
+ const expectedEnv = param.param;
56
+ if (!userEnvs.hasOwnProperty(expectedEnv)) {
57
+ throw new error_1.FirebaseError("Build specified parameter " +
58
+ expectedEnv +
59
+ " but it was not present in the user dotenv files");
60
+ }
61
+ }
40
62
  const bkEndpoints = [];
41
63
  for (const endpointId of Object.keys(build.endpoints)) {
42
64
  const endpoint = build.endpoints[endpointId];
@@ -59,23 +81,20 @@ function resolveBackend(build) {
59
81
  else {
60
82
  timeout = 60;
61
83
  }
62
- const bkEndpoint = Object.assign({ id: endpointId, project: "", region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: "", labels: endpoint.labels, environmentVariables: endpoint.environmentVariables, secretEnvironmentVariables: undefined, availableMemoryMb: endpoint.availableMemoryMb, timeoutSeconds: timeout }, trigger);
84
+ const bkEndpoint = Object.assign({ id: endpointId, project: endpoint.project, region: region, entryPoint: endpoint.entryPoint, platform: endpoint.platform, runtime: endpoint.runtime, timeoutSeconds: timeout }, trigger);
63
85
  proto.renameIfPresent(bkEndpoint, endpoint, "maxInstances", "maxInstances", resolveInt);
64
86
  proto.renameIfPresent(bkEndpoint, endpoint, "minInstances", "minInstances", resolveInt);
65
87
  proto.renameIfPresent(bkEndpoint, endpoint, "concurrency", "concurrency", resolveInt);
66
- proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings");
88
+ proto.copyIfPresent(bkEndpoint, endpoint, "ingressSettings", "availableMemoryMb", "environmentVariables", "labels");
67
89
  if (endpoint.vpc) {
68
90
  bkEndpoint.vpc = {
69
- connector: resolveString(endpoint.vpc.connector),
70
- egressSettings: endpoint.vpc.egressSettings,
91
+ connector: resolveString(endpoint.vpc.connector).replace("$REGION", region),
71
92
  };
93
+ proto.copyIfPresent(bkEndpoint.vpc, endpoint.vpc, "egressSettings");
72
94
  }
73
95
  if (endpoint.serviceAccount) {
74
96
  bkEndpoint.serviceAccountEmail = endpoint.serviceAccount;
75
97
  }
76
- else {
77
- bkEndpoint.serviceAccountEmail = "default";
78
- }
79
98
  bkEndpoints.push(bkEndpoint);
80
99
  }
81
100
  }