firebase-tools 10.3.0 → 10.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) 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 +13 -6
  5. package/lib/commands/ext-export.js +7 -1
  6. package/lib/commands/ext-install.js +12 -7
  7. package/lib/commands/ext-update.js +5 -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/deploy.js +3 -7
  24. package/lib/deploy/functions/prepare.js +7 -5
  25. package/lib/deploy/functions/prepareFunctionsUpload.js +7 -13
  26. package/lib/deploy/functions/release/fabricator.js +13 -1
  27. package/lib/deploy/functions/release/index.js +1 -1
  28. package/lib/deploy/functions/runtimes/node/parseTriggers.js +12 -5
  29. package/lib/deploy/hosting/deploy.js +10 -0
  30. package/lib/emulator/auth/apiSpec.js +37 -0
  31. package/lib/emulator/commandUtils.js +2 -2
  32. package/lib/emulator/controller.js +14 -8
  33. package/lib/emulator/downloadableEmulators.js +5 -5
  34. package/lib/emulator/extensionsEmulator.js +3 -0
  35. package/lib/emulator/functionsEmulator.js +4 -4
  36. package/lib/emulator/functionsEmulatorShared.js +17 -1
  37. package/lib/emulator/storage/apis/firebase.js +4 -6
  38. package/lib/emulator/storage/files.js +5 -5
  39. package/lib/emulator/storage/index.js +6 -9
  40. package/lib/emulator/storage/rules/config.js +6 -5
  41. package/lib/emulator/storage/rules/manager.js +49 -32
  42. package/lib/emulator/storage/rules/runtime.js +4 -0
  43. package/lib/emulator/storage/rules/utils.js +2 -2
  44. package/lib/emulator/storage/server.js +1 -1
  45. package/lib/extensions/askUserForParam.js +103 -28
  46. package/lib/extensions/manifest.js +38 -6
  47. package/lib/extensions/paramHelper.js +28 -6
  48. package/lib/fsutils.js +14 -1
  49. package/lib/functions/projectConfig.js +34 -0
  50. package/lib/gcp/cloudfunctions.js +5 -4
  51. package/lib/init/features/functions/index.js +4 -2
  52. package/lib/init/features/hosting/index.js +32 -41
  53. package/lib/init/features/index.js +22 -12
  54. package/lib/init/index.js +28 -11
  55. package/lib/requireConfig.js +11 -9
  56. package/lib/serve/functions.js +5 -5
  57. package/npm-shrinkwrap.json +2 -2
  58. package/package.json +1 -1
  59. package/schema/firebase-config.json +93 -36
  60. package/lib/prepareUpload.js +0 -44
@@ -8,13 +8,13 @@ const prompt_1 = require("../prompt");
8
8
  const error_1 = require("../error");
9
9
  const requirePermissions_1 = require("../requirePermissions");
10
10
  const projectUtils_1 = require("../projectUtils");
11
- const requireConfig = require("../requireConfig");
11
+ const requireConfig_1 = require("../requireConfig");
12
12
  const logger_1 = require("../logger");
13
13
  const LOG_TAG = "hosting:sites";
14
14
  exports.default = new command_1.Command("hosting:sites:delete <siteId>")
15
15
  .description("delete a Firebase Hosting site")
16
16
  .withForce()
17
- .before(requireConfig)
17
+ .before(requireConfig_1.requireConfig)
18
18
  .before(requirePermissions_1.requirePermissions, ["firebasehosting.sites.delete"])
19
19
  .action(async (siteId, options) => {
20
20
  const projectId = (0, projectUtils_1.needProjectId)(options);
@@ -5,7 +5,7 @@ var { Command } = require("../command");
5
5
  const { logger } = require("../logger");
6
6
  var utils = require("../utils");
7
7
  var { requirePermissions } = require("../requirePermissions");
8
- var requireConfig = require("../requireConfig");
8
+ var { requireConfig } = require("../requireConfig");
9
9
  var { serve } = require("../serve/index");
10
10
  var { filterTargets } = require("../filterTargets");
11
11
  var { needProjectNumber } = require("../projectUtils");
@@ -3,12 +3,12 @@ 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
  const error_1 = require("../error");
9
9
  exports.default = new command_1.Command("target:apply <type> <name> <resources...>")
10
10
  .description("apply a deploy target to a resource")
11
- .before(requireConfig)
11
+ .before(requireConfig_1.requireConfig)
12
12
  .action((type, name, resources, options) => {
13
13
  if (!options.project) {
14
14
  throw new error_1.FirebaseError(`Must have an active project to set deploy targets. Try ${clc.bold("firebase use --add")}`);
@@ -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:clear <type> <target>")
8
8
  .description("clear all resources from a named resource target")
9
- .before(requireConfig)
9
+ .before(requireConfig_1.requireConfig)
10
10
  .action((type, name, options) => {
11
11
  const existed = options.rc.clearTarget(options.project, type, name);
12
12
  if (existed) {
@@ -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}`);
@@ -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 });
@@ -73,6 +73,7 @@ function mergeRequiredAPIs(backend) {
73
73
  }
74
74
  exports.mergeRequiredAPIs = mergeRequiredAPIs;
75
75
  function addResourcesToBackend(projectId, runtime, annotation, want) {
76
+ var _a;
76
77
  Object.freeze(annotation);
77
78
  for (const region of annotation.regions || [api.functionsDefaultRegion]) {
78
79
  let triggered;
@@ -88,12 +89,18 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
88
89
  });
89
90
  }
90
91
  else if (annotation.httpsTrigger) {
91
- const trigger = {};
92
- if (annotation.failurePolicy) {
93
- logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
92
+ if ((_a = annotation.labels) === null || _a === void 0 ? void 0 : _a["deployment-callable"]) {
93
+ delete annotation.labels["deployment-callable"];
94
+ triggered = { callableTrigger: {} };
95
+ }
96
+ else {
97
+ const trigger = {};
98
+ if (annotation.failurePolicy) {
99
+ logger_1.logger.warn(`Ignoring retry policy for HTTPS function ${annotation.name}`);
100
+ }
101
+ proto.copyIfPresent(trigger, annotation.httpsTrigger, "invoker");
102
+ triggered = { httpsTrigger: trigger };
94
103
  }
95
- proto.copyIfPresent(trigger, annotation.httpsTrigger, "invoker");
96
- triggered = { httpsTrigger: trigger };
97
104
  }
98
105
  else if (annotation.schedule) {
99
106
  want.requiredAPIs.push({
@@ -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)) {
@@ -35,7 +35,6 @@ const runtimes = require("../deploy/functions/runtimes");
35
35
  const backend = require("../deploy/functions/backend");
36
36
  const functionsEnv = require("../functions/env");
37
37
  const EVENT_INVOKE = "functions:invoke";
38
- const LOCAL_SECRETS_FILE = ".secret.local";
39
38
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
40
39
  class FunctionsEmulator {
41
40
  constructor(args) {
@@ -654,13 +653,14 @@ class FunctionsEmulator {
654
653
  }
655
654
  async resolveSecretEnvs(backend, trigger) {
656
655
  let secretEnvs = {};
656
+ const secretPath = (0, functionsEmulatorShared_1.getSecretLocalPath)(backend, this.args.projectDir);
657
657
  try {
658
- const data = fs.readFileSync(path.join(backend.functionsDir, LOCAL_SECRETS_FILE), "utf8");
658
+ const data = fs.readFileSync(secretPath, "utf8");
659
659
  secretEnvs = functionsEnv.parseStrict(data);
660
660
  }
661
661
  catch (e) {
662
662
  if (e.code !== "ENOENT") {
663
- this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${LOCAL_SECRETS_FILE}: ${e.message}`);
663
+ this.logger.logLabeled("ERROR", "functions", `Failed to read local secrets file ${secretPath}: ${e.message}`);
664
664
  }
665
665
  }
666
666
  if (trigger) {
@@ -687,7 +687,7 @@ class FunctionsEmulator {
687
687
  if (errs.length > 0) {
688
688
  this.logger.logLabeled("ERROR", "functions", "Unable to access secret environment variables from Google Cloud Secret Manager. " +
689
689
  "Make sure the credential used for the Functions Emulator have access " +
690
- `or provide override values in ${LOCAL_SECRETS_FILE}:\n\t` +
690
+ `or provide override values in ${secretPath}:\n\t` +
691
691
  errs.join("\n\t"));
692
692
  }
693
693
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
3
+ exports.getSecretLocalPath = exports.getSignatureType = exports.formatHost = exports.findModuleRoot = exports.waitForBody = exports.getServiceFromEventType = exports.getFunctionService = exports.getTemporarySocketPath = exports.getEmulatedTriggersFromDefinitions = exports.emulatedFunctionsByRegion = exports.emulatedFunctionsFromEndpoints = exports.EmulatedTrigger = exports.HttpConstants = void 0;
4
4
  const _ = require("lodash");
5
5
  const os = require("os");
6
6
  const path = require("path");
@@ -9,6 +9,7 @@ const backend = require("../deploy/functions/backend");
9
9
  const constants_1 = require("./constants");
10
10
  const proto_1 = require("../gcp/proto");
11
11
  const logger_1 = require("../logger");
12
+ const manifest_1 = require("../extensions/manifest");
12
13
  const memoryLookup = {
13
14
  "128MB": 128,
14
15
  "256MB": 256,
@@ -64,6 +65,10 @@ function emulatedFunctionsFromEndpoints(endpoints) {
64
65
  if (backend.isHttpsTriggered(endpoint)) {
65
66
  def.httpsTrigger = endpoint.httpsTrigger;
66
67
  }
68
+ else if (backend.isCallableTriggered(endpoint)) {
69
+ def.httpsTrigger = {};
70
+ def.labels = Object.assign(Object.assign({}, def.labels), { "deployment-callable": "true" });
71
+ }
67
72
  else if (backend.isEventTriggered(endpoint)) {
68
73
  const eventTrigger = endpoint.eventTrigger;
69
74
  if (endpoint.platform === "gcfv1") {
@@ -226,3 +231,14 @@ function getSignatureType(def) {
226
231
  return def.platform === "gcfv2" ? "cloudevent" : "event";
227
232
  }
228
233
  exports.getSignatureType = getSignatureType;
234
+ const LOCAL_SECRETS_FILE = ".secret.local";
235
+ function getSecretLocalPath(backend, projectDir) {
236
+ const secretsFile = backend.extensionInstanceId
237
+ ? `${backend.extensionInstanceId}${LOCAL_SECRETS_FILE}`
238
+ : LOCAL_SECRETS_FILE;
239
+ const secretDirectory = backend.extensionInstanceId
240
+ ? path.join(projectDir, manifest_1.ENV_DIRECTORY)
241
+ : backend.functionsDir;
242
+ return path.join(secretDirectory, secretsFile);
243
+ }
244
+ exports.getSecretLocalPath = getSecretLocalPath;
@@ -52,8 +52,10 @@ function createFirebaseEndpoints(emulator) {
52
52
  next();
53
53
  });
54
54
  }
55
- firebaseStorageAPI.use((req, res, next) => {
56
- if (!emulator.rules) {
55
+ firebaseStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
56
+ const bucketId = req.params[0];
57
+ storageLayer.createBucket(bucketId);
58
+ if (!emulator.rulesManager.getRuleset(bucketId)) {
57
59
  emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.STORAGE).log("WARN", "Permission denied because no Storage ruleset is currently loaded, check your rules for syntax errors.");
58
60
  return res.status(403).json({
59
61
  error: {
@@ -64,10 +66,6 @@ function createFirebaseEndpoints(emulator) {
64
66
  }
65
67
  next();
66
68
  });
67
- firebaseStorageAPI.use(/.*\/b\/(.+?)\/.*/, (req, res, next) => {
68
- storageLayer.createBucket(req.params[0]);
69
- next();
70
- });
71
69
  firebaseStorageAPI.get("/b/:bucketId/o/:objectId", async (req, res) => {
72
70
  var _a;
73
71
  let metadata;