firebase-tools 14.14.0 → 14.15.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 (67) hide show
  1. package/lib/apphosting/backend.js +40 -15
  2. package/lib/auth.js +37 -2
  3. package/lib/commands/apphosting-backends-create.js +8 -5
  4. package/lib/commands/dataconnect-sdk-generate.js +3 -5
  5. package/lib/commands/dataconnect-sql-diff.js +2 -2
  6. package/lib/commands/dataconnect-sql-grant.js +2 -2
  7. package/lib/commands/dataconnect-sql-migrate.js +2 -2
  8. package/lib/commands/dataconnect-sql-setup.js +2 -2
  9. package/lib/commands/dataconnect-sql-shell.js +2 -2
  10. package/lib/commands/init.js +3 -0
  11. package/lib/commands/login.js +12 -7
  12. package/lib/crashlytics/addNote.js +27 -0
  13. package/lib/crashlytics/deleteNote.js +23 -0
  14. package/lib/crashlytics/getIssueDetails.js +5 -20
  15. package/lib/crashlytics/getSampleCrash.js +6 -20
  16. package/lib/crashlytics/listNotes.js +29 -0
  17. package/lib/crashlytics/listTopDevices.js +33 -0
  18. package/lib/crashlytics/listTopIssues.js +8 -23
  19. package/lib/crashlytics/listTopOperatingSystems.js +32 -0
  20. package/lib/crashlytics/listTopVersions.js +32 -0
  21. package/lib/crashlytics/updateIssue.js +35 -0
  22. package/lib/crashlytics/utils.js +38 -0
  23. package/lib/dataconnect/appFinder.js +103 -0
  24. package/lib/dataconnect/load.js +105 -6
  25. package/lib/deploy/dataconnect/prepare.js +1 -3
  26. package/lib/emulator/controller.js +2 -2
  27. package/lib/emulator/downloadableEmulatorInfo.json +17 -17
  28. package/lib/init/features/dataconnect/create_app.js +48 -0
  29. package/lib/init/features/dataconnect/index.js +19 -30
  30. package/lib/init/features/dataconnect/sdk.js +218 -161
  31. package/lib/init/features/index.js +3 -2
  32. package/lib/init/index.js +5 -1
  33. package/lib/management/apps.js +3 -3
  34. package/lib/mcp/prompts/core/deploy.js +51 -8
  35. package/lib/mcp/prompts/crashlytics/common.js +10 -0
  36. package/lib/mcp/prompts/crashlytics/fix_issue.js +89 -0
  37. package/lib/mcp/prompts/crashlytics/index.js +6 -0
  38. package/lib/mcp/prompts/crashlytics/prioritize_issues.js +79 -0
  39. package/lib/mcp/prompts/index.js +3 -2
  40. package/lib/mcp/tools/core/init.js +5 -1
  41. package/lib/mcp/tools/crashlytics/add_note.js +32 -0
  42. package/lib/mcp/tools/crashlytics/constants.js +11 -0
  43. package/lib/mcp/tools/crashlytics/delete_note.js +35 -0
  44. package/lib/mcp/tools/crashlytics/get_issue_details.js +2 -4
  45. package/lib/mcp/tools/crashlytics/get_sample_crash.js +4 -4
  46. package/lib/mcp/tools/crashlytics/index.js +16 -2
  47. package/lib/mcp/tools/crashlytics/list_notes.js +37 -0
  48. package/lib/mcp/tools/crashlytics/list_top_devices.js +33 -0
  49. package/lib/mcp/tools/crashlytics/list_top_issues.js +5 -7
  50. package/lib/mcp/tools/crashlytics/list_top_operating_systems.js +33 -0
  51. package/lib/mcp/tools/crashlytics/list_top_versions.js +33 -0
  52. package/lib/mcp/tools/crashlytics/update_issue.js +37 -0
  53. package/lib/mcp/tools/dataconnect/execute_graphql.js +2 -2
  54. package/lib/mcp/tools/dataconnect/execute_graphql_read.js +2 -2
  55. package/lib/mcp/tools/dataconnect/execute_mutation.js +2 -2
  56. package/lib/mcp/tools/dataconnect/execute_query.js +2 -2
  57. package/lib/mcp/tools/dataconnect/generate_operation.js +2 -2
  58. package/lib/mcp/tools/dataconnect/get_connector.js +2 -2
  59. package/lib/mcp/tools/dataconnect/get_schema.js +2 -2
  60. package/lib/utils.js +11 -1
  61. package/package.json +1 -1
  62. package/templates/init/dataconnect/connector.yaml +0 -16
  63. package/templates/init/dataconnect/dataconnect.yaml +2 -1
  64. package/templates/init/dataconnect/mutations.gql +29 -29
  65. package/templates/init/dataconnect/queries.gql +73 -73
  66. package/templates/init/dataconnect/schema.gql +48 -48
  67. package/lib/dataconnect/fileUtils.js +0 -168
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listTopVersions = void 0;
4
+ const logger_1 = require("../logger");
5
+ const error_1 = require("../error");
6
+ const utils_1 = require("./utils");
7
+ async function listTopVersions(appId, versionCount, issueId) {
8
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
9
+ try {
10
+ const queryParams = new URLSearchParams();
11
+ queryParams.set("page_size", `${versionCount}`);
12
+ if (issueId) {
13
+ queryParams.set("filter.issue.id", issueId);
14
+ }
15
+ logger_1.logger.debug(`[mcp][crashlytics] listTopVersions called with appId: ${appId}, versionCount: ${versionCount}, issueId: ${issueId}`);
16
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
17
+ method: "GET",
18
+ headers: {
19
+ "Content-Type": "application/json",
20
+ },
21
+ path: `/projects/${requestProjectNumber}/apps/${appId}/reports/topVersions`,
22
+ queryParams: queryParams,
23
+ timeout: utils_1.TIMEOUT,
24
+ });
25
+ return response.body;
26
+ }
27
+ catch (err) {
28
+ logger_1.logger.debug(err.message);
29
+ throw new error_1.FirebaseError(`Failed to fetch the top versions for the Firebase app id: ${appId}. Error: ${err}.`, { original: err });
30
+ }
31
+ }
32
+ exports.listTopVersions = listTopVersions;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateIssue = exports.IssueState = void 0;
4
+ const logger_1 = require("../logger");
5
+ const error_1 = require("../error");
6
+ const utils_1 = require("./utils");
7
+ var IssueState;
8
+ (function (IssueState) {
9
+ IssueState["OPEN"] = "OPEN";
10
+ IssueState["CLOSED"] = "CLOSED";
11
+ })(IssueState = exports.IssueState || (exports.IssueState = {}));
12
+ async function updateIssue(appId, issueId, state) {
13
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
14
+ try {
15
+ logger_1.logger.debug(`[mcp][crashlytics] updateIssue called with appId: ${appId}, issueId: ${issueId}, state: ${state}`);
16
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
17
+ method: "PATCH",
18
+ headers: {
19
+ "Content-Type": "application/json",
20
+ },
21
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}`,
22
+ queryParams: { updateMask: "state" },
23
+ body: { state },
24
+ timeout: utils_1.TIMEOUT,
25
+ });
26
+ return response.body;
27
+ }
28
+ catch (err) {
29
+ logger_1.logger.debug(err.message);
30
+ throw new error_1.FirebaseError(`Failed to update issue ${issueId} for app ${appId}. Error: ${err}.`, {
31
+ original: err,
32
+ });
33
+ }
34
+ }
35
+ exports.updateIssue = updateIssue;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parsePlatform = exports.parseProjectNumber = exports.PLATFORM_PATH = exports.CRASHLYTICS_API_CLIENT = exports.TIMEOUT = void 0;
4
+ const error_1 = require("../error");
5
+ const apiv2_1 = require("../apiv2");
6
+ const api_1 = require("../api");
7
+ exports.TIMEOUT = 10000;
8
+ exports.CRASHLYTICS_API_CLIENT = new apiv2_1.Client({
9
+ urlPrefix: (0, api_1.crashlyticsApiOrigin)(),
10
+ apiVersion: "v1alpha",
11
+ });
12
+ var PLATFORM_PATH;
13
+ (function (PLATFORM_PATH) {
14
+ PLATFORM_PATH["ANDROID"] = "topAndroidDevices";
15
+ PLATFORM_PATH["IOS"] = "topAppleDevices";
16
+ })(PLATFORM_PATH = exports.PLATFORM_PATH || (exports.PLATFORM_PATH = {}));
17
+ function parseProjectNumber(appId) {
18
+ const appIdParts = appId.split(":");
19
+ if (appIdParts.length > 1) {
20
+ return appIdParts[1];
21
+ }
22
+ throw new error_1.FirebaseError("Unable to get the projectId from the AppId.");
23
+ }
24
+ exports.parseProjectNumber = parseProjectNumber;
25
+ function parsePlatform(appId) {
26
+ const appIdParts = appId.split(":");
27
+ if (appIdParts.length < 3) {
28
+ throw new error_1.FirebaseError("Unable to get the platform from the AppId.");
29
+ }
30
+ if (appIdParts[2] === "android") {
31
+ return PLATFORM_PATH.ANDROID;
32
+ }
33
+ else if (appIdParts[2] === "ios") {
34
+ return PLATFORM_PATH.IOS;
35
+ }
36
+ throw new error_1.FirebaseError(`Only android or ios apps are supported.`);
37
+ }
38
+ exports.parsePlatform = parsePlatform;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFrameworksFromPackageJson = exports.WEB_FRAMEWORKS_SIGNALS = exports.WEB_FRAMEWORKS = exports.isPathInside = exports.detectApps = exports.getPlatformFromFolder = exports.appDescription = void 0;
4
+ const fs = require("fs-extra");
5
+ const path = require("path");
6
+ const glob_1 = require("glob");
7
+ const types_1 = require("./types");
8
+ function appDescription(a) {
9
+ return `${a.directory} (${a.platform.toLowerCase()})`;
10
+ }
11
+ exports.appDescription = appDescription;
12
+ async function getPlatformFromFolder(dirPath) {
13
+ const apps = await detectApps(dirPath);
14
+ const hasWeb = apps.some((app) => app.platform === types_1.Platform.WEB);
15
+ const hasAndroid = apps.some((app) => app.platform === types_1.Platform.ANDROID);
16
+ const hasIOS = apps.some((app) => app.platform === types_1.Platform.IOS);
17
+ const hasDart = apps.some((app) => app.platform === types_1.Platform.FLUTTER);
18
+ if (!hasWeb && !hasAndroid && !hasIOS && !hasDart) {
19
+ return types_1.Platform.NONE;
20
+ }
21
+ else if (hasWeb && !hasAndroid && !hasIOS && !hasDart) {
22
+ return types_1.Platform.WEB;
23
+ }
24
+ else if (hasAndroid && !hasWeb && !hasIOS && !hasDart) {
25
+ return types_1.Platform.ANDROID;
26
+ }
27
+ else if (hasIOS && !hasWeb && !hasAndroid && !hasDart) {
28
+ return types_1.Platform.IOS;
29
+ }
30
+ else if (hasDart && !hasWeb && !hasIOS && !hasAndroid) {
31
+ return types_1.Platform.FLUTTER;
32
+ }
33
+ return types_1.Platform.MULTIPLE;
34
+ }
35
+ exports.getPlatformFromFolder = getPlatformFromFolder;
36
+ async function detectApps(dirPath) {
37
+ const packageJsonFiles = await detectFiles(dirPath, "package.json");
38
+ const pubSpecYamlFiles = await detectFiles(dirPath, "pubspec.yaml");
39
+ const srcMainFolders = await detectFiles(dirPath, "src/main/");
40
+ const xCodeProjects = await detectFiles(dirPath, "*.xcodeproj/");
41
+ const webApps = await Promise.all(packageJsonFiles.map((p) => packageJsonToWebApp(dirPath, p)));
42
+ const flutterApps = pubSpecYamlFiles.map((f) => ({
43
+ platform: types_1.Platform.FLUTTER,
44
+ directory: path.dirname(f),
45
+ }));
46
+ const androidApps = srcMainFolders
47
+ .map((f) => ({
48
+ platform: types_1.Platform.ANDROID,
49
+ directory: path.dirname(path.dirname(f)),
50
+ }))
51
+ .filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
52
+ const iosApps = xCodeProjects
53
+ .map((f) => ({
54
+ platform: types_1.Platform.IOS,
55
+ directory: path.dirname(f),
56
+ }))
57
+ .filter((a) => !flutterApps.some((f) => isPathInside(f.directory, a.directory)));
58
+ return [...webApps, ...flutterApps, ...androidApps, ...iosApps];
59
+ }
60
+ exports.detectApps = detectApps;
61
+ function isPathInside(parent, child) {
62
+ const relativePath = path.relative(parent, child);
63
+ return !relativePath.startsWith(`..`);
64
+ }
65
+ exports.isPathInside = isPathInside;
66
+ async function packageJsonToWebApp(dirPath, packageJsonFile) {
67
+ const fullPath = path.join(dirPath, packageJsonFile);
68
+ const packageJson = JSON.parse((await fs.readFile(fullPath)).toString());
69
+ return {
70
+ platform: types_1.Platform.WEB,
71
+ directory: path.dirname(packageJsonFile),
72
+ frameworks: getFrameworksFromPackageJson(packageJson),
73
+ };
74
+ }
75
+ exports.WEB_FRAMEWORKS = ["react", "angular"];
76
+ exports.WEB_FRAMEWORKS_SIGNALS = {
77
+ react: ["react", "next"],
78
+ angular: ["@angular/core"],
79
+ };
80
+ function getFrameworksFromPackageJson(packageJson) {
81
+ var _a, _b;
82
+ const devDependencies = Object.keys((_a = packageJson.devDependencies) !== null && _a !== void 0 ? _a : {});
83
+ const dependencies = Object.keys((_b = packageJson.dependencies) !== null && _b !== void 0 ? _b : {});
84
+ const allDeps = Array.from(new Set([...devDependencies, ...dependencies]));
85
+ return exports.WEB_FRAMEWORKS.filter((framework) => exports.WEB_FRAMEWORKS_SIGNALS[framework].find((dep) => allDeps.includes(dep)));
86
+ }
87
+ exports.getFrameworksFromPackageJson = getFrameworksFromPackageJson;
88
+ async function detectFiles(dirPath, filePattern) {
89
+ const options = {
90
+ cwd: dirPath,
91
+ ignore: [
92
+ "**/dataconnect*/**",
93
+ "**/node_modules/**",
94
+ "**/dist/**",
95
+ "**/build/**",
96
+ "**/out/**",
97
+ "**/.next/**",
98
+ "**/coverage/**",
99
+ ],
100
+ absolute: false,
101
+ };
102
+ return (0, glob_1.glob)(`**/${filePattern}`, options);
103
+ }
@@ -1,19 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.load = void 0;
3
+ exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = exports.load = exports.loadAll = exports.pickService = void 0;
4
4
  const path = require("path");
5
- const fileUtils = require("./fileUtils");
5
+ const fs = require("fs-extra");
6
+ const clc = require("colorette");
7
+ const glob_1 = require("glob");
8
+ const error_1 = require("../error");
6
9
  const types_1 = require("./types");
10
+ const utils_1 = require("../utils");
11
+ async function pickService(projectId, config, serviceId) {
12
+ const serviceInfos = await loadAll(projectId, config);
13
+ if (serviceInfos.length === 0) {
14
+ throw new error_1.FirebaseError("No Data Connect services found in firebase.json." +
15
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add a Data Connect service.`);
16
+ }
17
+ else if (serviceInfos.length === 1) {
18
+ if (serviceId && serviceId !== serviceInfos[0].dataConnectYaml.serviceId) {
19
+ throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${serviceInfos[0].dataConnectYaml.serviceId}.` +
20
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
21
+ }
22
+ return serviceInfos[0];
23
+ }
24
+ else {
25
+ if (!serviceId) {
26
+ throw new error_1.FirebaseError("Multiple Data Connect services found in firebase.json. Please specify a service ID to use.");
27
+ }
28
+ const maybe = serviceInfos.find((i) => i.dataConnectYaml.serviceId === serviceId);
29
+ if (!maybe) {
30
+ const serviceIds = serviceInfos.map((i) => i.dataConnectYaml.serviceId);
31
+ throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${serviceIds.join(", ")}.` +
32
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
33
+ }
34
+ return maybe;
35
+ }
36
+ }
37
+ exports.pickService = pickService;
38
+ async function loadAll(projectId, config) {
39
+ const serviceCfgs = readFirebaseJson(config);
40
+ return await Promise.all(serviceCfgs.map((c) => load(projectId, config, c.source)));
41
+ }
42
+ exports.loadAll = loadAll;
7
43
  async function load(projectId, config, sourceDirectory) {
8
44
  const resolvedDir = config.path(sourceDirectory);
9
- const dataConnectYaml = await fileUtils.readDataConnectYaml(resolvedDir);
45
+ const dataConnectYaml = await readDataConnectYaml(resolvedDir);
10
46
  const serviceName = `projects/${projectId}/locations/${dataConnectYaml.location}/services/${dataConnectYaml.serviceId}`;
11
47
  const schemaDir = path.join(resolvedDir, dataConnectYaml.schema.source);
12
- const schemaGQLs = await fileUtils.readGQLFiles(schemaDir);
48
+ const schemaGQLs = await readGQLFiles(schemaDir);
13
49
  const connectorInfo = await Promise.all(dataConnectYaml.connectorDirs.map(async (dir) => {
14
50
  const connectorDir = path.join(resolvedDir, dir);
15
- const connectorYaml = await fileUtils.readConnectorYaml(connectorDir);
16
- const connectorGqls = await fileUtils.readGQLFiles(connectorDir);
51
+ const connectorYaml = await readConnectorYaml(connectorDir);
52
+ const connectorGqls = await readGQLFiles(connectorDir);
17
53
  return {
18
54
  directory: connectorDir,
19
55
  connectorYaml,
@@ -42,3 +78,66 @@ async function load(projectId, config, sourceDirectory) {
42
78
  };
43
79
  }
44
80
  exports.load = load;
81
+ function readFirebaseJson(config) {
82
+ if (!(config === null || config === void 0 ? void 0 : config.has("dataconnect"))) {
83
+ return [];
84
+ }
85
+ const validator = (cfg) => {
86
+ if (!cfg["source"]) {
87
+ throw new error_1.FirebaseError("Invalid firebase.json: DataConnect requires `source`");
88
+ }
89
+ return {
90
+ source: cfg["source"],
91
+ };
92
+ };
93
+ const configs = config.get("dataconnect");
94
+ if (typeof configs === "object" && !Array.isArray(configs)) {
95
+ return [validator(configs)];
96
+ }
97
+ else if (Array.isArray(configs)) {
98
+ return configs.map(validator);
99
+ }
100
+ else {
101
+ throw new error_1.FirebaseError("Invalid firebase.json: dataconnect should be of the form { source: string }");
102
+ }
103
+ }
104
+ exports.readFirebaseJson = readFirebaseJson;
105
+ async function readDataConnectYaml(sourceDirectory) {
106
+ const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "dataconnect.yaml");
107
+ const dataconnectYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
108
+ return validateDataConnectYaml(dataconnectYaml);
109
+ }
110
+ exports.readDataConnectYaml = readDataConnectYaml;
111
+ function validateDataConnectYaml(unvalidated) {
112
+ if (!unvalidated["location"]) {
113
+ throw new error_1.FirebaseError("Missing required field 'location' in dataconnect.yaml");
114
+ }
115
+ return unvalidated;
116
+ }
117
+ async function readConnectorYaml(sourceDirectory) {
118
+ const file = await (0, utils_1.readFileFromDirectory)(sourceDirectory, "connector.yaml");
119
+ const connectorYaml = await (0, utils_1.wrappedSafeLoad)(file.source);
120
+ return validateConnectorYaml(connectorYaml);
121
+ }
122
+ exports.readConnectorYaml = readConnectorYaml;
123
+ function validateConnectorYaml(unvalidated) {
124
+ return unvalidated;
125
+ }
126
+ async function readGQLFiles(sourceDir) {
127
+ if (!fs.existsSync(sourceDir)) {
128
+ return [];
129
+ }
130
+ const files = await (0, glob_1.glob)("**/*.{gql,graphql}", { cwd: sourceDir, absolute: true, nodir: true });
131
+ return files.map((f) => toFile(sourceDir, f));
132
+ }
133
+ function toFile(sourceDir, fullPath) {
134
+ const relPath = path.relative(sourceDir, fullPath);
135
+ if (!fs.existsSync(fullPath)) {
136
+ throw new error_1.FirebaseError(`file ${fullPath} not found`);
137
+ }
138
+ const content = fs.readFileSync(fullPath).toString();
139
+ return {
140
+ path: relPath,
141
+ content,
142
+ };
143
+ }
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const clc = require("colorette");
4
4
  const load_1 = require("../../dataconnect/load");
5
- const fileUtils_1 = require("../../dataconnect/fileUtils");
6
5
  const logger_1 = require("../../logger");
7
6
  const utils = require("../../utils");
8
7
  const projectUtils_1 = require("../../projectUtils");
@@ -26,9 +25,8 @@ async function default_1(context, options) {
26
25
  }
27
26
  await (0, ensureApis_1.ensureApis)(projectId);
28
27
  await (0, requireTosAcceptance_1.requireTosAcceptance)(firedata_1.DATA_CONNECT_TOS_ID)(options);
29
- const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(options.config);
30
28
  const filters = (0, filters_1.getResourceFilters)(options);
31
- const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, options.config, c.source)));
29
+ const serviceInfos = await (0, load_1.loadAll)(projectId, options.config);
32
30
  for (const si of serviceInfos) {
33
31
  si.deploymentMetadata = await (0, build_1.build)(options, si.sourceDirectory, options.dryRun);
34
32
  }
@@ -44,7 +44,7 @@ const firestoreEmulator_1 = require("./firestoreEmulator");
44
44
  const hostingEmulator_1 = require("./hostingEmulator");
45
45
  const pubsubEmulator_1 = require("./pubsubEmulator");
46
46
  const storage_1 = require("./storage");
47
- const fileUtils_1 = require("../dataconnect/fileUtils");
47
+ const load_1 = require("../dataconnect/load");
48
48
  const tasksEmulator_1 = require("./tasksEmulator");
49
49
  const apphosting_1 = require("./apphosting");
50
50
  const webhook_1 = require("../dataconnect/webhook");
@@ -559,7 +559,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
559
559
  await startEmulator(pubsubEmulator);
560
560
  }
561
561
  if (listenForEmulator.dataconnect) {
562
- const config = (0, fileUtils_1.readFirebaseJson)(options.config);
562
+ const config = (0, load_1.readFirebaseJson)(options.config);
563
563
  if (!config.length) {
564
564
  throw new error_1.FirebaseError("No Data Connect service found in firebase.json");
565
565
  }
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.11.2",
57
+ "version": "2.12.0",
58
58
  "expectedSize": 29447008,
59
- "expectedChecksum": "13bc7d3bb0a0bbfe601991361e4413c2",
60
- "expectedChecksumSHA256": "e3b029eb461f0fe6f0c825c7a71d42c7a09c2b8ee4fac10c3e187d78fe5083f6",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.2",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2"
59
+ "expectedChecksum": "73a09e7f54b642c0232ce89e9a4dd14a",
60
+ "expectedChecksumSHA256": "5464803fec776eac553e1431474f3d4fcc7cc2f4fe43a58e8c1ce9d7ede28acd",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.12.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0"
63
63
  },
64
64
  "win32": {
65
- "version": "2.11.2",
66
- "expectedSize": 29934592,
67
- "expectedChecksum": "032a0749781fc338b446d753dd543bf5",
68
- "expectedChecksumSHA256": "2d0498b3ef94b4e777d6fc1d526279376caf3842549f39f13a8ca327393bf810",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.2",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2.exe"
65
+ "version": "2.12.0",
66
+ "expectedSize": 29936128,
67
+ "expectedChecksum": "b4077d6d298024c9c454b8eb9ad9bf69",
68
+ "expectedChecksumSHA256": "37be096ab8d4d29fa902ca12df97327997b1dc06d4b5026e11aa11d65f183a71",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.12.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.11.2",
74
- "expectedSize": 29368504,
75
- "expectedChecksum": "065a7523f881952040ac678a9a1e9323",
76
- "expectedChecksumSHA256": "41ca6561cf77107b5d1d829233844d21c46a5e4bb823c150b1b32591dc2463e0",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.2",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2"
73
+ "version": "2.12.0",
74
+ "expectedSize": 29372600,
75
+ "expectedChecksum": "04fb5bc9ef194170a5ba0966247eaa79",
76
+ "expectedChecksumSHA256": "42397dd1b23094944aaf20044bc1ec0a5b400c79b536d15803eb4fa6b2bba025",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.12.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0"
79
79
  }
80
80
  }
81
81
  }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNextApp = exports.createReactApp = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const clc = require("colorette");
6
+ const utils_1 = require("../../../utils");
7
+ async function createReactApp(webAppId) {
8
+ const args = ["create", "vite@latest", webAppId, "--", "--template", "react"];
9
+ await executeCommand("npm", args);
10
+ }
11
+ exports.createReactApp = createReactApp;
12
+ async function createNextApp(webAppId) {
13
+ const args = [
14
+ "create-next-app@latest",
15
+ webAppId,
16
+ "--ts",
17
+ "--eslint",
18
+ "--tailwind",
19
+ "--src-dir",
20
+ "--app",
21
+ "--turbopack",
22
+ "--import-alias",
23
+ '"@/*"',
24
+ "--skip-install",
25
+ ];
26
+ await executeCommand("npx", args);
27
+ }
28
+ exports.createNextApp = createNextApp;
29
+ async function executeCommand(command, args) {
30
+ (0, utils_1.logLabeledBullet)("dataconnect", `Running ${clc.bold(`${command} ${args.join(" ")}`)}`);
31
+ return new Promise((resolve, reject) => {
32
+ const childProcess = (0, child_process_1.spawn)(command, args, {
33
+ stdio: "inherit",
34
+ shell: true,
35
+ });
36
+ childProcess.on("close", (code) => {
37
+ if (code === 0) {
38
+ resolve();
39
+ }
40
+ else {
41
+ reject(new Error(`Command failed with exit code ${code}`));
42
+ }
43
+ });
44
+ childProcess.on("error", (err) => {
45
+ reject(err);
46
+ });
47
+ });
48
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toDNSCompatibleId = exports.postSetup = exports.actuate = exports.askQuestions = void 0;
3
+ exports.newUniqueId = exports.toDNSCompatibleId = exports.postSetup = exports.actuate = exports.askQuestions = void 0;
4
4
  const path_1 = require("path");
5
5
  const clc = require("colorette");
6
6
  const fs = require("fs-extra");
@@ -15,9 +15,9 @@ const names_1 = require("../../../dataconnect/names");
15
15
  const logger_1 = require("../../../logger");
16
16
  const templates_1 = require("../../../templates");
17
17
  const utils_1 = require("../../../utils");
18
+ Object.defineProperty(exports, "newUniqueId", { enumerable: true, get: function () { return utils_1.newUniqueId; } });
18
19
  const cloudbilling_1 = require("../../../gcp/cloudbilling");
19
20
  const sdk = require("./sdk");
20
- const fileUtils_1 = require("../../../dataconnect/fileUtils");
21
21
  const fdcExperience_1 = require("../../../gemini/fdcExperience");
22
22
  const configstore_1 = require("../../../configstore");
23
23
  const track_1 = require("../../../track");
@@ -64,7 +64,7 @@ async function askQuestions(setup) {
64
64
  (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");
65
65
  }
66
66
  info.appDescription = await (0, prompt_1.input)({
67
- message: `Describe your app to automatically generate a schema [Enter to skip]:`,
67
+ message: `Describe your app to automatically generate a schema with Gemini [Enter to skip]:`,
68
68
  });
69
69
  if (info.appDescription) {
70
70
  configstore_1.configstore.set("gemini", true);
@@ -77,6 +77,7 @@ async function askQuestions(setup) {
77
77
  }
78
78
  setup.featureInfo = setup.featureInfo || {};
79
79
  setup.featureInfo.dataconnect = info;
80
+ await sdk.askQuestions(setup);
80
81
  }
81
82
  exports.askQuestions = askQuestions;
82
83
  async function actuate(setup, config, options) {
@@ -94,6 +95,7 @@ async function actuate(setup, config, options) {
94
95
  info.cloudSqlDatabase = info.cloudSqlDatabase || `fdcdb`;
95
96
  try {
96
97
  await actuateWithInfo(setup, config, info, options);
98
+ await sdk.actuate(setup, config);
97
99
  }
98
100
  finally {
99
101
  void (0, track_1.trackGA4)("dataconnect_init", {
@@ -220,31 +222,27 @@ function schemasDeploySequence(projectId, info, schemaFiles, linkToCloudSql) {
220
222
  },
221
223
  ];
222
224
  }
223
- async function postSetup(setup, config, options) {
225
+ async function postSetup(setup) {
224
226
  var _a;
225
227
  const info = (_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.dataconnect;
226
228
  if (!info) {
227
229
  throw new Error("Data Connect feature RequiredInfo is not provided");
228
230
  }
229
231
  const instructions = [];
230
- const cwdPlatformGuess = await (0, fileUtils_1.getPlatformFromFolder)(process.cwd());
231
- if (cwdPlatformGuess !== types_1.Platform.NONE || (0, utils_1.envOverride)("FDC_CONNECTOR", "")) {
232
- await sdk.doSetup(setup, config, options);
233
- }
234
- else {
235
- instructions.push(`To add the generated SDK to your app, run ${clc.bold("firebase init dataconnect:sdk")}`);
236
- }
237
232
  if (info.appDescription) {
238
233
  instructions.push(`You can visualize the Data Connect Schema in Firebase Console:
239
234
 
240
235
  https://console.firebase.google.com/project/${setup.projectId}/dataconnect/locations/${info.locationId}/services/${info.serviceId}/schema`);
241
236
  }
242
- if (setup.projectId && !setup.isBillingEnabled) {
243
- instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId));
237
+ if (!setup.isBillingEnabled) {
238
+ instructions.push((0, freeTrial_1.upgradeInstructions)(setup.projectId || "your-firebase-project"));
244
239
  }
245
- logger_1.logger.info(`\n${clc.bold("To get started with Firebase Data Connect:")}`);
246
- for (const i of instructions) {
247
- (0, utils_1.logBullet)(i + "\n");
240
+ instructions.push(`Install the Data Connect VS Code Extensions. You can explore Data Connect Query on local pgLite and Cloud SQL Postgres Instance.`);
241
+ if (instructions.length) {
242
+ logger_1.logger.info(`\n${clc.bold("To get started with Firebase Data Connect:")}`);
243
+ for (const i of instructions) {
244
+ (0, utils_1.logBullet)(i + "\n");
245
+ }
248
246
  }
249
247
  }
250
248
  exports.postSetup = postSetup;
@@ -271,7 +269,7 @@ async function writeFiles(config, info, serviceGql, options) {
271
269
  async function writeConnectorFiles(config, connectorInfo, options) {
272
270
  const subbedConnectorYaml = subConnectorYamlValues({ connectorId: connectorInfo.id });
273
271
  const dir = config.get("dataconnect.source") || "dataconnect";
274
- await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, "connector.yaml"), subbedConnectorYaml, !!options.force);
272
+ await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, "connector.yaml"), subbedConnectorYaml, !!options.force, true);
275
273
  for (const f of connectorInfo.files) {
276
274
  await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, f.path), f.content, !!options.force);
277
275
  }
@@ -315,7 +313,7 @@ async function promptForExistingServices(setup, info) {
315
313
  const choice = await chooseExistingService(existingServicesAndSchemas);
316
314
  if (!choice) {
317
315
  const existingServiceIds = existingServices.map((s) => s.name.split("/").pop());
318
- info.serviceId = newUniqueId(defaultServiceId(), existingServiceIds);
316
+ info.serviceId = (0, utils_1.newUniqueId)(defaultServiceId(), existingServiceIds);
319
317
  info.analyticsFlow += "_pick_new_service";
320
318
  return;
321
319
  }
@@ -346,7 +344,7 @@ async function promptForExistingServices(setup, info) {
346
344
  const id = c.name.split("/").pop();
347
345
  return {
348
346
  id,
349
- path: connectors.length === 1 ? "./connector" : `./${id}`,
347
+ path: connectors.length === 1 ? "./example" : `./${id}`,
350
348
  files: c.source.files || [],
351
349
  };
352
350
  });
@@ -419,7 +417,7 @@ async function promptForCloudSQL(setup, info) {
419
417
  info.analyticsFlow += "_pick_new_csql";
420
418
  info.cloudSqlInstanceId = await (0, prompt_1.input)({
421
419
  message: `What ID would you like to use for your new CloudSQL instance?`,
422
- default: newUniqueId(`${defaultServiceId().toLowerCase()}-fdc`, instances.map((i) => i.name)),
420
+ default: (0, utils_1.newUniqueId)(`${defaultServiceId().toLowerCase()}-fdc`, instances.map((i) => i.name)),
423
421
  });
424
422
  }
425
423
  }
@@ -436,7 +434,7 @@ async function promptForCloudSQL(setup, info) {
436
434
  try {
437
435
  const dbs = await cloudsql.listDatabases(setup.projectId, info.cloudSqlInstanceId);
438
436
  const existing = dbs.map((d) => d.name);
439
- info.cloudSqlDatabase = newUniqueId("fdcdb", existing);
437
+ info.cloudSqlDatabase = (0, utils_1.newUniqueId)("fdcdb", existing);
440
438
  }
441
439
  catch (err) {
442
440
  logger_1.logger.debug(`[dataconnect] Cannot list databases during init: ${err}`);
@@ -464,15 +462,6 @@ async function locationChoices(setup) {
464
462
  ];
465
463
  }
466
464
  }
467
- function newUniqueId(recommended, existingIDs) {
468
- let id = recommended;
469
- let i = 1;
470
- while (existingIDs.includes(id)) {
471
- id = `${recommended}-${i}`;
472
- i++;
473
- }
474
- return id;
475
- }
476
465
  function defaultServiceId() {
477
466
  return toDNSCompatibleId((0, path_1.basename)(process.cwd()));
478
467
  }