firebase-tools 14.11.2 → 14.12.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.
Files changed (53) hide show
  1. package/lib/api.js +3 -1
  2. package/lib/command.js +9 -3
  3. package/lib/commands/firestore-databases-create.js +11 -0
  4. package/lib/commands/init.js +7 -5
  5. package/lib/commands/internaltesting-functions-discover.js +20 -5
  6. package/lib/commands/use.js +5 -0
  7. package/lib/crashlytics/buildToolsJarHelper.js +1 -2
  8. package/lib/dataconnect/ensureApis.js +1 -0
  9. package/lib/deploy/dataconnect/prepare.js +2 -2
  10. package/lib/deploy/dataconnect/release.js +2 -2
  11. package/lib/deploy/firestore/deploy.js +10 -0
  12. package/lib/deploy/functions/prepare.js +5 -5
  13. package/lib/deploy/functions/prepareFunctionsUpload.js +3 -1
  14. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  15. package/lib/firestore/api-sort.js +96 -3
  16. package/lib/firestore/api-types.js +20 -1
  17. package/lib/firestore/api.js +68 -1
  18. package/lib/firestore/pretty-print.js +5 -1
  19. package/lib/firestore/validator.js +1 -1
  20. package/lib/functions/deprecationWarnings.js +4 -4
  21. package/lib/gcp/cloudsql/connect.js +1 -1
  22. package/lib/init/features/aitools/claude.js +7 -7
  23. package/lib/init/features/dataconnect/index.js +1 -1
  24. package/lib/init/features/dataconnect/sdk.js +2 -3
  25. package/lib/init/features/index.js +3 -1
  26. package/lib/init/index.js +8 -0
  27. package/lib/management/studio.js +120 -0
  28. package/lib/mcp/index.js +75 -2
  29. package/lib/mcp/prompt.js +10 -0
  30. package/lib/mcp/prompts/core/deploy.js +58 -0
  31. package/lib/mcp/prompts/core/index.js +5 -0
  32. package/lib/mcp/prompts/index.js +45 -0
  33. package/lib/mcp/tools/core/consult_assistant.js +7 -2
  34. package/lib/mcp/tools/core/get_sdk_config.js +10 -0
  35. package/lib/mcp/tools/core/init.js +1 -1
  36. package/lib/mcp/tools/database/get_data.js +49 -0
  37. package/lib/mcp/tools/database/get_rules.js +39 -0
  38. package/lib/mcp/tools/database/index.js +8 -0
  39. package/lib/mcp/tools/database/set_data.js +57 -0
  40. package/lib/mcp/tools/database/set_rules.js +41 -0
  41. package/lib/mcp/tools/database/validate_rules.js +41 -0
  42. package/lib/mcp/tools/index.js +4 -1
  43. package/lib/mcp/tools/rules/get_rules.js +1 -1
  44. package/lib/mcp/types.js +2 -0
  45. package/lib/mcp/util.js +2 -0
  46. package/lib/requireAuth.js +5 -1
  47. package/lib/rtdb.js +10 -6
  48. package/lib/scopes.js +2 -1
  49. package/lib/utils.js +24 -1
  50. package/package.json +1 -1
  51. package/prompts/FIREBASE.md +1 -2
  52. package/schema/firebase-config.json +3 -0
  53. package/templates/init/firestore/firestore.indexes.json +26 -1
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.availablePrompts = void 0;
4
+ const core_1 = require("./core");
5
+ const prompts = {
6
+ core: core_1.corePrompts,
7
+ firestore: [],
8
+ storage: [],
9
+ dataconnect: [],
10
+ auth: [],
11
+ messaging: [],
12
+ remoteconfig: [],
13
+ crashlytics: [],
14
+ apphosting: [],
15
+ database: [],
16
+ };
17
+ function namespacePrompts(promptsToNamespace, feature) {
18
+ return promptsToNamespace.map((p) => {
19
+ const newPrompt = Object.assign({}, p);
20
+ newPrompt.mcp = Object.assign({}, p.mcp);
21
+ if (newPrompt.mcp.omitPrefix) {
22
+ }
23
+ else if (feature === "core") {
24
+ newPrompt.mcp.name = `firebase:${p.mcp.name}`;
25
+ }
26
+ else {
27
+ newPrompt.mcp.name = `firebase:${feature}:${p.mcp.name}`;
28
+ }
29
+ newPrompt.mcp._meta = Object.assign(Object.assign({}, p.mcp._meta), { feature });
30
+ return newPrompt;
31
+ });
32
+ }
33
+ function availablePrompts(features) {
34
+ const allPrompts = namespacePrompts(prompts["core"], "core");
35
+ if (!features) {
36
+ features = Object.keys(prompts).filter((f) => f !== "core");
37
+ }
38
+ for (const feature of features) {
39
+ if (prompts[feature] && feature !== "core") {
40
+ allPrompts.push(...namespacePrompts(prompts[feature], feature));
41
+ }
42
+ }
43
+ return allPrompts;
44
+ }
45
+ exports.availablePrompts = availablePrompts;
@@ -7,11 +7,16 @@ const util_1 = require("../../util");
7
7
  const fdcExperience_1 = require("../../../gemini/fdcExperience");
8
8
  exports.consult_assistant = (0, tool_1.tool)({
9
9
  name: "consult_assistant",
10
- description: "Send a question to an AI assistant specifically enhanced to answer Firebase questions.",
10
+ description: "Access an AI assistant specialized in all aspects of **Firebase**. " +
11
+ "Use this tool to get **detailed information**, **best practices**, **troubleshooting steps**, **code examples**, and **contextual help** regarding Firebase services, features, and project configuration. " +
12
+ "This includes questions about Firestore, Authentication, Cloud Functions, Hosting, Storage, Analytics, and more. " +
13
+ "It can also provide insights based on the **current Firebase project context**.",
11
14
  inputSchema: zod_1.z.object({
12
15
  prompt: zod_1.z
13
16
  .string()
14
- .describe("A description of what the user is trying to do or learn with Firebase."),
17
+ .describe("The specific question or task related to Firebase. " +
18
+ "Be precise and include relevant details, such as the Firebase service in question, the desired outcome, or any error messages encountered. " +
19
+ "Examples: 'What's the best way to deploy a React app to Firebase Hosting?', 'Explain Firebase Authentication with Google Sign-In.' , 'What are the current project settings for 'projectId'? "),
15
20
  }),
16
21
  annotations: {
17
22
  title: "Consult Firebase Assistant",
@@ -37,5 +37,15 @@ exports.get_sdk_config = (0, tool_1.tool)({
37
37
  if (!appId)
38
38
  return (0, util_1.mcpError)(`Could not find an app for platform '${inputPlatform}' in project '${projectId}'`);
39
39
  const sdkConfig = await (0, apps_1.getAppConfig)(appId, platform);
40
+ if ("configFilename" in sdkConfig) {
41
+ return {
42
+ content: [
43
+ {
44
+ type: "text",
45
+ text: `SDK config content for \`${sdkConfig.configFilename}\`:\n\n\`\`\`\n${Buffer.from(sdkConfig.configFileContents, "base64").toString("utf-8")}\n\`\`\``,
46
+ },
47
+ ],
48
+ };
49
+ }
40
50
  return (0, util_1.toContent)(sdkConfig, { format: "json" });
41
51
  });
@@ -135,7 +135,7 @@ exports.init = (0, tool_1.tool)({
135
135
  isNewInstance: false,
136
136
  isNewDatabase: false,
137
137
  schemaGql: [],
138
- shouldProvisionCSQL: false,
138
+ shouldProvisionCSQL: true,
139
139
  };
140
140
  }
141
141
  const setup = {
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_data = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const util_1 = require("../../util");
7
+ const url = require("node:url");
8
+ const apiv2_1 = require("../../../apiv2");
9
+ const consumers_1 = require("node:stream/consumers");
10
+ const node_path_1 = require("node:path");
11
+ exports.get_data = (0, tool_1.tool)({
12
+ name: "get_data",
13
+ description: "Returns RTDB data from the specified location",
14
+ inputSchema: zod_1.z.object({
15
+ databaseUrl: zod_1.z
16
+ .string()
17
+ .optional()
18
+ .describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
19
+ path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
20
+ }),
21
+ annotations: {
22
+ title: "Get Realtime Database data",
23
+ readOnlyHint: true,
24
+ },
25
+ _meta: {
26
+ requiresAuth: false,
27
+ requiresProject: false,
28
+ },
29
+ }, async ({ path: getPath, databaseUrl }, { projectId, host }) => {
30
+ if (!getPath.startsWith("/")) {
31
+ return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${getPath}')`);
32
+ }
33
+ const dbUrl = new url.URL(databaseUrl
34
+ ? `${databaseUrl}/${getPath}.json`
35
+ : node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${getPath}.json`));
36
+ const client = new apiv2_1.Client({
37
+ urlPrefix: dbUrl.origin,
38
+ auth: true,
39
+ });
40
+ host.logger.debug(`sending read request to path '${getPath}' for url '${dbUrl.toString()}'`);
41
+ const res = await client.request({
42
+ method: "GET",
43
+ path: dbUrl.pathname,
44
+ responseType: "stream",
45
+ resolveOnHTTPError: true,
46
+ });
47
+ const content = await (0, consumers_1.text)(res.body);
48
+ return (0, util_1.toContent)(content);
49
+ });
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.get_rules = void 0;
4
+ const zod_1 = require("zod");
5
+ const apiv2_1 = require("../../../apiv2");
6
+ const tool_1 = require("../../tool");
7
+ const util_1 = require("../../util");
8
+ exports.get_rules = (0, tool_1.tool)({
9
+ name: "get_rules",
10
+ description: "Get an RTDB database's rules",
11
+ inputSchema: zod_1.z.object({
12
+ databaseUrl: zod_1.z
13
+ .string()
14
+ .optional()
15
+ .describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
16
+ }),
17
+ annotations: {
18
+ title: "Get Realtime Database rules",
19
+ readOnlyHint: true,
20
+ },
21
+ _meta: {
22
+ requiresAuth: false,
23
+ requiresProject: false,
24
+ },
25
+ }, async ({ databaseUrl }, { projectId }) => {
26
+ const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
27
+ const client = new apiv2_1.Client({ urlPrefix: dbUrl });
28
+ const response = await client.request({
29
+ method: "GET",
30
+ path: "/.settings/rules.json",
31
+ responseType: "stream",
32
+ resolveOnHTTPError: true,
33
+ });
34
+ if (response.status !== 200) {
35
+ return (0, util_1.mcpError)(`Failed to fetch current rules. Code: ${response.status}`);
36
+ }
37
+ const rules = await response.response.text();
38
+ return (0, util_1.toContent)(rules);
39
+ });
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.realtimeDatabaseTools = void 0;
4
+ const get_rules_1 = require("./get_rules");
5
+ const get_data_1 = require("./get_data");
6
+ const set_data_1 = require("./set_data");
7
+ const validate_rules_1 = require("./validate_rules");
8
+ exports.realtimeDatabaseTools = [get_data_1.get_data, set_data_1.set_data, get_rules_1.get_rules, validate_rules_1.validate_rules];
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.set_data = void 0;
4
+ const zod_1 = require("zod");
5
+ const tool_1 = require("../../tool");
6
+ const util_1 = require("../../util");
7
+ const url = require("node:url");
8
+ const utils_1 = require("../../../utils");
9
+ const apiv2_1 = require("../../../apiv2");
10
+ const error_1 = require("../../../error");
11
+ const node_path_1 = require("node:path");
12
+ exports.set_data = (0, tool_1.tool)({
13
+ name: "set_data",
14
+ description: "Writes RTDB data to the specified location",
15
+ inputSchema: zod_1.z.object({
16
+ databaseUrl: zod_1.z
17
+ .string()
18
+ .optional()
19
+ .describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.us-central1.firebasedatabase.app. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
20
+ path: zod_1.z.string().describe("The path to the data to read. (ex: /my/cool/path)"),
21
+ data: zod_1.z.string().describe('The JSON to write. (ex: {"alphabet": ["a", "b", "c"]})'),
22
+ }),
23
+ annotations: {
24
+ title: "Set Realtime Database data",
25
+ readOnlyHint: false,
26
+ idempotentHint: true,
27
+ },
28
+ _meta: {
29
+ requiresAuth: false,
30
+ requiresProject: false,
31
+ },
32
+ }, async ({ path: setPath, databaseUrl, data }, { projectId, host }) => {
33
+ if (!setPath.startsWith("/")) {
34
+ return (0, util_1.mcpError)(`paths must start with '/' (you passed ''${setPath}')`);
35
+ }
36
+ const dbUrl = new url.URL(databaseUrl
37
+ ? `${databaseUrl}/${setPath}.json`
38
+ : node_path_1.default.join(`https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`, `${setPath}.json`));
39
+ const client = new apiv2_1.Client({
40
+ urlPrefix: dbUrl.origin,
41
+ auth: true,
42
+ });
43
+ const inStream = (0, utils_1.stringToStream)(data);
44
+ host.logger.debug(`sending write request to path '${setPath}' for url '${dbUrl.toString()}'`);
45
+ try {
46
+ await client.request({
47
+ method: "PUT",
48
+ path: dbUrl.pathname,
49
+ body: inStream,
50
+ });
51
+ }
52
+ catch (err) {
53
+ host.logger.debug((0, error_1.getErrMsg)(err));
54
+ return (0, util_1.mcpError)(`Unexpected error while setting data: ${(0, error_1.getErrMsg)(err)}`);
55
+ }
56
+ return (0, util_1.toContent)("write successful!");
57
+ });
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validate_rules = void 0;
4
+ const zod_1 = require("zod");
5
+ const apiv2_1 = require("../../../apiv2");
6
+ const tool_1 = require("../../tool");
7
+ const util_1 = require("../../util");
8
+ const rtdb_1 = require("../../../rtdb");
9
+ const error_1 = require("../../../error");
10
+ exports.validate_rules = (0, tool_1.tool)({
11
+ name: "validate_rules",
12
+ description: "Validates an RTDB database's rules",
13
+ inputSchema: zod_1.z.object({
14
+ databaseUrl: zod_1.z
15
+ .string()
16
+ .optional()
17
+ .describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
18
+ rules: zod_1.z
19
+ .string()
20
+ .describe('The rules object, as a string (ex: {".read": false, ".write": false})'),
21
+ }),
22
+ annotations: {
23
+ title: "Validate Realtime Database rules",
24
+ idempotentHint: true,
25
+ },
26
+ _meta: {
27
+ requiresAuth: true,
28
+ requiresProject: false,
29
+ },
30
+ }, async ({ databaseUrl, rules }, { projectId, host }) => {
31
+ const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
32
+ const client = new apiv2_1.Client({ urlPrefix: dbUrl });
33
+ try {
34
+ await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
35
+ }
36
+ catch (e) {
37
+ host.logger.debug(`failed to update rules at url ${dbUrl}`);
38
+ return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
39
+ }
40
+ return (0, util_1.toContent)("the inputted rules are valid!");
41
+ });
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validate_rules = void 0;
4
+ const zod_1 = require("zod");
5
+ const apiv2_1 = require("../../../apiv2");
6
+ const tool_1 = require("../../tool");
7
+ const util_1 = require("../../util");
8
+ const rtdb_1 = require("../../../rtdb");
9
+ const error_1 = require("../../../error");
10
+ exports.validate_rules = (0, tool_1.tool)({
11
+ name: "validate_rules",
12
+ description: "Validates an RTDB database's rules",
13
+ inputSchema: zod_1.z.object({
14
+ databaseUrl: zod_1.z
15
+ .string()
16
+ .optional()
17
+ .describe("connect to the database at url. If omitted, use default database instance <project>-default-rtdb.firebaseio.com. Can point to emulator URL (e.g. localhost:6000/<instance>)"),
18
+ rules: zod_1.z
19
+ .string()
20
+ .describe('The rules object, as a string (ex: {"rules": {".read": false, ".write": false}})'),
21
+ }),
22
+ annotations: {
23
+ title: "Validate Realtime Database rules",
24
+ idempotentHint: true,
25
+ },
26
+ _meta: {
27
+ requiresAuth: true,
28
+ requiresProject: false,
29
+ },
30
+ }, async ({ databaseUrl, rules }, { projectId, host }) => {
31
+ const dbUrl = databaseUrl !== null && databaseUrl !== void 0 ? databaseUrl : `https://${projectId}-default-rtdb.us-central1.firebasedatabase.app`;
32
+ const client = new apiv2_1.Client({ urlPrefix: dbUrl });
33
+ try {
34
+ await (0, rtdb_1.updateRulesWithClient)(client, rules, { dryRun: true });
35
+ }
36
+ catch (e) {
37
+ host.logger.debug(`failed to validate rules at url ${dbUrl}`);
38
+ return (0, util_1.mcpError)((0, error_1.getErrMsg)(e));
39
+ }
40
+ return (0, util_1.toContent)("the inputted rules are valid!");
41
+ });
@@ -10,8 +10,9 @@ const index_6 = require("./messaging/index");
10
10
  const index_7 = require("./remoteconfig/index");
11
11
  const index_8 = require("./crashlytics/index");
12
12
  const index_9 = require("./apphosting/index");
13
+ const index_10 = require("./database/index");
13
14
  function availableTools(activeFeatures) {
14
- const toolDefs = addFeaturePrefix("firebase", index_4.coreTools);
15
+ const toolDefs = [];
15
16
  if (!(activeFeatures === null || activeFeatures === void 0 ? void 0 : activeFeatures.length)) {
16
17
  activeFeatures = Object.keys(tools);
17
18
  }
@@ -22,6 +23,7 @@ function availableTools(activeFeatures) {
22
23
  }
23
24
  exports.availableTools = availableTools;
24
25
  const tools = {
26
+ core: addFeaturePrefix("firebase", index_4.coreTools),
25
27
  firestore: addFeaturePrefix("firestore", index_3.firestoreTools),
26
28
  auth: addFeaturePrefix("auth", index_1.authTools),
27
29
  dataconnect: addFeaturePrefix("dataconnect", index_2.dataconnectTools),
@@ -30,6 +32,7 @@ const tools = {
30
32
  remoteconfig: addFeaturePrefix("remoteconfig", index_7.remoteConfigTools),
31
33
  crashlytics: addFeaturePrefix("crashlytics", index_8.crashlyticsTools),
32
34
  apphosting: addFeaturePrefix("apphosting", index_9.appHostingTools),
35
+ database: addFeaturePrefix("database", index_10.realtimeDatabaseTools),
33
36
  };
34
37
  function addFeaturePrefix(feature, tools) {
35
38
  return tools.map((tool) => (Object.assign(Object.assign({}, tool), { mcp: Object.assign(Object.assign({}, tool.mcp), { name: `${feature}_${tool.mcp.name}`, _meta: Object.assign(Object.assign({}, tool.mcp._meta), { feature }) }) })));
@@ -21,7 +21,7 @@ function getRulesTool(productName, releaseName) {
21
21
  }, async (_, { projectId }) => {
22
22
  const rulesetName = await (0, rules_1.getLatestRulesetName)(projectId, releaseName);
23
23
  if (!rulesetName)
24
- return (0, util_1.mcpError)(`No active Firestore rules were found in project '${projectId}'`);
24
+ return (0, util_1.mcpError)(`No active ${productName} rules were found in project '${projectId}'`);
25
25
  const rules = await (0, rules_1.getRulesetContent)(rulesetName);
26
26
  return (0, util_1.toContent)(rules[0].content);
27
27
  });
package/lib/mcp/types.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SERVER_FEATURES = void 0;
4
4
  exports.SERVER_FEATURES = [
5
+ "core",
5
6
  "firestore",
6
7
  "storage",
7
8
  "dataconnect",
@@ -10,4 +11,5 @@ exports.SERVER_FEATURES = [
10
11
  "remoteconfig",
11
12
  "crashlytics",
12
13
  "apphosting",
14
+ "database",
13
15
  ];
package/lib/mcp/util.js CHANGED
@@ -56,6 +56,7 @@ function commandExistsSync(command) {
56
56
  }
57
57
  exports.commandExistsSync = commandExistsSync;
58
58
  const SERVER_FEATURE_APIS = {
59
+ core: "",
59
60
  firestore: (0, api_1.firestoreOrigin)(),
60
61
  storage: (0, api_1.storageOrigin)(),
61
62
  dataconnect: (0, api_1.dataconnectOrigin)(),
@@ -64,6 +65,7 @@ const SERVER_FEATURE_APIS = {
64
65
  remoteconfig: (0, api_1.remoteConfigApiOrigin)(),
65
66
  crashlytics: (0, api_1.crashlyticsApiOrigin)(),
66
67
  apphosting: (0, api_1.apphostingOrigin)(),
68
+ database: (0, api_1.realtimeOrigin)(),
67
69
  };
68
70
  async function checkFeatureActive(feature, projectId, options) {
69
71
  var _a;
@@ -61,7 +61,11 @@ async function refreshAuth() {
61
61
  exports.refreshAuth = refreshAuth;
62
62
  async function requireAuth(options, skipAutoAuth = false) {
63
63
  lastOptions = options;
64
- api.setScopes([scopes.CLOUD_PLATFORM, scopes.FIREBASE_PLATFORM]);
64
+ const requiredScopes = [scopes.CLOUD_PLATFORM];
65
+ if ((0, env_1.isFirebaseStudio)()) {
66
+ requiredScopes.push(scopes.USERINFO_EMAIL);
67
+ }
68
+ api.setScopes(requiredScopes);
65
69
  options.authScopes = api.getScopes();
66
70
  const tokens = options.tokens;
67
71
  const user = options.user;
package/lib/rtdb.js CHANGED
@@ -1,16 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateRules = void 0;
3
+ exports.updateRulesWithClient = exports.updateRules = void 0;
4
4
  const apiv2_1 = require("./apiv2");
5
5
  const database_1 = require("./management/database");
6
6
  const error_1 = require("./error");
7
7
  const api_1 = require("./database/api");
8
8
  const utils = require("./utils");
9
9
  async function updateRules(projectId, instance, src, options = {}) {
10
- const queryParams = {};
11
- if (options.dryRun) {
12
- queryParams.dryRun = "true";
13
- }
14
10
  const downstreamOptions = { instance: instance, project: projectId };
15
11
  await (0, database_1.populateInstanceDetails)(downstreamOptions);
16
12
  if (!downstreamOptions.instanceDetails) {
@@ -18,6 +14,14 @@ async function updateRules(projectId, instance, src, options = {}) {
18
14
  }
19
15
  const origin = utils.getDatabaseUrl((0, api_1.realtimeOriginOrCustomUrl)(downstreamOptions.instanceDetails.databaseUrl), instance, "");
20
16
  const client = new apiv2_1.Client({ urlPrefix: origin });
17
+ return updateRulesWithClient(client, options);
18
+ }
19
+ exports.updateRules = updateRules;
20
+ async function updateRulesWithClient(client, src, options = {}) {
21
+ const queryParams = {};
22
+ if (options.dryRun) {
23
+ queryParams.dryRun = "true";
24
+ }
21
25
  const response = await client.request({
22
26
  method: "PUT",
23
27
  path: ".settings/rules.json",
@@ -32,4 +36,4 @@ async function updateRules(projectId, instance, src, options = {}) {
32
36
  throw new error_1.FirebaseError("Unexpected error while deploying database rules.", { exit: 2 });
33
37
  }
34
38
  }
35
- exports.updateRules = updateRules;
39
+ exports.updateRulesWithClient = updateRulesWithClient;
package/lib/scopes.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CLOUD_PUBSUB = exports.CLOUD_STORAGE = exports.CLOUD_PLATFORM = exports.FIREBASE_PLATFORM = exports.CLOUD_PROJECTS_READONLY = exports.EMAIL = exports.OPENID = void 0;
3
+ exports.CLOUD_PUBSUB = exports.CLOUD_STORAGE = exports.CLOUD_PLATFORM = exports.FIREBASE_PLATFORM = exports.CLOUD_PROJECTS_READONLY = exports.USERINFO_EMAIL = exports.EMAIL = exports.OPENID = void 0;
4
4
  exports.OPENID = "openid";
5
5
  exports.EMAIL = "email";
6
+ exports.USERINFO_EMAIL = "https://www.googleapis.com/auth/userinfo.email";
6
7
  exports.CLOUD_PROJECTS_READONLY = "https://www.googleapis.com/auth/cloudplatformprojects.readonly";
7
8
  exports.FIREBASE_PLATFORM = "https://www.googleapis.com/auth/firebase";
8
9
  exports.CLOUD_PLATFORM = "https://www.googleapis.com/auth/cloud-platform";
package/lib/utils.js CHANGED
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
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;
4
+ exports.deepEqual = exports.promptForDirectory = exports.updateOrCreateGitignore = exports.readSecretValue = exports.generatePassword = 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");
8
8
  const yaml = require("yaml");
9
+ const crypto = require("node:crypto");
9
10
  const _ = require("lodash");
10
11
  const url = require("url");
11
12
  const http = require("http");
@@ -546,6 +547,28 @@ function generateId(n = 6) {
546
547
  return id;
547
548
  }
548
549
  exports.generateId = generateId;
550
+ function generatePassword(n = 20) {
551
+ const lower = "abcdefghijklmnopqrstuvwxyz";
552
+ const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
553
+ const numbers = "0123456789";
554
+ const special = "!@#$%^&*()_+~`|}{[]:;?><,./-=";
555
+ const all = lower + upper + numbers + special;
556
+ let pw = "";
557
+ pw += lower[crypto.randomInt(lower.length)];
558
+ pw += upper[crypto.randomInt(upper.length)];
559
+ pw += numbers[crypto.randomInt(numbers.length)];
560
+ pw += special[crypto.randomInt(special.length)];
561
+ for (let i = 4; i < n; i++) {
562
+ pw += all[crypto.randomInt(all.length)];
563
+ }
564
+ const pwArray = pw.split("");
565
+ for (let i = pwArray.length - 1; i > 0; i--) {
566
+ const j = crypto.randomInt(i);
567
+ [pwArray[i], pwArray[j]] = [pwArray[j], pwArray[i]];
568
+ }
569
+ return pwArray.join("");
570
+ }
571
+ exports.generatePassword = generatePassword;
549
572
  function readSecretValue(prompt, dataFile) {
550
573
  if ((!dataFile || dataFile === "-") && tty.isatty(0)) {
551
574
  return (0, prompt_1.password)({ message: prompt });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "14.11.2",
3
+ "version": "14.12.1",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -32,8 +32,7 @@ firebase deploy --except functions
32
32
 
33
33
  firebase use staging
34
34
  firebase use production
35
-
36
- ````
35
+ ```
37
36
  </example>
38
37
 
39
38
  ## Local Development
@@ -88,6 +88,9 @@
88
88
  "database": {
89
89
  "type": "string"
90
90
  },
91
+ "edition": {
92
+ "type": "string"
93
+ },
91
94
  "indexes": {
92
95
  "type": "string"
93
96
  },
@@ -1,5 +1,5 @@
1
1
  {
2
- // Example:
2
+ // Example (Standard Edition):
3
3
  //
4
4
  // "indexes": [
5
5
  // {
@@ -21,6 +21,31 @@
21
21
  // },
22
22
  // ]
23
23
  // ]
24
+ //
25
+ // Example (Enterprise Edition):
26
+ //
27
+ // "indexes": [
28
+ // {
29
+ // "collectionGroup": "reviews",
30
+ // "queryScope": "COLLECTION_GROUP",
31
+ // "apiScope": "MONGODB_COMPATIBLE_API",
32
+ // "density": "DENSE",
33
+ // "multikey": false,
34
+ // "fields": [
35
+ // { "fieldPath": "baz", "mode": "ASCENDING" }
36
+ // ]
37
+ // },
38
+ // {
39
+ // "collectionGroup": "items",
40
+ // "queryScope": "COLLECTION_GROUP",
41
+ // "apiScope": "MONGODB_COMPATIBLE_API",
42
+ // "density": "SPARSE_ANY",
43
+ // "multikey": true,
44
+ // "fields": [
45
+ // { "fieldPath": "baz", "mode": "ASCENDING" }
46
+ // ]
47
+ // },
48
+ // ]
24
49
  "indexes": [],
25
50
  "fieldOverrides": []
26
51
  }