firebase-tools 14.15.2 → 14.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/lib/commands/firestore-bulkdelete.js +73 -0
  2. package/lib/commands/firestore-operations-cancel.js +44 -0
  3. package/lib/commands/firestore-operations-describe.js +29 -0
  4. package/lib/commands/firestore-operations-list.js +29 -0
  5. package/lib/commands/firestore-utils.js +15 -0
  6. package/lib/commands/functions-config-export.js +5 -2
  7. package/lib/commands/index.js +5 -0
  8. package/lib/config.js +16 -4
  9. package/lib/crashlytics/{listNotes.js → events.js} +11 -9
  10. package/lib/crashlytics/filters.js +77 -0
  11. package/lib/crashlytics/issues.js +50 -0
  12. package/lib/crashlytics/notes.js +67 -0
  13. package/lib/crashlytics/reports.js +47 -0
  14. package/lib/crashlytics/types.js +60 -0
  15. package/lib/dataconnect/ensureApis.js +3 -3
  16. package/lib/deploy/apphosting/deploy.js +2 -1
  17. package/lib/deploy/apphosting/util.js +5 -8
  18. package/lib/deploy/functions/deploy.js +4 -3
  19. package/lib/deploy/functions/prepare.js +8 -6
  20. package/lib/emulator/apphosting/developmentServer.js +3 -3
  21. package/lib/emulator/apphosting/serve.js +29 -29
  22. package/lib/emulator/commandUtils.js +7 -1
  23. package/lib/emulator/controller.js +15 -31
  24. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  25. package/lib/emulator/hub.js +7 -1
  26. package/lib/emulator/initEmulators.js +1 -1
  27. package/lib/extensions/runtimes/common.js +3 -2
  28. package/lib/firestore/api.js +45 -0
  29. package/lib/firestore/pretty-print.js +23 -0
  30. package/lib/functions/projectConfig.js +69 -9
  31. package/lib/gcp/cloudfunctions.js +1 -6
  32. package/lib/gcp/cloudfunctionsv2.js +1 -9
  33. package/lib/gcp/cloudsql/cloudsqladmin.js +2 -2
  34. package/lib/init/features/dataconnect/create_app.js +7 -2
  35. package/lib/init/features/dataconnect/index.js +101 -60
  36. package/lib/init/features/dataconnect/sdk.js +35 -17
  37. package/lib/mcp/errors.js +2 -10
  38. package/lib/mcp/index.js +0 -3
  39. package/lib/mcp/prompts/crashlytics/connect.js +114 -0
  40. package/lib/mcp/prompts/crashlytics/index.js +2 -3
  41. package/lib/mcp/tools/auth/disable_user.js +1 -1
  42. package/lib/mcp/tools/auth/get_user.js +9 -2
  43. package/lib/mcp/tools/core/index.js +4 -0
  44. package/lib/mcp/tools/core/init.js +11 -2
  45. package/lib/mcp/tools/core/login.js +46 -0
  46. package/lib/mcp/tools/core/logout.js +62 -0
  47. package/lib/mcp/tools/crashlytics/events.js +42 -0
  48. package/lib/mcp/tools/crashlytics/index.js +16 -20
  49. package/lib/mcp/tools/crashlytics/issues.js +56 -0
  50. package/lib/mcp/tools/crashlytics/notes.js +78 -0
  51. package/lib/mcp/tools/crashlytics/reports.js +100 -0
  52. package/lib/mcp/tools/dataconnect/index.js +2 -2
  53. package/lib/mcp/tools/dataconnect/{info.js → list_services.js} +5 -5
  54. package/lib/mcp/util.js +1 -17
  55. package/lib/serve/functions.js +4 -3
  56. package/lib/unzip.js +13 -0
  57. package/lib/utils.js +17 -1
  58. package/package.json +1 -1
  59. package/schema/firebase-config.json +160 -59
  60. package/lib/crashlytics/addNote.js +0 -27
  61. package/lib/crashlytics/deleteNote.js +0 -23
  62. package/lib/crashlytics/getIssueDetails.js +0 -26
  63. package/lib/crashlytics/getSampleCrash.js +0 -34
  64. package/lib/crashlytics/listTopDevices.js +0 -33
  65. package/lib/crashlytics/listTopIssues.js +0 -30
  66. package/lib/crashlytics/listTopOperatingSystems.js +0 -32
  67. package/lib/crashlytics/listTopVersions.js +0 -32
  68. package/lib/crashlytics/updateIssue.js +0 -35
  69. package/lib/mcp/prompts/crashlytics/common.js +0 -10
  70. package/lib/mcp/prompts/crashlytics/fix_issue.js +0 -89
  71. package/lib/mcp/prompts/crashlytics/prioritize_issues.js +0 -79
  72. package/lib/mcp/tools/crashlytics/add_note.js +0 -32
  73. package/lib/mcp/tools/crashlytics/constants.js +0 -11
  74. package/lib/mcp/tools/crashlytics/delete_note.js +0 -35
  75. package/lib/mcp/tools/crashlytics/get_issue_details.js +0 -31
  76. package/lib/mcp/tools/crashlytics/get_sample_crash.js +0 -43
  77. package/lib/mcp/tools/crashlytics/list_notes.js +0 -37
  78. package/lib/mcp/tools/crashlytics/list_top_devices.js +0 -33
  79. package/lib/mcp/tools/crashlytics/list_top_issues.js +0 -38
  80. package/lib/mcp/tools/crashlytics/list_top_operating_systems.js +0 -33
  81. package/lib/mcp/tools/crashlytics/list_top_versions.js +0 -33
  82. package/lib/mcp/tools/crashlytics/update_issue.js +0 -37
  83. package/lib/mcp/tools/database/set_rules.js +0 -41
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const requirePermissions_1 = require("../requirePermissions");
8
+ const types_1 = require("../emulator/types");
9
+ const commandUtils_1 = require("../emulator/commandUtils");
10
+ const prompt_1 = require("../prompt");
11
+ const utils = require("../utils");
12
+ const clc = require("colorette");
13
+ const utils_1 = require("../utils");
14
+ const error_1 = require("../error");
15
+ function confirmationMessage(options, databaseId, collectionIds) {
16
+ const root = `projects/${options.project}/databases/${databaseId}/documents`;
17
+ return ("You are about to delete all documents in the following collection groups: " +
18
+ clc.cyan(collectionIds.map((item) => `"${item}"`).join(", ")) +
19
+ " in " +
20
+ clc.cyan(`"${root}"`) +
21
+ ". Are you sure?");
22
+ }
23
+ exports.command = new command_1.Command("firestore:bulkdelete")
24
+ .description("managed bulk delete service to delete data from one or more collection groups")
25
+ .option("--database <databaseName>", 'Database ID for database to delete from. "(default)" if none is provided.')
26
+ .option("--collection-ids <collectionIds>", "A comma-separated list of collection group IDs to delete. Deletes all documents in the specified collection groups.")
27
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.bulkDeleteDocuments"])
28
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
29
+ .action(async (options) => {
30
+ if (!options.collectionIds) {
31
+ throw new error_1.FirebaseError("Missing required flag --collection-ids=[comma separated list of collection groups]");
32
+ }
33
+ let collectionIds = [];
34
+ try {
35
+ collectionIds = options.collectionIds
36
+ .split(",")
37
+ .filter((id) => id.trim() !== "");
38
+ }
39
+ catch (e) {
40
+ throw new error_1.FirebaseError("The value for --collection-ids must a list of comma separated collection group names");
41
+ }
42
+ if (collectionIds.length === 0) {
43
+ throw new error_1.FirebaseError("Must specify at least one collection ID in --collection-ids.");
44
+ }
45
+ const databaseId = options.database || "(default)";
46
+ const api = new fsi.FirestoreApi();
47
+ const confirmed = await (0, prompt_1.confirm)({
48
+ message: confirmationMessage(options, databaseId, collectionIds),
49
+ default: false,
50
+ force: options.force,
51
+ nonInteractive: options.nonInteractive,
52
+ });
53
+ if (!confirmed) {
54
+ return utils.reject("Command aborted.", { exit: 1 });
55
+ }
56
+ const op = await api.bulkDeleteDocuments(options.project, databaseId, collectionIds);
57
+ if (options.json) {
58
+ logger_1.logger.info(JSON.stringify(op, undefined, 2));
59
+ }
60
+ else {
61
+ if (op.name) {
62
+ (0, utils_1.logSuccess)(`Successfully started bulk delete operation.`);
63
+ (0, utils_1.logBullet)(`Operation name: ` + clc.cyan(op.name));
64
+ (0, utils_1.logBullet)("You can monitor the operation's progress using the " +
65
+ clc.cyan(`gcloud firestore operations describe`) +
66
+ ` command.`);
67
+ }
68
+ else {
69
+ (0, utils_1.logLabeledError)(`Bulk Delete:`, `Failed to start a bulk delete operation.`);
70
+ }
71
+ }
72
+ return op;
73
+ });
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const types_1 = require("../emulator/types");
7
+ const commandUtils_1 = require("../emulator/commandUtils");
8
+ const firestore_utils_1 = require("./firestore-utils");
9
+ const prompt_1 = require("../prompt");
10
+ const clc = require("colorette");
11
+ const utils = require("../utils");
12
+ const logger_1 = require("../logger");
13
+ exports.command = new command_1.Command("firestore:operations:cancel <operationName>")
14
+ .description("cancels a long-running Cloud Firestore admin operation")
15
+ .option("--database <databaseName>", 'Database ID for which the operation is running. "(default)" if none is provided.')
16
+ .option("--force", "Forces the operation cancellation without asking for confirmation")
17
+ .before(commandUtils_1.errorMissingProject)
18
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
19
+ .action(async (operationName, options) => {
20
+ const databaseId = options.database || "(default)";
21
+ operationName = (0, firestore_utils_1.getShortOperationName)(operationName);
22
+ if (!options.force) {
23
+ const fullName = `/projects/${options.project}/databases/${databaseId}/operations/${operationName}`;
24
+ const confirmMessage = `You are about to cancel the operation: ${clc.bold(clc.yellow(clc.underline(fullName)))}. Do you wish to continue?`;
25
+ const consent = await (0, prompt_1.confirm)(confirmMessage);
26
+ if (!consent) {
27
+ return utils.reject("Command aborted.", { exit: 1 });
28
+ }
29
+ }
30
+ const api = new fsi.FirestoreApi();
31
+ const status = await api.cancelOperation(options.project, databaseId, operationName);
32
+ if (options.json) {
33
+ logger_1.logger.info(JSON.stringify(status, undefined, 2));
34
+ }
35
+ else {
36
+ if (status.success) {
37
+ utils.logSuccess("Operation cancelled successfully.");
38
+ }
39
+ else {
40
+ utils.logWarning("Canceling the operation failed.");
41
+ }
42
+ }
43
+ return status;
44
+ });
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const types_1 = require("../emulator/types");
8
+ const commandUtils_1 = require("../emulator/commandUtils");
9
+ const pretty_print_1 = require("../firestore/pretty-print");
10
+ const firestore_utils_1 = require("./firestore-utils");
11
+ exports.command = new command_1.Command("firestore:operations:describe <operationName>")
12
+ .description("retrieves information about a Cloud Firestore admin operation")
13
+ .option("--database <databaseName>", 'Database ID for which the operation is running. "(default)" if none is provided.')
14
+ .before(commandUtils_1.errorMissingProject)
15
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
16
+ .action(async (operationName, options) => {
17
+ const databaseId = options.database || "(default)";
18
+ operationName = (0, firestore_utils_1.getShortOperationName)(operationName);
19
+ const api = new fsi.FirestoreApi();
20
+ const operation = await api.describeOperation(options.project, databaseId, operationName);
21
+ if (options.json) {
22
+ logger_1.logger.info(JSON.stringify(operation, undefined, 2));
23
+ }
24
+ else {
25
+ const printer = new pretty_print_1.PrettyPrint();
26
+ printer.prettyPrintOperation(operation);
27
+ }
28
+ return operation;
29
+ });
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const types_1 = require("../emulator/types");
8
+ const commandUtils_1 = require("../emulator/commandUtils");
9
+ const pretty_print_1 = require("../firestore/pretty-print");
10
+ exports.command = new command_1.Command("firestore:operations:list")
11
+ .description("list pending Cloud Firestore admin operations and their status")
12
+ .option("--database <databaseName>", 'Database ID for database to list operations for. "(default)" if none is provided.')
13
+ .option("--limit <number>", "The maximum number of operations to list. Uses 100 by default.")
14
+ .before(commandUtils_1.errorMissingProject)
15
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
16
+ .action(async (options) => {
17
+ const databaseId = options.database || "(default)";
18
+ const limit = options.limit === undefined ? 100 : Number(options.limit);
19
+ const api = new fsi.FirestoreApi();
20
+ const { operations } = await api.listOperations(options.project, databaseId, limit);
21
+ if (options.json) {
22
+ logger_1.logger.info(JSON.stringify(operations, undefined, 2));
23
+ }
24
+ else {
25
+ const printer = new pretty_print_1.PrettyPrint();
26
+ printer.prettyPrintOperations(operations);
27
+ }
28
+ return operations;
29
+ });
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getShortOperationName = void 0;
4
+ const error_1 = require("../error");
5
+ function getShortOperationName(operationName) {
6
+ let opName = operationName;
7
+ if (operationName.includes("/operations/")) {
8
+ opName = operationName.split("/operations/")[1];
9
+ }
10
+ if (opName.length === 0 || opName.includes("/")) {
11
+ throw new error_1.FirebaseError(`"${operationName}" is not a valid operation name.`);
12
+ }
13
+ return opName;
14
+ }
15
+ exports.getShortOperationName = getShortOperationName;
@@ -78,7 +78,10 @@ exports.command = new command_1.Command("functions:config:export")
78
78
  .before(requireInteractive_1.default)
79
79
  .action(async (options) => {
80
80
  const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions)[0];
81
- const functionsDir = config.source;
81
+ const configDir = (0, projectConfig_1.resolveConfigDir)(config);
82
+ if (!configDir) {
83
+ throw new error_1.FirebaseError("functions:config:export requires a local env directory. Set functions[].configDir in firebase.json when using remoteSource.");
84
+ }
82
85
  let pInfos = configExport.getProjectInfos(options);
83
86
  checkReservedAliases(pInfos);
84
87
  (0, utils_1.logBullet)("Importing functions configs from projects [" +
@@ -111,6 +114,6 @@ exports.command = new command_1.Command("functions:config:export")
111
114
  filesToWrite[".env"] =
112
115
  `${header}# .env file contains environment variables that applies to all projects.\n`;
113
116
  for (const [filename, content] of Object.entries(filesToWrite)) {
114
- await options.config.askWriteProjectFile(path.join(functionsDir, filename), content);
117
+ await options.config.askWriteProjectFile(path.join(configDir, filename), content);
115
118
  }
116
119
  });
@@ -96,8 +96,13 @@ function load(client) {
96
96
  client.ext.dev.usage = loadCommand("ext-dev-usage");
97
97
  client.firestore = {};
98
98
  client.firestore.delete = loadCommand("firestore-delete");
99
+ client.firestore.bulkDelete = loadCommand("firestore-bulkdelete");
99
100
  client.firestore.indexes = loadCommand("firestore-indexes-list");
100
101
  client.firestore.locations = loadCommand("firestore-locations");
102
+ client.firestore.operations = {};
103
+ client.firestore.operations.cancel = loadCommand("firestore-operations-cancel");
104
+ client.firestore.operations.describe = loadCommand("firestore-operations-describe");
105
+ client.firestore.operations.list = loadCommand("firestore-operations-list");
101
106
  client.firestore.databases = {};
102
107
  client.firestore.databases.list = loadCommand("firestore-databases-list");
103
108
  client.firestore.databases.get = loadCommand("firestore-databases-get");
package/lib/config.js CHANGED
@@ -42,13 +42,25 @@ class Config {
42
42
  });
43
43
  if (this.get("functions")) {
44
44
  if (this.projectDir && fsutils.dirExistsSync(this.path(Config.DEFAULT_FUNCTIONS_SOURCE))) {
45
- if (Array.isArray(this.get("functions"))) {
46
- if (!this.get("functions.[0].source")) {
47
- this.set("functions.[0].source", Config.DEFAULT_FUNCTIONS_SOURCE);
45
+ const funcs = this.get("functions");
46
+ if (Array.isArray(funcs)) {
47
+ let emptyIdx;
48
+ for (let i = 0; i < funcs.length; i++) {
49
+ const hasSource = this.get(`functions.[${i}].source`);
50
+ const hasRemote = this.get(`functions.[${i}].remoteSource`);
51
+ if (!hasSource && !hasRemote) {
52
+ emptyIdx = i;
53
+ break;
54
+ }
55
+ }
56
+ if (emptyIdx !== undefined) {
57
+ this.set(`functions.[${emptyIdx}].source`, Config.DEFAULT_FUNCTIONS_SOURCE);
48
58
  }
49
59
  }
50
60
  else {
51
- if (!this.get("functions.source")) {
61
+ const hasSource = this.get("functions.source");
62
+ const hasRemote = this.get("functions.remoteSource");
63
+ if (!hasSource && !hasRemote) {
52
64
  this.set("functions.source", Config.DEFAULT_FUNCTIONS_SOURCE);
53
65
  }
54
66
  }
@@ -1,29 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listNotes = void 0;
3
+ exports.listEvents = void 0;
4
4
  const logger_1 = require("../logger");
5
5
  const error_1 = require("../error");
6
6
  const utils_1 = require("./utils");
7
- async function listNotes(appId, issueId, noteCount) {
7
+ const filters_1 = require("./filters");
8
+ async function listEvents(appId, filter, pageSize = 1) {
8
9
  const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
9
10
  try {
10
- const queryParams = new URLSearchParams();
11
- queryParams.set("page_size", `${noteCount}`);
12
- logger_1.logger.debug(`[mcp][crashlytics] listNotes called with appId: ${appId}, issueId: ${issueId}, noteCount: ${noteCount}`);
11
+ const queryParams = (0, filters_1.filterToUrlSearchParams)(filter);
12
+ queryParams.set("page_size", `${pageSize}`);
13
+ logger_1.logger.debug(`[crashlytics] listEvents called with appId: ${appId}, filter: ${queryParams.toString()}, pageSize: ${pageSize}`);
13
14
  const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
14
15
  method: "GET",
15
16
  headers: {
16
17
  "Content-Type": "application/json",
17
18
  },
18
- path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}/notes`,
19
+ path: `/projects/${requestProjectNumber}/apps/${appId}/events`,
19
20
  queryParams: queryParams,
20
21
  timeout: utils_1.TIMEOUT,
21
22
  });
22
23
  return response.body;
23
24
  }
24
25
  catch (err) {
25
- logger_1.logger.debug(err.message);
26
- throw new error_1.FirebaseError(`Failed to fetch notes for issue ${issueId} for app ${appId}. Error: ${err}.`, { original: err });
26
+ throw new error_1.FirebaseError(`Failed to list events for app_id ${appId}.`, {
27
+ original: (0, error_1.getError)(err),
28
+ });
27
29
  }
28
30
  }
29
- exports.listNotes = listNotes;
31
+ exports.listEvents = listEvents;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterToUrlSearchParams = exports.EventFilterSchema = exports.IssueIdSchema = exports.ApplicationIdSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.ApplicationIdSchema = zod_1.z
6
+ .string()
7
+ .describe("Firebase app id. For an Android application, read the " +
8
+ "mobilesdk_app_id value specified in the google-services.json file for " +
9
+ "the current package name. For an iOS Application, read the GOOGLE_APP_ID " +
10
+ "from GoogleService-Info.plist. If neither is available, ask the user to " +
11
+ "provide the app id.");
12
+ exports.IssueIdSchema = zod_1.z.string().describe("Crashlytics issue id, as hexidecimal uuid");
13
+ exports.EventFilterSchema = zod_1.z
14
+ .object({
15
+ intervalStartTime: zod_1.z.string().optional().describe(`A timestamp in ISO 8601 string format`),
16
+ intervalEndTime: zod_1.z.string().optional().describe(`A timestamp in ISO 8601 string format.`),
17
+ versionDisplayNames: zod_1.z
18
+ .array(zod_1.z.string())
19
+ .optional()
20
+ .describe(`The version display names should be obtained from an API response.`),
21
+ issueId: zod_1.z.string().optional().describe(`Count events for the given issue`),
22
+ issueVariantId: zod_1.z.string().optional().describe(`Count events for the given issue variant`),
23
+ issueErrorTypes: zod_1.z
24
+ .array(zod_1.z.enum(["FATAL", "NON_FATAL", "ANR"]))
25
+ .optional()
26
+ .describe(`Count FATAL events (crashes), NON_FATAL events (exceptions) or ANR events (application not responding)`),
27
+ issueSignals: zod_1.z
28
+ .array(zod_1.z.enum(["SIGNAL_EARLY", "SIGNAL_FRESH", "SIGNAL_REGRESSED", "SIGNAL_REPETITIVE"]))
29
+ .optional()
30
+ .describe(`Count events matching the given signals`),
31
+ operatingSystemDisplayNames: zod_1.z
32
+ .array(zod_1.z.string())
33
+ .optional()
34
+ .describe(`The operating system displayNames should be obtained from an API response`),
35
+ deviceDisplayNames: zod_1.z
36
+ .array(zod_1.z.string())
37
+ .optional()
38
+ .describe(`The operating system displayNames should be obtained from an API response`),
39
+ deviceFormFactors: zod_1.z
40
+ .array(zod_1.z.enum(["PHONE", "TABLET", "DESKTOP", "TV", "WATCH"]))
41
+ .optional()
42
+ .describe(`Count events originating from the given device form factors`),
43
+ })
44
+ .optional()
45
+ .describe(`Only events matching the given filters will be counted. All filters are optional.
46
+ If setting a time interval, set both intervalStartTime and intervalEndTime.`);
47
+ const toolToParamMap = {
48
+ intervalStartTime: "filter.interval.start_time",
49
+ intervalEndTime: "filter.interval.end_time",
50
+ versionDisplayNames: "filter.version.display_names",
51
+ issueId: "filter.issue.id",
52
+ issueVariantId: "filter.issue.variant_id",
53
+ issueErrorTypes: "filter.issue.error_types",
54
+ issueSignals: "filter.issue.signals",
55
+ operatingSystemDisplayNames: "filter.operating_system.display_names",
56
+ deviceDisplayNames: "filter.device.display_names",
57
+ deviceFormFactors: "filter.device.form_factors",
58
+ };
59
+ function filterToUrlSearchParams(filter) {
60
+ const params = new URLSearchParams();
61
+ for (const [key, value] of Object.entries(filter || {})) {
62
+ if (value === undefined) {
63
+ continue;
64
+ }
65
+ const paramKey = toolToParamMap[key];
66
+ if (Array.isArray(value)) {
67
+ for (const v of value) {
68
+ params.append(paramKey, v);
69
+ }
70
+ }
71
+ else if (value) {
72
+ params.set(paramKey, value);
73
+ }
74
+ }
75
+ return params;
76
+ }
77
+ exports.filterToUrlSearchParams = filterToUrlSearchParams;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateIssue = exports.getIssue = void 0;
4
+ const logger_1 = require("../logger");
5
+ const error_1 = require("../error");
6
+ const utils_1 = require("./utils");
7
+ async function getIssue(appId, issueId) {
8
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
9
+ logger_1.logger.debug(`[crashlytics] getIssue called with appId: ${appId}, issueId: ${issueId}`);
10
+ try {
11
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
12
+ method: "GET",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ },
16
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}`,
17
+ timeout: utils_1.TIMEOUT,
18
+ });
19
+ return response.body;
20
+ }
21
+ catch (err) {
22
+ throw new error_1.FirebaseError(`Failed to fetch issue for appId ${appId}, issueId ${issueId}`, {
23
+ original: (0, error_1.getError)(err),
24
+ });
25
+ }
26
+ }
27
+ exports.getIssue = getIssue;
28
+ async function updateIssue(appId, issueId, state) {
29
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
30
+ try {
31
+ logger_1.logger.debug(`[crashlytics] updateIssue called with appId: ${appId}, issueId: ${issueId}, state: ${state}`);
32
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
33
+ method: "PATCH",
34
+ headers: {
35
+ "Content-Type": "application/json",
36
+ },
37
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}`,
38
+ queryParams: { updateMask: "state" },
39
+ body: { issue: { state } },
40
+ timeout: utils_1.TIMEOUT,
41
+ });
42
+ return response.body;
43
+ }
44
+ catch (err) {
45
+ throw new error_1.FirebaseError(`Failed to update issue ${issueId} for app ${appId}`, {
46
+ original: (0, error_1.getError)(err),
47
+ });
48
+ }
49
+ }
50
+ exports.updateIssue = updateIssue;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listNotes = exports.deleteNote = exports.createNote = void 0;
4
+ const logger_1 = require("../logger");
5
+ const error_1 = require("../error");
6
+ const utils_1 = require("./utils");
7
+ async function createNote(appId, issueId, note) {
8
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
9
+ logger_1.logger.debug(`[crashlytics] createNote called with appId: ${appId}, issueId: ${issueId}, note: ${note}`);
10
+ try {
11
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
12
+ method: "POST",
13
+ headers: {
14
+ "Content-Type": "application/json",
15
+ },
16
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}/notes`,
17
+ body: { body: note },
18
+ timeout: utils_1.TIMEOUT,
19
+ });
20
+ return response.body;
21
+ }
22
+ catch (err) {
23
+ throw new error_1.FirebaseError(`Failed to create note for issue ${issueId}, app ${appId}`, {
24
+ original: (0, error_1.getError)(err),
25
+ });
26
+ }
27
+ }
28
+ exports.createNote = createNote;
29
+ async function deleteNote(appId, issueId, noteId) {
30
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
31
+ logger_1.logger.debug(`[crashlytics] deleteNote called with appId: ${appId}, issueId: ${issueId}, noteId: ${noteId}`);
32
+ try {
33
+ await utils_1.CRASHLYTICS_API_CLIENT.request({
34
+ method: "DELETE",
35
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}/notes/${noteId}`,
36
+ timeout: utils_1.TIMEOUT,
37
+ });
38
+ }
39
+ catch (err) {
40
+ throw new error_1.FirebaseError(`Failed to delete note ${noteId} from issue ${issueId} for app ${appId}`, { original: (0, error_1.getError)(err) });
41
+ }
42
+ }
43
+ exports.deleteNote = deleteNote;
44
+ async function listNotes(appId, issueId, pageSize = 20) {
45
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
46
+ try {
47
+ const queryParams = new URLSearchParams();
48
+ queryParams.set("page_size", `${pageSize}`);
49
+ logger_1.logger.debug(`[crashlytics] listNotes called with appId: ${appId}, issueId: ${issueId}, pageSize: ${pageSize}`);
50
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
51
+ method: "GET",
52
+ headers: {
53
+ "Content-Type": "application/json",
54
+ },
55
+ path: `/projects/${requestProjectNumber}/apps/${appId}/issues/${issueId}/notes`,
56
+ queryParams: queryParams,
57
+ timeout: utils_1.TIMEOUT,
58
+ });
59
+ return response.body.notes || [];
60
+ }
61
+ catch (err) {
62
+ throw new error_1.FirebaseError(`Failed to list notes for issue ${issueId}, app ${appId}`, {
63
+ original: (0, error_1.getError)(err),
64
+ });
65
+ }
66
+ }
67
+ exports.listNotes = listNotes;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getReport = exports.CrashlyticsReport = exports.ReportInputSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const logger_1 = require("../logger");
6
+ const error_1 = require("../error");
7
+ const utils_1 = require("./utils");
8
+ const filters_1 = require("./filters");
9
+ const DEFAULT_PAGE_SIZE = 10;
10
+ exports.ReportInputSchema = zod_1.z.object({
11
+ appId: filters_1.ApplicationIdSchema,
12
+ filter: filters_1.EventFilterSchema,
13
+ pageSize: zod_1.z.number().optional().describe("Number of rows to return").default(DEFAULT_PAGE_SIZE),
14
+ });
15
+ var CrashlyticsReport;
16
+ (function (CrashlyticsReport) {
17
+ CrashlyticsReport["TopIssues"] = "topIssues";
18
+ CrashlyticsReport["TopVariants"] = "topVariants";
19
+ CrashlyticsReport["TopVersions"] = "topVersions";
20
+ CrashlyticsReport["TopOperatingSystems"] = "topOperatingSystems";
21
+ CrashlyticsReport["TopAppleDevices"] = "topAppleDevices";
22
+ CrashlyticsReport["TopAndroidDevices"] = "topAndroidDevices";
23
+ })(CrashlyticsReport = exports.CrashlyticsReport || (exports.CrashlyticsReport = {}));
24
+ async function getReport(report, appId, filter, pageSize = DEFAULT_PAGE_SIZE) {
25
+ const requestProjectNumber = (0, utils_1.parseProjectNumber)(appId);
26
+ try {
27
+ const queryParams = (0, filters_1.filterToUrlSearchParams)(filter);
28
+ queryParams.set("page_size", `${pageSize}`);
29
+ logger_1.logger.debug(`[crashlytics] report ${report} called with appId: ${appId} filter: ${queryParams.toString()}, page_size: ${pageSize}`);
30
+ const response = await utils_1.CRASHLYTICS_API_CLIENT.request({
31
+ method: "GET",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ },
35
+ path: `/projects/${requestProjectNumber}/apps/${appId}/reports/${report}`,
36
+ queryParams: queryParams,
37
+ timeout: utils_1.TIMEOUT,
38
+ });
39
+ return response.body;
40
+ }
41
+ catch (err) {
42
+ throw new error_1.FirebaseError(`Failed to fetch ${report} report for app: ${appId}`, {
43
+ original: (0, error_1.getError)(err),
44
+ });
45
+ }
46
+ }
47
+ exports.getReport = getReport;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ThreadState = exports.Signal = exports.State = exports.SessionEventType = exports.FormFactor = exports.TrackType = exports.ErrorType = void 0;
4
+ var ErrorType;
5
+ (function (ErrorType) {
6
+ ErrorType["ERROR_TYPE_UNSPECIFIED"] = "ERROR_TYPE_UNSPECIFIED";
7
+ ErrorType["FATAL"] = "FATAL";
8
+ ErrorType["NON_FATAL"] = "NON_FATAL";
9
+ ErrorType["ANR"] = "ANR";
10
+ })(ErrorType = exports.ErrorType || (exports.ErrorType = {}));
11
+ var TrackType;
12
+ (function (TrackType) {
13
+ TrackType["TRACK_TYPE_UNSPECIFIED"] = "TRACK_TYPE_UNSPECIFIED";
14
+ TrackType["TRACK_TYPE_PROD"] = "TRACK_TYPE_PROD";
15
+ TrackType["TRACK_TYPE_INTERNAL"] = "TRACK_TYPE_INTERNAL";
16
+ TrackType["TRACK_TYPE_OPEN_TESTING"] = "TRACK_TYPE_OPEN_TESTING";
17
+ TrackType["TRACK_TYPE_CLOSED_TESTING"] = "TRACK_TYPE_CLOSED_TESTING";
18
+ TrackType["TRACK_TYPE_EARLY_ACCESS"] = "TRACK_TYPE_EARLY_ACCESS";
19
+ })(TrackType = exports.TrackType || (exports.TrackType = {}));
20
+ var FormFactor;
21
+ (function (FormFactor) {
22
+ FormFactor["FORM_FACTOR_UNSPECIFIED"] = "FORM_FACTOR_UNSPECIFIED";
23
+ FormFactor["PHONE"] = "PHONE";
24
+ FormFactor["TABLET"] = "TABLET";
25
+ FormFactor["DESKTOP"] = "DESKTOP";
26
+ FormFactor["TV"] = "TV";
27
+ FormFactor["WATCH"] = "WATCH";
28
+ })(FormFactor = exports.FormFactor || (exports.FormFactor = {}));
29
+ var SessionEventType;
30
+ (function (SessionEventType) {
31
+ SessionEventType["SESSION_EVENT_TYPE_UNKNOWN"] = "SESSION_EVENT_TYPE_UNKNOWN";
32
+ SessionEventType["SESSION_START"] = "SESSION_START";
33
+ })(SessionEventType = exports.SessionEventType || (exports.SessionEventType = {}));
34
+ var State;
35
+ (function (State) {
36
+ State["STATE_UNSPECIFIED"] = "STATE_UNSPECIFIED";
37
+ State["OPEN"] = "OPEN";
38
+ State["CLOSED"] = "CLOSED";
39
+ State["MUTED"] = "MUTED";
40
+ })(State = exports.State || (exports.State = {}));
41
+ var Signal;
42
+ (function (Signal) {
43
+ Signal["SIGNAL_UNSPECIFIED"] = "SIGNAL_UNSPECIFIED";
44
+ Signal["SIGNAL_EARLY"] = "SIGNAL_EARLY";
45
+ Signal["SIGNAL_FRESH"] = "SIGNAL_FRESH";
46
+ Signal["SIGNAL_REGRESSED"] = "SIGNAL_REGRESSED";
47
+ Signal["SIGNAL_REPETITIVE"] = "SIGNAL_REPETITIVE";
48
+ })(Signal = exports.Signal || (exports.Signal = {}));
49
+ var ThreadState;
50
+ (function (ThreadState) {
51
+ ThreadState["STATE_UNSPECIFIED"] = "STATE_UNSPECIFIED";
52
+ ThreadState["THREAD_STATE_TERMINATED"] = "THREAD_STATE_TERMINATED";
53
+ ThreadState["THREAD_STATE_RUNNABLE"] = "THREAD_STATE_RUNNABLE";
54
+ ThreadState["THREAD_STATE_TIMED_WAITING"] = "THREAD_STATE_TIMED_WAITING";
55
+ ThreadState["THREAD_STATE_BLOCKED"] = "THREAD_STATE_BLOCKED";
56
+ ThreadState["THREAD_STATE_WAITING"] = "THREAD_STATE_WAITING";
57
+ ThreadState["THREAD_STATE_NEW"] = "THREAD_STATE_NEW";
58
+ ThreadState["THREAD_STATE_NATIVE_RUNNABLE"] = "THREAD_STATE_NATIVE_RUNNABLE";
59
+ ThreadState["THREAD_STATE_NATIVE_WAITING"] = "THREAD_STATE_NATIVE_WAITING";
60
+ })(ThreadState = exports.ThreadState || (exports.ThreadState = {}));
@@ -4,10 +4,10 @@ exports.ensureGIFApis = exports.ensureApis = void 0;
4
4
  const api = require("../api");
5
5
  const ensureApiEnabled_1 = require("../ensureApiEnabled");
6
6
  const prefix = "dataconnect";
7
- async function ensureApis(projectId) {
7
+ async function ensureApis(projectId, silent = false) {
8
8
  await Promise.all([
9
- (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix),
10
- (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix),
9
+ (0, ensureApiEnabled_1.ensure)(projectId, api.dataconnectOrigin(), prefix, silent),
10
+ (0, ensureApiEnabled_1.ensure)(projectId, api.cloudSQLAdminOrigin(), prefix, silent),
11
11
  ]);
12
12
  }
13
13
  exports.ensureApis = ensureApis;
@@ -60,7 +60,8 @@ async function default_1(context, options) {
60
60
  }
61
61
  }
62
62
  for (const cfg of context.backendConfigs.values()) {
63
- const { projectSourcePath, zippedSourcePath } = await (0, util_1.createArchive)(cfg, options.projectRoot);
63
+ const projectSourcePath = options.projectRoot ? options.projectRoot : process.cwd();
64
+ const zippedSourcePath = await (0, util_1.createArchive)(cfg, projectSourcePath);
64
65
  const backendLocation = context.backendLocations.get(cfg.backendId);
65
66
  if (!backendLocation) {
66
67
  throw new error_1.FirebaseError(`Failed to find location for backend ${cfg.backendId}. Please contact support with the contents of your firebase-debug.log to report your issue.`);