firebase-tools 11.5.0 → 11.8.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 (63) hide show
  1. package/lib/command.js +33 -7
  2. package/lib/commands/crashlytics-mappingfile-generateid.js +26 -0
  3. package/lib/commands/crashlytics-mappingfile-upload.js +46 -0
  4. package/lib/commands/crashlytics-symbols-upload.js +18 -87
  5. package/lib/commands/emulators-exec.js +4 -1
  6. package/lib/commands/emulators-export.js +5 -2
  7. package/lib/commands/emulators-start.js +23 -17
  8. package/lib/commands/ext-dev-publish.js +3 -0
  9. package/lib/commands/functions-delete.js +2 -0
  10. package/lib/commands/functions-secrets-get.js +2 -0
  11. package/lib/commands/index.js +3 -0
  12. package/lib/commands/login.js +2 -2
  13. package/lib/crashlytics/buildToolsJarHelper.js +51 -0
  14. package/lib/deploy/functions/backend.js +4 -4
  15. package/lib/deploy/functions/build.js +76 -9
  16. package/lib/deploy/functions/checkIam.js +6 -5
  17. package/lib/deploy/functions/params.js +22 -16
  18. package/lib/deploy/functions/prepare.js +1 -1
  19. package/lib/deploy/functions/release/fabricator.js +22 -5
  20. package/lib/deploy/functions/release/index.js +2 -0
  21. package/lib/deploy/functions/runtimes/discovery/index.js +1 -16
  22. package/lib/deploy/functions/runtimes/discovery/parsing.js +16 -0
  23. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +59 -131
  24. package/lib/deploy/functions/runtimes/node/parseTriggers.js +1 -1
  25. package/lib/emulator/auth/index.js +7 -2
  26. package/lib/emulator/auth/operations.js +10 -10
  27. package/lib/emulator/commandUtils.js +32 -15
  28. package/lib/emulator/constants.js +14 -6
  29. package/lib/emulator/controller.js +49 -17
  30. package/lib/emulator/downloadableEmulators.js +7 -7
  31. package/lib/emulator/eventarcEmulator.js +148 -0
  32. package/lib/emulator/extensionsEmulator.js +3 -1
  33. package/lib/emulator/functionsEmulator.js +44 -4
  34. package/lib/emulator/functionsEmulatorRuntime.js +12 -23
  35. package/lib/emulator/functionsEmulatorShared.js +6 -1
  36. package/lib/emulator/hub.js +7 -3
  37. package/lib/emulator/hubClient.js +2 -2
  38. package/lib/emulator/hubExport.js +22 -2
  39. package/lib/emulator/registry.js +1 -0
  40. package/lib/emulator/storage/apis/firebase.js +145 -129
  41. package/lib/emulator/storage/apis/gcloud.js +102 -42
  42. package/lib/emulator/storage/files.js +39 -17
  43. package/lib/emulator/storage/metadata.js +76 -55
  44. package/lib/emulator/storage/multipart.js +2 -2
  45. package/lib/emulator/storage/rules/runtime.js +12 -4
  46. package/lib/emulator/storage/server.js +2 -1
  47. package/lib/emulator/storage/upload.js +46 -9
  48. package/lib/emulator/types.js +3 -0
  49. package/lib/emulator/ui.js +7 -2
  50. package/lib/extensions/extensionsApi.js +2 -1
  51. package/lib/extensions/extensionsHelper.js +29 -1
  52. package/lib/functions/constants.js +14 -0
  53. package/lib/functions/env.js +9 -9
  54. package/lib/gcp/cloudfunctions.js +15 -18
  55. package/lib/gcp/cloudfunctionsv2.js +15 -18
  56. package/lib/gcp/cloudscheduler.js +32 -14
  57. package/lib/serve/index.js +15 -0
  58. package/lib/track.js +122 -3
  59. package/lib/utils.js +14 -1
  60. package/npm-shrinkwrap.json +542 -9
  61. package/package.json +5 -4
  62. package/schema/firebase-config.json +12 -0
  63. package/templates/extensions/CHANGELOG.md +1 -7
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.exportEmulatorData = exports.startAll = exports.shouldStart = exports.filterEmulatorTargets = exports.cleanShutdown = exports.onExit = exports.exportOnExit = exports.startEmulator = void 0;
3
+ exports.exportEmulatorData = exports.startAll = exports.shouldStart = exports.filterEmulatorTargets = exports.cleanShutdown = exports.onExit = exports.exportOnExit = void 0;
4
4
  const clc = require("colorette");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
@@ -16,6 +16,7 @@ const auth_1 = require("./auth");
16
16
  const databaseEmulator_1 = require("./databaseEmulator");
17
17
  const firestoreEmulator_1 = require("./firestoreEmulator");
18
18
  const hostingEmulator_1 = require("./hostingEmulator");
19
+ const eventarcEmulator_1 = require("./eventarcEmulator");
19
20
  const error_1 = require("../error");
20
21
  const projectUtils_1 = require("../projectUtils");
21
22
  const pubsubEmulator_1 = require("./pubsubEmulator");
@@ -93,19 +94,13 @@ async function getAndCheckAddress(emulator, options) {
93
94
  }
94
95
  return { host, port };
95
96
  }
96
- async function startEmulator(instance) {
97
- const name = instance.getName();
98
- void (0, track_1.track)("Emulator Run", name);
99
- await registry_1.EmulatorRegistry.start(instance);
100
- }
101
- exports.startEmulator = startEmulator;
102
97
  async function exportOnExit(options) {
103
98
  const exportOnExitDir = options.exportOnExit;
104
99
  if (exportOnExitDir) {
105
100
  try {
106
101
  utils.logBullet(`Automatically exporting data using ${commandUtils_1.FLAG_EXPORT_ON_EXIT_NAME} "${exportOnExitDir}" ` +
107
102
  "please wait for the export to finish...");
108
- await exportEmulatorData(exportOnExitDir, options);
103
+ await exportEmulatorData(exportOnExitDir, options, "exit");
109
104
  }
110
105
  catch (e) {
111
106
  utils.logWarning(e);
@@ -215,9 +210,8 @@ async function startAll(options, showUI = true) {
215
210
  if (targets.length === 0) {
216
211
  throw new error_1.FirebaseError(`No emulators to start, run ${clc.bold("firebase init emulators")} to get started.`);
217
212
  }
218
- const deprecationNotices = [];
219
213
  if (targets.some(downloadableEmulators_1.requiresJava)) {
220
- if (!(await commandUtils.checkJavaSupported())) {
214
+ if ((await commandUtils.checkJavaMajorVersion()) < commandUtils_1.MIN_SUPPORTED_JAVA_MAJOR_VERSION) {
221
215
  utils.logLabeledError("emulators", commandUtils_1.JAVA_DEPRECATION_WARNING, "warn");
222
216
  throw new error_1.FirebaseError(commandUtils_1.JAVA_DEPRECATION_WARNING);
223
217
  }
@@ -225,7 +219,8 @@ async function startAll(options, showUI = true) {
225
219
  const hubLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HUB);
226
220
  hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);
227
221
  const projectId = (0, projectUtils_1.getProjectId)(options) || "";
228
- if (constants_1.Constants.isDemoProject(projectId)) {
222
+ const isDemoProject = constants_1.Constants.isDemoProject(projectId);
223
+ if (isDemoProject) {
229
224
  hubLogger.logLabeled("BULLET", "emulators", `Detected demo project ID "${projectId}", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.`);
230
225
  }
231
226
  const onlyOptions = options.only;
@@ -249,6 +244,15 @@ async function startAll(options, showUI = true) {
249
244
  await (0, frameworks_1.prepareFrameworks)(targets, options, options);
250
245
  }
251
246
  }
247
+ function startEmulator(instance) {
248
+ const name = instance.getName();
249
+ void (0, track_1.track)("Emulator Run", name);
250
+ void (0, track_1.trackEmulator)("emulator_run", {
251
+ emulator_name: name,
252
+ is_demo_project: String(isDemoProject),
253
+ });
254
+ return registry_1.EmulatorRegistry.start(instance);
255
+ }
252
256
  if (shouldStart(options, types_1.Emulators.HUB)) {
253
257
  const hubAddr = await getAndCheckAddress(types_1.Emulators.HUB, options);
254
258
  const hub = new hub_1.EmulatorHub(Object.assign({ projectId }, hubAddr));
@@ -264,6 +268,10 @@ async function startAll(options, showUI = true) {
264
268
  const foundMetadata = findExportMetadata(importDir);
265
269
  if (foundMetadata) {
266
270
  exportMetadata = foundMetadata;
271
+ void (0, track_1.trackEmulator)("emulator_import", {
272
+ initiated_by: "start",
273
+ emulator_name: types_1.Emulators.HUB,
274
+ });
267
275
  }
268
276
  else {
269
277
  hubLogger.logLabeled("WARN", "emulators", `Could not find import/export metadata file, ${clc.bold("skipping data import!")}`);
@@ -287,7 +295,7 @@ async function startAll(options, showUI = true) {
287
295
  }
288
296
  }
289
297
  if (shouldStart(options, types_1.Emulators.EXTENSIONS)) {
290
- const projectNumber = constants_1.Constants.isDemoProject(projectId)
298
+ const projectNumber = isDemoProject
291
299
  ? constants_1.Constants.FAKE_PROJECT_NUMBER
292
300
  : await (0, projectUtils_1.needProjectNumber)(options);
293
301
  const aliases = (0, projectUtils_1.getAliases)(options, projectId);
@@ -330,6 +338,12 @@ async function startAll(options, showUI = true) {
330
338
  projectAlias: options.projectAlias,
331
339
  });
332
340
  await startEmulator(functionsEmulator);
341
+ const eventarcAddr = await getAndCheckAddress(types_1.Emulators.EVENTARC, options);
342
+ const eventarcEmulator = new eventarcEmulator_1.EventarcEmulator({
343
+ host: eventarcAddr.host,
344
+ port: eventarcAddr.port,
345
+ });
346
+ await startEmulator(eventarcEmulator);
333
347
  }
334
348
  if (shouldStart(options, types_1.Emulators.FIRESTORE)) {
335
349
  const firestoreLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FIRESTORE);
@@ -346,6 +360,10 @@ async function startAll(options, showUI = true) {
346
360
  const exportMetadataFilePath = path.resolve(importDirAbsPath, exportMetadata.firestore.metadata_file);
347
361
  firestoreLogger.logLabeled("BULLET", "firestore", `Importing data from ${exportMetadataFilePath}`);
348
362
  args.seed_from_export = exportMetadataFilePath;
363
+ void (0, track_1.trackEmulator)("emulator_import", {
364
+ initiated_by: "start",
365
+ emulator_name: types_1.Emulators.FIRESTORE,
366
+ });
349
367
  }
350
368
  const config = options.config;
351
369
  const rulesLocalPath = (_a = config.src.firestore) === null || _a === void 0 ? void 0 : _a.rules;
@@ -407,6 +425,11 @@ async function startAll(options, showUI = true) {
407
425
  const importDirAbsPath = path.resolve(options.import);
408
426
  const databaseExportDir = path.resolve(importDirAbsPath, exportMetadata.database.path);
409
427
  const files = fs.readdirSync(databaseExportDir).filter((f) => f.endsWith(".json"));
428
+ void (0, track_1.trackEmulator)("emulator_import", {
429
+ initiated_by: "start",
430
+ emulator_name: types_1.Emulators.DATABASE,
431
+ count: files.length,
432
+ });
410
433
  for (const f of files) {
411
434
  const fPath = path.join(databaseExportDir, f);
412
435
  const ns = path.basename(f, ".json");
@@ -429,7 +452,7 @@ async function startAll(options, showUI = true) {
429
452
  utils.assertIsString(options.import);
430
453
  const importDirAbsPath = path.resolve(options.import);
431
454
  const authExportDir = path.resolve(importDirAbsPath, exportMetadata.auth.path);
432
- await authEmulator.importData(authExportDir, projectId);
455
+ await authEmulator.importData(authExportDir, projectId, { initiatedBy: "start" });
433
456
  }
434
457
  }
435
458
  if (shouldStart(options, types_1.Emulators.PUBSUB)) {
@@ -458,7 +481,7 @@ async function startAll(options, showUI = true) {
458
481
  utils.assertIsString(options.import);
459
482
  const importDirAbsPath = path.resolve(options.import);
460
483
  const storageExportDir = path.resolve(importDirAbsPath, exportMetadata.storage.path);
461
- storageEmulator.storageLayer.import(storageExportDir);
484
+ storageEmulator.storageLayer.import(storageExportDir, { initiatedBy: "start" });
462
485
  }
463
486
  }
464
487
  if (shouldStart(options, types_1.Emulators.HOSTING)) {
@@ -488,17 +511,26 @@ async function startAll(options, showUI = true) {
488
511
  const ui = new ui_1.EmulatorUI(Object.assign({ projectId: projectId, auto_download: true }, uiAddr));
489
512
  await startEmulator(ui);
490
513
  }
514
+ let serviceEmulatorCount = 0;
491
515
  const running = registry_1.EmulatorRegistry.listRunning();
492
516
  for (const name of running) {
493
517
  const instance = registry_1.EmulatorRegistry.get(name);
494
518
  if (instance) {
495
519
  await instance.connect();
496
520
  }
521
+ if (types_1.ALL_SERVICE_EMULATORS.includes(name)) {
522
+ serviceEmulatorCount++;
523
+ }
497
524
  }
498
- return { deprecationNotices };
525
+ void (0, track_1.trackEmulator)("emulators_started", {
526
+ count: serviceEmulatorCount,
527
+ count_all: running.length,
528
+ is_demo_project: String(isDemoProject),
529
+ });
530
+ return { deprecationNotices: [] };
499
531
  }
500
532
  exports.startAll = startAll;
501
- async function exportEmulatorData(exportPath, options) {
533
+ async function exportEmulatorData(exportPath, options, initiatedBy) {
502
534
  const projectId = options.project;
503
535
  if (!projectId) {
504
536
  throw new error_1.FirebaseError("Could not determine project ID, make sure you're running in a Firebase project directory or add the --project flag.", { exit: 1 });
@@ -536,7 +568,7 @@ async function exportEmulatorData(exportPath, options) {
536
568
  }
537
569
  utils.logBullet(`Exporting data to: ${exportAbsPath}`);
538
570
  try {
539
- await hubClient.postExport(exportAbsPath);
571
+ await hubClient.postExport({ path: exportAbsPath, initiatedBy });
540
572
  }
541
573
  catch (e) {
542
574
  throw new error_1.FirebaseError("Export request failed, see emulator logs for more information.", {
@@ -67,15 +67,15 @@ exports.DownloadDetails = {
67
67
  },
68
68
  }
69
69
  : {
70
- version: "1.8.1",
71
- downloadPath: path.join(CACHE_DIR, "ui-v1.8.1.zip"),
72
- unzipDir: path.join(CACHE_DIR, "ui-v1.8.1"),
73
- binaryPath: path.join(CACHE_DIR, "ui-v1.8.1", "server", "server.js"),
70
+ version: "1.9.0",
71
+ downloadPath: path.join(CACHE_DIR, "ui-v1.9.0.zip"),
72
+ unzipDir: path.join(CACHE_DIR, "ui-v1.9.0"),
73
+ binaryPath: path.join(CACHE_DIR, "ui-v1.9.0", "server", "server.js"),
74
74
  opts: {
75
75
  cacheDir: CACHE_DIR,
76
- remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.8.1.zip",
77
- expectedSize: 3056552,
78
- expectedChecksum: "92590fdda20f9883588438d9551111b5",
76
+ remoteUrl: "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.9.0.zip",
77
+ expectedSize: 3062710,
78
+ expectedChecksum: "984597f41d497bd318dac131615eb9d5",
79
79
  namePrefix: "ui",
80
80
  },
81
81
  },
@@ -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;
@@ -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) {
@@ -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}/*`];
@@ -317,8 +318,7 @@ class FunctionsEmulator {
317
318
  (0, validate_1.functionIdsAreValid)([Object.assign(Object.assign({}, definition), { id: definition.name })]);
318
319
  }
319
320
  catch (e) {
320
- this.logger.logLabeled("WARN", `functions[${definition.id}]`, `Invalid function id: ${e.message}`);
321
- continue;
321
+ throw new error_1.FirebaseError(`functions[${definition.id}]: Invalid function id: ${e.message}`);
322
322
  }
323
323
  let added = false;
324
324
  let url = undefined;
@@ -341,6 +341,9 @@ class FunctionsEmulator {
341
341
  case constants_1.Constants.SERVICE_PUBSUB:
342
342
  added = await this.addPubsubTrigger(definition.name, key, definition.eventTrigger, signature, definition.schedule);
343
343
  break;
344
+ case constants_1.Constants.SERVICE_EVENTARC:
345
+ added = await this.addEventarcTrigger(this.args.projectId, key, definition.eventTrigger);
346
+ break;
344
347
  case constants_1.Constants.SERVICE_AUTH:
345
348
  added = this.addAuthTrigger(this.args.projectId, key, definition.eventTrigger);
346
349
  break;
@@ -380,6 +383,27 @@ class FunctionsEmulator {
380
383
  this.startRuntime(emulatableBackend, { nodeBinary: emulatableBackend.nodeBinary });
381
384
  }
382
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
+ }
383
407
  async performPostLoadOperations() {
384
408
  if (!this.blockingFunctionsConfig.triggers &&
385
409
  !this.blockingFunctionsConfig.forwardInboundCredentials) {
@@ -556,7 +580,13 @@ class FunctionsEmulator {
556
580
  return record;
557
581
  }
558
582
  getTriggerKey(def) {
559
- 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
+ }
560
590
  }
561
591
  getBackendInfo() {
562
592
  const cf3Triggers = this.getCF3Triggers();
@@ -700,6 +730,10 @@ class FunctionsEmulator {
700
730
  const pubsubHost = (0, functionsEmulatorShared_1.formatHost)(pubsubEmulator);
701
731
  process.env.PUBSUB_EMULATOR_HOST = pubsubHost;
702
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
+ }
703
737
  if (this.args.debugPort) {
704
738
  envs["FUNCTION_DEBUG_MODE"] = "true";
705
739
  }
@@ -896,6 +930,9 @@ class FunctionsEmulator {
896
930
  }
897
931
  });
898
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
+ });
899
936
  worker.waitForDone().then(() => {
900
937
  resolve({ status: "acknowledged" });
901
938
  });
@@ -971,6 +1008,9 @@ class FunctionsEmulator {
971
1008
  });
972
1009
  await worker.waitForSocketReady();
973
1010
  void (0, track_1.track)(EVENT_INVOKE, "https");
1011
+ void (0, track_1.trackEmulator)(EVENT_INVOKE_GA4, {
1012
+ function_service: "https",
1013
+ });
974
1014
  this.logger.log("DEBUG", `[functions] Runtime ready! Sending request!`);
975
1015
  const url = new url_1.URL(`${req.protocol}://${req.hostname}${req.url}`);
976
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({