firebase-tools 14.14.0 → 14.15.1
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/apphosting/backend.js +40 -15
- package/lib/auth.js +37 -2
- package/lib/command.js +19 -5
- package/lib/commands/apphosting-backends-create.js +8 -5
- package/lib/commands/dataconnect-sdk-generate.js +3 -5
- package/lib/commands/dataconnect-sql-diff.js +2 -2
- package/lib/commands/dataconnect-sql-grant.js +2 -2
- package/lib/commands/dataconnect-sql-migrate.js +2 -2
- package/lib/commands/dataconnect-sql-setup.js +2 -2
- package/lib/commands/dataconnect-sql-shell.js +2 -2
- package/lib/commands/init.js +11 -0
- package/lib/commands/login.js +12 -7
- package/lib/crashlytics/addNote.js +27 -0
- package/lib/crashlytics/deleteNote.js +23 -0
- package/lib/crashlytics/getIssueDetails.js +5 -20
- package/lib/crashlytics/getSampleCrash.js +6 -20
- package/lib/crashlytics/listNotes.js +29 -0
- package/lib/crashlytics/listTopDevices.js +33 -0
- package/lib/crashlytics/listTopIssues.js +8 -23
- package/lib/crashlytics/listTopOperatingSystems.js +32 -0
- package/lib/crashlytics/listTopVersions.js +32 -0
- package/lib/crashlytics/updateIssue.js +35 -0
- package/lib/crashlytics/utils.js +38 -0
- package/lib/dataconnect/appFinder.js +103 -0
- package/lib/dataconnect/load.js +105 -6
- package/lib/deploy/dataconnect/prepare.js +1 -3
- package/lib/emulator/controller.js +2 -2
- package/lib/emulator/downloadableEmulatorInfo.json +17 -17
- package/lib/experiments.js +5 -0
- package/lib/init/features/dataconnect/create_app.js +48 -0
- package/lib/init/features/dataconnect/index.js +19 -45
- package/lib/init/features/dataconnect/sdk.js +218 -161
- package/lib/init/features/index.js +3 -3
- package/lib/init/index.js +5 -2
- package/lib/management/apps.js +3 -3
- package/lib/mcp/prompts/core/deploy.js +51 -8
- package/lib/mcp/prompts/crashlytics/common.js +10 -0
- package/lib/mcp/prompts/crashlytics/fix_issue.js +89 -0
- package/lib/mcp/prompts/crashlytics/index.js +6 -0
- package/lib/mcp/prompts/crashlytics/prioritize_issues.js +79 -0
- package/lib/mcp/prompts/dataconnect/index.js +9 -0
- package/lib/mcp/prompts/dataconnect/schema.js +68 -0
- package/lib/mcp/prompts/index.js +5 -3
- package/lib/mcp/tools/core/init.js +16 -3
- package/lib/mcp/tools/crashlytics/add_note.js +32 -0
- package/lib/mcp/tools/crashlytics/constants.js +11 -0
- package/lib/mcp/tools/crashlytics/delete_note.js +35 -0
- package/lib/mcp/tools/crashlytics/get_issue_details.js +2 -4
- package/lib/mcp/tools/crashlytics/get_sample_crash.js +4 -4
- package/lib/mcp/tools/crashlytics/index.js +16 -2
- package/lib/mcp/tools/crashlytics/list_notes.js +37 -0
- package/lib/mcp/tools/crashlytics/list_top_devices.js +33 -0
- package/lib/mcp/tools/crashlytics/list_top_issues.js +5 -7
- package/lib/mcp/tools/crashlytics/list_top_operating_systems.js +33 -0
- package/lib/mcp/tools/crashlytics/list_top_versions.js +33 -0
- package/lib/mcp/tools/crashlytics/update_issue.js +37 -0
- package/lib/mcp/tools/dataconnect/compile.js +43 -0
- package/lib/mcp/tools/dataconnect/execute_graphql.js +4 -4
- package/lib/mcp/tools/dataconnect/execute_graphql_read.js +4 -4
- package/lib/mcp/tools/dataconnect/execute_mutation.js +4 -4
- package/lib/mcp/tools/dataconnect/execute_query.js +4 -4
- package/lib/mcp/tools/dataconnect/generate_operation.js +2 -2
- package/lib/mcp/tools/dataconnect/get_connector.js +3 -3
- package/lib/mcp/tools/dataconnect/get_schema.js +3 -3
- package/lib/mcp/tools/dataconnect/index.js +2 -0
- package/lib/mcp/tools/firestore/list_collections.js +0 -3
- package/lib/mcp/tools/index.js +3 -0
- package/lib/mcp/util/dataconnect/compile.js +18 -0
- package/lib/mcp/util/dataconnect/content.js +655 -0
- package/lib/utils.js +11 -1
- package/package.json +1 -1
- package/templates/init/dataconnect/connector.yaml +0 -16
- package/templates/init/dataconnect/dataconnect.yaml +2 -1
- package/templates/init/dataconnect/mutations.gql +29 -29
- package/templates/init/dataconnect/queries.gql +73 -73
- package/templates/init/dataconnect/schema.gql +48 -48
- package/lib/dataconnect/fileUtils.js +0 -168
- /package/lib/mcp/{tools → util}/dataconnect/converter.js +0 -0
- /package/lib/mcp/{tools → util}/dataconnect/emulator.js +0 -0
|
@@ -1,45 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.listTopIssues = void 0;
|
|
4
|
-
const apiv2_1 = require("../apiv2");
|
|
5
4
|
const logger_1 = require("../logger");
|
|
6
5
|
const error_1 = require("../error");
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
urlPrefix: (0, api_1.crashlyticsApiOrigin)(),
|
|
11
|
-
apiVersion: "v1alpha",
|
|
12
|
-
});
|
|
13
|
-
async function listTopIssues(projectId, appId, issueType, issueCount) {
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
async function listTopIssues(appId, issueType, issueCount) {
|
|
8
|
+
const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
|
|
14
9
|
try {
|
|
15
10
|
const queryParams = new URLSearchParams();
|
|
16
11
|
queryParams.set("page_size", `${issueCount}`);
|
|
17
12
|
queryParams.set("filter.issue.error_types", `${issueType}`);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
throw new error_1.FirebaseError("Unable to get the projectId from the AppId.");
|
|
21
|
-
}
|
|
22
|
-
const response = await apiClient.request({
|
|
13
|
+
logger_1.logger.debug(`[mcp][crashlytics] listTopIssues called with appId: ${appId}, issueType: ${issueType}, issueCount: ${issueCount}`);
|
|
14
|
+
const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
|
|
23
15
|
method: "GET",
|
|
24
16
|
headers: {
|
|
25
17
|
"Content-Type": "application/json",
|
|
26
18
|
},
|
|
27
|
-
path: `/projects/${
|
|
19
|
+
path: `/projects/${requestProjectNumber}/apps/${appId}/reports/topIssues`,
|
|
28
20
|
queryParams: queryParams,
|
|
29
|
-
timeout: TIMEOUT,
|
|
21
|
+
timeout: utils_1.TIMEOUT,
|
|
30
22
|
});
|
|
31
23
|
return response.body;
|
|
32
24
|
}
|
|
33
25
|
catch (err) {
|
|
34
26
|
logger_1.logger.debug(err.message);
|
|
35
|
-
throw new error_1.FirebaseError(`Failed to fetch the top issues for the Firebase
|
|
27
|
+
throw new error_1.FirebaseError(`Failed to fetch the top issues for the Firebase app id: ${appId}. Error: ${err}.`, { original: err });
|
|
36
28
|
}
|
|
37
29
|
}
|
|
38
30
|
exports.listTopIssues = listTopIssues;
|
|
39
|
-
function parseProjectId(appId) {
|
|
40
|
-
const appIdParts = appId.split(":");
|
|
41
|
-
if (appIdParts.length > 1) {
|
|
42
|
-
return appIdParts[1];
|
|
43
|
-
}
|
|
44
|
-
return undefined;
|
|
45
|
-
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listTopOperatingSystems = void 0;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const error_1 = require("../error");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
async function listTopOperatingSystems(appId, osCount, issueId) {
|
|
8
|
+
const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
|
|
9
|
+
try {
|
|
10
|
+
const queryParams = new URLSearchParams();
|
|
11
|
+
queryParams.set("page_size", `${osCount}`);
|
|
12
|
+
if (issueId) {
|
|
13
|
+
queryParams.set("filter.issue.id", issueId);
|
|
14
|
+
}
|
|
15
|
+
logger_1.logger.debug(`[mcp][crashlytics] listTopOperatingSystems called with appId: ${appId}, osCount: ${osCount}, 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/topOperatingSystems`,
|
|
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 operating systems for the Firebase app id: ${appId}. Error: ${err}.`, { original: err });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.listTopOperatingSystems = listTopOperatingSystems;
|
|
@@ -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
|
+
}
|
package/lib/dataconnect/load.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
16
|
-
const connectorGqls = await
|
|
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
|
|
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
|
|
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,
|
|
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.
|
|
57
|
+
"version": "2.12.0",
|
|
58
58
|
"expectedSize": 29447008,
|
|
59
|
-
"expectedChecksum": "
|
|
60
|
-
"expectedChecksumSHA256": "
|
|
61
|
-
"remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.
|
|
62
|
-
"downloadPathRelativeToCacheDir": "dataconnect-emulator-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.
|
|
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.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.
|
|
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.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
|
}
|
package/lib/experiments.js
CHANGED
|
@@ -116,6 +116,11 @@ exports.ALL_EXPERIMENTS = experiments({
|
|
|
116
116
|
default: true,
|
|
117
117
|
public: false,
|
|
118
118
|
},
|
|
119
|
+
mcpalpha: {
|
|
120
|
+
shortDescription: "Opt-in to early MCP features before they're widely released.",
|
|
121
|
+
default: false,
|
|
122
|
+
public: true,
|
|
123
|
+
},
|
|
119
124
|
apptesting: {
|
|
120
125
|
shortDescription: "Adds experimental App Testing feature",
|
|
121
126
|
public: true,
|
|
@@ -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
|
+
}
|