firebase-tools 10.2.1 → 10.3.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 (125) hide show
  1. package/lib/appdistribution/options-parser-util.js +1 -1
  2. package/lib/auth.js +3 -3
  3. package/lib/command.js +1 -1
  4. package/lib/commands/apps-android-sha-create.js +2 -2
  5. package/lib/commands/apps-sdkconfig.js +1 -1
  6. package/lib/commands/database-rules-list.js +2 -2
  7. package/lib/commands/emulators-start.js +1 -1
  8. package/lib/commands/ext-configure.js +67 -7
  9. package/lib/commands/ext-dev-init.js +49 -49
  10. package/lib/commands/ext-export.js +7 -2
  11. package/lib/commands/ext-install.js +173 -109
  12. package/lib/commands/ext-uninstall.js +17 -8
  13. package/lib/commands/ext-update.js +67 -12
  14. package/lib/commands/functions-config-export.js +1 -1
  15. package/lib/commands/hosting-clone.js +3 -3
  16. package/lib/commands/remoteconfig-get.js +1 -1
  17. package/lib/config.js +6 -3
  18. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  19. package/lib/deploy/extensions/planner.js +7 -6
  20. package/lib/deploy/extensions/tasks.js +1 -1
  21. package/lib/deploy/functions/backend.js +21 -5
  22. package/lib/deploy/functions/checkIam.js +5 -5
  23. package/lib/deploy/functions/containerCleaner.js +3 -3
  24. package/lib/deploy/functions/ensure.js +3 -3
  25. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  26. package/lib/deploy/functions/prepare.js +5 -3
  27. package/lib/deploy/functions/pricing.js +1 -1
  28. package/lib/deploy/functions/prompts.js +2 -2
  29. package/lib/deploy/functions/release/fabricator.js +7 -7
  30. package/lib/deploy/functions/release/index.js +1 -1
  31. package/lib/deploy/functions/release/planner.js +43 -26
  32. package/lib/deploy/functions/release/reporter.js +3 -0
  33. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  34. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  36. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  37. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  42. package/lib/deploy/functions/services/index.js +9 -1
  43. package/lib/deploy/functions/services/storage.js +10 -4
  44. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  45. package/lib/deploy/functions/validate.js +3 -3
  46. package/lib/deploy/hosting/deploy.js +2 -2
  47. package/lib/deploy/hosting/hashcache.js +21 -19
  48. package/lib/deploy/hosting/uploader.js +5 -5
  49. package/lib/deploy/remoteconfig/functions.js +2 -2
  50. package/lib/emulator/auth/cloudFunctions.js +1 -1
  51. package/lib/emulator/auth/operations.js +1 -1
  52. package/lib/emulator/constants.js +4 -0
  53. package/lib/emulator/controller.js +54 -24
  54. package/lib/emulator/download.js +18 -1
  55. package/lib/emulator/downloadableEmulators.js +1 -1
  56. package/lib/emulator/emulatorLogger.js +12 -1
  57. package/lib/emulator/extensions/validation.js +70 -0
  58. package/lib/emulator/extensionsEmulator.js +175 -0
  59. package/lib/emulator/functionsEmulator.js +95 -43
  60. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  61. package/lib/emulator/functionsEmulatorShared.js +30 -11
  62. package/lib/emulator/functionsEmulatorShell.js +1 -1
  63. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  64. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  65. package/lib/emulator/hub.js +4 -3
  66. package/lib/emulator/loggingEmulator.js +1 -1
  67. package/lib/emulator/pubsubEmulator.js +1 -1
  68. package/lib/emulator/registry.js +10 -2
  69. package/lib/emulator/storage/apis/firebase.js +314 -332
  70. package/lib/emulator/storage/apis/gcloud.js +241 -121
  71. package/lib/emulator/storage/crc.js +5 -1
  72. package/lib/emulator/storage/errors.js +9 -0
  73. package/lib/emulator/storage/files.js +159 -300
  74. package/lib/emulator/storage/index.js +27 -73
  75. package/lib/emulator/storage/metadata.js +65 -51
  76. package/lib/emulator/storage/multipart.js +62 -0
  77. package/lib/emulator/storage/persistence.js +78 -0
  78. package/lib/emulator/storage/rules/config.js +33 -0
  79. package/lib/emulator/storage/rules/manager.js +81 -0
  80. package/lib/emulator/storage/rules/runtime.js +8 -7
  81. package/lib/emulator/storage/rules/utils.js +48 -0
  82. package/lib/emulator/storage/server.js +2 -2
  83. package/lib/emulator/storage/upload.js +106 -0
  84. package/lib/emulator/types.js +3 -0
  85. package/lib/ensureApiEnabled.js +5 -1
  86. package/lib/error.js +1 -1
  87. package/lib/extensions/askUserForParam.js +31 -25
  88. package/lib/extensions/changelog.js +3 -1
  89. package/lib/extensions/checkProjectBilling.js +1 -1
  90. package/lib/extensions/displayExtensionInfo.js +1 -1
  91. package/lib/extensions/emulator/optionsHelper.js +56 -8
  92. package/lib/extensions/emulator/specHelper.js +10 -23
  93. package/lib/extensions/export.js +1 -51
  94. package/lib/extensions/extensionsApi.js +1 -1
  95. package/lib/extensions/extensionsHelper.js +32 -19
  96. package/lib/extensions/manifest.js +144 -0
  97. package/lib/extensions/metricsUtils.js +4 -4
  98. package/lib/extensions/paramHelper.js +34 -12
  99. package/lib/extensions/refs.js +1 -1
  100. package/lib/extensions/secretsUtils.js +3 -3
  101. package/lib/functional.js +1 -1
  102. package/lib/functions/env.js +6 -7
  103. package/lib/functions/events/v2.js +11 -0
  104. package/lib/gcp/cloudfunctions.js +43 -11
  105. package/lib/gcp/cloudfunctionsv2.js +48 -17
  106. package/lib/gcp/cloudtasks.js +1 -1
  107. package/lib/gcp/docker.js +2 -2
  108. package/lib/gcp/resourceManager.js +4 -4
  109. package/lib/gcp/run.js +2 -2
  110. package/lib/hosting/api.js +1 -1
  111. package/lib/hosting/proxy.js +2 -2
  112. package/lib/init/features/account.js +1 -1
  113. package/lib/management/database.js +1 -1
  114. package/lib/previews.js +1 -1
  115. package/lib/serve/functions.js +2 -1
  116. package/lib/utils.js +15 -2
  117. package/npm-shrinkwrap.json +786 -393
  118. package/package.json +1 -1
  119. package/schema/firebase-config.json +5 -0
  120. package/templates/init/functions/javascript/package.lint.json +3 -3
  121. package/templates/init/functions/javascript/package.nolint.json +2 -2
  122. package/templates/init/functions/typescript/package.lint.json +7 -7
  123. package/templates/init/functions/typescript/package.nolint.json +3 -3
  124. package/lib/deploy/extensions/params.js +0 -39
  125. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -71,7 +71,7 @@ class Delegate {
71
71
  },
72
72
  stdio: ["ignore", "pipe", "pipe"],
73
73
  });
74
- if (genBinary.status != 0) {
74
+ if (genBinary.status !== 0) {
75
75
  throw new error_1.FirebaseError("Failed to run codegen", {
76
76
  children: [new Error(genBinary.stderr.toString())],
77
77
  });
@@ -96,7 +96,7 @@ class Delegate {
96
96
  childProcess.once("exit", resolve);
97
97
  childProcess.once("error", reject);
98
98
  });
99
- await (0, node_fetch_1.default)(`http://localhost:${adminPort}/quitquitquit`);
99
+ await (0, node_fetch_1.default)(`http://localhost:${adminPort}/__/quitquitquit`);
100
100
  setTimeout(() => {
101
101
  if (!childProcess.killed) {
102
102
  childProcess.kill("SIGKILL");
@@ -4,12 +4,20 @@ exports.Delegate = exports.tryCreateDelegate = void 0;
4
4
  const util_1 = require("util");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
+ const portfinder = require("portfinder");
8
+ const semver = require("semver");
9
+ const spawn = require("cross-spawn");
10
+ const node_fetch_1 = require("node-fetch");
7
11
  const error_1 = require("../../../../error");
8
12
  const parseRuntimeAndValidateSDK_1 = require("./parseRuntimeAndValidateSDK");
9
13
  const logger_1 = require("../../../../logger");
14
+ const previews_1 = require("../../../../previews");
15
+ const utils_1 = require("../../../../utils");
16
+ const discovery = require("../discovery");
10
17
  const validate = require("./validate");
11
18
  const versioning = require("./versioning");
12
19
  const parseTriggers = require("./parseTriggers");
20
+ const MIN_FUNCTIONS_SDK_VERSION = "3.19.0";
13
21
  async function tryCreateDelegate(context) {
14
22
  const packageJsonPath = path.join(context.sourceDir, "package.json");
15
23
  if (!(await (0, util_1.promisify)(fs.exists)(packageJsonPath))) {
@@ -50,7 +58,52 @@ class Delegate {
50
58
  watch() {
51
59
  return Promise.resolve(() => Promise.resolve());
52
60
  }
61
+ serve(port, envs) {
62
+ var _a;
63
+ const childProcess = spawn("./node_modules/.bin/firebase-functions", [this.sourceDir], {
64
+ env: Object.assign(Object.assign({}, envs), { PORT: port.toString(), FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH }),
65
+ cwd: this.sourceDir,
66
+ stdio: ["ignore", "pipe", "inherit"],
67
+ });
68
+ (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
69
+ logger_1.logger.debug(chunk.toString());
70
+ });
71
+ return Promise.resolve(async () => {
72
+ const p = new Promise((resolve, reject) => {
73
+ childProcess.once("exit", resolve);
74
+ childProcess.once("error", reject);
75
+ });
76
+ await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
77
+ setTimeout(() => {
78
+ if (!childProcess.killed) {
79
+ childProcess.kill("SIGKILL");
80
+ }
81
+ }, 10000);
82
+ return p;
83
+ });
84
+ }
53
85
  async discoverSpec(config, env) {
86
+ if (previews_1.previews.functionsv2) {
87
+ if (semver.lt(this.sdkVersion, MIN_FUNCTIONS_SDK_VERSION)) {
88
+ (0, utils_1.logLabeledWarning)("functions", `You are using an old version of firebase-functions SDK (${this.sdkVersion}). ` +
89
+ `Please update firebase-functions SDK to >=${MIN_FUNCTIONS_SDK_VERSION}`);
90
+ return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
91
+ }
92
+ let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
93
+ if (!discovered) {
94
+ const getPort = (0, util_1.promisify)(portfinder.getPort);
95
+ const port = await getPort();
96
+ const kill = await this.serve(port, env);
97
+ try {
98
+ discovered = await discovery.detectFromPort(port, this.projectId, this.runtime);
99
+ }
100
+ finally {
101
+ await kill();
102
+ }
103
+ }
104
+ discovered.environmentVariables = env;
105
+ return discovered;
106
+ }
54
107
  return parseTriggers.discoverBackend(this.projectId, this.sourceDir, this.runtime, config, env);
55
108
  }
56
109
  }
@@ -46,11 +46,11 @@ function getRuntimeChoice(sourceDir, runtimeFromConfig) {
46
46
  ? exports.UNSUPPORTED_NODE_VERSION_FIREBASE_JSON_MSG
47
47
  : exports.UNSUPPORTED_NODE_VERSION_PACKAGE_JSON_MSG) + exports.DEPRECATED_NODE_VERSION_INFO;
48
48
  if (!runtime || !ENGINE_RUNTIMES_NAMES.includes(runtime)) {
49
- track("functions_runtime_notices", "package_missing_runtime");
49
+ void track("functions_runtime_notices", "package_missing_runtime");
50
50
  throw new error_1.FirebaseError(errorMessage, { exit: 1 });
51
51
  }
52
52
  if (runtimes.isDeprecatedRuntime(runtime) || !runtimes.isValidRuntime(runtime)) {
53
- track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
53
+ void track("functions_runtime_notices", `${runtime}_deploy_prohibited`);
54
54
  throw new error_1.FirebaseError(errorMessage, { exit: 1 });
55
55
  }
56
56
  return runtime;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.addResourcesToBackend = exports.discoverBackend = exports.useStrategy = void 0;
3
+ exports.addResourcesToBackend = exports.mergeRequiredAPIs = exports.discoverBackend = exports.useStrategy = void 0;
4
4
  const path = require("path");
5
5
  const _ = require("lodash");
6
6
  const child_process_1 = require("child_process");
@@ -9,7 +9,7 @@ const logger_1 = require("../../../../logger");
9
9
  const backend = require("../../backend");
10
10
  const api = require("../../../../api");
11
11
  const proto = require("../../../../gcp/proto");
12
- const eventTypes_1 = require("../../eventTypes");
12
+ const v2events = require("../../../../functions/events/v2");
13
13
  const TRIGGER_PARSER = path.resolve(__dirname, "./triggerParser.js");
14
14
  function removeInspectOptions(options) {
15
15
  return options.filter((opt) => !opt.startsWith("--inspect"));
@@ -58,45 +58,87 @@ async function discoverBackend(projectId, sourceDir, runtime, configValues, envs
58
58
  return want;
59
59
  }
60
60
  exports.discoverBackend = discoverBackend;
61
+ function mergeRequiredAPIs(backend) {
62
+ const apiToReasons = {};
63
+ for (const { api, reason } of backend.requiredAPIs) {
64
+ const reasons = apiToReasons[api] || new Set();
65
+ reasons.add(reason);
66
+ apiToReasons[api] = reasons;
67
+ }
68
+ const merged = [];
69
+ for (const [api, reasons] of Object.entries(apiToReasons)) {
70
+ merged.push({ api, reason: Array.from(reasons).join(" ") });
71
+ }
72
+ backend.requiredAPIs = merged;
73
+ }
74
+ exports.mergeRequiredAPIs = mergeRequiredAPIs;
61
75
  function addResourcesToBackend(projectId, runtime, annotation, want) {
76
+ var _a;
62
77
  Object.freeze(annotation);
63
78
  for (const region of annotation.regions || [api.functionsDefaultRegion]) {
64
79
  let triggered;
65
80
  const triggerCount = +!!annotation.httpsTrigger + +!!annotation.eventTrigger + +!!annotation.taskQueueTrigger;
66
- if (triggerCount != 1) {
81
+ if (triggerCount !== 1) {
67
82
  throw new error_1.FirebaseError("Unexpected annotation generated by the Firebase Functions SDK. This should never happen.");
68
83
  }
69
84
  if (annotation.taskQueueTrigger) {
70
85
  triggered = { taskQueueTrigger: annotation.taskQueueTrigger };
71
- want.requiredAPIs["cloudtasks"] = "cloudtasks.googleapis.com";
86
+ want.requiredAPIs.push({
87
+ api: "cloudtasks.googleapis.com",
88
+ reason: "Needed for task queue functions.",
89
+ });
72
90
  }
73
91
  else if (annotation.httpsTrigger) {
74
- const trigger = {};
75
- if (annotation.failurePolicy) {
76
- 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 };
77
103
  }
78
- proto.copyIfPresent(trigger, annotation.httpsTrigger, "invoker");
79
- triggered = { httpsTrigger: trigger };
80
104
  }
81
105
  else if (annotation.schedule) {
82
- want.requiredAPIs["pubsub"] = "pubsub.googleapis.com";
83
- want.requiredAPIs["scheduler"] = "cloudscheduler.googleapis.com";
106
+ want.requiredAPIs.push({
107
+ api: "cloudscheduler.googleapis.com",
108
+ reason: "Needed for scheduled functions.",
109
+ });
84
110
  triggered = { scheduleTrigger: annotation.schedule };
85
111
  }
86
112
  else {
87
113
  triggered = {
88
114
  eventTrigger: {
89
115
  eventType: annotation.eventTrigger.eventType,
90
- eventFilters: {
91
- resource: annotation.eventTrigger.resource,
92
- },
116
+ eventFilters: [
117
+ {
118
+ attribute: "resource",
119
+ value: annotation.eventTrigger.resource,
120
+ },
121
+ ],
93
122
  retry: !!annotation.failurePolicy,
94
123
  },
95
124
  };
96
- if (eventTypes_1.STORAGE_V2_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
97
- triggered.eventTrigger.eventFilters = {
98
- bucket: annotation.eventTrigger.resource,
99
- };
125
+ if (annotation.platform === "gcfv2") {
126
+ if (annotation.eventTrigger.eventType === v2events.PUBSUB_PUBLISH_EVENT) {
127
+ triggered.eventTrigger.eventFilters = [
128
+ {
129
+ attribute: "topic",
130
+ value: annotation.eventTrigger.resource,
131
+ },
132
+ ];
133
+ }
134
+ if (v2events.STORAGE_EVENTS.find((event) => { var _a; return event === (((_a = annotation.eventTrigger) === null || _a === void 0 ? void 0 : _a.eventType) || ""); })) {
135
+ triggered.eventTrigger.eventFilters = [
136
+ {
137
+ attribute: "bucket",
138
+ value: annotation.eventTrigger.resource,
139
+ },
140
+ ];
141
+ }
100
142
  }
101
143
  }
102
144
  const endpoint = Object.assign({ platform: annotation.platform || "gcfv1", id: annotation.name, region: region, project: projectId, entryPoint: annotation.entryPoint, runtime: runtime }, triggered);
@@ -105,7 +147,8 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
105
147
  if (maybeId && !maybeId.includes("/")) {
106
148
  maybeId = `projects/${projectId}/locations/${region}/connectors/${maybeId}`;
107
149
  }
108
- endpoint.vpcConnector = maybeId;
150
+ endpoint.vpc = { connector: maybeId };
151
+ proto.renameIfPresent(endpoint.vpc, annotation, "egressSettings", "vpcConnectorEgressSettings");
109
152
  }
110
153
  if (annotation.secrets) {
111
154
  const secretEnvs = [];
@@ -119,9 +162,10 @@ function addResourcesToBackend(projectId, runtime, annotation, want) {
119
162
  }
120
163
  endpoint.secretEnvironmentVariables = secretEnvs;
121
164
  }
122
- proto.copyIfPresent(endpoint, annotation, "concurrency", "serviceAccountEmail", "labels", "vpcConnectorEgressSettings", "ingressSettings", "timeout", "maxInstances", "minInstances", "availableMemoryMb");
165
+ proto.copyIfPresent(endpoint, annotation, "concurrency", "serviceAccountEmail", "labels", "ingressSettings", "timeout", "maxInstances", "minInstances", "availableMemoryMb");
123
166
  want.endpoints[region] = want.endpoints[region] || {};
124
167
  want.endpoints[region][endpoint.id] = endpoint;
168
+ mergeRequiredAPIs(want);
125
169
  }
126
170
  }
127
171
  exports.addResourcesToBackend = addResourcesToBackend;
@@ -52,7 +52,7 @@ exports.getLatestSDKVersion = getLatestSDKVersion;
52
52
  function checkFunctionsSDKVersion(currentVersion) {
53
53
  try {
54
54
  if (semver.lt(currentVersion, MIN_SDK_VERSION)) {
55
- track("functions_runtime_notices", "functions_sdk_too_old");
55
+ void track("functions_runtime_notices", "functions_sdk_too_old");
56
56
  utils.logWarning(exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING);
57
57
  }
58
58
  const latest = exports.getLatestSDKVersion();
@@ -63,7 +63,7 @@ function checkFunctionsSDKVersion(currentVersion) {
63
63
  return;
64
64
  }
65
65
  utils.logWarning(clc.bold.yellow("functions: ") +
66
- "package.json indicates an outdated version of firebase-functions.\nPlease upgrade using " +
66
+ "package.json indicates an outdated version of firebase-functions. Please upgrade using " +
67
67
  clc.bold("npm install --save firebase-functions@latest") +
68
68
  " in your functions directory.");
69
69
  if (semver.major(currentVersion) < semver.major(latest)) {
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureFirebaseAlertsTriggerRegion = exports.obtainFirebaseAlertsBindings = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
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
+ function ensureFirebaseAlertsTriggerRegion(endpoint) {
22
+ if (!endpoint.eventTrigger.region) {
23
+ endpoint.eventTrigger.region = "global";
24
+ }
25
+ if (endpoint.eventTrigger.region !== "global") {
26
+ throw new error_1.FirebaseError("A firebase alerts trigger must specify 'global' trigger location");
27
+ }
28
+ return Promise.resolve();
29
+ }
30
+ exports.ensureFirebaseAlertsTriggerRegion = ensureFirebaseAlertsTriggerRegion;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serviceForEndpoint = exports.EVENT_SERVICE_MAPPING = exports.StorageService = exports.PubSubService = exports.NoOpService = void 0;
3
+ exports.serviceForEndpoint = exports.EVENT_SERVICE_MAPPING = exports.FirebaseAlertsService = exports.StorageService = exports.PubSubService = exports.NoOpService = void 0;
4
4
  const backend = require("../backend");
5
5
  const storage_1 = require("./storage");
6
+ const firebaseAlerts_1 = require("./firebaseAlerts");
6
7
  const noop = () => Promise.resolve();
7
8
  exports.NoOpService = {
8
9
  name: "noop",
@@ -22,12 +23,19 @@ exports.StorageService = {
22
23
  requiredProjectBindings: storage_1.obtainStorageBindings,
23
24
  ensureTriggerRegion: storage_1.ensureStorageTriggerRegion,
24
25
  };
26
+ exports.FirebaseAlertsService = {
27
+ name: "firebasealerts",
28
+ api: "logging.googleapis.com",
29
+ requiredProjectBindings: firebaseAlerts_1.obtainFirebaseAlertsBindings,
30
+ ensureTriggerRegion: firebaseAlerts_1.ensureFirebaseAlertsTriggerRegion,
31
+ };
25
32
  exports.EVENT_SERVICE_MAPPING = {
26
33
  "google.cloud.pubsub.topic.v1.messagePublished": exports.PubSubService,
27
34
  "google.cloud.storage.object.v1.finalized": exports.StorageService,
28
35
  "google.cloud.storage.object.v1.archived": exports.StorageService,
29
36
  "google.cloud.storage.object.v1.deleted": exports.StorageService,
30
37
  "google.cloud.storage.object.v1.metadataUpdated": exports.StorageService,
38
+ "google.firebase.firebasealerts.alerts.v1.published": exports.FirebaseAlertsService,
31
39
  };
32
40
  function serviceForEndpoint(endpoint) {
33
41
  if (!backend.isEventTriggered(endpoint)) {
@@ -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;
@@ -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;
@@ -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");
@@ -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";