firebase-tools 14.16.0 → 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 (39) hide show
  1. package/lib/crashlytics/{listNotes.js → events.js} +11 -9
  2. package/lib/crashlytics/filters.js +77 -0
  3. package/lib/crashlytics/issues.js +50 -0
  4. package/lib/crashlytics/notes.js +67 -0
  5. package/lib/crashlytics/reports.js +47 -0
  6. package/lib/crashlytics/types.js +60 -0
  7. package/lib/deploy/apphosting/deploy.js +2 -1
  8. package/lib/deploy/apphosting/util.js +5 -8
  9. package/lib/emulator/apphosting/developmentServer.js +3 -3
  10. package/lib/emulator/apphosting/serve.js +29 -29
  11. package/lib/emulator/initEmulators.js +1 -1
  12. package/lib/init/features/dataconnect/index.js +29 -4
  13. package/lib/init/features/dataconnect/sdk.js +17 -12
  14. package/lib/mcp/tools/crashlytics/events.js +42 -0
  15. package/lib/mcp/tools/crashlytics/index.js +16 -20
  16. package/lib/mcp/tools/crashlytics/issues.js +56 -0
  17. package/lib/mcp/tools/crashlytics/notes.js +78 -0
  18. package/lib/mcp/tools/crashlytics/reports.js +100 -0
  19. package/package.json +1 -1
  20. package/lib/crashlytics/addNote.js +0 -27
  21. package/lib/crashlytics/deleteNote.js +0 -23
  22. package/lib/crashlytics/getIssueDetails.js +0 -26
  23. package/lib/crashlytics/getSampleCrash.js +0 -34
  24. package/lib/crashlytics/listTopDevices.js +0 -33
  25. package/lib/crashlytics/listTopIssues.js +0 -30
  26. package/lib/crashlytics/listTopOperatingSystems.js +0 -32
  27. package/lib/crashlytics/listTopVersions.js +0 -32
  28. package/lib/crashlytics/updateIssue.js +0 -35
  29. package/lib/mcp/tools/crashlytics/add_note.js +0 -32
  30. package/lib/mcp/tools/crashlytics/constants.js +0 -11
  31. package/lib/mcp/tools/crashlytics/delete_note.js +0 -35
  32. package/lib/mcp/tools/crashlytics/get_issue_details.js +0 -31
  33. package/lib/mcp/tools/crashlytics/get_sample_crash.js +0 -43
  34. package/lib/mcp/tools/crashlytics/list_notes.js +0 -37
  35. package/lib/mcp/tools/crashlytics/list_top_devices.js +0 -33
  36. package/lib/mcp/tools/crashlytics/list_top_issues.js +0 -38
  37. package/lib/mcp/tools/crashlytics/list_top_operating_systems.js +0 -33
  38. package/lib/mcp/tools/crashlytics/list_top_versions.js +0 -33
  39. package/lib/mcp/tools/crashlytics/update_issue.js +0 -37
@@ -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 = {}));
@@ -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.`);
@@ -7,28 +7,25 @@ const path = require("path");
7
7
  const tmp = require("tmp");
8
8
  const error_1 = require("../../error");
9
9
  const fsAsync = require("../../fsAsync");
10
- async function createArchive(config, projectRoot) {
10
+ async function createArchive(config, rootDir) {
11
11
  const tmpFile = tmp.fileSync({ prefix: `${config.backendId}-`, postfix: ".zip" }).name;
12
12
  const fileStream = fs.createWriteStream(tmpFile, {
13
13
  flags: "w",
14
14
  encoding: "binary",
15
15
  });
16
16
  const archive = archiver("zip");
17
- if (!projectRoot) {
18
- projectRoot = process.cwd();
19
- }
20
17
  const ignore = config.ignore || ["node_modules", ".git"];
21
18
  ignore.push("firebase-debug.log", "firebase-debug.*.log");
22
- const gitIgnorePatterns = parseGitIgnorePatterns(projectRoot);
19
+ const gitIgnorePatterns = parseGitIgnorePatterns(rootDir);
23
20
  ignore.push(...gitIgnorePatterns);
24
21
  try {
25
22
  const files = await fsAsync.readdirRecursive({
26
- path: projectRoot,
23
+ path: rootDir,
27
24
  ignore: ignore,
28
25
  isGitIgnore: true,
29
26
  });
30
27
  for (const file of files) {
31
- const name = path.relative(projectRoot, file.name);
28
+ const name = path.relative(rootDir, file.name);
32
29
  archive.file(file.name, {
33
30
  name,
34
31
  mode: file.mode,
@@ -39,7 +36,7 @@ async function createArchive(config, projectRoot) {
39
36
  catch (err) {
40
37
  throw new error_1.FirebaseError("Could not read source directory. Remove links and shortcuts and try again.", { original: err, exit: 1 });
41
38
  }
42
- return { projectSourcePath: projectRoot, zippedSourcePath: tmpFile };
39
+ return tmpFile;
43
40
  }
44
41
  exports.createArchive = createArchive;
45
42
  function parseGitIgnorePatterns(projectRoot, gitIgnorePath = ".gitignore") {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.detectStartCommand = exports.detectPackageManager = exports.logger = void 0;
3
+ exports.detectPackageManagerStartCommand = exports.detectPackageManager = exports.logger = void 0;
4
4
  const fs_extra_1 = require("fs-extra");
5
5
  const path_1 = require("path");
6
6
  const emulatorLogger_1 = require("../emulatorLogger");
@@ -20,7 +20,7 @@ async function detectPackageManager(rootdir) {
20
20
  throw new error_1.FirebaseError("Unsupported package manager");
21
21
  }
22
22
  exports.detectPackageManager = detectPackageManager;
23
- async function detectStartCommand(rootDir) {
23
+ async function detectPackageManagerStartCommand(rootDir) {
24
24
  try {
25
25
  const packageManager = await detectPackageManager(rootDir);
26
26
  return `${packageManager} run dev`;
@@ -29,4 +29,4 @@ async function detectStartCommand(rootDir) {
29
29
  throw new error_1.FirebaseError("Failed to auto-detect your project's start command. Consider manually setting the start command by setting `firebase.json#emulators.apphosting.startCommand`");
30
30
  }
31
31
  }
32
- exports.detectStartCommand = detectStartCommand;
32
+ exports.detectPackageManagerStartCommand = detectPackageManagerStartCommand;
@@ -22,17 +22,6 @@ const fetchWebSetup_1 = require("../../fetchWebSetup");
22
22
  const apps_1 = require("../../management/apps");
23
23
  const child_process_1 = require("child_process");
24
24
  const semver_1 = require("semver");
25
- async function start(options) {
26
- var _a;
27
- const hostname = constants_1.DEFAULT_HOST;
28
- let port = (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PORTS.apphosting;
29
- while (!(await availablePort(hostname, port))) {
30
- port += 1;
31
- }
32
- await serve(options === null || options === void 0 ? void 0 : options.projectId, options === null || options === void 0 ? void 0 : options.backendId, port, options === null || options === void 0 ? void 0 : options.startCommand, options === null || options === void 0 ? void 0 : options.rootDirectory);
33
- return { hostname, port };
34
- }
35
- exports.start = start;
36
25
  const secretResourceRegex = /^projects\/([^/]+)\/secrets\/([^/]+)(?:\/versions\/((?:latest)|\d+))?$/;
37
26
  const secretShorthandRegex = /^([^/@]+)(?:@((?:latest)|\d+))?$/;
38
27
  async function loadSecret(project, name) {
@@ -70,21 +59,41 @@ async function loadSecret(project, name) {
70
59
  throw err;
71
60
  }
72
61
  }
73
- async function serve(projectId, backendId, port, startCommand, backendRelativeDir) {
74
- backendRelativeDir = backendRelativeDir !== null && backendRelativeDir !== void 0 ? backendRelativeDir : "./";
75
- const backendRoot = (0, projectPath_1.resolveProjectPath)({}, backendRelativeDir);
62
+ async function start(options) {
63
+ var _a, _b;
64
+ const hostname = constants_1.DEFAULT_HOST;
65
+ let port = (_a = options === null || options === void 0 ? void 0 : options.port) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_PORTS.apphosting;
66
+ while (!(await availablePort(hostname, port))) {
67
+ port += 1;
68
+ }
69
+ const backendRoot = (0, projectPath_1.resolveProjectPath)({}, (_b = options === null || options === void 0 ? void 0 : options.rootDirectory) !== null && _b !== void 0 ? _b : "./");
70
+ let startCommand;
71
+ if (options === null || options === void 0 ? void 0 : options.startCommand) {
72
+ startCommand = options === null || options === void 0 ? void 0 : options.startCommand;
73
+ if (startCommand.includes("--port") || startCommand.includes(" -p ")) {
74
+ throw new error_1.FirebaseError("Specifying a port in the start command is not supported by the apphosting emulator");
75
+ }
76
+ if (startCommand.includes("ng serve")) {
77
+ startCommand += ` --port ${port}`;
78
+ }
79
+ developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `running custom start command: '${startCommand}'`);
80
+ }
81
+ else {
82
+ startCommand = await (0, developmentServer_1.detectPackageManagerStartCommand)(backendRoot);
83
+ developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `starting app with: '${startCommand}'`);
84
+ }
76
85
  const apphostingLocalConfig = await (0, config_1.getLocalAppHostingConfiguration)(backendRoot);
77
86
  const resolveEnv = Object.entries(apphostingLocalConfig.env).map(async ([key, value]) => [
78
87
  key,
79
- value.value ? value.value : await loadSecret(projectId, value.secret),
88
+ value.value ? value.value : await loadSecret(options === null || options === void 0 ? void 0 : options.projectId, value.secret),
80
89
  ]);
81
- const environmentVariablesToInject = Object.assign(Object.assign(Object.assign({ NODE_ENV: process.env.NODE_ENV }, getEmulatorEnvs()), Object.fromEntries(await Promise.all(resolveEnv))), { FIREBASE_APP_HOSTING: "1", X_GOOGLE_TARGET_PLATFORM: "fah", GCLOUD_PROJECT: projectId, PROJECT_ID: projectId, PORT: port.toString() });
90
+ const environmentVariablesToInject = Object.assign(Object.assign(Object.assign({ NODE_ENV: process.env.NODE_ENV }, getEmulatorEnvs()), Object.fromEntries(await Promise.all(resolveEnv))), { FIREBASE_APP_HOSTING: "1", X_GOOGLE_TARGET_PLATFORM: "fah", GCLOUD_PROJECT: options === null || options === void 0 ? void 0 : options.projectId, PROJECT_ID: options === null || options === void 0 ? void 0 : options.projectId, PORT: port.toString() });
82
91
  const packageManager = await (0, developmentServer_1.detectPackageManager)(backendRoot).catch(() => undefined);
83
92
  if (packageManager === "pnpm") {
84
93
  (0, utils_1.logLabeledWarning)("apphosting", `Firebase JS SDK autoinit does not currently support PNPM.`);
85
94
  }
86
95
  else {
87
- const webappConfig = await getBackendAppConfig(projectId, backendId);
96
+ const webappConfig = await getBackendAppConfig(options === null || options === void 0 ? void 0 : options.projectId, options === null || options === void 0 ? void 0 : options.backendId);
88
97
  if (webappConfig) {
89
98
  environmentVariablesToInject["FIREBASE_WEBAPP_CONFIG"] || (environmentVariablesToInject["FIREBASE_WEBAPP_CONFIG"] = JSON.stringify(webappConfig));
90
99
  environmentVariablesToInject["FIREBASE_CONFIG"] || (environmentVariablesToInject["FIREBASE_CONFIG"] = JSON.stringify({
@@ -95,23 +104,14 @@ async function serve(projectId, backendId, port, startCommand, backendRelativeDi
95
104
  }
96
105
  await tripFirebasePostinstall(backendRoot, environmentVariablesToInject);
97
106
  }
98
- if (startCommand) {
99
- developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `running custom start command: '${startCommand}'`);
100
- (0, spawn_1.spawnWithCommandString)(startCommand, backendRoot, environmentVariablesToInject)
101
- .catch((err) => {
102
- developmentServer_2.logger.logLabeled("ERROR", types_1.Emulators.APPHOSTING, `failed to start Dev Server: ${err}`);
103
- })
104
- .then(() => developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `Dev Server stopped`));
105
- return;
106
- }
107
- const detectedStartCommand = await (0, developmentServer_1.detectStartCommand)(backendRoot);
108
- developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `starting app with: '${detectedStartCommand}'`);
109
- (0, spawn_1.spawnWithCommandString)(detectedStartCommand, backendRoot, environmentVariablesToInject)
107
+ (0, spawn_1.spawnWithCommandString)(startCommand, backendRoot, environmentVariablesToInject)
110
108
  .catch((err) => {
111
109
  developmentServer_2.logger.logLabeled("ERROR", types_1.Emulators.APPHOSTING, `failed to start Dev Server: ${err}`);
112
110
  })
113
111
  .then(() => developmentServer_2.logger.logLabeled("BULLET", types_1.Emulators.APPHOSTING, `Dev Server stopped`));
112
+ return { hostname, port };
114
113
  }
114
+ exports.start = start;
115
115
  function availablePort(host, port) {
116
116
  return (0, portUtils_1.checkListenable)({
117
117
  address: host,
@@ -24,7 +24,7 @@ exports.AdditionalInitFns = {
24
24
  additionalConfigs.set("rootDirectory", backendRelativeDir);
25
25
  const backendRoot = (0, path_1.join)(cwd, backendRelativeDir);
26
26
  try {
27
- const startCommand = await (0, developmentServer_1.detectStartCommand)(backendRoot);
27
+ const startCommand = await (0, developmentServer_1.detectPackageManagerStartCommand)(backendRoot);
28
28
  additionalConfigs.set("startCommand", startCommand);
29
29
  }
30
30
  catch (e) {
@@ -424,7 +424,7 @@ async function promptForCloudSQL(setup, info) {
424
424
  info.locationId = await (0, prompt_1.select)({
425
425
  message: "What location would like to use?",
426
426
  choices,
427
- default: "us-central1",
427
+ default: "us-east4",
428
428
  });
429
429
  info.shouldProvisionCSQL = await (0, prompt_1.confirm)({
430
430
  message: `Would you like to provision your Cloud SQL instance and database now?`,
@@ -452,14 +452,39 @@ async function locationChoices(setup) {
452
452
  }
453
453
  else {
454
454
  return [
455
- { name: "us-central1", value: "us-central1" },
456
- { name: "europe-north1", value: "europe-north1" },
455
+ { name: "asia-east1", value: "asia-east1" },
456
+ { name: "asia-east2", value: "asia-east2" },
457
+ { name: "asia-northeast1", value: "asia-northeast1" },
458
+ { name: "asia-northeast2", value: "asia-northeast2" },
459
+ { name: "asia-northeast3", value: "asia-northeast3" },
460
+ { name: "asia-south1", value: "asia-south1" },
461
+ { name: "asia-southeast1", value: "asia-southeast1" },
462
+ { name: "asia-southeast2", value: "asia-southeast2" },
463
+ { name: "australia-southeast1", value: "australia-southeast1" },
464
+ { name: "australia-southeast2", value: "australia-southeast2" },
457
465
  { name: "europe-central2", value: "europe-central2" },
466
+ { name: "europe-north1", value: "europe-north1" },
467
+ { name: "europe-southwest1", value: "europe-southwest1" },
458
468
  { name: "europe-west1", value: "europe-west1" },
469
+ { name: "europe-west2", value: "europe-west2" },
470
+ { name: "europe-west3", value: "europe-west3" },
471
+ { name: "europe-west4", value: "europe-west4" },
472
+ { name: "europe-west6", value: "europe-west6" },
473
+ { name: "europe-west8", value: "europe-west8" },
474
+ { name: "europe-west9", value: "europe-west9" },
475
+ { name: "me-west1", value: "me-west1" },
476
+ { name: "northamerica-northeast1", value: "northamerica-northeast1" },
477
+ { name: "northamerica-northeast2", value: "northamerica-northeast2" },
478
+ { name: "southamerica-east1", value: "southamerica-east1" },
459
479
  { name: "southamerica-west1", value: "southamerica-west1" },
480
+ { name: "us-central1", value: "us-central1" },
481
+ { name: "us-east1", value: "us-east1" },
460
482
  { name: "us-east4", value: "us-east4" },
483
+ { name: "us-south1", value: "us-south1" },
461
484
  { name: "us-west1", value: "us-west1" },
462
- { name: "asia-southeast1", value: "asia-southeast1" },
485
+ { name: "us-west2", value: "us-west2" },
486
+ { name: "us-west3", value: "us-west3" },
487
+ { name: "us-west4", value: "us-west4" },
463
488
  ];
464
489
  }
465
490
  }
@@ -41,18 +41,23 @@ async function askQuestions(setup) {
41
41
  { name: "no", value: "no" },
42
42
  ],
43
43
  });
44
- switch (choice) {
45
- case "react":
46
- await (0, create_app_1.createReactApp)((0, utils_1.newUniqueId)("web-app", (0, fsutils_1.listFiles)(cwd)));
47
- break;
48
- case "next":
49
- await (0, create_app_1.createNextApp)((0, utils_1.newUniqueId)("web-app", (0, fsutils_1.listFiles)(cwd)));
50
- break;
51
- case "flutter":
52
- await (0, create_app_1.createFlutterApp)((0, utils_1.newUniqueId)("flutter_app", (0, fsutils_1.listFiles)(cwd)));
53
- break;
54
- case "no":
55
- break;
44
+ try {
45
+ switch (choice) {
46
+ case "react":
47
+ await (0, create_app_1.createReactApp)((0, utils_1.newUniqueId)("web-app", (0, fsutils_1.listFiles)(cwd)));
48
+ break;
49
+ case "next":
50
+ await (0, create_app_1.createNextApp)((0, utils_1.newUniqueId)("web-app", (0, fsutils_1.listFiles)(cwd)));
51
+ break;
52
+ case "flutter":
53
+ await (0, create_app_1.createFlutterApp)((0, utils_1.newUniqueId)("flutter_app", (0, fsutils_1.listFiles)(cwd)));
54
+ break;
55
+ case "no":
56
+ break;
57
+ }
58
+ }
59
+ catch (err) {
60
+ (0, utils_1.logLabeledError)("dataconnect", `Failed to create a ${choice} app template`);
56
61
  }
57
62
  }
58
63
  setup.featureInfo = setup.featureInfo || {};