firebase-tools 15.22.1 → 15.22.3

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.
package/lib/apiv2.js CHANGED
@@ -4,12 +4,15 @@ exports.Client = exports.CLI_OAUTH_PROJECT_NUMBER = exports.GOOG_USER_PROJECT_HE
4
4
  exports.setRefreshToken = setRefreshToken;
5
5
  exports.setAccessToken = setAccessToken;
6
6
  exports.getAccessToken = getAccessToken;
7
+ exports.noKeepAliveAgent = noKeepAliveAgent;
7
8
  const url_1 = require("url");
8
9
  const stream_1 = require("stream");
9
10
  const proxy_agent_1 = require("proxy-agent");
10
11
  const retry = require("retry");
11
12
  const abort_controller_1 = require("abort-controller");
12
13
  const node_fetch_1 = require("node-fetch");
14
+ const http = require("http");
15
+ const https = require("https");
13
16
  const util_1 = require("util");
14
17
  const auth = require("./auth");
15
18
  const error_1 = require("./error");
@@ -59,6 +62,28 @@ function proxyURIFromEnv() {
59
62
  process.env.http_proxy ||
60
63
  undefined);
61
64
  }
65
+ const httpAgentNoKeepAlive = new http.Agent({ keepAlive: false });
66
+ const httpsAgentNoKeepAlive = new https.Agent({ keepAlive: false });
67
+ function noKeepAliveAgent(parsedURL) {
68
+ return parsedURL.protocol === "https:" ? httpsAgentNoKeepAlive : httpAgentNoKeepAlive;
69
+ }
70
+ function isPrematureCloseError(err) {
71
+ for (const candidate of [err, err?.original]) {
72
+ if (!candidate) {
73
+ continue;
74
+ }
75
+ const e = candidate;
76
+ const code = e.code || e.errno;
77
+ const message = typeof e.message === "string" ? e.message : "";
78
+ if (code === "ERR_STREAM_PREMATURE_CLOSE" ||
79
+ code === "ECONNRESET" ||
80
+ /premature close/i.test(message) ||
81
+ /socket hang up/i.test(message)) {
82
+ return true;
83
+ }
84
+ }
85
+ return false;
86
+ }
62
87
  class Client {
63
88
  constructor(opts) {
64
89
  this.opts = opts;
@@ -237,9 +262,17 @@ class Client {
237
262
  };
238
263
  fetchOptions.signal = signal;
239
264
  }
240
- if (typeof options.body === "string" || isStream(options.body)) {
265
+ let bodyReplayable = true;
266
+ if (typeof options.body === "string" || Buffer.isBuffer(options.body)) {
241
267
  fetchOptions.body = options.body;
242
268
  }
269
+ else if (options.body instanceof FormData) {
270
+ fetchOptions.body = options.body.getBuffer();
271
+ }
272
+ else if (isStream(options.body)) {
273
+ fetchOptions.body = options.body;
274
+ bodyReplayable = false;
275
+ }
243
276
  else if (options.body !== undefined) {
244
277
  fetchOptions.body = JSON.stringify(options.body);
245
278
  }
@@ -258,6 +291,7 @@ class Client {
258
291
  operationOptions.maxTimeout = options.retryMaxTimeout;
259
292
  }
260
293
  const operation = retry.operation(operationOptions);
294
+ let disabledKeepAlive = false;
261
295
  return await new Promise((resolve, reject) => {
262
296
  operation.attempt(async (currentAttempt) => {
263
297
  let res;
@@ -314,6 +348,20 @@ class Client {
314
348
  }
315
349
  }
316
350
  catch (err) {
351
+ if (!disabledKeepAlive &&
352
+ bodyReplayable &&
353
+ !proxyURIFromEnv() &&
354
+ isPrematureCloseError(err)) {
355
+ disabledKeepAlive = true;
356
+ fetchOptions.agent = noKeepAliveAgent;
357
+ const closeHeaders = new node_fetch_1.Headers(fetchOptions.headers);
358
+ closeHeaders.set("Connection", "close");
359
+ fetchOptions.headers = closeHeaders;
360
+ logger_1.logger.debug(`*** [apiv2] retrying ${fetchURL} without keep-alive after a premature close error`);
361
+ if (operation.retry(err instanceof Error ? err : new Error(`${err}`))) {
362
+ return;
363
+ }
364
+ }
317
365
  return err instanceof error_1.FirebaseError ? reject(err) : reject(new error_1.FirebaseError(`${err}`));
318
366
  }
319
367
  this.logResponse(res, body, options);
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureAuthEventarcTriggerRegion = ensureAuthEventarcTriggerRegion;
4
+ const error_1 = require("../../../error");
5
+ function ensureAuthEventarcTriggerRegion(endpoint) {
6
+ if (!endpoint.eventTrigger.region) {
7
+ endpoint.eventTrigger.region = "global";
8
+ }
9
+ if (endpoint.eventTrigger.region !== "global") {
10
+ throw new error_1.FirebaseError("A Firebase Auth Eventarc trigger must specify 'global' trigger location");
11
+ }
12
+ return Promise.resolve();
13
+ }
@@ -11,6 +11,7 @@ const testLab_1 = require("./testLab");
11
11
  const firestore_1 = require("./firestore");
12
12
  const dataconnect_1 = require("./dataconnect");
13
13
  const ailogic_1 = require("./ailogic");
14
+ const authEventarc_1 = require("./authEventarc");
14
15
  const noop = () => Promise.resolve();
15
16
  exports.noop = noop;
16
17
  const noopProjectBindings = () => Promise.resolve([]);
@@ -108,6 +109,16 @@ const dataconnectService = {
108
109
  unregisterTrigger: exports.noop,
109
110
  getDefaultRegion: dataconnect_1.getDefaultRegion,
110
111
  };
112
+ const authEventarcService = {
113
+ name: "autheventarc",
114
+ api: "eventarc.googleapis.com",
115
+ requiredProjectBindings: exports.noopProjectBindings,
116
+ ensureTriggerRegion: authEventarc_1.ensureAuthEventarcTriggerRegion,
117
+ validateTrigger: exports.noop,
118
+ registerTrigger: exports.noop,
119
+ unregisterTrigger: exports.noop,
120
+ getDefaultRegion: () => Promise.resolve(exports.DEFAULT_GLOBAL_TRIGGER_REGION),
121
+ };
111
122
  const EVENT_SERVICE_MAPPING = {
112
123
  "google.cloud.pubsub.topic.v1.messagePublished": pubSubService,
113
124
  "google.cloud.storage.object.v1.finalized": storageService,
@@ -136,6 +147,8 @@ const EVENT_SERVICE_MAPPING = {
136
147
  "google.firebase.dataconnect.connector.v1.mutationExecuted": dataconnectService,
137
148
  "google.firebase.ailogic.v1.beforeGenerate": aiLogicService,
138
149
  "google.firebase.ailogic.v1.afterGenerate": aiLogicService,
150
+ "google.firebase.auth.user.v2.created": authEventarcService,
151
+ "google.firebase.auth.user.v2.deleted": authEventarcService,
139
152
  };
140
153
  function serviceForEndpoint(endpoint) {
141
154
  let eventType;
@@ -54,36 +54,36 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.4.13",
58
- "expectedSize": 32456560,
59
- "expectedChecksum": "92a91871a0228b5362a8f33b6c9d3cbf",
60
- "expectedChecksumSHA256": "b872c6dff20ca6303b0c4f438907ff5a640062c565878b21a3bf19bbd102af03",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.13",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.13"
57
+ "version": "3.4.14",
58
+ "expectedSize": 32468976,
59
+ "expectedChecksum": "d62fc079187a1adc2ceef7dc0e1a1e44",
60
+ "expectedChecksumSHA256": "4bbf0ae04ca83dcc765804549d30f94d02c7a8b6baeef5f3c52104e771627e43",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.14",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.14"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.4.13",
66
- "expectedSize": 30588130,
67
- "expectedChecksum": "f5aa657a9577ed9e10a8c3f2dde81f3f",
68
- "expectedChecksumSHA256": "5be04cbb6537afe7be953ae2d2cf7bad359bd2f9e181ebbd6dab5195493d7e78",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.13",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.13"
65
+ "version": "3.4.14",
66
+ "expectedSize": 30604754,
67
+ "expectedChecksum": "306e88ebe314eec82ae44842aa87d20e",
68
+ "expectedChecksumSHA256": "3eeaf0dbfd48d3d7cf761e256e8850bc6159dccc128dd59f87bbeeaaaa6a62c6",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.14",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.14"
71
71
  },
72
72
  "win32": {
73
- "version": "3.4.13",
74
- "expectedSize": 32494592,
75
- "expectedChecksum": "6ee9ba34ee39693847d903f9dadb7f3e",
76
- "expectedChecksumSHA256": "d76fb70953d2df92d414901380f19863da14443c05da93d9888c2469feba613a",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.13",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.13.exe"
73
+ "version": "3.4.14",
74
+ "expectedSize": 32509952,
75
+ "expectedChecksum": "c5cbd9482be394bf466d4328b975d88e",
76
+ "expectedChecksumSHA256": "b9f14587c5d5f87bccd093e1dddc9f5bb2a77be007201028d9ee8339e79219f0",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.14",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.14.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.4.13",
82
- "expectedSize": 31613112,
83
- "expectedChecksum": "15e94d04f887b73e932a65a27e4ff080",
84
- "expectedChecksumSHA256": "3eb5acc989dea31fecc0b8a9eab6db3bc23f6f49f600012cffef837561189728",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.13",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.13"
81
+ "version": "3.4.14",
82
+ "expectedSize": 31625400,
83
+ "expectedChecksum": "506a09ca12b61df98f705728047219dd",
84
+ "expectedChecksumSHA256": "257d7aa4f4dd1e4de9c9d3db9f7b7834323f0d53e658e6814a933b1ec73ca27d",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.14",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.14"
87
87
  }
88
88
  }
89
89
  }
@@ -29,7 +29,7 @@ async function openBillingAccount(projectId, url, open) {
29
29
  message: "Press enter when finished upgrading your project to continue setting up your extension.",
30
30
  default: true,
31
31
  });
32
- return cloudbilling.checkBillingEnabled(projectId);
32
+ return cloudbilling.checkBillingEnabled(projectId, true);
33
33
  }
34
34
  async function chooseBillingAccount(projectId, accounts) {
35
35
  const choices = accounts.map((m) => m.displayName);
@@ -65,7 +65,7 @@ async function setUpBillingAccount(projectId) {
65
65
  return logBillingStatus(billingEnabled, projectId);
66
66
  }
67
67
  async function enableBilling(projectId) {
68
- const billingAccounts = await cloudbilling.listBillingAccounts();
68
+ const billingAccounts = await cloudbilling.listBillingAccounts(projectId);
69
69
  if (billingAccounts) {
70
70
  const accounts = billingAccounts.filter((account) => account.open);
71
71
  return accounts.length > 0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AI_LOGIC_TRIGGERS_TO_EVENTS = exports.AI_LOGIC_EVENTS_TO_TRIGGER = exports.CONVERTABLE_EVENTS = exports.DATACONNECT_EVENT = 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;
3
+ exports.AI_LOGIC_TRIGGERS_TO_EVENTS = exports.AI_LOGIC_EVENTS_TO_TRIGGER = exports.CONVERTABLE_EVENTS = exports.AUTH_EVENTS = exports.DATACONNECT_EVENT = 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
  const ailogic_1 = require("../../deploy/functions/services/ailogic");
5
5
  exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
6
6
  exports.STORAGE_EVENTS = [
@@ -30,6 +30,10 @@ exports.FIRESTORE_EVENTS = [
30
30
  ];
31
31
  exports.FIREALERTS_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
32
32
  exports.DATACONNECT_EVENT = "google.firebase.dataconnect.connector.v1.mutationExecuted";
33
+ exports.AUTH_EVENTS = [
34
+ "google.firebase.auth.user.v2.created",
35
+ "google.firebase.auth.user.v2.deleted",
36
+ ];
33
37
  exports.CONVERTABLE_EVENTS = {
34
38
  "google.cloud.firestore.document.v1.created": "google.cloud.firestore.document.v1.created.withAuthContext",
35
39
  "google.cloud.firestore.document.v1.updated": "google.cloud.firestore.document.v1.updated.withAuthContext",
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isBillingEnabled = isBillingEnabled;
4
+ exports.clearCache = clearCache;
4
5
  exports.checkBillingEnabled = checkBillingEnabled;
5
6
  exports.setBillingAccount = setBillingAccount;
6
7
  exports.listBillingAccounts = listBillingAccounts;
7
8
  const api_1 = require("../api");
8
9
  const apiv2_1 = require("../apiv2");
9
10
  const utils = require("../utils");
11
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
10
12
  const API_VERSION = "v1";
11
13
  const client = new apiv2_1.Client({ urlPrefix: (0, api_1.cloudbillingOrigin)(), apiVersion: API_VERSION });
12
14
  async function isBillingEnabled(setup) {
@@ -19,20 +21,51 @@ async function isBillingEnabled(setup) {
19
21
  setup.isBillingEnabled = await checkBillingEnabled(setup.projectId);
20
22
  return setup.isBillingEnabled;
21
23
  }
22
- async function checkBillingEnabled(projectId) {
23
- const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), {
24
- retries: 3,
25
- retryCodes: [429, 500, 503],
24
+ const billingEnabledCache = new Map();
25
+ function clearCache() {
26
+ billingEnabledCache.clear();
27
+ }
28
+ function checkBillingEnabled(projectId, forceRefresh = false) {
29
+ if (!forceRefresh) {
30
+ const cached = billingEnabledCache.get(projectId);
31
+ if (cached !== undefined) {
32
+ return cached;
33
+ }
34
+ }
35
+ const promise = (async () => {
36
+ await (0, ensureApiEnabled_1.ensure)(projectId, "cloudbilling.googleapis.com", "billing", true);
37
+ const res = await client.get(utils.endpoint(["projects", projectId, "billingInfo"]), {
38
+ retries: 3,
39
+ retryCodes: [429, 500, 503],
40
+ headers: { "x-goog-user-project": projectId },
41
+ });
42
+ return res.body.billingEnabled;
43
+ })().catch((err) => {
44
+ billingEnabledCache.delete(projectId);
45
+ throw err;
26
46
  });
27
- return res.body.billingEnabled;
47
+ billingEnabledCache.set(projectId, promise);
48
+ return promise;
28
49
  }
29
50
  async function setBillingAccount(projectId, billingAccountName) {
51
+ await (0, ensureApiEnabled_1.ensure)(projectId, "cloudbilling.googleapis.com", "billing", true);
30
52
  const res = await client.put(utils.endpoint(["projects", projectId, "billingInfo"]), {
31
53
  billingAccountName: billingAccountName,
32
- }, { retryCodes: [429, 500, 503] });
33
- return res.body.billingEnabled;
54
+ }, {
55
+ retryCodes: [429, 500, 503],
56
+ headers: { "x-goog-user-project": projectId },
57
+ });
58
+ const enabled = res.body.billingEnabled;
59
+ billingEnabledCache.set(projectId, Promise.resolve(enabled));
60
+ return enabled;
34
61
  }
35
- async function listBillingAccounts() {
36
- const res = await client.get(utils.endpoint(["billingAccounts"]), { retryCodes: [429, 500, 503] });
62
+ async function listBillingAccounts(projectId) {
63
+ if (projectId) {
64
+ await (0, ensureApiEnabled_1.ensure)(projectId, "cloudbilling.googleapis.com", "billing", true);
65
+ }
66
+ const res = await client.get(utils.endpoint(["billingAccounts"]), {
67
+ retryCodes: [429, 500, 503],
68
+ headers: projectId ? { "x-goog-user-project": projectId } : undefined,
69
+ });
37
70
  return res.body.billingAccounts || [];
38
71
  }
package/lib/mcp/index.js CHANGED
@@ -274,7 +274,7 @@ class FirebaseMcpServer {
274
274
  async mcpCallTool(request) {
275
275
  await this.detectProjectRoot();
276
276
  const toolName = request.params.name;
277
- const toolArgs = request.params.arguments;
277
+ const toolArgs = request.params.arguments ?? {};
278
278
  const tool = await this.getTool(toolName);
279
279
  if (!tool)
280
280
  throw new Error(`Tool '${toolName}' could not be found.`);
package/lib/mcp/tool.js CHANGED
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.tool = tool;
4
- const zod_to_json_schema_1 = require("zod-to-json-schema");
4
+ const zod_1 = require("zod");
5
5
  const util_1 = require("./util");
6
6
  const availability_1 = require("./util/availability");
7
7
  function tool(feature, options, fn) {
8
8
  const { isAvailable, ...mcpOptions } = options;
9
9
  return {
10
- mcp: { ...mcpOptions, inputSchema: (0, util_1.cleanSchema)((0, zod_to_json_schema_1.zodToJsonSchema)(options.inputSchema)) },
10
+ mcp: {
11
+ ...mcpOptions,
12
+ inputSchema: (0, util_1.cleanSchema)(zod_1.z.toJSONSchema(options.inputSchema, { target: "draft-7", io: "input" })),
13
+ },
11
14
  fn,
12
15
  isAvailable: (ctx) => {
13
16
  const isAvailableFunc = isAvailable || (0, availability_1.getDefaultFeatureAvailabilityCheck)(feature);
@@ -30,7 +30,7 @@ exports.fetch_logs = (0, tool_1.tool)("apphosting", {
30
30
  requiresAuth: true,
31
31
  requiresProject: true,
32
32
  },
33
- }, async ({ buildLogs, backendId, location } = {}, { projectId }) => {
33
+ }, async ({ buildLogs, backendId, location }, { projectId }) => {
34
34
  location || (location = "");
35
35
  if (!backendId) {
36
36
  return (0, util_1.toContent)(`backendId must be specified.`);
@@ -30,7 +30,7 @@ exports.list_backends = (0, tool_1.tool)("apphosting", {
30
30
  requiresAuth: true,
31
31
  requiresProject: true,
32
32
  },
33
- }, async ({ location } = {}, { projectId }) => {
33
+ }, async ({ location }, { projectId }) => {
34
34
  projectId = projectId || "";
35
35
  if (!location)
36
36
  location = "-";
@@ -20,7 +20,17 @@ function getAuthClient(config) {
20
20
  if (authClient) {
21
21
  return authClient;
22
22
  }
23
- authClient = new google_auth_library_1.GoogleAuth(config);
23
+ const authConfig = {
24
+ ...config,
25
+ clientOptions: {
26
+ ...config.clientOptions,
27
+ transporterOptions: {
28
+ ...config.clientOptions?.transporterOptions,
29
+ agent: apiv2.noKeepAliveAgent,
30
+ },
31
+ },
32
+ };
33
+ authClient = new google_auth_library_1.GoogleAuth(authConfig);
24
34
  return authClient;
25
35
  }
26
36
  async function autoAuth(options, authScopes) {