firebase-tools 15.16.0 → 15.18.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 (54) hide show
  1. package/lib/api.js +1 -1
  2. package/lib/apphosting/localbuilds.js +116 -5
  3. package/lib/apphosting/universalMakerDownload.js +72 -0
  4. package/lib/apphosting/universalMakerInfo.json +16 -0
  5. package/lib/archiveDirectory.js +1 -1
  6. package/lib/deploy/apphosting/release.js +27 -13
  7. package/lib/deploy/apphosting/util.js +4 -19
  8. package/lib/deploy/functions/backend.js +2 -1
  9. package/lib/deploy/functions/prepare.js +140 -35
  10. package/lib/deploy/functions/prepareFunctionsUpload.js +1 -1
  11. package/lib/deploy/functions/services/ailogic.js +17 -0
  12. package/lib/deploy/functions/services/database.js +16 -0
  13. package/lib/deploy/functions/services/firestore.js +1 -0
  14. package/lib/deploy/functions/services/storage.js +15 -1
  15. package/lib/deploy/functions/triggerRegionHelper.js +111 -2
  16. package/lib/deploy/functions/validate.js +1 -1
  17. package/lib/downloadUtils.js +24 -0
  18. package/lib/emulator/download.js +2 -24
  19. package/lib/emulator/downloadableEmulatorInfo.json +31 -31
  20. package/lib/emulator/functionsEmulatorShared.js +2 -1
  21. package/lib/env.js +5 -1
  22. package/lib/firestore/api-sort.js +22 -0
  23. package/lib/firestore/api-types.js +11 -1
  24. package/lib/firestore/api.js +21 -1
  25. package/lib/firestore/fsConfig.js +8 -0
  26. package/lib/firestore/pretty-print.js +26 -8
  27. package/lib/frameworks/next/index.js +1 -1
  28. package/lib/fsAsync.js +53 -10
  29. package/lib/mcp/apps/deploy/mcp-app.js +120 -0
  30. package/lib/mcp/apps/deploy/vite.config.js +16 -0
  31. package/lib/mcp/apps/init/mcp-app.js +230 -0
  32. package/lib/mcp/apps/init/vite.config.js +16 -0
  33. package/lib/mcp/apps/update_environment/mcp-app.js +38 -36
  34. package/lib/mcp/apps/update_environment/vite.config.js +16 -0
  35. package/lib/mcp/index.js +16 -5
  36. package/lib/mcp/resources/deploy_ui.js +31 -0
  37. package/lib/mcp/resources/index.js +4 -0
  38. package/lib/mcp/resources/init_ui.js +31 -0
  39. package/lib/mcp/resources/update_environment_ui.js +3 -3
  40. package/lib/mcp/tools/auth/get_users.js +1 -1
  41. package/lib/mcp/tools/core/deploy.js +87 -0
  42. package/lib/mcp/tools/core/deploy_status.js +32 -0
  43. package/lib/mcp/tools/core/index.js +4 -0
  44. package/lib/mcp/tools/core/init.js +3 -0
  45. package/lib/mcp/tools/core/update_environment.js +3 -0
  46. package/lib/mcp/tools/firestore/query_collection.js +1 -1
  47. package/lib/mcp/tools/functions/list_functions.js +2 -2
  48. package/lib/mcp/util/jobs.js +31 -0
  49. package/lib/mcp/util.js +5 -4
  50. package/lib/tsconfig.compile.tsbuildinfo +1 -1
  51. package/lib/tsconfig.publish.tsbuildinfo +1 -1
  52. package/package.json +3 -3
  53. package/templates/init/functions/dart/pubspec.yaml +1 -1
  54. package/templates/init/functions/dart/server.dart +2 -2
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERATE_CONTENT = exports.AI_LOGIC_BEFORE_GENERATE_CONTENT = void 0;
4
4
  exports.isAILogicEvent = isAILogicEvent;
5
+ exports.isGlobalAILogicEndpoint = isGlobalAILogicEndpoint;
5
6
  const backend = require("../backend");
6
7
  const error_1 = require("../../../error");
7
8
  const ailogicApi = require("../../../gcp/ailogic");
@@ -19,12 +20,28 @@ function isAILogicEvent(endpoint) {
19
20
  }
20
21
  return exports.AI_LOGIC_EVENTS.includes(endpoint.blockingTrigger.eventType);
21
22
  }
23
+ function isGlobalAILogicEndpoint(endpoint) {
24
+ if (!isAILogicEvent(endpoint)) {
25
+ return false;
26
+ }
27
+ return !endpoint.blockingTrigger.options?.regionalWebhook;
28
+ }
22
29
  class AILogicService {
23
30
  constructor() {
24
31
  this.ensureTriggerRegion = () => Promise.resolve();
25
32
  this.name = "ailogic";
26
33
  this.api = "firebasevertexai.googleapis.com";
27
34
  }
35
+ async requiredProjectBindings(projectNumber) {
36
+ return [
37
+ {
38
+ role: "roles/run.invoker",
39
+ members: [
40
+ `serviceAccount:service-${projectNumber}@gcp-sa-firebasevertexai.iam.gserviceaccount.com`,
41
+ ],
42
+ },
43
+ ];
44
+ }
28
45
  validateTrigger(endpoint, wantBackend) {
29
46
  if (!isAILogicEvent(endpoint)) {
30
47
  return;
@@ -1,7 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clearCache = clearCache;
4
+ exports.getDatabaseInstanceDetails = getDatabaseInstanceDetails;
3
5
  exports.ensureDatabaseTriggerRegion = ensureDatabaseTriggerRegion;
4
6
  const error_1 = require("../../../error");
7
+ const database_1 = require("../../../management/database");
8
+ const instanceCache = new Map();
9
+ function clearCache() {
10
+ instanceCache.clear();
11
+ }
12
+ async function getDatabaseInstanceDetails(projectId, instanceName) {
13
+ const key = `${projectId}/${instanceName}`;
14
+ if (instanceCache.has(key)) {
15
+ return instanceCache.get(key);
16
+ }
17
+ const details = await (0, database_1.getDatabaseInstanceDetails)(projectId, instanceName);
18
+ instanceCache.set(key, details);
19
+ return details;
20
+ }
5
21
  function ensureDatabaseTriggerRegion(endpoint) {
6
22
  if (!endpoint.eventTrigger.region) {
7
23
  endpoint.eventTrigger.region = endpoint.region;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.clearCache = clearCache;
4
+ exports.getDatabase = getDatabase;
4
5
  exports.ensureFirestoreTriggerRegion = ensureFirestoreTriggerRegion;
5
6
  const firestore = require("../../../gcp/firestore");
6
7
  const error_1 = require("../../../error");
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clearCache = clearCache;
4
+ exports.getBucket = getBucket;
3
5
  exports.obtainStorageBindings = obtainStorageBindings;
4
6
  exports.ensureStorageTriggerRegion = ensureStorageTriggerRegion;
5
7
  const storage = require("../../../gcp/storage");
@@ -7,6 +9,18 @@ const logger_1 = require("../../../logger");
7
9
  const error_1 = require("../../../error");
8
10
  const location_1 = require("../../../gcp/location");
9
11
  const PUBSUB_PUBLISHER_ROLE = "roles/pubsub.publisher";
12
+ const bucketCache = new Map();
13
+ function clearCache() {
14
+ bucketCache.clear();
15
+ }
16
+ async function getBucket(bucketName) {
17
+ if (bucketCache.has(bucketName)) {
18
+ return bucketCache.get(bucketName);
19
+ }
20
+ const b = await storage.getBucket(bucketName);
21
+ bucketCache.set(bucketName, b);
22
+ return b;
23
+ }
10
24
  async function obtainStorageBindings(projectNumber) {
11
25
  const storageResponse = await storage.getServiceAccount(projectNumber);
12
26
  const storageServiceAgent = `serviceAccount:${storageResponse.email_address}`;
@@ -26,7 +40,7 @@ async function ensureStorageTriggerRegion(endpoint) {
26
40
  }
27
41
  logger_1.logger.debug(`Looking up bucket region for the storage event trigger on bucket ${eventTrigger.eventFilters.bucket}`);
28
42
  try {
29
- const bucket = await storage.getBucket(eventTrigger.eventFilters.bucket);
43
+ const bucket = await getBucket(eventTrigger.eventFilters.bucket);
30
44
  eventTrigger.region = bucket.location.toLowerCase();
31
45
  logger_1.logger.debug("Setting the event trigger region to", eventTrigger.region, ".");
32
46
  }
@@ -3,13 +3,122 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ensureTriggerRegions = ensureTriggerRegions;
4
4
  const backend = require("./backend");
5
5
  const services_1 = require("./services");
6
+ const logger_1 = require("../../logger");
7
+ const utils = require("../../utils");
8
+ const storage_1 = require("./services/storage");
9
+ const firestore_1 = require("./services/firestore");
10
+ const database_1 = require("./services/database");
6
11
  async function ensureTriggerRegions(want) {
7
12
  const regionLookups = [];
13
+ const triggerRegionMap = new Map();
8
14
  for (const ep of backend.allEndpoints(want)) {
9
- if (ep.platform === "gcfv1" || !backend.isEventTriggered(ep)) {
15
+ if (!backend.isEventTriggered(ep)) {
10
16
  continue;
11
17
  }
12
- regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep));
18
+ if (ep.platform === "gcfv1") {
19
+ const eventType = ep.eventTrigger.eventType || "";
20
+ const resource = ep.eventTrigger.eventFilters?.resource;
21
+ if (eventType.includes("storage")) {
22
+ const bucketName = extractBucketName(resource);
23
+ if (bucketName) {
24
+ regionLookups.push((0, storage_1.getBucket)(bucketName)
25
+ .then((bucket) => {
26
+ triggerRegionMap.set(backend.functionName(ep), bucket.location.toLowerCase());
27
+ })
28
+ .catch((err) => {
29
+ logger_1.logger.debug(`Failed to resolve trigger region for V1 storage function ${ep.id}:`, err);
30
+ }));
31
+ }
32
+ }
33
+ else if (eventType.includes("firestore")) {
34
+ const dbId = extractDatabaseId(resource);
35
+ regionLookups.push((0, firestore_1.getDatabase)(ep.project, dbId)
36
+ .then((db) => {
37
+ triggerRegionMap.set(backend.functionName(ep), db.locationId.toLowerCase());
38
+ })
39
+ .catch((err) => {
40
+ logger_1.logger.debug(`Failed to resolve trigger region for V1 firestore function ${ep.id}:`, err);
41
+ }));
42
+ }
43
+ else if (eventType.includes("database")) {
44
+ const instanceName = extractInstanceName(resource);
45
+ if (instanceName) {
46
+ regionLookups.push((0, database_1.getDatabaseInstanceDetails)(ep.project, instanceName)
47
+ .then((details) => {
48
+ if (details.location && details.location !== "-") {
49
+ triggerRegionMap.set(backend.functionName(ep), details.location.toLowerCase());
50
+ }
51
+ })
52
+ .catch((err) => {
53
+ logger_1.logger.debug(`Failed to resolve trigger region for V1 database function ${ep.id}:`, err);
54
+ }));
55
+ }
56
+ }
57
+ }
58
+ else {
59
+ regionLookups.push((0, services_1.serviceForEndpoint)(ep).ensureTriggerRegion(ep));
60
+ }
13
61
  }
14
62
  await Promise.all(regionLookups);
63
+ if (process.env.FIREBASE_SUPPRESS_REGION_WARNING === "true") {
64
+ return;
65
+ }
66
+ const offendingFunctions = [];
67
+ for (const ep of backend.allEndpoints(want)) {
68
+ if (!backend.isEventTriggered(ep)) {
69
+ continue;
70
+ }
71
+ let triggerRegion;
72
+ if (ep.platform === "gcfv1") {
73
+ triggerRegion = triggerRegionMap.get(backend.functionName(ep));
74
+ }
75
+ else {
76
+ triggerRegion = ep.eventTrigger.region;
77
+ }
78
+ if (ep.region !== "us-central1" || !triggerRegion || triggerRegion === "global") {
79
+ continue;
80
+ }
81
+ if (!isUSRegion(triggerRegion)) {
82
+ offendingFunctions.push(`- ${ep.id} (us-central1, Trigger: ${triggerRegion})`);
83
+ }
84
+ }
85
+ if (offendingFunctions.length > 0) {
86
+ utils.logLabeledWarning("functions", `The following functions have triggers in different regions than they are located:\n` +
87
+ offendingFunctions.join("\n") +
88
+ `\nTo avoid unnecessary cross-region network hops, consider assigning these functions to their trigger regions or collocating them. ` +
89
+ `To suppress this warning, set FIREBASE_SUPPRESS_REGION_WARNING=true in your environment variables.`);
90
+ }
91
+ }
92
+ function extractBucketName(resource) {
93
+ if (!resource)
94
+ return null;
95
+ const match = /buckets\/([^/]+)/.exec(resource);
96
+ if (match)
97
+ return match[1];
98
+ if (!resource.includes("/"))
99
+ return resource;
100
+ return null;
101
+ }
102
+ function extractDatabaseId(resource) {
103
+ if (!resource)
104
+ return "(default)";
105
+ const match = /databases\/([^/]+)/.exec(resource);
106
+ if (match)
107
+ return match[1];
108
+ if (!resource.includes("/"))
109
+ return resource;
110
+ return "(default)";
111
+ }
112
+ function extractInstanceName(resource) {
113
+ if (!resource)
114
+ return null;
115
+ const match = /instances\/([^/]+)/.exec(resource);
116
+ if (match)
117
+ return match[1];
118
+ if (!resource.includes("/"))
119
+ return resource;
120
+ return null;
121
+ }
122
+ function isUSRegion(region) {
123
+ return region === "us" || region.startsWith("nam") || region.startsWith("us-");
15
124
  }
@@ -221,7 +221,7 @@ async function secretsAreValid(projectId, wantBackend) {
221
221
  validatePlatformTargets(endpoints);
222
222
  await validateSecretVersions(projectId, endpoints);
223
223
  }
224
- const secretsSupportedPlatforms = ["gcfv1", "gcfv2"];
224
+ const secretsSupportedPlatforms = backend.AllFunctionsPlatforms;
225
225
  function validatePlatformTargets(endpoints) {
226
226
  const unsupported = endpoints.filter((e) => !secretsSupportedPlatforms.includes(e.platform));
227
227
  if (unsupported.length > 0) {
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.downloadToTmp = downloadToTmp;
4
+ exports.validateSize = validateSize;
5
+ exports.validateChecksum = validateChecksum;
4
6
  const url_1 = require("url");
7
+ const crypto = require("crypto");
5
8
  const fs = require("fs-extra");
6
9
  const ProgressBar = require("progress");
7
10
  const tmp = require("tmp");
@@ -37,3 +40,24 @@ async function downloadToTmp(remoteUrl, auth = false) {
37
40
  });
38
41
  return tmpfile.name;
39
42
  }
43
+ function validateSize(filepath, expectedSize) {
44
+ return new Promise((resolve, reject) => {
45
+ const stat = fs.statSync(filepath);
46
+ return stat.size === expectedSize
47
+ ? resolve()
48
+ : reject(new error_1.FirebaseError(`download failed, expected ${expectedSize} bytes but got ${stat.size}`, { exit: 1 }));
49
+ });
50
+ }
51
+ function validateChecksum(filepath, expectedChecksum, algorithm = "md5") {
52
+ return new Promise((resolve, reject) => {
53
+ const hash = crypto.createHash(algorithm);
54
+ const stream = fs.createReadStream(filepath);
55
+ stream.on("data", (data) => hash.update(data));
56
+ stream.on("end", () => {
57
+ const checksum = hash.digest("hex");
58
+ return checksum === expectedChecksum
59
+ ? resolve()
60
+ : reject(new error_1.FirebaseError(`download failed, expected checksum ${expectedChecksum} but got ${checksum}`, { exit: 1 }));
61
+ });
62
+ });
63
+ }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.downloadEmulator = downloadEmulator;
4
4
  exports.downloadExtensionVersion = downloadExtensionVersion;
5
- const crypto = require("crypto");
6
5
  const fs = require("fs-extra");
7
6
  const path = require("path");
8
7
  const tmp = require("tmp");
@@ -36,8 +35,8 @@ async function downloadEmulator(name) {
36
35
  throw err;
37
36
  }
38
37
  if (!emulator.opts.skipChecksumAndSize) {
39
- await validateSize(tmpfile, emulator.opts.expectedSize);
40
- await validateChecksum(tmpfile, emulator.opts.expectedChecksum);
38
+ await downloadUtils.validateSize(tmpfile, emulator.opts.expectedSize);
39
+ await downloadUtils.validateChecksum(tmpfile, emulator.opts.expectedChecksum, "md5");
41
40
  }
42
41
  if (emulator.opts.skipCache) {
43
42
  removeOldFiles(name, emulator, true);
@@ -82,24 +81,3 @@ function removeOldFiles(name, emulator, removeAllVersions = false) {
82
81
  }
83
82
  }
84
83
  }
85
- function validateSize(filepath, expectedSize) {
86
- return new Promise((resolve, reject) => {
87
- const stat = fs.statSync(filepath);
88
- return stat.size === expectedSize
89
- ? resolve()
90
- : reject(new error_1.FirebaseError(`download failed, expected ${expectedSize} bytes but got ${stat.size}`, { exit: 1 }));
91
- });
92
- }
93
- function validateChecksum(filepath, expectedChecksum) {
94
- return new Promise((resolve, reject) => {
95
- const hash = crypto.createHash("md5");
96
- const stream = fs.createReadStream(filepath);
97
- stream.on("data", (data) => hash.update(data));
98
- stream.on("end", () => {
99
- const checksum = hash.digest("hex");
100
- return checksum === expectedChecksum
101
- ? resolve()
102
- : reject(new error_1.FirebaseError(`download failed, expected checksum ${expectedChecksum} but got ${checksum}`, { exit: 1 }));
103
- });
104
- });
105
- }
@@ -44,46 +44,46 @@
44
44
  }
45
45
  },
46
46
  "pubsub": {
47
- "version": "0.8.30",
48
- "expectedSize": 52917173,
49
- "expectedChecksum": "f7935320d11894d0c4cd26dd064ee42f",
50
- "expectedChecksumSHA256": "aabf028a55ec3f06fe2223471dad26623be542848abebd7563f8f0b8697dd2a3",
51
- "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.30.zip",
52
- "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.30.zip",
53
- "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.30/pubsub-emulator/bin/cloud-pubsub-emulator"
47
+ "version": "0.8.31",
48
+ "expectedSize": 52942385,
49
+ "expectedChecksum": "04a85aa9873222e9b7a430c6c49cfd9e",
50
+ "expectedChecksumSHA256": "b474e7d3200f5a8b5f7f467ee3b087c94b939e7d41527e935296a7bcabdbea9a",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.31.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.31.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.31/pubsub-emulator/bin/cloud-pubsub-emulator"
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "3.4.6",
58
- "expectedSize": 32361376,
59
- "expectedChecksum": "85cf160307a0ff39b8e625676b89789e",
60
- "expectedChecksumSHA256": "4f66d53776975c32df3c86870602965d2a5a495a14d86906cdbea84c8e40156b",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.6",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
57
+ "version": "3.4.8",
58
+ "expectedSize": 32398304,
59
+ "expectedChecksum": "fee47f6867b5aa939ab667fb801b9807",
60
+ "expectedChecksumSHA256": "0586ffa3614f9231932ee50fb424ee4385100188aac40de91289c705d36ceb67",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.8",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.8"
63
63
  },
64
64
  "darwin_arm64": {
65
- "version": "3.4.6",
66
- "expectedSize": 30504594,
67
- "expectedChecksum": "c1e5c18caa1fadcaf813c98bc05a8d77",
68
- "expectedChecksumSHA256": "1934ec8f6e8788a5521801072df278950b9525b0294c97cc009e65a7446d5567",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.6",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
65
+ "version": "3.4.8",
66
+ "expectedSize": 30537682,
67
+ "expectedChecksum": "f4ccfe080736fb0a35d60841cf439eab",
68
+ "expectedChecksumSHA256": "d3efdafa7c82f843b1cca7ae2b2404235fa9c918b892806ce4a9910e6907318f",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.8",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.8"
71
71
  },
72
72
  "win32": {
73
- "version": "3.4.6",
74
- "expectedSize": 32401408,
75
- "expectedChecksum": "c57c0ca3c50f358122ac741e501c9fde",
76
- "expectedChecksumSHA256": "ed4bde0ebf9499ffd8915091f9ec3886a1c5ba145f54261b1c8c701a38268984",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.6",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6.exe"
73
+ "version": "3.4.8",
74
+ "expectedSize": 32438272,
75
+ "expectedChecksum": "5850b1887b23c351d83b32142bac6858",
76
+ "expectedChecksumSHA256": "d9c2ed151a5d0a9c0530f442f92b3434fe98469e5e6fee230628ba81e9a1540d",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.8",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.8.exe"
79
79
  },
80
80
  "linux": {
81
- "version": "3.4.6",
82
- "expectedSize": 31518904,
83
- "expectedChecksum": "b05791a643618dd8fbdb5e56b474c45b",
84
- "expectedChecksumSHA256": "716ca10ddb2341873fc0febcffbc84420ac1f20262c3d08f9422cee025d1f60e",
85
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.6",
86
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.6"
81
+ "version": "3.4.8",
82
+ "expectedSize": 31555768,
83
+ "expectedChecksum": "c6b43a325a0e3559d7bc325591dda14f",
84
+ "expectedChecksumSHA256": "f1809b93e985e497da8ed0a23c049b28f9ff4482b021c9f86af6b8c1a6949fef",
85
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.8",
86
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.8"
87
87
  }
88
88
  }
89
89
  }
@@ -21,6 +21,7 @@ const fs = require("fs");
21
21
  const crypto_1 = require("crypto");
22
22
  const _ = require("lodash");
23
23
  const backend = require("../deploy/functions/backend");
24
+ const build = require("../deploy/functions/build");
24
25
  const constants_1 = require("./constants");
25
26
  const manifest_1 = require("../extensions/manifest");
26
27
  const extensionsHelper_1 = require("../extensions/extensionsHelper");
@@ -70,7 +71,7 @@ function prepareEndpoints(endpoints) {
70
71
  function emulatedFunctionsFromEndpoints(endpoints) {
71
72
  const regionDefinitions = [];
72
73
  for (const endpoint of endpoints) {
73
- if (!endpoint.region) {
74
+ if (!endpoint.region || endpoint.region === build.REGION_TBD) {
74
75
  endpoint.region = "us-central1";
75
76
  }
76
77
  const def = {
package/lib/env.js CHANGED
@@ -22,7 +22,7 @@ function setFirebaseMcp(value) {
22
22
  isFirebaseMcpFlag = value;
23
23
  }
24
24
  function detectAIAgent() {
25
- if (process.env.ANTIGRAVITY_CLI_ALIAS)
25
+ if (process.env.ANTIGRAVITY_AGENT)
26
26
  return "antigravity";
27
27
  if (process.env.CLAUDECODE)
28
28
  return "claude_code";
@@ -36,5 +36,9 @@ function detectAIAgent() {
36
36
  return "gemini_cli";
37
37
  if (process.env.OPENCODE)
38
38
  return "open_code";
39
+ if (process.env.ANDROID_STUDIO_AGENT)
40
+ return "android_studio_agent";
41
+ if (process.env.KIRO_AGENT_PATH)
42
+ return "kiro";
39
43
  return "unknown";
40
44
  }
@@ -147,6 +147,9 @@ function compareIndexField(a, b) {
147
147
  if (a.vectorConfig !== b.vectorConfig) {
148
148
  return compareVectorConfig(a.vectorConfig, b.vectorConfig);
149
149
  }
150
+ if (a.searchConfig !== b.searchConfig) {
151
+ return compareSearchConfig(a.searchConfig, b.searchConfig);
152
+ }
150
153
  return 0;
151
154
  }
152
155
  function compareFieldIndex(a, b) {
@@ -232,6 +235,25 @@ function compareVectorConfig(a, b) {
232
235
  }
233
236
  return a.dimension - b.dimension;
234
237
  }
238
+ function compareSearchConfig(a, b) {
239
+ if (!a) {
240
+ if (!b) {
241
+ return 0;
242
+ }
243
+ else {
244
+ return 1;
245
+ }
246
+ }
247
+ else if (!b) {
248
+ return -1;
249
+ }
250
+ const aTextSpecs = a?.textSpec?.indexSpecs?.length || 0;
251
+ const bTextSpecs = b?.textSpec?.indexSpecs?.length || 0;
252
+ if (aTextSpecs !== bTextSpecs) {
253
+ return aTextSpecs - bTextSpecs;
254
+ }
255
+ return 0;
256
+ }
235
257
  function compareArrays(a, b, fn) {
236
258
  const minFields = Math.min(a.length, b.length);
237
259
  for (let i = 0; i < minFields; i++) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecurrenceType = exports.DatabaseEdition = exports.DataAccessMode = exports.RealtimeUpdatesMode = exports.PointInTimeRecoveryEnablement = exports.DatabaseDeleteProtectionState = exports.EnablementOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.Density = exports.ApiScope = exports.QueryScope = exports.Mode = void 0;
3
+ exports.RecurrenceType = exports.DatabaseEdition = exports.DataAccessMode = exports.RealtimeUpdatesMode = exports.PointInTimeRecoveryEnablement = exports.DatabaseDeleteProtectionState = exports.EnablementOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.TextMatchType = exports.TextIndexType = exports.ArrayConfig = exports.Order = exports.Density = exports.ApiScope = exports.QueryScope = exports.Mode = void 0;
4
4
  exports.validateEnablementOption = validateEnablementOption;
5
5
  const error_1 = require("../error");
6
6
  var Mode;
@@ -36,6 +36,16 @@ var ArrayConfig;
36
36
  (function (ArrayConfig) {
37
37
  ArrayConfig["CONTAINS"] = "CONTAINS";
38
38
  })(ArrayConfig || (exports.ArrayConfig = ArrayConfig = {}));
39
+ var TextIndexType;
40
+ (function (TextIndexType) {
41
+ TextIndexType["TEXT_INDEX_TYPE_UNSPECIFIED"] = "TEXT_INDEX_TYPE_UNSPECIFIED";
42
+ TextIndexType["TOKENIZED"] = "TOKENIZED";
43
+ })(TextIndexType || (exports.TextIndexType = TextIndexType = {}));
44
+ var TextMatchType;
45
+ (function (TextMatchType) {
46
+ TextMatchType["TEXT_MATCH_TYPE_UNSPECIFIED"] = "TEXT_MATCH_TYPE_UNSPECIFIED";
47
+ TextMatchType["MATCH_GLOBALLY"] = "MATCH_GLOBALLY";
48
+ })(TextMatchType || (exports.TextMatchType = TextMatchType = {}));
39
49
  var State;
40
50
  (function (State) {
41
51
  State["CREATING"] = "CREATING";
@@ -254,7 +254,7 @@ class FirestoreApi {
254
254
  validator.assertHas(index, "fields");
255
255
  index.fields.forEach((field) => {
256
256
  validator.assertHas(field, "fieldPath");
257
- validator.assertHasOneOf(field, ["order", "arrayConfig", "vectorConfig"]);
257
+ validator.assertHasOneOf(field, ["order", "arrayConfig", "searchConfig", "vectorConfig"]);
258
258
  if (field.order) {
259
259
  validator.assertEnum(field, "order", Object.keys(types.Order));
260
260
  }
@@ -265,6 +265,20 @@ class FirestoreApi {
265
265
  validator.assertType("vectorConfig.dimension", field.vectorConfig.dimension, "number");
266
266
  validator.assertHas(field.vectorConfig, "flat");
267
267
  }
268
+ if (field.searchConfig) {
269
+ if (field.searchConfig.textSpec) {
270
+ validator.assertHas(field.searchConfig.textSpec, "indexSpecs");
271
+ for (const spec of field.searchConfig.textSpec.indexSpecs) {
272
+ validator.assertHas(spec, "indexType");
273
+ validator.assertEnum(spec, "indexType", Object.keys(types.TextIndexType));
274
+ validator.assertHas(spec, "matchType");
275
+ validator.assertEnum(spec, "matchType", Object.keys(types.TextMatchType));
276
+ }
277
+ }
278
+ if (field.searchConfig.geoSpec?.geoJsonIndexingDisabled !== undefined) {
279
+ validator.assertType("searchConfig.geoSpec.geoJsonIndexingDisabled", field.searchConfig.geoSpec.geoJsonIndexingDisabled, "boolean");
280
+ }
281
+ }
268
282
  });
269
283
  }
270
284
  validateField(field) {
@@ -405,6 +419,9 @@ class FirestoreApi {
405
419
  if (!utils.deepEqual(iField.vectorConfig, sField.vectorConfig)) {
406
420
  return false;
407
421
  }
422
+ if (!utils.deepEqual(iField.searchConfig, sField.searchConfig)) {
423
+ return false;
424
+ }
408
425
  i++;
409
426
  }
410
427
  return true;
@@ -488,6 +505,9 @@ class FirestoreApi {
488
505
  else if (field.vectorConfig) {
489
506
  f.vectorConfig = field.vectorConfig;
490
507
  }
508
+ else if (field.searchConfig) {
509
+ f.searchConfig = field.searchConfig;
510
+ }
491
511
  else if (field.mode === types.Mode.ARRAY_CONTAINS) {
492
512
  f.arrayConfig = types.ArrayConfig.CONTAINS;
493
513
  }
@@ -35,6 +35,14 @@ function getFirestoreConfig(projectId, options) {
35
35
  return [];
36
36
  }
37
37
  }
38
+ if (onlyDatabases.has("rules")) {
39
+ onlyDatabases.delete("rules");
40
+ allDatabases = true;
41
+ }
42
+ if (onlyDatabases.has("indexes")) {
43
+ onlyDatabases.delete("indexes");
44
+ allDatabases = true;
45
+ }
38
46
  const results = [];
39
47
  for (const c of fsConfig) {
40
48
  const { database, target } = c;
@@ -19,6 +19,15 @@ class PrettyPrint {
19
19
  logger_1.logger.info(this.prettyIndexString(index));
20
20
  });
21
21
  }
22
+ getDatabaseEdition(database) {
23
+ return !database.databaseEdition ||
24
+ database.databaseEdition === types.DatabaseEdition.DATABASE_EDITION_UNSPECIFIED
25
+ ? types.DatabaseEdition.STANDARD
26
+ : database.databaseEdition;
27
+ }
28
+ getDatabaseApiType(database) {
29
+ return database.type;
30
+ }
22
31
  prettyPrintDatabases(databases) {
23
32
  if (databases.length === 0) {
24
33
  logger_1.logger.info("No databases found.");
@@ -26,10 +35,18 @@ class PrettyPrint {
26
35
  }
27
36
  const sortedDatabases = databases.sort(sort.compareApiDatabase);
28
37
  const table = new Table({
29
- head: ["Database Name"],
30
- colWidths: [Math.max(...sortedDatabases.map((database) => database.name.length + 5), 20)],
38
+ head: ["Database Name", "Edition", "Type"],
39
+ colWidths: [
40
+ Math.max(...sortedDatabases.map((database) => database.name.length + 5), 20),
41
+ 20,
42
+ 20,
43
+ ],
31
44
  });
32
- table.push(...sortedDatabases.map((database) => [this.prettyDatabaseString(database)]));
45
+ table.push(...sortedDatabases.map((database) => {
46
+ const edition = this.getDatabaseEdition(database);
47
+ const apiType = this.getDatabaseApiType(database);
48
+ return [this.prettyDatabaseString(database), edition, apiType];
49
+ }));
33
50
  logger_1.logger.info(table.toString());
34
51
  }
35
52
  prettyPrintDatabase(database) {
@@ -41,11 +58,9 @@ class PrettyPrint {
41
58
  head: ["Field", "Value"],
42
59
  colWidths: [30, colValueWidth],
43
60
  });
44
- const edition = !database.databaseEdition ||
45
- database.databaseEdition === types.DatabaseEdition.DATABASE_EDITION_UNSPECIFIED
46
- ? types.DatabaseEdition.STANDARD
47
- : database.databaseEdition;
48
- table.push(["Name", clc.yellow(database.name)], ["Create Time", clc.yellow(database.createTime)], ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(database.type)], ["Edition", clc.yellow(edition)], ["Location", clc.yellow(database.locationId)], ["Delete Protection State", clc.yellow(database.deleteProtectionState)], ["Point In Time Recovery", clc.yellow(database.pointInTimeRecoveryEnablement)], ["Earliest Version Time", clc.yellow(database.earliestVersionTime)], ["Version Retention Period", clc.yellow(database.versionRetentionPeriod)]);
61
+ const edition = this.getDatabaseEdition(database);
62
+ const apiType = this.getDatabaseApiType(database);
63
+ table.push(["Name", clc.yellow(database.name)], ["Create Time", clc.yellow(database.createTime)], ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(apiType)], ["Edition", clc.yellow(edition)], ["Location", clc.yellow(database.locationId)], ["Delete Protection State", clc.yellow(database.deleteProtectionState)], ["Point In Time Recovery", clc.yellow(database.pointInTimeRecoveryEnablement)], ["Earliest Version Time", clc.yellow(database.earliestVersionTime)], ["Version Retention Period", clc.yellow(database.versionRetentionPeriod)]);
49
64
  if (database.cmekConfig) {
50
65
  table.push(["KMS Key Name", clc.yellow(database.cmekConfig.kmsKeyName)]);
51
66
  if (database.cmekConfig.activeKeyVersion) {
@@ -195,6 +210,9 @@ class PrettyPrint {
195
210
  else if (field.arrayConfig) {
196
211
  configString = field.arrayConfig;
197
212
  }
213
+ else if (field.searchConfig) {
214
+ configString = "SEARCH";
215
+ }
198
216
  else if (field.vectorConfig) {
199
217
  configString = `VECTOR<${field.vectorConfig.dimension}>`;
200
218
  }
@@ -31,7 +31,7 @@ const logger_1 = require("../../logger");
31
31
  const env_1 = require("../../functions/env");
32
32
  const DEFAULT_BUILD_SCRIPT = ["next build"];
33
33
  const PUBLIC_DIR = "public";
34
- exports.supportedRange = "12 - 15.0";
34
+ exports.supportedRange = "12 - 16.0";
35
35
  exports.name = "Next.js";
36
36
  exports.support = "preview";
37
37
  exports.type = 2;