firebase-tools 15.16.0 → 15.17.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.
- package/lib/api.js +1 -1
- package/lib/deploy/apphosting/release.js +1 -1
- package/lib/deploy/functions/prepare.js +113 -3
- package/lib/deploy/functions/services/ailogic.js +7 -0
- package/lib/deploy/functions/services/database.js +16 -0
- package/lib/deploy/functions/services/firestore.js +1 -0
- package/lib/deploy/functions/services/storage.js +15 -1
- package/lib/deploy/functions/triggerRegionHelper.js +111 -2
- package/lib/emulator/downloadableEmulatorInfo.json +23 -23
- package/lib/emulator/functionsEmulatorShared.js +2 -1
- package/lib/env.js +5 -1
- package/lib/firestore/api-sort.js +22 -0
- package/lib/firestore/api-types.js +11 -1
- package/lib/firestore/api.js +21 -1
- package/lib/firestore/fsConfig.js +8 -0
- package/lib/firestore/pretty-print.js +26 -8
- package/lib/frameworks/next/index.js +1 -1
- package/lib/mcp/apps/deploy/mcp-app.js +120 -0
- package/lib/mcp/apps/deploy/vite.config.js +16 -0
- package/lib/mcp/apps/init/mcp-app.js +230 -0
- package/lib/mcp/apps/init/vite.config.js +16 -0
- package/lib/mcp/apps/update_environment/mcp-app.js +38 -36
- package/lib/mcp/apps/update_environment/vite.config.js +16 -0
- package/lib/mcp/index.js +16 -5
- package/lib/mcp/resources/deploy_ui.js +31 -0
- package/lib/mcp/resources/index.js +4 -0
- package/lib/mcp/resources/init_ui.js +31 -0
- package/lib/mcp/resources/update_environment_ui.js +3 -3
- package/lib/mcp/tools/auth/get_users.js +1 -1
- package/lib/mcp/tools/core/deploy.js +87 -0
- package/lib/mcp/tools/core/deploy_status.js +32 -0
- package/lib/mcp/tools/core/index.js +4 -0
- package/lib/mcp/tools/core/init.js +3 -0
- package/lib/mcp/tools/core/update_environment.js +3 -0
- package/lib/mcp/tools/firestore/query_collection.js +1 -1
- package/lib/mcp/tools/functions/list_functions.js +2 -2
- package/lib/mcp/util/jobs.js +31 -0
- package/lib/mcp/util.js +5 -4
- package/lib/tsconfig.compile.tsbuildinfo +1 -1
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/templates/init/functions/dart/pubspec.yaml +1 -1
- package/templates/init/functions/dart/server.dart +2 -2
package/lib/api.js
CHANGED
|
@@ -66,7 +66,7 @@ const functionsV2Origin = () => utils.envOverride("FIREBASE_FUNCTIONS_V2_URL", "
|
|
|
66
66
|
exports.functionsV2Origin = functionsV2Origin;
|
|
67
67
|
const runOrigin = () => utils.envOverride("CLOUD_RUN_URL", "https://run.googleapis.com");
|
|
68
68
|
exports.runOrigin = runOrigin;
|
|
69
|
-
const functionsDefaultRegion = () => utils.envOverride("FIREBASE_FUNCTIONS_DEFAULT_REGION", "
|
|
69
|
+
const functionsDefaultRegion = () => utils.envOverride("FIREBASE_FUNCTIONS_DEFAULT_REGION", "REGION_TBD");
|
|
70
70
|
exports.functionsDefaultRegion = functionsDefaultRegion;
|
|
71
71
|
const cloudbuildOrigin = () => utils.envOverride("FIREBASE_CLOUDBUILD_URL", "https://cloudbuild.googleapis.com");
|
|
72
72
|
exports.cloudbuildOrigin = cloudbuildOrigin;
|
|
@@ -37,6 +37,7 @@ async function default_1(context, options) {
|
|
|
37
37
|
(0, utils_1.logLabeledBullet)("apphosting", `You may also track the rollout(s) at:\n\t${(0, api_1.consoleOrigin)()}/project/${projectId}/apphosting`);
|
|
38
38
|
const rolloutsSpinner = ora(`Starting rollout(s) for backend(s) ${backendIds.join(", ")}; this may take a few minutes. It's safe to exit now.\n`).start();
|
|
39
39
|
const results = await Promise.allSettled(rollouts);
|
|
40
|
+
rolloutsSpinner.stop();
|
|
40
41
|
let failed = false;
|
|
41
42
|
for (let i = 0; i < results.length; i++) {
|
|
42
43
|
const res = results[i];
|
|
@@ -51,7 +52,6 @@ async function default_1(context, options) {
|
|
|
51
52
|
(0, utils_1.logLabeledError)("apphosting", `${res.reason}`);
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
|
-
rolloutsSpinner.stop();
|
|
55
55
|
if (failed) {
|
|
56
56
|
throw new error_1.FirebaseError("One or more rollouts failed. Please review the errors above and try again.");
|
|
57
57
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EVENTARC_SOURCE_ENV = void 0;
|
|
3
|
+
exports.DEFAULT_FUNCTION_REGION = exports.EVENTARC_SOURCE_ENV = void 0;
|
|
4
4
|
exports.prepare = prepare;
|
|
5
5
|
exports.matchRegionsForExisting = matchRegionsForExisting;
|
|
6
6
|
exports.resolveDefaultRegions = resolveDefaultRegions;
|
|
@@ -23,6 +23,12 @@ const runtimes = require("./runtimes");
|
|
|
23
23
|
const supported = require("./runtimes/supported");
|
|
24
24
|
const validate = require("./validate");
|
|
25
25
|
const ensure = require("./ensure");
|
|
26
|
+
const events = require("../../functions/events/v1");
|
|
27
|
+
const firestore_1 = require("./services/firestore");
|
|
28
|
+
const storage_1 = require("./services/storage");
|
|
29
|
+
const database_1 = require("./services/database");
|
|
30
|
+
const ailogic_1 = require("./services/ailogic");
|
|
31
|
+
const names_1 = require("../../dataconnect/names");
|
|
26
32
|
const api_1 = require("../../api");
|
|
27
33
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
28
34
|
const utils_1 = require("../../utils");
|
|
@@ -43,6 +49,7 @@ const functional_1 = require("../../functional");
|
|
|
43
49
|
const prepare_1 = require("../extensions/prepare");
|
|
44
50
|
const prompt = require("../../prompt");
|
|
45
51
|
exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
|
|
52
|
+
exports.DEFAULT_FUNCTION_REGION = "us-central1";
|
|
46
53
|
async function prepare(context, options, payload) {
|
|
47
54
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
48
55
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
@@ -256,9 +263,112 @@ function matchRegionsForExisting(want, have) {
|
|
|
256
263
|
}
|
|
257
264
|
async function resolveDefaultRegions(want, have) {
|
|
258
265
|
matchRegionsForExisting(want, have);
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
const endpoints = Object.values(want.endpoints[build.REGION_TBD] || {});
|
|
267
|
+
for (const endpoint of endpoints) {
|
|
268
|
+
let resolvedRegion = "us-central1";
|
|
269
|
+
try {
|
|
270
|
+
if (backend.isBlockingTriggered(endpoint)) {
|
|
271
|
+
resolvedRegion = resolveRegionForBlockingTrigger(endpoint);
|
|
272
|
+
}
|
|
273
|
+
else if (backend.isEventTriggered(endpoint)) {
|
|
274
|
+
resolvedRegion = await resolveRegionForEventTrigger(endpoint);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
logger_1.logger.debug(`Failed to resolve region for endpoint ${endpoint.id}. Defaulting to us-central1.`, (0, error_1.getErrStack)(err));
|
|
279
|
+
}
|
|
280
|
+
moveEndpointToRegion(want, endpoint, resolvedRegion);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function resolveRegionForBlockingTrigger(endpoint) {
|
|
284
|
+
const eventType = endpoint.blockingTrigger.eventType;
|
|
285
|
+
if (events.AUTH_BLOCKING_EVENTS.includes(eventType)) {
|
|
286
|
+
return "us-east1";
|
|
287
|
+
}
|
|
288
|
+
if ((0, ailogic_1.isGlobalAILogicEndpoint)(endpoint)) {
|
|
289
|
+
return "us-east1";
|
|
290
|
+
}
|
|
291
|
+
return exports.DEFAULT_FUNCTION_REGION;
|
|
292
|
+
}
|
|
293
|
+
async function resolveRegionForEventTrigger(endpoint) {
|
|
294
|
+
const eventTrigger = endpoint.eventTrigger;
|
|
295
|
+
const eventType = eventTrigger.eventType;
|
|
296
|
+
if (eventType.startsWith("google.cloud.pubsub.") ||
|
|
297
|
+
eventType.startsWith("providers/cloud.auth/eventTypes/") ||
|
|
298
|
+
eventType.startsWith("providers/firebase.auth/eventTypes/") ||
|
|
299
|
+
eventType.startsWith("google.firebase.testlab.") ||
|
|
300
|
+
eventType.startsWith("google.firebase.remoteconfig.") ||
|
|
301
|
+
eventType.startsWith("google.firebase.firebasealerts.")) {
|
|
302
|
+
return "us-east1";
|
|
303
|
+
}
|
|
304
|
+
if (eventType.startsWith("google.cloud.firestore.")) {
|
|
305
|
+
try {
|
|
306
|
+
const databaseId = eventTrigger.eventFilters?.database || "(default)";
|
|
307
|
+
const db = await (0, firestore_1.getDatabase)(endpoint.project, databaseId);
|
|
308
|
+
const locationId = db.locationId.toLowerCase();
|
|
309
|
+
if (locationId === "nam5" || locationId === "nam7")
|
|
310
|
+
return "us-central1";
|
|
311
|
+
if (locationId === "eur3")
|
|
312
|
+
return "europe-west1";
|
|
313
|
+
return locationId;
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
logger_1.logger.debug("Failed to resolve Firestore database location", (0, error_1.getErrStack)(err));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (eventType.startsWith("google.cloud.storage.")) {
|
|
320
|
+
try {
|
|
321
|
+
const bucketName = eventTrigger.eventFilters?.bucket;
|
|
322
|
+
if (bucketName) {
|
|
323
|
+
const bucket = await (0, storage_1.getBucket)(bucketName);
|
|
324
|
+
const locationId = bucket.location.toLowerCase();
|
|
325
|
+
if (locationId === "us")
|
|
326
|
+
return "us-east1";
|
|
327
|
+
if (locationId === "eu")
|
|
328
|
+
return "europe-west1";
|
|
329
|
+
if (locationId === "asia")
|
|
330
|
+
return "asia-east1";
|
|
331
|
+
return locationId;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
logger_1.logger.debug("Failed to resolve Cloud Storage bucket location", (0, error_1.getErrStack)(err));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (eventType.startsWith("google.firebase.database.")) {
|
|
339
|
+
if (eventTrigger.region)
|
|
340
|
+
return eventTrigger.region;
|
|
341
|
+
try {
|
|
342
|
+
const instanceName = eventTrigger.eventFilters?.instance;
|
|
343
|
+
if (instanceName) {
|
|
344
|
+
const details = await (0, database_1.getDatabaseInstanceDetails)(endpoint.project, instanceName);
|
|
345
|
+
if (details.location && details.location !== "-") {
|
|
346
|
+
return details.location.toLowerCase();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
catch (err) {
|
|
351
|
+
logger_1.logger.debug("Failed to resolve Realtime Database instance location", (0, error_1.getErrStack)(err));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (eventType.startsWith("google.firebase.dataconnect.")) {
|
|
355
|
+
if (eventTrigger.region)
|
|
356
|
+
return eventTrigger.region;
|
|
357
|
+
try {
|
|
358
|
+
const service = eventTrigger.eventFilters?.service;
|
|
359
|
+
if (service) {
|
|
360
|
+
return (0, names_1.parseServiceName)(service).location;
|
|
361
|
+
}
|
|
362
|
+
const connector = eventTrigger.eventFilters?.connector;
|
|
363
|
+
if (connector) {
|
|
364
|
+
return (0, names_1.parseConnectorName)(connector).location;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
logger_1.logger.debug("Failed to resolve DataConnect location", (0, error_1.getErrStack)(err));
|
|
369
|
+
}
|
|
261
370
|
}
|
|
371
|
+
return exports.DEFAULT_FUNCTION_REGION;
|
|
262
372
|
}
|
|
263
373
|
function inferDetailsFromExisting(want, have, usedDotenv) {
|
|
264
374
|
for (const wantE of backend.allEndpoints(want)) {
|
|
@@ -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,6 +20,12 @@ 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();
|
|
@@ -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
|
|
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 (
|
|
15
|
+
if (!backend.isEventTriggered(ep)) {
|
|
10
16
|
continue;
|
|
11
17
|
}
|
|
12
|
-
|
|
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
|
}
|
|
@@ -54,36 +54,36 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "3.4.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
57
|
+
"version": "3.4.7",
|
|
58
|
+
"expectedSize": 32361408,
|
|
59
|
+
"expectedChecksum": "b756c557573420a54da92e9d66934355",
|
|
60
|
+
"expectedChecksumSHA256": "d97bbc39e6c9960d233db975c3f2e66d43c2a34436f8e39fae3cbe2a815e2c6b",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.7",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.7"
|
|
63
63
|
},
|
|
64
64
|
"darwin_arm64": {
|
|
65
|
-
"version": "3.4.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
65
|
+
"version": "3.4.7",
|
|
66
|
+
"expectedSize": 30504610,
|
|
67
|
+
"expectedChecksum": "f3015d7cfd03bd4fcb7d76d6aac29c84",
|
|
68
|
+
"expectedChecksumSHA256": "3b0d4d273dabd04dc01ef57001180ed6daae2f1781d25f1f01eb6da5d25d5ae5",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.7",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.7"
|
|
71
71
|
},
|
|
72
72
|
"win32": {
|
|
73
|
-
"version": "3.4.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
73
|
+
"version": "3.4.7",
|
|
74
|
+
"expectedSize": 32403456,
|
|
75
|
+
"expectedChecksum": "7fac83eee96f4f638d84a9c3a6950040",
|
|
76
|
+
"expectedChecksumSHA256": "42a88d0377565a94b966336d45b45a8ec24c6a81b0206654327d7722107569de",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.7",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.7.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.4.
|
|
81
|
+
"version": "3.4.7",
|
|
82
82
|
"expectedSize": 31518904,
|
|
83
|
-
"expectedChecksum": "
|
|
84
|
-
"expectedChecksumSHA256": "
|
|
85
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.
|
|
86
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.
|
|
83
|
+
"expectedChecksum": "2aa615b65f12a9efd768e13ab92bf457",
|
|
84
|
+
"expectedChecksumSHA256": "7ce36de105950172ee6f41100df93c92ba2cd2b2f89cb157740fc1ae808fcebb",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.7",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.7"
|
|
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.
|
|
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";
|
package/lib/firestore/api.js
CHANGED
|
@@ -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;
|