firebase-tools 14.11.0 → 14.11.2
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/apptesting/ensureProjectConfigured.js +56 -0
- package/lib/commands/ext-export.js +1 -1
- package/lib/commands/functions-config-clone.js +2 -0
- package/lib/commands/functions-config-get.js +2 -0
- package/lib/commands/functions-config-set.js +2 -0
- package/lib/commands/functions-config-unset.js +2 -0
- package/lib/commands/init.js +1 -1
- package/lib/commands/login.js +2 -2
- package/lib/deploy/functions/params.js +4 -0
- package/lib/deploy/functions/prepare.js +3 -1
- package/lib/deploy/functions/prepareFunctionsUpload.js +2 -0
- package/lib/deploy/functions/release/reporter.js +1 -0
- package/lib/emulator/downloadableEmulatorInfo.json +18 -18
- package/lib/env.js +18 -0
- package/lib/experiments.js +7 -0
- package/lib/firestore/api.js +15 -1
- package/lib/functions/deprecationWarnings.js +21 -0
- package/lib/init/features/aitools/promptUpdater.js +4 -1
- package/lib/init/features/apptesting/index.js +4 -0
- package/lib/mcp/errors.js +7 -1
- package/lib/mcp/index.js +78 -60
- package/lib/mcp/logging-transport.js +23 -0
- package/lib/mcp/tools/apphosting/fetch_logs.js +22 -22
- package/lib/mcp/tools/apphosting/list_backends.js +10 -10
- package/lib/mcp/tools/auth/disable_user.js +7 -7
- package/lib/mcp/tools/auth/get_user.js +6 -6
- package/lib/mcp/tools/auth/index.js +10 -10
- package/lib/mcp/tools/auth/list_users.js +6 -6
- package/lib/mcp/tools/auth/set_claims.js +7 -7
- package/lib/mcp/tools/auth/set_sms_region_policy.js +6 -6
- package/lib/mcp/tools/core/consult_assistant.js +6 -6
- package/lib/mcp/tools/core/create_android_sha.js +9 -9
- package/lib/mcp/tools/core/create_app.js +7 -7
- package/lib/mcp/tools/core/create_project.js +13 -13
- package/lib/mcp/tools/core/get_admin_sdk_config.js +7 -7
- package/lib/mcp/tools/core/get_environment.js +8 -8
- package/lib/mcp/tools/core/get_project.js +5 -5
- package/lib/mcp/tools/core/get_sdk_config.js +9 -9
- package/lib/mcp/tools/core/index.js +24 -24
- package/lib/mcp/tools/core/init.js +8 -8
- package/lib/mcp/tools/core/list_apps.js +6 -6
- package/lib/mcp/tools/core/list_projects.js +6 -6
- package/lib/mcp/tools/core/update_environment.js +10 -10
- package/lib/mcp/tools/crashlytics/index.js +2 -2
- package/lib/mcp/tools/crashlytics/list_top_issues.js +6 -6
- package/lib/mcp/tools/dataconnect/emulator.js +6 -6
- package/lib/mcp/tools/dataconnect/execute_graphql.js +10 -10
- package/lib/mcp/tools/dataconnect/execute_graphql_read.js +10 -10
- package/lib/mcp/tools/dataconnect/execute_mutation.js +13 -13
- package/lib/mcp/tools/dataconnect/execute_query.js +13 -13
- package/lib/mcp/tools/dataconnect/generate_operation.js +8 -8
- package/lib/mcp/tools/dataconnect/generate_schema.js +6 -6
- package/lib/mcp/tools/dataconnect/get_connector.js +8 -8
- package/lib/mcp/tools/dataconnect/get_schema.js +8 -8
- package/lib/mcp/tools/dataconnect/index.js +18 -18
- package/lib/mcp/tools/dataconnect/list_services.js +5 -5
- package/lib/mcp/tools/firestore/delete_document.js +13 -13
- package/lib/mcp/tools/firestore/get_documents.js +13 -13
- package/lib/mcp/tools/firestore/list_collections.js +9 -9
- package/lib/mcp/tools/firestore/query_collection.js +13 -13
- package/lib/mcp/tools/index.js +18 -18
- package/lib/mcp/tools/messaging/index.js +2 -2
- package/lib/mcp/tools/messaging/send_message.js +7 -7
- package/lib/mcp/tools/remoteconfig/get_template.js +5 -5
- package/lib/mcp/tools/remoteconfig/index.js +4 -4
- package/lib/mcp/tools/remoteconfig/publish_template.js +7 -7
- package/lib/mcp/tools/remoteconfig/rollback_template.js +6 -6
- package/lib/mcp/tools/rules/get_rules.js +8 -8
- package/lib/mcp/tools/rules/validate_rules.js +10 -10
- package/lib/mcp/tools/storage/get_download_url.js +8 -8
- package/lib/mcp/tools/storage/get_rules.js +8 -8
- package/lib/mcp/util.js +2 -1
- package/lib/requireAuth.js +9 -3
- package/lib/timeout.js +21 -0
- package/lib/track.js +2 -2
- package/lib/utils.js +7 -2
- package/package.json +1 -1
- package/templates/init/dataconnect/mutations.gql +1 -1
- package/templates/init/dataconnect/queries.gql +3 -3
- package/lib/emulator/adminSdkConfig.test.js +0 -16
- package/lib/firestore/api-sort.test.js +0 -74
- package/lib/firestore/backupUtils.test.js +0 -18
- package/lib/firestore/pretty-print.test.js +0 -61
- package/lib/firestore/util.test.js +0 -42
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.publish_template = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
exports.publish_template = (0,
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const publish_1 = require("../../../remoteconfig/publish");
|
|
8
|
+
exports.publish_template = (0, tool_1.tool)({
|
|
9
9
|
name: "publish_template",
|
|
10
10
|
description: "Publishes a new remote config template for the project",
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
@@ -25,10 +25,10 @@ exports.publish_template = (0, tool_js_1.tool)({
|
|
|
25
25
|
},
|
|
26
26
|
}, async ({ template, force }, { projectId }) => {
|
|
27
27
|
if (template === undefined) {
|
|
28
|
-
return (0,
|
|
28
|
+
return (0, util_1.mcpError)(`No template specified in the publish requests`);
|
|
29
29
|
}
|
|
30
30
|
if (force === undefined) {
|
|
31
|
-
return (0,
|
|
31
|
+
return (0, util_1.toContent)(await (0, publish_1.publishTemplate)(projectId, template));
|
|
32
32
|
}
|
|
33
|
-
return (0,
|
|
33
|
+
return (0, util_1.toContent)(await (0, publish_1.publishTemplate)(projectId, template, { force }));
|
|
34
34
|
});
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.rollback_template = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
exports.rollback_template = (0,
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const rollback_1 = require("../../../remoteconfig/rollback");
|
|
8
|
+
exports.rollback_template = (0, tool_1.tool)({
|
|
9
9
|
name: "rollback_template",
|
|
10
10
|
description: "Rollback to a specific version of Remote Config template for a project",
|
|
11
11
|
inputSchema: zod_1.z.object({
|
|
@@ -23,7 +23,7 @@ exports.rollback_template = (0, tool_js_1.tool)({
|
|
|
23
23
|
},
|
|
24
24
|
}, async ({ version_number }, { projectId }) => {
|
|
25
25
|
if (version_number === undefined) {
|
|
26
|
-
return (0,
|
|
26
|
+
return (0, util_1.mcpError)(`No version number specified in the rollback requests`);
|
|
27
27
|
}
|
|
28
|
-
return (0,
|
|
28
|
+
return (0, util_1.toContent)(await (0, rollback_1.rollbackTemplate)(projectId, version_number));
|
|
29
29
|
});
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRulesTool = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const rules_1 = require("../../../gcp/rules");
|
|
8
8
|
function getRulesTool(productName, releaseName) {
|
|
9
|
-
return (0,
|
|
9
|
+
return (0, tool_1.tool)({
|
|
10
10
|
name: "get_rules",
|
|
11
11
|
description: `Retrieves the active ${productName} security rules for the current project.`,
|
|
12
12
|
inputSchema: zod_1.z.object({}),
|
|
@@ -19,11 +19,11 @@ function getRulesTool(productName, releaseName) {
|
|
|
19
19
|
requiresAuth: true,
|
|
20
20
|
},
|
|
21
21
|
}, async (_, { projectId }) => {
|
|
22
|
-
const rulesetName = await (0,
|
|
22
|
+
const rulesetName = await (0, rules_1.getLatestRulesetName)(projectId, releaseName);
|
|
23
23
|
if (!rulesetName)
|
|
24
|
-
return (0,
|
|
25
|
-
const rules = await (0,
|
|
26
|
-
return (0,
|
|
24
|
+
return (0, util_1.mcpError)(`No active Firestore rules were found in project '${projectId}'`);
|
|
25
|
+
const rules = await (0, rules_1.getRulesetContent)(rulesetName);
|
|
26
|
+
return (0, util_1.toContent)(rules[0].content);
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
exports.getRulesTool = getRulesTool;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validateRulesTool = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const rules_1 = require("../../../gcp/rules");
|
|
8
8
|
const path_1 = require("path");
|
|
9
9
|
function formatRulesetIssues(issues, rulesSource) {
|
|
10
10
|
const sourceLines = rulesSource.split("\n");
|
|
@@ -39,7 +39,7 @@ function formatRulesetIssues(issues, rulesSource) {
|
|
|
39
39
|
return formattedOutput.join("\n\n");
|
|
40
40
|
}
|
|
41
41
|
function validateRulesTool(productName) {
|
|
42
|
-
return (0,
|
|
42
|
+
return (0, tool_1.tool)({
|
|
43
43
|
name: "validate_rules",
|
|
44
44
|
description: `Checks the provided ${productName} Rules source for syntax and validation errors. Provide EITHER the source code to validate OR a path to a source file.`,
|
|
45
45
|
inputSchema: zod_1.z.object({
|
|
@@ -63,18 +63,18 @@ function validateRulesTool(productName) {
|
|
|
63
63
|
}, async ({ source, source_file }, { projectId, config, host }) => {
|
|
64
64
|
var _a, _b;
|
|
65
65
|
if (source && source_file) {
|
|
66
|
-
return (0,
|
|
66
|
+
return (0, util_1.mcpError)("Must supply `source` or `source_file`, not both.");
|
|
67
67
|
}
|
|
68
68
|
let rulesSourceContent;
|
|
69
69
|
if (source_file) {
|
|
70
70
|
try {
|
|
71
71
|
const filePath = (0, path_1.resolve)(source_file, host.cachedProjectRoot);
|
|
72
72
|
if (filePath.includes("../"))
|
|
73
|
-
return (0,
|
|
73
|
+
return (0, util_1.mcpError)("Cannot read files outside of the project directory.");
|
|
74
74
|
rulesSourceContent = config.readProjectFile(source_file);
|
|
75
75
|
}
|
|
76
76
|
catch (e) {
|
|
77
|
-
return (0,
|
|
77
|
+
return (0, util_1.mcpError)(`Failed to read source_file '${source_file}': ${e.message}`);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
else if (source) {
|
|
@@ -83,16 +83,16 @@ function validateRulesTool(productName) {
|
|
|
83
83
|
else {
|
|
84
84
|
rulesSourceContent = "";
|
|
85
85
|
}
|
|
86
|
-
const result = await (0,
|
|
86
|
+
const result = await (0, rules_1.testRuleset)(projectId, [
|
|
87
87
|
{ name: "test.rules", content: rulesSourceContent },
|
|
88
88
|
]);
|
|
89
89
|
if ((_b = (_a = result.body) === null || _a === void 0 ? void 0 : _a.issues) === null || _b === void 0 ? void 0 : _b.length) {
|
|
90
90
|
const issues = result.body.issues;
|
|
91
91
|
let out = `Found ${issues.length} issues in rules source:\n\n`;
|
|
92
92
|
out += formatRulesetIssues(issues, rulesSourceContent);
|
|
93
|
-
return (0,
|
|
93
|
+
return (0, util_1.toContent)(out);
|
|
94
94
|
}
|
|
95
|
-
return (0,
|
|
95
|
+
return (0, util_1.toContent)("OK: No errors detected.");
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
exports.validateRulesTool = validateRulesTool;
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.get_object_download_url = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
exports.get_object_download_url = (0,
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const storage_1 = require("../../../gcp/storage");
|
|
8
|
+
const types_1 = require("../../../emulator/types");
|
|
9
|
+
exports.get_object_download_url = (0, tool_1.tool)({
|
|
10
10
|
name: "get_object_download_url",
|
|
11
11
|
description: "Retrieves the download URL for an object in Firebase Storage.",
|
|
12
12
|
inputSchema: zod_1.z.object({
|
|
@@ -33,8 +33,8 @@ exports.get_object_download_url = (0, tool_js_1.tool)({
|
|
|
33
33
|
}
|
|
34
34
|
let emulatorUrl;
|
|
35
35
|
if (use_emulator) {
|
|
36
|
-
emulatorUrl = await host.getEmulatorUrl(
|
|
36
|
+
emulatorUrl = await host.getEmulatorUrl(types_1.Emulators.STORAGE);
|
|
37
37
|
}
|
|
38
|
-
const downloadUrl = await (0,
|
|
39
|
-
return (0,
|
|
38
|
+
const downloadUrl = await (0, storage_1.getDownloadUrl)(bucket, object_path, emulatorUrl);
|
|
39
|
+
return (0, util_1.toContent)(downloadUrl);
|
|
40
40
|
});
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.get_rules = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
exports.get_rules = (0,
|
|
5
|
+
const tool_1 = require("../../tool");
|
|
6
|
+
const util_1 = require("../../util");
|
|
7
|
+
const rules_1 = require("../../../gcp/rules");
|
|
8
|
+
exports.get_rules = (0, tool_1.tool)({
|
|
9
9
|
name: "get_rules",
|
|
10
10
|
description: "Retrieves the Firebase Cloud Storage Rules for the default bucket.",
|
|
11
11
|
inputSchema: zod_1.z.object({}),
|
|
@@ -18,9 +18,9 @@ exports.get_rules = (0, tool_js_1.tool)({
|
|
|
18
18
|
requiresAuth: true,
|
|
19
19
|
},
|
|
20
20
|
}, async (_, { projectId }) => {
|
|
21
|
-
const rulesetName = await (0,
|
|
21
|
+
const rulesetName = await (0, rules_1.getLatestRulesetName)(projectId, "firebase.storage");
|
|
22
22
|
if (!rulesetName)
|
|
23
|
-
return (0,
|
|
24
|
-
const rules = await (0,
|
|
25
|
-
return (0,
|
|
23
|
+
return (0, util_1.mcpError)(`No active Firebase Storage rules were found in project '${projectId}'`);
|
|
24
|
+
const rules = await (0, rules_1.getRulesetContent)(rulesetName);
|
|
25
|
+
return (0, util_1.toContent)(rules[0].content);
|
|
26
26
|
});
|
package/lib/mcp/util.js
CHANGED
|
@@ -6,6 +6,7 @@ const js_yaml_1 = require("js-yaml");
|
|
|
6
6
|
const os_1 = require("os");
|
|
7
7
|
const api_1 = require("../api");
|
|
8
8
|
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
9
|
+
const timeout_1 = require("../timeout");
|
|
9
10
|
function toContent(data, options) {
|
|
10
11
|
if (typeof data === "string")
|
|
11
12
|
return { content: [{ type: "text", text: data }] };
|
|
@@ -70,7 +71,7 @@ async function checkFeatureActive(feature, projectId, options) {
|
|
|
70
71
|
return true;
|
|
71
72
|
try {
|
|
72
73
|
if (projectId)
|
|
73
|
-
return await (0, ensureApiEnabled_1.check)(projectId, SERVER_FEATURE_APIS[feature], "", true);
|
|
74
|
+
return await (0, timeout_1.timeoutFallback)((0, ensureApiEnabled_1.check)(projectId, SERVER_FEATURE_APIS[feature], "", true), true, 3000);
|
|
74
75
|
}
|
|
75
76
|
catch (e) {
|
|
76
77
|
return true;
|
package/lib/requireAuth.js
CHANGED
|
@@ -10,6 +10,8 @@ const logger_1 = require("./logger");
|
|
|
10
10
|
const utils = require("./utils");
|
|
11
11
|
const scopes = require("./scopes");
|
|
12
12
|
const auth_1 = require("./auth");
|
|
13
|
+
const env_1 = require("./env");
|
|
14
|
+
const timeout_1 = require("./timeout");
|
|
13
15
|
const AUTH_ERROR_MESSAGE = `Command requires authentication, please run ${clc.bold("firebase login")}`;
|
|
14
16
|
let authClient;
|
|
15
17
|
let lastOptions;
|
|
@@ -28,13 +30,14 @@ async function autoAuth(options, authScopes) {
|
|
|
28
30
|
logger_1.logger.debug(`Running auto auth`);
|
|
29
31
|
let clientEmail;
|
|
30
32
|
try {
|
|
31
|
-
const
|
|
33
|
+
const timeoutMillis = (0, env_1.isFirebaseMcp)() ? 5000 : 15000;
|
|
34
|
+
const credentials = await (0, timeout_1.timeoutError)(client.getCredentials(), new error_1.FirebaseError(`Authenticating with default credentials timed out after ${timeoutMillis / 1000} seconds. Please try running \`firebase login\` instead.`), timeoutMillis);
|
|
32
35
|
clientEmail = credentials.client_email;
|
|
33
36
|
}
|
|
34
37
|
catch (e) {
|
|
35
38
|
logger_1.logger.debug(`Error getting account credentials.`);
|
|
36
39
|
}
|
|
37
|
-
if (
|
|
40
|
+
if ((0, env_1.isFirebaseStudio)() && token && clientEmail) {
|
|
38
41
|
const activeAccount = {
|
|
39
42
|
user: { email: clientEmail },
|
|
40
43
|
tokens: {
|
|
@@ -56,7 +59,7 @@ async function refreshAuth() {
|
|
|
56
59
|
return lastOptions.tokens;
|
|
57
60
|
}
|
|
58
61
|
exports.refreshAuth = refreshAuth;
|
|
59
|
-
async function requireAuth(options) {
|
|
62
|
+
async function requireAuth(options, skipAutoAuth = false) {
|
|
60
63
|
lastOptions = options;
|
|
61
64
|
api.setScopes([scopes.CLOUD_PLATFORM, scopes.FIREBASE_PLATFORM]);
|
|
62
65
|
options.authScopes = api.getScopes();
|
|
@@ -76,6 +79,9 @@ async function requireAuth(options) {
|
|
|
76
79
|
else if (user && (!(0, auth_1.isExpired)(tokens) || (tokens === null || tokens === void 0 ? void 0 : tokens.refresh_token))) {
|
|
77
80
|
logger_1.logger.debug(`> authorizing via signed-in user (${user.email})`);
|
|
78
81
|
}
|
|
82
|
+
else if (skipAutoAuth) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
79
85
|
else {
|
|
80
86
|
try {
|
|
81
87
|
return await autoAuth(options, options.authScopes);
|
package/lib/timeout.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.timeoutError = exports.timeoutFallback = void 0;
|
|
4
|
+
async function timeoutFallback(promise, value, timeoutMillis = 2000) {
|
|
5
|
+
return Promise.race([
|
|
6
|
+
promise,
|
|
7
|
+
new Promise((resolve) => setTimeout(() => resolve(value), timeoutMillis)),
|
|
8
|
+
]);
|
|
9
|
+
}
|
|
10
|
+
exports.timeoutFallback = timeoutFallback;
|
|
11
|
+
async function timeoutError(promise, error, timeoutMillis = 5000) {
|
|
12
|
+
if (typeof error === "string")
|
|
13
|
+
error = new Error(error);
|
|
14
|
+
return Promise.race([
|
|
15
|
+
promise,
|
|
16
|
+
new Promise((resolve, reject) => {
|
|
17
|
+
setTimeout(() => reject(error || new Error("Operation timed out.")), timeoutMillis);
|
|
18
|
+
}),
|
|
19
|
+
]);
|
|
20
|
+
}
|
|
21
|
+
exports.timeoutError = timeoutError;
|
package/lib/track.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var _a;
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
3
|
exports.cliSession = exports.vscodeSession = exports.emulatorSession = exports.trackVSCode = exports.trackEmulator = exports.trackGA4 = exports.usageEnabled = exports.GA4_PROPERTIES = void 0;
|
|
5
4
|
const node_fetch_1 = require("node-fetch");
|
|
@@ -7,6 +6,7 @@ const uuid_1 = require("uuid");
|
|
|
7
6
|
const auth_1 = require("./auth");
|
|
8
7
|
const configstore_1 = require("./configstore");
|
|
9
8
|
const logger_1 = require("./logger");
|
|
9
|
+
const env_1 = require("./env");
|
|
10
10
|
const pkg = require("../package.json");
|
|
11
11
|
exports.GA4_PROPERTIES = {
|
|
12
12
|
cli: {
|
|
@@ -43,7 +43,7 @@ const GA4_USER_PROPS = {
|
|
|
43
43
|
value: process.env.FIREPIT_VERSION || "none",
|
|
44
44
|
},
|
|
45
45
|
is_firebase_studio: {
|
|
46
|
-
value: (
|
|
46
|
+
value: (0, env_1.isFirebaseStudio)().toString(),
|
|
47
47
|
},
|
|
48
48
|
};
|
|
49
49
|
async function trackGA4(eventName, params, duration = 1) {
|
package/lib/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = void 0;
|
|
3
|
+
exports.openInBrowserPopup = exports.openInBrowser = exports.connectableHostname = exports.randomInt = exports.debounce = exports.last = exports.cloneDeep = exports.groupBy = exports.assertIsStringOrUndefined = exports.assertIsNumber = exports.assertIsString = exports.thirtyDaysFromNow = exports.isRunningInWSL = exports.isCloudEnvironment = exports.datetimeString = exports.createDestroyer = exports.sleep = exports.promiseWithSpinner = exports.tryParse = exports.promiseProps = exports.withTimeout = exports.promiseWhile = exports.promiseAllSettled = exports.getFunctionsEventProvider = exports.endpoint = exports.makeActiveProject = exports.streamToString = exports.stringToStream = exports.explainStdin = exports.allSettled = exports.reject = exports.logLabeledError = exports.logLabeledWarning = exports.logWarningToStderr = exports.logWarning = exports.logLabeledBullet = exports.logBullet = exports.logLabeledSuccess = exports.logSuccess = exports.addSubdomain = exports.addDatabaseNamespace = exports.getDatabaseViewDataUrl = exports.getDatabaseUrl = exports.envOverride = exports.setVSCodeEnvVars = exports.getInheritedOption = exports.consoleUrl = exports.vscodeEnvVars = exports.envOverrides = exports.IS_WINDOWS = void 0;
|
|
4
|
+
exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generateId = exports.wrappedSafeLoad = exports.readFileFromDirectory = exports.getHostnameFromUrl = void 0;
|
|
5
5
|
const fs = require("fs-extra");
|
|
6
6
|
const tty = require("tty");
|
|
7
7
|
const path = require("node:path");
|
|
@@ -115,6 +115,11 @@ function logWarning(message, type = "warn", data = undefined) {
|
|
|
115
115
|
logger_1.logger[type](clc.yellow(clc.bold(`${WARNING_CHAR} `)), message, data);
|
|
116
116
|
}
|
|
117
117
|
exports.logWarning = logWarning;
|
|
118
|
+
function logWarningToStderr(message) {
|
|
119
|
+
const prefix = clc.bold(`${WARNING_CHAR} `);
|
|
120
|
+
process.stderr.write(clc.yellow(prefix + message) + "\n");
|
|
121
|
+
}
|
|
122
|
+
exports.logWarningToStderr = logWarningToStderr;
|
|
118
123
|
function logLabeledWarning(label, message, type = "warn", data = undefined) {
|
|
119
124
|
logger_1.logger[type](clc.yellow(clc.bold(`${WARNING_CHAR} ${label}:`)), message, data);
|
|
120
125
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# # Create a movie based on user input
|
|
4
4
|
# mutation CreateMovie($title: String!, $genre: String!, $imageUrl: String!)
|
|
5
|
-
# @auth(level: USER_EMAIL_VERIFIED) {
|
|
5
|
+
# @auth(level: USER_EMAIL_VERIFIED, insecureReason: "Any email verified users can create a new movie.") {
|
|
6
6
|
# movie_insert(data: { title: $title, genre: $genre, imageUrl: $imageUrl })
|
|
7
7
|
# }
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# # @auth() directives control who can call each operation.
|
|
4
4
|
# # Anyone should be able to list all movies, so the auth level is set to PUBLIC
|
|
5
|
-
# query ListMovies @auth(level: PUBLIC) {
|
|
5
|
+
# query ListMovies @auth(level: PUBLIC, insecureReason: "Anyone can list all movies.") {
|
|
6
6
|
# movies {
|
|
7
7
|
# id
|
|
8
8
|
# title
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
# }
|
|
41
41
|
|
|
42
42
|
# # Get movie by id
|
|
43
|
-
# query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
|
|
43
|
+
# query GetMovieById($id: UUID!) @auth(level: PUBLIC, insecureReason: "Anyone can get a movie by id.") {
|
|
44
44
|
# movie(id: $id) {
|
|
45
45
|
# id
|
|
46
46
|
# title
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
# }
|
|
65
65
|
|
|
66
66
|
# # Search for movies, actors, and reviews
|
|
67
|
-
# query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC) {
|
|
67
|
+
# query SearchMovie($titleInput: String, $genre: String) @auth(level: PUBLIC, insecureReason: "Anyone can search for movies.") {
|
|
68
68
|
# movies(
|
|
69
69
|
# where: {
|
|
70
70
|
# _and: [{ genre: { eq: $genre } }, { title: { contains: $titleInput } }]
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const adminSdkConfig_1 = require("./adminSdkConfig");
|
|
5
|
-
describe("adminSdkConfig", () => {
|
|
6
|
-
describe("getProjectAdminSdkConfigOrCached", () => {
|
|
7
|
-
it("should return a fake config for a demo project id", async () => {
|
|
8
|
-
const projectId = "demo-project-1234";
|
|
9
|
-
await (0, chai_1.expect)((0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)).to.eventually.deep.equal({
|
|
10
|
-
projectId: "demo-project-1234",
|
|
11
|
-
databaseURL: "https://demo-project-1234.firebaseio.com",
|
|
12
|
-
storageBucket: "demo-project-1234.appspot.com",
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const firestore_1 = require("../gcp/firestore");
|
|
5
|
-
const proto_1 = require("../gcp/proto");
|
|
6
|
-
const sort = require("./api-sort");
|
|
7
|
-
describe("compareApiBackup", () => {
|
|
8
|
-
it("should compare backups by location", () => {
|
|
9
|
-
const nam5Backup = {
|
|
10
|
-
name: "projects/example/locations/nam5/backups/backupid",
|
|
11
|
-
};
|
|
12
|
-
const usWest1Backup = {
|
|
13
|
-
name: "projects/example/locations/us-west1/backups/backupid",
|
|
14
|
-
};
|
|
15
|
-
(0, chai_1.expect)(sort.compareApiBackup(usWest1Backup, nam5Backup)).to.greaterThanOrEqual(1);
|
|
16
|
-
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup, usWest1Backup)).to.lessThanOrEqual(-1);
|
|
17
|
-
});
|
|
18
|
-
it("should compare backups by snapshotTime (descending) if location is the same", () => {
|
|
19
|
-
const earlierBackup = {
|
|
20
|
-
name: "projects/example/locations/nam5/backups/backupid",
|
|
21
|
-
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
22
|
-
};
|
|
23
|
-
const laterBackup = {
|
|
24
|
-
name: "projects/example/locations/nam5/backups/backupid",
|
|
25
|
-
snapshotTime: "2024-02-02T00:00:00.000000Z",
|
|
26
|
-
};
|
|
27
|
-
(0, chai_1.expect)(sort.compareApiBackup(earlierBackup, laterBackup)).to.greaterThanOrEqual(1);
|
|
28
|
-
(0, chai_1.expect)(sort.compareApiBackup(laterBackup, earlierBackup)).to.lessThanOrEqual(-1);
|
|
29
|
-
});
|
|
30
|
-
it("should compare backups by full name if location and snapshotTime are the same", () => {
|
|
31
|
-
const nam5Backup1 = {
|
|
32
|
-
name: "projects/example/locations/nam5/backups/earlier-backupid",
|
|
33
|
-
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
34
|
-
};
|
|
35
|
-
const nam5Backup2 = {
|
|
36
|
-
name: "projects/example/locations/nam5/backups/later-backupid",
|
|
37
|
-
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
38
|
-
};
|
|
39
|
-
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup2, nam5Backup1)).to.greaterThanOrEqual(1);
|
|
40
|
-
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup1, nam5Backup2)).to.lessThanOrEqual(-1);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
describe("compareApiBackupSchedule", () => {
|
|
44
|
-
it("daily schedules should precede weekly ones", () => {
|
|
45
|
-
const dailySchedule = {
|
|
46
|
-
name: "projects/example/databases/mydatabase/backupSchedules/schedule",
|
|
47
|
-
dailyRecurrence: {},
|
|
48
|
-
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
49
|
-
};
|
|
50
|
-
const weeklySchedule = {
|
|
51
|
-
name: "projects/example/databases/mydatabase/backupSchedules/schedule",
|
|
52
|
-
weeklyRecurrence: {
|
|
53
|
-
day: firestore_1.DayOfWeek.FRIDAY,
|
|
54
|
-
},
|
|
55
|
-
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24 * 7),
|
|
56
|
-
};
|
|
57
|
-
(0, chai_1.expect)(sort.compareApiBackupSchedule(weeklySchedule, dailySchedule)).to.greaterThanOrEqual(1);
|
|
58
|
-
(0, chai_1.expect)(sort.compareApiBackup(dailySchedule, weeklySchedule)).to.lessThanOrEqual(-1);
|
|
59
|
-
});
|
|
60
|
-
it("should compare schedules with the same recurrence by name", () => {
|
|
61
|
-
const dailySchedule1 = {
|
|
62
|
-
name: "projects/example/databases/mydatabase/backupSchedules/schedule1",
|
|
63
|
-
dailyRecurrence: {},
|
|
64
|
-
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
65
|
-
};
|
|
66
|
-
const dailySchedule2 = {
|
|
67
|
-
name: "projects/example/databases/mydatabase/backupSchedules/schedule2",
|
|
68
|
-
dailyRecurrence: {},
|
|
69
|
-
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
70
|
-
};
|
|
71
|
-
(0, chai_1.expect)(sort.compareApiBackupSchedule(dailySchedule1, dailySchedule2)).to.lessThanOrEqual(-1);
|
|
72
|
-
(0, chai_1.expect)(sort.compareApiBackup(dailySchedule2, dailySchedule1)).to.greaterThanOrEqual(1);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const backupUtils_1 = require("./backupUtils");
|
|
5
|
-
describe("calculateRetention", () => {
|
|
6
|
-
it("should accept minutes", () => {
|
|
7
|
-
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("5m")).to.eq(300);
|
|
8
|
-
});
|
|
9
|
-
it("should accept hours", () => {
|
|
10
|
-
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("3h")).to.eq(10800);
|
|
11
|
-
});
|
|
12
|
-
it("should accept days", () => {
|
|
13
|
-
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("2d")).to.eq(172800);
|
|
14
|
-
});
|
|
15
|
-
it("should accept weeks", () => {
|
|
16
|
-
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("3w")).to.eq(1814400);
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const API = require("./api-types");
|
|
5
|
-
const pretty_print_1 = require("./pretty-print");
|
|
6
|
-
const printer = new pretty_print_1.PrettyPrint();
|
|
7
|
-
describe("prettyIndexString", () => {
|
|
8
|
-
it("should correctly print an order type Index", () => {
|
|
9
|
-
(0, chai_1.expect)(printer.prettyIndexString({
|
|
10
|
-
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
11
|
-
queryScope: API.QueryScope.COLLECTION,
|
|
12
|
-
fields: [
|
|
13
|
-
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
14
|
-
{ fieldPath: "bar", order: API.Order.DESCENDING },
|
|
15
|
-
],
|
|
16
|
-
}, false)).to.contain("(foo,ASCENDING) (bar,DESCENDING) ");
|
|
17
|
-
});
|
|
18
|
-
it("should correctly print a contains type Index", () => {
|
|
19
|
-
(0, chai_1.expect)(printer.prettyIndexString({
|
|
20
|
-
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
21
|
-
queryScope: API.QueryScope.COLLECTION,
|
|
22
|
-
fields: [
|
|
23
|
-
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
24
|
-
{ fieldPath: "baz", arrayConfig: API.ArrayConfig.CONTAINS },
|
|
25
|
-
],
|
|
26
|
-
}, false)).to.contain("(foo,ASCENDING) (baz,CONTAINS) ");
|
|
27
|
-
});
|
|
28
|
-
it("should correctly print a vector type Index", () => {
|
|
29
|
-
(0, chai_1.expect)(printer.prettyIndexString({
|
|
30
|
-
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
31
|
-
queryScope: API.QueryScope.COLLECTION,
|
|
32
|
-
fields: [{ fieldPath: "foo", vectorConfig: { dimension: 100, flat: {} } }],
|
|
33
|
-
}, false)).to.contain("(foo,VECTOR<100>) ");
|
|
34
|
-
});
|
|
35
|
-
it("should correctly print a vector type Index with other fields", () => {
|
|
36
|
-
(0, chai_1.expect)(printer.prettyIndexString({
|
|
37
|
-
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
38
|
-
queryScope: API.QueryScope.COLLECTION,
|
|
39
|
-
fields: [
|
|
40
|
-
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
41
|
-
{ fieldPath: "bar", vectorConfig: { dimension: 200, flat: {} } },
|
|
42
|
-
],
|
|
43
|
-
}, false)).to.contain("(foo,ASCENDING) (bar,VECTOR<200>) ");
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
describe("firebaseConsoleDatabaseUrl", () => {
|
|
47
|
-
it("should provide a console link", () => {
|
|
48
|
-
(0, chai_1.expect)(printer.firebaseConsoleDatabaseUrl("example-project", "example-db")).to.equal("https://console.firebase.google.com/project/example-project/firestore/databases/example-db/data");
|
|
49
|
-
});
|
|
50
|
-
it("should convert (default) to -default-", () => {
|
|
51
|
-
(0, chai_1.expect)(printer.firebaseConsoleDatabaseUrl("example-project", "(default)")).to.equal("https://console.firebase.google.com/project/example-project/firestore/databases/-default-/data");
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
describe("prettyStringArray", () => {
|
|
55
|
-
it("should correctly print an array of strings", () => {
|
|
56
|
-
(0, chai_1.expect)(printer.prettyStringArray(["kms-key-1", "kms-key-2"])).to.equal("kms-key-1\nkms-key-2\n");
|
|
57
|
-
});
|
|
58
|
-
it("should print nothing if the array is empty", () => {
|
|
59
|
-
(0, chai_1.expect)(printer.prettyStringArray([])).to.equal("");
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const chai_1 = require("chai");
|
|
4
|
-
const util = require("./util");
|
|
5
|
-
describe("IndexNameParsing", () => {
|
|
6
|
-
it("should parse an index name correctly", () => {
|
|
7
|
-
const name = "/projects/myproject/databases/(default)/collectionGroups/collection/indexes/abc123/";
|
|
8
|
-
(0, chai_1.expect)(util.parseIndexName(name)).to.eql({
|
|
9
|
-
projectId: "myproject",
|
|
10
|
-
databaseId: "(default)",
|
|
11
|
-
collectionGroupId: "collection",
|
|
12
|
-
indexId: "abc123",
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
it("should parse a field name correctly", () => {
|
|
16
|
-
const name = "/projects/myproject/databases/(default)/collectionGroups/collection/fields/abc123/";
|
|
17
|
-
(0, chai_1.expect)(util.parseFieldName(name)).to.eql({
|
|
18
|
-
projectId: "myproject",
|
|
19
|
-
databaseId: "(default)",
|
|
20
|
-
collectionGroupId: "collection",
|
|
21
|
-
fieldPath: "abc123",
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
it("should parse an index name from a named database correctly", () => {
|
|
25
|
-
const name = "/projects/myproject/databases/named-db/collectionGroups/collection/indexes/abc123/";
|
|
26
|
-
(0, chai_1.expect)(util.parseIndexName(name)).to.eql({
|
|
27
|
-
projectId: "myproject",
|
|
28
|
-
databaseId: "named-db",
|
|
29
|
-
collectionGroupId: "collection",
|
|
30
|
-
indexId: "abc123",
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
it("should parse a field name from a named database correctly", () => {
|
|
34
|
-
const name = "/projects/myproject/databases/named-db/collectionGroups/collection/fields/abc123/";
|
|
35
|
-
(0, chai_1.expect)(util.parseFieldName(name)).to.eql({
|
|
36
|
-
projectId: "myproject",
|
|
37
|
-
databaseId: "named-db",
|
|
38
|
-
collectionGroupId: "collection",
|
|
39
|
-
fieldPath: "abc123",
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
});
|