firebase-tools 14.12.1 → 14.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/dataconnect-services-list.js +5 -5
  3. package/lib/commands/dataconnect-sql-grant.js +5 -0
  4. package/lib/commands/dataconnect-sql-setup.js +1 -3
  5. package/lib/crashlytics/getIssueDetails.js +41 -0
  6. package/lib/crashlytics/getSampleCrash.js +48 -0
  7. package/lib/dataconnect/client.js +23 -15
  8. package/lib/dataconnect/ensureApis.js +5 -9
  9. package/lib/dataconnect/errors.js +7 -1
  10. package/lib/dataconnect/fileUtils.js +5 -6
  11. package/lib/dataconnect/freeTrial.js +16 -39
  12. package/lib/dataconnect/provisionCloudSql.js +67 -70
  13. package/lib/dataconnect/schemaMigration.js +222 -170
  14. package/lib/deploy/dataconnect/deploy.js +9 -11
  15. package/lib/deploy/dataconnect/prepare.js +7 -10
  16. package/lib/deploy/dataconnect/release.js +42 -30
  17. package/lib/deploy/functions/backend.js +8 -2
  18. package/lib/deploy/functions/build.js +23 -1
  19. package/lib/deploy/functions/ensure.js +1 -1
  20. package/lib/deploy/functions/functionsDeployHelper.js +8 -1
  21. package/lib/deploy/functions/prepare.js +8 -4
  22. package/lib/deploy/functions/pricing.js +12 -5
  23. package/lib/deploy/functions/release/fabricator.js +25 -3
  24. package/lib/emulator/controller.js +7 -3
  25. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  26. package/lib/emulator/functionsEmulator.js +11 -1
  27. package/lib/experiments.js +4 -0
  28. package/lib/extensions/extensionsHelper.js +4 -15
  29. package/lib/extensions/utils.js +1 -12
  30. package/lib/firestore/api.js +25 -11
  31. package/lib/firestore/pretty-print.js +7 -0
  32. package/lib/functional.js +7 -1
  33. package/lib/functions/env.js +19 -15
  34. package/lib/functions/projectConfig.js +25 -2
  35. package/lib/functions/secrets.js +3 -0
  36. package/lib/gcp/cloudfunctionsv2.js +3 -31
  37. package/lib/gcp/cloudscheduler.js +1 -1
  38. package/lib/gcp/cloudsql/cloudsqladmin.js +2 -14
  39. package/lib/gcp/cloudsql/connect.js +3 -2
  40. package/lib/gcp/cloudsql/permissionsSetup.js +23 -16
  41. package/lib/gcp/k8s.js +32 -0
  42. package/lib/gcp/runv2.js +178 -0
  43. package/lib/gemini/fdcExperience.js +5 -3
  44. package/lib/init/features/dataconnect/index.js +266 -162
  45. package/lib/init/features/dataconnect/sdk.js +36 -20
  46. package/lib/init/features/project.js +4 -0
  47. package/lib/management/studio.js +1 -1
  48. package/lib/mcp/tools/core/init.js +7 -6
  49. package/lib/mcp/tools/crashlytics/get_issue_details.js +33 -0
  50. package/lib/mcp/tools/crashlytics/get_sample_crash.js +43 -0
  51. package/lib/mcp/tools/crashlytics/index.js +7 -1
  52. package/lib/mcp/tools/crashlytics/list_top_issues.js +2 -1
  53. package/lib/rtdb.js +1 -1
  54. package/package.json +1 -1
  55. package/schema/firebase-config.json +6 -0
  56. package/lib/extensions/resolveSource.js +0 -24
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.actuate = exports.generateSdkYaml = exports.doSetup = exports.FDC_APP_FOLDER = void 0;
3
+ exports.actuate = exports.generateSdkYaml = exports.doSetup = 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");
@@ -14,14 +14,16 @@ const error_1 = require("../../../error");
14
14
  const lodash_1 = require("lodash");
15
15
  const utils_1 = require("../../../utils");
16
16
  const auth_1 = require("../../../auth");
17
- exports.FDC_APP_FOLDER = "_FDC_APP_FOLDER";
18
- async function doSetup(setup, config) {
19
- const sdkInfo = await askQuestions(setup, config);
17
+ exports.FDC_APP_FOLDER = "FDC_APP_FOLDER";
18
+ exports.FDC_SDK_FRAMEWORKS_ENV = "FDC_SDK_FRAMEWORKS";
19
+ exports.FDC_SDK_PLATFORM_ENV = "FDC_SDK_PLATFORM";
20
+ async function doSetup(setup, config, options) {
21
+ const sdkInfo = await askQuestions(setup, config, options);
20
22
  await actuate(sdkInfo, config);
21
23
  (0, utils_1.logSuccess)(`If you'd like to add more generated SDKs to your app your later, run ${clc.bold("firebase init dataconnect:sdk")} again`);
22
24
  }
23
25
  exports.doSetup = doSetup;
24
- async function askQuestions(setup, config) {
26
+ async function askQuestions(setup, config, options) {
25
27
  var _a;
26
28
  const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(config);
27
29
  const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(setup.projectId || "", config, c.source)));
@@ -39,7 +41,13 @@ async function askQuestions(setup, config) {
39
41
  throw new error_1.FirebaseError(`Your config has no connectors to set up SDKs for. Run ${clc.bold("firebase init dataconnect")} to set up a service and connectors.`);
40
42
  }
41
43
  let appDir = process.env[exports.FDC_APP_FOLDER] || process.cwd();
42
- let targetPlatform = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
44
+ let targetPlatform = (0, utils_1.envOverride)(exports.FDC_SDK_PLATFORM_ENV, (await (0, fileUtils_1.getPlatformFromFolder)(appDir)) || types_1.Platform.NONE);
45
+ if (options.nonInteractive && targetPlatform === types_1.Platform.NONE) {
46
+ throw new error_1.FirebaseError(`In non-interactive mode, the target platform and app directory must be specified using environment variables if they cannot be automatically detected.
47
+ Please set the ${exports.FDC_SDK_PLATFORM_ENV} and ${exports.FDC_APP_FOLDER} environment variables.
48
+ For example:
49
+ ${clc.bold(`${exports.FDC_SDK_PLATFORM_ENV}=WEB ${exports.FDC_APP_FOLDER}=app-dir ${exports.FDC_SDK_FRAMEWORKS_ENV}=react firebase init dataconnect:sdk --non-interactive`)}`);
50
+ }
43
51
  if (targetPlatform === types_1.Platform.NONE && !((_a = process.env[exports.FDC_APP_FOLDER]) === null || _a === void 0 ? void 0 : _a.length)) {
44
52
  appDir = await (0, utils_1.promptForDirectory)({
45
53
  config,
@@ -74,16 +82,24 @@ async function askQuestions(setup, config) {
74
82
  if (targetPlatform === types_1.Platform.WEB) {
75
83
  const unusedFrameworks = fileUtils_1.SUPPORTED_FRAMEWORKS.filter((framework) => { var _a; return !((_a = newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk[framework]); });
76
84
  if (unusedFrameworks.length > 0) {
77
- const additionalFrameworks = await (0, prompt_1.checkbox)({
78
- message: "Which frameworks would you like to generate SDKs for in addition to the TypeScript SDK? Press Enter to skip.\n",
79
- choices: fileUtils_1.SUPPORTED_FRAMEWORKS.map((frameworkStr) => {
80
- var _a, _b;
81
- return ({
82
- value: frameworkStr,
83
- checked: (_b = (_a = newConnectorYaml === null || newConnectorYaml === void 0 ? void 0 : newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) === null || _b === void 0 ? void 0 : _b[frameworkStr],
84
- });
85
- }),
86
- });
85
+ let additionalFrameworks = [];
86
+ if (options.nonInteractive) {
87
+ additionalFrameworks = (0, utils_1.envOverride)(exports.FDC_SDK_FRAMEWORKS_ENV, "")
88
+ .split(",")
89
+ .filter((f) => f);
90
+ }
91
+ else {
92
+ additionalFrameworks = await (0, prompt_1.checkbox)({
93
+ message: "Which frameworks would you like to generate SDKs for in addition to the TypeScript SDK? Press Enter to skip.\n",
94
+ choices: fileUtils_1.SUPPORTED_FRAMEWORKS.map((frameworkStr) => {
95
+ var _a, _b;
96
+ return ({
97
+ value: frameworkStr,
98
+ checked: (_b = (_a = newConnectorYaml === null || newConnectorYaml === void 0 ? void 0 : newConnectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) === null || _b === void 0 ? void 0 : _b[frameworkStr],
99
+ });
100
+ }),
101
+ });
102
+ }
87
103
  for (const framework of additionalFrameworks) {
88
104
  newConnectorYaml.generate.javascriptSdk[framework] = true;
89
105
  }
@@ -119,7 +135,7 @@ async function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appD
119
135
  if (targetPlatform === types_1.Platform.IOS) {
120
136
  const swiftSdk = {
121
137
  outputDir: path.relative(connectorDir, path.join(appDir, `dataconnect-generated/swift`)),
122
- package: (0, lodash_1.upperFirst)((0, lodash_1.camelCase)(connectorYaml.connectorId)) + "Connector",
138
+ package: "DataConnectGenerated",
123
139
  };
124
140
  connectorYaml.generate.swiftSdk = swiftSdk;
125
141
  }
@@ -128,7 +144,7 @@ async function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appD
128
144
  const packageJsonDir = path.relative(connectorDir, appDir);
129
145
  const javascriptSdk = {
130
146
  outputDir: path.relative(connectorDir, path.join(appDir, `dataconnect-generated/js/${pkg}`)),
131
- package: `@firebasegen/${pkg}`,
147
+ package: `@dataconnect/generated`,
132
148
  packageJsonDir,
133
149
  };
134
150
  const packageJson = await (0, fileUtils_1.resolvePackageJson)(appDir);
@@ -145,14 +161,14 @@ async function generateSdkYaml(targetPlatform, connectorYaml, connectorDir, appD
145
161
  const pkg = `${(0, lodash_1.snakeCase)(connectorYaml.connectorId)}_connector`;
146
162
  const dartSdk = {
147
163
  outputDir: path.relative(connectorDir, path.join(appDir, `dataconnect-generated/dart/${pkg}`)),
148
- package: pkg,
164
+ package: "dataconnect_generated",
149
165
  };
150
166
  connectorYaml.generate.dartSdk = dartSdk;
151
167
  }
152
168
  if (targetPlatform === types_1.Platform.ANDROID) {
153
169
  const kotlinSdk = {
154
170
  outputDir: path.relative(connectorDir, path.join(appDir, `dataconnect-generated/kotlin`)),
155
- package: `connectors.${(0, lodash_1.snakeCase)(connectorYaml.connectorId)}`,
171
+ package: `com.google.firebase.dataconnect.generated`,
156
172
  };
157
173
  for (const candidateSubdir of ["app/src/main/java", "app/src/main/kotlin"]) {
158
174
  const candidateDir = path.join(appDir, candidateSubdir);
@@ -82,6 +82,10 @@ async function doSetup(setup, config, options) {
82
82
  projectMetaData = await (0, projects_1.getFirebaseProject)(projectEnvVar);
83
83
  }
84
84
  else {
85
+ if (options.nonInteractive) {
86
+ logger_1.logger.info("No default project found. Continuing without a project in non interactive mode.");
87
+ return;
88
+ }
85
89
  projectMetaData = await projectChoicePrompt(options);
86
90
  if (!projectMetaData) {
87
91
  return;
@@ -110,7 +110,7 @@ async function updateStudioFirebaseProject(projectId) {
110
110
  if (err.original) {
111
111
  message += ` (original: ${err.original.message})`;
112
112
  }
113
- logger_1.logger.warn(`Failed to update active Firebase Project for current Studio Workspace: ${message}`);
113
+ logger_1.logger.debug(`Failed to update active Firebase Project for current Studio Workspace: ${message}`);
114
114
  }
115
115
  recordStudioProjectSyncTime();
116
116
  }
@@ -54,6 +54,10 @@ exports.init = (0, tool_1.tool)({
54
54
  .describe("Provide this object to initialize Cloud Firestore in this project directory."),
55
55
  dataconnect: zod_1.z
56
56
  .object({
57
+ app_description: zod_1.z
58
+ .string()
59
+ .optional()
60
+ .describe("Provide a description of the app you are trying to build. If present, Gemini will help generate Data Connect Schema, Connector and seed data"),
57
61
  service_id: zod_1.z
58
62
  .string()
59
63
  .optional()
@@ -74,7 +78,7 @@ exports.init = (0, tool_1.tool)({
74
78
  .describe("The Postgres database ID to use in the Firebase Data Connect service."),
75
79
  })
76
80
  .optional()
77
- .describe("Provide this object to initialize Firebase Data Connect in this project directory."),
81
+ .describe("Provide this object to initialize Firebase Data Connect with Cloud SQL Postgres in this project directory."),
78
82
  storage: zod_1.z
79
83
  .object({
80
84
  rules_filename: zod_1.z
@@ -127,15 +131,12 @@ exports.init = (0, tool_1.tool)({
127
131
  if (features.dataconnect) {
128
132
  featuresList.push("dataconnect");
129
133
  featureInfo.dataconnect = {
134
+ analyticsFlow: "mcp",
135
+ appDescription: features.dataconnect.app_description || "",
130
136
  serviceId: features.dataconnect.service_id || "",
131
137
  locationId: features.dataconnect.location_id || "",
132
138
  cloudSqlInstanceId: features.dataconnect.cloudsql_instance_id || "",
133
139
  cloudSqlDatabase: features.dataconnect.cloudsql_database || "",
134
- connectors: [],
135
- isNewInstance: false,
136
- isNewDatabase: false,
137
- schemaGql: [],
138
- shouldProvisionCSQL: true,
139
140
  };
140
141
  }
141
142
  const setup = {
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_issue_details = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const util_1 = require("../../util");
7
+ const getIssueDetails_1 = require("../../../crashlytics/getIssueDetails");
8
+ exports.get_issue_details = (0, tool_1.tool)({
9
+ name: "get_issue_details",
10
+ description: "Gets the details about a specific crashlytics issue.",
11
+ inputSchema: zod_1.z.object({
12
+ app_id: zod_1.z
13
+ .string()
14
+ .describe("The AppID for which the issues list should be fetched. For an Android application, read the mobilesdk_app_id value specified in the google-services.json file for the current package name. For an iOS Application, read the GOOGLE_APP_ID from GoogleService-Info.plist. If neither is available, use the `firebase_list_apps` tool to find an app_id to pass to this tool."),
15
+ issue_id: zod_1.z
16
+ .string()
17
+ .describe("The issue ID for which the details needs to be fetched. This is the value of the field `id` in the list of issues. Defaults to the first id in the list of issues."),
18
+ }),
19
+ annotations: {
20
+ title: "Gets the details of a specific issue.",
21
+ readOnlyHint: true,
22
+ },
23
+ _meta: {
24
+ requiresAuth: true,
25
+ requiresProject: false,
26
+ },
27
+ }, async ({ app_id, issue_id }) => {
28
+ if (!app_id)
29
+ return (0, util_1.mcpError)(`Must specify 'app_id' parameter.`);
30
+ if (!issue_id)
31
+ return (0, util_1.mcpError)(`Must specify 'issue_id' parameter.`);
32
+ return (0, util_1.toContent)(await (0, getIssueDetails_1.getIssueDetails)(app_id, issue_id));
33
+ });
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_sample_crash = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const util_1 = require("../../util");
7
+ const getSampleCrash_1 = require("../../../crashlytics/getSampleCrash");
8
+ exports.get_sample_crash = (0, tool_1.tool)({
9
+ name: "get_sample_crash_for_issue",
10
+ description: "Gets the sample crash for an issue.",
11
+ inputSchema: zod_1.z.object({
12
+ app_id: zod_1.z
13
+ .string()
14
+ .describe("AppId for which the issues list should be fetched. For an Android application, read the mobilesdk_app_id value specified in the google-services.json file for the current package name. For an iOS Application, read the GOOGLE_APP_ID from GoogleService-Info.plist. If neither is available, use the `firebase_list_apps` tool to find an app_id to pass to this tool."),
15
+ issue_id: zod_1.z
16
+ .string()
17
+ .describe("The issue Id for which the sample crash needs to be fetched. This is the value of the field `id` in the list of issues. Defaults to the first id in the list of issues."),
18
+ variant_id: zod_1.z
19
+ .string()
20
+ .optional()
21
+ .describe("The issue variant Id used as a filter to get sample issues."),
22
+ sample_count: zod_1.z
23
+ .number()
24
+ .describe("Number of samples that needs to be fetched. Maximum value is 3. Defaults to 1.")
25
+ .default(1),
26
+ }),
27
+ annotations: {
28
+ title: "Gets a sample of a crash for a specific issue.",
29
+ readOnlyHint: true,
30
+ },
31
+ _meta: {
32
+ requiresAuth: true,
33
+ requiresProject: false,
34
+ },
35
+ }, async ({ app_id, issue_id, variant_id, sample_count }) => {
36
+ if (!app_id)
37
+ return (0, util_1.mcpError)(`Must specify 'app_id' parameter.`);
38
+ if (!issue_id)
39
+ return (0, util_1.mcpError)(`Must specify 'issue_id' parameter.`);
40
+ if (sample_count > 3)
41
+ sample_count = 3;
42
+ return (0, util_1.toContent)(await (0, getSampleCrash_1.getSampleCrash)(app_id, issue_id, sample_count, variant_id));
43
+ });
@@ -1,5 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.crashlyticsTools = void 0;
4
+ const get_issue_details_1 = require("./get_issue_details");
4
5
  const list_top_issues_1 = require("./list_top_issues");
5
- exports.crashlyticsTools = [list_top_issues_1.list_top_issues];
6
+ const get_sample_crash_1 = require("./get_sample_crash");
7
+ exports.crashlyticsTools = [
8
+ list_top_issues_1.list_top_issues,
9
+ get_issue_details_1.get_issue_details,
10
+ get_sample_crash_1.get_sample_crash,
11
+ ];
@@ -16,7 +16,8 @@ exports.list_top_issues = (0, tool_1.tool)({
16
16
  issue_count: zod_1.z
17
17
  .number()
18
18
  .optional()
19
- .describe("Number of issues that needs to be fetched. Defaults to 10 if unspecified."),
19
+ .describe("Number of issues that needs to be fetched. Defaults to 10 if unspecified.")
20
+ .default(10),
20
21
  issue_type: zod_1.z
21
22
  .enum(["FATAL", "NON-FATAL", "ANR"])
22
23
  .optional()
package/lib/rtdb.js CHANGED
@@ -14,7 +14,7 @@ async function updateRules(projectId, instance, src, options = {}) {
14
14
  }
15
15
  const origin = utils.getDatabaseUrl((0, api_1.realtimeOriginOrCustomUrl)(downstreamOptions.instanceDetails.databaseUrl), instance, "");
16
16
  const client = new apiv2_1.Client({ urlPrefix: origin });
17
- return updateRulesWithClient(client, options);
17
+ return updateRulesWithClient(client, src, options);
18
18
  }
19
19
  exports.updateRules = updateRules;
20
20
  async function updateRulesWithClient(client, src, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.12.1",
3
+ "version": "14.14.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -247,6 +247,9 @@
247
247
  "codebase": {
248
248
  "type": "string"
249
249
  },
250
+ "configDir": {
251
+ "type": "string"
252
+ },
250
253
  "ignore": {
251
254
  "items": {
252
255
  "type": "string"
@@ -279,6 +282,9 @@
279
282
  }
280
283
  ]
281
284
  },
285
+ "prefix": {
286
+ "type": "string"
287
+ },
282
288
  "runtime": {
283
289
  "enum": [
284
290
  "nodejs18",
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getExtensionRegistry = void 0;
4
- const apiv2_1 = require("../apiv2");
5
- const api_1 = require("../api");
6
- const EXTENSIONS_REGISTRY_ENDPOINT = "/extensions.json";
7
- async function getExtensionRegistry(onlyFeatured = false) {
8
- var _a;
9
- const client = new apiv2_1.Client({ urlPrefix: (0, api_1.firebaseExtensionsRegistryOrigin)() });
10
- const res = await client.get(EXTENSIONS_REGISTRY_ENDPOINT);
11
- const extensions = res.body.mods || {};
12
- if (onlyFeatured) {
13
- const featuredList = new Set(((_a = res.body.featured) === null || _a === void 0 ? void 0 : _a.discover) || []);
14
- const filteredExtensions = {};
15
- for (const [name, extension] of Object.entries(extensions)) {
16
- if (featuredList.has(name)) {
17
- filteredExtensions[name] = extension;
18
- }
19
- }
20
- return filteredExtensions;
21
- }
22
- return extensions;
23
- }
24
- exports.getExtensionRegistry = getExtensionRegistry;