firebase-tools 10.6.0 → 10.7.2

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 (97) 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/hosting-channel-deploy.js +2 -2
  10. package/lib/deploy/database/deploy.js +4 -0
  11. package/lib/deploy/database/index.js +1 -0
  12. package/lib/deploy/extensions/deploy.js +4 -4
  13. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  14. package/lib/deploy/extensions/planner.js +36 -9
  15. package/lib/deploy/extensions/prepare.js +1 -1
  16. package/lib/deploy/extensions/secrets.js +2 -2
  17. package/lib/deploy/extensions/tasks.js +60 -21
  18. package/lib/deploy/functions/backend.js +37 -2
  19. package/lib/deploy/functions/build.js +173 -0
  20. package/lib/deploy/functions/checkIam.js +11 -14
  21. package/lib/deploy/functions/containerCleaner.js +8 -7
  22. package/lib/deploy/functions/deploy.js +49 -28
  23. package/lib/deploy/functions/ensure.js +4 -4
  24. package/lib/deploy/functions/functionsDeployHelper.js +99 -24
  25. package/lib/deploy/functions/prepare.js +129 -71
  26. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  27. package/lib/deploy/functions/pricing.js +6 -3
  28. package/lib/deploy/functions/prompts.js +1 -7
  29. package/lib/deploy/functions/release/executor.js +1 -1
  30. package/lib/deploy/functions/release/fabricator.js +69 -25
  31. package/lib/deploy/functions/release/index.js +20 -6
  32. package/lib/deploy/functions/release/planner.js +18 -10
  33. package/lib/deploy/functions/release/reporter.js +14 -11
  34. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +50 -3
  36. package/lib/deploy/functions/runtimes/golang/index.js +3 -0
  37. package/lib/deploy/functions/runtimes/node/index.js +7 -0
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +132 -6
  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/validate.js +33 -7
  44. package/lib/deploy/hosting/args.js +2 -0
  45. package/lib/deploy/hosting/convertConfig.js +39 -8
  46. package/lib/deploy/hosting/deploy.js +3 -3
  47. package/lib/deploy/hosting/prepare.js +2 -2
  48. package/lib/deploy/hosting/release.js +6 -2
  49. package/lib/deploy/index.js +82 -93
  50. package/lib/deploy/remoteconfig/deploy.js +4 -0
  51. package/lib/deploy/remoteconfig/index.js +3 -1
  52. package/lib/emulator/auth/cloudFunctions.js +6 -2
  53. package/lib/emulator/auth/operations.js +5 -1
  54. package/lib/emulator/auth/server.js +8 -1
  55. package/lib/emulator/auth/state.js +27 -24
  56. package/lib/emulator/auth/utils.js +3 -25
  57. package/lib/emulator/controller.js +17 -14
  58. package/lib/emulator/databaseEmulator.js +36 -3
  59. package/lib/emulator/downloadableEmulators.js +39 -23
  60. package/lib/emulator/extensions/validation.js +2 -2
  61. package/lib/emulator/extensionsEmulator.js +85 -21
  62. package/lib/emulator/functionsEmulator.js +89 -15
  63. package/lib/emulator/functionsEmulatorRuntime.js +1 -1
  64. package/lib/emulator/functionsEmulatorShared.js +25 -2
  65. package/lib/emulator/functionsEmulatorShell.js +2 -3
  66. package/lib/emulator/functionsEmulatorUtils.js +5 -1
  67. package/lib/emulator/pubsubEmulator.js +13 -9
  68. package/lib/emulator/registry.js +34 -12
  69. package/lib/emulator/storage/apis/firebase.js +33 -6
  70. package/lib/emulator/storage/apis/gcloud.js +6 -3
  71. package/lib/emulator/storage/files.js +9 -1
  72. package/lib/ensureApiEnabled.js +8 -4
  73. package/lib/extensions/changelog.js +1 -1
  74. package/lib/extensions/emulator/optionsHelper.js +4 -3
  75. package/lib/extensions/emulator/specHelper.js +7 -1
  76. package/lib/extensions/extensionsHelper.js +30 -24
  77. package/lib/extensions/manifest.js +27 -7
  78. package/lib/extensions/paramHelper.js +7 -5
  79. package/lib/extensions/provisioningHelper.js +2 -2
  80. package/lib/extensions/warnings.js +11 -4
  81. package/lib/functions/events/index.js +7 -0
  82. package/lib/functions/events/v1.js +6 -0
  83. package/lib/functions/projectConfig.js +32 -6
  84. package/lib/functionsShellCommandAction.js +1 -1
  85. package/lib/gcp/cloudfunctions.js +38 -5
  86. package/lib/gcp/cloudfunctionsv2.js +46 -7
  87. package/lib/gcp/identityPlatform.js +44 -0
  88. package/lib/gcp/secretManager.js +1 -1
  89. package/lib/metaprogramming.js +2 -0
  90. package/lib/previews.js +1 -1
  91. package/lib/serve/functions.js +16 -19
  92. package/lib/serve/hosting.js +25 -12
  93. package/lib/serve/index.js +6 -0
  94. package/lib/track.js +15 -21
  95. package/npm-shrinkwrap.json +256 -527
  96. package/package.json +6 -3
  97. package/schema/firebase-config.json +6 -0
@@ -7,13 +7,13 @@ const error_1 = require("../../../error");
7
7
  const utils = require("../../../utils");
8
8
  const backend = require("../backend");
9
9
  const v2events = require("../../../functions/events/v2");
10
- function calculateChangesets(want, have, keyFn, options) {
10
+ function calculateChangesets(want, have, keyFn, deleteAll) {
11
11
  const toCreate = utils.groupBy(Object.keys(want)
12
12
  .filter((id) => !have[id])
13
13
  .map((id) => want[id]), keyFn);
14
14
  const toDelete = utils.groupBy(Object.keys(have)
15
15
  .filter((id) => !want[id])
16
- .filter((id) => options.deleteAll || (0, deploymentTool_1.isFirebaseManaged)(have[id].labels || {}))
16
+ .filter((id) => deleteAll || (0, deploymentTool_1.isFirebaseManaged)(have[id].labels || {}))
17
17
  .map((id) => have[id]), keyFn);
18
18
  const toUpdate = utils.groupBy(Object.keys(want)
19
19
  .filter((id) => have[id])
@@ -48,20 +48,25 @@ function calculateUpdate(want, have) {
48
48
  return update;
49
49
  }
50
50
  exports.calculateUpdate = calculateUpdate;
51
- function createDeploymentPlan(want, have, options = {}) {
51
+ function createDeploymentPlan(args) {
52
+ let { wantBackend, haveBackend, codebase, filters, deleteAll } = args;
52
53
  let deployment = {};
53
- want = backend.matchingBackend(want, (endpoint) => {
54
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
54
+ wantBackend = backend.matchingBackend(wantBackend, (endpoint) => {
55
+ return (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
55
56
  });
56
- have = backend.matchingBackend(have, (endpoint) => {
57
- return (0, functionsDeployHelper_1.functionMatchesAnyGroup)(endpoint, options.filters || []);
57
+ const wantedEndpoint = backend.hasEndpoint(wantBackend);
58
+ haveBackend = backend.matchingBackend(haveBackend, (endpoint) => {
59
+ return wantedEndpoint(endpoint) || (0, functionsDeployHelper_1.endpointMatchesAnyFilter)(endpoint, filters);
58
60
  });
59
- const regions = new Set([...Object.keys(want.endpoints), ...Object.keys(have.endpoints)]);
61
+ const regions = new Set([
62
+ ...Object.keys(wantBackend.endpoints),
63
+ ...Object.keys(haveBackend.endpoints),
64
+ ]);
60
65
  for (const region of regions) {
61
- const changesets = calculateChangesets(want.endpoints[region] || {}, have.endpoints[region] || {}, (e) => `${e.region}-${e.availableMemoryMb || "default"}`, options);
66
+ const changesets = calculateChangesets(wantBackend.endpoints[region] || {}, haveBackend.endpoints[region] || {}, (e) => `${codebase}-${e.region}-${e.availableMemoryMb || "default"}`, deleteAll);
62
67
  deployment = Object.assign(Object.assign({}, deployment), changesets);
63
68
  }
64
- if (upgradedToGCFv2WithoutSettingConcurrency(want, have)) {
69
+ if (upgradedToGCFv2WithoutSettingConcurrency(wantBackend, haveBackend)) {
65
70
  utils.logLabeledBullet("functions", "You are updating one or more functions to Google Cloud Functions v2, " +
66
71
  "which introduces support for concurrent execution. New functions " +
67
72
  "default to 80 concurrent executions, but existing functions keep the " +
@@ -157,6 +162,9 @@ function checkForIllegalUpdate(want, have) {
157
162
  else if (backend.isTaskQueueTriggered(e)) {
158
163
  return "a task queue";
159
164
  }
165
+ else if (backend.isBlockingTriggered(e)) {
166
+ return e.blockingTrigger.eventType;
167
+ }
160
168
  throw Error("Functions release planner is not able to handle an unknown trigger type");
161
169
  };
162
170
  const wantType = triggerType(want);
@@ -4,7 +4,7 @@ exports.triggerTag = exports.printAbortedErrors = exports.printErrors = exports.
4
4
  const backend = require("../backend");
5
5
  const clc = require("cli-color");
6
6
  const logger_1 = require("../../../logger");
7
- const track = require("../../../track");
7
+ const track_1 = require("../../../track");
8
8
  const utils = require("../../../utils");
9
9
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
10
10
  class DeploymentError extends Error {
@@ -36,23 +36,23 @@ async function logAndTrackDeployStats(summary) {
36
36
  totalTime += result.durationMs;
37
37
  if (!result.error) {
38
38
  totalSuccesses++;
39
- reports.push(track.track("function_deploy_success", tag, result.durationMs));
39
+ reports.push((0, track_1.track)("function_deploy_success", tag, result.durationMs));
40
40
  }
41
41
  else if (result.error instanceof AbortedDeploymentError) {
42
42
  totalAborts++;
43
- reports.push(track.track("function_deploy_abort", tag, result.durationMs));
43
+ reports.push((0, track_1.track)("function_deploy_abort", tag, result.durationMs));
44
44
  }
45
45
  else {
46
46
  totalErrors++;
47
- reports.push(track.track("function_deploy_failure", tag, result.durationMs));
47
+ reports.push((0, track_1.track)("function_deploy_failure", tag, result.durationMs));
48
48
  }
49
49
  }
50
50
  const regionCountTag = regions.size < 5 ? regions.size.toString() : ">=5";
51
- reports.push(track.track("functions_region_count", regionCountTag, 1));
51
+ reports.push((0, track_1.track)("functions_region_count", regionCountTag, 1));
52
52
  const gcfv1 = summary.results.find((r) => r.endpoint.platform === "gcfv1");
53
53
  const gcfv2 = summary.results.find((r) => r.endpoint.platform === "gcfv2");
54
54
  const tag = gcfv1 && gcfv2 ? "v1+v2" : gcfv1 ? "v1" : "v2";
55
- reports.push(track.track("functions_codebase_deploy", tag, summary.results.length));
55
+ reports.push((0, track_1.track)("functions_codebase_deploy", tag, summary.results.length));
56
56
  const avgTime = totalTime / (totalSuccesses + totalErrors);
57
57
  logger_1.logger.debug(`Total Function Deployment time: ${summary.totalTime}`);
58
58
  logger_1.logger.debug(`${totalErrors + totalSuccesses + totalAborts} Functions Deployed`);
@@ -61,15 +61,15 @@ async function logAndTrackDeployStats(summary) {
61
61
  logger_1.logger.debug(`Average Function Deployment time: ${avgTime}`);
62
62
  if (totalErrors + totalSuccesses > 0) {
63
63
  if (totalErrors === 0) {
64
- reports.push(track.track("functions_deploy_result", "success", totalSuccesses));
64
+ reports.push((0, track_1.track)("functions_deploy_result", "success", totalSuccesses));
65
65
  }
66
66
  else if (totalSuccesses > 0) {
67
- reports.push(track.track("functions_deploy_result", "partial_success", totalSuccesses));
68
- reports.push(track.track("functions_deploy_result", "partial_failure", totalErrors));
69
- reports.push(track.track("functions_deploy_result", "partial_error_ratio", totalErrors / (totalSuccesses + totalErrors)));
67
+ reports.push((0, track_1.track)("functions_deploy_result", "partial_success", totalSuccesses));
68
+ reports.push((0, track_1.track)("functions_deploy_result", "partial_failure", totalErrors));
69
+ reports.push((0, track_1.track)("functions_deploy_result", "partial_error_ratio", totalErrors / (totalSuccesses + totalErrors)));
70
70
  }
71
71
  else {
72
- reports.push(track.track("functions_deploy_result", "failure", totalErrors));
72
+ reports.push((0, track_1.track)("functions_deploy_result", "failure", totalErrors));
73
73
  }
74
74
  }
75
75
  await utils.allSettled(reports);
@@ -171,6 +171,9 @@ function triggerTag(endpoint) {
171
171
  }
172
172
  return `${prefix}.https`;
173
173
  }
174
+ if (backend.isBlockingTriggered(endpoint)) {
175
+ return `${prefix}.blocking`;
176
+ }
174
177
  return endpoint.eventTrigger.eventType;
175
178
  }
176
179
  exports.triggerTag = triggerTag;
@@ -19,31 +19,37 @@ function assertKeyTypes(prefix, yaml, schema) {
19
19
  }
20
20
  for (const [keyAsString, value] of Object.entries(yaml)) {
21
21
  const key = keyAsString;
22
- const fullKey = prefix ? prefix + "." + key : key;
22
+ const fullKey = prefix ? `${prefix}.${keyAsString}` : keyAsString;
23
23
  if (!schema[key] || schema[key] === "omit") {
24
24
  throw new error_1.FirebaseError(`Unexpected key ${fullKey}. You may need to install a newer version of the Firebase CLI.`);
25
25
  }
26
- if (schema[key] === "string") {
26
+ const schemaType = schema[key];
27
+ if (typeof schemaType === "function") {
28
+ if (!schemaType(value)) {
29
+ throw new error_1.FirebaseError(`${Array.isArray(value) ? "array" : typeof value} ${fullKey} failed validation`);
30
+ }
31
+ }
32
+ else if (schemaType === "string") {
27
33
  if (typeof value !== "string") {
28
34
  throw new error_1.FirebaseError(`Expected ${fullKey} to be string; was ${typeof value}`);
29
35
  }
30
36
  }
31
- else if (schema[key] === "number") {
37
+ else if (schemaType === "number") {
32
38
  if (typeof value !== "number") {
33
39
  throw new error_1.FirebaseError(`Expected ${fullKey} to be a number; was ${typeof value}`);
34
40
  }
35
41
  }
36
- else if (schema[key] === "boolean") {
42
+ else if (schemaType === "boolean") {
37
43
  if (typeof value !== "boolean") {
38
44
  throw new error_1.FirebaseError(`Expected ${fullKey} to be a boolean; was ${typeof value}`);
39
45
  }
40
46
  }
41
- else if (schema[key] === "array") {
47
+ else if (schemaType === "array") {
42
48
  if (!Array.isArray(value)) {
43
49
  throw new error_1.FirebaseError(`Expected ${fullKey} to be an array; was ${typeof value}`);
44
50
  }
45
51
  }
46
- else if (schema[key] === "object") {
52
+ else if (schemaType === "object") {
47
53
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
48
54
  throw new error_1.FirebaseError(`Expected ${fullKey} to be an object; was ${typeof value}`);
49
55
  }
@@ -5,6 +5,12 @@ const backend = require("../../backend");
5
5
  const proto_1 = require("../../../../gcp/proto");
6
6
  const parsing_1 = require("./parsing");
7
7
  const error_1 = require("../../../../error");
8
+ const CHANNEL_NAME_REGEX = new RegExp("(projects\\/" +
9
+ "(?<project>(?:\\d+)|(?:[A-Za-z]+[A-Za-z\\d-]*[A-Za-z\\d]?))\\/)?" +
10
+ "locations\\/" +
11
+ "(?<location>[A-Za-z\\d\\-_]+)\\/" +
12
+ "channels\\/" +
13
+ "(?<channel>[A-Za-z\\d\\-_]+)");
8
14
  function backendFromV1Alpha1(yaml, project, region, runtime) {
9
15
  const manifest = JSON.parse(JSON.stringify(yaml));
10
16
  const bkend = backend.empty();
@@ -42,9 +48,9 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
42
48
  const ep = manifest.endpoints[id];
43
49
  (0, parsing_1.assertKeyTypes)(prefix, ep, {
44
50
  region: "array",
45
- platform: "string",
51
+ platform: (platform) => backend.AllFunctionsPlatforms.includes(platform),
46
52
  entryPoint: "string",
47
- availableMemoryMb: "number",
53
+ availableMemoryMb: (mem) => backend.AllMemoryOptions.includes(mem),
48
54
  maxInstances: "number",
49
55
  minInstances: "number",
50
56
  concurrency: "number",
@@ -52,7 +58,7 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
52
58
  timeoutSeconds: "number",
53
59
  vpc: "object",
54
60
  labels: "object",
55
- ingressSettings: "string",
61
+ ingressSettings: (setting) => backend.AllIngressSettings.includes(setting),
56
62
  environmentVariables: "object",
57
63
  secretEnvironmentVariables: "array",
58
64
  httpsTrigger: "object",
@@ -60,7 +66,16 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
60
66
  eventTrigger: "object",
61
67
  scheduleTrigger: "object",
62
68
  taskQueueTrigger: "object",
69
+ blockingTrigger: "object",
70
+ cpu: (cpu) => typeof cpu === "number" || cpu === "gcf_gen1",
63
71
  });
72
+ if (ep.vpc) {
73
+ (0, parsing_1.assertKeyTypes)(prefix + ".vpc", ep.vpc, {
74
+ connector: "string",
75
+ egressSettings: (setting) => backend.AllVpcEgressSettings.includes(setting),
76
+ });
77
+ (0, parsing_1.requireKeys)(prefix + ".vpc", ep.vpc, "connector");
78
+ }
64
79
  let triggerCount = 0;
65
80
  if (ep.httpsTrigger) {
66
81
  triggerCount++;
@@ -77,6 +92,9 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
77
92
  if (ep.taskQueueTrigger) {
78
93
  triggerCount++;
79
94
  }
95
+ if (ep.blockingTrigger) {
96
+ triggerCount++;
97
+ }
80
98
  if (!triggerCount) {
81
99
  throw new error_1.FirebaseError("Expected trigger in endpoint " + id);
82
100
  }
@@ -97,6 +115,7 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
97
115
  channel: "string",
98
116
  });
99
117
  triggered = { eventTrigger: ep.eventTrigger };
118
+ (0, proto_1.renameIfPresent)(triggered.eventTrigger, ep.eventTrigger, "channel", "channel", (c) => resolveChannelName(project, c, defaultRegion));
100
119
  for (const [k, v] of Object.entries(triggered.eventTrigger.eventFilters)) {
101
120
  if (k === "topic" && !v.startsWith("projects/")) {
102
121
  triggered.eventTrigger.eventFilters[k] = `projects/${project}/topics/${v}`;
@@ -151,6 +170,14 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
151
170
  }
152
171
  triggered = { taskQueueTrigger: ep.taskQueueTrigger };
153
172
  }
173
+ else if (backend.isBlockingTriggered(ep)) {
174
+ (0, parsing_1.requireKeys)(prefix + ".blockingTrigger", ep.blockingTrigger, "eventType");
175
+ (0, parsing_1.assertKeyTypes)(prefix + ".blockingTrigger", ep.blockingTrigger, {
176
+ eventType: "string",
177
+ options: "object",
178
+ });
179
+ triggered = { blockingTrigger: ep.blockingTrigger };
180
+ }
154
181
  else {
155
182
  throw new error_1.FirebaseError(`Do not recognize trigger type for endpoint ${id}. Try upgrading ` +
156
183
  "firebase-tools with npm install -g firebase-tools@latest");
@@ -165,3 +192,23 @@ function parseEndpoints(manifest, id, project, defaultRegion, runtime) {
165
192
  }
166
193
  return allParsed;
167
194
  }
195
+ function resolveChannelName(projectId, channel, defaultRegion) {
196
+ if (!channel.includes("/")) {
197
+ const location = defaultRegion;
198
+ const channelId = channel;
199
+ return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
200
+ }
201
+ const match = CHANNEL_NAME_REGEX.exec(channel);
202
+ if (!(match === null || match === void 0 ? void 0 : match.groups)) {
203
+ throw new error_1.FirebaseError("Invalid channel name format.");
204
+ }
205
+ const matchedProjectId = match.groups.project;
206
+ const location = match.groups.location;
207
+ const channelId = match.groups.channel;
208
+ if (matchedProjectId) {
209
+ return "projects/" + matchedProjectId + "/locations/" + location + "/channels/" + channelId;
210
+ }
211
+ else {
212
+ return "projects/" + projectId + "/locations/" + location + "/channels/" + channelId;
213
+ }
214
+ }
@@ -105,6 +105,9 @@ class Delegate {
105
105
  return p;
106
106
  });
107
107
  }
108
+ async discoverBuild(configValues, envs) {
109
+ return { requiredAPIs: [], endpoints: {}, params: [] };
110
+ }
108
111
  async discoverSpec(configValues, envs) {
109
112
  let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
110
113
  if (!discovered) {
@@ -84,6 +84,10 @@ class Delegate {
84
84
  }
85
85
  async discoverSpec(config, env) {
86
86
  if (previews_1.previews.functionsv2) {
87
+ if (!semver.valid(this.sdkVersion)) {
88
+ logger_1.logger.debug(`Could not parse firebase-functions version '${this.sdkVersion}' into semver. Falling back to parseTriggers.`);
89
+ return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
90
+ }
87
91
  if (semver.lt(this.sdkVersion, MIN_FUNCTIONS_SDK_VERSION)) {
88
92
  (0, utils_1.logLabeledWarning)("functions", `You are using an old version of firebase-functions SDK (${this.sdkVersion}). ` +
89
93
  `Please update firebase-functions SDK to >=${MIN_FUNCTIONS_SDK_VERSION}`);
@@ -106,5 +110,8 @@ class Delegate {
106
110
  }
107
111
  return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
108
112
  }
113
+ async discoverBuild(config, env) {
114
+ return parseTriggers.discoverBuild(this.projectId, this.sourceDir, this.runtime, config, env);
115
+ }
109
116
  }
110
117
  exports.Delegate = Delegate;
@@ -4,7 +4,7 @@ exports.getRuntimeChoice = exports.DEPRECATED_NODE_VERSION_INFO = exports.UNSUPP
4
4
  const path = require("path");
5
5
  const clc = require("cli-color");
6
6
  const error_1 = require("../../../../error");
7
- const track = require("../../../../track");
7
+ const track_1 = require("../../../../track");
8
8
  const runtimes = require("../../runtimes");
9
9
  const cjson = require("cjson");
10
10
  const ENGINE_RUNTIMES = {
@@ -46,11 +46,11 @@ function getRuntimeChoice(sourceDir, runtimeFromConfig) {
46
46
  ? exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG
47
47
  : exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + exports.DEPRECATED_NODE_VERSION_INFO;
48
48
  if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) {
49
- void track("functions_runtime_notices", "package_missing_runtime");
49
+ void (0, track_1.track)("functions_runtime_notices", "package_missing_runtime");
50
50
  throw new error_1.FirebaseError(errorMessage, { exit: 1 });
51
51
  }
52
52
  if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) {
53
- void track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
53
+ void (0, track_1.track)("functions_runtime_notices", `${runtime}_deploy_prohibited`);
54
54
  throw new error_1.FirebaseError(errorMessage, { exit: 1 });
55
55
  }
56
56
  return runtime;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addResourcesToBackend = exports.mergeRequiredAPIs = exports.discoverBackend = exports.useStrategy = void 0;
3
+ exports.addResourcesToBackend = exports.addResourcesToBuild = exports.mergeRequiredAPIs = exports.discoverBackend = exports.discoverBuild = exports.useStrategy = void 0;
4
4
  const path = require("path");
5
5
  const _ = require("lodash");
6
6
  const child_process_1 = require("child_process");
@@ -9,7 +9,7 @@ const logger_1 = require("../../../../logger");
9
9
  const backend = require("../../backend");
10
10
  const api = require("../../../../api");
11
11
  const proto = require("../../../../gcp/proto");
12
- const v2events = require("../../../../functions/events/v2");
12
+ const events = require("../../../../functions/events");
13
13
  const TRIGGER_PARSER = path.resolve(__dirname, "./triggerParser.js");
14
14
  function removeInspectOptions(options) {
15
15
  return options.filter((opt) => !opt.startsWith("--inspect"));
@@ -49,6 +49,19 @@ function useStrategy(context) {
49
49
  return Promise.resolve(true);
50
50
  }
51
51
  exports.useStrategy = useStrategy;
52
+ async function discoverBuild(projectId, sourceDir, runtime, configValues, envs) {
53
+ const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
54
+ const want = {
55
+ requiredAPIs: [],
56
+ endpoints: {},
57
+ params: [],
58
+ };
59
+ for (const annotation of triggerAnnotations) {
60
+ addResourcesToBuild(projectId, runtime, annotation, want);
61
+ }
62
+ return want;
63
+ }
64
+ exports.discoverBuild = discoverBuild;
52
65
  async function discoverBackend(projectId, sourceDir, runtime, configValues, envs) {
53
66
  const triggerAnnotations = await parseTriggers(projectId, sourceDir, configValues, envs);
54
67
  const want = Object.assign(Object.assign({}, backend.empty()), { environmentVariables: envs });
@@ -62,7 +75,9 @@ function mergeRequiredAPIs(backend) {
62
75
  const apiToReasons = {};
63
76
  for (const { api, reason } of backend.requiredAPIs) {
64
77
  const reasons = apiToReasons[api] || new Set();
65
- reasons.add(reason);
78
+ if (reason) {
79
+ reasons.add(reason);
80
+ }
66
81
  apiToReasons[api] = reasons;
67
82
  }
68
83
  const merged = [];
@@ -72,12 +87,109 @@ function mergeRequiredAPIs(backend) {
72
87
  backend.requiredAPIs = merged;
73
88
  }
74
89
  exports.mergeRequiredAPIs = mergeRequiredAPIs;
90
+ function addResourcesToBuild(projectId, runtime, annotation, want) {
91
+ var _a;
92
+ Object.freeze(annotation);
93
+ const regions = annotation.regions || [api.functionsDefaultRegion];
94
+ let triggered;
95
+ const triggerCount = +!!annotation.httpsTrigger +
96
+ +!!annotation.eventTrigger +
97
+ +!!annotation.taskQueueTrigger +
98
+ +!!annotation.blockingTrigger;
99
+ if (triggerCount !== 1) {
100
+ throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
101
+ }
102
+ if (annotation.taskQueueTrigger) {
103
+ want.requiredAPIs.push({
104
+ api: "cloudtasks.googleapis.com",
105
+ reason: "Needed for task queue functions.",
106
+ });
107
+ triggered = {
108
+ taskQueueTrigger: {},
109
+ };
110
+ proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "invoker");
111
+ proto.copyIfPresent(triggered.taskQueueTrigger, annotation.taskQueueTrigger, "rateLimits");
112
+ if (annotation.taskQueueTrigger.retryConfig) {
113
+ triggered.taskQueueTrigger.retryConfig = Object.assign(annotation.taskQueueTrigger.retryConfig, {
114
+ maxRetryDurationSeconds: proto.secondsFromDuration(annotation.taskQueueTrigger.retryConfig.maxRetryDuration || "0"),
115
+ });
116
+ }
117
+ }
118
+ else if (annotation.httpsTrigger) {
119
+ if ((_a = annotation.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]) {
120
+ delete annotation.labels["deployment-callable"];
121
+ triggered = { callableTrigger: {} };
122
+ }
123
+ else {
124
+ const trigger = {};
125
+ if (annotation.failurePolicy) {
126
+ logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
127
+ }
128
+ if (annotation.httpsTrigger.invoker) {
129
+ trigger.invoker = annotation.httpsTrigger.invoker[0];
130
+ }
131
+ triggered = { httpsTrigger: trigger };
132
+ }
133
+ }
134
+ else if (annotation.schedule) {
135
+ want.requiredAPIs.push({
136
+ api: "cloudscheduler.googleapis.com",
137
+ reason: "Needed for scheduled functions.",
138
+ });
139
+ triggered = {
140
+ scheduleTrigger: {
141
+ schedule: annotation.schedule.schedule,
142
+ timeZone: annotation.schedule.timeZone || "what's the default timezone?",
143
+ retryConfig: annotation.schedule.retryConfig || {},
144
+ },
145
+ };
146
+ }
147
+ else if (annotation.blockingTrigger) {
148
+ if (events.v1.AUTH_BLOCKING_EVENTS.includes(annotation.blockingTrigger.eventType)) {
149
+ want.requiredAPIs.push({
150
+ api: "identitytoolkit.googleapis.com",
151
+ reason: "Needed for auth blocking functions.",
152
+ });
153
+ }
154
+ triggered = {
155
+ blockingTrigger: {
156
+ eventType: annotation.blockingTrigger.eventType,
157
+ },
158
+ };
159
+ }
160
+ else {
161
+ triggered = {
162
+ eventTrigger: {
163
+ eventType: annotation.eventTrigger.eventType,
164
+ eventFilters: { resource: annotation.eventTrigger.resource },
165
+ retry: !!annotation.failurePolicy,
166
+ },
167
+ };
168
+ }
169
+ const endpointId = annotation.name;
170
+ const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", region: regions, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime, serviceAccount: annotation.serviceAccountEmail || "default" }, triggered);
171
+ if (annotation.vpcConnector != null) {
172
+ let maybeId = annotation.vpcConnector;
173
+ if (maybeId && !maybeId.includes("/")) {
174
+ maybeId = `projects/${projectId}/locations/$REGION/connectors/${maybeId}`;
175
+ }
176
+ endpoint.vpc = { connector: maybeId };
177
+ proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
178
+ }
179
+ proto.copyIfPresent(endpoint, annotation, "concurrency", "labels", "ingressSettings", "maxInstances", "minInstances", "availableMemoryMb");
180
+ proto.renameIfPresent(endpoint, annotation, "timeoutSeconds", "timeout", proto.secondsFromDuration);
181
+ want.endpoints[endpointId] = endpoint;
182
+ }
183
+ exports.addResourcesToBuild = addResourcesToBuild;
75
184
  function addResourcesToBackend(projectId, runtime, annotation, want) {
76
185
  var _a;
77
186
  Object.freeze(annotation);
78
187
  for (const region of annotation.regions || [api.functionsDefaultRegion]) {
79
188
  let triggered;
80
- const triggerCount = +!!annotation.httpsTrigger + +!!annotation.eventTrigger + +!!annotation.taskQueueTrigger;
189
+ const triggerCount = +!!annotation.httpsTrigger +
190
+ +!!annotation.eventTrigger +
191
+ +!!annotation.taskQueueTrigger +
192
+ +!!annotation.blockingTrigger;
81
193
  if (triggerCount !== 1) {
82
194
  throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
83
195
  }
@@ -109,6 +221,20 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
109
221
  });
110
222
  triggered = { scheduleTrigger: annotation.schedule };
111
223
  }
224
+ else if (annotation.blockingTrigger) {
225
+ if (events.v1.AUTH_BLOCKING_EVENTS.includes(annotation.blockingTrigger.eventType)) {
226
+ want.requiredAPIs.push({
227
+ api: "identitytoolkit.googleapis.com",
228
+ reason: "Needed for auth blocking functions.",
229
+ });
230
+ }
231
+ triggered = {
232
+ blockingTrigger: {
233
+ eventType: annotation.blockingTrigger.eventType,
234
+ options: annotation.blockingTrigger.options,
235
+ },
236
+ };
237
+ }
112
238
  else {
113
239
  triggered = {
114
240
  eventTrigger: {
@@ -118,10 +244,10 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
118
244
  },
119
245
  };
120
246
  if (annotation.platform === "gcfv2") {
121
- if (annotation.eventTrigger.eventType === v2events.PUBSUB_PUBLISH_EVENT) {
247
+ if (annotation.eventTrigger.eventType === events.v2.PUBSUB_PUBLISH_EVENT) {
122
248
  triggered.eventTrigger.eventFilters = { topic: annotation.eventTrigger.resource };
123
249
  }
124
- if (v2events.STORAGE_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
250
+ if (events.v2.STORAGE_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
125
251
  triggered.eventTrigger.eventFilters = { bucket: annotation.eventTrigger.resource };
126
252
  }
127
253
  }
@@ -7,7 +7,7 @@ const semver = require("semver");
7
7
  const spawn = require("cross-spawn");
8
8
  const utils = require("../../../../utils");
9
9
  const logger_1 = require("../../../../logger");
10
- const track = require("../../../../track");
10
+ const track_1 = require("../../../../track");
11
11
  const MIN_SDK_VERSION = "2.0.0";
12
12
  exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold.yellow("functions: ") +
13
13
  "You must have a " +
@@ -52,7 +52,7 @@ exports.getLatestSDKVersion = getLatestSDKVersion;
52
52
  function checkFunctionsSDKVersion(currentVersion) {
53
53
  try {
54
54
  if (semver.lt(currentVersion, MIN_SDK_VERSION)) {
55
- void track("functions_runtime_notices", "functions_sdk_too_old");
55
+ void (0, track_1.track)("functions_runtime_notices", "functions_sdk_too_old");
56
56
  utils.logWarning(exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING);
57
57
  }
58
58
  const latest = exports.getLatestSDKVersion();
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthBlockingService = void 0;
4
+ const backend = require("../backend");
5
+ const identityPlatform = require("../../../gcp/identityPlatform");
6
+ const events = require("../../../functions/events");
7
+ const error_1 = require("../../../error");
8
+ const utils_1 = require("../../../utils");
9
+ const index_1 = require("./index");
10
+ class AuthBlockingService {
11
+ constructor() {
12
+ this.name = "authblocking";
13
+ this.api = "identitytoolkit.googleapis.com";
14
+ this.triggerQueue = Promise.resolve();
15
+ this.ensureTriggerRegion = index_1.noop;
16
+ }
17
+ validateTrigger(endpoint, wantBackend) {
18
+ if (!backend.isBlockingTriggered(endpoint)) {
19
+ return;
20
+ }
21
+ const blockingEndpoints = backend
22
+ .allEndpoints(wantBackend)
23
+ .filter((ep) => backend.isBlockingTriggered(ep));
24
+ if (blockingEndpoints.find((ep) => ep.blockingTrigger.eventType === endpoint.blockingTrigger.eventType &&
25
+ ep.id !== endpoint.id)) {
26
+ throw new error_1.FirebaseError(`Can only create at most one Auth Blocking Trigger for ${endpoint.blockingTrigger.eventType} events`);
27
+ }
28
+ }
29
+ configChanged(newConfig, config) {
30
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
31
+ if (((_b = (_a = newConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) !==
32
+ ((_d = (_c = config.triggers) === null || _c === void 0 ? void 0 : _c.beforeCreate) === null || _d === void 0 ? void 0 : _d.functionUri) ||
33
+ ((_f = (_e = newConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeSignIn) === null || _f === void 0 ? void 0 : _f.functionUri) !== ((_h = (_g = config.triggers) === null || _g === void 0 ? void 0 : _g.beforeSignIn) === null || _h === void 0 ? void 0 : _h.functionUri)) {
34
+ return true;
35
+ }
36
+ if (!!((_j = newConfig.forwardInboundCredentials) === null || _j === void 0 ? void 0 : _j.accessToken) !==
37
+ !!((_k = config.forwardInboundCredentials) === null || _k === void 0 ? void 0 : _k.accessToken) ||
38
+ !!((_l = newConfig.forwardInboundCredentials) === null || _l === void 0 ? void 0 : _l.idToken) !==
39
+ !!((_m = config.forwardInboundCredentials) === null || _m === void 0 ? void 0 : _m.idToken) ||
40
+ !!((_o = newConfig.forwardInboundCredentials) === null || _o === void 0 ? void 0 : _o.refreshToken) !==
41
+ !!((_p = config.forwardInboundCredentials) === null || _p === void 0 ? void 0 : _p.refreshToken)) {
42
+ return true;
43
+ }
44
+ return false;
45
+ }
46
+ async registerTriggerLocked(endpoint) {
47
+ const newBlockingConfig = await identityPlatform.getBlockingFunctionsConfig(endpoint.project);
48
+ const oldBlockingConfig = (0, utils_1.cloneDeep)(newBlockingConfig);
49
+ if (endpoint.blockingTrigger.eventType === events.v1.BEFORE_CREATE_EVENT) {
50
+ newBlockingConfig.triggers = Object.assign(Object.assign({}, newBlockingConfig.triggers), { beforeCreate: {
51
+ functionUri: endpoint.uri,
52
+ } });
53
+ }
54
+ else {
55
+ newBlockingConfig.triggers = Object.assign(Object.assign({}, newBlockingConfig.triggers), { beforeSignIn: {
56
+ functionUri: endpoint.uri,
57
+ } });
58
+ }
59
+ newBlockingConfig.forwardInboundCredentials = Object.assign(Object.assign({}, oldBlockingConfig.forwardInboundCredentials), endpoint.blockingTrigger.options);
60
+ if (!this.configChanged(newBlockingConfig, oldBlockingConfig)) {
61
+ return;
62
+ }
63
+ await identityPlatform.setBlockingFunctionsConfig(endpoint.project, newBlockingConfig);
64
+ }
65
+ registerTrigger(ep) {
66
+ if (!backend.isBlockingTriggered(ep)) {
67
+ return Promise.resolve();
68
+ }
69
+ this.triggerQueue = this.triggerQueue.then(() => this.registerTriggerLocked(ep));
70
+ return this.triggerQueue;
71
+ }
72
+ async unregisterTriggerLocked(endpoint) {
73
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
74
+ const blockingConfig = await identityPlatform.getBlockingFunctionsConfig(endpoint.project);
75
+ if (endpoint.uri !== ((_b = (_a = blockingConfig.triggers) === null || _a === void 0 ? void 0 : _a.beforeCreate) === null || _b === void 0 ? void 0 : _b.functionUri) &&
76
+ endpoint.uri !== ((_d = (_c = blockingConfig.triggers) === null || _c === void 0 ? void 0 : _c.beforeSignIn) === null || _d === void 0 ? void 0 : _d.functionUri)) {
77
+ return;
78
+ }
79
+ if (endpoint.uri === ((_f = (_e = blockingConfig.triggers) === null || _e === void 0 ? void 0 : _e.beforeCreate) === null || _f === void 0 ? void 0 : _f.functionUri)) {
80
+ (_g = blockingConfig.triggers) === null || _g === void 0 ? true : delete _g.beforeCreate;
81
+ }
82
+ if (endpoint.uri === ((_j = (_h = blockingConfig.triggers) === null || _h === void 0 ? void 0 : _h.beforeSignIn) === null || _j === void 0 ? void 0 : _j.functionUri)) {
83
+ (_k = blockingConfig.triggers) === null || _k === void 0 ? true : delete _k.beforeSignIn;
84
+ }
85
+ await identityPlatform.setBlockingFunctionsConfig(endpoint.project, blockingConfig);
86
+ }
87
+ unregisterTrigger(ep) {
88
+ if (!backend.isBlockingTriggered(ep)) {
89
+ return Promise.resolve();
90
+ }
91
+ this.triggerQueue = this.triggerQueue.then(() => this.unregisterTriggerLocked(ep));
92
+ return this.triggerQueue;
93
+ }
94
+ }
95
+ exports.AuthBlockingService = AuthBlockingService;