firebase-tools 10.3.1 → 10.4.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 (63) hide show
  1. package/lib/accountExporter.js +95 -84
  2. package/lib/commands/deploy.js +1 -1
  3. package/lib/commands/experimental-functions-shell.js +1 -1
  4. package/lib/commands/ext-configure.js +7 -6
  5. package/lib/commands/ext-export.js +7 -1
  6. package/lib/commands/ext-install.js +7 -6
  7. package/lib/commands/ext-update.js +4 -3
  8. package/lib/commands/functions-config-export.js +5 -3
  9. package/lib/commands/functions-shell.js +1 -1
  10. package/lib/commands/hosting-channel-create.js +2 -2
  11. package/lib/commands/hosting-channel-delete.js +2 -2
  12. package/lib/commands/hosting-channel-deploy.js +2 -2
  13. package/lib/commands/hosting-channel-list.js +2 -2
  14. package/lib/commands/hosting-channel-open.js +2 -2
  15. package/lib/commands/hosting-sites-delete.js +2 -2
  16. package/lib/commands/serve.js +1 -1
  17. package/lib/commands/target-apply.js +2 -2
  18. package/lib/commands/target-clear.js +2 -2
  19. package/lib/commands/target-remove.js +2 -2
  20. package/lib/commands/target.js +2 -2
  21. package/lib/config.js +14 -4
  22. package/lib/deploy/extensions/planner.js +9 -3
  23. package/lib/deploy/functions/checkIam.js +44 -1
  24. package/lib/deploy/functions/deploy.js +3 -7
  25. package/lib/deploy/functions/prepare.js +7 -5
  26. package/lib/deploy/functions/prepareFunctionsUpload.js +7 -13
  27. package/lib/deploy/functions/release/fabricator.js +13 -1
  28. package/lib/deploy/functions/release/index.js +1 -1
  29. package/lib/deploy/functions/services/firebaseAlerts.js +1 -17
  30. package/lib/deploy/functions/services/index.js +2 -1
  31. package/lib/deploy/hosting/deploy.js +10 -0
  32. package/lib/emulator/auth/apiSpec.js +37 -0
  33. package/lib/emulator/commandUtils.js +2 -2
  34. package/lib/emulator/controller.js +14 -8
  35. package/lib/emulator/downloadableEmulators.js +5 -5
  36. package/lib/emulator/extensionsEmulator.js +3 -0
  37. package/lib/emulator/functionsEmulator.js +8 -18
  38. package/lib/emulator/functionsEmulatorShared.js +31 -1
  39. package/lib/emulator/storage/apis/firebase.js +4 -6
  40. package/lib/emulator/storage/files.js +5 -5
  41. package/lib/emulator/storage/index.js +6 -9
  42. package/lib/emulator/storage/rules/config.js +6 -5
  43. package/lib/emulator/storage/rules/manager.js +49 -32
  44. package/lib/emulator/storage/rules/runtime.js +4 -0
  45. package/lib/emulator/storage/rules/utils.js +2 -2
  46. package/lib/emulator/storage/server.js +1 -1
  47. package/lib/extensions/askUserForParam.js +87 -16
  48. package/lib/extensions/extensionsHelper.js +11 -2
  49. package/lib/extensions/manifest.js +36 -4
  50. package/lib/extensions/paramHelper.js +11 -6
  51. package/lib/fsutils.js +14 -1
  52. package/lib/functions/projectConfig.js +34 -0
  53. package/lib/init/features/functions/index.js +4 -2
  54. package/lib/init/features/hosting/index.js +32 -41
  55. package/lib/init/features/index.js +22 -12
  56. package/lib/init/index.js +28 -11
  57. package/lib/requireConfig.js +11 -9
  58. package/lib/requirePermissions.js +4 -1
  59. package/lib/serve/functions.js +5 -5
  60. package/npm-shrinkwrap.json +2 -2
  61. package/package.json +1 -1
  62. package/schema/firebase-config.json +93 -36
  63. package/lib/prepareUpload.js +0 -44
@@ -2,11 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const clc = require("cli-color");
4
4
  const command_1 = require("../command");
5
- const requireConfig = require("../requireConfig");
5
+ const requireConfig_1 = require("../requireConfig");
6
6
  const utils = require("../utils");
7
7
  exports.default = new command_1.Command("target:remove <type> <resource>")
8
8
  .description("remove a resource target")
9
- .before(requireConfig)
9
+ .before(requireConfig_1.requireConfig)
10
10
  .action((type, resource, options) => {
11
11
  const name = options.rc.removeTarget(options.project, type, resource);
12
12
  if (name) {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const clc = require("cli-color");
4
4
  const command_1 = require("../command");
5
5
  const logger_1 = require("../logger");
6
- const requireConfig = require("../requireConfig");
6
+ const requireConfig_1 = require("../requireConfig");
7
7
  const utils = require("../utils");
8
8
  function logTargets(type, targets) {
9
9
  logger_1.logger.info(clc.cyan("[ " + type + " ]"));
@@ -13,7 +13,7 @@ function logTargets(type, targets) {
13
13
  }
14
14
  exports.default = new command_1.Command("target [type]")
15
15
  .description("display configured deploy targets for the current project")
16
- .before(requireConfig)
16
+ .before(requireConfig_1.requireConfig)
17
17
  .action((type, options) => {
18
18
  if (!options.project) {
19
19
  return utils.reject("No active project, cannot list deploy targets.");
package/lib/config.js CHANGED
@@ -39,10 +39,17 @@ class Config {
39
39
  _.set(this.data, target, this.materialize(target));
40
40
  }
41
41
  });
42
- if (this.projectDir &&
43
- !this.get("functions.source") &&
44
- fsutils.dirExistsSync(this.path("functions"))) {
45
- this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
42
+ if (this.projectDir && fsutils.dirExistsSync(this.path(Config.DEFAULT_FUNCTIONS_SOURCE))) {
43
+ if (Array.isArray(this.get("functions"))) {
44
+ if (!this.get("functions.[0].source")) {
45
+ this.set("functions.[0].source", Config.DEFAULT_FUNCTIONS_SOURCE);
46
+ }
47
+ }
48
+ else {
49
+ if (!this.get("functions.source")) {
50
+ this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
51
+ }
52
+ }
46
53
  }
47
54
  }
48
55
  materialize(target) {
@@ -144,6 +151,9 @@ class Config {
144
151
  fs.ensureFileSync(this.path(p));
145
152
  fs.writeFileSync(this.path(p), content, "utf8");
146
153
  }
154
+ projectFileExists(p) {
155
+ return fs.existsSync(this.path(p));
156
+ }
147
157
  deleteProjectFile(p) {
148
158
  fs.removeSync(this.path(p));
149
159
  }
@@ -81,11 +81,17 @@ async function want(args) {
81
81
  }
82
82
  exports.want = want;
83
83
  async function resolveVersion(ref) {
84
- if (!ref.version || ref.version === "latest") {
85
- return "latest";
86
- }
87
84
  const extensionRef = refs.toExtensionRef(ref);
88
85
  const versions = await extensionsApi.listExtensionVersions(extensionRef);
86
+ if (versions.length === 0) {
87
+ throw new error_1.FirebaseError(`No versions found for ${extensionRef}`);
88
+ }
89
+ if (!ref.version || ref.version === "latest") {
90
+ return versions
91
+ .map((ev) => ev.spec.version)
92
+ .sort(semver.compare)
93
+ .pop();
94
+ }
89
95
  const maxSatisfying = semver.maxSatisfying(versions.map((ev) => ev.spec.version), ref.version);
90
96
  if (!maxSatisfying) {
91
97
  throw new error_1.FirebaseError(`No version of ${extensionRef} matches requested version ${ref.version}`);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureServiceAgentRoles = exports.mergeBindings = exports.checkHttpIam = exports.checkServiceAccountIam = void 0;
3
+ exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainEventarcServiceAgentBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.obtainBinding = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_SERVICE_AGENT_ROLE = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
4
4
  const cli_color_1 = require("cli-color");
5
5
  const logger_1 = require("../../logger");
6
6
  const functionsDeployHelper_1 = require("./functionsDeployHelper");
@@ -12,6 +12,10 @@ const utils = require("../../utils");
12
12
  const resourceManager_1 = require("../../gcp/resourceManager");
13
13
  const services_1 = require("./services");
14
14
  const PERMISSION = "cloudfunctions.functions.setIamPolicy";
15
+ exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
16
+ exports.RUN_INVOKER_ROLE = "roles/run.invoker";
17
+ exports.EVENTARC_EVENT_RECEIVER_ROLE = "roles/eventarc.eventReceiver";
18
+ exports.EVENTARC_SERVICE_AGENT_ROLE = "roles/eventarc.serviceAgent";
15
19
  async function checkServiceAccountIam(projectId) {
16
20
  const saEmail = `${projectId}@appspot.gserviceaccount.com`;
17
21
  let passed = false;
@@ -67,6 +71,37 @@ function reduceEventsToServices(services, endpoint) {
67
71
  }
68
72
  return services;
69
73
  }
74
+ function obtainBinding(existingPolicy, serviceAccount, role) {
75
+ let binding = existingPolicy.bindings.find((b) => b.role === role);
76
+ if (!binding) {
77
+ binding = {
78
+ role,
79
+ members: [],
80
+ };
81
+ }
82
+ if (!binding.members.find((m) => m === serviceAccount)) {
83
+ binding.members.push(serviceAccount);
84
+ }
85
+ return binding;
86
+ }
87
+ exports.obtainBinding = obtainBinding;
88
+ function obtainPubSubServiceAgentBindings(projectNumber, existingPolicy) {
89
+ const pubsubServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
90
+ return [obtainBinding(existingPolicy, pubsubServiceAgent, exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE)];
91
+ }
92
+ exports.obtainPubSubServiceAgentBindings = obtainPubSubServiceAgentBindings;
93
+ function obtainDefaultComputeServiceAgentBindings(projectNumber, existingPolicy) {
94
+ const defaultComputeServiceAgent = `serviceAccount:${projectNumber}-compute@developer.gserviceaccount.com`;
95
+ const invokerBinding = obtainBinding(existingPolicy, defaultComputeServiceAgent, exports.RUN_INVOKER_ROLE);
96
+ const eventReceiverBinding = obtainBinding(existingPolicy, defaultComputeServiceAgent, exports.EVENTARC_EVENT_RECEIVER_ROLE);
97
+ return [invokerBinding, eventReceiverBinding];
98
+ }
99
+ exports.obtainDefaultComputeServiceAgentBindings = obtainDefaultComputeServiceAgentBindings;
100
+ function obtainEventarcServiceAgentBindings(projectNumber, existingPolicy) {
101
+ const eventarcServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-eventarc.iam.gserviceaccount.com`;
102
+ return [obtainBinding(existingPolicy, eventarcServiceAgent, exports.EVENTARC_SERVICE_AGENT_ROLE)];
103
+ }
104
+ exports.obtainEventarcServiceAgentBindings = obtainEventarcServiceAgentBindings;
70
105
  function mergeBindings(policy, allRequiredBindings) {
71
106
  for (const requiredBindings of allRequiredBindings) {
72
107
  if (requiredBindings.length === 0) {
@@ -107,6 +142,14 @@ async function ensureServiceAgentRoles(projectNumber, want, have) {
107
142
  const findRequiredBindings = [];
108
143
  newServices.forEach((service) => findRequiredBindings.push(service.requiredProjectBindings(projectNumber, policy)));
109
144
  const allRequiredBindings = await Promise.all(findRequiredBindings);
145
+ if (haveServices.length === 0) {
146
+ allRequiredBindings.push(obtainPubSubServiceAgentBindings(projectNumber, policy));
147
+ allRequiredBindings.push(obtainDefaultComputeServiceAgentBindings(projectNumber, policy));
148
+ allRequiredBindings.push(obtainEventarcServiceAgentBindings(projectNumber, policy));
149
+ }
150
+ if (!allRequiredBindings.find((bindings) => bindings.length > 0)) {
151
+ return;
152
+ }
110
153
  mergeBindings(policy, allRequiredBindings);
111
154
  try {
112
155
  await (0, resourceManager_1.setIamPolicy)(projectNumber, policy, "bindings");
@@ -9,7 +9,6 @@ const utils_1 = require("../../utils");
9
9
  const gcs = require("../../gcp/storage");
10
10
  const gcf = require("../../gcp/cloudfunctions");
11
11
  const gcfv2 = require("../../gcp/cloudfunctionsv2");
12
- const utils = require("../../utils");
13
12
  const backend = require("./backend");
14
13
  (0, tmp_1.setGracefulCleanup)();
15
14
  async function uploadSourceV1(context, region) {
@@ -33,7 +32,7 @@ async function uploadSourceV2(context, region) {
33
32
  context.storage = Object.assign(Object.assign({}, context.storage), { [region]: res.storageSource });
34
33
  }
35
34
  async function deploy(context, options, payload) {
36
- if (!options.config.src.functions) {
35
+ if (!context.config) {
37
36
  return;
38
37
  }
39
38
  if (!context.functionsSourceV1 && !context.functionsSourceV2) {
@@ -53,12 +52,9 @@ async function deploy(context, options, payload) {
53
52
  }
54
53
  }
55
54
  await Promise.all(uploads);
56
- utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
55
+ const source = context.config.source;
57
56
  if (uploads.length) {
58
- (0, utils_1.logSuccess)(clc.green.bold("functions:") +
59
- " " +
60
- clc.bold(options.config.src.functions.source) +
61
- " folder uploaded successfully");
57
+ (0, utils_1.logSuccess)(`${clc.green.bold("functions:")} ${clc.bold(source)} folder uploaded successfully`);
62
58
  }
63
59
  }
64
60
  catch (err) {
@@ -19,6 +19,7 @@ const logger_1 = require("../../logger");
19
19
  const triggerRegionHelper_1 = require("./triggerRegionHelper");
20
20
  const checkIam_1 = require("./checkIam");
21
21
  const error_1 = require("../../error");
22
+ const projectConfig_1 = require("../../functions/projectConfig");
22
23
  function hasUserConfig(config) {
23
24
  return Object.keys(config).length > 1;
24
25
  }
@@ -28,16 +29,17 @@ function hasDotenv(opts) {
28
29
  async function prepare(context, options, payload) {
29
30
  const projectId = (0, projectUtils_1.needProjectId)(options);
30
31
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
31
- const sourceDirName = options.config.get("functions.source");
32
+ context.config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
33
+ const sourceDirName = context.config.source;
32
34
  if (!sourceDirName) {
33
- throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions.source defined in firebase.json`);
35
+ throw new error_1.FirebaseError(`No functions code detected at default location (./functions), and no functions source defined in firebase.json`);
34
36
  }
35
37
  const sourceDir = options.config.path(sourceDirName);
36
38
  const delegateContext = {
37
39
  projectId,
38
40
  sourceDir,
39
41
  projectDir: options.config.projectDir,
40
- runtime: options.config.get("functions.runtime") || "",
42
+ runtime: context.config.runtime || "",
41
43
  };
42
44
  const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
43
45
  logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
@@ -95,10 +97,10 @@ async function prepare(context, options, payload) {
95
97
  " directory for uploading...");
96
98
  }
97
99
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
98
- context.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(runtimeConfig, options);
100
+ context.functionsSourceV1 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config, runtimeConfig);
99
101
  }
100
102
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
101
- context.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(undefined, options);
103
+ context.functionsSourceV2 = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, context.config);
102
104
  }
103
105
  for (const endpoint of backend.allEndpoints(wantBackend)) {
104
106
  endpoint.environmentVariables = wantBackend.environmentVariables;
@@ -48,15 +48,14 @@ async function pipeAsync(from, to) {
48
48
  from.pipe(to);
49
49
  });
50
50
  }
51
- async function packageSource(options, sourceDir, configValues) {
52
- var _a;
51
+ async function packageSource(sourceDir, config, runtimeConfig) {
53
52
  const tmpFile = tmp.fileSync({ prefix: "firebase-functions-", postfix: ".zip" }).name;
54
53
  const fileStream = fs.createWriteStream(tmpFile, {
55
54
  flags: "w",
56
55
  encoding: "binary",
57
56
  });
58
57
  const archive = archiver("zip");
59
- const ignore = ((_a = options.config.src.functions) === null || _a === void 0 ? void 0 : _a.ignore) || ["node_modules", ".git"];
58
+ const ignore = config.ignore || ["node_modules", ".git"];
60
59
  ignore.push("firebase-debug.log", "firebase-debug.*.log", CONFIG_DEST_FILE);
61
60
  try {
62
61
  const files = await fsAsync.readdirRecursive({ path: sourceDir, ignore: ignore });
@@ -66,8 +65,8 @@ async function packageSource(options, sourceDir, configValues) {
66
65
  mode: file.mode,
67
66
  });
68
67
  });
69
- if (typeof configValues !== "undefined") {
70
- archive.append(JSON.stringify(configValues, null, 2), {
68
+ if (typeof runtimeConfig !== "undefined") {
69
+ archive.append(JSON.stringify(runtimeConfig, null, 2), {
71
70
  name: CONFIG_DEST_FILE,
72
71
  mode: 420,
73
72
  });
@@ -81,20 +80,15 @@ async function packageSource(options, sourceDir, configValues) {
81
80
  exit: 1,
82
81
  });
83
82
  }
84
- utils.assertDefined(options.config.src.functions);
85
- utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
86
83
  utils.logBullet(clc.cyan.bold("functions:") +
87
84
  " packaged " +
88
- clc.bold(options.config.src.functions.source) +
85
+ clc.bold(sourceDir) +
89
86
  " (" +
90
87
  filesize(archive.pointer()) +
91
88
  ") for uploading");
92
89
  return tmpFile;
93
90
  }
94
- async function prepareFunctionsUpload(runtimeConfig, options) {
95
- utils.assertDefined(options.config.src.functions);
96
- utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
97
- const sourceDir = options.config.path(options.config.src.functions.source);
98
- return packageSource(options, sourceDir, runtimeConfig);
91
+ async function prepareFunctionsUpload(sourceDir, config, runtimeConfig) {
92
+ return packageSource(sourceDir, config, runtimeConfig);
99
93
  }
100
94
  exports.prepareFunctionsUpload = prepareFunctionsUpload;
@@ -180,6 +180,13 @@ class Fabricator {
180
180
  .catch(rethrowAs(endpoint, "set invoker"));
181
181
  }
182
182
  }
183
+ else if (backend.isCallableTriggered(endpoint)) {
184
+ await this.executor
185
+ .run(async () => {
186
+ await gcf.setInvokerCreate(endpoint.project, backend.functionName(endpoint), ["public"]);
187
+ })
188
+ .catch(rethrowAs(endpoint, "set invoker"));
189
+ }
183
190
  else if (backend.isTaskQueueTriggered(endpoint)) {
184
191
  const invoker = endpoint.taskQueueTrigger.invoker;
185
192
  if (invoker && !invoker.includes("private")) {
@@ -232,12 +239,17 @@ class Fabricator {
232
239
  .catch(rethrowAs(endpoint, "set invoker"));
233
240
  }
234
241
  }
242
+ else if (backend.isCallableTriggered(endpoint)) {
243
+ await this.executor
244
+ .run(() => run.setInvokerCreate(endpoint.project, serviceName, ["public"]))
245
+ .catch(rethrowAs(endpoint, "set invoker"));
246
+ }
235
247
  else if (backend.isTaskQueueTriggered(endpoint)) {
236
248
  const invoker = endpoint.taskQueueTrigger.invoker;
237
249
  if (invoker && !invoker.includes("private")) {
238
250
  await this.executor
239
251
  .run(async () => {
240
- await gcf.setInvokerCreate(endpoint.project, backend.functionName(endpoint), invoker);
252
+ await run.setInvokerCreate(endpoint.project, serviceName, invoker);
241
253
  })
242
254
  .catch(rethrowAs(endpoint, "set invoker"));
243
255
  }
@@ -15,7 +15,7 @@ const functionsConfig_1 = require("../../../functionsConfig");
15
15
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
16
16
  const error_1 = require("../../../error");
17
17
  async function release(context, options, payload) {
18
- if (!options.config.has("functions")) {
18
+ if (!context.config) {
19
19
  return;
20
20
  }
21
21
  const plan = planner.createDeploymentPlan(payload.functions.backend, await backend.existingBackend(context), { filters: context.filters });
@@ -1,23 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureFirebaseAlertsTriggerRegion = exports.obtainFirebaseAlertsBindings = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
3
+ exports.ensureFirebaseAlertsTriggerRegion = void 0;
4
4
  const error_1 = require("../../../error");
5
- exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = "roles/iam.serviceAccountTokenCreator";
6
- function obtainFirebaseAlertsBindings(projectNumber, existingPolicy) {
7
- const pubsubServiceAgent = `serviceAccount:service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
8
- let pubsubBinding = existingPolicy.bindings.find((b) => b.role === exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE);
9
- if (!pubsubBinding) {
10
- pubsubBinding = {
11
- role: exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE,
12
- members: [],
13
- };
14
- }
15
- if (!pubsubBinding.members.find((m) => m === pubsubServiceAgent)) {
16
- pubsubBinding.members.push(pubsubServiceAgent);
17
- }
18
- return Promise.resolve([pubsubBinding]);
19
- }
20
- exports.obtainFirebaseAlertsBindings = obtainFirebaseAlertsBindings;
21
5
  function ensureFirebaseAlertsTriggerRegion(endpoint) {
22
6
  if (!endpoint.eventTrigger.region) {
23
7
  endpoint.eventTrigger.region = "global";
@@ -5,6 +5,7 @@ const backend = require("../backend");
5
5
  const storage_1 = require("./storage");
6
6
  const firebaseAlerts_1 = require("./firebaseAlerts");
7
7
  const noop = () => Promise.resolve();
8
+ const noopProjectBindings = () => Promise.resolve([]);
8
9
  exports.NoOpService = {
9
10
  name: "noop",
10
11
  api: "",
@@ -26,7 +27,7 @@ exports.StorageService = {
26
27
  exports.FirebaseAlertsService = {
27
28
  name: "firebasealerts",
28
29
  api: "logging.googleapis.com",
29
- requiredProjectBindings: firebaseAlerts_1.obtainFirebaseAlertsBindings,
30
+ requiredProjectBindings: noopProjectBindings,
30
31
  ensureTriggerRegion: firebaseAlerts_1.ensureFirebaseAlertsTriggerRegion,
31
32
  };
32
33
  exports.EVENT_SERVICE_MAPPING = {
@@ -41,12 +41,22 @@ async function deploy(context, options) {
41
41
  const publicDir = options.config.path(deploy.config.public);
42
42
  const files = (0, listFiles_1.listFiles)(publicDir, deploy.config.ignore);
43
43
  (0, utils_1.logLabeledBullet)(`hosting[${deploy.site}]`, `found ${files.length} files in ${clc.bold(deploy.config.public)}`);
44
+ let concurrency = 200;
45
+ const envConcurrency = (0, utils_1.envOverride)("FIREBASE_HOSTING_UPLOAD_CONCURRENCY", "");
46
+ if (envConcurrency) {
47
+ const c = parseInt(envConcurrency, 10);
48
+ if (!isNaN(c) && c > 0) {
49
+ concurrency = c;
50
+ }
51
+ }
52
+ logger_1.logger.debug(`[hosting] uploading with ${concurrency} concurrency`);
44
53
  const uploader = new uploader_1.Uploader({
45
54
  version: deploy.version,
46
55
  files: files,
47
56
  public: publicDir,
48
57
  cwd: options.cwd,
49
58
  projectRoot: (0, detectProjectRoot_1.detectProjectRoot)(options),
59
+ uploadConcurrency: concurrency,
50
60
  });
51
61
  const progressInterval = setInterval(() => updateSpinner(uploader.statusMessage(), debugging), debugging ? 2000 : 200);
52
62
  try {
@@ -5705,6 +5705,28 @@ exports.default = {
5705
5705
  },
5706
5706
  type: "object",
5707
5707
  },
5708
+ GoogleCloudIdentitytoolkitAdminV2AllowByDefault: {
5709
+ description: "Defines a policy of allowing every region by default and adding disallowed regions to a disallow list.",
5710
+ properties: {
5711
+ disallowedRegions: {
5712
+ description: "Two letter unicode region codes to disallow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
5713
+ items: { type: "string" },
5714
+ type: "array",
5715
+ },
5716
+ },
5717
+ type: "object",
5718
+ },
5719
+ GoogleCloudIdentitytoolkitAdminV2AllowlistOnly: {
5720
+ description: "Defines a policy of only allowing regions by explicitly adding them to an allowlist.",
5721
+ properties: {
5722
+ allowedRegions: {
5723
+ description: "Two letter unicode region codes to allow as defined by https://cldr.unicode.org/ The full list of these region codes is here: https://github.com/unicode-cldr/cldr-localenames-full/blob/master/main/en/territories.json",
5724
+ items: { type: "string" },
5725
+ type: "array",
5726
+ },
5727
+ },
5728
+ type: "object",
5729
+ },
5708
5730
  GoogleCloudIdentitytoolkitAdminV2Anonymous: {
5709
5731
  description: "Configuration options related to authenticating an anonymous user.",
5710
5732
  properties: {
@@ -6315,6 +6337,18 @@ exports.default = {
6315
6337
  },
6316
6338
  type: "object",
6317
6339
  },
6340
+ GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig: {
6341
+ description: "Configures the regions where users are allowed to send verification SMS for the project or tenant. This is based on the calling code of the destination phone number.",
6342
+ properties: {
6343
+ allowByDefault: {
6344
+ $ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowByDefault",
6345
+ },
6346
+ allowlistOnly: {
6347
+ $ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2AllowlistOnly",
6348
+ },
6349
+ },
6350
+ type: "object",
6351
+ },
6318
6352
  GoogleCloudIdentitytoolkitAdminV2SmsTemplate: {
6319
6353
  description: "The template to use when sending an SMS.",
6320
6354
  properties: {
@@ -6424,6 +6458,9 @@ exports.default = {
6424
6458
  readOnly: true,
6425
6459
  type: "string",
6426
6460
  },
6461
+ smsRegionConfig: {
6462
+ $ref: "#/components/schemas/GoogleCloudIdentitytoolkitAdminV2SmsRegionConfig",
6463
+ },
6427
6464
  testPhoneNumbers: {
6428
6465
  additionalProperties: { type: "string" },
6429
6466
  description: "A map of pairs that can be used for MFA. The phone number should be in E.164 format (https://www.itu.int/rec/T-REC-E.164/) and a maximum of 10 pairs can be added (error will be thrown once exceeded).",
@@ -10,7 +10,7 @@ const logger_1 = require("../logger");
10
10
  const path = require("path");
11
11
  const constants_1 = require("./constants");
12
12
  const requireAuth_1 = require("../requireAuth");
13
- const requireConfig = require("../requireConfig");
13
+ const requireConfig_1 = require("../requireConfig");
14
14
  const types_1 = require("./types");
15
15
  const error_1 = require("../error");
16
16
  const registry_1 = require("./registry");
@@ -102,7 +102,7 @@ async function beforeEmulatorCommand(options) {
102
102
  options.config = DEFAULT_CONFIG;
103
103
  }
104
104
  else {
105
- await requireConfig(options);
105
+ await (0, requireConfig_1.requireConfig)(options);
106
106
  }
107
107
  }
108
108
  exports.beforeEmulatorCommand = beforeEmulatorCommand;
@@ -38,6 +38,7 @@ const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
38
38
  const auth_2 = require("../auth");
39
39
  const extensionsEmulator_1 = require("./extensionsEmulator");
40
40
  const previews_1 = require("../previews");
41
+ const projectConfig_1 = require("../functions/projectConfig");
41
42
  const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
42
43
  async function getAndCheckAddress(emulator, options) {
43
44
  var _a, _b, _c, _d;
@@ -140,7 +141,7 @@ function filterEmulatorTargets(options) {
140
141
  }
141
142
  exports.filterEmulatorTargets = filterEmulatorTargets;
142
143
  function shouldStart(options, name) {
143
- var _a, _b, _c;
144
+ var _a, _b;
144
145
  if (name === types_1.Emulators.HUB) {
145
146
  return !!options.project;
146
147
  }
@@ -155,9 +156,15 @@ function shouldStart(options, name) {
155
156
  }
156
157
  return (!!options.project && targets.some((target) => types_1.EMULATORS_SUPPORTED_BY_UI.includes(target)));
157
158
  }
158
- if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets && !((_c = options.config.src.functions) === null || _c === void 0 ? void 0 : _c.source)) {
159
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN", "functions", `The functions emulator is configured but there is no functions source directory. Have you run ${clc.bold("firebase init functions")}?`);
160
- return false;
159
+ if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets) {
160
+ try {
161
+ (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
162
+ return true;
163
+ }
164
+ catch (err) {
165
+ emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN", "functions", `The functions emulator is configured but there is no functions source directory. Have you run ${clc.bold("firebase init functions")}?`);
166
+ return false;
167
+ }
161
168
  }
162
169
  if (name === types_1.Emulators.HOSTING && emulatorInTargets && !options.config.get("hosting")) {
163
170
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HOSTING).logLabeled("WARN", "hosting", `The hosting emulator is configured but there is no hosting configuration. Have you run ${clc.bold("firebase init hosting")}?`);
@@ -253,16 +260,15 @@ async function startAll(options, showUI = true) {
253
260
  const emulatableBackends = [];
254
261
  const projectDir = (options.extDevDir || options.config.projectDir);
255
262
  if (shouldStart(options, types_1.Emulators.FUNCTIONS)) {
256
- utils.assertDefined(options.config.src.functions);
257
- utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
263
+ const functionsCfg = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
258
264
  utils.assertIsStringOrUndefined(options.extDevDir);
259
- const functionsDir = path.join(projectDir, options.config.src.functions.source);
265
+ const functionsDir = path.join(projectDir, functionsCfg.source);
260
266
  emulatableBackends.push({
261
267
  functionsDir,
262
268
  env: Object.assign({}, options.extDevEnv),
263
269
  secretEnv: [],
264
270
  predefinedTriggers: options.extDevTriggers,
265
- nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || options.config.get("functions.runtime")),
271
+ nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || functionsCfg.runtime),
266
272
  });
267
273
  }
268
274
  if (shouldStart(options, types_1.Emulators.EXTENSIONS) && previews_1.previews.extensionsemulator) {
@@ -40,13 +40,13 @@ exports.DownloadDetails = {
40
40
  },
41
41
  },
42
42
  storage: {
43
- downloadPath: path.join(CACHE_DIR, "cloud-storage-rules-runtime-v1.0.1.jar"),
44
- version: "1.0.1",
43
+ downloadPath: path.join(CACHE_DIR, "cloud-storage-rules-runtime-v1.0.2.jar"),
44
+ version: "1.0.2",
45
45
  opts: {
46
46
  cacheDir: CACHE_DIR,
47
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.0.1.jar",
48
- expectedSize: 32729999,
49
- expectedChecksum: "1a441f5e16c17aa7a27db71c9c9186d5",
47
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.0.2.jar",
48
+ expectedSize: 35704306,
49
+ expectedChecksum: "0dd3e17939610fc3dbdf53fb24cfda86",
50
50
  namePrefix: "cloud-storage-rules-emulator",
51
51
  },
52
52
  },
@@ -65,6 +65,9 @@ class ExtensionsEmulator {
65
65
  "./functions/package.json",
66
66
  "./functions/node_modules",
67
67
  ];
68
+ if (!fs.existsSync(args.path)) {
69
+ return false;
70
+ }
68
71
  for (const requiredFile of requiredFiles) {
69
72
  const f = path.join(args.path, requiredFile);
70
73
  if (!fs.existsSync(f)) {
@@ -289,7 +289,7 @@ class FunctionsEmulator {
289
289
  });
290
290
  for (const definition of toSetup) {
291
291
  try {
292
- (0, validate_1.functionIdsAreValid)([definition]);
292
+ (0, validate_1.functionIdsAreValid)([Object.assign(Object.assign({}, definition), { id: definition.name })]);
293
293
  }
294
294
  catch (e) {
295
295
  this.logger.logLabeled("WARN", `functions[${definition.id}]`, `Invalid function id: ${e.message}`);
@@ -488,26 +488,16 @@ class FunctionsEmulator {
488
488
  return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
489
489
  }
490
490
  getBackendInfo() {
491
- const cf3Triggers = Object.values(this.triggers)
492
- .filter((t) => !t.backend.extensionInstanceId)
493
- .map((t) => t.def);
491
+ const cf3Triggers = this.getCF3Triggers();
494
492
  return this.args.emulatableBackends.map((e) => {
495
- var _a;
496
- const envWithSecrets = Object.assign({}, e.env);
497
- for (const s of e.secretEnv) {
498
- envWithSecrets[s.key] = backend.secretVersionName(s);
499
- }
500
- return {
501
- directory: e.functionsDir,
502
- env: envWithSecrets,
503
- extensionInstanceId: e.extensionInstanceId,
504
- extension: e.extension,
505
- extensionVersion: e.extensionVersion,
506
- extensionSpec: e.extensionSpec,
507
- functionTriggers: (_a = e.predefinedTriggers) !== null && _a !== void 0 ? _a : cf3Triggers,
508
- };
493
+ return (0, functionsEmulatorShared_1.toBackendInfo)(e, cf3Triggers);
509
494
  });
510
495
  }
496
+ getCF3Triggers() {
497
+ return Object.values(this.triggers)
498
+ .filter((t) => !t.backend.extensionInstanceId)
499
+ .map((t) => t.def);
500
+ }
511
501
  addTriggerRecord(def, opts) {
512
502
  const key = this.getTriggerKey(def);
513
503
  this.triggers[key] = {