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
@@ -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") {
@@ -51,7 +51,7 @@ async function uploadSourceV2(projectId, source, wantBackend) {
51
51
  return res.storageSource;
52
52
  }
53
53
  async function uploadCodebase(context, codebase, wantBackend) {
54
- var _a;
54
+ var _a, _b, _c, _d;
55
55
  const source = (_a = context.sources) === null || _a === void 0 ? void 0 : _a[codebase];
56
56
  if (!source || (!source.functionsSourceV1 && !source.functionsSourceV2)) {
57
57
  return;
@@ -67,9 +67,10 @@ async function uploadCodebase(context, codebase, wantBackend) {
67
67
  if (storage) {
68
68
  source.storage = storage;
69
69
  }
70
- const sourceDir = (0, projectConfig_1.configForCodebase)(context.config, codebase).source;
70
+ const cfg = (0, projectConfig_1.configForCodebase)(context.config, codebase);
71
+ const label = (_d = (_b = cfg.source) !== null && _b !== void 0 ? _b : (_c = cfg.remoteSource) === null || _c === void 0 ? void 0 : _c.dir) !== null && _d !== void 0 ? _d : "remote";
71
72
  if (uploads.length) {
72
- (0, utils_1.logSuccess)(`${clc.green(clc.bold("functions:"))} ${clc.bold(sourceDir)} folder uploaded successfully`);
73
+ (0, utils_1.logLabeledSuccess)("functions", `${clc.bold(label)} source uploaded successfully`);
73
74
  }
74
75
  }
75
76
  catch (err) {
@@ -73,12 +73,13 @@ async function prepare(context, options, payload) {
73
73
  for (const [codebase, wantBuild] of Object.entries(wantBuilds)) {
74
74
  const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
75
75
  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
76
+ const localCfg = (0, projectConfig_1.requireLocal)(config, "Remote sources are not supported.");
76
77
  const userEnvOpt = {
77
- functionsSource: options.config.path(config.source),
78
+ functionsSource: options.config.path(localCfg.source),
78
79
  projectId: projectId,
79
80
  projectAlias: options.projectAlias,
80
81
  };
81
- proto.convertIfPresent(userEnvOpt, config, "configDir", (cd) => options.config.path(cd));
82
+ proto.convertIfPresent(userEnvOpt, localCfg, "configDir", (cd) => options.config.path(cd));
82
83
  const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
83
84
  const envs = Object.assign(Object.assign({}, userEnvs), firebaseEnvs);
84
85
  const { backend: wantBackend, envs: resolvedEnvs } = await build.resolveBackend({
@@ -142,20 +143,21 @@ async function prepare(context, options, payload) {
142
143
  validate.endpointsAreUnique(wantBackends);
143
144
  context.sources = {};
144
145
  for (const [codebase, wantBackend] of Object.entries(wantBackends)) {
145
- const config = (0, projectConfig_1.configForCodebase)(context.config, codebase);
146
- const sourceDirName = config.source;
146
+ const cfg = (0, projectConfig_1.configForCodebase)(context.config, codebase);
147
+ const localCfg = (0, projectConfig_1.requireLocal)(cfg, "Remote sources are not supported.");
148
+ const sourceDirName = localCfg.source;
147
149
  const sourceDir = options.config.path(sourceDirName);
148
150
  const source = {};
149
151
  if (backend.someEndpoint(wantBackend, () => true)) {
150
152
  (0, utils_1.logLabeledBullet)("functions", `preparing ${clc.bold(sourceDirName)} directory for uploading...`);
151
153
  }
152
154
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
153
- const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, config);
155
+ const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, localCfg);
154
156
  source.functionsSourceV2 = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.pathToSource;
155
157
  source.functionsSourceV2Hash = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.hash;
156
158
  }
157
159
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
158
- const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, config, runtimeConfig);
160
+ const packagedSource = await (0, prepareFunctionsUpload_1.prepareFunctionsUpload)(sourceDir, localCfg, runtimeConfig);
159
161
  source.functionsSourceV1 = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.pathToSource;
160
162
  source.functionsSourceV1Hash = packagedSource === null || packagedSource === void 0 ? void 0 : packagedSource.hash;
161
163
  }
@@ -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,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DEFAULT_CONFIG = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.DESC_VERBOSITY = exports.FLAG_VERBOSITY = exports.FLAG_VERBOSITY_NAME = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
3
+ exports.JAVA_DEPRECATION_WARNING = exports.MIN_SUPPORTED_JAVA_MAJOR_VERSION = exports.checkJavaMajorVersion = exports.emulatorExec = exports.getListenOverview = exports.shutdownWhenKilled = exports.setExportOnExitOptions = exports.parseInspectionPort = exports.beforeEmulatorCommand = exports.errorMissingProject = exports.warnEmulatorNotSupported = exports.printNoticeIfEmulated = exports.DEFAULT_CONFIG = exports.DESC_TEST_PARAMS = exports.FLAG_TEST_PARAMS = exports.DESC_TEST_CONFIG = exports.FLAG_TEST_CONFIG = exports.DESC_UI = exports.FLAG_UI = exports.DESC_VERBOSITY = exports.FLAG_VERBOSITY = exports.FLAG_VERBOSITY_NAME = exports.EXPORT_ON_EXIT_CWD_DANGER = exports.EXPORT_ON_EXIT_USAGE_ERROR = exports.DESC_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT = exports.FLAG_EXPORT_ON_EXIT_NAME = exports.DESC_IMPORT = exports.FLAG_IMPORT = exports.DESC_INSPECT_FUNCTIONS = exports.FLAG_INSPECT_FUNCTIONS = exports.DESC_ONLY = exports.FLAG_ONLY = void 0;
4
4
  const clc = require("colorette");
5
5
  const childProcess = require("child_process");
6
6
  const controller = require("../emulator/controller");
@@ -86,6 +86,12 @@ async function warnEmulatorNotSupported(options, emulator) {
86
86
  }
87
87
  }
88
88
  exports.warnEmulatorNotSupported = warnEmulatorNotSupported;
89
+ async function errorMissingProject(options) {
90
+ if (!options.project) {
91
+ throw new error_1.FirebaseError("Project is not defined. Either use `--project` or use `firebase use` to set your active project.");
92
+ }
93
+ }
94
+ exports.errorMissingProject = errorMissingProject;
89
95
  async function beforeEmulatorCommand(options) {
90
96
  const optionsWithDefaultConfig = Object.assign(Object.assign({}, options), { config: exports.DEFAULT_CONFIG });
91
97
  const optionsWithConfig = options.config ? options : optionsWithDefaultConfig;
@@ -98,7 +98,7 @@ exports.filterEmulatorTargets = filterEmulatorTargets;
98
98
  function shouldStart(options, name) {
99
99
  var _a, _b;
100
100
  if (name === types_1.Emulators.HUB) {
101
- return !!options.project;
101
+ return true;
102
102
  }
103
103
  const targets = filterEmulatorTargets(options);
104
104
  const emulatorInTargets = targets.includes(name);
@@ -109,7 +109,7 @@ function shouldStart(options, name) {
109
109
  if (((_b = (_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a.ui) === null || _b === void 0 ? void 0 : _b.enabled) === false) {
110
110
  return false;
111
111
  }
112
- return (!!options.project && targets.some((target) => types_1.EMULATORS_SUPPORTED_BY_UI.includes(target)));
112
+ return targets.some((target) => types_1.EMULATORS_SUPPORTED_BY_UI.includes(target));
113
113
  }
114
114
  if (name === types_1.Emulators.FUNCTIONS && emulatorInTargets) {
115
115
  try {
@@ -188,7 +188,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
188
188
  }
189
189
  const hubLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.HUB);
190
190
  hubLogger.logLabeled("BULLET", "emulators", `Starting emulators: ${targets.join(", ")}`);
191
- const projectId = (0, projectUtils_1.getProjectId)(options) || "";
191
+ const projectId = (0, projectUtils_1.getProjectId)(options) || hub_1.EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
192
192
  const isDemoProject = constants_1.Constants.isDemoProject(projectId);
193
193
  if (isDemoProject) {
194
194
  hubLogger.logLabeled("BULLET", "emulators", `Detected demo project ID "${projectId}", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.`);
@@ -340,7 +340,8 @@ async function startAll(options, showUI = true, runningTestScript = false) {
340
340
  const functionsCfg = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
341
341
  utils.assertIsStringOrUndefined(options.extDevDir);
342
342
  for (const cfg of functionsCfg) {
343
- const functionsDir = path.join(projectDir, cfg.source);
343
+ const localCfg = (0, projectConfig_1.requireLocal)(cfg, "Remote sources are not supported in the Functions emulator.");
344
+ const functionsDir = path.join(projectDir, localCfg.source);
344
345
  const runtime = ((_g = options.extDevRuntime) !== null && _g !== void 0 ? _g : cfg.runtime);
345
346
  if (runtime && !(0, supported_1.isRuntime)(runtime)) {
346
347
  throw new error_1.FirebaseError(`Cannot load functions from ${functionsDir} because it has invalid runtime ${runtime}`);
@@ -348,14 +349,14 @@ async function startAll(options, showUI = true, runningTestScript = false) {
348
349
  const backend = {
349
350
  functionsDir,
350
351
  runtime,
351
- codebase: cfg.codebase,
352
- prefix: cfg.prefix,
352
+ codebase: localCfg.codebase,
353
+ prefix: localCfg.prefix,
353
354
  env: Object.assign({}, options.extDevEnv),
354
355
  secretEnv: [],
355
356
  predefinedTriggers: options.extDevTriggers,
356
- ignore: cfg.ignore,
357
+ ignore: localCfg.ignore,
357
358
  };
358
- proto.convertIfPresent(backend, cfg, "configDir", (cd) => path.join(projectDir, cd));
359
+ proto.convertIfPresent(backend, localCfg, "configDir", (cd) => path.join(projectDir, cd));
359
360
  emulatableBackends.push(backend);
360
361
  }
361
362
  }
@@ -372,7 +373,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
372
373
  }
373
374
  const functionsLogger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
374
375
  const functionsAddr = legacyGetFirstAddr(types_1.Emulators.FUNCTIONS);
375
- const projectId = (0, projectUtils_1.needProjectId)(options);
376
376
  const inspectFunctions = commandUtils.parseInspectionPort(options);
377
377
  if (inspectFunctions) {
378
378
  functionsLogger.logLabeled("WARN", "functions", `You are running the Functions emulator in debug mode. This means that functions will execute in sequence rather than in parallel.`);
@@ -461,13 +461,8 @@ async function startAll(options, showUI = true, runningTestScript = false) {
461
461
  firestoreLogger.logLabeled("WARN", "firestore", "The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration.");
462
462
  }
463
463
  if (singleProjectModeEnabled) {
464
- if (projectId) {
465
- args.single_project_mode = true;
466
- args.single_project_mode_error = false;
467
- }
468
- else {
469
- firestoreLogger.logLabeled("DEBUG", "firestore", "Could not enable single_project_mode: missing projectId.");
470
- }
464
+ args.single_project_mode = true;
465
+ args.single_project_mode_error = false;
471
466
  }
472
467
  const firestoreEmulator = new firestoreEmulator_1.FirestoreEmulator(args);
473
468
  await startEmulator(firestoreEmulator);
@@ -525,9 +520,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
525
520
  }
526
521
  }
527
522
  if (listenForEmulator.auth) {
528
- if (!projectId) {
529
- throw new error_1.FirebaseError(`Cannot start the ${constants_1.Constants.description(types_1.Emulators.AUTH)} without a project: run 'firebase init' or provide the --project flag`);
530
- }
531
523
  const authAddr = legacyGetFirstAddr(types_1.Emulators.AUTH);
532
524
  const authEmulator = new auth_2.AuthEmulator({
533
525
  host: authAddr.host,
@@ -546,9 +538,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
546
538
  }
547
539
  }
548
540
  if (listenForEmulator.pubsub) {
549
- if (!projectId) {
550
- throw new error_1.FirebaseError("Cannot start the Pub/Sub emulator without a project: run 'firebase init' or provide the --project flag");
551
- }
552
541
  const pubsubAddr = legacyGetFirstAddr(types_1.Emulators.PUBSUB);
553
542
  const pubsubEmulator = new pubsubEmulator_1.PubsubEmulator({
554
543
  host: pubsubAddr.host,
@@ -571,7 +560,6 @@ async function startAll(options, showUI = true, runningTestScript = false) {
571
560
  projectId,
572
561
  auto_download: true,
573
562
  configDir: config[0].source,
574
- rc: options.rc,
575
563
  config: options.config,
576
564
  autoconnectToPostgres: true,
577
565
  postgresListen: listenForEmulator["dataconnect.postgres"],
@@ -612,7 +600,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
612
600
  const storageEmulator = new storage_1.StorageEmulator({
613
601
  host: storageAddr.host,
614
602
  port: storageAddr.port,
615
- projectId: projectId,
603
+ projectId,
616
604
  rules: (0, config_1.getStorageRulesConfig)(projectId, options),
617
605
  });
618
606
  await startEmulator(storageEmulator);
@@ -671,13 +659,12 @@ async function startAll(options, showUI = true, runningTestScript = false) {
671
659
  await startEmulator(loggingEmulator);
672
660
  }
673
661
  if (showUI && !shouldStart(options, types_1.Emulators.UI)) {
674
- hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting, either because none of the running " +
675
- "emulators have a UI component or the Emulator UI cannot " +
676
- "determine the Project ID. Pass the --project flag to specify a project.");
662
+ hubLogger.logLabeled("WARN", "emulators", "The Emulator UI is not starting because none of the running " +
663
+ "emulators have a UI component.");
677
664
  }
678
665
  if (listenForEmulator.ui) {
679
666
  const ui = new ui_1.EmulatorUI({
680
- projectId: projectId,
667
+ projectId,
681
668
  listen: listenForEmulator[types_1.Emulators.UI],
682
669
  });
683
670
  await startEmulator(ui);
@@ -726,9 +713,6 @@ function getListenConfig(options, emulator) {
726
713
  }
727
714
  async function exportEmulatorData(exportPath, options, initiatedBy) {
728
715
  const projectId = options.project;
729
- if (!projectId) {
730
- throw new error_1.FirebaseError("Could not determine project ID, make sure you're running in a Firebase project directory or add the --project flag.", { exit: 1 });
731
- }
732
716
  const hubClient = new hubClient_1.EmulatorHubClient(projectId);
733
717
  if (!hubClient.foundHub()) {
734
718
  throw new error_1.FirebaseError(`Did not find any running emulators for project ${clc.bold(projectId)}.`, { exit: 1 });
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.12.0",
58
- "expectedSize": 29447008,
59
- "expectedChecksum": "73a09e7f54b642c0232ce89e9a4dd14a",
60
- "expectedChecksumSHA256": "5464803fec776eac553e1431474f3d4fcc7cc2f4fe43a58e8c1ce9d7ede28acd",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.12.0",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0"
57
+ "version": "2.13.0",
58
+ "expectedSize": 29475680,
59
+ "expectedChecksum": "8bceed44e84d08f135ad10b8b62337e9",
60
+ "expectedChecksumSHA256": "92e0cda1a3fd690421cae4edd7d985e5c5de7069e731ad200ba6988c2321d76c",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.13.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0"
63
63
  },
64
64
  "win32": {
65
- "version": "2.12.0",
66
- "expectedSize": 29936128,
67
- "expectedChecksum": "b4077d6d298024c9c454b8eb9ad9bf69",
68
- "expectedChecksumSHA256": "37be096ab8d4d29fa902ca12df97327997b1dc06d4b5026e11aa11d65f183a71",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.12.0",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0.exe"
65
+ "version": "2.13.0",
66
+ "expectedSize": 29965312,
67
+ "expectedChecksum": "67794a3cb334805a06fffd872be7d67c",
68
+ "expectedChecksumSHA256": "2905c570d40e586dd1ca0aeee10785786dc0486500640064e4e1e43be3b97899",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.13.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.12.0",
74
- "expectedSize": 29372600,
75
- "expectedChecksum": "04fb5bc9ef194170a5ba0966247eaa79",
76
- "expectedChecksumSHA256": "42397dd1b23094944aaf20044bc1ec0a5b400c79b536d15803eb4fa6b2bba025",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.12.0",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.12.0"
73
+ "version": "2.13.0",
74
+ "expectedSize": 29401272,
75
+ "expectedChecksum": "d72e185be8b782b8d4bca86d6bc76d2d",
76
+ "expectedChecksumSHA256": "87cb9541f157c3e9986b6eba80d161e6ef1debd17075e4d95e78861d3cdd353f",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.13.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.13.0"
79
79
  }
80
80
  }
81
81
  }
@@ -26,8 +26,13 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
26
26
  }
27
27
  static getLocatorFilePath(projectId) {
28
28
  const dir = os.tmpdir();
29
+ if (!projectId) {
30
+ projectId = EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
31
+ }
29
32
  const filename = `hub-${projectId}.json`;
30
- return path.join(dir, filename);
33
+ const locatorPath = path.join(dir, filename);
34
+ logger_1.logger.debug(`Emulator locator file path: ${locatorPath}`);
35
+ return locatorPath;
31
36
  }
32
37
  constructor(args) {
33
38
  super({
@@ -177,6 +182,7 @@ class EmulatorHub extends ExpressBasedEmulator_1.ExpressBasedEmulator {
177
182
  }
178
183
  }
179
184
  exports.EmulatorHub = EmulatorHub;
185
+ EmulatorHub.MISSING_PROJECT_PLACEHOLDER = "demo-no-project";
180
186
  EmulatorHub.CLI_VERSION = pkg.version;
181
187
  EmulatorHub.PATH_EXPORT = "/_admin/export";
182
188
  EmulatorHub.PATH_DISABLE_FUNCTIONS = "/functions/disableBackgroundTriggers";
@@ -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) {
@@ -135,13 +135,14 @@ exports.copyDirectory = copyDirectory;
135
135
  async function getCodebaseRuntime(options) {
136
136
  const config = (0, projectConfig_1.normalizeAndValidate)(options.config.src.functions);
137
137
  const codebaseConfig = (0, projectConfig_1.configForCodebase)(config, options.codebase || projectConfig_1.DEFAULT_CODEBASE);
138
- const sourceDirName = codebaseConfig.source;
138
+ const localCfg = (0, projectConfig_1.requireLocal)(codebaseConfig);
139
+ const sourceDirName = localCfg.source;
139
140
  const sourceDir = options.config.path(sourceDirName);
140
141
  const delegateContext = {
141
142
  projectId: "",
142
143
  sourceDir,
143
144
  projectDir: options.config.projectDir,
144
- runtime: codebaseConfig.runtime,
145
+ runtime: localCfg.runtime,
145
146
  };
146
147
  let delegate;
147
148
  try {
@@ -544,6 +544,19 @@ class FirestoreApi {
544
544
  }
545
545
  return database;
546
546
  }
547
+ async bulkDeleteDocuments(project, databaseId, collectionIds) {
548
+ var _a;
549
+ const name = `/projects/${project}/databases/${databaseId}`;
550
+ const url = `${name}:bulkDeleteDocuments`;
551
+ const payload = {
552
+ name,
553
+ collectionIds,
554
+ };
555
+ const res = await this.apiClient.post(url, payload);
556
+ return {
557
+ name: (_a = res.body) === null || _a === void 0 ? void 0 : _a.name,
558
+ };
559
+ }
547
560
  async restoreDatabase(project, databaseId, backupName, encryptionConfig) {
548
561
  const url = `/projects/${project}/databases:restore`;
549
562
  const payload = {
@@ -559,5 +572,37 @@ class FirestoreApi {
559
572
  }
560
573
  return database;
561
574
  }
575
+ async listOperations(project, databaseId, limit) {
576
+ const url = `/projects/${project}/databases/${databaseId}/operations`;
577
+ const res = await this.apiClient.get(url, {
578
+ queryParams: {
579
+ pageSize: limit,
580
+ },
581
+ });
582
+ return res.body;
583
+ }
584
+ async describeOperation(project, databaseId, operationName) {
585
+ const url = `/projects/${project}/databases/${databaseId}/operations/${operationName}`;
586
+ const res = await this.apiClient.get(url);
587
+ return res.body;
588
+ }
589
+ async cancelOperation(project, databaseId, operationName) {
590
+ var _a, _b, _c, _d;
591
+ const url = `/projects/${project}/databases/${databaseId}/operations/${operationName}:cancel`;
592
+ try {
593
+ const res = await this.apiClient.post(url);
594
+ return { success: res.status === 200 };
595
+ }
596
+ catch (error) {
597
+ const reason = "Cannot cancel an operation that is completed.";
598
+ const details = ((_c = (_b = (_a = error.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.details) || [];
599
+ for (const detail of details) {
600
+ if ((_d = detail.detail) === null || _d === void 0 ? void 0 : _d.includes(reason)) {
601
+ throw new error_1.FirebaseError(reason);
602
+ }
603
+ }
604
+ throw error;
605
+ }
606
+ }
562
607
  }
563
608
  exports.FirestoreApi = FirestoreApi;
@@ -120,6 +120,29 @@ class PrettyPrint {
120
120
  table.push(["Name", clc.yellow(backup.name)], ["Database", clc.yellow(backup.database)], ["Database UID", clc.yellow(backup.databaseUid)], ["State", clc.yellow(backup.state)], ["Snapshot Time", clc.yellow(backup.snapshotTime)], ["Expire Time", clc.yellow(backup.expireTime)], ["Stats", clc.yellow(backup.stats)]);
121
121
  logger_1.logger.info(table.toString());
122
122
  }
123
+ prettyPrintOperation(operation) {
124
+ const table = new Table({
125
+ head: ["Operation", ""],
126
+ });
127
+ table.push(["Name", clc.yellow(operation.name)], ["Done?", clc.yellow(operation.done ? "YES" : "NO")], ["Metadata", clc.yellow(JSON.stringify(operation.metadata, undefined, 2))]);
128
+ if (operation.response) {
129
+ table.push(["Response", clc.yellow(JSON.stringify(operation.response, undefined, 2))]);
130
+ }
131
+ logger_1.logger.info(table.toString());
132
+ }
133
+ prettyPrintOperations(operations) {
134
+ if (operations.length === 0) {
135
+ logger_1.logger.info("No operations found.");
136
+ return;
137
+ }
138
+ const table = new Table({
139
+ head: ["Operation Name", "Done"],
140
+ });
141
+ for (const op of operations) {
142
+ table.push([clc.yellow(op.name), op.done ? clc.green("YES") : clc.yellow("NO")]);
143
+ }
144
+ logger_1.logger.info(table.toString());
145
+ }
123
146
  prettyPrintLocations(locations) {
124
147
  if (locations.length === 0) {
125
148
  logger_1.logger.info("No Locations Available");