firebase-tools 14.6.0 → 14.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 (32) hide show
  1. package/lib/commands/functions-delete.js +0 -2
  2. package/lib/commands/functions-list.js +23 -32
  3. package/lib/commands/init.js +14 -1
  4. package/lib/crashlytics/listTopIssues.js +2 -1
  5. package/lib/deploy/functions/checkIam.js +2 -2
  6. package/lib/emulator/commandUtils.js +2 -1
  7. package/lib/emulator/controller.js +1 -1
  8. package/lib/emulator/dataconnect/pgliteServer.js +124 -102
  9. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  10. package/lib/ensureApiEnabled.js +22 -0
  11. package/lib/fsutils.js +16 -1
  12. package/lib/gcp/serviceusage.js +2 -2
  13. package/lib/gcp/storage.js +8 -4
  14. package/lib/mcp/index.js +15 -1
  15. package/lib/mcp/tools/crashlytics/list_top_issues.js +7 -2
  16. package/lib/mcp/tools/dataconnect/emulator.js +3 -19
  17. package/lib/mcp/tools/dataconnect/execute_graphql.js +1 -1
  18. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +1 -1
  19. package/lib/mcp/tools/dataconnect/execute_mutation.js +1 -1
  20. package/lib/mcp/tools/dataconnect/execute_query.js +1 -1
  21. package/lib/mcp/tools/firestore/delete_document.js +2 -2
  22. package/lib/mcp/tools/firestore/get_documents.js +2 -2
  23. package/lib/mcp/tools/firestore/list_collections.js +2 -2
  24. package/lib/mcp/tools/firestore/query_collection.js +2 -2
  25. package/lib/mcp/tools/storage/get_download_url.js +8 -2
  26. package/lib/operation-poller.js +3 -1
  27. package/lib/track.js +4 -0
  28. package/package.json +4 -2
  29. package/templates/init/functions/javascript/index.js +14 -1
  30. package/templates/init/functions/python/main.py +8 -0
  31. package/templates/init/functions/typescript/index.ts +14 -1
  32. package/lib/mcp/tools/firestore/emulator.js +0 -16
@@ -16,7 +16,6 @@ const planner = require("../deploy/functions/release/planner");
16
16
  const fabricator = require("../deploy/functions/release/fabricator");
17
17
  const executor = require("../deploy/functions/release/executor");
18
18
  const reporter = require("../deploy/functions/release/reporter");
19
- const containerCleaner = require("../deploy/functions/containerCleaner");
20
19
  const getProjectNumber_1 = require("../getProjectNumber");
21
20
  exports.command = new command_1.Command("functions:delete [filters...]")
22
21
  .description("delete one or more Cloud Functions by name or group name.")
@@ -91,5 +90,4 @@ exports.command = new command_1.Command("functions:delete [filters...]")
91
90
  exit: 1,
92
91
  });
93
92
  }
94
- await containerCleaner.cleanupBuildImages([], allEpToDelete);
95
93
  });
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
- const error_1 = require("../error");
6
5
  const projectUtils_1 = require("../projectUtils");
7
6
  const requirePermissions_1 = require("../requirePermissions");
8
7
  const backend = require("../deploy/functions/backend");
@@ -12,36 +11,28 @@ exports.command = new command_1.Command("functions:list")
12
11
  .description("list all deployed functions in your Firebase project")
13
12
  .before(requirePermissions_1.requirePermissions, ["cloudfunctions.functions.list"])
14
13
  .action(async (options) => {
15
- try {
16
- const context = {
17
- projectId: (0, projectUtils_1.needProjectId)(options),
18
- };
19
- const existing = await backend.existingBackend(context);
20
- const endpointsList = backend.allEndpoints(existing).sort(backend.compareFunctions);
21
- const table = new Table({
22
- head: ["Function", "Version", "Trigger", "Location", "Memory", "Runtime"],
23
- style: { head: ["yellow"] },
24
- });
25
- for (const endpoint of endpointsList) {
26
- const trigger = backend.endpointTriggerType(endpoint);
27
- const availableMemoryMb = endpoint.availableMemoryMb || "---";
28
- const entry = [
29
- endpoint.id,
30
- endpoint.platform === "gcfv2" ? "v2" : "v1",
31
- trigger,
32
- endpoint.region,
33
- availableMemoryMb,
34
- endpoint.runtime,
35
- ];
36
- table.push(entry);
37
- }
38
- logger_1.logger.info(table.toString());
39
- return endpointsList;
40
- }
41
- catch (err) {
42
- throw new error_1.FirebaseError("Failed to list functions", {
43
- exit: 1,
44
- original: err,
45
- });
14
+ const context = {
15
+ projectId: (0, projectUtils_1.needProjectId)(options),
16
+ };
17
+ const existing = await backend.existingBackend(context);
18
+ const endpointsList = backend.allEndpoints(existing).sort(backend.compareFunctions);
19
+ const table = new Table({
20
+ head: ["Function", "Version", "Trigger", "Location", "Memory", "Runtime"],
21
+ style: { head: ["yellow"] },
22
+ });
23
+ for (const endpoint of endpointsList) {
24
+ const trigger = backend.endpointTriggerType(endpoint);
25
+ const availableMemoryMb = endpoint.availableMemoryMb || "---";
26
+ const entry = [
27
+ endpoint.id,
28
+ endpoint.platform === "gcfv2" ? "v2" : "v1",
29
+ trigger,
30
+ endpoint.region,
31
+ availableMemoryMb,
32
+ endpoint.runtime,
33
+ ];
34
+ table.push(entry);
46
35
  }
36
+ logger_1.logger.info(table.toString());
37
+ return endpointsList;
47
38
  });
@@ -16,6 +16,7 @@ const utils = require("../utils");
16
16
  const experiments_1 = require("../experiments");
17
17
  const templates_1 = require("../templates");
18
18
  const error_1 = require("../error");
19
+ const track_1 = require("../track");
19
20
  const homeDir = os.homedir();
20
21
  const BANNER_TEXT = (0, templates_1.readTemplateSync)("banner.txt");
21
22
  const GITIGNORE_TEMPLATE = (0, templates_1.readTemplateSync)("_gitignore");
@@ -114,13 +115,14 @@ exports.command = new command_1.Command("init [feature]")
114
115
  .before(requireAuth_1.requireAuth)
115
116
  .action(initAction);
116
117
  async function initAction(feature, options) {
117
- var _a;
118
+ var _a, _b;
118
119
  if (feature && !featureNames.includes(feature)) {
119
120
  return utils.reject(clc.bold(feature) +
120
121
  " is not a supported feature; must be one of " +
121
122
  featureNames.join(", ") +
122
123
  ".");
123
124
  }
125
+ const start = process.uptime();
124
126
  const cwd = options.cwd || process.cwd();
125
127
  const warnings = [];
126
128
  let warningText = "";
@@ -168,6 +170,15 @@ async function initAction(feature, options) {
168
170
  message: "Which Firebase features do you want to set up for this directory? " +
169
171
  "Press Space to select features, then Enter to confirm your choices.",
170
172
  choices: choices.filter((c) => !c.hidden),
173
+ validate: (choices) => {
174
+ if (choices.length === 0) {
175
+ return ("Must select at least one feature. Use " +
176
+ clc.bold(clc.underline("SPACEBAR")) +
177
+ " to select features, or specify a feature by running " +
178
+ clc.bold("firebase init [feature_name]"));
179
+ }
180
+ return true;
181
+ },
171
182
  });
172
183
  }
173
184
  if (!setup.features || ((_a = setup.features) === null || _a === void 0 ? void 0 : _a.length) === 0) {
@@ -191,6 +202,8 @@ async function initAction(feature, options) {
191
202
  if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
192
203
  config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
193
204
  }
205
+ const duration = Math.floor((process.uptime() - start) * 1000);
206
+ await (0, track_1.trackGA4)("product_init", { products_initialized: (_b = setup.features) === null || _b === void 0 ? void 0 : _b.join(",") }, duration);
194
207
  logger_1.logger.info();
195
208
  utils.logSuccess("Firebase initialization complete!");
196
209
  }
@@ -10,10 +10,11 @@ const apiClient = new apiv2_1.Client({
10
10
  urlPrefix: (0, api_1.crashlyticsApiOrigin)(),
11
11
  apiVersion: "v1alpha",
12
12
  });
13
- async function listTopIssues(projectId, appId, issueCount) {
13
+ async function listTopIssues(projectId, appId, issueType, issueCount) {
14
14
  try {
15
15
  const queryParams = new URLSearchParams();
16
16
  queryParams.set("page_size", `${issueCount}`);
17
+ queryParams.set("filter.issue.error_types", `${issueType}`);
17
18
  const requestProjectId = parseProjectId(appId);
18
19
  if (requestProjectId === undefined) {
19
20
  throw new error_1.FirebaseError("Unable to get the projectId from the AppId.");
@@ -120,10 +120,10 @@ async function ensureGenkitMonitoringRoles(projectId, projectNumber, want, have,
120
120
  .map((endpoint) => endpoint.serviceAccount || "")
121
121
  .filter((value, index, self) => self.indexOf(value) === index);
122
122
  const defaultServiceAccountIndex = serviceAccounts.indexOf("");
123
- if (defaultServiceAccountIndex) {
123
+ if (defaultServiceAccountIndex !== -1) {
124
124
  serviceAccounts[defaultServiceAccountIndex] = await gce.getDefaultServiceAccount(projectNumber);
125
125
  }
126
- const members = serviceAccounts.map((sa) => `serviceAccount:${sa}`);
126
+ const members = serviceAccounts.filter((sa) => !!sa).map((sa) => `serviceAccount:${sa}`);
127
127
  const requiredBindings = [];
128
128
  for (const monitoringRole of exports.GENKIT_MONITORING_ROLES) {
129
129
  requiredBindings.push({
@@ -92,7 +92,8 @@ async function beforeEmulatorCommand(options) {
92
92
  const canStartWithoutConfig = options.only &&
93
93
  !controller.shouldStart(optionsWithConfig, types_1.Emulators.FUNCTIONS) &&
94
94
  !controller.shouldStart(optionsWithConfig, types_1.Emulators.HOSTING);
95
- if (!constants_1.Constants.isDemoProject(options.project)) {
95
+ if (!constants_1.Constants.isDemoProject(options.project) ||
96
+ controller.shouldStart(optionsWithConfig, types_1.Emulators.EXTENSIONS)) {
96
97
  try {
97
98
  await (0, requireAuth_1.requireAuth)(options);
98
99
  }
@@ -740,7 +740,7 @@ async function exportEmulatorData(exportPath, options, initiatedBy) {
740
740
  const exportAbsPath = path.resolve(exportPath);
741
741
  if (!fs.existsSync(exportAbsPath)) {
742
742
  utils.logBullet(`Creating export directory ${exportAbsPath}`);
743
- fs.mkdirSync(exportAbsPath);
743
+ fs.mkdirSync(exportAbsPath, { recursive: true });
744
744
  }
745
745
  const existingMetadata = hubExport_1.HubExport.readMetadata(exportAbsPath);
746
746
  const isExportDirEmpty = fs.readdirSync(exportAbsPath).length === 0;
@@ -19,16 +19,17 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
19
19
  function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.fromNodeSocket = exports.PGliteExtendedQueryPatch = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
22
+ exports.fromNodeSocket = exports.PostgresServer = exports.TRUNCATE_TABLES_SQL = void 0;
23
23
  const pglite_1 = require("@electric-sql/pglite");
24
24
  const { dynamicImport } = require(true && "../../dynamicImport");
25
25
  const net = require("node:net");
26
26
  const node_stream_1 = require("node:stream");
27
27
  const fs = require("fs");
28
+ const path = require("node:path");
28
29
  const pg_gateway_1 = require("pg-gateway");
29
30
  const logger_1 = require("../../logger");
30
31
  const error_1 = require("../../error");
31
- const node_string_decoder_1 = require("node:string_decoder");
32
+ const fsutils_1 = require("../../fsutils");
32
33
  exports.TRUNCATE_TABLES_SQL = `
33
34
  DO $do$
34
35
  DECLARE _clear text;
@@ -45,22 +46,43 @@ class PostgresServer {
45
46
  async createPGServer(host = "127.0.0.1", port) {
46
47
  const getDb = this.getDb.bind(this);
47
48
  const server = net.createServer(async (socket) => {
48
- const connection = await fromNodeSocket(socket, {
49
- serverVersion: "16.3 (PGlite 0.2.0)",
49
+ await fromNodeSocket(socket, {
50
+ serverVersion: "17.4 (PGlite 0.3.3)",
50
51
  auth: { method: "trust" },
51
- async onMessage(data, { isAuthenticated }) {
52
- if (!isAuthenticated) {
53
- return;
54
- }
55
- const db = await getDb();
56
- if (data[0] === pg_gateway_1.FrontendMessageCode.Terminate) {
57
- await db.query("DEALLOCATE ALL");
58
- }
59
- const result = await db.execProtocolRaw(data);
60
- return extendedQueryPatch.filterResponse(data, result);
52
+ onMessage(data, { isAuthenticated }) {
53
+ return __asyncGenerator(this, arguments, function* onMessage_1() {
54
+ var _a, e_1, _b, _c;
55
+ if (!isAuthenticated) {
56
+ return yield __await(void 0);
57
+ }
58
+ const db = yield __await(getDb());
59
+ if (data[0] === pg_gateway_1.FrontendMessageCode.Terminate) {
60
+ yield __await(db.query("DEALLOCATE ALL"));
61
+ }
62
+ const response = yield __await(db.execProtocolRaw(data));
63
+ try {
64
+ for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
65
+ _c = _f.value;
66
+ _d = false;
67
+ try {
68
+ const message = _c;
69
+ yield yield __await(message);
70
+ }
71
+ finally {
72
+ _d = true;
73
+ }
74
+ }
75
+ }
76
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
77
+ finally {
78
+ try {
79
+ if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
80
+ }
81
+ finally { if (e_1) throw e_1.error; }
82
+ }
83
+ });
61
84
  },
62
85
  });
63
- const extendedQueryPatch = new PGliteExtendedQueryPatch(connection);
64
86
  socket.on("end", () => {
65
87
  logger_1.logger.debug("Postgres client disconnected");
66
88
  });
@@ -79,30 +101,15 @@ class PostgresServer {
79
101
  }
80
102
  async getDb() {
81
103
  if (!this.db) {
82
- if (this.dataDirectory && !fs.existsSync(this.dataDirectory)) {
83
- fs.mkdirSync(this.dataDirectory, { recursive: true });
84
- }
85
- const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
86
- const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
87
- const pgliteArgs = {
88
- debug: this.debug,
89
- extensions: {
90
- vector,
91
- uuidOssp,
92
- },
93
- dataDir: this.dataDirectory,
94
- };
95
- if (this.importPath) {
96
- logger_1.logger.debug(`Importing from ${this.importPath}`);
97
- const rf = fs.readFileSync(this.importPath);
98
- const file = new File([rf], this.importPath);
99
- pgliteArgs.loadDataDir = file;
100
- }
101
- this.db = await this.forceCreateDB(pgliteArgs);
102
- await this.db.waitReady;
104
+ this.db = await this.forceCreateDB();
103
105
  }
104
106
  return this.db;
105
107
  }
108
+ async getExtensions() {
109
+ const vector = (await dynamicImport("@electric-sql/pglite/vector")).vector;
110
+ const uuidOssp = (await dynamicImport("@electric-sql/pglite/contrib/uuid_ossp")).uuid_ossp;
111
+ return { vector, uuidOssp };
112
+ }
106
113
  async clearDb() {
107
114
  const db = await this.getDb();
108
115
  await db.query(exports.TRUNCATE_TABLES_SQL);
@@ -113,18 +120,92 @@ class PostgresServer {
113
120
  const arrayBuff = await dump.arrayBuffer();
114
121
  fs.writeFileSync(exportPath, new Uint8Array(arrayBuff));
115
122
  }
116
- async forceCreateDB(pgliteArgs) {
123
+ async migrateDb(pgliteArgs) {
124
+ if (!this.baseDataDirectory) {
125
+ throw new error_1.FirebaseError("Cannot migrate database without a data directory.");
126
+ }
127
+ const { PGlite: PGlite02 } = await dynamicImport("pglite-2");
128
+ const pgDump = (await dynamicImport("@electric-sql/pglite-tools/pg_dump")).pgDump;
129
+ logger_1.logger.info("Opening database with Postgres 16...");
130
+ const extensions = await this.getExtensions();
131
+ const dataDir = this.baseDataDirectory;
132
+ const oldDb = new PGlite02(Object.assign(Object.assign({}, pgliteArgs), { dataDir }));
133
+ await oldDb.waitReady;
134
+ const oldVersion = await oldDb.query("SELECT version();");
135
+ logger_1.logger.debug(`Old database version: ${oldVersion.rows[0].version}`);
136
+ if (!oldVersion.rows[0].version.includes("PostgreSQL 16")) {
137
+ await oldDb.close();
138
+ throw new error_1.FirebaseError("Migration started, but DB version is not PostgreSQL 16.");
139
+ }
140
+ logger_1.logger.info("Dumping data from old database...");
141
+ const dumpDir = await oldDb.dumpDataDir("none");
142
+ const tempOldDb = await PGlite02.create({
143
+ loadDataDir: dumpDir,
144
+ extensions,
145
+ });
146
+ const dumpResult = await pgDump({ pg: tempOldDb, args: ["--verbose", "--verbose"] });
147
+ await tempOldDb.close();
148
+ await oldDb.close();
149
+ logger_1.logger.info(`Moving old database directory to ${this.baseDataDirectory}/pg16...`);
150
+ const pg16Dir = this.getVersionedDataDir(16);
151
+ (0, fsutils_1.moveAll)(this.baseDataDirectory, pg16Dir);
152
+ logger_1.logger.info("If you need to use an older version of the Firebase CLI, you can restore from that directory.");
153
+ logger_1.logger.info("Creating new database with Postgres 17...");
154
+ const pg17Dir = this.getVersionedDataDir(17);
155
+ const newDb = new pglite_1.PGlite(Object.assign(Object.assign({}, pgliteArgs), { dataDir: pg17Dir }));
156
+ await newDb.waitReady;
157
+ logger_1.logger.info("Importing data into new database...");
158
+ const dumpText = await dumpResult.text();
159
+ await newDb.exec(dumpText);
160
+ await newDb.exec("SET SEARCH_PATH = public;");
161
+ logger_1.logger.info("Postgres database migration successful.");
162
+ return newDb;
163
+ }
164
+ getVersionedDataDir(version) {
165
+ if (!this.baseDataDirectory) {
166
+ return;
167
+ }
168
+ return path.join(this.baseDataDirectory, `pg${version}`);
169
+ }
170
+ async forceCreateDB() {
171
+ const baseArgs = {
172
+ debug: this.debug,
173
+ extensions: await this.getExtensions(),
174
+ };
175
+ const pg17Dir = this.getVersionedDataDir(17);
176
+ if (pg17Dir && !fs.existsSync(pg17Dir)) {
177
+ fs.mkdirSync(pg17Dir, { recursive: true });
178
+ }
179
+ if (this.importPath) {
180
+ logger_1.logger.debug(`Importing from ${this.importPath}`);
181
+ const rf = fs.readFileSync(this.importPath);
182
+ const file = new File([rf], this.importPath);
183
+ baseArgs.loadDataDir = file;
184
+ }
185
+ if (this.baseDataDirectory && fs.existsSync(this.baseDataDirectory)) {
186
+ const versionFilePath = path.join(this.baseDataDirectory, "PG_VERSION");
187
+ if (fs.existsSync(versionFilePath)) {
188
+ const version = fs.readFileSync(versionFilePath, "utf-8").trim();
189
+ logger_1.logger.debug(`Found Postgres version file with version: ${version}`);
190
+ if (version === "16") {
191
+ logger_1.logger.info("Detected a Postgres 16 data directory from an older version of firebase-tools. Migrating to Postgres 17...");
192
+ return this.migrateDb(baseArgs);
193
+ }
194
+ }
195
+ }
117
196
  try {
118
- const db = await pglite_1.PGlite.create(pgliteArgs);
197
+ const db = new pglite_1.PGlite(Object.assign(Object.assign({}, baseArgs), { dataDir: pg17Dir }));
198
+ await db.waitReady;
119
199
  return db;
120
200
  }
121
201
  catch (err) {
122
- if (pgliteArgs.dataDir && (0, error_1.hasMessage)(err) && /Database already exists/.test(err.message)) {
123
- fs.rmSync(pgliteArgs.dataDir, { force: true, recursive: true });
124
- const db = await pglite_1.PGlite.create(pgliteArgs);
202
+ if (pg17Dir && (0, error_1.hasMessage)(err) && /Database already exists/.test(err.message)) {
203
+ fs.rmSync(pg17Dir, { force: true, recursive: true });
204
+ const db = new pglite_1.PGlite(Object.assign(Object.assign({}, baseArgs), { dataDir: pg17Dir }));
205
+ await db.waitReady;
125
206
  return db;
126
207
  }
127
- logger_1.logger.debug(`Error from pglite: ${err}`);
208
+ logger_1.logger.warn(`Error from pglite: ${err}`);
128
209
  throw new error_1.FirebaseError("Unexpected error starting up Postgres.");
129
210
  }
130
211
  }
@@ -140,71 +221,12 @@ class PostgresServer {
140
221
  constructor(args) {
141
222
  this.db = undefined;
142
223
  this.server = undefined;
143
- this.dataDirectory = args.dataDirectory;
224
+ this.baseDataDirectory = args.dataDirectory;
144
225
  this.importPath = args.importPath;
145
226
  this.debug = args.debug ? 5 : 0;
146
227
  }
147
228
  }
148
229
  exports.PostgresServer = PostgresServer;
149
- class PGliteExtendedQueryPatch {
150
- constructor(connection) {
151
- this.connection = connection;
152
- this.isExtendedQuery = false;
153
- this.eqpErrored = false;
154
- }
155
- filterResponse(message, response) {
156
- return __asyncGenerator(this, arguments, function* filterResponse_1() {
157
- var _a, e_1, _b, _c;
158
- const pipelineStartMessages = [
159
- pg_gateway_1.FrontendMessageCode.Parse,
160
- pg_gateway_1.FrontendMessageCode.Bind,
161
- pg_gateway_1.FrontendMessageCode.Close,
162
- ];
163
- const decoder = new node_string_decoder_1.StringDecoder();
164
- const decoded = decoder.write(message);
165
- logger_1.logger.debug(decoded);
166
- if (pipelineStartMessages.includes(message[0])) {
167
- this.isExtendedQuery = true;
168
- }
169
- if (message[0] === pg_gateway_1.FrontendMessageCode.Sync) {
170
- this.isExtendedQuery = false;
171
- this.eqpErrored = false;
172
- return yield __await(this.connection.createReadyForQuery());
173
- }
174
- try {
175
- for (var _d = true, _e = __asyncValues((0, pg_gateway_1.getMessages)(response)), _f; _f = yield __await(_e.next()), _a = _f.done, !_a;) {
176
- _c = _f.value;
177
- _d = false;
178
- try {
179
- const message = _c;
180
- if (this.eqpErrored) {
181
- continue;
182
- }
183
- if (this.isExtendedQuery && message[0] === pg_gateway_1.BackendMessageCode.ErrorMessage) {
184
- this.eqpErrored = true;
185
- }
186
- if (this.isExtendedQuery && message[0] === pg_gateway_1.BackendMessageCode.ReadyForQuery) {
187
- logger_1.logger.debug("Filtered out a ReadyForQuery.");
188
- continue;
189
- }
190
- yield yield __await(message);
191
- }
192
- finally {
193
- _d = true;
194
- }
195
- }
196
- }
197
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
198
- finally {
199
- try {
200
- if (!_d && !_a && (_b = _e.return)) yield __await(_b.call(_e));
201
- }
202
- finally { if (e_1) throw e_1.error; }
203
- }
204
- });
205
- }
206
- }
207
- exports.PGliteExtendedQueryPatch = PGliteExtendedQueryPatch;
208
230
  async function fromNodeSocket(socket, options) {
209
231
  const rs = node_stream_1.Readable.toWeb(socket);
210
232
  const ws = node_stream_1.Writable.toWeb(socket);
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.6.3",
58
- "expectedSize": 27517696,
59
- "expectedChecksum": "01e0c04374c29e5c5e9d75613362f8e5",
60
- "expectedChecksumSHA256": "54182c7545c99cb524d65959a165f1d727a1407edae91648dffc9c79850fad17",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.6.3",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.3"
57
+ "version": "2.7.1",
58
+ "expectedSize": 27607808,
59
+ "expectedChecksum": "3cf0b4dcb96639f1b7ddf3e2052ea28f",
60
+ "expectedChecksumSHA256": "76b6db1f87e14e3c39035301ae199bec0458a2e7534a1db962276a92fa99c215",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.7.1",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
63
63
  },
64
64
  "win32": {
65
- "version": "2.6.3",
66
- "expectedSize": 27975168,
67
- "expectedChecksum": "d58c93123ac5fdddfdc3e92301ba335b",
68
- "expectedChecksumSHA256": "ab228eb606b522aa8bb5916994282215f74c33eb3db0c59c836b3bf8ccaab289",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.6.3",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.3.exe"
65
+ "version": "2.7.1",
66
+ "expectedSize": 28067328,
67
+ "expectedChecksum": "8dacedaecac242199dfcf3f6c023f89a",
68
+ "expectedChecksumSHA256": "fc098efa9aed9b1ac7d35b90da47608190f3a5562f37444012785a9793d743a0",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.7.1",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.6.3",
74
- "expectedSize": 27431064,
75
- "expectedChecksum": "67a7457a5a77c2f8d4edc9898e88291b",
76
- "expectedChecksumSHA256": "efa12b568cdda0e08f6273c371f3485a5fb512f45c7ba76abbfb55f7a25ccbb6",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.6.3",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.3"
73
+ "version": "2.7.1",
74
+ "expectedSize": 27521176,
75
+ "expectedChecksum": "3c03d85c2d1de80a2f2ba13fe3b5a3ff",
76
+ "expectedChecksumSHA256": "94c35ab64bd8d206e7843513848e7375cd8f9b6a06d013b1ec541ac831cda8a8",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.7.1",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.7.1"
79
79
  }
80
80
  }
81
81
  }
@@ -8,6 +8,7 @@ const apiv2_1 = require("./apiv2");
8
8
  const utils = require("./utils");
9
9
  const error_1 = require("./error");
10
10
  const logger_1 = require("./logger");
11
+ const configstore_1 = require("./configstore");
11
12
  exports.POLL_SETTINGS = {
12
13
  pollInterval: 10000,
13
14
  pollsBeforeRetry: 12,
@@ -18,6 +19,9 @@ const apiClient = new apiv2_1.Client({
18
19
  });
19
20
  async function check(projectId, apiUri, prefix, silent = false) {
20
21
  const apiName = apiUri.startsWith("http") ? new URL(apiUri).hostname : apiUri;
22
+ if (checkAPIEnablementCache(projectId, apiName)) {
23
+ return true;
24
+ }
21
25
  const res = await apiClient.get(`/projects/${projectId}/services/${apiName}`, {
22
26
  headers: { "x-goog-quota-user": `projects/${projectId}` },
23
27
  skipLog: { resBody: true },
@@ -26,6 +30,9 @@ async function check(projectId, apiUri, prefix, silent = false) {
26
30
  if (isEnabled && !silent) {
27
31
  utils.logLabeledSuccess(prefix, `required API ${(0, colorette_1.bold)(apiName)} is enabled`);
28
32
  }
33
+ if (isEnabled) {
34
+ cacheEnabledAPI(projectId, apiName);
35
+ }
29
36
  return isEnabled;
30
37
  }
31
38
  exports.check = check;
@@ -39,6 +46,7 @@ async function enable(projectId, apiName) {
39
46
  headers: { "x-goog-quota-user": `projects/${projectId}` },
40
47
  skipLog: { resBody: true },
41
48
  });
49
+ cacheEnabledAPI(projectId, apiName);
42
50
  }
43
51
  catch (err) {
44
52
  if ((0, error_1.isBillingError)(err)) {
@@ -120,3 +128,17 @@ function enableApiURI(projectId, apiName) {
120
128
  return `https://console.cloud.google.com/apis/library/${apiName}?project=${projectId}`;
121
129
  }
122
130
  exports.enableApiURI = enableApiURI;
131
+ const API_ENABLEMENT_CACHE_KEY = "apiEnablementCache";
132
+ function checkAPIEnablementCache(projectId, apiName) {
133
+ var _a;
134
+ const cache = configstore_1.configstore.get(API_ENABLEMENT_CACHE_KEY);
135
+ return !!((_a = cache === null || cache === void 0 ? void 0 : cache[projectId]) === null || _a === void 0 ? void 0 : _a[apiName]);
136
+ }
137
+ function cacheEnabledAPI(projectId, apiName) {
138
+ const cache = (configstore_1.configstore.get(API_ENABLEMENT_CACHE_KEY) || {});
139
+ if (!cache[projectId]) {
140
+ cache[projectId] = {};
141
+ }
142
+ cache[projectId][apiName] = true;
143
+ configstore_1.configstore.set(API_ENABLEMENT_CACHE_KEY, cache);
144
+ }
package/lib/fsutils.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listFiles = exports.readFile = exports.dirExistsSync = exports.fileExistsSync = void 0;
3
+ exports.moveAll = exports.listFiles = exports.readFile = exports.dirExistsSync = exports.fileExistsSync = void 0;
4
4
  const fs_1 = require("fs");
5
+ const path = require("path");
5
6
  const error_1 = require("./error");
7
+ const fs_extra_1 = require("fs-extra");
6
8
  function fileExistsSync(path) {
7
9
  try {
8
10
  return (0, fs_1.statSync)(path).isFile();
@@ -45,3 +47,16 @@ function listFiles(path) {
45
47
  }
46
48
  }
47
49
  exports.listFiles = listFiles;
50
+ function moveAll(srcDir, destDir) {
51
+ if (!(0, fs_1.existsSync)(destDir)) {
52
+ (0, fs_1.mkdirSync)(destDir, { recursive: true });
53
+ }
54
+ const files = listFiles(srcDir);
55
+ for (const f of files) {
56
+ const srcPath = path.join(srcDir, f);
57
+ if (srcPath === destDir)
58
+ continue;
59
+ (0, fs_extra_1.moveSync)(srcPath, path.join(destDir, f));
60
+ }
61
+ }
62
+ exports.moveAll = moveAll;
@@ -20,7 +20,7 @@ const serviceUsagePollerOptions = {
20
20
  async function generateServiceIdentity(projectNumber, service, prefix) {
21
21
  utils.logLabeledBullet(prefix, `generating the service identity for ${(0, colorette_1.bold)(service)}...`);
22
22
  try {
23
- const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`);
23
+ const res = await exports.apiClient.post(`projects/${projectNumber}/services/${service}:generateServiceIdentity`, {}, { headers: { "x-goog-quota-user": `projects/${projectNumber}` } });
24
24
  return res.body;
25
25
  }
26
26
  catch (err) {
@@ -35,6 +35,6 @@ async function generateServiceIdentityAndPoll(projectNumber, service, prefix) {
35
35
  if (op.done) {
36
36
  return;
37
37
  }
38
- await poller.pollOperation(Object.assign(Object.assign({}, serviceUsagePollerOptions), { operationResourceName: op.name }));
38
+ await poller.pollOperation(Object.assign(Object.assign({}, serviceUsagePollerOptions), { operationResourceName: op.name, headers: { "x-goog-quota-user": `projects/${projectNumber}` } }));
39
39
  }
40
40
  exports.generateServiceIdentityAndPoll = generateServiceIdentityAndPoll;
@@ -143,15 +143,19 @@ async function getServiceAccount(projectId) {
143
143
  }
144
144
  }
145
145
  exports.getServiceAccount = getServiceAccount;
146
- async function getDownloadUrl(bucketName, objectPath) {
146
+ async function getDownloadUrl(bucketName, objectPath, emulatorUrl) {
147
147
  try {
148
- const localAPIClient = new apiv2_1.Client({ urlPrefix: (0, api_1.firebaseStorageOrigin)() });
148
+ const origin = emulatorUrl || (0, api_1.firebaseStorageOrigin)();
149
+ const localAPIClient = new apiv2_1.Client({ urlPrefix: origin });
149
150
  const response = await localAPIClient.get(`/v0/b/${bucketName}/o/${encodeURIComponent(objectPath)}`);
151
+ if (emulatorUrl) {
152
+ return `${origin}/v0/b/${bucketName}/o/${encodeURIComponent(objectPath)}?alt=media`;
153
+ }
150
154
  if (!response.body.downloadTokens) {
151
- throw new Error("no download tokens exist for ${objectPath}, please visit the Firebase console to make one");
155
+ throw new Error(`no download tokens exist for ${objectPath}, please visit the Firebase console to make one`);
152
156
  }
153
157
  const [token] = response.body.downloadTokens.split(",");
154
- return `${(0, api_1.firebaseStorageOrigin)()}/v0/b/${bucketName}/o/${encodeURIComponent(objectPath)}?alt=media&token=${token}`;
158
+ return `${origin}/v0/b/${bucketName}/o/${encodeURIComponent(objectPath)}?alt=media&token=${token}`;
155
159
  }
156
160
  catch (err) {
157
161
  logger_1.logger.error(err);
package/lib/mcp/index.js CHANGED
@@ -102,6 +102,19 @@ class FirebaseMcpServer {
102
102
  this.emulatorHubClient = new hubClient_js_1.EmulatorHubClient(projectId);
103
103
  return this.emulatorHubClient;
104
104
  }
105
+ async getEmulatorUrl(emulatorType) {
106
+ const hubClient = await this.getEmulatorHubClient();
107
+ if (!hubClient) {
108
+ throw Error("Emulator Hub not found or is not running. You can start the emulator by running `firebase emulators:start` in your firebase project directory.");
109
+ }
110
+ const emulators = await hubClient.getEmulators();
111
+ const emulatorInfo = emulators[emulatorType];
112
+ if (!emulatorInfo) {
113
+ throw Error("No Firestore Emulator found running. Make sure your project firebase.json file includes firestore and then rerun emulator using `firebase emulators:start` from your project directory.");
114
+ }
115
+ const host = emulatorInfo.host.includes(":") ? `[${emulatorInfo.host}]` : emulatorInfo.host;
116
+ return `http://${host}:${emulatorInfo.port}`;
117
+ }
105
118
  get availableTools() {
106
119
  var _a;
107
120
  return (0, index_js_2.availableTools)(((_a = this.activeFeatures) === null || _a === void 0 ? void 0 : _a.length) ? this.activeFeatures : this.detectedFeatures);
@@ -125,7 +138,8 @@ class FirebaseMcpServer {
125
138
  }
126
139
  async getAuthenticatedUser() {
127
140
  try {
128
- return await (0, requireAuth_js_1.requireAuth)(await this.resolveOptions());
141
+ const email = await (0, requireAuth_js_1.requireAuth)(await this.resolveOptions());
142
+ return email !== null && email !== void 0 ? email : "Application Default Credentials";
129
143
  }
130
144
  catch (e) {
131
145
  return null;
@@ -17,6 +17,10 @@ exports.list_top_issues = (0, tool_js_1.tool)({
17
17
  .number()
18
18
  .optional()
19
19
  .describe("Number of issues that needs to be fetched. Defaults to 10 if unspecified."),
20
+ issue_type: zod_1.z
21
+ .enum(["FATAL", "NON-FATAL", "ANR"])
22
+ .optional()
23
+ .describe("Types of issues that can be fetched comma-separated. Defaults to `FATAL` (Crashes). Other values include NON-FATAL (Non-fatal issues), ANR (Application not responding)."),
20
24
  }),
21
25
  annotations: {
22
26
  title: "List Top Crashlytics Issues.",
@@ -26,9 +30,10 @@ exports.list_top_issues = (0, tool_js_1.tool)({
26
30
  requiresAuth: true,
27
31
  requiresProject: true,
28
32
  },
29
- }, async ({ app_id, issue_count }, { projectId }) => {
33
+ }, async ({ app_id, issue_type, issue_count }, { projectId }) => {
30
34
  if (!app_id)
31
35
  return (0, util_js_1.mcpError)(`Must specify 'app_id' parameter.`);
36
+ issue_type !== null && issue_type !== void 0 ? issue_type : (issue_type = "FATAL");
32
37
  issue_count !== null && issue_count !== void 0 ? issue_count : (issue_count = 10);
33
- return (0, util_js_1.toContent)(await (0, listTopIssues_js_1.listTopIssues)(projectId, app_id, issue_count));
38
+ return (0, util_js_1.toContent)(await (0, listTopIssues_js_1.listTopIssues)(projectId, app_id, issue_type, issue_count));
34
39
  });
@@ -4,26 +4,10 @@ exports.getDataConnectEmulatorClient = void 0;
4
4
  const types_js_1 = require("../../../emulator/types.js");
5
5
  const apiv2_js_1 = require("../../../apiv2.js");
6
6
  const dataplaneClient_js_1 = require("../../../dataconnect/dataplaneClient.js");
7
- function formatEndpoint(emulatorInfo) {
8
- const host = emulatorInfo.host.includes(":") ? `[${emulatorInfo.host}]` : emulatorInfo.host;
9
- return {
10
- host: emulatorInfo.host,
11
- port: emulatorInfo.port,
12
- url: `http://${host}:${emulatorInfo.port}`,
13
- };
14
- }
15
- async function getDataConnectEmulatorClient(hubClient) {
16
- if (!hubClient) {
17
- throw Error("Emulator Hub not found or is not running. Please ensure the emulator is started, you can start the Data Connect emualtor by running `firebase emulators:start --only dataconnect`.");
18
- }
19
- const emulators = await hubClient.getEmulators();
20
- const dcEmulatorInfo = emulators[types_js_1.Emulators.DATACONNECT];
21
- if (!dcEmulatorInfo) {
22
- throw Error("No Data Connect Emulator found running, you can start the emualtor by running `firebase emulators:start --only dataconnect`.");
23
- }
24
- const emulatorDetails = formatEndpoint(dcEmulatorInfo);
7
+ async function getDataConnectEmulatorClient(host) {
8
+ const emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.DATACONNECT);
25
9
  const apiClient = new apiv2_js_1.Client({
26
- urlPrefix: emulatorDetails.url,
10
+ urlPrefix: emulatorUrl,
27
11
  apiVersion: dataplaneClient_js_1.DATACONNECT_API_VERSION,
28
12
  auth: false,
29
13
  });
@@ -34,7 +34,7 @@ exports.execute_graphql = (0, tool_js_1.tool)({
34
34
  const serviceInfo = await (0, fileUtils_js_1.pickService)(projectId, config, service_id || undefined);
35
35
  let apiClient;
36
36
  if (use_emulator) {
37
- apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(await host.getEmulatorHubClient());
37
+ apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(host);
38
38
  }
39
39
  else {
40
40
  apiClient = dataplane.dataconnectDataplaneClient();
@@ -34,7 +34,7 @@ exports.execute_graphql_read = (0, tool_js_1.tool)({
34
34
  const serviceInfo = await (0, fileUtils_js_1.pickService)(projectId, config, service_id || undefined);
35
35
  let apiClient;
36
36
  if (use_emulator) {
37
- apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(await host.getEmulatorHubClient());
37
+ apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(host);
38
38
  }
39
39
  else {
40
40
  apiClient = dataplane.dataconnectDataplaneClient();
@@ -49,7 +49,7 @@ exports.execute_mutation = (0, tool_js_1.tool)({
49
49
  }
50
50
  const connectorPath = `${serviceInfo.serviceName}/connectors/${connector_id}`;
51
51
  if (use_emulator) {
52
- apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(await host.getEmulatorHubClient());
52
+ apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(host);
53
53
  }
54
54
  else {
55
55
  apiClient = dataplane.dataconnectDataplaneClient();
@@ -49,7 +49,7 @@ exports.execute_query = (0, tool_js_1.tool)({
49
49
  }
50
50
  const connectorPath = `${serviceInfo.serviceName}/connectors/${connector_id}`;
51
51
  if (use_emulator) {
52
- apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(await host.getEmulatorHubClient());
52
+ apiClient = await (0, emulator_js_1.getDataConnectEmulatorClient)(host);
53
53
  }
54
54
  else {
55
55
  apiClient = dataplane.dataconnectDataplaneClient();
@@ -6,7 +6,7 @@ const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const firestore_js_1 = require("../../../gcp/firestore.js");
8
8
  const delete_js_1 = require("../../../firestore/delete.js");
9
- const emulator_js_1 = require("./emulator.js");
9
+ const types_js_1 = require("../../../emulator/types.js");
10
10
  exports.delete_document = (0, tool_js_1.tool)({
11
11
  name: "delete_document",
12
12
  description: "Deletes a Firestore documents from a database in the current project by full document paths. Use this if you know the exact path of a document.",
@@ -31,7 +31,7 @@ exports.delete_document = (0, tool_js_1.tool)({
31
31
  }, async ({ path, database, use_emulator }, { projectId, host }) => {
32
32
  let emulatorUrl;
33
33
  if (use_emulator) {
34
- emulatorUrl = await (0, emulator_js_1.getFirestoreEmulatorUrl)(await host.getEmulatorHubClient());
34
+ emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.FIRESTORE);
35
35
  }
36
36
  const { documents, missing } = await (0, firestore_js_1.getDocuments)(projectId, [path], database, emulatorUrl);
37
37
  if (missing.length > 0 && documents && documents.length === 0) {
@@ -6,7 +6,7 @@ const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const firestore_js_1 = require("../../../gcp/firestore.js");
8
8
  const converter_js_1 = require("./converter.js");
9
- const emulator_js_1 = require("./emulator.js");
9
+ const types_js_1 = require("../../../emulator/types.js");
10
10
  exports.get_documents = (0, tool_js_1.tool)({
11
11
  name: "get_documents",
12
12
  description: "Retrieves one or more Firestore documents from a database in the current project by full document paths. Use this if you know the exact path of a document.",
@@ -33,7 +33,7 @@ exports.get_documents = (0, tool_js_1.tool)({
33
33
  return (0, util_js_1.mcpError)("Must supply at least one document path.");
34
34
  let emulatorUrl;
35
35
  if (use_emulator) {
36
- emulatorUrl = await (0, emulator_js_1.getFirestoreEmulatorUrl)(await host.getEmulatorHubClient());
36
+ emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.FIRESTORE);
37
37
  }
38
38
  const { documents, missing } = await (0, firestore_js_1.getDocuments)(projectId, paths, database, emulatorUrl);
39
39
  if (missing.length > 0 && documents && documents.length === 0) {
@@ -6,7 +6,7 @@ const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const firestore_js_1 = require("../../../gcp/firestore.js");
8
8
  const errors_js_1 = require("../../errors.js");
9
- const emulator_js_1 = require("./emulator.js");
9
+ const types_js_1 = require("../../../emulator/types.js");
10
10
  exports.list_collections = (0, tool_js_1.tool)({
11
11
  name: "list_collections",
12
12
  description: "Retrieves a list of collections from a Firestore database in the current project.",
@@ -28,7 +28,7 @@ exports.list_collections = (0, tool_js_1.tool)({
28
28
  }, async ({ database, use_emulator }, { projectId, host }) => {
29
29
  let emulatorUrl;
30
30
  if (use_emulator) {
31
- emulatorUrl = await (0, emulator_js_1.getFirestoreEmulatorUrl)(await host.getEmulatorHubClient());
31
+ emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.FIRESTORE);
32
32
  }
33
33
  if (!projectId)
34
34
  return errors_js_1.NO_PROJECT_ERROR;
@@ -6,7 +6,7 @@ const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const firestore_js_1 = require("../../../gcp/firestore.js");
8
8
  const converter_js_1 = require("./converter.js");
9
- const emulator_js_1 = require("./emulator.js");
9
+ const types_js_1 = require("../../../emulator/types.js");
10
10
  exports.query_collection = (0, tool_js_1.tool)({
11
11
  name: "query_collection",
12
12
  description: "Retrieves one or more Firestore documents from a collection is a database in the current project by a collection with a full document path. Use this if you know the exact path of a collection and the filtering clause you would like for the document.",
@@ -123,7 +123,7 @@ exports.query_collection = (0, tool_js_1.tool)({
123
123
  structuredQuery.limit = limit ? limit : 10;
124
124
  let emulatorUrl;
125
125
  if (use_emulator) {
126
- emulatorUrl = await (0, emulator_js_1.getFirestoreEmulatorUrl)(await host.getEmulatorHubClient());
126
+ emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.FIRESTORE);
127
127
  }
128
128
  const { documents } = await (0, firestore_js_1.queryCollection)(projectId, structuredQuery, database, emulatorUrl);
129
129
  const docs = documents.map(converter_js_1.firestoreDocumentToJson);
@@ -5,6 +5,7 @@ const zod_1 = require("zod");
5
5
  const tool_js_1 = require("../../tool.js");
6
6
  const util_js_1 = require("../../util.js");
7
7
  const storage_js_1 = require("../../../gcp/storage.js");
8
+ const types_js_1 = require("../../../emulator/types.js");
8
9
  exports.get_object_download_url = (0, tool_js_1.tool)({
9
10
  name: "get_object_download_url",
10
11
  description: "Retrieves the download URL for an object in Firebase Storage.",
@@ -16,6 +17,7 @@ exports.get_object_download_url = (0, tool_js_1.tool)({
16
17
  object_path: zod_1.z
17
18
  .string()
18
19
  .describe("The path to the object in Firebase storage without the bucket name attached"),
20
+ use_emulator: zod_1.z.boolean().default(false).describe("Target the Storage emulator if true."),
19
21
  }),
20
22
  annotations: {
21
23
  title: "Get Storage Object Download URL",
@@ -25,10 +27,14 @@ exports.get_object_download_url = (0, tool_js_1.tool)({
25
27
  requiresProject: true,
26
28
  requiresAuth: true,
27
29
  },
28
- }, async ({ bucket, object_path }, { projectId }) => {
30
+ }, async ({ bucket, object_path, use_emulator }, { projectId, host }) => {
29
31
  if (!bucket) {
30
32
  bucket = `${projectId}.firebasestorage.app`;
31
33
  }
32
- const downloadUrl = await (0, storage_js_1.getDownloadUrl)(bucket, object_path);
34
+ let emulatorUrl;
35
+ if (use_emulator) {
36
+ emulatorUrl = await host.getEmulatorUrl(types_js_1.Emulators.STORAGE);
37
+ }
38
+ const downloadUrl = await (0, storage_js_1.getDownloadUrl)(bucket, object_path, emulatorUrl);
33
39
  return (0, util_js_1.toContent)(downloadUrl);
34
40
  });
@@ -34,7 +34,9 @@ class OperationPoller {
34
34
  return async () => {
35
35
  let res;
36
36
  try {
37
- res = await apiClient.get(options.operationResourceName);
37
+ res = await apiClient.get(options.operationResourceName, {
38
+ headers: options.headers,
39
+ });
38
40
  }
39
41
  catch (err) {
40
42
  if (err.status === 500 || err.status === 503) {
package/lib/track.js CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ var _a;
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  exports.cliSession = exports.vscodeSession = exports.emulatorSession = exports.trackVSCode = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
4
5
  const node_fetch_1 = require("node-fetch");
@@ -41,6 +42,9 @@ const GA4_USER_PROPS = {
41
42
  firepit_version: {
42
43
  value: process.env.FIREPIT_VERSION || "none",
43
44
  },
45
+ is_firebase_studio: {
46
+ value: (_a = process.env.MONOSPACE_ENV) !== null && _a !== void 0 ? _a : "false",
47
+ },
44
48
  };
45
49
  async function trackGA4(eventName, params, duration = 1) {
46
50
  const session = cliSession();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.6.0",
3
+ "version": "14.8.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -60,7 +60,8 @@
60
60
  ]
61
61
  },
62
62
  "dependencies": {
63
- "@electric-sql/pglite": "^0.2.17",
63
+ "@electric-sql/pglite": "^0.3.3",
64
+ "@electric-sql/pglite-tools": "^0.2.8",
64
65
  "@google-cloud/cloud-sql-connector": "^1.3.3",
65
66
  "@google-cloud/pubsub": "^4.5.0",
66
67
  "@inquirer/prompts": "^7.4.0",
@@ -110,6 +111,7 @@
110
111
  "p-limit": "^3.0.1",
111
112
  "pg": "^8.11.3",
112
113
  "pg-gateway": "^0.3.0-beta.4",
114
+ "pglite-2": "npm:@electric-sql/pglite@0.2.17",
113
115
  "portfinder": "^1.0.32",
114
116
  "progress": "^2.0.3",
115
117
  "proxy-agent": "^6.3.0",
@@ -7,9 +7,22 @@
7
7
  * See a full list of supported triggers at https://firebase.google.com/docs/functions
8
8
  */
9
9
 
10
- const {onRequest} = require("firebase-functions/v2/https");
10
+ const {setGlobalOptions} = require("firebase-functions");
11
+ const {onRequest} = require("firebase-functions/https");
11
12
  const logger = require("firebase-functions/logger");
12
13
 
14
+ // For cost control, you can set the maximum number of containers that can be
15
+ // running at the same time. This helps mitigate the impact of unexpected
16
+ // traffic spikes by instead downgrading performance. This limit is a
17
+ // per-function limit. You can override the limit for each function using the
18
+ // `maxInstances` option in the function's options, e.g.
19
+ // `onRequest({ maxInstances: 5 }, (req, res) => { ... })`.
20
+ // NOTE: setGlobalOptions does not apply to functions using the v1 API. V1
21
+ // functions should each use functions.runWith({ maxInstances: 10 }) instead.
22
+ // In the v1 API, each function can only serve one request per container, so
23
+ // this will be the maximum concurrent request count.
24
+ setGlobalOptions({ maxInstances: 10 });
25
+
13
26
  // Create and deploy your first functions
14
27
  // https://firebase.google.com/docs/functions/get-started
15
28
 
@@ -3,8 +3,16 @@
3
3
  # Deploy with `firebase deploy`
4
4
 
5
5
  from firebase_functions import https_fn
6
+ from firebase_functions.options import set_global_options
6
7
  from firebase_admin import initialize_app
7
8
 
9
+ # For cost control, you can set the maximum number of containers that can be
10
+ # running at the same time. This helps mitigate the impact of unexpected
11
+ # traffic spikes by instead downgrading performance. This limit is a per-function
12
+ # limit. You can override the limit for each function using the max_instances
13
+ # parameter in the decorator, e.g. @https_fn.on_request(max_instances=5).
14
+ set_global_options(max_instances=10)
15
+
8
16
  # initialize_app()
9
17
  #
10
18
  #
@@ -7,12 +7,25 @@
7
7
  * See a full list of supported triggers at https://firebase.google.com/docs/functions
8
8
  */
9
9
 
10
- import {onRequest} from "firebase-functions/v2/https";
10
+ import {setGlobalOptions} from "firebase-functions";
11
+ import {onRequest} from "firebase-functions/https";
11
12
  import * as logger from "firebase-functions/logger";
12
13
 
13
14
  // Start writing functions
14
15
  // https://firebase.google.com/docs/functions/typescript
15
16
 
17
+ // For cost control, you can set the maximum number of containers that can be
18
+ // running at the same time. This helps mitigate the impact of unexpected
19
+ // traffic spikes by instead downgrading performance. This limit is a
20
+ // per-function limit. You can override the limit for each function using the
21
+ // `maxInstances` option in the function's options, e.g.
22
+ // `onRequest({ maxInstances: 5 }, (req, res) => { ... })`.
23
+ // NOTE: setGlobalOptions does not apply to functions using the v1 API. V1
24
+ // functions should each use functions.runWith({ maxInstances: 10 }) instead.
25
+ // In the v1 API, each function can only serve one request per container, so
26
+ // this will be the maximum concurrent request count.
27
+ setGlobalOptions({ maxInstances: 10 });
28
+
16
29
  // export const helloWorld = onRequest((request, response) => {
17
30
  // logger.info("Hello logs!", {structuredData: true});
18
31
  // response.send("Hello from Firebase!");
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFirestoreEmulatorUrl = void 0;
4
- const types_js_1 = require("../../../emulator/types.js");
5
- async function getFirestoreEmulatorUrl(hubClient) {
6
- if (!hubClient) {
7
- throw Error("Emulator Hub not found or is not running. You can start the emulator by running `firebase emulators:start` in your firebase project directory.");
8
- }
9
- const emulators = await hubClient.getEmulators();
10
- const firestoreEmulatorInfo = emulators[types_js_1.Emulators.FIRESTORE];
11
- if (!firestoreEmulatorInfo) {
12
- throw Error("No Firestore Emulator found running. Make sure your project firebase.json file includes firestore and then rerun emulator using `firebase emulators:start` from your project directory.");
13
- }
14
- return `http://${firestoreEmulatorInfo.host}:${firestoreEmulatorInfo.port}`;
15
- }
16
- exports.getFirestoreEmulatorUrl = getFirestoreEmulatorUrl;