firebase-tools 10.2.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/lib/apiv2.js +3 -0
  2. package/lib/appdistribution/options-parser-util.js +1 -1
  3. package/lib/auth.js +3 -3
  4. package/lib/command.js +1 -1
  5. package/lib/commands/apps-android-sha-create.js +2 -2
  6. package/lib/commands/apps-sdkconfig.js +1 -1
  7. package/lib/commands/auth-import.js +1 -1
  8. package/lib/commands/database-rules-list.js +2 -2
  9. package/lib/commands/emulators-start.js +1 -1
  10. package/lib/commands/ext-configure.js +58 -4
  11. package/lib/commands/ext-dev-init.js +49 -49
  12. package/lib/commands/ext-export.js +7 -2
  13. package/lib/commands/ext-install.js +163 -104
  14. package/lib/commands/ext-uninstall.js +17 -8
  15. package/lib/commands/ext-update.js +64 -11
  16. package/lib/commands/functions-config-clone.js +1 -1
  17. package/lib/commands/functions-config-export.js +1 -1
  18. package/lib/commands/hosting-clone.js +3 -3
  19. package/lib/commands/remoteconfig-get.js +1 -1
  20. package/lib/config.js +6 -3
  21. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  22. package/lib/deploy/extensions/planner.js +7 -6
  23. package/lib/deploy/extensions/tasks.js +1 -1
  24. package/lib/deploy/functions/backend.js +21 -5
  25. package/lib/deploy/functions/checkIam.js +5 -5
  26. package/lib/deploy/functions/containerCleaner.js +3 -3
  27. package/lib/deploy/functions/ensure.js +3 -3
  28. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  29. package/lib/deploy/functions/prepare.js +5 -3
  30. package/lib/deploy/functions/pricing.js +1 -1
  31. package/lib/deploy/functions/prompts.js +2 -2
  32. package/lib/deploy/functions/release/fabricator.js +7 -7
  33. package/lib/deploy/functions/release/index.js +1 -1
  34. package/lib/deploy/functions/release/planner.js +43 -26
  35. package/lib/deploy/functions/release/reporter.js +3 -0
  36. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  39. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  40. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  41. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  42. package/lib/deploy/functions/runtimes/node/parseTriggers.js +52 -15
  43. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  44. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  45. package/lib/deploy/functions/services/index.js +9 -1
  46. package/lib/deploy/functions/services/storage.js +10 -4
  47. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  48. package/lib/deploy/functions/validate.js +3 -3
  49. package/lib/deploy/hosting/client.js +9 -0
  50. package/lib/deploy/hosting/convertConfig.js +6 -0
  51. package/lib/deploy/hosting/deploy.js +2 -2
  52. package/lib/deploy/hosting/hashcache.js +21 -19
  53. package/lib/deploy/hosting/index.js +5 -5
  54. package/lib/deploy/hosting/prepare.js +25 -25
  55. package/lib/deploy/hosting/release.js +21 -24
  56. package/lib/deploy/hosting/uploader.js +5 -5
  57. package/lib/deploy/remoteconfig/functions.js +2 -2
  58. package/lib/emulator/auth/cloudFunctions.js +1 -1
  59. package/lib/emulator/auth/operations.js +1 -1
  60. package/lib/emulator/commandUtils.js +5 -1
  61. package/lib/emulator/constants.js +4 -0
  62. package/lib/emulator/controller.js +54 -24
  63. package/lib/emulator/download.js +18 -1
  64. package/lib/emulator/downloadableEmulators.js +30 -13
  65. package/lib/emulator/emulatorLogger.js +12 -1
  66. package/lib/emulator/extensions/validation.js +70 -0
  67. package/lib/emulator/extensionsEmulator.js +175 -0
  68. package/lib/emulator/functionsEmulator.js +106 -44
  69. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  70. package/lib/emulator/functionsEmulatorShared.js +17 -10
  71. package/lib/emulator/functionsEmulatorShell.js +1 -1
  72. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  73. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  74. package/lib/emulator/hub.js +4 -3
  75. package/lib/emulator/loggingEmulator.js +1 -1
  76. package/lib/emulator/pubsubEmulator.js +1 -1
  77. package/lib/emulator/registry.js +10 -2
  78. package/lib/emulator/storage/apis/firebase.js +314 -332
  79. package/lib/emulator/storage/apis/gcloud.js +241 -121
  80. package/lib/emulator/storage/crc.js +5 -1
  81. package/lib/emulator/storage/errors.js +9 -0
  82. package/lib/emulator/storage/files.js +159 -300
  83. package/lib/emulator/storage/index.js +27 -73
  84. package/lib/emulator/storage/metadata.js +65 -51
  85. package/lib/emulator/storage/multipart.js +62 -0
  86. package/lib/emulator/storage/persistence.js +78 -0
  87. package/lib/emulator/storage/rules/config.js +33 -0
  88. package/lib/emulator/storage/rules/manager.js +81 -0
  89. package/lib/emulator/storage/rules/runtime.js +8 -7
  90. package/lib/emulator/storage/rules/utils.js +48 -0
  91. package/lib/emulator/storage/server.js +2 -2
  92. package/lib/emulator/storage/upload.js +106 -0
  93. package/lib/emulator/types.js +3 -0
  94. package/lib/ensureApiEnabled.js +5 -1
  95. package/lib/error.js +1 -1
  96. package/lib/extensions/askUserForParam.js +1 -1
  97. package/lib/extensions/changelog.js +3 -1
  98. package/lib/extensions/checkProjectBilling.js +1 -1
  99. package/lib/extensions/displayExtensionInfo.js +1 -1
  100. package/lib/extensions/emulator/optionsHelper.js +56 -8
  101. package/lib/extensions/emulator/specHelper.js +10 -23
  102. package/lib/extensions/export.js +1 -51
  103. package/lib/extensions/extensionsApi.js +1 -1
  104. package/lib/extensions/extensionsHelper.js +32 -19
  105. package/lib/extensions/listExtensions.js +2 -0
  106. package/lib/extensions/manifest.js +144 -0
  107. package/lib/extensions/metricsUtils.js +4 -4
  108. package/lib/extensions/paramHelper.js +9 -8
  109. package/lib/extensions/refs.js +1 -1
  110. package/lib/extensions/secretsUtils.js +3 -3
  111. package/lib/functional.js +1 -1
  112. package/lib/functions/env.js +6 -7
  113. package/lib/functions/events/v2.js +11 -0
  114. package/lib/gcp/cloudfunctions.js +42 -11
  115. package/lib/gcp/cloudfunctionsv2.js +48 -17
  116. package/lib/gcp/cloudtasks.js +1 -1
  117. package/lib/gcp/docker.js +2 -2
  118. package/lib/gcp/resourceManager.js +4 -4
  119. package/lib/gcp/run.js +2 -2
  120. package/lib/gcp/storage.js +1 -0
  121. package/lib/hosting/api.js +1 -1
  122. package/lib/hosting/functionsProxy.js +15 -5
  123. package/lib/hosting/proxy.js +2 -2
  124. package/lib/init/features/account.js +1 -1
  125. package/lib/management/database.js +1 -1
  126. package/lib/previews.js +1 -1
  127. package/lib/responseToError.js +16 -7
  128. package/lib/serve/functions.js +2 -1
  129. package/lib/serve/hosting.js +1 -1
  130. package/lib/utils.js +15 -2
  131. package/npm-shrinkwrap.json +904 -412
  132. package/package.json +3 -3
  133. package/schema/firebase-config.json +32 -0
  134. package/templates/init/functions/javascript/package.lint.json +3 -3
  135. package/templates/init/functions/javascript/package.nolint.json +2 -2
  136. package/templates/init/functions/typescript/package.lint.json +7 -7
  137. package/templates/init/functions/typescript/package.nolint.json +3 -3
  138. package/lib/deploy/extensions/params.js +0 -39
  139. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -2,12 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureStorageTriggerRegion = exports.obtainStorageBindings = void 0;
4
4
  const storage = require("../../../gcp/storage");
5
+ const backend = require("../backend");
5
6
  const logger_1 = require("../../../logger");
6
7
  const error_1 = require("../../../error");
7
8
  const location_1 = require("../../../gcp/location");
8
9
  const PUBSUB_PUBLISHER_ROLE = "roles/pubsub.publisher";
9
- async function obtainStorageBindings(projectId, existingPolicy) {
10
- const storageResponse = await storage.getServiceAccount(projectId);
10
+ async function obtainStorageBindings(projectNumber, existingPolicy) {
11
+ const storageResponse = await storage.getServiceAccount(projectNumber);
11
12
  const storageServiceAgent = `serviceAccount:${storageResponse.email_address}`;
12
13
  let pubsubBinding = existingPolicy.bindings.find((b) => b.role === PUBSUB_PUBLISHER_ROLE);
13
14
  if (!pubsubBinding) {
@@ -22,11 +23,16 @@ async function obtainStorageBindings(projectId, existingPolicy) {
22
23
  return [pubsubBinding];
23
24
  }
24
25
  exports.obtainStorageBindings = obtainStorageBindings;
25
- async function ensureStorageTriggerRegion(endpoint, eventTrigger) {
26
+ async function ensureStorageTriggerRegion(endpoint) {
27
+ const { eventTrigger } = endpoint;
26
28
  if (!eventTrigger.region) {
27
29
  logger_1.logger.debug("Looking up bucket region for the storage event trigger");
30
+ const bucketFilter = backend.findEventFilter(endpoint, "bucket");
31
+ if (!bucketFilter) {
32
+ throw new error_1.FirebaseError("Storage event trigger unexpectedly missing event filter with bucket attribute.");
33
+ }
28
34
  try {
29
- const bucket = await storage.getBucket(eventTrigger.eventFilters.bucket);
35
+ const bucket = await storage.getBucket(bucketFilter.value);
30
36
  eventTrigger.region = bucket.location.toLowerCase();
31
37
  logger_1.logger.debug("Setting the event trigger region to", eventTrigger.region, ".");
32
38
  }
@@ -9,7 +9,7 @@ async function ensureTriggerRegions(want) {
9
9
  if (ep.platform === "gcfv1" || !backend.isEventTriggered(ep)) {
10
10
  continue;
11
11
  }
12
- regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep, ep.eventTrigger));
12
+ regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep));
13
13
  }
14
14
  await Promise.all(regionLookups);
15
15
  }
@@ -14,7 +14,7 @@ function endpointsAreValid(wantBackend) {
14
14
  functionIdsAreValid(backend.allEndpoints(wantBackend));
15
15
  const gcfV1WithConcurrency = backend
16
16
  .allEndpoints(wantBackend)
17
- .filter((endpoint) => (endpoint.concurrency || 1) != 1 && endpoint.platform == "gcfv1")
17
+ .filter((endpoint) => (endpoint.concurrency || 1) !== 1 && endpoint.platform === "gcfv1")
18
18
  .map((endpoint) => endpoint.id);
19
19
  if (gcfV1WithConcurrency.length) {
20
20
  const msg = `Cannot set concurrency on the functions ${gcfV1WithConcurrency.join(",")} because they are GCF gen 1`;
@@ -23,7 +23,7 @@ function endpointsAreValid(wantBackend) {
23
23
  const tooSmallForConcurrency = backend
24
24
  .allEndpoints(wantBackend)
25
25
  .filter((endpoint) => {
26
- if ((endpoint.concurrency || 1) == 1) {
26
+ if ((endpoint.concurrency || 1) === 1) {
27
27
  return false;
28
28
  }
29
29
  const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
@@ -98,7 +98,7 @@ async function validateSecretVersions(projectId, endpoints) {
98
98
  for (const result of results) {
99
99
  if (result.status === "fulfilled") {
100
100
  const sv = result.value;
101
- if (sv.state != "ENABLED") {
101
+ if (sv.state !== "ENABLED") {
102
102
  errs.push(new error_1.FirebaseError(`Expected secret ${sv.secret.name}@${sv.versionId} to be in state ENABLED not ${sv.state}.`));
103
103
  }
104
104
  secretVersions[sv.secret.name] = sv;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.client = void 0;
4
+ const api_1 = require("../../api");
5
+ const apiv2_1 = require("../../apiv2");
6
+ exports.client = new apiv2_1.Client({
7
+ urlPrefix: api_1.hostingApiOrigin,
8
+ apiVersion: "v1beta1",
9
+ });
@@ -46,6 +46,12 @@ function convertConfig(config) {
46
46
  }
47
47
  else if ("function" in rewrite) {
48
48
  vRewrite.function = rewrite.function;
49
+ if (rewrite.region) {
50
+ vRewrite.functionRegion = rewrite.region;
51
+ }
52
+ else {
53
+ vRewrite.functionRegion = "us-central1";
54
+ }
49
55
  }
50
56
  else if ("dynamicLinks" in rewrite) {
51
57
  vRewrite.dynamicLinks = rewrite.dynamicLinks;
@@ -53,7 +53,7 @@ async function deploy(context, options) {
53
53
  await uploader.start();
54
54
  }
55
55
  catch (err) {
56
- track("Hosting Deploy", "failure");
56
+ void track("Hosting Deploy", "failure");
57
57
  throw err;
58
58
  }
59
59
  finally {
@@ -65,7 +65,7 @@ async function deploy(context, options) {
65
65
  (0, utils_1.logLabeledSuccess)("hosting[" + deploy.site + "]", "file upload complete");
66
66
  const dt = Date.now() - t0;
67
67
  logger_1.logger.debug("[hosting] deploy completed after " + dt + "ms");
68
- track("Hosting Deploy", "success", dt);
68
+ void track("Hosting Deploy", "success", dt);
69
69
  return runDeploys(deploys, debugging);
70
70
  }
71
71
  const debugging = !!(options.debug || options.nonInteractive);
@@ -1,46 +1,48 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dump = exports.load = void 0;
2
4
  const fs = require("fs-extra");
3
5
  const path = require("path");
4
- const { logger } = require("../../logger");
6
+ const logger_1 = require("../../logger");
5
7
  function cachePath(cwd, name) {
6
- return path.resolve(cwd, ".firebase/hosting." + name + ".cache");
8
+ return path.resolve(cwd, `.firebase/hosting.${name}.cache`);
7
9
  }
8
- exports.load = function (cwd, name) {
10
+ function load(cwd, name) {
9
11
  try {
10
- const out = {};
11
- const lines = fs.readFileSync(cachePath(cwd, name), {
12
- encoding: "utf8",
13
- });
14
- lines.split("\n").forEach(function (line) {
12
+ const out = new Map();
13
+ const lines = fs.readFileSync(cachePath(cwd, name), "utf8");
14
+ for (const line of lines.split("\n")) {
15
15
  const d = line.split(",");
16
16
  if (d.length === 3) {
17
- out[d[0]] = { mtime: parseInt(d[1]), hash: d[2] };
17
+ out.set(d[0], { mtime: parseInt(d[1]), hash: d[2] });
18
18
  }
19
- });
19
+ }
20
20
  return out;
21
21
  }
22
22
  catch (e) {
23
23
  if (e.code === "ENOENT") {
24
- logger.debug("[hosting] hash cache [" + name + "] not populated");
24
+ logger_1.logger.debug(`[hosting] hash cache [${name}] not populated`);
25
25
  }
26
26
  else {
27
- logger.debug("[hosting] hash cache [" + name + "] load error:", e.message);
27
+ logger_1.logger.debug(`[hosting] hash cache [${name}] load error: ${e.message}`);
28
28
  }
29
- return {};
29
+ return new Map();
30
30
  }
31
- };
32
- exports.dump = function (cwd, name, data) {
31
+ }
32
+ exports.load = load;
33
+ function dump(cwd, name, data) {
33
34
  let st = "";
34
35
  let count = 0;
35
36
  for (const [path, d] of data) {
36
37
  count++;
37
- st += path + "," + d.mtime + "," + d.hash + "\n";
38
+ st += `${path},${d.mtime},${d.hash}\n`;
38
39
  }
39
40
  try {
40
41
  fs.outputFileSync(cachePath(cwd, name), st, { encoding: "utf8" });
41
- logger.debug("[hosting] hash cache [" + name + "] stored for", count, "files");
42
+ logger_1.logger.debug(`[hosting] hash cache [${name}] stored for ${count} files`);
42
43
  }
43
44
  catch (e) {
44
- logger.debug("[hosting] unable to store hash cache [" + name + "]", e.stack);
45
+ logger_1.logger.debug(`[hosting] unable to store hash cache [${name}]: ${e.stack}`);
45
46
  }
46
- };
47
+ }
48
+ exports.dump = dump;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.release = exports.deploy = exports.prepare = void 0;
4
- const prepare = require("./prepare");
5
- exports.prepare = prepare;
6
- const deploy_1 = require("./deploy");
4
+ var prepare_1 = require("./prepare");
5
+ Object.defineProperty(exports, "prepare", { enumerable: true, get: function () { return prepare_1.prepare; } });
6
+ var deploy_1 = require("./deploy");
7
7
  Object.defineProperty(exports, "deploy", { enumerable: true, get: function () { return deploy_1.deploy; } });
8
- const release = require("./release");
9
- exports.release = release;
8
+ var release_1 = require("./release");
9
+ Object.defineProperty(exports, "release", { enumerable: true, get: function () { return release_1.release; } });
@@ -1,44 +1,44 @@
1
1
  "use strict";
2
- const _ = require("lodash");
3
- const api = require("../../api");
4
- const { convertConfig } = require("./convertConfig");
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepare = void 0;
4
+ const error_1 = require("../../error");
5
+ const client_1 = require("./client");
6
+ const projectUtils_1 = require("../../projectUtils");
7
+ const normalizedHostingConfigs_1 = require("../../hosting/normalizedHostingConfigs");
8
+ const validate_1 = require("./validate");
9
+ const convertConfig_1 = require("./convertConfig");
5
10
  const deploymentTool = require("../../deploymentTool");
6
- const { FirebaseError } = require("../../error");
7
- const { normalizedHostingConfigs } = require("../../hosting/normalizedHostingConfigs");
8
- const { validateDeploy } = require("./validate");
9
- module.exports = function (context, options) {
11
+ async function prepare(context, options) {
10
12
  if (options.public) {
11
- if (_.isArray(options.config.get("hosting"))) {
12
- throw new FirebaseError("Cannot specify --public option with multi-site configuration.");
13
+ if (Array.isArray(options.config.get("hosting"))) {
14
+ throw new error_1.FirebaseError("Cannot specify --public option with multi-site configuration.");
13
15
  }
14
16
  options.config.set("hosting.public", options.public);
15
17
  }
16
- const configs = normalizedHostingConfigs(options, { resolveTargets: true });
18
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
19
+ const configs = (0, normalizedHostingConfigs_1.normalizedHostingConfigs)(options, { resolveTargets: true });
17
20
  if (configs.length === 0) {
18
21
  return Promise.resolve();
19
22
  }
20
23
  context.hosting = {
21
- deploys: configs.map(function (cfg) {
24
+ deploys: configs.map((cfg) => {
22
25
  return { config: cfg, site: cfg.site };
23
26
  }),
24
27
  };
25
28
  const versionCreates = [];
26
- _.each(context.hosting.deploys, function (deploy) {
29
+ for (const deploy of context.hosting.deploys) {
27
30
  const cfg = deploy.config;
28
- validateDeploy(deploy, options);
31
+ (0, validate_1.validateDeploy)(deploy, options);
29
32
  const data = {
30
- config: convertConfig(cfg),
33
+ config: (0, convertConfig_1.convertConfig)(cfg),
31
34
  labels: deploymentTool.labels(),
32
35
  };
33
- versionCreates.push(api
34
- .request("POST", `/v1beta1/sites/${deploy.site}/versions`, {
35
- origin: api.hostingApiOrigin,
36
- auth: true,
37
- data,
38
- })
39
- .then(function (result) {
40
- deploy.version = result.body.name;
36
+ versionCreates.push(client_1.client
37
+ .post(`/projects/${projectNumber}/sites/${deploy.site}/versions`, data)
38
+ .then((res) => {
39
+ deploy.version = res.body.name;
41
40
  }));
42
- });
43
- return Promise.all(versionCreates);
44
- };
41
+ }
42
+ await Promise.all(versionCreates);
43
+ }
44
+ exports.prepare = prepare;
@@ -1,34 +1,31 @@
1
1
  "use strict";
2
- const api = require("../../api");
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.release = void 0;
4
+ const client_1 = require("./client");
5
+ const logger_1 = require("../../logger");
6
+ const projectUtils_1 = require("../../projectUtils");
3
7
  const utils = require("../../utils");
4
- const { logger } = require("../../logger");
5
- module.exports = function (context, options) {
8
+ async function release(context, options) {
6
9
  if (!context.hosting || !context.hosting.deploys) {
7
- return Promise.resolve();
10
+ return;
8
11
  }
9
- logger.debug(JSON.stringify(context.hosting.deploys, null, 2));
10
- return Promise.all(context.hosting.deploys.map(async function (deploy) {
11
- utils.logLabeledBullet("hosting[" + deploy.site + "]", "finalizing version...");
12
- const finalizeResult = await api.request("PATCH", `/v1beta1/${deploy.version}?updateMask=status`, {
13
- origin: api.hostingApiOrigin,
14
- auth: true,
15
- data: { status: "FINALIZED" },
16
- });
17
- logger.debug("[hosting] finalized version for " + deploy.site + ":", finalizeResult.body);
18
- utils.logLabeledSuccess("hosting[" + deploy.site + "]", "version finalized");
19
- utils.logLabeledBullet("hosting[" + deploy.site + "]", "releasing new version...");
12
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
13
+ logger_1.logger.debug(JSON.stringify(context.hosting.deploys, null, 2));
14
+ await Promise.all(context.hosting.deploys.map(async (deploy) => {
15
+ utils.logLabeledBullet(`hosting[${deploy.site}]`, "finalizing version...");
16
+ const finalizeResult = await client_1.client.patch(`/${deploy.version}`, { status: "FINALIZED" }, { queryParams: { updateMask: "status" } });
17
+ logger_1.logger.debug(`[hosting] finalized version for ${deploy.site}:${finalizeResult.body}`);
18
+ utils.logLabeledSuccess(`hosting[${deploy.site}]`, "version finalized");
19
+ utils.logLabeledBullet(`hosting[${deploy.site}]`, "releasing new version...");
20
20
  const channelSegment = context.hostingChannel && context.hostingChannel !== "live"
21
21
  ? `/channels/${context.hostingChannel}`
22
22
  : "";
23
23
  if (channelSegment) {
24
- logger.debug("[hosting] releasing to channel:", context.hostingChannel);
24
+ logger_1.logger.debug("[hosting] releasing to channel:", context.hostingChannel);
25
25
  }
26
- const releaseResult = await api.request("POST", `/v1beta1/sites/${deploy.site}${channelSegment}/releases?version_name=${deploy.version}`, {
27
- auth: true,
28
- origin: api.hostingApiOrigin,
29
- data: { message: options.message || null },
30
- });
31
- logger.debug("[hosting] release:", releaseResult.body);
32
- utils.logLabeledSuccess("hosting[" + deploy.site + "]", "release complete");
26
+ const releaseResult = await client_1.client.post(`/projects/${projectNumber}/sites/${deploy.site}${channelSegment}/releases`, { message: options.message || null }, { queryParams: { versionName: deploy.version } });
27
+ logger_1.logger.debug("[hosting] release:", releaseResult.body);
28
+ utils.logLabeledSuccess(`hosting[${deploy.site}]`, "release complete");
33
29
  }));
34
- };
30
+ }
31
+ exports.release = release;
@@ -11,7 +11,7 @@ const zlib = require("zlib");
11
11
  const apiv2_1 = require("../../apiv2");
12
12
  const queue_1 = require("../../throttler/queue");
13
13
  const api_1 = require("../../api");
14
- const hashcache = require("./hashcache");
14
+ const hashcache_1 = require("./hashcache");
15
15
  const logger_1 = require("../../logger");
16
16
  const error_1 = require("../../error");
17
17
  const MIN_UPLOAD_TIMEOUT = 30000;
@@ -54,7 +54,7 @@ class Uploader {
54
54
  this.public = options.public || this.cwd;
55
55
  this.files = options.files;
56
56
  this.fileCount = this.files.length;
57
- this.cache = hashcache.load(this.projectRoot, this.hashcacheName());
57
+ this.cache = (0, hashcache_1.load)(this.projectRoot, this.hashcacheName());
58
58
  this.cacheNew = new Map();
59
59
  this.sizeMap = {};
60
60
  this.hashMap = {};
@@ -78,7 +78,7 @@ class Uploader {
78
78
  .wait()
79
79
  .then(this.queuePopulate.bind(this))
80
80
  .then(() => {
81
- hashcache.dump(this.projectRoot, this.hashcacheName(), this.cacheNew);
81
+ (0, hashcache_1.dump)(this.projectRoot, this.hashcacheName(), this.cacheNew);
82
82
  logger_1.logger.debug("[hosting][hash queue][FINAL]", this.hashQueue.stats());
83
83
  this.populateQueue.close();
84
84
  return this.populateQueue.wait();
@@ -91,7 +91,7 @@ class Uploader {
91
91
  this.uploadQueue.wait().catch((err) => {
92
92
  if (err.message.includes("content hash")) {
93
93
  logger_1.logger.debug("[hosting][upload queue] upload failed with content hash error. Deleting hash cache");
94
- hashcache.dump(this.projectRoot, this.hashcacheName(), new Map());
94
+ (0, hashcache_1.dump)(this.projectRoot, this.hashcacheName(), new Map());
95
95
  }
96
96
  });
97
97
  const fin = (err) => {
@@ -123,7 +123,7 @@ class Uploader {
123
123
  const stats = fs.statSync(path.resolve(this.public, filePath));
124
124
  const mtime = stats.mtime.getTime();
125
125
  this.sizeMap[filePath] = stats.size;
126
- const cached = this.cache[filePath];
126
+ const cached = this.cache.get(filePath);
127
127
  if (cached && cached.mtime === mtime) {
128
128
  this.cacheNew.set(filePath, cached);
129
129
  this.addHash(filePath, cached.hash);
@@ -20,10 +20,10 @@ async function getEtag(projectNumber, versionNumber) {
20
20
  exports.getEtag = getEtag;
21
21
  function validateInputRemoteConfigTemplate(template) {
22
22
  const templateCopy = JSON.parse(JSON.stringify(template));
23
- if (!templateCopy || templateCopy == "null" || templateCopy == "undefined") {
23
+ if (!templateCopy || templateCopy === "null" || templateCopy === "undefined") {
24
24
  throw new error_1.FirebaseError(`Invalid Remote Config template: ${JSON.stringify(templateCopy)}`);
25
25
  }
26
- if (typeof templateCopy.etag !== "string" || templateCopy.etag == "") {
26
+ if (typeof templateCopy.etag !== "string" || templateCopy.etag === "") {
27
27
  throw new error_1.FirebaseError("ETag must be a non-empty string");
28
28
  }
29
29
  if (templateCopy.conditions && !Array.isArray(templateCopy.conditions)) {
@@ -35,7 +35,7 @@ class AuthCloudFunction {
35
35
  catch (e) {
36
36
  err = e;
37
37
  }
38
- if (err || (res === null || res === void 0 ? void 0 : res.status) != 200) {
38
+ if (err || (res === null || res === void 0 ? void 0 : res.status) !== 200) {
39
39
  this.logger.logLabeled("WARN", "functions", `Firebase Authentication function was not triggered due to emulation error. Please file a bug.`);
40
40
  }
41
41
  }
@@ -1378,7 +1378,7 @@ function mfaSignInFinalize(state, reqBody) {
1378
1378
  (0, errors_1.assert)(sessionInfo, "MISSING_SESSION_INFO");
1379
1379
  const phoneNumber = verifyPhoneNumber(state, sessionInfo, code);
1380
1380
  let { user, signInProvider } = parsePendingCredential(state, reqBody.mfaPendingCredential);
1381
- const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo == phoneNumber);
1381
+ const enrollment = (_b = user.mfaInfo) === null || _b === void 0 ? void 0 : _b.find((enrollment) => enrollment.unobfuscatedPhoneInfo === phoneNumber);
1382
1382
  (0, errors_1.assert)(enrollment && enrollment.mfaEnrollmentId, "MFA_ENROLLMENT_NOT_FOUND");
1383
1383
  user = state.updateUserByLocalId(user.localId, { lastLoginAt: Date.now().toString() });
1384
1384
  (0, errors_1.assert)(!user.disabled, "USER_DISABLED");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.emulatorExec = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
3
+ exports.emulatorExec = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
4
4
  const clc = require("cli-color");
5
5
  const childProcess = require("child_process");
6
6
  const controller = require("../emulator/controller");
@@ -36,6 +36,7 @@ exports.DESC_EXPORT_ON_EXIT = "automatically export emulator data (emulators:exp
36
36
  `when no dir is provided the location of ${exports.FLAG_IMPORT} is used`;
37
37
  exports.EXPORT_ON_EXIT_USAGE_ERROR = `"${exports.FLAG_EXPORT_ON_EXIT_NAME}" must be used with "${exports.FLAG_IMPORT}"` +
38
38
  ` or provide a dir directly to "${exports.FLAG_EXPORT_ON_EXIT}"`;
39
+ exports.EXPORT_ON_EXIT_CWD_DANGER = `"${exports.FLAG_EXPORT_ON_EXIT_NAME}" must not point to the current directory or parents. Please choose a new/dedicated directory for exports.`;
39
40
  exports.FLAG_UI = "--ui";
40
41
  exports.DESC_UI = "run the Emulator UI";
41
42
  exports.FLAG_TEST_CONFIG = "--test-config <firebase.json file>";
@@ -131,6 +132,9 @@ function setExportOnExitOptions(options) {
131
132
  if (options.exportOnExit === true || !options.exportOnExit) {
132
133
  throw new error_1.FirebaseError(exports.EXPORT_ON_EXIT_USAGE_ERROR);
133
134
  }
135
+ if (path.resolve(".").startsWith(path.resolve(options.exportOnExit))) {
136
+ throw new error_1.FirebaseError(exports.EXPORT_ON_EXIT_CWD_DANGER);
137
+ }
134
138
  }
135
139
  return;
136
140
  }
@@ -8,6 +8,7 @@ const DEFAULT_PORTS = {
8
8
  logging: 4500,
9
9
  hosting: 5000,
10
10
  functions: 5001,
11
+ extensions: 5001,
11
12
  firestore: 8080,
12
13
  pubsub: 8085,
13
14
  database: 9000,
@@ -25,6 +26,7 @@ exports.FIND_AVAILBLE_PORT_BY_DEFAULT = {
25
26
  pubsub: false,
26
27
  auth: false,
27
28
  storage: false,
29
+ extensions: false,
28
30
  };
29
31
  exports.EMULATOR_DESCRIPTION = {
30
32
  ui: "Emulator UI",
@@ -37,6 +39,7 @@ exports.EMULATOR_DESCRIPTION = {
37
39
  pubsub: "Pub/Sub Emulator",
38
40
  auth: "Authentication Emulator",
39
41
  storage: "Storage Emulator",
42
+ extensions: "Extensions Emulator",
40
43
  };
41
44
  const DEFAULT_HOST = "localhost";
42
45
  class Constants {
@@ -87,6 +90,7 @@ class Constants {
87
90
  }
88
91
  exports.Constants = Constants;
89
92
  Constants.FAKE_PROJECT_ID_PREFIX = "demo-";
93
+ Constants.FAKE_PROJECT_NUMBER = "0";
90
94
  Constants.DEFAULT_DATABASE_EMULATOR_NAMESPACE = "fake-server";
91
95
  Constants.FIRESTORE_EMULATOR_HOST = "FIRESTORE_EMULATOR_HOST";
92
96
  Constants.FIREBASE_DATABASE_EMULATOR_HOST = "FIREBASE_DATABASE_EMULATOR_HOST";
@@ -33,10 +33,17 @@ const prompt_1 = require("../prompt");
33
33
  const commandUtils_1 = require("./commandUtils");
34
34
  const fsutils_1 = require("../fsutils");
35
35
  const storage_1 = require("./storage");
36
+ const config_1 = require("./storage/rules/config");
36
37
  const getDefaultDatabaseInstance_1 = require("../getDefaultDatabaseInstance");
37
38
  const auth_2 = require("../auth");
39
+ const extensionsEmulator_1 = require("./extensionsEmulator");
40
+ const previews_1 = require("../previews");
41
+ const START_LOGGING_EMULATOR = utils.envOverride("START_LOGGING_EMULATOR", "false", (val) => val === "true");
38
42
  async function getAndCheckAddress(emulator, options) {
39
43
  var _a, _b, _c, _d;
44
+ if (emulator === types_1.Emulators.EXTENSIONS) {
45
+ emulator = types_1.Emulators.FUNCTIONS;
46
+ }
40
47
  let host = ((_b = (_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a[emulator]) === null || _b === void 0 ? void 0 : _b.host) || constants_1.Constants.getDefaultHost(emulator);
41
48
  if (host === "localhost" && utils.isRunningInWSL()) {
42
49
  host = "127.0.0.1";
@@ -56,7 +63,7 @@ async function getAndCheckAddress(emulator, options) {
56
63
  if (!portOpen) {
57
64
  if (findAvailablePort) {
58
65
  const newPort = await portUtils.findAvailablePort(host, port);
59
- if (newPort != port) {
66
+ if (newPort !== port) {
60
67
  loggerForEmulator.logLabeled("WARN", emulator, `${constants_1.Constants.description(emulator)} unable to start on port ${port}, starting on ${newPort} instead.`);
61
68
  port = newPort;
62
69
  }
@@ -86,7 +93,7 @@ async function getAndCheckAddress(emulator, options) {
86
93
  }
87
94
  async function startEmulator(instance) {
88
95
  const name = instance.getName();
89
- track("Emulator Run", name);
96
+ void track("Emulator Run", name);
90
97
  await registry_1.EmulatorRegistry.start(instance);
91
98
  }
92
99
  exports.startEmulator = startEmulator;
@@ -115,7 +122,11 @@ async function cleanShutdown() {
115
122
  }
116
123
  exports.cleanShutdown = cleanShutdown;
117
124
  function filterEmulatorTargets(options) {
118
- let targets = types_1.ALL_SERVICE_EMULATORS.filter((e) => {
125
+ let targets = [...types_1.ALL_SERVICE_EMULATORS];
126
+ if (previews_1.previews.extensionsemulator) {
127
+ targets.push(types_1.Emulators.EXTENSIONS);
128
+ }
129
+ targets = targets.filter((e) => {
119
130
  return options.config.has(e) || options.config.has(`emulators.${e}`);
120
131
  });
121
132
  const onlyOptions = options.only;
@@ -222,7 +233,7 @@ async function startAll(options, showUI = true) {
222
233
  if (shouldStart(options, types_1.Emulators.HUB)) {
223
234
  const hubAddr = await getAndCheckAddress(types_1.Emulators.HUB, options);
224
235
  const hub = new hub_1.EmulatorHub(Object.assign({ projectId }, hubAddr));
225
- track("emulators:start", "hub");
236
+ void track("emulators:start", "hub");
226
237
  await startEmulator(hub);
227
238
  }
228
239
  let exportMetadata = {
@@ -239,19 +250,47 @@ async function startAll(options, showUI = true) {
239
250
  hubLogger.logLabeled("WARN", "emulators", `Could not find import/export metadata file, ${clc.bold("skipping data import!")}`);
240
251
  }
241
252
  }
253
+ const emulatableBackends = [];
254
+ const projectDir = (options.extDevDir || options.config.projectDir);
242
255
  if (shouldStart(options, types_1.Emulators.FUNCTIONS)) {
243
- const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
244
- const functionsAddr = await getAndCheckAddress(types_1.Emulators.FUNCTIONS, options);
245
- const projectId = (0, projectUtils_1.needProjectId)(options);
246
256
  utils.assertDefined(options.config.src.functions);
247
257
  utils.assertDefined(options.config.src.functions.source, "Error: 'functions.source' is not defined");
248
- utils.assertIsStringOrUndefined(options.extensionDir);
249
- const projectDir = options.extensionDir || options.config.projectDir;
258
+ utils.assertIsStringOrUndefined(options.extDevDir);
250
259
  const functionsDir = path.join(projectDir, options.config.src.functions.source);
260
+ emulatableBackends.push({
261
+ functionsDir,
262
+ env: Object.assign({}, options.extDevEnv),
263
+ secretEnv: [],
264
+ predefinedTriggers: options.extDevTriggers,
265
+ nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || options.config.get("functions.runtime")),
266
+ });
267
+ }
268
+ if (shouldStart(options, types_1.Emulators.EXTENSIONS) && previews_1.previews.extensionsemulator) {
269
+ const projectNumber = constants_1.Constants.isDemoProject(projectId)
270
+ ? constants_1.Constants.FAKE_PROJECT_NUMBER
271
+ : await (0, projectUtils_1.needProjectNumber)(options);
272
+ const aliases = (0, projectUtils_1.getAliases)(options, projectId);
273
+ const extensionEmulator = new extensionsEmulator_1.ExtensionsEmulator({
274
+ projectId,
275
+ projectDir: options.config.projectDir,
276
+ projectNumber,
277
+ aliases,
278
+ extensions: options.config.get("extensions"),
279
+ });
280
+ const extensionsBackends = await extensionEmulator.getExtensionBackends();
281
+ const filteredExtensionsBackends = extensionEmulator.filterUnemulatedTriggers(options, extensionsBackends);
282
+ emulatableBackends.push(...filteredExtensionsBackends);
283
+ void track("Emulator Run", types_1.Emulators.EXTENSIONS);
284
+ registry_1.EmulatorRegistry.registerExtensionsEmulator();
285
+ }
286
+ if (emulatableBackends.length) {
287
+ const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
288
+ const functionsAddr = await getAndCheckAddress(types_1.Emulators.FUNCTIONS, options);
289
+ const projectId = (0, projectUtils_1.needProjectId)(options);
251
290
  let inspectFunctions;
252
291
  if (options.inspectFunctions) {
253
292
  inspectFunctions = commandUtils.parseInspectionPort(options);
254
- functionsLogger.logLabeled("WARN", "functions", `You are running the functions emulator in debug mode (port=${inspectFunctions}). This means that functions will execute in sequence rather than in parallel.`);
293
+ functionsLogger.logLabeled("WARN", "functions", `You are running the Functions emulator in debug mode (port=${inspectFunctions}). This means that functions will execute in sequence rather than in parallel.`);
255
294
  }
256
295
  const emulatorsNotRunning = types_1.ALL_SERVICE_EMULATORS.filter((e) => {
257
296
  return e !== types_1.Emulators.FUNCTIONS && !shouldStart(options, e);
@@ -260,14 +299,6 @@ async function startAll(options, showUI = true) {
260
299
  functionsLogger.logLabeled("WARN", "functions", `The following emulators are not running, calls to these services from the Functions emulator will affect production: ${clc.bold(emulatorsNotRunning.join(", "))}`);
261
300
  }
262
301
  const account = (0, auth_2.getProjectDefaultAccount)(options.projectRoot);
263
- const emulatableBackends = [
264
- {
265
- functionsDir,
266
- env: Object.assign({}, options.extensionEnv),
267
- predefinedTriggers: options.extensionTriggers,
268
- nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extensionNodeVersion || options.config.get("functions.runtime")),
269
- },
270
- ];
271
302
  const functionsEmulator = new functionsEmulator_1.FunctionsEmulator({
272
303
  projectId,
273
304
  projectDir,
@@ -276,6 +307,7 @@ async function startAll(options, showUI = true) {
276
307
  host: functionsAddr.host,
277
308
  port: functionsAddr.port,
278
309
  debugPort: inspectFunctions,
310
+ projectAlias: options.projectAlias,
279
311
  });
280
312
  await startEmulator(functionsEmulator);
281
313
  }
@@ -395,15 +427,11 @@ async function startAll(options, showUI = true) {
395
427
  }
396
428
  if (shouldStart(options, types_1.Emulators.STORAGE)) {
397
429
  const storageAddr = await getAndCheckAddress(types_1.Emulators.STORAGE, options);
398
- const storageConfig = options.config.data.storage;
399
- if (!(storageConfig === null || storageConfig === void 0 ? void 0 : storageConfig.rules)) {
400
- throw new error_1.FirebaseError("Cannot start the Storage emulator without rules file specified in firebase.json: run 'firebase init' and set up your Storage configuration");
401
- }
402
430
  const storageEmulator = new storage_1.StorageEmulator({
403
431
  host: storageAddr.host,
404
432
  port: storageAddr.port,
405
433
  projectId: projectId,
406
- rules: options.config.path(storageConfig.rules),
434
+ rules: (0, config_1.getStorageRulesConfig)(projectId, options),
407
435
  });
408
436
  await startEmulator(storageEmulator);
409
437
  if (exportMetadata.storage) {
@@ -425,13 +453,15 @@ async function startAll(options, showUI = true) {
425
453
  if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
426
454
  hubLogger.logLabeled("WARN", "emulators", "The Emulator UI requires a project ID to start. Configure your default project with 'firebase use' or pass the --project flag.");
427
455
  }
428
- if (showUI && shouldStart(options, types_1.Emulators.UI)) {
456
+ if (showUI && (shouldStart(options, types_1.Emulators.UI) || START_LOGGING_EMULATOR)) {
429
457
  const loggingAddr = await getAndCheckAddress(types_1.Emulators.LOGGING, options);
430
458
  const loggingEmulator = new loggingEmulator_1.LoggingEmulator({
431
459
  host: loggingAddr.host,
432
460
  port: loggingAddr.port,
433
461
  });
434
462
  await startEmulator(loggingEmulator);
463
+ }
464
+ if (showUI && shouldStart(options, types_1.Emulators.UI)) {
435
465
  const uiAddr = await getAndCheckAddress(types_1.Emulators.UI, options);
436
466
  const ui = new ui_1.EmulatorUI(Object.assign({ projectId: projectId, auto_download: true }, uiAddr));
437
467
  await startEmulator(ui);