firebase-tools 13.11.3 → 13.11.4

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.
@@ -200,17 +200,15 @@ function load(client) {
200
200
  client.setup.emulators.pubsub = loadCommand("setup-emulators-pubsub");
201
201
  client.setup.emulators.storage = loadCommand("setup-emulators-storage");
202
202
  client.setup.emulators.ui = loadCommand("setup-emulators-ui");
203
- if (experiments.isEnabled("dataconnect")) {
204
- client.dataconnect = {};
205
- client.setup.emulators.dataconnect = loadCommand("setup-emulators-dataconnect");
206
- client.dataconnect.services = {};
207
- client.dataconnect.services.list = loadCommand("dataconnect-services-list");
208
- client.dataconnect.sql = {};
209
- client.dataconnect.sql.diff = loadCommand("dataconnect-sql-diff");
210
- client.dataconnect.sql.migrate = loadCommand("dataconnect-sql-migrate");
211
- client.dataconnect.sdk = {};
212
- client.dataconnect.sdk.generate = loadCommand("dataconnect-sdk-generate");
213
- }
203
+ client.dataconnect = {};
204
+ client.setup.emulators.dataconnect = loadCommand("setup-emulators-dataconnect");
205
+ client.dataconnect.services = {};
206
+ client.dataconnect.services.list = loadCommand("dataconnect-services-list");
207
+ client.dataconnect.sql = {};
208
+ client.dataconnect.sql.diff = loadCommand("dataconnect-sql-diff");
209
+ client.dataconnect.sql.migrate = loadCommand("dataconnect-sql-migrate");
210
+ client.dataconnect.sdk = {};
211
+ client.dataconnect.sdk.generate = loadCommand("dataconnect-sdk-generate");
214
212
  client.target = loadCommand("target");
215
213
  client.target.apply = loadCommand("target-apply");
216
214
  client.target.clear = loadCommand("target-clear");
@@ -76,13 +76,11 @@ if ((0, experiments_1.isEnabled)("genkit")) {
76
76
  checked: false,
77
77
  });
78
78
  }
79
- if ((0, experiments_1.isEnabled)("dataconnect")) {
80
- choices.push({
81
- value: "dataconnect",
82
- name: "Data Connect: Set up a Firebase Data Connect service.",
83
- checked: false,
84
- });
85
- }
79
+ choices.push({
80
+ value: "dataconnect",
81
+ name: "Data Connect: Set up a Firebase Data Connect service.",
82
+ checked: false,
83
+ });
86
84
  const featureNames = choices.map((choice) => choice.value);
87
85
  const DESCRIPTION = `Interactively configure the current directory as a Firebase project or initialize new features in an already configured Firebase project directory.
88
86
 
@@ -12,7 +12,7 @@ const rimraf = require("rimraf");
12
12
  const utils = require("../utils");
13
13
  const JAR_CACHE_DIR = process.env.FIREBASE_CRASHLYTICS_BUILDTOOLS_PATH ||
14
14
  path.join(os.homedir(), ".cache", "firebase", "crashlytics", "buildtools");
15
- const JAR_VERSION = "3.0.0";
15
+ const JAR_VERSION = "3.0.2";
16
16
  const JAR_URL = `https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/${JAR_VERSION}/firebase-crashlytics-buildtools-${JAR_VERSION}.jar`;
17
17
  async function fetchBuildtoolsJar() {
18
18
  if (process.env.CRASHLYTICS_LOCAL_JAR) {
@@ -132,7 +132,7 @@ async function listConnectors(serviceName) {
132
132
  pageToken,
133
133
  },
134
134
  });
135
- connectors.push(...res.body.connectors);
135
+ connectors.push(...(res.body.connectors || []));
136
136
  if (res.body.nextPageToken) {
137
137
  await getNextPage(res.body.nextPageToken);
138
138
  }
@@ -539,9 +539,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
539
539
  rc: options.rc,
540
540
  });
541
541
  await startEmulator(dataConnectEmulator);
542
- if (!utils.isVSCodeExtension()) {
543
- await dataConnectEmulator.connectToPostgres();
544
- }
545
542
  }
546
543
  if (listenForEmulator.storage) {
547
544
  const storageAddr = legacyGetFirstAddr(types_1.Emulators.STORAGE);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DataConnectEmulatorClient = exports.DataConnectEmulator = void 0;
3
+ exports.checkIfDataConnectEmulatorRunningOnAddress = exports.DataConnectEmulatorClient = exports.DataConnectEmulator = exports.dataConnectEmulatorEvents = void 0;
4
4
  const childProcess = require("child_process");
5
5
  const clc = require("colorette");
6
6
  const api_1 = require("../api");
@@ -11,10 +11,16 @@ const error_1 = require("../error");
11
11
  const emulatorLogger_1 = require("./emulatorLogger");
12
12
  const types_2 = require("../dataconnect/types");
13
13
  const portUtils_1 = require("./portUtils");
14
+ const apiv2_1 = require("../apiv2");
14
15
  const registry_1 = require("./registry");
16
+ const load_1 = require("../dataconnect/load");
17
+ const utils_1 = require("../utils");
18
+ const events_1 = require("events");
19
+ exports.dataConnectEmulatorEvents = new events_1.EventEmitter();
15
20
  class DataConnectEmulator {
16
21
  constructor(args) {
17
22
  this.args = args;
23
+ this.usingExistingEmulator = false;
18
24
  this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.DATACONNECT);
19
25
  this.emulatorClient = new DataConnectEmulatorClient();
20
26
  }
@@ -33,17 +39,39 @@ class DataConnectEmulator {
33
39
  catch (err) {
34
40
  this.logger.log("DEBUG", `'fdc build' failed with error: ${err.message}`);
35
41
  }
36
- return (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
37
- auto_download: this.args.auto_download,
38
- listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
39
- config_dir: this.args.configDir,
40
- service_location: this.args.locationId,
41
- });
42
+ const alreadyRunning = await this.discoverRunningInstance();
43
+ if (alreadyRunning) {
44
+ this.logger.logLabeled("INFO", "Data Connect", "Detected an instance of the emulator already running with your service, reusing it. This emulator will not be shut down at the end of this command.");
45
+ this.usingExistingEmulator = true;
46
+ this.watchUnmanagedInstance();
47
+ }
48
+ else {
49
+ await (0, downloadableEmulators_1.start)(types_1.Emulators.DATACONNECT, {
50
+ auto_download: this.args.auto_download,
51
+ listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
52
+ config_dir: this.args.configDir,
53
+ service_location: this.args.locationId,
54
+ });
55
+ this.usingExistingEmulator = false;
56
+ }
57
+ if (!(0, utils_1.isVSCodeExtension)()) {
58
+ await this.connectToPostgres();
59
+ }
60
+ return;
42
61
  }
43
- connect() {
62
+ async connect() {
63
+ const emuInfo = await this.emulatorClient.getInfo();
64
+ if (!emuInfo) {
65
+ this.logger.logLabeled("ERROR", "Data Connect", "Could not connect to Data Connect emulator. Check dataconnect-debug.log for more details.");
66
+ return Promise.reject();
67
+ }
44
68
  return Promise.resolve();
45
69
  }
46
- stop() {
70
+ async stop() {
71
+ if (this.usingExistingEmulator) {
72
+ this.logger.logLabeled("INFO", "Data Connect", "Skipping cleanup of Data Connect emulator, as it was not started by this process.");
73
+ return;
74
+ }
47
75
  return (0, downloadableEmulators_1.stop)(types_1.Emulators.DATACONNECT);
48
76
  }
49
77
  getInfo() {
@@ -107,6 +135,35 @@ class DataConnectEmulator {
107
135
  }
108
136
  return (_b = (_a = this.args.rc.getDataconnect()) === null || _a === void 0 ? void 0 : _a.postgres) === null || _b === void 0 ? void 0 : _b.localConnectionString;
109
137
  }
138
+ async discoverRunningInstance() {
139
+ const emuInfo = await this.emulatorClient.getInfo();
140
+ if (!emuInfo) {
141
+ return false;
142
+ }
143
+ const serviceInfo = await (0, load_1.load)(this.args.projectId, this.args.locationId, this.args.configDir);
144
+ const sameService = emuInfo.services.find((s) => serviceInfo.dataConnectYaml.serviceId === s.serviceId);
145
+ if (!sameService) {
146
+ 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'`);
147
+ }
148
+ if (sameService.connectionString &&
149
+ sameService.connectionString !== this.getLocalConectionString()) {
150
+ throw new error_1.FirebaseError(`There is a Data Connect emulator already running, but it is using a different Postgres connection string. Please stop that instance of the Data Connect emulator, or specify a different port in 'firebase.json'`);
151
+ }
152
+ return true;
153
+ }
154
+ watchUnmanagedInstance() {
155
+ return setInterval(async () => {
156
+ if (!this.usingExistingEmulator) {
157
+ return;
158
+ }
159
+ const emuInfo = await this.emulatorClient.getInfo();
160
+ if (!emuInfo) {
161
+ this.logger.logLabeled("INFO", "Data Connect", "The already running emulator seems to have shut down. Starting a new instance of the Data Connect emulator...");
162
+ await this.start();
163
+ exports.dataConnectEmulatorEvents.emit("restart");
164
+ }
165
+ }, 5000);
166
+ }
110
167
  async connectToPostgres(localConnectionString, database, serviceId) {
111
168
  const connectionString = localConnectionString !== null && localConnectionString !== void 0 ? localConnectionString : this.getLocalConectionString();
112
169
  if (!connectionString) {
@@ -114,8 +171,22 @@ class DataConnectEmulator {
114
171
  Run ${clc.bold("firebase setup:emulators:dataconnect")} to set up a Postgres connection.`;
115
172
  throw new error_1.FirebaseError(msg);
116
173
  }
117
- await this.emulatorClient.configureEmulator({ connectionString, database, serviceId });
118
- return true;
174
+ const MAX_RETRIES = 3;
175
+ for (let i = 1; i <= MAX_RETRIES; i++) {
176
+ try {
177
+ this.logger.logLabeled("DEBUG", "Data Connect", `Connecting to ${connectionString}}`);
178
+ await this.emulatorClient.configureEmulator({ connectionString, database, serviceId });
179
+ return true;
180
+ }
181
+ catch (err) {
182
+ if (i === MAX_RETRIES) {
183
+ throw err;
184
+ }
185
+ this.logger.logLabeled("DEBUG", "Data Connect", `Retrying connectToPostgress call (${i} of ${MAX_RETRIES} attempts): ${err}`);
186
+ await new Promise((resolve) => setTimeout(resolve, 800));
187
+ }
188
+ }
189
+ return false;
119
190
  }
120
191
  }
121
192
  exports.DataConnectEmulator = DataConnectEmulator;
@@ -124,6 +195,7 @@ class DataConnectEmulatorClient {
124
195
  this.client = undefined;
125
196
  }
126
197
  async configureEmulator(body) {
198
+ var _a, _b;
127
199
  if (!this.client) {
128
200
  this.client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATACONNECT);
129
201
  }
@@ -133,10 +205,33 @@ class DataConnectEmulatorClient {
133
205
  }
134
206
  catch (err) {
135
207
  if (err.status === 500) {
136
- throw new error_1.FirebaseError(`Data Connect emulator: ${err.context.body.message}`);
208
+ throw new error_1.FirebaseError(`Data Connect emulator: ${(_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.message}`);
137
209
  }
138
210
  throw err;
139
211
  }
140
212
  }
213
+ async getInfo() {
214
+ if (!this.client) {
215
+ this.client = registry_1.EmulatorRegistry.client(types_1.Emulators.DATACONNECT);
216
+ }
217
+ return getInfo(this.client);
218
+ }
141
219
  }
142
220
  exports.DataConnectEmulatorClient = DataConnectEmulatorClient;
221
+ async function checkIfDataConnectEmulatorRunningOnAddress(l) {
222
+ const client = new apiv2_1.Client({
223
+ urlPrefix: `http:/${l.family === "IPv6" ? `[${l.address}]` : l.address}:${l.port}`,
224
+ auth: false,
225
+ });
226
+ return getInfo(client);
227
+ }
228
+ exports.checkIfDataConnectEmulatorRunningOnAddress = checkIfDataConnectEmulatorRunningOnAddress;
229
+ async function getInfo(client) {
230
+ try {
231
+ const res = await client.get("emulator/info");
232
+ return res.body;
233
+ }
234
+ catch (err) {
235
+ return;
236
+ }
237
+ }
@@ -149,7 +149,7 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
149
149
  const locatorPath = EmulatorHub.getLocatorFilePath(this.args.projectId);
150
150
  return new Promise((resolve, reject) => {
151
151
  fs.unlink(locatorPath, (e) => {
152
- if (e) {
152
+ if (e && e.code !== "ENOENT") {
153
153
  reject(e);
154
154
  }
155
155
  else {
@@ -11,6 +11,7 @@ const types_1 = require("./types");
11
11
  const constants_1 = require("./constants");
12
12
  const emulatorLogger_1 = require("./emulatorLogger");
13
13
  const node_child_process_1 = require("node:child_process");
14
+ const dataconnectEmulator_1 = require("./dataconnectEmulator");
14
15
  const RESTRICTED_PORTS = new Set([
15
16
  1,
16
17
  7,
@@ -219,6 +220,14 @@ async function resolveHostAndAssignPorts(listenConfig) {
219
220
  available.push(listen);
220
221
  }
221
222
  else {
223
+ if (/^dataconnect/i.exec(name)) {
224
+ const alreadyRunning = await (0, dataconnectEmulator_1.checkIfDataConnectEmulatorRunningOnAddress)(listen);
225
+ if (alreadyRunning) {
226
+ emuLogger.logLabeled("DEBUG", "dataconnect", `Detected already running emulator on ${listen.address}:${listen.port}. Will attempt to reuse it.`);
227
+ }
228
+ available.push(listen);
229
+ continue;
230
+ }
222
231
  if (!portFixed) {
223
232
  if (i > 0) {
224
233
  emuLogger.logLabeled("DEBUG", name, `Port ${p} taken on secondary address ${addr.address}, will keep searching to find a better port.`);
@@ -92,8 +92,8 @@ exports.ALL_EXPERIMENTS = experiments({
92
92
  public: false,
93
93
  },
94
94
  dataconnect: {
95
- shortDescription: "Enable Data Connect related features.",
96
- fullDescription: "Enable Data Connect related features.",
95
+ shortDescription: "Deprecated. Previosuly, enabled Data Connect related features.",
96
+ fullDescription: "Deprecated. Previously, enabled Data Connect related features.",
97
97
  public: false,
98
98
  },
99
99
  genkit: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.11.3",
3
+ "version": "13.11.4",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {