firebase-tools 15.19.0 → 15.20.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/agentSkills.js +2 -1
- package/lib/api.js +1 -3
- package/lib/appdistribution/client.js +6 -5
- package/lib/appdistribution/options-parser-util.js +21 -0
- package/lib/archiveFile.js +30 -0
- package/lib/command.js +7 -0
- package/lib/commands/appdistribution-distribute.js +13 -4
- package/lib/commands/apptesting.js +9 -3
- package/lib/commands/crashlytics-sourcemap-upload.js +61 -0
- package/lib/commands/index.js +4 -0
- package/lib/crashlytics/sourcemap.js +270 -0
- package/lib/dataconnect/ensureApis.js +0 -13
- package/lib/deploy/apphosting/prepare.js +6 -6
- package/lib/deploy/dataconnect/context.js +0 -2
- package/lib/deploy/dataconnect/deploy.js +0 -19
- package/lib/deploy/functions/prepare.js +8 -104
- package/lib/deploy/functions/services/ailogic.js +17 -10
- package/lib/deploy/functions/services/auth.js +3 -0
- package/lib/deploy/functions/services/database.js +18 -0
- package/lib/deploy/functions/services/dataconnect.js +20 -0
- package/lib/deploy/functions/services/firestore.js +12 -0
- package/lib/deploy/functions/services/index.js +18 -7
- package/lib/deploy/functions/services/storage.js +14 -0
- package/lib/deploy/functions/triggerRegionHelper.js +2 -4
- package/lib/emulator/downloadableEmulatorInfo.json +24 -24
- package/lib/experiments.js +8 -3
- package/lib/firebase_studio/migrate.js +8 -0
- package/lib/gcp/location.js +16 -1
- package/lib/gemini/fdcExperience.js +171 -26
- package/lib/init/features/dataconnect/create_app.js +3 -4
- package/lib/init/features/dataconnect/index.js +49 -15
- package/lib/init/features/dataconnect/sdk.js +2 -2
- package/lib/mcp/tools/apptesting/tests.js +3 -1
- package/lib/tsconfig.compile.tsbuildinfo +1 -1
- package/lib/tsconfig.publish.tsbuildinfo +1 -1
- package/lib/utils.js +48 -0
- package/package.json +1 -1
- package/lib/dataconnect/cloudAICompanionTypes.js +0 -2
|
@@ -9,7 +9,6 @@ const provisionCloudSql_1 = require("../../dataconnect/provisionCloudSql");
|
|
|
9
9
|
const names_1 = require("../../dataconnect/names");
|
|
10
10
|
const api_1 = require("../../api");
|
|
11
11
|
const ensureApiEnabled = require("../../ensureApiEnabled");
|
|
12
|
-
const prompt_1 = require("../../prompt");
|
|
13
12
|
async function default_1(context, options) {
|
|
14
13
|
const dataconnect = context.dataconnect;
|
|
15
14
|
if (!dataconnect) {
|
|
@@ -31,29 +30,11 @@ async function default_1(context, options) {
|
|
|
31
30
|
filters?.some((f) => si.dataConnectYaml.serviceId === f.serviceId));
|
|
32
31
|
});
|
|
33
32
|
dataconnect.deployStats.numServiceCreated = servicesToCreate.length;
|
|
34
|
-
const servicesToDelete = filters
|
|
35
|
-
? []
|
|
36
|
-
: services.filter((s) => !serviceInfos.some((si) => matches(si, s)));
|
|
37
|
-
dataconnect.deployStats.numServiceDeleted = servicesToDelete.length;
|
|
38
33
|
await Promise.all(servicesToCreate.map(async (s) => {
|
|
39
34
|
const { projectId, locationId, serviceId } = splitName(s.serviceName);
|
|
40
35
|
await client.createService(projectId, locationId, serviceId);
|
|
41
36
|
utils.logLabeledSuccess("dataconnect", `Created service ${s.serviceName}`);
|
|
42
37
|
}));
|
|
43
|
-
if (servicesToDelete.length) {
|
|
44
|
-
const serviceToDeleteList = servicesToDelete.map((s) => " - " + s.name).join("\n");
|
|
45
|
-
if (await (0, prompt_1.confirm)({
|
|
46
|
-
force: false,
|
|
47
|
-
nonInteractive: options.nonInteractive,
|
|
48
|
-
message: `The following services exist on ${projectId} but are not listed in your 'firebase.json'\n${serviceToDeleteList}\nWould you like to delete these services?`,
|
|
49
|
-
default: false,
|
|
50
|
-
})) {
|
|
51
|
-
await Promise.all(servicesToDelete.map(async (s) => {
|
|
52
|
-
await client.deleteService(s.name);
|
|
53
|
-
utils.logLabeledSuccess("dataconnect", `Deleted service ${s.name}`);
|
|
54
|
-
}));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
38
|
utils.logLabeledBullet("dataconnect", "Checking for CloudSQL resources...");
|
|
58
39
|
await Promise.all(serviceInfos
|
|
59
40
|
.filter((si) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.EVENTARC_SOURCE_ENV = void 0;
|
|
4
4
|
exports.prepare = prepare;
|
|
5
5
|
exports.resolveDefaultRegionsForBuild = resolveDefaultRegionsForBuild;
|
|
6
6
|
exports.inferDetailsFromExisting = inferDetailsFromExisting;
|
|
@@ -23,12 +23,7 @@ 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
|
|
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
|
+
const services_1 = require("./services");
|
|
32
27
|
const api_1 = require("../../api");
|
|
33
28
|
const functionsDeployHelper_1 = require("./functionsDeployHelper");
|
|
34
29
|
const utils_1 = require("../../utils");
|
|
@@ -49,7 +44,6 @@ const functional_1 = require("../../functional");
|
|
|
49
44
|
const prepare_1 = require("../extensions/prepare");
|
|
50
45
|
const prompt = require("../../prompt");
|
|
51
46
|
exports.EVENTARC_SOURCE_ENV = "EVENTARC_CLOUD_EVENT_SOURCE";
|
|
52
|
-
exports.DEFAULT_FUNCTION_REGION = "us-central1";
|
|
53
47
|
async function prepare(context, options, payload) {
|
|
54
48
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
55
49
|
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
@@ -234,7 +228,7 @@ async function prepare(context, options, payload) {
|
|
|
234
228
|
async function resolveDefaultRegionsForBuild(buildObj, have) {
|
|
235
229
|
for (const [id, endpoint] of Object.entries(buildObj.endpoints)) {
|
|
236
230
|
if (!endpoint.region?.length || endpoint.region.includes(build.REGION_TBD)) {
|
|
237
|
-
let resolvedRegion =
|
|
231
|
+
let resolvedRegion = services_1.FALLBACK_DEPLOYMENT_REGION;
|
|
238
232
|
let matching;
|
|
239
233
|
for (const region of Object.keys(have.endpoints)) {
|
|
240
234
|
if (have.endpoints[region][id]) {
|
|
@@ -249,109 +243,19 @@ async function resolveDefaultRegionsForBuild(buildObj, have) {
|
|
|
249
243
|
}
|
|
250
244
|
else {
|
|
251
245
|
try {
|
|
252
|
-
|
|
253
|
-
resolvedRegion = resolveRegionForBlockingTrigger(endpoint.blockingTrigger);
|
|
254
|
-
}
|
|
255
|
-
else if (build.isEventTriggered(endpoint)) {
|
|
256
|
-
resolvedRegion = await resolveRegionForEventTrigger(endpoint.project, endpoint.eventTrigger);
|
|
257
|
-
}
|
|
246
|
+
resolvedRegion = await resolveRegionForTrigger(endpoint);
|
|
258
247
|
}
|
|
259
248
|
catch (err) {
|
|
260
|
-
logger_1.logger.debug(`Failed to resolve region for endpoint ${id}. Defaulting to ${
|
|
249
|
+
logger_1.logger.debug(`Failed to resolve region for endpoint ${id}. Defaulting to ${services_1.FALLBACK_DEPLOYMENT_REGION}.`, (0, error_1.getErrStack)(err));
|
|
261
250
|
}
|
|
262
251
|
}
|
|
263
252
|
endpoint.region = [resolvedRegion];
|
|
264
253
|
}
|
|
265
254
|
}
|
|
266
255
|
}
|
|
267
|
-
function
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
return "us-east1";
|
|
271
|
-
}
|
|
272
|
-
if ((0, ailogic_1.isGlobalAILogicTrigger)(blockingTrigger)) {
|
|
273
|
-
return "us-east1";
|
|
274
|
-
}
|
|
275
|
-
return exports.DEFAULT_FUNCTION_REGION;
|
|
276
|
-
}
|
|
277
|
-
async function resolveRegionForEventTrigger(project, eventTrigger) {
|
|
278
|
-
const eventType = eventTrigger.eventType;
|
|
279
|
-
if (eventType.startsWith("google.cloud.pubsub.") ||
|
|
280
|
-
eventType.startsWith("providers/cloud.auth/eventTypes/") ||
|
|
281
|
-
eventType.startsWith("providers/firebase.auth/eventTypes/") ||
|
|
282
|
-
eventType.startsWith("google.firebase.testlab.") ||
|
|
283
|
-
eventType.startsWith("google.firebase.remoteconfig.") ||
|
|
284
|
-
eventType.startsWith("google.firebase.firebasealerts.")) {
|
|
285
|
-
return "us-east1";
|
|
286
|
-
}
|
|
287
|
-
if (eventType.startsWith("google.cloud.firestore.")) {
|
|
288
|
-
try {
|
|
289
|
-
const databaseId = eventTrigger.eventFilters?.database || "(default)";
|
|
290
|
-
const db = await (0, firestore_1.getDatabase)(project, databaseId);
|
|
291
|
-
const locationId = db.locationId.toLowerCase();
|
|
292
|
-
if (locationId === "nam5" || locationId === "nam7")
|
|
293
|
-
return "us-central1";
|
|
294
|
-
if (locationId === "eur3")
|
|
295
|
-
return "europe-west1";
|
|
296
|
-
return locationId;
|
|
297
|
-
}
|
|
298
|
-
catch (err) {
|
|
299
|
-
logger_1.logger.debug("Failed to resolve Firestore database location", (0, error_1.getErrStack)(err));
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (eventType.startsWith("google.cloud.storage.")) {
|
|
303
|
-
try {
|
|
304
|
-
const bucketName = eventTrigger.eventFilters?.bucket;
|
|
305
|
-
if (bucketName) {
|
|
306
|
-
const bucket = await (0, storage_1.getBucket)(bucketName);
|
|
307
|
-
const locationId = bucket.location.toLowerCase();
|
|
308
|
-
if (locationId === "us")
|
|
309
|
-
return "us-east1";
|
|
310
|
-
if (locationId === "eu")
|
|
311
|
-
return "europe-west1";
|
|
312
|
-
if (locationId === "asia")
|
|
313
|
-
return "asia-east1";
|
|
314
|
-
return locationId;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
catch (err) {
|
|
318
|
-
logger_1.logger.debug("Failed to resolve Cloud Storage bucket location", (0, error_1.getErrStack)(err));
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (eventType.startsWith("google.firebase.database.")) {
|
|
322
|
-
if (eventTrigger.region)
|
|
323
|
-
return eventTrigger.region;
|
|
324
|
-
try {
|
|
325
|
-
const instanceName = eventTrigger.eventFilters?.instance;
|
|
326
|
-
if (instanceName) {
|
|
327
|
-
const details = await (0, database_1.getDatabaseInstanceDetails)(project, instanceName);
|
|
328
|
-
if (details.location && details.location !== "-") {
|
|
329
|
-
return details.location.toLowerCase();
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
catch (err) {
|
|
334
|
-
logger_1.logger.debug("Failed to resolve Realtime Database instance location", (0, error_1.getErrStack)(err));
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
if (eventType.startsWith("google.firebase.dataconnect.")) {
|
|
338
|
-
if (eventTrigger.region)
|
|
339
|
-
return eventTrigger.region;
|
|
340
|
-
try {
|
|
341
|
-
const service = eventTrigger.eventFilters?.service;
|
|
342
|
-
if (service) {
|
|
343
|
-
return (0, names_1.parseServiceName)(service).location;
|
|
344
|
-
}
|
|
345
|
-
const connector = eventTrigger.eventFilters?.connector;
|
|
346
|
-
if (connector) {
|
|
347
|
-
return (0, names_1.parseConnectorName)(connector).location;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
catch (err) {
|
|
351
|
-
logger_1.logger.debug("Failed to resolve DataConnect location", (0, error_1.getErrStack)(err));
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return exports.DEFAULT_FUNCTION_REGION;
|
|
256
|
+
async function resolveRegionForTrigger(endpoint) {
|
|
257
|
+
const service = (0, services_1.serviceForEndpoint)(endpoint);
|
|
258
|
+
return await service.getDefaultRegion(endpoint);
|
|
355
259
|
}
|
|
356
260
|
function inferDetailsFromExisting(want, have, usedDotenv) {
|
|
357
261
|
for (const wantE of backend.allEndpoints(want)) {
|
|
@@ -4,6 +4,7 @@ exports.AILogicService = exports.AI_LOGIC_EVENTS = exports.AI_LOGIC_AFTER_GENERA
|
|
|
4
4
|
exports.isAILogicEvent = isAILogicEvent;
|
|
5
5
|
exports.isGlobalAILogicTrigger = isGlobalAILogicTrigger;
|
|
6
6
|
const backend = require("../backend");
|
|
7
|
+
const build = require("../build");
|
|
7
8
|
const error_1 = require("../../../error");
|
|
8
9
|
const ailogicApi = require("../../../gcp/ailogic");
|
|
9
10
|
const ailogic_1 = require("../../../gcp/ailogic");
|
|
@@ -27,19 +28,19 @@ function isGlobalAILogicTrigger(blockingTrigger) {
|
|
|
27
28
|
class AILogicService {
|
|
28
29
|
constructor() {
|
|
29
30
|
this.ensureTriggerRegion = () => Promise.resolve();
|
|
31
|
+
this.requiredProjectBindings = async (projectNumber) => {
|
|
32
|
+
return [
|
|
33
|
+
{
|
|
34
|
+
role: "roles/run.invoker",
|
|
35
|
+
members: [
|
|
36
|
+
`serviceAccount:service-${projectNumber}@gcp-sa-firebasevertexai.iam.gserviceaccount.com`,
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
};
|
|
30
41
|
this.name = "ailogic";
|
|
31
42
|
this.api = "firebasevertexai.googleapis.com";
|
|
32
43
|
}
|
|
33
|
-
async requiredProjectBindings(projectNumber) {
|
|
34
|
-
return [
|
|
35
|
-
{
|
|
36
|
-
role: "roles/run.invoker",
|
|
37
|
-
members: [
|
|
38
|
-
`serviceAccount:service-${projectNumber}@gcp-sa-firebasevertexai.iam.gserviceaccount.com`,
|
|
39
|
-
],
|
|
40
|
-
},
|
|
41
|
-
];
|
|
42
|
-
}
|
|
43
44
|
validateTrigger(endpoint, wantBackend) {
|
|
44
45
|
if (!isAILogicEvent(endpoint)) {
|
|
45
46
|
return;
|
|
@@ -91,5 +92,11 @@ class AILogicService {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
}
|
|
95
|
+
async getDefaultRegion(endpoint) {
|
|
96
|
+
if (build.isBlockingTriggered(endpoint) && isGlobalAILogicTrigger(endpoint.blockingTrigger)) {
|
|
97
|
+
return "us-east1";
|
|
98
|
+
}
|
|
99
|
+
return "us-central1";
|
|
100
|
+
}
|
|
94
101
|
}
|
|
95
102
|
exports.AILogicService = AILogicService;
|
|
@@ -129,5 +129,8 @@ class AuthBlockingService {
|
|
|
129
129
|
this.triggerQueue = this.triggerQueue.then(() => this.unregisterTriggerLocked(ep));
|
|
130
130
|
return this.triggerQueue;
|
|
131
131
|
}
|
|
132
|
+
async getDefaultRegion() {
|
|
133
|
+
return "us-east1";
|
|
134
|
+
}
|
|
132
135
|
}
|
|
133
136
|
exports.AuthBlockingService = AuthBlockingService;
|
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.clearCache = clearCache;
|
|
4
4
|
exports.getDatabaseInstanceDetails = getDatabaseInstanceDetails;
|
|
5
5
|
exports.ensureDatabaseTriggerRegion = ensureDatabaseTriggerRegion;
|
|
6
|
+
exports.getDefaultRegion = getDefaultRegion;
|
|
6
7
|
const error_1 = require("../../../error");
|
|
8
|
+
const build = require("../build");
|
|
7
9
|
const database_1 = require("../../../management/database");
|
|
8
10
|
const instanceCache = new Map();
|
|
9
11
|
function clearCache() {
|
|
@@ -27,3 +29,19 @@ function ensureDatabaseTriggerRegion(endpoint) {
|
|
|
27
29
|
}
|
|
28
30
|
return Promise.resolve();
|
|
29
31
|
}
|
|
32
|
+
async function getDefaultRegion(endpoint) {
|
|
33
|
+
if (!build.isEventTriggered(endpoint)) {
|
|
34
|
+
throw new error_1.FirebaseError("Database getDefaultRegion requires an event-triggered endpoint");
|
|
35
|
+
}
|
|
36
|
+
if (endpoint.eventTrigger.region) {
|
|
37
|
+
return endpoint.eventTrigger.region;
|
|
38
|
+
}
|
|
39
|
+
const instanceName = endpoint.eventTrigger.eventFilters?.instance;
|
|
40
|
+
if (instanceName) {
|
|
41
|
+
const details = await getDatabaseInstanceDetails(endpoint.project, instanceName);
|
|
42
|
+
if (details.location && details.location !== "-") {
|
|
43
|
+
return details.location.toLowerCase();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
throw new error_1.FirebaseError("Could not resolve database instance location");
|
|
47
|
+
}
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ensureDataConnectTriggerRegion = ensureDataConnectTriggerRegion;
|
|
4
4
|
exports.getDataConnectP4SA = getDataConnectP4SA;
|
|
5
|
+
exports.getDefaultRegion = getDefaultRegion;
|
|
5
6
|
const api_1 = require("../../../api");
|
|
6
7
|
const error_1 = require("../../../error");
|
|
8
|
+
const build = require("../build");
|
|
9
|
+
const names_1 = require("../../../dataconnect/names");
|
|
7
10
|
const AUTOPUSH_DATACONNECT_SA_DOMAIN = "gcp-sa-autopush-dataconnect.iam.gserviceaccount.com";
|
|
8
11
|
const STAGING_DATACONNECT_SA_DOMAIN = "gcp-sa-staging-dataconnect.iam.gserviceaccount.com";
|
|
9
12
|
const PROD_DATACONNECT_SA_DOMAIN = "gcp-sa-firebasedataconnect.iam.gserviceaccount.com";
|
|
@@ -26,3 +29,20 @@ function getDataConnectP4SA(projectNumber) {
|
|
|
26
29
|
}
|
|
27
30
|
return `service-${projectNumber}@${PROD_DATACONNECT_SA_DOMAIN}`;
|
|
28
31
|
}
|
|
32
|
+
async function getDefaultRegion(endpoint) {
|
|
33
|
+
if (!build.isEventTriggered(endpoint)) {
|
|
34
|
+
throw new error_1.FirebaseError("DataConnect getDefaultRegion requires an event-triggered endpoint");
|
|
35
|
+
}
|
|
36
|
+
if (endpoint.eventTrigger.region) {
|
|
37
|
+
return endpoint.eventTrigger.region;
|
|
38
|
+
}
|
|
39
|
+
const service = endpoint.eventTrigger.eventFilters?.service;
|
|
40
|
+
if (service) {
|
|
41
|
+
return (0, names_1.parseServiceName)(service).location;
|
|
42
|
+
}
|
|
43
|
+
const connector = endpoint.eventTrigger.eventFilters?.connector;
|
|
44
|
+
if (connector) {
|
|
45
|
+
return (0, names_1.parseConnectorName)(connector).location;
|
|
46
|
+
}
|
|
47
|
+
throw new error_1.FirebaseError("Could not resolve DataConnect location");
|
|
48
|
+
}
|
|
@@ -3,8 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.clearCache = clearCache;
|
|
4
4
|
exports.getDatabase = getDatabase;
|
|
5
5
|
exports.ensureFirestoreTriggerRegion = ensureFirestoreTriggerRegion;
|
|
6
|
+
exports.getDefaultRegion = getDefaultRegion;
|
|
6
7
|
const firestore = require("../../../gcp/firestore");
|
|
7
8
|
const error_1 = require("../../../error");
|
|
9
|
+
const build = require("../build");
|
|
10
|
+
const location_1 = require("../../../gcp/location");
|
|
8
11
|
const dbCache = new Map();
|
|
9
12
|
const dbPromiseCache = new Map();
|
|
10
13
|
function clearCache() {
|
|
@@ -43,3 +46,12 @@ async function ensureFirestoreTriggerRegion(endpoint) {
|
|
|
43
46
|
throw new error_1.FirebaseError("A firestore trigger location must match the firestore database region.");
|
|
44
47
|
}
|
|
45
48
|
}
|
|
49
|
+
async function getDefaultRegion(endpoint) {
|
|
50
|
+
if (!build.isEventTriggered(endpoint)) {
|
|
51
|
+
throw new error_1.FirebaseError("Firestore getDefaultRegion requires an event-triggered endpoint");
|
|
52
|
+
}
|
|
53
|
+
const databaseId = endpoint.eventTrigger.eventFilters?.database || "(default)";
|
|
54
|
+
const db = await getDatabase(endpoint.project, databaseId);
|
|
55
|
+
const locationId = db.locationId.toLowerCase();
|
|
56
|
+
return location_1.FIRESTORE_DUAL_REGION_TO_REGION_MAPPING[locationId] || locationId;
|
|
57
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noopProjectBindings = exports.noop = void 0;
|
|
3
|
+
exports.FALLBACK_DEPLOYMENT_REGION = exports.DEFAULT_GLOBAL_TRIGGER_REGION = exports.noopProjectBindings = exports.noop = void 0;
|
|
4
4
|
exports.serviceForEndpoint = serviceForEndpoint;
|
|
5
|
-
const backend = require("../backend");
|
|
6
5
|
const auth_1 = require("./auth");
|
|
7
6
|
const storage_1 = require("./storage");
|
|
8
7
|
const firebaseAlerts_1 = require("./firebaseAlerts");
|
|
@@ -16,6 +15,8 @@ const noop = () => Promise.resolve();
|
|
|
16
15
|
exports.noop = noop;
|
|
17
16
|
const noopProjectBindings = () => Promise.resolve([]);
|
|
18
17
|
exports.noopProjectBindings = noopProjectBindings;
|
|
18
|
+
exports.DEFAULT_GLOBAL_TRIGGER_REGION = "us-east1";
|
|
19
|
+
exports.FALLBACK_DEPLOYMENT_REGION = "us-central1";
|
|
19
20
|
const noOpService = {
|
|
20
21
|
name: "noop",
|
|
21
22
|
api: "",
|
|
@@ -23,6 +24,7 @@ const noOpService = {
|
|
|
23
24
|
validateTrigger: exports.noop,
|
|
24
25
|
registerTrigger: exports.noop,
|
|
25
26
|
unregisterTrigger: exports.noop,
|
|
27
|
+
getDefaultRegion: () => Promise.resolve(exports.FALLBACK_DEPLOYMENT_REGION),
|
|
26
28
|
};
|
|
27
29
|
const pubSubService = {
|
|
28
30
|
name: "pubsub",
|
|
@@ -32,6 +34,7 @@ const pubSubService = {
|
|
|
32
34
|
validateTrigger: exports.noop,
|
|
33
35
|
registerTrigger: exports.noop,
|
|
34
36
|
unregisterTrigger: exports.noop,
|
|
37
|
+
getDefaultRegion: () => Promise.resolve(exports.DEFAULT_GLOBAL_TRIGGER_REGION),
|
|
35
38
|
};
|
|
36
39
|
const storageService = {
|
|
37
40
|
name: "storage",
|
|
@@ -41,6 +44,7 @@ const storageService = {
|
|
|
41
44
|
validateTrigger: exports.noop,
|
|
42
45
|
registerTrigger: exports.noop,
|
|
43
46
|
unregisterTrigger: exports.noop,
|
|
47
|
+
getDefaultRegion: storage_1.getDefaultRegion,
|
|
44
48
|
};
|
|
45
49
|
const firebaseAlertsService = {
|
|
46
50
|
name: "firebasealerts",
|
|
@@ -50,6 +54,7 @@ const firebaseAlertsService = {
|
|
|
50
54
|
validateTrigger: exports.noop,
|
|
51
55
|
registerTrigger: exports.noop,
|
|
52
56
|
unregisterTrigger: exports.noop,
|
|
57
|
+
getDefaultRegion: () => Promise.resolve(exports.DEFAULT_GLOBAL_TRIGGER_REGION),
|
|
53
58
|
};
|
|
54
59
|
const authBlockingService = new auth_1.AuthBlockingService();
|
|
55
60
|
const aiLogicService = new ailogic_1.AILogicService();
|
|
@@ -61,6 +66,7 @@ const databaseService = {
|
|
|
61
66
|
validateTrigger: exports.noop,
|
|
62
67
|
registerTrigger: exports.noop,
|
|
63
68
|
unregisterTrigger: exports.noop,
|
|
69
|
+
getDefaultRegion: database_1.getDefaultRegion,
|
|
64
70
|
};
|
|
65
71
|
const remoteConfigService = {
|
|
66
72
|
name: "remoteconfig",
|
|
@@ -70,6 +76,7 @@ const remoteConfigService = {
|
|
|
70
76
|
validateTrigger: exports.noop,
|
|
71
77
|
registerTrigger: exports.noop,
|
|
72
78
|
unregisterTrigger: exports.noop,
|
|
79
|
+
getDefaultRegion: () => Promise.resolve(exports.DEFAULT_GLOBAL_TRIGGER_REGION),
|
|
73
80
|
};
|
|
74
81
|
const testLabService = {
|
|
75
82
|
name: "testlab",
|
|
@@ -79,6 +86,7 @@ const testLabService = {
|
|
|
79
86
|
validateTrigger: exports.noop,
|
|
80
87
|
registerTrigger: exports.noop,
|
|
81
88
|
unregisterTrigger: exports.noop,
|
|
89
|
+
getDefaultRegion: () => Promise.resolve(exports.DEFAULT_GLOBAL_TRIGGER_REGION),
|
|
82
90
|
};
|
|
83
91
|
const firestoreService = {
|
|
84
92
|
name: "firestore",
|
|
@@ -88,6 +96,7 @@ const firestoreService = {
|
|
|
88
96
|
validateTrigger: exports.noop,
|
|
89
97
|
registerTrigger: exports.noop,
|
|
90
98
|
unregisterTrigger: exports.noop,
|
|
99
|
+
getDefaultRegion: firestore_1.getDefaultRegion,
|
|
91
100
|
};
|
|
92
101
|
const dataconnectService = {
|
|
93
102
|
name: "dataconnect",
|
|
@@ -97,6 +106,7 @@ const dataconnectService = {
|
|
|
97
106
|
validateTrigger: exports.noop,
|
|
98
107
|
registerTrigger: exports.noop,
|
|
99
108
|
unregisterTrigger: exports.noop,
|
|
109
|
+
getDefaultRegion: dataconnect_1.getDefaultRegion,
|
|
100
110
|
};
|
|
101
111
|
const EVENT_SERVICE_MAPPING = {
|
|
102
112
|
"google.cloud.pubsub.topic.v1.messagePublished": pubSubService,
|
|
@@ -128,11 +138,12 @@ const EVENT_SERVICE_MAPPING = {
|
|
|
128
138
|
"google.firebase.ailogic.v1.afterGenerate": aiLogicService,
|
|
129
139
|
};
|
|
130
140
|
function serviceForEndpoint(endpoint) {
|
|
131
|
-
|
|
132
|
-
|
|
141
|
+
let eventType;
|
|
142
|
+
if ("eventTrigger" in endpoint && endpoint.eventTrigger?.eventType) {
|
|
143
|
+
eventType = endpoint.eventTrigger.eventType;
|
|
133
144
|
}
|
|
134
|
-
if (
|
|
135
|
-
|
|
145
|
+
else if ("blockingTrigger" in endpoint && endpoint.blockingTrigger?.eventType) {
|
|
146
|
+
eventType = endpoint.blockingTrigger.eventType;
|
|
136
147
|
}
|
|
137
|
-
return noOpService;
|
|
148
|
+
return eventType ? EVENT_SERVICE_MAPPING[eventType] || noOpService : noOpService;
|
|
138
149
|
}
|
|
@@ -4,10 +4,12 @@ exports.clearCache = clearCache;
|
|
|
4
4
|
exports.getBucket = getBucket;
|
|
5
5
|
exports.obtainStorageBindings = obtainStorageBindings;
|
|
6
6
|
exports.ensureStorageTriggerRegion = ensureStorageTriggerRegion;
|
|
7
|
+
exports.getDefaultRegion = getDefaultRegion;
|
|
7
8
|
const storage = require("../../../gcp/storage");
|
|
8
9
|
const logger_1 = require("../../../logger");
|
|
9
10
|
const error_1 = require("../../../error");
|
|
10
11
|
const location_1 = require("../../../gcp/location");
|
|
12
|
+
const build = require("../build");
|
|
11
13
|
const PUBSUB_PUBLISHER_ROLE = "roles/pubsub.publisher";
|
|
12
14
|
const bucketCache = new Map();
|
|
13
15
|
function clearCache() {
|
|
@@ -54,3 +56,15 @@ async function ensureStorageTriggerRegion(endpoint) {
|
|
|
54
56
|
throw new error_1.FirebaseError(`A function in region ${endpoint.region} cannot listen to a bucket in region ${eventTrigger.region}`);
|
|
55
57
|
}
|
|
56
58
|
}
|
|
59
|
+
async function getDefaultRegion(endpoint) {
|
|
60
|
+
if (!build.isEventTriggered(endpoint)) {
|
|
61
|
+
throw new error_1.FirebaseError("Storage getDefaultRegion requires an event-triggered endpoint");
|
|
62
|
+
}
|
|
63
|
+
const bucketName = endpoint.eventTrigger.eventFilters?.bucket;
|
|
64
|
+
if (!bucketName) {
|
|
65
|
+
throw new error_1.FirebaseError("Could not find bucket name in event trigger filters");
|
|
66
|
+
}
|
|
67
|
+
const bucket = await getBucket(bucketName);
|
|
68
|
+
const locationId = bucket.location.toLowerCase();
|
|
69
|
+
return location_1.STORAGE_MULTI_REGION_TO_REGION_MAPPING[locationId] || locationId;
|
|
70
|
+
}
|
|
@@ -8,6 +8,7 @@ const utils = require("../../utils");
|
|
|
8
8
|
const storage_1 = require("./services/storage");
|
|
9
9
|
const firestore_1 = require("./services/firestore");
|
|
10
10
|
const database_1 = require("./services/database");
|
|
11
|
+
const location_1 = require("../../gcp/location");
|
|
11
12
|
async function ensureTriggerRegions(want) {
|
|
12
13
|
const regionLookups = [];
|
|
13
14
|
const triggerRegionMap = new Map();
|
|
@@ -78,7 +79,7 @@ async function ensureTriggerRegions(want) {
|
|
|
78
79
|
if (ep.region !== "us-central1" || !triggerRegion || triggerRegion === "global") {
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
|
-
if (!isUSRegion(triggerRegion)) {
|
|
82
|
+
if (!(0, location_1.isUSRegion)(triggerRegion)) {
|
|
82
83
|
offendingFunctions.push(`- ${ep.id} (us-central1, Trigger: ${triggerRegion})`);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
@@ -119,6 +120,3 @@ function extractInstanceName(resource) {
|
|
|
119
120
|
return resource;
|
|
120
121
|
return null;
|
|
121
122
|
}
|
|
122
|
-
function isUSRegion(region) {
|
|
123
|
-
return region === "us" || region.startsWith("nam") || region.startsWith("us-");
|
|
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.10",
|
|
58
|
+
"expectedSize": 32439760,
|
|
59
|
+
"expectedChecksum": "c3609b01993c1f66d340eda59fa31467",
|
|
60
|
+
"expectedChecksumSHA256": "257419922a81b4856b1ce7c1ac0684b4690a380072fe132d1960070f7d00941a",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v3.4.10",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.10"
|
|
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.10",
|
|
66
|
+
"expectedSize": 30571186,
|
|
67
|
+
"expectedChecksum": "fd17de6fb356a5e0c1c67ae7f4adfa08",
|
|
68
|
+
"expectedChecksumSHA256": "9c6da9eea98ee85b16b6e0c5075d1c757aaa252526470fbe63cfb915e3585361",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-arm64-v3.4.10",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.10"
|
|
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.10",
|
|
74
|
+
"expectedSize": 32479744,
|
|
75
|
+
"expectedChecksum": "3ed78e5d74da96e89f385a64fa6151fc",
|
|
76
|
+
"expectedChecksumSHA256": "bc4bb887b5adbba5b916fa41a091f544aac70f35d5209c483fac90f89abb2c93",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v3.4.10",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.10.exe"
|
|
79
79
|
},
|
|
80
80
|
"linux": {
|
|
81
|
-
"version": "3.4.
|
|
82
|
-
"expectedSize":
|
|
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.
|
|
81
|
+
"version": "3.4.10",
|
|
82
|
+
"expectedSize": 31592632,
|
|
83
|
+
"expectedChecksum": "a003103017717ddc4ea9967d5820ea51",
|
|
84
|
+
"expectedChecksumSHA256": "50b22fdaf7bf1c30b5f331ef07a7f19c51309ca9c9897be426e86254e02b514c",
|
|
85
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v3.4.10",
|
|
86
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-3.4.10"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
package/lib/experiments.js
CHANGED
|
@@ -127,9 +127,9 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
127
127
|
public: false,
|
|
128
128
|
},
|
|
129
129
|
abiu: {
|
|
130
|
-
shortDescription: "Enable
|
|
131
|
-
default:
|
|
132
|
-
public:
|
|
130
|
+
shortDescription: "Enable Automatic Base Image Updates (ABIU) and runtime selection for App Hosting",
|
|
131
|
+
default: true,
|
|
132
|
+
public: true,
|
|
133
133
|
},
|
|
134
134
|
dataconnect: {
|
|
135
135
|
shortDescription: "Deprecated. Previosuly, enabled SQL Connect related features.",
|
|
@@ -182,6 +182,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
182
182
|
default: true,
|
|
183
183
|
public: false,
|
|
184
184
|
},
|
|
185
|
+
crashlyticsWeb: {
|
|
186
|
+
shortDescription: "Enable the ability to upload source maps for web apps to Crashlytics.",
|
|
187
|
+
default: false,
|
|
188
|
+
public: true,
|
|
189
|
+
},
|
|
185
190
|
});
|
|
186
191
|
function isValidExperiment(name) {
|
|
187
192
|
return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
|
|
@@ -242,6 +242,14 @@ async function injectAntigravityContext(rootPath, projectId, appName, nonInterac
|
|
|
242
242
|
global: installLocation === "global",
|
|
243
243
|
background: false,
|
|
244
244
|
agentName: "gemini-cli",
|
|
245
|
+
skillPackage: "firebase/agent-skills",
|
|
246
|
+
});
|
|
247
|
+
await (0, agentSkills_1.installAgentSkills)({
|
|
248
|
+
cwd: rootPath,
|
|
249
|
+
global: installLocation === "global",
|
|
250
|
+
background: false,
|
|
251
|
+
agentName: "gemini-cli",
|
|
252
|
+
skillPackage: "genkit-ai/skills",
|
|
245
253
|
});
|
|
246
254
|
const systemInstructionsTemplate = await (0, templates_1.readTemplate)("firebase-studio-export/system_instructions_template.md");
|
|
247
255
|
const systemInstructions = systemInstructionsTemplate.replace("${appName}", appName);
|
package/lib/gcp/location.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DUAL_REGION_MAPPING = exports.MULTI_REGION_MAPPING = void 0;
|
|
3
|
+
exports.STORAGE_MULTI_REGION_TO_REGION_MAPPING = exports.FIRESTORE_DUAL_REGION_TO_REGION_MAPPING = exports.DUAL_REGION_MAPPING = exports.MULTI_REGION_MAPPING = void 0;
|
|
4
4
|
exports.regionInLocation = regionInLocation;
|
|
5
|
+
exports.isUSRegion = isUSRegion;
|
|
5
6
|
exports.MULTI_REGION_MAPPING = {
|
|
6
7
|
"us-central1": "us",
|
|
7
8
|
"us-east1": "us",
|
|
@@ -34,6 +35,16 @@ exports.DUAL_REGION_MAPPING = {
|
|
|
34
35
|
"us-central1": "nam4",
|
|
35
36
|
"us-east1": "nam4",
|
|
36
37
|
};
|
|
38
|
+
exports.FIRESTORE_DUAL_REGION_TO_REGION_MAPPING = {
|
|
39
|
+
nam5: "us-central1",
|
|
40
|
+
nam7: "us-central1",
|
|
41
|
+
eur3: "europe-west1",
|
|
42
|
+
};
|
|
43
|
+
exports.STORAGE_MULTI_REGION_TO_REGION_MAPPING = {
|
|
44
|
+
us: "us-east1",
|
|
45
|
+
eu: "europe-west1",
|
|
46
|
+
asia: "asia-east1",
|
|
47
|
+
};
|
|
37
48
|
function regionInLocation(region, location) {
|
|
38
49
|
region = region.toLowerCase();
|
|
39
50
|
location = location.toLowerCase();
|
|
@@ -42,3 +53,7 @@ function regionInLocation(region, location) {
|
|
|
42
53
|
}
|
|
43
54
|
return false;
|
|
44
55
|
}
|
|
56
|
+
function isUSRegion(region) {
|
|
57
|
+
const lower = region.toLowerCase();
|
|
58
|
+
return lower === "us" || lower.startsWith("nam") || lower.startsWith("us-");
|
|
59
|
+
}
|