firebase-tools 13.13.2 → 13.14.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 (37) hide show
  1. package/lib/api.js +4 -2
  2. package/lib/apphosting/githubConnections.js +108 -34
  3. package/lib/auth.js +2 -7
  4. package/lib/command.js +1 -1
  5. package/lib/commands/dataconnect-sdk-generate.js +2 -7
  6. package/lib/commands/firestore-backups-schedules-create.js +11 -14
  7. package/lib/commands/firestore-backups-schedules-list.js +1 -1
  8. package/lib/commands/firestore-backups-schedules-update.js +4 -3
  9. package/lib/commands/firestore-databases-create.js +5 -6
  10. package/lib/commands/firestore-databases-restore.js +4 -4
  11. package/lib/commands/firestore-databases-update.js +5 -6
  12. package/lib/commands/init.js +2 -2
  13. package/lib/commands/login.js +1 -2
  14. package/lib/dataconnect/fileUtils.js +2 -2
  15. package/lib/dataconnect/filters.js +12 -1
  16. package/lib/dataconnect/load.js +6 -5
  17. package/lib/dataconnect/schemaMigration.js +22 -0
  18. package/lib/deploy/dataconnect/prepare.js +16 -2
  19. package/lib/emulator/constants.js +1 -0
  20. package/lib/emulator/controller.js +2 -5
  21. package/lib/emulator/dataconnectEmulator.js +3 -2
  22. package/lib/emulator/downloadableEmulators.js +12 -12
  23. package/lib/emulator/eventarcEmulator.js +68 -18
  24. package/lib/emulator/functionsEmulator.js +79 -0
  25. package/lib/emulator/functionsEmulatorShared.js +4 -0
  26. package/lib/emulator/pubsubEmulator.js +1 -1
  27. package/lib/experiments.js +5 -0
  28. package/lib/frameworks/angular/utils.js +13 -5
  29. package/lib/functions/events/v2.js +2 -1
  30. package/lib/gcp/devConnect.js +13 -1
  31. package/lib/init/features/account.js +1 -1
  32. package/lib/init/features/dataconnect/index.js +69 -19
  33. package/lib/init/features/dataconnect/sdk.js +2 -3
  34. package/lib/init/features/hosting/github.js +44 -21
  35. package/lib/requireAuth.js +2 -2
  36. package/package.json +8 -8
  37. package/templates/init/dataconnect/dataconnect.yaml +1 -1
@@ -11,10 +11,12 @@ const logger_1 = require("../logger");
11
11
  const error_1 = require("../error");
12
12
  const projectUtils_1 = require("../projectUtils");
13
13
  const utils_1 = require("../utils");
14
+ const experiments = require("../experiments");
14
15
  const errors = require("./errors");
15
16
  async function diffSchema(schema) {
16
17
  const { serviceName, instanceName, databaseId } = getIdentifiers(schema);
17
18
  await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, false);
19
+ setCompatibleMode(schema, databaseId, instanceName);
18
20
  try {
19
21
  await (0, client_1.upsertSchema)(schema, true);
20
22
  (0, utils_1.logLabeledSuccess)("dataconnect", `Database schema is up to date.`);
@@ -43,6 +45,7 @@ async function migrateSchema(args) {
43
45
  const { options, schema, validateOnly } = args;
44
46
  const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema);
45
47
  await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, true);
48
+ setCompatibleMode(schema, databaseId, instanceName);
46
49
  try {
47
50
  await (0, client_1.upsertSchema)(schema, validateOnly);
48
51
  logger_1.logger.debug(`Database schema was up to date for ${instanceId}:${databaseId}`);
@@ -79,6 +82,25 @@ async function migrateSchema(args) {
79
82
  return [];
80
83
  }
81
84
  exports.migrateSchema = migrateSchema;
85
+ function setCompatibleMode(schema, databaseId, instanceName) {
86
+ var _a;
87
+ if (experiments.isEnabled("fdccompatiblemode")) {
88
+ if ((_a = schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.schemaValidation) {
89
+ schema.primaryDatasource.postgresql.schemaValidation = "COMPATIBLE";
90
+ }
91
+ else {
92
+ schema.primaryDatasource = {
93
+ postgresql: {
94
+ database: databaseId,
95
+ cloudSql: {
96
+ instance: instanceName,
97
+ },
98
+ schemaValidation: "COMPATIBLE",
99
+ },
100
+ };
101
+ }
102
+ }
103
+ }
82
104
  function getIdentifiers(schema) {
83
105
  var _a, _b;
84
106
  const databaseId = (_a = schema.primaryDatasource.postgresql) === null || _a === void 0 ? void 0 : _a.database;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const path = require("path");
3
+ const clc = require("colorette");
4
4
  const load_1 = require("../../dataconnect/load");
5
5
  const fileUtils_1 = require("../../dataconnect/fileUtils");
6
6
  const logger_1 = require("../../logger");
@@ -11,6 +11,7 @@ const build_1 = require("../../dataconnect/build");
11
11
  const ensureApis_1 = require("../../dataconnect/ensureApis");
12
12
  const requireTosAcceptance_1 = require("../../requireTosAcceptance");
13
13
  const firedata_1 = require("../../gcp/firedata");
14
+ const error_1 = require("../../error");
14
15
  async function default_1(context, options) {
15
16
  const projectId = (0, projectUtils_1.needProjectId)(options);
16
17
  await (0, ensureApis_1.ensureApis)(projectId);
@@ -18,10 +19,23 @@ async function default_1(context, options) {
18
19
  const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(options.config);
19
20
  utils.logLabeledBullet("dataconnect", `Preparing to deploy`);
20
21
  const filters = (0, filters_1.getResourceFilters)(options);
21
- const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, path.join(options.cwd || process.cwd(), c.source))));
22
+ const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, options.config, c.source)));
22
23
  for (const si of serviceInfos) {
23
24
  si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory);
24
25
  }
26
+ const unmatchedFilters = filters === null || filters === void 0 ? void 0 : filters.filter((f) => {
27
+ const serviceMatched = serviceInfos.some((s) => s.dataConnectYaml.serviceId === f.serviceId);
28
+ const connectorMatched = f.connectorId
29
+ ? serviceInfos.some((s) => {
30
+ return (s.dataConnectYaml.serviceId === f.serviceId &&
31
+ s.connectorInfo.some((c) => c.connectorYaml.connectorId === f.connectorId));
32
+ })
33
+ : true;
34
+ return !serviceMatched || !connectorMatched;
35
+ });
36
+ if (unmatchedFilters === null || unmatchedFilters === void 0 ? void 0 : unmatchedFilters.length) {
37
+ throw new error_1.FirebaseError(`The following filters were specified in --only but didn't match anything in this project: ${unmatchedFilters.map(filters_1.toString).map(clc.bold).join(", ")}`);
38
+ }
25
39
  context.dataconnect = {
26
40
  serviceInfos,
27
41
  filters,
@@ -106,6 +106,7 @@ Constants.SERVICE_FIRESTORE = "firestore.googleapis.com";
106
106
  Constants.SERVICE_REALTIME_DATABASE = "firebaseio.com";
107
107
  Constants.SERVICE_PUBSUB = "pubsub.googleapis.com";
108
108
  Constants.SERVICE_EVENTARC = "eventarc.googleapis.com";
109
+ Constants.SERVICE_FIREALERTS = "firebasealerts.googleapis.com";
109
110
  Constants.SERVICE_ANALYTICS = "app-measurement.com";
110
111
  Constants.SERVICE_AUTH = "firebaseauth.googleapis.com";
111
112
  Constants.SERVICE_CRASHLYTICS = "fabric.io";
@@ -526,17 +526,14 @@ async function startAll(options, showUI = true, runningTestScript = false) {
526
526
  else if (config.length > 1) {
527
527
  logger_1.logger.warn(`TODO: Add support for multiple services in the Data Connect emulator. Currently emulating first service ${config[0].source}`);
528
528
  }
529
- let configDir = config[0].source;
530
- if (!path.isAbsolute(configDir)) {
531
- const cwd = options.cwd || process.cwd();
532
- configDir = path.resolve(path.join(cwd), configDir);
533
- }
529
+ const configDir = config[0].source;
534
530
  const dataConnectEmulator = new dataconnectEmulator_1.DataConnectEmulator({
535
531
  listen: listenForEmulator.dataconnect,
536
532
  projectId,
537
533
  auto_download: true,
538
534
  configDir,
539
535
  rc: options.rc,
536
+ config: options.config,
540
537
  });
541
538
  await startEmulator(dataConnectEmulator);
542
539
  }
@@ -27,7 +27,8 @@ class DataConnectEmulator {
27
27
  }
28
28
  async start() {
29
29
  try {
30
- const info = await DataConnectEmulator.build({ configDir: this.args.configDir });
30
+ const resolvedConfigDir = this.args.config.path(this.args.configDir);
31
+ const info = await DataConnectEmulator.build({ configDir: resolvedConfigDir });
31
32
  if ((0, types_2.requiresVector)(info.metadata)) {
32
33
  if (constants_1.Constants.isDemoProject(this.args.projectId)) {
33
34
  this.logger.logLabeled("WARN", "Data Connect", "Detected a 'demo-' project, but vector embeddings require a real project. Operations that use vector_embed will fail.");
@@ -143,7 +144,7 @@ class DataConnectEmulator {
143
144
  if (!emuInfo) {
144
145
  return false;
145
146
  }
146
- const serviceInfo = await (0, load_1.load)(this.args.projectId, this.args.configDir);
147
+ const serviceInfo = await (0, load_1.load)(this.args.projectId, this.args.config, this.args.configDir);
147
148
  const sameService = emuInfo.services.find((s) => serviceInfo.dataConnectYaml.serviceId === s.serviceId);
148
149
  if (!sameService) {
149
150
  throw new error_1.FirebaseError(`There is a Data Connect emulator already running on ${this.args.listen[0].address}:${this.args.listen[0].port}, but it is emulating a different service. Please stop that instance of the Data Connect emulator, or specify a different port in 'firebase.json'`);
@@ -35,9 +35,9 @@ const EMULATOR_UPDATE_DETAILS = {
35
35
  ui: experiments.isEnabled("emulatoruisnapshot")
36
36
  ? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
37
37
  : {
38
- version: "1.12.1",
39
- expectedSize: 3498269,
40
- expectedChecksum: "a7f4398a00e5ca22abdcd78dc3877d00",
38
+ version: "1.13.0",
39
+ expectedSize: 3605485,
40
+ expectedChecksum: "ec0aa91592c56af9ff7df18168d58459",
41
41
  },
42
42
  pubsub: {
43
43
  version: "0.8.14",
@@ -46,20 +46,20 @@ const EMULATOR_UPDATE_DETAILS = {
46
46
  },
47
47
  dataconnect: process.platform === "darwin"
48
48
  ? {
49
- version: "1.2.4",
50
- expectedSize: 24097600,
51
- expectedChecksum: "e0a344620b71d64b79d99b2c358e7646",
49
+ version: "1.3.0",
50
+ expectedSize: 24175424,
51
+ expectedChecksum: "e0aefd484499308434a0405a5a55574c",
52
52
  }
53
53
  : process.platform === "win32"
54
54
  ? {
55
- version: "1.2.4",
56
- expectedSize: 24509440,
57
- expectedChecksum: "44ebc07e481a85bd735fe8007fab3efe",
55
+ version: "1.3.0",
56
+ expectedSize: 24585728,
57
+ expectedChecksum: "c7f9fad2025d9f2c9d2dff44a3edebbe",
58
58
  }
59
59
  : {
60
- version: "1.2.4",
61
- expectedSize: 24010904,
62
- expectedChecksum: "bf10e866f4c4e132bf8115460508e6e2",
60
+ version: "1.3.0",
61
+ expectedSize: 24084632,
62
+ expectedChecksum: "f332b33b67680a32ea76bea866540656",
63
63
  },
64
64
  };
65
65
  exports.DownloadDetails = {
@@ -9,22 +9,36 @@ const emulatorLogger_1 = require("./emulatorLogger");
9
9
  const registry_1 = require("./registry");
10
10
  const error_1 = require("../error");
11
11
  const eventarcEmulatorUtils_1 = require("./eventarcEmulatorUtils");
12
+ const cors = require("cors");
13
+ const GOOGLE_CHANNEL = "google";
12
14
  class EventarcEmulator {
13
15
  constructor(args) {
14
16
  this.args = args;
15
17
  this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.EVENTARC);
16
- this.customEvents = {};
18
+ this.events = {};
17
19
  }
18
20
  createHubServer() {
19
21
  const registerTriggerRoute = `/emulator/v1/projects/:project_id/triggers/:trigger_name(*)`;
20
22
  const registerTriggerHandler = (req, res) => {
23
+ try {
24
+ const { projectId, triggerName, eventTrigger, key } = getTriggerIdentifiers(req);
25
+ this.logger.logLabeled("BULLET", "eventarc", `Registering Eventarc event trigger for ${key} with trigger name ${triggerName}.`);
26
+ const eventTriggers = this.events[key] || [];
27
+ eventTriggers.push({ projectId, triggerName, eventTrigger });
28
+ this.events[key] = eventTriggers;
29
+ res.status(200).send({ res: "OK" });
30
+ }
31
+ catch (error) {
32
+ res.status(400).send({ error });
33
+ }
34
+ };
35
+ const getTriggerIdentifiers = (req) => {
21
36
  const projectId = req.params.project_id;
22
37
  const triggerName = req.params.trigger_name;
23
38
  if (!projectId || !triggerName) {
24
39
  const error = "Missing project ID or trigger name.";
25
40
  this.logger.log("ERROR", error);
26
- res.status(400).send({ error });
27
- return;
41
+ throw error;
28
42
  }
29
43
  const bodyString = req.rawBody.toString();
30
44
  const substituted = bodyString.replaceAll("${PROJECT_ID}", projectId);
@@ -33,27 +47,56 @@ class EventarcEmulator {
33
47
  if (!eventTrigger) {
34
48
  const error = `Missing event trigger for ${triggerName}.`;
35
49
  this.logger.log("ERROR", error);
50
+ throw error;
51
+ }
52
+ const channel = eventTrigger.channel || GOOGLE_CHANNEL;
53
+ const key = `${eventTrigger.eventType}-${channel}`;
54
+ return { projectId, triggerName, eventTrigger, key };
55
+ };
56
+ const removeTriggerRoute = `/emulator/v1/remove/projects/:project_id/triggers/:trigger_name`;
57
+ const removeTriggerHandler = (req, res) => {
58
+ try {
59
+ const { projectId, triggerName, eventTrigger, key } = getTriggerIdentifiers(req);
60
+ this.logger.logLabeled("BULLET", "eventarc", `Removing Eventarc event trigger for ${key} with trigger name ${triggerName}.`);
61
+ const eventTriggers = this.events[key] || [];
62
+ const triggerIdentifier = { projectId, triggerName, eventTrigger };
63
+ const removeIdx = eventTriggers.findIndex((e) => JSON.stringify(triggerIdentifier) === JSON.stringify(e));
64
+ if (removeIdx === -1) {
65
+ this.logger.logLabeled("ERROR", "eventarc", "Tried to remove nonexistent trigger");
66
+ throw new Error(`Unable to delete function trigger ${triggerName}`);
67
+ }
68
+ eventTriggers.splice(removeIdx, 1);
69
+ if (eventTriggers.length === 0) {
70
+ delete this.events[key];
71
+ }
72
+ else {
73
+ this.events[key] = eventTriggers;
74
+ }
75
+ res.status(200).send({ res: "OK" });
76
+ }
77
+ catch (error) {
36
78
  res.status(400).send({ error });
37
- return;
38
79
  }
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" });
80
+ };
81
+ const getTriggersRoute = `/google/getTriggers`;
82
+ const getTriggersHandler = (req, res) => {
83
+ res.status(200).send(this.events);
45
84
  };
46
85
  const publishEventsRoute = `/projects/:project_id/locations/:location/channels/:channel::publishEvents`;
86
+ const publishNativeEventsRoute = `/google/publishEvents`;
47
87
  const publishEventsHandler = (req, res) => {
48
- const channel = `projects/${req.params.project_id}/locations/${req.params.location}/channels/${req.params.channel}`;
88
+ const isCustom = req.params.project_id && req.params.channel;
89
+ const channel = isCustom
90
+ ? `projects/${req.params.project_id}/locations/${req.params.location}/channels/${req.params.channel}`
91
+ : GOOGLE_CHANNEL;
49
92
  const body = JSON.parse(req.rawBody.toString());
50
93
  for (const event of body.events) {
51
94
  if (!event.type) {
52
95
  res.sendStatus(400);
53
96
  return;
54
97
  }
55
- this.logger.log("INFO", `Received custom event at channel ${channel}: ${JSON.stringify(event, null, 2)}`);
56
- this.triggerCustomEventFunction(channel, event);
98
+ this.logger.log("INFO", `Received event at channel ${channel}: ${JSON.stringify(event, null, 2)}`);
99
+ this.triggerEventFunction(channel, event);
57
100
  }
58
101
  res.sendStatus(200);
59
102
  };
@@ -70,27 +113,34 @@ class EventarcEmulator {
70
113
  const hub = express();
71
114
  hub.post([registerTriggerRoute], dataMiddleware, registerTriggerHandler);
72
115
  hub.post([publishEventsRoute], dataMiddleware, publishEventsHandler);
116
+ hub.post([publishNativeEventsRoute], dataMiddleware, cors({ origin: true }), publishEventsHandler);
117
+ hub.post([removeTriggerRoute], dataMiddleware, removeTriggerHandler);
118
+ hub.get([getTriggersRoute], cors({ origin: true }), getTriggersHandler);
73
119
  hub.all("*", (req, res) => {
74
120
  this.logger.log("DEBUG", `Eventarc emulator received unknown request at path ${req.path}`);
75
121
  res.sendStatus(404);
76
122
  });
77
123
  return hub;
78
124
  }
79
- async triggerCustomEventFunction(channel, event) {
125
+ async triggerEventFunction(channel, event) {
80
126
  if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.FUNCTIONS)) {
81
127
  this.logger.log("INFO", "Functions emulator not found. This should not happen.");
82
128
  return Promise.reject();
83
129
  }
84
130
  const key = `${event.type}-${channel}`;
85
- const triggers = this.customEvents[key] || [];
131
+ const triggers = this.events[key] || [];
132
+ const eventPayload = channel === GOOGLE_CHANNEL ? event : (0, eventarcEmulatorUtils_1.cloudEventFromProtoToJson)(event);
86
133
  return await Promise.all(triggers
87
134
  .filter((trigger) => !trigger.eventTrigger.eventFilters ||
88
135
  this.matchesAll(event, trigger.eventTrigger.eventFilters))
89
- .map((trigger) => registry_1.EmulatorRegistry.client(types_1.Emulators.FUNCTIONS)
136
+ .map((trigger) => this.callFunctionTrigger(trigger, eventPayload)));
137
+ }
138
+ callFunctionTrigger(trigger, event) {
139
+ return registry_1.EmulatorRegistry.client(types_1.Emulators.FUNCTIONS)
90
140
  .request({
91
141
  method: "POST",
92
142
  path: `/functions/projects/${trigger.projectId}/triggers/${trigger.triggerName}`,
93
- body: JSON.stringify((0, eventarcEmulatorUtils_1.cloudEventFromProtoToJson)(event)),
143
+ body: JSON.stringify(event),
94
144
  responseType: "stream",
95
145
  resolveOnHTTPError: true,
96
146
  })
@@ -101,7 +151,7 @@ class EventarcEmulator {
101
151
  })
102
152
  .catch((err) => {
103
153
  this.logger.log("ERROR", `Failed to trigger Functions emulator for ${trigger.triggerName}: ${err}`);
104
- })));
154
+ });
105
155
  }
106
156
  matchesAll(event, eventFilters) {
107
157
  return Object.entries(eventFilters).every(([key, value]) => {
@@ -344,6 +344,15 @@ class FunctionsEmulator {
344
344
  else {
345
345
  this.workerPools[emulatableBackend.codebase].refresh();
346
346
  }
347
+ const toRemove = Object.keys(this.triggers).filter((recordKey) => {
348
+ const record = this.getTriggerRecordByKey(recordKey);
349
+ if (force) {
350
+ return true;
351
+ }
352
+ return !triggerDefinitions.some((def) => record.def.entryPoint === def.entryPoint &&
353
+ JSON.stringify(record.def.eventTrigger) === JSON.stringify(def.eventTrigger));
354
+ });
355
+ await this.removeTriggers(toRemove);
347
356
  const toSetup = triggerDefinitions.filter((definition) => {
348
357
  if (force) {
349
358
  return true;
@@ -394,6 +403,9 @@ class FunctionsEmulator {
394
403
  case constants_1.Constants.SERVICE_STORAGE:
395
404
  added = this.addStorageTrigger(this.args.projectId, key, definition.eventTrigger);
396
405
  break;
406
+ case constants_1.Constants.SERVICE_FIREALERTS:
407
+ added = await this.addFirealertsTrigger(this.args.projectId, key, definition.eventTrigger);
408
+ break;
397
409
  default:
398
410
  this.logger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`);
399
411
  break;
@@ -442,6 +454,25 @@ class FunctionsEmulator {
442
454
  }
443
455
  }
444
456
  }
457
+ async removeTriggers(toRemove) {
458
+ for (const triggerKey of toRemove) {
459
+ const definition = this.triggers[triggerKey].def;
460
+ const service = (0, functionsEmulatorShared_1.getFunctionService)(definition);
461
+ const key = this.getTriggerKey(definition);
462
+ switch (service) {
463
+ case constants_1.Constants.SERVICE_EVENTARC:
464
+ await this.removeEventarcTrigger(this.args.projectId, key, definition.eventTrigger);
465
+ delete this.triggers[key];
466
+ break;
467
+ case constants_1.Constants.SERVICE_FIREALERTS:
468
+ await this.removeFirealertsTrigger(this.args.projectId, key, definition.eventTrigger);
469
+ delete this.triggers[key];
470
+ break;
471
+ default:
472
+ break;
473
+ }
474
+ }
475
+ }
445
476
  addEventarcTrigger(projectId, key, eventTrigger) {
446
477
  if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
447
478
  return Promise.resolve(false);
@@ -458,6 +489,54 @@ class FunctionsEmulator {
458
489
  return false;
459
490
  });
460
491
  }
492
+ removeEventarcTrigger(projectId, key, eventTrigger) {
493
+ if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
494
+ return Promise.resolve(false);
495
+ }
496
+ const bundle = {
497
+ eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "eventarc.googleapis.com" }),
498
+ };
499
+ logger_1.logger.debug(`removeEventarcTrigger`, JSON.stringify(bundle));
500
+ return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
501
+ .post(`/emulator/v1/remove/projects/${projectId}/triggers/${key}`, bundle)
502
+ .then(() => true)
503
+ .catch((err) => {
504
+ this.logger.log("WARN", "Error removing Eventarc function: " + err);
505
+ return false;
506
+ });
507
+ }
508
+ addFirealertsTrigger(projectId, key, eventTrigger) {
509
+ if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
510
+ return Promise.resolve(false);
511
+ }
512
+ const bundle = {
513
+ eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firebasealerts.googleapis.com" }),
514
+ };
515
+ logger_1.logger.debug(`addFirealertsTrigger`, JSON.stringify(bundle));
516
+ return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
517
+ .post(`/emulator/v1/projects/${projectId}/triggers/${key}`, bundle)
518
+ .then(() => true)
519
+ .catch((err) => {
520
+ this.logger.log("WARN", "Error adding FireAlerts function: " + err);
521
+ return false;
522
+ });
523
+ }
524
+ removeFirealertsTrigger(projectId, key, eventTrigger) {
525
+ if (!registry_1.EmulatorRegistry.isRunning(types_1.Emulators.EVENTARC)) {
526
+ return Promise.resolve(false);
527
+ }
528
+ const bundle = {
529
+ eventTrigger: Object.assign(Object.assign({}, eventTrigger), { service: "firebasealerts.googleapis.com" }),
530
+ };
531
+ logger_1.logger.debug(`removeFirealertsTrigger`, JSON.stringify(bundle));
532
+ return registry_1.EmulatorRegistry.client(types_1.Emulators.EVENTARC)
533
+ .post(`/emulator/v1/remove/projects/${projectId}/triggers/${key}`, bundle)
534
+ .then(() => true)
535
+ .catch((err) => {
536
+ this.logger.log("WARN", "Error removing FireAlerts function: " + err);
537
+ return false;
538
+ });
539
+ }
461
540
  async performPostLoadOperations() {
462
541
  if (!this.blockingFunctionsConfig.triggers &&
463
542
  !this.blockingFunctionsConfig.forwardInboundCredentials) {
@@ -17,6 +17,7 @@ const events = require("../functions/events");
17
17
  const utils_1 = require("../utils");
18
18
  const V2_EVENTS = [
19
19
  events.v2.PUBSUB_PUBLISH_EVENT,
20
+ events.v2.FIREALERTS_EVENT,
20
21
  ...events.v2.STORAGE_EVENTS,
21
22
  ...events.v2.DATABASE_EVENTS,
22
23
  ...events.v2.FIRESTORE_EVENTS,
@@ -204,6 +205,9 @@ function getServiceFromEventType(eventType) {
204
205
  if (eventType.includes("storage")) {
205
206
  return constants_1.Constants.SERVICE_STORAGE;
206
207
  }
208
+ if (eventType.includes("firebasealerts")) {
209
+ return constants_1.Constants.SERVICE_FIREALERTS;
210
+ }
207
211
  if (eventType.includes("analytics")) {
208
212
  return constants_1.Constants.SERVICE_ANALYTICS;
209
213
  }
@@ -132,7 +132,7 @@ class PubsubEmulator {
132
132
  };
133
133
  }
134
134
  createCloudEventRequestBody(topic, message) {
135
- const truncatedPublishTime = new Date(message.publishTime.getMilliseconds()).toISOString();
135
+ const truncatedPublishTime = new Date(message.publishTime.getTime()).toISOString();
136
136
  const data = {
137
137
  message: {
138
138
  messageId: message.id,
@@ -102,6 +102,11 @@ exports.ALL_EXPERIMENTS = experiments({
102
102
  default: true,
103
103
  public: false,
104
104
  },
105
+ fdccompatiblemode: {
106
+ shortDescription: "Enable Data Connect schema migrations in Compatible Mode",
107
+ fullDescription: "Enable Data Connect schema migrations in Compatible Mode",
108
+ public: false,
109
+ },
105
110
  });
106
111
  function isValidExperiment(name) {
107
112
  return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = void 0;
3
+ exports.tryToGetOptionsForTarget = exports.getAngularVersion = exports.getBuildConfig = exports.getServerConfig = exports.getBrowserConfig = exports.getContext = exports.getAllTargets = void 0;
4
4
  const utils_1 = require("../utils");
5
5
  const error_1 = require("../../error");
6
6
  const path_1 = require("path");
@@ -144,8 +144,9 @@ async function getContext(dir, targetOrConfiguration) {
144
144
  if (apps.length === 1)
145
145
  project = apps[0];
146
146
  }
147
- if (!project)
148
- throw new error_1.FirebaseError("Unable to determine the application to deploy, specify a target via the FIREBASE_FRAMEWORKS_BUILD_TARGET environment variable");
147
+ if (!project) {
148
+ throwCannotDetermineTarget();
149
+ }
149
150
  const workspaceProject = workspace.projects.get(project);
150
151
  if (!workspaceProject)
151
152
  throw new error_1.FirebaseError(`No project ${project} found.`);
@@ -335,14 +336,14 @@ async function getContext(dir, targetOrConfiguration) {
335
336
  if (!buildOrBrowserTarget) {
336
337
  throw new error_1.FirebaseError(`No build target on ${project}`);
337
338
  }
338
- const browserTargetOptions = await architectHost.getOptionsForTarget(buildOrBrowserTarget);
339
+ const browserTargetOptions = await tryToGetOptionsForTarget(architectHost, buildOrBrowserTarget);
339
340
  if (!browserTargetOptions) {
340
341
  const targetString = targetStringFromTarget(buildOrBrowserTarget);
341
342
  throw new error_1.FirebaseError(`Couldn't find options for ${targetString}.`);
342
343
  }
343
344
  const baseHref = browserTargetOptions.baseHref || "/";
344
345
  (0, utils_2.assertIsString)(baseHref);
345
- const buildTargetOptions = buildTarget && (await architectHost.getOptionsForTarget(buildTarget));
346
+ const buildTargetOptions = buildTarget && (await tryToGetOptionsForTarget(architectHost, buildTarget));
346
347
  const ssr = buildTarget ? !!(buildTargetOptions === null || buildTargetOptions === void 0 ? void 0 : buildTargetOptions.ssr) : !!serverTarget;
347
348
  return {
348
349
  architect,
@@ -467,3 +468,10 @@ function getAngularVersion(cwd) {
467
468
  return angularVersionSemver.toString();
468
469
  }
469
470
  exports.getAngularVersion = getAngularVersion;
471
+ async function tryToGetOptionsForTarget(architectHost, target) {
472
+ return await architectHost.getOptionsForTarget(target).catch(throwCannotDetermineTarget);
473
+ }
474
+ exports.tryToGetOptionsForTarget = tryToGetOptionsForTarget;
475
+ function throwCannotDetermineTarget(error) {
476
+ throw new error_1.FirebaseError(`Unable to determine the application to deploy, specify a target via the FIREBASE_FRAMEWORKS_BUILD_TARGET environment variable.`, { original: error });
477
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
3
+ exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIREALERTS_EVENT = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
4
4
  exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
5
5
  exports.STORAGE_EVENTS = [
6
6
  "google.cloud.storage.object.v1.finalized",
@@ -27,5 +27,6 @@ exports.FIRESTORE_EVENTS = [
27
27
  "google.cloud.firestore.document.v1.updated.withAuthContext",
28
28
  "google.cloud.firestore.document.v1.deleted.withAuthContext",
29
29
  ];
30
+ exports.FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
30
31
  exports.FIRESTORE_EVENT_REGEX = /^google\.cloud\.firestore\.document\.v1\.[^\.]*$/;
31
32
  exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = /^google\.cloud\.firestore\.document\.v1\..*\.withAuthContext$/;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateP4SA = exports.serviceAgentEmail = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
3
+ exports.generateP4SA = exports.serviceAgentEmail = exports.sortConnectionsByCreateTime = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.fetchGitHubInstallations = exports.listAllBranches = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
6
  const serviceusage_1 = require("./serviceusage");
@@ -95,6 +95,12 @@ async function listAllBranches(repoLinkName) {
95
95
  return branches;
96
96
  }
97
97
  exports.listAllBranches = listAllBranches;
98
+ async function fetchGitHubInstallations(projectId, location, connectionId) {
99
+ const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}:fetchGitHubInstallations`;
100
+ const res = await exports.client.get(name);
101
+ return res.body.installations;
102
+ }
103
+ exports.fetchGitHubInstallations = fetchGitHubInstallations;
98
104
  async function createGitRepositoryLink(projectId, location, connectionId, gitRepositoryLinkId, cloneUri) {
99
105
  const res = await exports.client.post(`projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}/gitRepositoryLinks`, { cloneUri }, { queryParams: { gitRepositoryLinkId } });
100
106
  return res.body;
@@ -106,6 +112,12 @@ async function getGitRepositoryLink(projectId, location, connectionId, gitReposi
106
112
  return res.body;
107
113
  }
108
114
  exports.getGitRepositoryLink = getGitRepositoryLink;
115
+ function sortConnectionsByCreateTime(connections) {
116
+ return connections.sort((a, b) => {
117
+ return Date.parse(a.createTime) - Date.parse(b.createTime);
118
+ });
119
+ }
120
+ exports.sortConnectionsByCreateTime = sortConnectionsByCreateTime;
109
121
  function serviceAgentEmail(projectNumber) {
110
122
  return `service-${projectNumber}@${(0, api_1.developerConnectP4SADomain)()}`;
111
123
  }
@@ -52,7 +52,7 @@ async function doSetup(setup, config, options) {
52
52
  if (!account) {
53
53
  throw new error_1.FirebaseError(`No account selected, have you run "firebase login"?`, { exit: 1 });
54
54
  }
55
- await (0, auth_1.setActiveAccount)(options, account);
55
+ (0, auth_1.setActiveAccount)(options, account);
56
56
  if (config.projectDir) {
57
57
  (0, auth_1.setProjectAccount)(config.projectDir, account.user.email);
58
58
  }