firebase-tools 11.4.2 → 11.7.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 (202) hide show
  1. package/lib/accountImporter.js +1 -1
  2. package/lib/auth.js +3 -4
  3. package/lib/bin/firebase.js +4 -4
  4. package/lib/command.js +35 -10
  5. package/lib/commands/apps-android-sha-create.js +1 -1
  6. package/lib/commands/apps-android-sha-delete.js +1 -1
  7. package/lib/commands/apps-create.js +1 -1
  8. package/lib/commands/apps-list.js +1 -1
  9. package/lib/commands/auth-export.js +1 -1
  10. package/lib/commands/auth-import.js +1 -1
  11. package/lib/commands/crashlytics-mappingfile-generateid.js +26 -0
  12. package/lib/commands/crashlytics-mappingfile-upload.js +46 -0
  13. package/lib/commands/crashlytics-symbols-upload.js +18 -87
  14. package/lib/commands/database-instances-list.js +1 -1
  15. package/lib/commands/database-push.js +1 -1
  16. package/lib/commands/database-remove.js +1 -1
  17. package/lib/commands/database-set.js +1 -1
  18. package/lib/commands/database-update.js +1 -1
  19. package/lib/commands/emulators-exec.js +4 -1
  20. package/lib/commands/emulators-export.js +5 -2
  21. package/lib/commands/emulators-start.js +24 -18
  22. package/lib/commands/ext-dev-deprecate.js +1 -1
  23. package/lib/commands/ext-dev-emulators-exec.js +1 -1
  24. package/lib/commands/ext-dev-emulators-start.js +1 -1
  25. package/lib/commands/ext-dev-extension-delete.js +1 -1
  26. package/lib/commands/ext-dev-list.js +1 -1
  27. package/lib/commands/ext-dev-publish.js +4 -1
  28. package/lib/commands/ext-dev-register.js +1 -1
  29. package/lib/commands/ext-dev-undeprecate.js +1 -1
  30. package/lib/commands/ext-dev-unpublish.js +1 -1
  31. package/lib/commands/ext-dev-usage.js +1 -1
  32. package/lib/commands/ext-info.js +1 -1
  33. package/lib/commands/ext-install.js +2 -2
  34. package/lib/commands/ext-update.js +1 -1
  35. package/lib/commands/ext.js +1 -1
  36. package/lib/commands/firestore-delete.js +2 -2
  37. package/lib/commands/firestore-indexes-list.js +3 -3
  38. package/lib/commands/functions-config-clone.js +1 -1
  39. package/lib/commands/functions-config-export.js +1 -1
  40. package/lib/commands/functions-config-set.js +1 -1
  41. package/lib/commands/functions-config-unset.js +1 -1
  42. package/lib/commands/functions-delete.js +3 -1
  43. package/lib/commands/functions-secrets-get.js +2 -0
  44. package/lib/commands/functions-secrets-set.js +1 -1
  45. package/lib/commands/help.js +1 -1
  46. package/lib/commands/hosting-channel-create.js +5 -5
  47. package/lib/commands/hosting-channel-delete.js +3 -3
  48. package/lib/commands/hosting-channel-deploy.js +6 -6
  49. package/lib/commands/hosting-channel-list.js +2 -2
  50. package/lib/commands/hosting-channel-open.js +2 -2
  51. package/lib/commands/hosting-clone.js +8 -8
  52. package/lib/commands/hosting-disable.js +1 -1
  53. package/lib/commands/hosting-sites-create.js +4 -4
  54. package/lib/commands/hosting-sites-delete.js +4 -4
  55. package/lib/commands/hosting-sites-list.js +2 -2
  56. package/lib/commands/index.js +3 -0
  57. package/lib/commands/init.js +5 -5
  58. package/lib/commands/login-add.js +1 -1
  59. package/lib/commands/login-ci.js +2 -2
  60. package/lib/commands/login-list.js +1 -1
  61. package/lib/commands/login-use.js +1 -1
  62. package/lib/commands/login.js +3 -3
  63. package/lib/commands/logout.js +1 -1
  64. package/lib/commands/open.js +3 -3
  65. package/lib/commands/projects-list.js +2 -2
  66. package/lib/commands/serve.js +1 -1
  67. package/lib/commands/target-apply.js +1 -1
  68. package/lib/commands/target-clear.js +1 -1
  69. package/lib/commands/target-remove.js +1 -1
  70. package/lib/commands/target.js +1 -1
  71. package/lib/commands/use.js +7 -7
  72. package/lib/config.js +1 -1
  73. package/lib/crashlytics/buildToolsJarHelper.js +51 -0
  74. package/lib/deploy/database/prepare.js +3 -3
  75. package/lib/deploy/database/release.js +3 -3
  76. package/lib/deploy/extensions/deploymentSummary.js +1 -1
  77. package/lib/deploy/extensions/errors.js +1 -1
  78. package/lib/deploy/extensions/secrets.js +1 -1
  79. package/lib/deploy/extensions/tasks.js +2 -2
  80. package/lib/deploy/firestore/deploy.js +2 -2
  81. package/lib/deploy/firestore/prepare.js +2 -2
  82. package/lib/deploy/functions/backend.js +4 -4
  83. package/lib/deploy/functions/build.js +78 -8
  84. package/lib/deploy/functions/checkIam.js +9 -8
  85. package/lib/deploy/functions/containerCleaner.js +2 -2
  86. package/lib/deploy/functions/deploy.js +2 -2
  87. package/lib/deploy/functions/ensure.js +1 -1
  88. package/lib/deploy/functions/params.js +15 -15
  89. package/lib/deploy/functions/prepare.js +1 -1
  90. package/lib/deploy/functions/prepareFunctionsUpload.js +2 -2
  91. package/lib/deploy/functions/prompts.js +1 -1
  92. package/lib/deploy/functions/release/fabricator.js +24 -7
  93. package/lib/deploy/functions/release/index.js +3 -1
  94. package/lib/deploy/functions/release/reporter.js +1 -1
  95. package/lib/deploy/functions/runtimes/discovery/index.js +1 -16
  96. package/lib/deploy/functions/runtimes/discovery/parsing.js +16 -0
  97. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +59 -131
  98. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +1 -1
  99. package/lib/deploy/functions/runtimes/node/parseTriggers.js +2 -6
  100. package/lib/deploy/functions/runtimes/node/versioning.js +4 -4
  101. package/lib/deploy/functions/validate.js +1 -1
  102. package/lib/deploy/hosting/deploy.js +10 -9
  103. package/lib/deploy/hosting/uploader.js +2 -2
  104. package/lib/deploy/hosting/validate.js +2 -2
  105. package/lib/deploy/index.js +7 -7
  106. package/lib/deploy/lifecycleHooks.js +5 -2
  107. package/lib/deploy/storage/prepare.js +5 -3
  108. package/lib/deploy/storage/release.js +7 -6
  109. package/lib/emulator/auth/index.js +7 -2
  110. package/lib/emulator/auth/operations.js +10 -10
  111. package/lib/emulator/commandUtils.js +33 -16
  112. package/lib/emulator/constants.js +14 -6
  113. package/lib/emulator/controller.js +50 -18
  114. package/lib/emulator/databaseEmulator.js +1 -1
  115. package/lib/emulator/downloadableEmulators.js +8 -8
  116. package/lib/emulator/emulatorLogger.js +1 -1
  117. package/lib/emulator/eventarcEmulator.js +148 -0
  118. package/lib/emulator/extensionsEmulator.js +5 -3
  119. package/lib/emulator/firestoreEmulator.js +1 -1
  120. package/lib/emulator/functionsEmulator.js +66 -14
  121. package/lib/emulator/functionsEmulatorRuntime.js +12 -23
  122. package/lib/emulator/functionsEmulatorShared.js +6 -1
  123. package/lib/emulator/hub.js +7 -3
  124. package/lib/emulator/hubClient.js +2 -2
  125. package/lib/emulator/hubExport.js +22 -2
  126. package/lib/emulator/loggingEmulator.js +2 -2
  127. package/lib/emulator/registry.js +1 -0
  128. package/lib/emulator/storage/apis/firebase.js +26 -12
  129. package/lib/emulator/storage/apis/gcloud.js +15 -8
  130. package/lib/emulator/storage/files.js +21 -4
  131. package/lib/emulator/storage/multipart.js +6 -5
  132. package/lib/emulator/storage/rules/runtime.js +3 -3
  133. package/lib/emulator/storage/rules/utils.js +4 -2
  134. package/lib/emulator/storage/server.js +2 -1
  135. package/lib/emulator/storage/upload.js +1 -0
  136. package/lib/emulator/types.js +4 -0
  137. package/lib/emulator/ui.js +7 -2
  138. package/lib/ensureApiEnabled.js +7 -7
  139. package/lib/extensions/askUserForConsent.js +1 -1
  140. package/lib/extensions/askUserForEventsConfig.js +1 -1
  141. package/lib/extensions/askUserForParam.js +1 -1
  142. package/lib/extensions/changelog.js +2 -2
  143. package/lib/extensions/checkProjectBilling.js +2 -2
  144. package/lib/extensions/displayExtensionInfo.js +1 -1
  145. package/lib/extensions/extensionsApi.js +3 -2
  146. package/lib/extensions/extensionsHelper.js +30 -2
  147. package/lib/extensions/listExtensions.js +1 -1
  148. package/lib/extensions/manifest.js +1 -1
  149. package/lib/extensions/metricsUtils.js +1 -1
  150. package/lib/extensions/paramHelper.js +1 -1
  151. package/lib/extensions/updateHelper.js +2 -2
  152. package/lib/extensions/warnings.js +2 -2
  153. package/lib/fetchMOTD.js +1 -1
  154. package/lib/firestore/delete.js +1 -1
  155. package/lib/firestore/indexes.js +2 -2
  156. package/lib/firestore/validator.js +1 -1
  157. package/lib/functions/env.js +11 -11
  158. package/lib/functions/runtimeConfigExport.js +1 -1
  159. package/lib/functionsConfig.js +1 -1
  160. package/lib/functionsConfigClone.js +1 -1
  161. package/lib/functionsShellCommandAction.js +1 -1
  162. package/lib/gcp/cloudfunctions.js +3 -3
  163. package/lib/gcp/cloudfunctionsv2.js +5 -5
  164. package/lib/gcp/cloudscheduler.js +63 -22
  165. package/lib/gcp/cloudtasks.js +17 -1
  166. package/lib/gcp/serviceusage.js +2 -2
  167. package/lib/handlePreviewToggles.js +4 -4
  168. package/lib/hosting/implicitInit.js +1 -1
  169. package/lib/hosting/normalizedHostingConfigs.js +3 -3
  170. package/lib/index.js +2 -2
  171. package/lib/init/features/database.js +1 -1
  172. package/lib/init/features/emulators.js +1 -1
  173. package/lib/init/features/firestore/index.js +2 -2
  174. package/lib/init/features/firestore/indexes.js +1 -1
  175. package/lib/init/features/firestore/rules.js +1 -1
  176. package/lib/init/features/functions/golang.js +1 -1
  177. package/lib/init/features/functions/index.js +8 -1
  178. package/lib/init/features/hosting/github.js +9 -9
  179. package/lib/init/features/hosting/index.js +1 -1
  180. package/lib/init/features/project.js +1 -1
  181. package/lib/init/features/remoteconfig.js +1 -1
  182. package/lib/init/features/storage.js +1 -1
  183. package/lib/init/index.js +1 -1
  184. package/lib/logError.js +3 -3
  185. package/lib/management/projects.js +1 -1
  186. package/lib/parseBoltRules.js +1 -1
  187. package/lib/profileReport.js +2 -2
  188. package/lib/projectUtils.js +1 -1
  189. package/lib/rc.js +1 -1
  190. package/lib/requireAuth.js +1 -1
  191. package/lib/requireDatabaseInstance.js +2 -2
  192. package/lib/requirePermissions.js +2 -2
  193. package/lib/rulesDeploy.js +11 -12
  194. package/lib/serve/hosting.js +2 -1
  195. package/lib/serve/index.js +15 -0
  196. package/lib/track.js +122 -3
  197. package/lib/utils.js +24 -11
  198. package/npm-shrinkwrap.json +523 -294
  199. package/package.json +6 -4
  200. package/schema/firebase-config.json +12 -0
  201. package/templates/extensions/CHANGELOG.md +1 -7
  202. package/templates/hosting/init.js +6 -2
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventarcEmulator = void 0;
4
+ const express = require("express");
5
+ const constants_1 = require("./constants");
6
+ const types_1 = require("./types");
7
+ const utils_1 = require("../utils");
8
+ const emulatorLogger_1 = require("./emulatorLogger");
9
+ const registry_1 = require("./registry");
10
+ const apiv2_1 = require("../apiv2");
11
+ const error_1 = require("../error");
12
+ class EventarcEmulator {
13
+ constructor(args) {
14
+ this.args = args;
15
+ this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EVENTARC);
16
+ this.customEvents = {};
17
+ }
18
+ createHubServer() {
19
+ const registerTriggerRoute = `/emulator/v1/projects/:project_id/triggers/:trigger_name(*)`;
20
+ const registerTriggerHandler = (req, res) => {
21
+ const projectId = req.params.project_id;
22
+ const triggerName = req.params.trigger_name;
23
+ if (!projectId || !triggerName) {
24
+ const error = "Missing project ID or trigger name.";
25
+ this.logger.log("ERROR", error);
26
+ res.status(400).send({ error });
27
+ return;
28
+ }
29
+ const bodyString = req.rawBody.toString();
30
+ const substituted = bodyString.replaceAll("${PROJECT_ID}", projectId);
31
+ const body = JSON.parse(substituted);
32
+ const eventTrigger = body.eventTrigger;
33
+ if (!eventTrigger) {
34
+ const error = `Missing event trigger for ${triggerName}.`;
35
+ this.logger.log("ERROR", error);
36
+ res.status(400).send({ error });
37
+ return;
38
+ }
39
+ const key = `${eventTrigger.eventType}-${eventTrigger.channel}`;
40
+ this.logger.logLabeled("BULLET", "eventarc", `Registering custom event trigger for ${key} with trigger name ${triggerName}.`);
41
+ const customEventTriggers = this.customEvents[key] || [];
42
+ customEventTriggers.push({ projectId, triggerName, eventTrigger });
43
+ this.customEvents[key] = customEventTriggers;
44
+ res.status(200).send({ res: "OK" });
45
+ };
46
+ const publishEventsRoute = `/projects/:project_id/locations/:location/channels/:channel::publishEvents`;
47
+ const publishEventsHandler = (req, res) => {
48
+ const channel = `projects/${req.params.project_id}/locations/${req.params.location}/channels/${req.params.channel}`;
49
+ const body = JSON.parse(req.rawBody.toString());
50
+ for (const event of body.events) {
51
+ if (!event.type) {
52
+ res.sendStatus(400);
53
+ return;
54
+ }
55
+ this.logger.log("INFO", `Received custom event at channel ${channel}: ${JSON.stringify(event, null, 2)}`);
56
+ this.triggerCustomEventFunction(channel, event);
57
+ }
58
+ res.sendStatus(200);
59
+ };
60
+ const dataMiddleware = (req, _, next) => {
61
+ const chunks = [];
62
+ req.on("data", (chunk) => {
63
+ chunks.push(chunk);
64
+ });
65
+ req.on("end", () => {
66
+ req.rawBody = Buffer.concat(chunks);
67
+ next();
68
+ });
69
+ };
70
+ const hub = express();
71
+ hub.post([registerTriggerRoute], dataMiddleware, registerTriggerHandler);
72
+ hub.post([publishEventsRoute], dataMiddleware, publishEventsHandler);
73
+ hub.all("*", (req, res) => {
74
+ this.logger.log("DEBUG", `Eventarc emulator received unknown request at path ${req.path}`);
75
+ res.sendStatus(404);
76
+ });
77
+ return hub;
78
+ }
79
+ async triggerCustomEventFunction(channel, event) {
80
+ const functionsEmulator = registry_1.EmulatorRegistry.get(types_1.Emulators.FUNCTIONS);
81
+ if (!functionsEmulator) {
82
+ this.logger.log("INFO", "Functions emulator not found. This should not happen.");
83
+ return Promise.reject();
84
+ }
85
+ const key = `${event.type}-${channel}`;
86
+ const triggers = this.customEvents[key] || [];
87
+ const apiClient = new apiv2_1.Client({
88
+ urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(functionsEmulator.getInfo())}`,
89
+ auth: false,
90
+ });
91
+ return await Promise.all(triggers
92
+ .filter((trigger) => !trigger.eventTrigger.eventFilters ||
93
+ this.matchesAll(event, trigger.eventTrigger.eventFilters))
94
+ .map((trigger) => apiClient
95
+ .request({
96
+ method: "POST",
97
+ path: `/functions/projects/${trigger.projectId}/triggers/${trigger.triggerName}`,
98
+ body: JSON.stringify(event),
99
+ responseType: "stream",
100
+ resolveOnHTTPError: true,
101
+ })
102
+ .then((res) => {
103
+ if (res.status >= 400) {
104
+ throw new error_1.FirebaseError(`Received non-200 status code: ${res.status}`);
105
+ }
106
+ })
107
+ .catch((err) => {
108
+ this.logger.log("ERROR", `Failed to trigger Functions emulator for ${trigger.triggerName}: ${err}`);
109
+ })));
110
+ }
111
+ matchesAll(event, eventFilters) {
112
+ return Object.entries(eventFilters).every(([key, value]) => {
113
+ var _a, _b;
114
+ let attr = (_a = event[key]) !== null && _a !== void 0 ? _a : event.attributes[key];
115
+ if (typeof attr === "object" && !Array.isArray(attr)) {
116
+ attr = (_b = attr.ceTimestamp) !== null && _b !== void 0 ? _b : attr.ceString;
117
+ }
118
+ return attr === value;
119
+ });
120
+ }
121
+ async start() {
122
+ const { host, port } = this.getInfo();
123
+ const server = this.createHubServer().listen(port, host);
124
+ this.destroyServer = (0, utils_1.createDestroyer)(server);
125
+ return Promise.resolve();
126
+ }
127
+ async connect() {
128
+ return Promise.resolve();
129
+ }
130
+ async stop() {
131
+ if (this.destroyServer) {
132
+ await this.destroyServer();
133
+ }
134
+ }
135
+ getInfo() {
136
+ const host = this.args.host || constants_1.Constants.getDefaultHost();
137
+ const port = this.args.port || constants_1.Constants.getDefaultPort(types_1.Emulators.EVENTARC);
138
+ return {
139
+ name: this.getName(),
140
+ host,
141
+ port,
142
+ };
143
+ }
144
+ getName() {
145
+ return types_1.Emulators.EVENTARC;
146
+ }
147
+ }
148
+ exports.EventarcEmulator = EventarcEmulator;
@@ -4,7 +4,7 @@ exports.ExtensionsEmulator = void 0;
4
4
  const fs = require("fs-extra");
5
5
  const os = require("os");
6
6
  const path = require("path");
7
- const clc = require("cli-color");
7
+ const clc = require("colorette");
8
8
  const Table = require("cli-table");
9
9
  const spawn = require("cross-spawn");
10
10
  const planner = require("../deploy/extensions/planner");
@@ -103,10 +103,11 @@ class ExtensionsEmulator {
103
103
  for (const requiredFile of requiredFiles) {
104
104
  const f = path.join(args.path, requiredFile);
105
105
  if (!fs.existsSync(f)) {
106
- emulatorLogger_1.EmulatorLogger.forExtension({ ref: args.extTarget }).logLabeled("BULLET", "extensions", `Detected invalid source code for ${args.extTarget}, expected to find ${f}`);
106
+ this.logger.logLabeled("BULLET", "extensions", `Detected invalid source code for ${args.extTarget}, expected to find ${f}`);
107
107
  return false;
108
108
  }
109
109
  }
110
+ this.logger.logLabeled("DEBUG", "extensions", `Source code valid for ${args.extTarget}`);
110
111
  return true;
111
112
  }
112
113
  installAndBuildSourceCode(sourceCodePath) {
@@ -166,6 +167,7 @@ class ExtensionsEmulator {
166
167
  STORAGE_BUCKET: `${projectId}.appspot.com`,
167
168
  ALLOWED_EVENT_TYPES: instance.allowedEventTypes ? instance.allowedEventTypes.join(",") : "",
168
169
  EVENTARC_CHANNEL: (_a = instance.eventarcChannel) !== null && _a !== void 0 ? _a : "",
170
+ EVENTARC_CLOUD_EVENT_SOURCE: `projects/${projectId}/instances/${instance.instanceId}`,
169
171
  };
170
172
  }
171
173
  async checkAndWarnAPIs(instances) {
@@ -186,7 +188,7 @@ class ExtensionsEmulator {
186
188
  apiToWarn.apiName,
187
189
  apiToWarn.instanceIds,
188
190
  apiToWarn.enabled ? "Yes" : "No",
189
- apiToWarn.enabled ? "" : clc.bold.underline(enablementUri),
191
+ apiToWarn.enabled ? "" : clc.bold(clc.underline(enablementUri)),
190
192
  ]);
191
193
  }
192
194
  if (constants_1.Constants.isDemoProject(this.args.projectId)) {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FirestoreEmulator = void 0;
4
4
  const chokidar = require("chokidar");
5
5
  const fs = require("fs");
6
- const clc = require("cli-color");
6
+ const clc = require("colorette");
7
7
  const path = require("path");
8
8
  const utils = require("../utils");
9
9
  const downloadableEmulators = require("./downloadableEmulators");
@@ -4,7 +4,7 @@ exports.FunctionsEmulator = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const express = require("express");
7
- const clc = require("cli-color");
7
+ const clc = require("colorette");
8
8
  const http = require("http");
9
9
  const jwt = require("jsonwebtoken");
10
10
  const cors = require("cors");
@@ -35,6 +35,7 @@ const v1_1 = require("../functions/events/v1");
35
35
  const apiv2_1 = require("../apiv2");
36
36
  const build_1 = require("../deploy/functions/build");
37
37
  const EVENT_INVOKE = "functions:invoke";
38
+ const EVENT_INVOKE_GA4 = "functions_invoke";
38
39
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
39
40
  class FunctionsEmulator {
40
41
  constructor(args) {
@@ -89,7 +90,7 @@ class FunctionsEmulator {
89
90
  next();
90
91
  });
91
92
  };
92
- const backgroundFunctionRoute = `/functions/projects/:project_id/triggers/:trigger_name`;
93
+ const backgroundFunctionRoute = `/functions/projects/:project_id/triggers/:trigger_name(*)`;
93
94
  const httpsFunctionRoute = `/${this.args.projectId}/:region/:trigger_name`;
94
95
  const multicastFunctionRoute = `/functions/projects/:project_id/trigger_multicast`;
95
96
  const httpsFunctionRoutes = [httpsFunctionRoute, `${httpsFunctionRoute}/*`];
@@ -245,15 +246,9 @@ class FunctionsEmulator {
245
246
  await this.destroyServer();
246
247
  }
247
248
  }
248
- async loadTriggers(emulatableBackend, force = false) {
249
- this.workerPool.refresh();
250
- if (!emulatableBackend.nodeBinary) {
251
- throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
252
- }
253
- this.blockingFunctionsConfig = {};
254
- let triggerDefinitions;
249
+ async discoverTriggers(emulatableBackend) {
255
250
  if (emulatableBackend.predefinedTriggers) {
256
- triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
251
+ return (0, functionsEmulatorShared_1.emulatedFunctionsByRegion)(emulatableBackend.predefinedTriggers, emulatableBackend.secretEnv);
257
252
  }
258
253
  else {
259
254
  const runtimeConfig = this.getRuntimeConfig(emulatableBackend);
@@ -284,8 +279,26 @@ class FunctionsEmulator {
284
279
  for (const e of endpoints) {
285
280
  e.codebase = emulatableBackend.codebase;
286
281
  }
287
- triggerDefinitions = (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
282
+ return (0, functionsEmulatorShared_1.emulatedFunctionsFromEndpoints)(endpoints);
288
283
  }
284
+ }
285
+ async loadTriggers(emulatableBackend, force = false) {
286
+ if (!emulatableBackend.nodeBinary) {
287
+ throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
288
+ }
289
+ let triggerDefinitions = [];
290
+ try {
291
+ triggerDefinitions = await this.discoverTriggers(emulatableBackend);
292
+ this.logger.logLabeled("SUCCESS", "functions", `Loaded functions definitions from source: ${triggerDefinitions
293
+ .map((t) => t.entryPoint)
294
+ .join(", ")}.`);
295
+ }
296
+ catch (e) {
297
+ this.logger.logLabeled("ERROR", "functions", `Failed to load function definition from source: ${e}`);
298
+ return;
299
+ }
300
+ this.workerPool.refresh();
301
+ this.blockingFunctionsConfig = {};
289
302
  const toSetup = triggerDefinitions.filter((definition) => {
290
303
  if (force) {
291
304
  return true;
@@ -305,8 +318,7 @@ class FunctionsEmulator {
305
318
  (0, validate_1.functionIdsAreValid)([Object.assign(Object.assign({}, definition), { id: definition.name })]);
306
319
  }
307
320
  catch (e) {
308
- this.logger.logLabeled("WARN", `functions[${definition.id}]`, `Invalid function id: ${e.message}`);
309
- continue;
321
+ throw new error_1.FirebaseError(`functions[${definition.id}]: Invalid function id: ${e.message}`);
310
322
  }
311
323
  let added = false;
312
324
  let url = undefined;
@@ -329,6 +341,9 @@ class FunctionsEmulator {
329
341
  case constants_1.Constants.SERVICE_PUBSUB:
330
342
  added = await this.addPubsubTrigger(definition.name, key, definition.eventTrigger, signature, definition.schedule);
331
343
  break;
344
+ case constants_1.Constants.SERVICE_EVENTARC:
345
+ added = await this.addEventarcTrigger(this.args.projectId, key, definition.eventTrigger);
346
+ break;
332
347
  case constants_1.Constants.SERVICE_AUTH:
333
348
  added = this.addAuthTrigger(this.args.projectId, key, definition.eventTrigger);
334
349
  break;
@@ -368,6 +383,27 @@ class FunctionsEmulator {
368
383
  this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
369
384
  }
370
385
  }
386
+ addEventarcTrigger(projectId, key, eventTrigger) {
387
+ const eventarcEmu = registry_1.EmulatorRegistry.get(types_1.Emulators.EVENTARC);
388
+ if (!eventarcEmu) {
389
+ return Promise.resolve(false);
390
+ }
391
+ const bundle = {
392
+ eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "eventarc.googleapis.com" }),
393
+ };
394
+ logger_1.logger.debug(`addEventarcTrigger`, JSON.stringify(bundle));
395
+ const client = new apiv2_1.Client({
396
+ urlPrefix: `http://${registry_1.EmulatorRegistry.getInfoHostString(eventarcEmu.getInfo())}`,
397
+ auth: false,
398
+ });
399
+ return client
400
+ .post(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle)
401
+ .then(() => true)
402
+ .catch((err) => {
403
+ this.logger.log("WARN", "Error adding Eventarc function: " + err);
404
+ return false;
405
+ });
406
+ }
371
407
  async performPostLoadOperations() {
372
408
  if (!this.blockingFunctionsConfig.triggers &&
373
409
  !this.blockingFunctionsConfig.forwardInboundCredentials) {
@@ -544,7 +580,13 @@ class FunctionsEmulator {
544
580
  return record;
545
581
  }
546
582
  getTriggerKey(def) {
547
- return def.eventTrigger ? `${def.id}-${this.triggerGeneration}` : def.id;
583
+ if (def.eventTrigger) {
584
+ const triggerKey = `${def.id}-${this.triggerGeneration}`;
585
+ return def.eventTrigger.channel ? `${triggerKey}-${def.eventTrigger.channel}` : triggerKey;
586
+ }
587
+ else {
588
+ return def.id;
589
+ }
548
590
  }
549
591
  getBackendInfo() {
550
592
  const cf3Triggers = this.getCF3Triggers();
@@ -688,6 +730,10 @@ class FunctionsEmulator {
688
730
  const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
689
731
  process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
690
732
  }
733
+ const eventarcEmulator = this.getEmulatorInfo(types_1.Emulators.EVENTARC);
734
+ if (eventarcEmulator) {
735
+ envs[constants_1.Constants.CLOUD_EVENTARC_EMULATOR_HOST] = `http://${(0, functionsEmulatorShared_1.formatHost)(eventarcEmulator)}`;
736
+ }
691
737
  if (this.args.debugPort) {
692
738
  envs["FUNCTION_DEBUG_MODE"] = "true";
693
739
  }
@@ -884,6 +930,9 @@ class FunctionsEmulator {
884
930
  }
885
931
  });
886
932
  void (0, track_1.track)(EVENT_INVOKE, (0, functionsEmulatorShared_1.getFunctionService)(trigger));
933
+ void (0, track_1.trackEmulator)(EVENT_INVOKE_GA4, {
934
+ function_service: (0, functionsEmulatorShared_1.getFunctionService)(trigger),
935
+ });
887
936
  worker.waitForDone().then(() => {
888
937
  resolve({ status: "acknowledged" });
889
938
  });
@@ -959,6 +1008,9 @@ class FunctionsEmulator {
959
1008
  });
960
1009
  await worker.waitForSocketReady();
961
1010
  void (0, track_1.track)(EVENT_INVOKE, "https");
1011
+ void (0, track_1.trackEmulator)(EVENT_INVOKE_GA4, {
1012
+ function_service: "https",
1013
+ });
962
1014
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
963
1015
  const url = new url_1.URL(`${req.protocol}://${req.hostname}${req.url}`);
964
1016
  const path = `${url.pathname}${url.search}`.replace(new RegExp(`\/${this.args.projectId}\/[^\/]*\/${triggerName}\/?`), "/");
@@ -675,24 +675,19 @@ async function initializeRuntime() {
675
675
  await initializeFirebaseFunctionsStubs();
676
676
  await initializeFirebaseAdminStubs();
677
677
  }
678
- async function loadTriggers(serializedFunctionTrigger) {
678
+ async function loadTriggers() {
679
679
  let triggerModule;
680
- if (serializedFunctionTrigger) {
681
- triggerModule = eval(serializedFunctionTrigger)();
680
+ try {
681
+ triggerModule = require(process.cwd());
682
682
  }
683
- else {
684
- try {
685
- triggerModule = require(process.cwd());
686
- }
687
- catch (err) {
688
- if (err.code !== "ERR_REQUIRE_ESM") {
689
- await moduleResolutionDetective(err);
690
- throw err;
691
- }
692
- const modulePath = require.resolve(process.cwd());
693
- const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
694
- triggerModule = await dynamicImport(moduleURL);
683
+ catch (err) {
684
+ if (err.code !== "ERR_REQUIRE_ESM") {
685
+ await moduleResolutionDetective(err);
686
+ throw err;
695
687
  }
688
+ const modulePath = require.resolve(process.cwd());
689
+ const moduleURL = (0, url_1.pathToFileURL)(modulePath).href;
690
+ triggerModule = await dynamicImport(moduleURL);
696
691
  }
697
692
  return triggerModule;
698
693
  }
@@ -716,8 +711,7 @@ async function handleMessage(message) {
716
711
  }
717
712
  if (!functionModule) {
718
713
  try {
719
- const serializedTriggers = runtimeArgs.opts ? runtimeArgs.opts.serializedTriggers : undefined;
720
- functionModule = await loadTriggers(serializedTriggers);
714
+ functionModule = await loadTriggers();
721
715
  }
722
716
  catch (e) {
723
717
  logDebug(e);
@@ -739,12 +733,7 @@ async function handleMessage(message) {
739
733
  logDebug(`Beginning invocation function ${FUNCTION_TARGET_NAME}!`);
740
734
  try {
741
735
  await invokeTrigger(trigger, runtimeArgs.frb);
742
- if (runtimeArgs.opts && runtimeArgs.opts.serializedTriggers) {
743
- await flushAndExit(0);
744
- }
745
- else {
746
- await goIdle();
747
- }
736
+ await goIdle();
748
737
  }
749
738
  catch (err) {
750
739
  new types_1.EmulatorLog("FATAL", "runtime-error", err.stack ? err.stack : err).log();
@@ -83,12 +83,14 @@ function emulatedFunctionsFromEndpoints(endpoints) {
83
83
  else {
84
84
  const { resource, topic, bucket } = endpoint.eventTrigger.eventFilters;
85
85
  const eventResource = resource || topic || bucket;
86
- if (!eventResource) {
86
+ if (!eventResource && !eventTrigger.channel) {
87
87
  continue;
88
88
  }
89
89
  def.eventTrigger = {
90
90
  eventType: eventTrigger.eventType,
91
91
  resource: eventResource,
92
+ channel: eventTrigger.channel,
93
+ eventFilters: eventTrigger.eventFilters,
92
94
  };
93
95
  }
94
96
  }
@@ -151,6 +153,9 @@ exports.getTemporarySocketPath = getTemporarySocketPath;
151
153
  function getFunctionService(def) {
152
154
  var _a;
153
155
  if (def.eventTrigger) {
156
+ if (def.eventTrigger.channel) {
157
+ return constants_1.Constants.SERVICE_EVENTARC;
158
+ }
154
159
  return (_a = def.eventTrigger.service) !== null && _a !== void 0 ? _a : getServiceFromEventType(def.eventTrigger.eventType);
155
160
  }
156
161
  if (def.blockingTrigger) {
@@ -32,10 +32,14 @@ class EmulatorHub {
32
32
  res.json(body);
33
33
  });
34
34
  this.hub.post(EmulatorHub.PATH_EXPORT, async (req, res) => {
35
- const exportPath = req.body.path;
36
- utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${exportPath}.`);
35
+ const path = req.body.path;
36
+ const initiatedBy = req.body.initiatedBy || "unknown";
37
+ utils.logLabeledBullet("emulators", `Received export request. Exporting data to ${path}.`);
37
38
  try {
38
- await new hubExport_1.HubExport(this.args.projectId, exportPath).exportAll();
39
+ await new hubExport_1.HubExport(this.args.projectId, {
40
+ path,
41
+ initiatedBy,
42
+ }).exportAll();
39
43
  utils.logLabeledSuccess("emulators", "Export complete.");
40
44
  res.status(200).send({
41
45
  message: "OK",
@@ -21,9 +21,9 @@ class EmulatorHubClient {
21
21
  const res = await apiClient.get(hub_1.EmulatorHub.PATH_EMULATORS);
22
22
  return res.body;
23
23
  }
24
- async postExport(path) {
24
+ async postExport(options) {
25
25
  const apiClient = new apiv2_1.Client({ urlPrefix: this.origin, auth: false });
26
- await apiClient.post(hub_1.EmulatorHub.PATH_EXPORT, { path });
26
+ await apiClient.post(hub_1.EmulatorHub.PATH_EXPORT, options);
27
27
  }
28
28
  get origin() {
29
29
  const locator = this.assertLocator();
@@ -13,10 +13,12 @@ const hub_1 = require("./hub");
13
13
  const downloadableEmulators_1 = require("./downloadableEmulators");
14
14
  const rimraf = require("rimraf");
15
15
  const apiv2_1 = require("../apiv2");
16
+ const track_1 = require("../track");
16
17
  class HubExport {
17
- constructor(projectId, exportPath) {
18
+ constructor(projectId, options) {
18
19
  this.projectId = projectId;
19
- this.exportPath = exportPath;
20
+ this.options = options;
21
+ this.exportPath = options.path;
20
22
  this.tmpDir = fs.mkdtempSync(`firebase-export-${new Date().getTime()}`);
21
23
  }
22
24
  static readMetadata(exportPath) {
@@ -66,6 +68,10 @@ class HubExport {
66
68
  if (!fs.existsSync(this.exportPath)) {
67
69
  fs.mkdirSync(this.exportPath);
68
70
  }
71
+ void (0, track_1.trackEmulator)("emulator_export", {
72
+ initiated_by: this.options.initiatedBy,
73
+ emulator_name: types_1.Emulators.HUB,
74
+ });
69
75
  const metadataPath = path.join(this.tmpDir, HubExport.METADATA_FILE_NAME);
70
76
  fs.writeFileSync(metadataPath, JSON.stringify(metadata, undefined, 2));
71
77
  logger_1.logger.debug(`hubExport: swapping ${this.tmpDir} with ${this.exportPath}`);
@@ -73,6 +79,10 @@ class HubExport {
73
79
  fse.moveSync(this.tmpDir, this.exportPath);
74
80
  }
75
81
  async exportFirestore(metadata) {
82
+ void (0, track_1.trackEmulator)("emulator_export", {
83
+ initiated_by: this.options.initiatedBy,
84
+ emulator_name: types_1.Emulators.FIRESTORE,
85
+ });
76
86
  const firestoreInfo = registry_1.EmulatorRegistry.get(types_1.Emulators.FIRESTORE).getInfo();
77
87
  const firestoreHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(firestoreInfo)}`;
78
88
  const firestoreExportBody = {
@@ -115,6 +125,11 @@ class HubExport {
115
125
  namespacesToExport.push(ns);
116
126
  }
117
127
  }
128
+ void (0, track_1.trackEmulator)("emulator_export", {
129
+ initiated_by: this.options.initiatedBy,
130
+ emulator_name: types_1.Emulators.DATABASE,
131
+ count: namespacesToExport.length,
132
+ });
118
133
  const dbExportPath = path.join(this.tmpDir, metadata.database.path);
119
134
  if (!fs.existsSync(dbExportPath)) {
120
135
  fs.mkdirSync(dbExportPath);
@@ -132,6 +147,10 @@ class HubExport {
132
147
  }
133
148
  }
134
149
  async exportAuth(metadata) {
150
+ void (0, track_1.trackEmulator)("emulator_export", {
151
+ initiated_by: this.options.initiatedBy,
152
+ emulator_name: types_1.Emulators.AUTH,
153
+ });
135
154
  const { host, port } = registry_1.EmulatorRegistry.get(types_1.Emulators.AUTH).getInfo();
136
155
  const authExportPath = path.join(this.tmpDir, metadata.auth.path);
137
156
  if (!fs.existsSync(authExportPath)) {
@@ -164,6 +183,7 @@ class HubExport {
164
183
  const storageHost = `http://${registry_1.EmulatorRegistry.getInfoHostString(storageEmulator.getInfo())}`;
165
184
  const storageExportBody = {
166
185
  path: storageExportPath,
186
+ initiatedBy: this.options.initiatedBy,
167
187
  };
168
188
  const client = new apiv2_1.Client({ urlPrefix: storageHost, auth: false });
169
189
  const res = await client.request({
@@ -7,7 +7,7 @@ const triple_beam_1 = require("triple-beam");
7
7
  const WebSocket = require("ws");
8
8
  const TransportStream = require("winston-transport");
9
9
  const logger_1 = require("../logger");
10
- const ansiStrip = require("cli-color/strip");
10
+ const stripAnsi = require("strip-ansi");
11
11
  class LoggingEmulator {
12
12
  constructor(args) {
13
13
  this.args = args;
@@ -106,7 +106,7 @@ class WebSocketTransport extends TransportStream {
106
106
  if (bundle.data && bundle.data.metadata && bundle.data.metadata.message) {
107
107
  bundle.message = bundle.data.metadata.message;
108
108
  }
109
- bundle.message = ansiStrip(bundle.message);
109
+ bundle.message = stripAnsi(bundle.message);
110
110
  this.history.push(bundle);
111
111
  this.connections.forEach((ws) => {
112
112
  ws.send(JSON.stringify(bundle));
@@ -39,6 +39,7 @@ class EmulatorRegistry {
39
39
  pubsub: 3.2,
40
40
  auth: 3.3,
41
41
  storage: 3.5,
42
+ eventarc: 3.6,
42
43
  hub: 4,
43
44
  logging: 5,
44
45
  };
@@ -121,10 +121,22 @@ function createFirebaseEndpoints(emulator) {
121
121
  var _a, _b, _c, _d;
122
122
  const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
123
123
  let listResponse;
124
+ let prefix = "";
125
+ if (req.query.prefix) {
126
+ prefix = req.query.prefix.toString();
127
+ if (prefix.charAt(prefix.length - 1) !== "/") {
128
+ return res.status(400).json({
129
+ error: {
130
+ code: 400,
131
+ message: "The prefix parameter is required to be empty or ends with a single / character.",
132
+ },
133
+ });
134
+ }
135
+ }
124
136
  try {
125
137
  listResponse = await storageLayer.listObjects({
126
138
  bucketId: req.params.bucketId,
127
- prefix: req.query.prefix ? req.query.prefix.toString() : "",
139
+ prefix: prefix,
128
140
  delimiter: req.query.delimiter ? req.query.delimiter.toString() : "",
129
141
  pageToken: (_b = req.query.pageToken) === null || _b === void 0 ? void 0 : _b.toString(),
130
142
  maxResults: maxResults ? +maxResults : undefined,
@@ -153,14 +165,17 @@ function createFirebaseEndpoints(emulator) {
153
165
  });
154
166
  });
155
167
  const handleUpload = async (req, res) => {
156
- if (!req.query.name) {
157
- res.sendStatus(400);
158
- return;
159
- }
168
+ var _a;
160
169
  const bucketId = req.params.bucketId;
161
- const objectId = req.query.name.toString();
170
+ const objectId = req.params.objectId
171
+ ? decodeURIComponent(req.params.objectId)
172
+ : ((_a = req.query.name) === null || _a === void 0 ? void 0 : _a.toString()) || null;
162
173
  const uploadType = req.header("x-goog-upload-protocol");
163
174
  if (uploadType === "multipart") {
175
+ if (!objectId) {
176
+ res.sendStatus(400);
177
+ return;
178
+ }
164
179
  const contentTypeHeader = req.header("content-type");
165
180
  if (!contentTypeHeader) {
166
181
  return res.sendStatus(400);
@@ -172,12 +187,7 @@ function createFirebaseEndpoints(emulator) {
172
187
  }
173
188
  catch (err) {
174
189
  if (err instanceof Error) {
175
- return res.status(400).json({
176
- error: {
177
- code: 400,
178
- message: err.message,
179
- },
180
- });
190
+ return res.status(400).send(err.message);
181
191
  }
182
192
  throw err;
183
193
  }
@@ -212,6 +222,10 @@ function createFirebaseEndpoints(emulator) {
212
222
  return;
213
223
  }
214
224
  if (uploadCommand === "start") {
225
+ if (!objectId) {
226
+ res.sendStatus(400);
227
+ return;
228
+ }
215
229
  const upload = uploadService.startResumableUpload({
216
230
  bucketId,
217
231
  objectId,