firebase-tools 15.9.1 → 15.10.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/appdistribution/distribution.js +1 -1
- package/lib/appdistribution/options-parser-util.js +7 -0
- package/lib/apphosting/config.js +24 -1
- package/lib/commands/appdistribution-distribute.js +5 -5
- package/lib/commands/apptesting.js +9 -10
- package/lib/commands/dataconnect-sql-setup.js +3 -4
- package/lib/commands/dataconnect-sql-shell.js +2 -1
- package/lib/commands/index.js +4 -6
- package/lib/commands/studio-export.js +24 -6
- package/lib/dataconnect/load.js +24 -1
- package/lib/dataconnect/schemaMigration.js +25 -17
- package/lib/dataconnect/types.js +1 -0
- package/lib/deploy/functions/build.js +24 -9
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +8 -2
- package/lib/emulator/apphosting/config.js +1 -21
- package/lib/emulator/controller.js +2 -1
- package/lib/emulator/hub.js +2 -0
- package/lib/emulator/hubExport.js +7 -6
- package/lib/experiments.js +5 -5
- package/lib/firebase_studio/migrate.js +162 -66
- package/lib/gcp/cloudfunctionsv2.js +23 -2
- package/lib/gcp/cloudsql/permissionsSetup.js +4 -4
- package/lib/init/features/hosting/index.js +71 -101
- package/lib/mcp/tools/apptesting/tests.js +2 -2
- package/lib/track.js +1 -1
- package/package.json +1 -1
- package/schema/dataconnect-yaml.json +4 -0
- package/templates/firebase-studio-export/system_instructions_template.md +14 -3
- package/templates/firebase-studio-export/workflows/startup_workflow.md +3 -7
|
@@ -3,15 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.askQuestions = askQuestions;
|
|
4
4
|
exports.actuate = actuate;
|
|
5
5
|
const clc = require("colorette");
|
|
6
|
-
const node_fs_1 = require("node:fs");
|
|
7
6
|
const path_1 = require("path");
|
|
8
7
|
const apiv2_1 = require("../../../apiv2");
|
|
9
|
-
const
|
|
8
|
+
const frameworks_1 = require("../../../frameworks");
|
|
9
|
+
const github = require("./github");
|
|
10
10
|
const prompt_1 = require("../../../prompt");
|
|
11
11
|
const logger_1 = require("../../../logger");
|
|
12
|
-
const frameworks_1 = require("../../../frameworks");
|
|
13
|
-
const constants_1 = require("../../../frameworks/constants");
|
|
14
|
-
const experiments = require("../../../experiments");
|
|
15
12
|
const getDefaultHostingSite_1 = require("../../../getDefaultHostingSite");
|
|
16
13
|
const utils_1 = require("../../../utils");
|
|
17
14
|
const interactive_1 = require("../../../hosting/interactive");
|
|
@@ -22,7 +19,51 @@ const INDEX_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/index.htm
|
|
|
22
19
|
const MISSING_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/404.html");
|
|
23
20
|
const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"];
|
|
24
21
|
async function askQuestions(setup, config, options) {
|
|
25
|
-
var _a, _b
|
|
22
|
+
var _a, _b;
|
|
23
|
+
const discoveredFramework = await (0, frameworks_1.discover)(config.projectDir, false);
|
|
24
|
+
if (discoveredFramework && discoveredFramework.mayWantBackend) {
|
|
25
|
+
const frameworkName = frameworks_1.WebFrameworks[discoveredFramework.framework]?.name ?? discoveredFramework.framework;
|
|
26
|
+
switch (discoveredFramework.framework) {
|
|
27
|
+
case "next":
|
|
28
|
+
case "angular":
|
|
29
|
+
case "nuxt":
|
|
30
|
+
case "nuxt2":
|
|
31
|
+
case "express":
|
|
32
|
+
case "svelekit":
|
|
33
|
+
case "sveltekit":
|
|
34
|
+
logger_1.logger.info();
|
|
35
|
+
const useAppHosting = await (0, prompt_1.confirm)({
|
|
36
|
+
message: `Detected a ${frameworkName} codebase with SSR features. We can't guarantee that ` +
|
|
37
|
+
`this site will work on Firebase Hosting, which is optimized for static sites. Another ` +
|
|
38
|
+
`product, Firebase App Hosting, was designed for SSR web apps. Would ` +
|
|
39
|
+
`you like to use App Hosting instead? Learn more here: ` +
|
|
40
|
+
`https://firebase.google.com/docs/app-hosting/product-comparison#hostings`,
|
|
41
|
+
default: true,
|
|
42
|
+
});
|
|
43
|
+
if (useAppHosting) {
|
|
44
|
+
setup.featureInfo || (setup.featureInfo = {});
|
|
45
|
+
setup.featureInfo.hosting = { redirectToAppHosting: true };
|
|
46
|
+
setup.features?.unshift("apphosting");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
default:
|
|
51
|
+
logger_1.logger.info();
|
|
52
|
+
logger_1.logger.info(`Detected a ${frameworkName} codebase with SSR features. We can't guarantee that ` +
|
|
53
|
+
`this site will work on Firebase Hosting, which is optimized for static sites. Another ` +
|
|
54
|
+
`product, Firebase App Hosting, was designed for SSR web apps.`);
|
|
55
|
+
logger_1.logger.info(`Learn about App Hosting here: https://firebase.google.com/docs/app-hosting/product-comparison#hostings`);
|
|
56
|
+
logger_1.logger.info(`Learn how to deploy frameworks with App Hosting here: https://firebase.blog/posts/2025/06/app-hosting-frameworks/`);
|
|
57
|
+
const continueWithHosting = await (0, prompt_1.confirm)({
|
|
58
|
+
message: `Would you like to continue setting up Firebase Hosting?`,
|
|
59
|
+
default: false,
|
|
60
|
+
});
|
|
61
|
+
if (!continueWithHosting) {
|
|
62
|
+
throw new error_1.FirebaseError("Hosting initialization cancelled.", { exit: 1 });
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
26
67
|
setup.featureInfo = setup.featureInfo || {};
|
|
27
68
|
setup.featureInfo.hosting = {};
|
|
28
69
|
if (setup.projectId) {
|
|
@@ -48,75 +89,18 @@ async function askQuestions(setup, config, options) {
|
|
|
48
89
|
setup.featureInfo.hosting.newSiteId = await (0, interactive_1.pickHostingSiteName)("", createOptions);
|
|
49
90
|
}
|
|
50
91
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
setup.featureInfo.hosting.useWebFrameworks = true;
|
|
62
|
-
setup.featureInfo.hosting.useDiscoveredFramework = true;
|
|
63
|
-
setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
setup.featureInfo.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (setup.featureInfo.hosting.useWebFrameworks) {
|
|
70
|
-
(_a = setup.featureInfo.hosting).source ?? (_a.source = await (0, prompt_1.input)({
|
|
71
|
-
message: "What folder would you like to use for your web application's root directory?",
|
|
72
|
-
default: "hosting",
|
|
73
|
-
}));
|
|
74
|
-
discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.featureInfo.hosting.source));
|
|
75
|
-
if (discoveredFramework) {
|
|
76
|
-
const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
|
|
77
|
-
(_b = setup.featureInfo.hosting).useDiscoveredFramework ?? (_b.useDiscoveredFramework = await (0, prompt_1.confirm)({
|
|
78
|
-
message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`,
|
|
79
|
-
default: true,
|
|
80
|
-
}));
|
|
81
|
-
if (setup.featureInfo.hosting.useDiscoveredFramework)
|
|
82
|
-
setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
|
|
83
|
-
}
|
|
84
|
-
const choices = [];
|
|
85
|
-
for (const value in frameworks_1.WebFrameworks) {
|
|
86
|
-
if (frameworks_1.WebFrameworks[value]) {
|
|
87
|
-
const { name, init } = frameworks_1.WebFrameworks[value];
|
|
88
|
-
if (init)
|
|
89
|
-
choices.push({ name, value });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
const defaultChoice = choices.find(({ value }) => value === discoveredFramework?.framework)?.value;
|
|
93
|
-
(_c = setup.featureInfo.hosting).webFramework ?? (_c.webFramework = await (0, prompt_1.select)({
|
|
94
|
-
message: "Please choose the framework:",
|
|
95
|
-
default: defaultChoice,
|
|
96
|
-
choices,
|
|
97
|
-
}));
|
|
98
|
-
setup.featureInfo.hosting.region =
|
|
99
|
-
setup.featureInfo.hosting.region ||
|
|
100
|
-
(await (0, prompt_1.select)({
|
|
101
|
-
message: "In which region would you like to host server-side content, if applicable?",
|
|
102
|
-
default: constants_1.DEFAULT_REGION,
|
|
103
|
-
choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
logger_1.logger.info();
|
|
108
|
-
logger_1.logger.info(`Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`);
|
|
109
|
-
logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
|
|
110
|
-
logger_1.logger.info("have a build process for your assets, use your build's output directory.");
|
|
111
|
-
logger_1.logger.info();
|
|
112
|
-
(_d = setup.featureInfo.hosting).public ?? (_d.public = await (0, prompt_1.input)({
|
|
113
|
-
message: "What do you want to use as your public directory?",
|
|
114
|
-
default: "public",
|
|
115
|
-
}));
|
|
116
|
-
(_e = setup.featureInfo.hosting).spa ?? (_e.spa = await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
|
|
117
|
-
}
|
|
92
|
+
logger_1.logger.info();
|
|
93
|
+
logger_1.logger.info(`Your ${clc.bold("public")} directory is the folder (relative to your project directory) that`);
|
|
94
|
+
logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
|
|
95
|
+
logger_1.logger.info("have a build process for your assets, use your build's output directory.");
|
|
96
|
+
logger_1.logger.info();
|
|
97
|
+
(_a = setup.featureInfo.hosting).public ?? (_a.public = await (0, prompt_1.input)({
|
|
98
|
+
message: "What do you want to use as your public directory?",
|
|
99
|
+
default: "public",
|
|
100
|
+
}));
|
|
101
|
+
(_b = setup.featureInfo.hosting).spa ?? (_b.spa = await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
|
|
118
102
|
if (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?")) {
|
|
119
|
-
return
|
|
103
|
+
return github.initGitHub(setup);
|
|
120
104
|
}
|
|
121
105
|
}
|
|
122
106
|
async function actuate(setup, config, options) {
|
|
@@ -124,40 +108,26 @@ async function actuate(setup, config, options) {
|
|
|
124
108
|
if (!hostingInfo) {
|
|
125
109
|
throw new error_1.FirebaseError("Could not find hosting info in setup.featureInfo.hosting. This should not happen.", { exit: 2 });
|
|
126
110
|
}
|
|
111
|
+
if (hostingInfo.redirectToAppHosting) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
127
114
|
if (hostingInfo.newSiteId && setup.projectId) {
|
|
128
115
|
await (0, api_1.createSite)(setup.projectId, hostingInfo.newSiteId);
|
|
129
116
|
logger_1.logger.info();
|
|
130
117
|
(0, utils_1.logSuccess)(`Firebase Hosting site ${hostingInfo.newSiteId} created!`);
|
|
131
118
|
logger_1.logger.info();
|
|
132
119
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
setup.config.hosting = {
|
|
141
|
-
source: hostingInfo.source,
|
|
142
|
-
ignore: DEFAULT_IGNORES,
|
|
143
|
-
frameworksBackend: {
|
|
144
|
-
region: hostingInfo.region,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
120
|
+
setup.config.hosting = {
|
|
121
|
+
public: hostingInfo.public,
|
|
122
|
+
ignore: DEFAULT_IGNORES,
|
|
123
|
+
};
|
|
124
|
+
if (hostingInfo.spa) {
|
|
125
|
+
setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
|
|
147
126
|
}
|
|
148
127
|
else {
|
|
149
|
-
|
|
150
|
-
public: hostingInfo.public,
|
|
151
|
-
ignore: DEFAULT_IGNORES,
|
|
152
|
-
};
|
|
153
|
-
if (hostingInfo.spa) {
|
|
154
|
-
setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
await config.askWriteProjectFile(`${hostingInfo.public}/404.html`, MISSING_TEMPLATE, !!options.force);
|
|
158
|
-
}
|
|
159
|
-
const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
|
|
160
|
-
const response = await c.get("/firebasejs/releases.json");
|
|
161
|
-
await config.askWriteProjectFile(`${hostingInfo.public}/index.html`, INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), !!options.force);
|
|
128
|
+
await config.askWriteProjectFile((0, path_1.join)(hostingInfo.public ?? "public", "404.html"), MISSING_TEMPLATE, !!options.force);
|
|
162
129
|
}
|
|
130
|
+
const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
|
|
131
|
+
const response = await c.get("/firebasejs/releases.json");
|
|
132
|
+
await config.askWriteProjectFile((0, path_1.join)(hostingInfo.public ?? "public", "index.html"), INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), !!options.force);
|
|
163
133
|
}
|
|
@@ -58,8 +58,8 @@ exports.run_tests = (0, tool_1.tool)("apptesting", {
|
|
|
58
58
|
}, async ({ appId, releaseBinaryFile, testDevices, testCase }) => {
|
|
59
59
|
const devices = testDevices || defaultDevices;
|
|
60
60
|
const client = new client_1.AppDistributionClient();
|
|
61
|
-
const
|
|
62
|
-
return (0, util_1.toContent)(await client.createReleaseTest(
|
|
61
|
+
const release = await (0, distribution_1.upload)(client, (0, options_parser_util_1.toAppName)(appId), new distribution_1.Distribution(releaseBinaryFile));
|
|
62
|
+
return (0, util_1.toContent)(await client.createReleaseTest(release.name, devices, testCase));
|
|
63
63
|
});
|
|
64
64
|
exports.check_status = (0, tool_1.tool)("apptesting", {
|
|
65
65
|
name: "check_status",
|
package/lib/track.js
CHANGED
|
@@ -33,7 +33,7 @@ exports.GA4_PROPERTIES = {
|
|
|
33
33
|
},
|
|
34
34
|
};
|
|
35
35
|
function usageEnabled() {
|
|
36
|
-
return (
|
|
36
|
+
return (!!process.env.IS_FIREBASE_CLI || (0, env_1.isFirebaseMcp)()) && !!configstore_1.configstore.get("usage");
|
|
37
37
|
}
|
|
38
38
|
const GA4_USER_PROPS = {
|
|
39
39
|
node_platform: {
|
package/package.json
CHANGED
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"type": "string",
|
|
11
11
|
"description": "The name of the PostgreSQL database."
|
|
12
12
|
},
|
|
13
|
+
"schema": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "The PostgreSQL schema name. Defaults to 'public' if not specified."
|
|
16
|
+
},
|
|
13
17
|
"cloudSql": {
|
|
14
18
|
"additionalProperties": false,
|
|
15
19
|
"type": "object",
|
|
@@ -9,6 +9,17 @@ App Name: ${appName}
|
|
|
9
9
|
|
|
10
10
|
# Migration Guidelines
|
|
11
11
|
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
- Use
|
|
12
|
+
- `docs/blueprint.md` describes what this app was supposed to do when it was exported. Read it before making changes to the app.
|
|
13
|
+
|
|
14
|
+
- Use Agent Skills to guide Firebase usage. If they are not available, install them by running `npx -y skills add firebase/agent-skills --skill "*" -y`
|
|
15
|
+
|
|
16
|
+
- When asked to deploy or publish the app, deploy to Firebase App Hosting using `firebase deploy`.
|
|
17
|
+
If you run into issues, ensure `firebase.json` contains a valid `apphosting` block (with `backendId` and `rootDir`).
|
|
18
|
+
DO NOT use `firebase apphosting:backends:create` or `firebase apphosting:rollouts:create`.
|
|
19
|
+
DO NOT try to deploy to Firebase Hosting.
|
|
20
|
+
|
|
21
|
+
- When asked to add AI features, use Genkit (as already configured in `src/ai/`).
|
|
22
|
+
|
|
23
|
+
# Important
|
|
24
|
+
|
|
25
|
+
Never display, log, or commit sensitive credentials, .env files, or service account keys.
|
|
@@ -5,12 +5,8 @@ description: Run initial checks and fix common migration issues
|
|
|
5
5
|
|
|
6
6
|
# Step 1: Check Compilation
|
|
7
7
|
|
|
8
|
-
Run \`npm run typecheck\` and \`npm run build\` to ensure the project is in a healthy state.
|
|
8
|
+
Run \`npm run typecheck\` and \`npm run build\` (depending on the app type) to ensure the project is in a healthy state.
|
|
9
9
|
|
|
10
|
-
# Step 2:
|
|
10
|
+
# Step 2: Cleanup Genkit config
|
|
11
11
|
|
|
12
|
-
If
|
|
13
|
-
|
|
14
|
-
# Step 3: Cleanup Genkit config
|
|
15
|
-
|
|
16
|
-
If genkit is otherwise unused in this project, remove the configuration in src/ai/genkit.ts and remove related dependencies in package.json.
|
|
12
|
+
If genkit is otherwise unused in this project, ask the user if they'd like to remove the configuration in src/ai/genkit.ts and remove related dependencies in package.json.
|