firebase-tools 14.19.0 → 14.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/appUtils.js +4 -4
- package/lib/apphosting/localbuilds.js +23 -0
- package/lib/command.js +1 -0
- package/lib/commands/apps-init.js +7 -7
- package/lib/commands/dataconnect-execute.js +229 -0
- package/lib/commands/firestore-backups-delete.js +1 -6
- package/lib/commands/firestore-backups-get.js +2 -8
- package/lib/commands/firestore-backups-list.js +4 -10
- package/lib/commands/firestore-backups-schedules-create.js +1 -6
- package/lib/commands/firestore-backups-schedules-delete.js +1 -6
- package/lib/commands/firestore-backups-schedules-list.js +1 -7
- package/lib/commands/firestore-backups-schedules-update.js +1 -6
- package/lib/commands/firestore-bulkdelete.js +7 -13
- package/lib/commands/firestore-databases-create.js +5 -10
- package/lib/commands/firestore-databases-delete.js +1 -6
- package/lib/commands/firestore-databases-get.js +1 -7
- package/lib/commands/firestore-databases-list.js +1 -7
- package/lib/commands/firestore-databases-restore.js +5 -10
- package/lib/commands/firestore-databases-update.js +1 -6
- package/lib/commands/firestore-locations.js +1 -7
- package/lib/commands/firestore-operations-cancel.js +3 -9
- package/lib/commands/firestore-operations-describe.js +2 -8
- package/lib/commands/firestore-operations-list.js +2 -8
- package/lib/commands/index.js +1 -0
- package/lib/dataconnect/build.js +16 -2
- package/lib/dataconnect/load.js +21 -1
- package/lib/dataconnect/names.js +6 -1
- package/lib/dataconnect/provisionCloudSql.js +38 -11
- package/lib/dataconnect/schemaMigration.js +16 -3
- package/lib/dataconnect/types.js +1 -10
- package/lib/deploy/apphosting/deploy.js +18 -5
- package/lib/deploy/apphosting/prepare.js +23 -1
- package/lib/deploy/apphosting/release.js +5 -0
- package/lib/deploy/apphosting/util.js +4 -3
- package/lib/deploy/dataconnect/context.js +26 -0
- package/lib/deploy/dataconnect/deploy.js +13 -4
- package/lib/deploy/dataconnect/prepare.js +11 -8
- package/lib/deploy/dataconnect/release.js +10 -2
- package/lib/deploy/index.js +39 -20
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/gcp/cloudsql/cloudsqladmin.js +4 -3
- package/lib/init/features/dataconnect/index.js +22 -6
- package/lib/init/features/dataconnect/sdk.js +40 -22
- package/lib/management/apps.js +24 -24
- package/lib/mcp/prompts/core/consult.js +2 -3
- package/lib/mcp/prompts/core/init.js +3 -4
- package/lib/mcp/resources/index.js +0 -4
- package/lib/mcp/tools/dataconnect/execute.js +0 -1
- package/lib/mcp/util/dataconnect/converter.js +5 -4
- package/lib/mcp/util/dataconnect/emulator.js +0 -1
- package/lib/responseToError.js +7 -6
- package/package.json +3 -1
- package/schema/firebase-config.json +6 -0
- package/lib/dataconnect/appFinder.js +0 -103
package/lib/deploy/index.js
CHANGED
|
@@ -26,6 +26,7 @@ const prepare_1 = require("./hosting/prepare");
|
|
|
26
26
|
const github_1 = require("../init/features/hosting/github");
|
|
27
27
|
const deploy_1 = require("../commands/deploy");
|
|
28
28
|
const requirePermissions_1 = require("../requirePermissions");
|
|
29
|
+
const context_1 = require("./dataconnect/context");
|
|
29
30
|
const TARGETS = {
|
|
30
31
|
hosting: HostingTarget,
|
|
31
32
|
database: DatabaseTarget,
|
|
@@ -63,7 +64,7 @@ const isDeployingWebFramework = (options) => {
|
|
|
63
64
|
};
|
|
64
65
|
exports.isDeployingWebFramework = isDeployingWebFramework;
|
|
65
66
|
const deploy = async function (targetNames, options, customContext = {}) {
|
|
66
|
-
var _a, _b, _c;
|
|
67
|
+
var _a, _b, _c, _d;
|
|
67
68
|
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
68
69
|
const payload = {};
|
|
69
70
|
const context = Object.assign({ projectId }, customContext);
|
|
@@ -115,34 +116,52 @@ const deploy = async function (targetNames, options, customContext = {}) {
|
|
|
115
116
|
logger_1.logger.info((0, colorette_1.bold)((0, colorette_1.white)("===") + " Deploying to '" + projectId + "'..."));
|
|
116
117
|
logger_1.logger.info();
|
|
117
118
|
(0, utils_1.logBullet)("deploying " + (0, colorette_1.bold)(targetNames.join(", ")));
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
let result = "predeploys_error";
|
|
120
|
+
try {
|
|
121
|
+
await chain(predeploys, context, options, payload);
|
|
122
|
+
result = "prepares_error";
|
|
123
|
+
await chain(prepares, context, options, payload);
|
|
124
|
+
result = "deploys_error";
|
|
125
|
+
await chain(deploys, context, options, payload);
|
|
126
|
+
result = "releases_error";
|
|
127
|
+
await chain(releases, context, options, payload);
|
|
128
|
+
result = "postdeploys_error";
|
|
129
|
+
await chain(postdeploys, context, options, payload);
|
|
130
|
+
result = "success";
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
const baseParams = {
|
|
134
|
+
interactive: options.nonInteractive ? "false" : "true",
|
|
135
|
+
dry_run: options.dryRun ? "true" : "false",
|
|
136
|
+
result: result,
|
|
137
|
+
};
|
|
138
|
+
const duration = Date.now() - startTime;
|
|
139
|
+
const params = Object.assign({}, baseParams);
|
|
140
|
+
Object.keys(TARGETS).reduce((accum, t) => {
|
|
141
|
+
accum[t] = "false";
|
|
142
|
+
return accum;
|
|
143
|
+
}, params);
|
|
144
|
+
for (const t of targetNames) {
|
|
145
|
+
params[t] = "true";
|
|
146
|
+
}
|
|
147
|
+
void (0, track_1.trackGA4)("product_deploy", params, duration);
|
|
148
|
+
const stats = (_a = context === null || context === void 0 ? void 0 : context.dataconnect) === null || _a === void 0 ? void 0 : _a.deployStats;
|
|
149
|
+
if (stats) {
|
|
150
|
+
const fdcParams = (0, context_1.deployStatsParams)(stats);
|
|
151
|
+
void (0, track_1.trackGA4)("dataconnect_deploy", Object.assign(Object.assign({}, fdcParams), baseParams), duration);
|
|
152
|
+
}
|
|
133
153
|
}
|
|
134
|
-
await (0, track_1.trackGA4)("product_deploy", analyticsParams, duration);
|
|
135
154
|
const successMessage = options.dryRun ? "Dry run complete!" : "Deploy complete!";
|
|
136
155
|
logger_1.logger.info();
|
|
137
156
|
(0, utils_1.logSuccess)((0, colorette_1.bold)((0, colorette_1.underline)(successMessage)));
|
|
138
157
|
logger_1.logger.info();
|
|
139
158
|
const deployedHosting = (0, lodash_1.includes)(targetNames, "hosting");
|
|
140
|
-
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)((
|
|
159
|
+
logger_1.logger.info((0, colorette_1.bold)("Project Console:"), (0, utils_1.consoleUrl)((_b = options.project) !== null && _b !== void 0 ? _b : "_", "/overview"));
|
|
141
160
|
if (deployedHosting) {
|
|
142
|
-
(0, lodash_1.each)((
|
|
161
|
+
(0, lodash_1.each)((_c = context.hosting) === null || _c === void 0 ? void 0 : _c.deploys, (deploy) => {
|
|
143
162
|
logger_1.logger.info((0, colorette_1.bold)("Hosting URL:"), (0, utils_1.addSubdomain)((0, api_1.hostingOrigin)(), deploy.config.site));
|
|
144
163
|
});
|
|
145
|
-
const versionNames = (
|
|
164
|
+
const versionNames = (_d = context.hosting) === null || _d === void 0 ? void 0 : _d.deploys.map((deploy) => deploy.version);
|
|
146
165
|
return { hosting: (versionNames === null || versionNames === void 0 ? void 0 : versionNames.length) === 1 ? versionNames[0] : versionNames };
|
|
147
166
|
}
|
|
148
167
|
else {
|
|
@@ -54,28 +54,28 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dataconnect": {
|
|
56
56
|
"darwin": {
|
|
57
|
-
"version": "2.
|
|
58
|
-
"expectedSize":
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
57
|
+
"version": "2.15.0",
|
|
58
|
+
"expectedSize": 29610848,
|
|
59
|
+
"expectedChecksum": "182ddca17f4974ec081fd5a769f8eeac",
|
|
60
|
+
"expectedChecksumSHA256": "3bcdc39b7f149f8c2d96f397188ca003daf1b43a1ce845780146b79217970a52",
|
|
61
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.15.0",
|
|
62
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0"
|
|
63
63
|
},
|
|
64
64
|
"win32": {
|
|
65
|
-
"version": "2.
|
|
66
|
-
"expectedSize":
|
|
67
|
-
"expectedChecksum": "
|
|
68
|
-
"expectedChecksumSHA256": "
|
|
69
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.
|
|
70
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
65
|
+
"version": "2.15.0",
|
|
66
|
+
"expectedSize": 30099456,
|
|
67
|
+
"expectedChecksum": "5dd4efd368c3987925c41784e21c7af6",
|
|
68
|
+
"expectedChecksumSHA256": "a664f29cd21f826dd4936f8b95f0673e0ea603ab0d85a426d8231f1aaa8947f9",
|
|
69
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.15.0",
|
|
70
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0.exe"
|
|
71
71
|
},
|
|
72
72
|
"linux": {
|
|
73
|
-
"version": "2.
|
|
74
|
-
"expectedSize":
|
|
75
|
-
"expectedChecksum": "
|
|
76
|
-
"expectedChecksumSHA256": "
|
|
77
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.
|
|
78
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.
|
|
73
|
+
"version": "2.15.0",
|
|
74
|
+
"expectedSize": 29532344,
|
|
75
|
+
"expectedChecksum": "38baf423fbfc9c67a89975a27f0a3974",
|
|
76
|
+
"expectedChecksumSHA256": "d3c5b26470639fedce9e3ac667948fa07473e77f6c2d0b361d78c5f2e856a1ec",
|
|
77
|
+
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.15.0",
|
|
78
|
+
"downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.0"
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = exports.iamUserIsCSQLAdmin = void 0;
|
|
3
|
+
exports.listUsers = exports.deleteUser = exports.getUser = exports.createUser = exports.deleteDatabase = exports.createDatabase = exports.getDatabase = exports.listDatabases = exports.updateInstanceForDataConnect = exports.createInstance = exports.DEFAULT_DATABASE_VERSION = exports.instanceConsoleLink = exports.getInstance = exports.listInstances = exports.iamUserIsCSQLAdmin = void 0;
|
|
4
4
|
const apiv2_1 = require("../../apiv2");
|
|
5
5
|
const api_1 = require("../../api");
|
|
6
6
|
const clc = require("colorette");
|
|
@@ -51,6 +51,7 @@ function instanceConsoleLink(projectId, instanceId) {
|
|
|
51
51
|
return `https://console.cloud.google.com/sql/instances/${instanceId}/overview?project=${projectId}`;
|
|
52
52
|
}
|
|
53
53
|
exports.instanceConsoleLink = instanceConsoleLink;
|
|
54
|
+
exports.DEFAULT_DATABASE_VERSION = "POSTGRES_15";
|
|
54
55
|
async function createInstance(args) {
|
|
55
56
|
const databaseFlags = [{ name: "cloudsql.iam_authentication", value: "on" }];
|
|
56
57
|
if (args.enableGoogleMlIntegration) {
|
|
@@ -60,7 +61,7 @@ async function createInstance(args) {
|
|
|
60
61
|
await client.post(`projects/${args.projectId}/instances`, {
|
|
61
62
|
name: args.instanceId,
|
|
62
63
|
region: args.location,
|
|
63
|
-
databaseVersion:
|
|
64
|
+
databaseVersion: exports.DEFAULT_DATABASE_VERSION,
|
|
64
65
|
settings: {
|
|
65
66
|
tier: "db-f1-micro",
|
|
66
67
|
edition: "ENTERPRISE",
|
|
@@ -70,7 +71,7 @@ async function createInstance(args) {
|
|
|
70
71
|
enableGoogleMlIntegration: args.enableGoogleMlIntegration,
|
|
71
72
|
databaseFlags,
|
|
72
73
|
storageAutoResize: false,
|
|
73
|
-
userLabels: { "firebase-data-connect": args.
|
|
74
|
+
userLabels: { "firebase-data-connect": args.freeTrialLabel },
|
|
74
75
|
insightsConfig: {
|
|
75
76
|
queryInsightsEnabled: true,
|
|
76
77
|
queryPlansPerMinute: 5,
|
|
@@ -66,12 +66,22 @@ async function askQuestions(setup) {
|
|
|
66
66
|
if (!configstore_1.configstore.get("gemini")) {
|
|
67
67
|
(0, utils_1.logBullet)("Learn more about Gemini in Firebase and how it uses your data: https://firebase.google.com/docs/gemini-in-firebase#how-gemini-in-firebase-uses-your-data");
|
|
68
68
|
}
|
|
69
|
-
|
|
70
|
-
message:
|
|
69
|
+
const wantToGenerate = await (0, prompt_1.confirm)({
|
|
70
|
+
message: "Do you want to generate schema and queries with Gemini?",
|
|
71
|
+
default: false,
|
|
71
72
|
});
|
|
72
|
-
if (
|
|
73
|
+
if (wantToGenerate) {
|
|
73
74
|
configstore_1.configstore.set("gemini", true);
|
|
74
75
|
await (0, ensureApis_1.ensureGIFApiTos)(setup.projectId);
|
|
76
|
+
info.appDescription = await (0, prompt_1.input)({
|
|
77
|
+
message: `Describe your app idea:`,
|
|
78
|
+
validate: async (s) => {
|
|
79
|
+
if (s.length > 0) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
return "Please enter a description for your app idea.";
|
|
83
|
+
},
|
|
84
|
+
});
|
|
75
85
|
}
|
|
76
86
|
}
|
|
77
87
|
if (hasBilling) {
|
|
@@ -105,9 +115,14 @@ async function actuate(setup, config, options) {
|
|
|
105
115
|
}
|
|
106
116
|
finally {
|
|
107
117
|
void (0, track_1.trackGA4)("dataconnect_init", {
|
|
108
|
-
project_status: setup.projectId ? (setup.isBillingEnabled ? "blaze" : "spark") : "missing",
|
|
109
118
|
flow: info.analyticsFlow,
|
|
110
|
-
|
|
119
|
+
project_status: setup.projectId
|
|
120
|
+
? setup.isBillingEnabled
|
|
121
|
+
? info.shouldProvisionCSQL
|
|
122
|
+
? "blaze_provisioned_csql"
|
|
123
|
+
: "blaze"
|
|
124
|
+
: "spark"
|
|
125
|
+
: "missing",
|
|
111
126
|
});
|
|
112
127
|
}
|
|
113
128
|
if (info.appDescription) {
|
|
@@ -136,6 +151,7 @@ async function actuateWithInfo(setup, config, info, options) {
|
|
|
136
151
|
instanceId: info.cloudSqlInstanceId,
|
|
137
152
|
databaseId: info.cloudSqlDatabase,
|
|
138
153
|
requireGoogleMlIntegration: false,
|
|
154
|
+
source: info.analyticsFlow.startsWith("mcp") ? "mcp_init" : "init",
|
|
139
155
|
});
|
|
140
156
|
}
|
|
141
157
|
const serviceName = `projects/${projectId}/locations/${info.locationId}/services/${info.serviceId}`;
|
|
@@ -448,7 +464,7 @@ async function promptForLocation(setup, info) {
|
|
|
448
464
|
if (info.locationId === "") {
|
|
449
465
|
const choices = await locationChoices(setup);
|
|
450
466
|
info.locationId = await (0, prompt_1.select)({
|
|
451
|
-
message: "What location
|
|
467
|
+
message: "What location would you like to use?",
|
|
452
468
|
choices,
|
|
453
469
|
default: exports.FDC_DEFAULT_REGION,
|
|
454
470
|
});
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addSdkGenerateToConnectorYaml = exports.actuate = exports.askQuestions = exports.FDC_SDK_PLATFORM_ENV = exports.FDC_SDK_FRAMEWORKS_ENV = exports.FDC_APP_FOLDER = void 0;
|
|
3
|
+
exports.addSdkGenerateToConnectorYaml = exports.actuate = exports.chooseApp = exports.askQuestions = exports.FDC_SDK_PLATFORM_ENV = exports.FDC_SDK_FRAMEWORKS_ENV = exports.FDC_APP_FOLDER = void 0;
|
|
4
4
|
const yaml = require("yaml");
|
|
5
5
|
const clc = require("colorette");
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const cwd = process.cwd();
|
|
8
8
|
const prompt_1 = require("../../../prompt");
|
|
9
|
-
const appFinder_1 = require("../../../dataconnect/appFinder");
|
|
10
9
|
const load_1 = require("../../../dataconnect/load");
|
|
11
|
-
const types_1 = require("../../../dataconnect/types");
|
|
12
10
|
const error_1 = require("../../../error");
|
|
13
11
|
const lodash_1 = require("lodash");
|
|
14
12
|
const utils_1 = require("../../../utils");
|
|
13
|
+
const appUtils_1 = require("../../../appUtils");
|
|
15
14
|
const dataconnectEmulator_1 = require("../../../emulator/dataconnectEmulator");
|
|
16
15
|
const auth_1 = require("../../../auth");
|
|
17
16
|
const create_app_1 = require("./create_app");
|
|
@@ -65,19 +64,20 @@ async function askQuestions(setup) {
|
|
|
65
64
|
}
|
|
66
65
|
exports.askQuestions = askQuestions;
|
|
67
66
|
async function chooseApp() {
|
|
68
|
-
let apps = await (0,
|
|
67
|
+
let apps = dedupeAppsByPlatformAndDirectory(await (0, appUtils_1.detectApps)(cwd));
|
|
69
68
|
if (apps.length) {
|
|
70
|
-
(0, utils_1.logLabeledSuccess)("dataconnect", `Detected existing apps ${apps.map((a) => (0,
|
|
69
|
+
(0, utils_1.logLabeledSuccess)("dataconnect", `Detected existing apps ${apps.map((a) => (0, appUtils_1.appDescription)(a)).join(", ")}`);
|
|
71
70
|
}
|
|
72
71
|
else {
|
|
73
72
|
(0, utils_1.logLabeledWarning)("dataconnect", "No app exists in the current directory.");
|
|
74
73
|
}
|
|
75
74
|
const envAppFolder = (0, utils_1.envOverride)(exports.FDC_APP_FOLDER, "");
|
|
76
|
-
const envPlatform = (0, utils_1.envOverride)(exports.FDC_SDK_PLATFORM_ENV,
|
|
75
|
+
const envPlatform = (0, utils_1.envOverride)(exports.FDC_SDK_PLATFORM_ENV, "");
|
|
77
76
|
const envFrameworks = (0, utils_1.envOverride)(exports.FDC_SDK_FRAMEWORKS_ENV, "")
|
|
78
77
|
.split(",")
|
|
78
|
+
.filter((f) => !!f)
|
|
79
79
|
.map((f) => f);
|
|
80
|
-
if (envAppFolder && envPlatform
|
|
80
|
+
if (envAppFolder && envPlatform) {
|
|
81
81
|
const envAppRelDir = path.relative(cwd, path.resolve(cwd, envAppFolder));
|
|
82
82
|
const matchedApps = apps.filter((app) => app.directory === envAppRelDir && (!app.platform || app.platform === envPlatform));
|
|
83
83
|
if (matchedApps.length) {
|
|
@@ -97,7 +97,7 @@ async function chooseApp() {
|
|
|
97
97
|
if (apps.length >= 2) {
|
|
98
98
|
const choices = apps.map((a) => {
|
|
99
99
|
return {
|
|
100
|
-
name: (0,
|
|
100
|
+
name: (0, appUtils_1.appDescription)(a),
|
|
101
101
|
value: a,
|
|
102
102
|
checked: a.directory === ".",
|
|
103
103
|
};
|
|
@@ -106,13 +106,14 @@ async function chooseApp() {
|
|
|
106
106
|
message: "Which apps do you want to set up Data Connect SDKs in?",
|
|
107
107
|
choices,
|
|
108
108
|
});
|
|
109
|
-
if (!pickedApps.length) {
|
|
109
|
+
if (!pickedApps || !pickedApps.length) {
|
|
110
110
|
throw new error_1.FirebaseError("Command Aborted. Please choose at least one app.");
|
|
111
111
|
}
|
|
112
112
|
apps = pickedApps;
|
|
113
113
|
}
|
|
114
114
|
return apps;
|
|
115
115
|
}
|
|
116
|
+
exports.chooseApp = chooseApp;
|
|
116
117
|
async function actuate(setup, config) {
|
|
117
118
|
var _a, _b;
|
|
118
119
|
const fdcInfo = (_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.dataconnect;
|
|
@@ -143,13 +144,13 @@ async function actuate(setup, config) {
|
|
|
143
144
|
exports.actuate = actuate;
|
|
144
145
|
async function actuateWithInfo(setup, config, info) {
|
|
145
146
|
if (!info.apps.length) {
|
|
146
|
-
info.apps = await (0,
|
|
147
|
+
info.apps = await (0, appUtils_1.detectApps)(cwd);
|
|
147
148
|
if (!info.apps.length) {
|
|
148
149
|
(0, utils_1.logLabeledBullet)("dataconnect", "No apps to setup Data Connect Generated SDKs");
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
|
-
const apps = info.apps;
|
|
153
|
+
const apps = dedupeAppsByPlatformAndDirectory(info.apps);
|
|
153
154
|
const connectorInfo = await chooseExistingConnector(setup, config);
|
|
154
155
|
const connectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml));
|
|
155
156
|
for (const app of apps) {
|
|
@@ -173,14 +174,14 @@ async function actuateWithInfo(setup, config, info) {
|
|
|
173
174
|
catch (e) {
|
|
174
175
|
(0, utils_1.logLabeledError)("dataconnect", `Failed to generate Data Connect SDKs\n${e === null || e === void 0 ? void 0 : e.message}`);
|
|
175
176
|
}
|
|
176
|
-
(0, utils_1.logLabeledSuccess)("dataconnect", `Installed generated SDKs for ${clc.bold(apps.map((a) => (0,
|
|
177
|
-
if (apps.some((a) => a.platform ===
|
|
177
|
+
(0, utils_1.logLabeledSuccess)("dataconnect", `Installed generated SDKs for ${clc.bold(apps.map((a) => (0, appUtils_1.appDescription)(a)).join(", "))}`);
|
|
178
|
+
if (apps.some((a) => a.platform === appUtils_1.Platform.IOS)) {
|
|
178
179
|
(0, utils_1.logBullet)(clc.bold("Please follow the instructions here to add your generated sdk to your XCode project:\n\thttps://firebase.google.com/docs/data-connect/ios-sdk#set-client"));
|
|
179
180
|
}
|
|
180
|
-
if (apps.some((a) => { var _a; return (_a = a.frameworks) === null || _a === void 0 ? void 0 : _a.includes(
|
|
181
|
+
if (apps.some((a) => { var _a; return (_a = a.frameworks) === null || _a === void 0 ? void 0 : _a.includes(appUtils_1.Framework.REACT); })) {
|
|
181
182
|
(0, utils_1.logBullet)("Visit https://firebase.google.com/docs/data-connect/web-sdk#react for more information on how to set up React Generated SDKs for Firebase Data Connect");
|
|
182
183
|
}
|
|
183
|
-
if (apps.some((a) => { var _a; return (_a = a.frameworks) === null || _a === void 0 ? void 0 : _a.includes(
|
|
184
|
+
if (apps.some((a) => { var _a; return (_a = a.frameworks) === null || _a === void 0 ? void 0 : _a.includes(appUtils_1.Framework.ANGULAR); })) {
|
|
184
185
|
(0, utils_1.logBullet)("Run `ng add @angular/fire` to install angular sdk dependencies.\nVisit https://github.com/invertase/tanstack-query-firebase/tree/main/packages/angular for more information on how to set up Angular Generated SDKs for Firebase Data Connect");
|
|
185
186
|
}
|
|
186
187
|
}
|
|
@@ -222,7 +223,7 @@ function addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app) {
|
|
|
222
223
|
}
|
|
223
224
|
const generate = connectorYaml.generate;
|
|
224
225
|
switch (app.platform) {
|
|
225
|
-
case
|
|
226
|
+
case appUtils_1.Platform.WEB: {
|
|
226
227
|
const javascriptSdk = {
|
|
227
228
|
outputDir: path.relative(connectorDir, path.join(appDir, `src/dataconnect-generated`)),
|
|
228
229
|
package: `@dataconnect/generated`,
|
|
@@ -241,7 +242,7 @@ function addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app) {
|
|
|
241
242
|
}
|
|
242
243
|
break;
|
|
243
244
|
}
|
|
244
|
-
case
|
|
245
|
+
case appUtils_1.Platform.FLUTTER: {
|
|
245
246
|
const dartSdk = {
|
|
246
247
|
outputDir: path.relative(connectorDir, path.join(appDir, `lib/dataconnect_generated`)),
|
|
247
248
|
package: "dataconnect_generated",
|
|
@@ -254,7 +255,7 @@ function addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app) {
|
|
|
254
255
|
}
|
|
255
256
|
break;
|
|
256
257
|
}
|
|
257
|
-
case
|
|
258
|
+
case appUtils_1.Platform.ANDROID: {
|
|
258
259
|
const kotlinSdk = {
|
|
259
260
|
outputDir: path.relative(connectorDir, path.join(appDir, `src/main/kotlin`)),
|
|
260
261
|
package: `com.google.firebase.dataconnect.generated`,
|
|
@@ -267,7 +268,7 @@ function addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app) {
|
|
|
267
268
|
}
|
|
268
269
|
break;
|
|
269
270
|
}
|
|
270
|
-
case
|
|
271
|
+
case appUtils_1.Platform.IOS: {
|
|
271
272
|
const swiftSdk = {
|
|
272
273
|
outputDir: path.relative(connectorDir, path.join(app.directory, `../FirebaseDataConnectGenerated`)),
|
|
273
274
|
package: "DataConnectGenerated",
|
|
@@ -281,9 +282,26 @@ function addSdkGenerateToConnectorYaml(connectorInfo, connectorYaml, app) {
|
|
|
281
282
|
break;
|
|
282
283
|
}
|
|
283
284
|
default:
|
|
284
|
-
throw new error_1.FirebaseError(`Unsupported platform ${app.platform} for Data Connect SDK generation. Supported platforms are: ${Object.values(
|
|
285
|
-
.filter((p) => p !== types_1.Platform.NONE && p !== types_1.Platform.MULTIPLE)
|
|
286
|
-
.join(", ")}\n${JSON.stringify(app)}`);
|
|
285
|
+
throw new error_1.FirebaseError(`Unsupported platform ${app.platform} for Data Connect SDK generation. Supported platforms are: ${Object.values(appUtils_1.Platform).join(", ")}\n${JSON.stringify(app)}`);
|
|
287
286
|
}
|
|
288
287
|
}
|
|
289
288
|
exports.addSdkGenerateToConnectorYaml = addSdkGenerateToConnectorYaml;
|
|
289
|
+
function dedupeAppsByPlatformAndDirectory(apps) {
|
|
290
|
+
var _a;
|
|
291
|
+
const uniqueApps = new Map();
|
|
292
|
+
for (const app of apps) {
|
|
293
|
+
const frameworkKey = app.frameworks ? [...app.frameworks].sort().join(",") : "";
|
|
294
|
+
const key = `${app.platform}:${app.directory}:${frameworkKey}`;
|
|
295
|
+
if (!uniqueApps.has(key)) {
|
|
296
|
+
const minifiedApp = {
|
|
297
|
+
platform: app.platform,
|
|
298
|
+
directory: app.directory,
|
|
299
|
+
};
|
|
300
|
+
if ((_a = app.frameworks) === null || _a === void 0 ? void 0 : _a.length) {
|
|
301
|
+
minifiedApp.frameworks = [...app.frameworks];
|
|
302
|
+
}
|
|
303
|
+
uniqueApps.set(key, minifiedApp);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return Array.from(uniqueApps.values());
|
|
307
|
+
}
|
package/lib/management/apps.js
CHANGED
|
@@ -9,46 +9,49 @@ const api_1 = require("../api");
|
|
|
9
9
|
const error_1 = require("../error");
|
|
10
10
|
const logger_1 = require("../logger");
|
|
11
11
|
const operation_poller_1 = require("../operation-poller");
|
|
12
|
-
const types_1 = require("../dataconnect/types");
|
|
13
12
|
const projectUtils_1 = require("../projectUtils");
|
|
14
|
-
const
|
|
13
|
+
const prompt_1 = require("../prompt");
|
|
15
14
|
const projects_1 = require("./projects");
|
|
16
|
-
const
|
|
15
|
+
const appUtils_1 = require("../appUtils");
|
|
17
16
|
const utils_1 = require("../utils");
|
|
18
17
|
const TIMEOUT_MILLIS = 30000;
|
|
19
18
|
exports.APP_LIST_PAGE_SIZE = 100;
|
|
20
19
|
const CREATE_APP_API_REQUEST_TIMEOUT_MILLIS = 15000;
|
|
21
20
|
async function getDisplayName() {
|
|
22
|
-
return await
|
|
21
|
+
return await (0, prompt_1.input)("What would you like to call your app?");
|
|
23
22
|
}
|
|
24
23
|
async function getPlatform(appDir, config) {
|
|
25
|
-
let
|
|
26
|
-
|
|
24
|
+
let targetPlatforms = await (0, appUtils_1.getPlatformsFromFolder)(appDir);
|
|
25
|
+
let targetPlatform;
|
|
26
|
+
if (targetPlatforms.length === 0) {
|
|
27
27
|
appDir = await (0, utils_1.promptForDirectory)({
|
|
28
28
|
config,
|
|
29
29
|
relativeTo: appDir,
|
|
30
30
|
message: "We couldn't determine what kind of app you're using. Where is your app directory?",
|
|
31
31
|
});
|
|
32
|
-
|
|
32
|
+
targetPlatforms = await (0, appUtils_1.getPlatformsFromFolder)(appDir);
|
|
33
33
|
}
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
36
|
-
(0, utils_1.logBullet)(`Couldn't automatically detect app
|
|
34
|
+
if (targetPlatforms.length !== 1) {
|
|
35
|
+
if (targetPlatforms.length === 0) {
|
|
36
|
+
(0, utils_1.logBullet)(`Couldn't automatically detect your app in directory ${appDir}.`);
|
|
37
37
|
}
|
|
38
38
|
else {
|
|
39
39
|
(0, utils_1.logSuccess)(`Detected multiple app platforms in directory ${appDir}`);
|
|
40
40
|
}
|
|
41
41
|
const platforms = [
|
|
42
|
-
{ name: "iOS (Swift)", value:
|
|
43
|
-
{ name: "Web (JavaScript)", value:
|
|
44
|
-
{ name: "Android (Kotlin)", value:
|
|
42
|
+
{ name: "iOS (Swift)", value: appUtils_1.Platform.IOS },
|
|
43
|
+
{ name: "Web (JavaScript)", value: appUtils_1.Platform.WEB },
|
|
44
|
+
{ name: "Android (Kotlin)", value: appUtils_1.Platform.ANDROID },
|
|
45
45
|
];
|
|
46
|
-
targetPlatform = await
|
|
46
|
+
targetPlatform = await (0, prompt_1.select)({
|
|
47
47
|
message: "Which platform do you want to set up an SDK for? Note: We currently do not support automatically setting up C++ or Unity projects.",
|
|
48
48
|
choices: platforms,
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
|
-
else
|
|
51
|
+
else {
|
|
52
|
+
targetPlatform = targetPlatforms[0];
|
|
53
|
+
}
|
|
54
|
+
if (targetPlatform === appUtils_1.Platform.FLUTTER) {
|
|
52
55
|
(0, utils_1.logWarning)(`Detected ${targetPlatform} app in directory ${appDir}`);
|
|
53
56
|
throw new error_1.FirebaseError(`Flutter is not supported by apps:configure.
|
|
54
57
|
Please follow the link below to set up firebase for your Flutter app:
|
|
@@ -58,18 +61,15 @@ https://firebase.google.com/docs/flutter/setup
|
|
|
58
61
|
else {
|
|
59
62
|
(0, utils_1.logSuccess)(`Detected ${targetPlatform} app in directory ${appDir}`);
|
|
60
63
|
}
|
|
61
|
-
return targetPlatform
|
|
62
|
-
? AppPlatform.PLATFORM_UNSPECIFIED
|
|
63
|
-
: targetPlatform;
|
|
64
|
+
return targetPlatform;
|
|
64
65
|
}
|
|
65
66
|
exports.getPlatform = getPlatform;
|
|
66
67
|
async function initiateIosAppCreation(options) {
|
|
67
68
|
if (!options.nonInteractive) {
|
|
68
69
|
options.displayName = options.displayName || (await getDisplayName());
|
|
69
|
-
options.bundleId =
|
|
70
|
-
options.bundleId || (await prompt.input("Please specify your iOS app bundle ID:"));
|
|
70
|
+
options.bundleId = options.bundleId || (await (0, prompt_1.input)("Please specify your iOS app bundle ID:"));
|
|
71
71
|
options.appStoreId =
|
|
72
|
-
options.appStoreId || (await
|
|
72
|
+
options.appStoreId || (await (0, prompt_1.input)("Please specify your iOS app App Store ID:"));
|
|
73
73
|
}
|
|
74
74
|
if (!options.bundleId) {
|
|
75
75
|
throw new error_1.FirebaseError("Bundle ID for iOS app cannot be empty");
|
|
@@ -93,7 +93,7 @@ async function initiateAndroidAppCreation(options) {
|
|
|
93
93
|
if (!options.nonInteractive) {
|
|
94
94
|
options.displayName = options.displayName || (await getDisplayName());
|
|
95
95
|
options.packageName =
|
|
96
|
-
options.packageName || (await
|
|
96
|
+
options.packageName || (await (0, prompt_1.input)("Please specify your Android app package name:"));
|
|
97
97
|
}
|
|
98
98
|
if (!options.packageName) {
|
|
99
99
|
throw new error_1.FirebaseError("Package name for Android app cannot be empty");
|
|
@@ -179,7 +179,7 @@ async function selectAppInteractively(apps, appPlatform) {
|
|
|
179
179
|
value: app,
|
|
180
180
|
};
|
|
181
181
|
});
|
|
182
|
-
return await
|
|
182
|
+
return await (0, prompt_1.select)({
|
|
183
183
|
message: `Select the ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}` +
|
|
184
184
|
"app to get the configuration data:",
|
|
185
185
|
choices,
|
|
@@ -416,7 +416,7 @@ async function writeConfigToFile(filename, nonInteractive, fileContents) {
|
|
|
416
416
|
if (nonInteractive) {
|
|
417
417
|
throw new error_1.FirebaseError(`${filename} already exists`);
|
|
418
418
|
}
|
|
419
|
-
const overwrite = await
|
|
419
|
+
const overwrite = await (0, prompt_1.confirm)(`${filename} already exists. Do you want to overwrite?`);
|
|
420
420
|
if (!overwrite) {
|
|
421
421
|
return false;
|
|
422
422
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.consult = void 0;
|
|
4
|
-
const
|
|
4
|
+
const appUtils_1 = require("../../../appUtils");
|
|
5
5
|
const fdcExperience_1 = require("../../../gemini/fdcExperience");
|
|
6
6
|
const errors_1 = require("../../errors");
|
|
7
7
|
const prompt_1 = require("../../prompt");
|
|
@@ -32,8 +32,7 @@ exports.consult = (0, prompt_1.prompt)({
|
|
|
32
32
|
},
|
|
33
33
|
];
|
|
34
34
|
}
|
|
35
|
-
const
|
|
36
|
-
const platforms = apps.map((a) => a.platform);
|
|
35
|
+
const platforms = await (0, appUtils_1.getPlatformsFromFolder)(config.projectDir);
|
|
37
36
|
const gifPrompt = `I am using a coding agent to build with Firebase and I have a specific question that I would like answered. Provide a robust and detailed response that will help the coding agent act on my behalf in a local workspace.
|
|
38
37
|
|
|
39
38
|
App Platform(s): ${platforms.join(", ")}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.init = void 0;
|
|
4
|
-
const
|
|
5
|
-
const types_1 = require("../../../dataconnect/types");
|
|
4
|
+
const appUtils_1 = require("../../../appUtils");
|
|
6
5
|
const prompt_1 = require("../../prompt");
|
|
7
6
|
exports.init = (0, prompt_1.prompt)({
|
|
8
7
|
name: "init",
|
|
@@ -12,7 +11,7 @@ exports.init = (0, prompt_1.prompt)({
|
|
|
12
11
|
},
|
|
13
12
|
}, async (_, mcp) => {
|
|
14
13
|
const { config, projectId, accountEmail, firebaseCliCommand } = mcp;
|
|
15
|
-
const
|
|
14
|
+
const platforms = await (0, appUtils_1.getPlatformsFromFolder)(config.projectDir);
|
|
16
15
|
return [
|
|
17
16
|
{
|
|
18
17
|
role: "user",
|
|
@@ -29,7 +28,7 @@ Your goal is to help the user setup Firebase services in this workspace. Firebas
|
|
|
29
28
|
|
|
30
29
|
Use this information to determine which Firebase services the user is already using (if any).
|
|
31
30
|
|
|
32
|
-
Workspace platform: ${
|
|
31
|
+
Workspace platform(s): ${platforms.length > 0 ? platforms.join(", ") : "<UNABLE TO DETECT>"}
|
|
33
32
|
Active user: ${accountEmail || "<NONE>"}
|
|
34
33
|
Active project: ${projectId || "<NONE>"}
|
|
35
34
|
|
|
@@ -5,19 +5,15 @@ const docs_1 = require("./docs");
|
|
|
5
5
|
const init_ai_1 = require("./guides/init_ai");
|
|
6
6
|
const init_auth_1 = require("./guides/init_auth");
|
|
7
7
|
const init_backend_1 = require("./guides/init_backend");
|
|
8
|
-
const init_data_connect_1 = require("./guides/init_data_connect");
|
|
9
8
|
const init_firestore_1 = require("./guides/init_firestore");
|
|
10
9
|
const init_firestore_rules_1 = require("./guides/init_firestore_rules");
|
|
11
10
|
const init_hosting_1 = require("./guides/init_hosting");
|
|
12
|
-
const init_rtdb_1 = require("./guides/init_rtdb");
|
|
13
11
|
const track_1 = require("../../track");
|
|
14
12
|
exports.resources = [
|
|
15
13
|
init_backend_1.init_backend,
|
|
16
14
|
init_ai_1.init_ai,
|
|
17
|
-
init_data_connect_1.init_data_connect,
|
|
18
15
|
init_firestore_1.init_firestore,
|
|
19
16
|
init_firestore_rules_1.init_firestore_rules,
|
|
20
|
-
init_rtdb_1.init_rtdb,
|
|
21
17
|
init_auth_1.init_auth,
|
|
22
18
|
init_hosting_1.init_hosting,
|
|
23
19
|
];
|
|
@@ -56,7 +56,6 @@ You can find candidate service_id in \`dataconnect.yaml\`
|
|
|
56
56
|
executeGraphQL = dataplane.executeGraphQLRead;
|
|
57
57
|
}
|
|
58
58
|
const response = await executeGraphQL(apiClient, serviceInfo.serviceName, {
|
|
59
|
-
name: "",
|
|
60
59
|
query,
|
|
61
60
|
variables: (0, converter_1.parseVariables)(unparsedVariables),
|
|
62
61
|
extensions: {
|
|
@@ -49,14 +49,15 @@ function graphqlResponseToToolResponse(g) {
|
|
|
49
49
|
}
|
|
50
50
|
exports.graphqlResponseToToolResponse = graphqlResponseToToolResponse;
|
|
51
51
|
function parseVariables(unparsedVariables) {
|
|
52
|
+
let obj;
|
|
52
53
|
try {
|
|
53
|
-
|
|
54
|
-
if (typeof variables !== "object")
|
|
55
|
-
throw new Error("not an object");
|
|
56
|
-
return variables;
|
|
54
|
+
obj = JSON.parse(unparsedVariables || "{}");
|
|
57
55
|
}
|
|
58
56
|
catch (e) {
|
|
59
57
|
throw new Error("Provided variables string `" + unparsedVariables + "` is not valid JSON.");
|
|
60
58
|
}
|
|
59
|
+
if (typeof obj !== "object" || obj == null)
|
|
60
|
+
throw new Error("not an object");
|
|
61
|
+
return obj;
|
|
61
62
|
}
|
|
62
63
|
exports.parseVariables = parseVariables;
|