firebase-tools 14.19.1 → 14.21.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 (118) hide show
  1. package/lib/appUtils.js +6 -5
  2. package/lib/command.js +5 -8
  3. package/lib/commands/apps-init.js +7 -7
  4. package/lib/commands/dataconnect-execute.js +229 -0
  5. package/lib/commands/dataconnect-sdk-generate.js +66 -11
  6. package/lib/commands/firestore-backups-delete.js +1 -6
  7. package/lib/commands/firestore-backups-get.js +2 -8
  8. package/lib/commands/firestore-backups-list.js +4 -10
  9. package/lib/commands/firestore-backups-schedules-create.js +1 -6
  10. package/lib/commands/firestore-backups-schedules-delete.js +1 -6
  11. package/lib/commands/firestore-backups-schedules-list.js +1 -7
  12. package/lib/commands/firestore-backups-schedules-update.js +1 -6
  13. package/lib/commands/firestore-bulkdelete.js +7 -13
  14. package/lib/commands/firestore-databases-clone.js +99 -0
  15. package/lib/commands/firestore-databases-create.js +5 -10
  16. package/lib/commands/firestore-databases-delete.js +1 -6
  17. package/lib/commands/firestore-databases-get.js +1 -7
  18. package/lib/commands/firestore-databases-list.js +1 -7
  19. package/lib/commands/firestore-databases-restore.js +5 -10
  20. package/lib/commands/firestore-databases-update.js +1 -6
  21. package/lib/commands/firestore-locations.js +1 -7
  22. package/lib/commands/firestore-operations-cancel.js +3 -9
  23. package/lib/commands/firestore-operations-describe.js +2 -8
  24. package/lib/commands/firestore-operations-list.js +2 -8
  25. package/lib/commands/functions-secrets-set.js +19 -1
  26. package/lib/commands/index.js +2 -0
  27. package/lib/commands/init.js +12 -8
  28. package/lib/commands/internaltesting-functions-discover.js +1 -3
  29. package/lib/dataconnect/build.js +16 -2
  30. package/lib/dataconnect/load.js +21 -1
  31. package/lib/dataconnect/names.js +6 -1
  32. package/lib/dataconnect/provisionCloudSql.js +39 -11
  33. package/lib/dataconnect/schemaMigration.js +16 -3
  34. package/lib/dataconnect/types.js +1 -10
  35. package/lib/deploy/dataconnect/context.js +26 -0
  36. package/lib/deploy/dataconnect/deploy.js +13 -4
  37. package/lib/deploy/dataconnect/prepare.js +11 -8
  38. package/lib/deploy/dataconnect/release.js +10 -2
  39. package/lib/deploy/extensions/prepare.js +3 -1
  40. package/lib/deploy/functions/checkIam.js +1 -1
  41. package/lib/deploy/functions/functionsDeployHelper.js +8 -7
  42. package/lib/deploy/functions/params.js +17 -3
  43. package/lib/deploy/functions/prepare.js +9 -6
  44. package/lib/deploy/index.js +39 -20
  45. package/lib/detectProjectRoot.js +1 -1
  46. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  47. package/lib/emulator/hubExport.js +5 -0
  48. package/lib/experiments.js +0 -7
  49. package/lib/firestore/api.js +15 -0
  50. package/lib/firestore/util.js +22 -1
  51. package/lib/functions/projectConfig.js +5 -1
  52. package/lib/functions/secrets.js +14 -1
  53. package/lib/gcp/cloudsql/cloudsqladmin.js +4 -3
  54. package/lib/init/features/dataconnect/index.js +39 -22
  55. package/lib/init/features/dataconnect/sdk.js +83 -42
  56. package/lib/init/features/functions/index.js +1 -0
  57. package/lib/init/index.js +2 -2
  58. package/lib/management/apps.js +24 -24
  59. package/lib/mcp/index.js +46 -18
  60. package/lib/mcp/prompt.js +4 -1
  61. package/lib/mcp/prompts/core/consult.js +3 -4
  62. package/lib/mcp/prompts/core/deploy.js +1 -1
  63. package/lib/mcp/prompts/core/init.js +4 -5
  64. package/lib/mcp/prompts/crashlytics/connect.js +1 -1
  65. package/lib/mcp/prompts/dataconnect/schema.js +1 -1
  66. package/lib/mcp/prompts/index.js +20 -10
  67. package/lib/mcp/resources/index.js +0 -4
  68. package/lib/mcp/tool.js +17 -2
  69. package/lib/mcp/tools/apphosting/fetch_logs.js +1 -1
  70. package/lib/mcp/tools/apphosting/list_backends.js +1 -1
  71. package/lib/mcp/tools/auth/get_users.js +1 -1
  72. package/lib/mcp/tools/auth/set_sms_region_policy.js +1 -1
  73. package/lib/mcp/tools/auth/update_user.js +1 -1
  74. package/lib/mcp/tools/core/create_android_sha.js +1 -1
  75. package/lib/mcp/tools/core/create_app.js +1 -1
  76. package/lib/mcp/tools/core/create_project.js +1 -1
  77. package/lib/mcp/tools/core/get_environment.js +1 -1
  78. package/lib/mcp/tools/core/get_project.js +1 -1
  79. package/lib/mcp/tools/core/get_sdk_config.js +1 -1
  80. package/lib/mcp/tools/core/get_security_rules.js +1 -1
  81. package/lib/mcp/tools/core/init.js +3 -2
  82. package/lib/mcp/tools/core/list_apps.js +1 -1
  83. package/lib/mcp/tools/core/list_projects.js +1 -1
  84. package/lib/mcp/tools/core/login.js +1 -1
  85. package/lib/mcp/tools/core/logout.js +1 -1
  86. package/lib/mcp/tools/core/read_resources.js +1 -1
  87. package/lib/mcp/tools/core/update_environment.js +1 -1
  88. package/lib/mcp/tools/core/validate_security_rules.js +15 -1
  89. package/lib/mcp/tools/crashlytics/events.js +3 -3
  90. package/lib/mcp/tools/crashlytics/issues.js +3 -3
  91. package/lib/mcp/tools/crashlytics/notes.js +4 -4
  92. package/lib/mcp/tools/crashlytics/reports.js +6 -6
  93. package/lib/mcp/tools/dataconnect/compile.js +1 -1
  94. package/lib/mcp/tools/dataconnect/execute.js +1 -2
  95. package/lib/mcp/tools/dataconnect/generate_operation.js +1 -1
  96. package/lib/mcp/tools/dataconnect/generate_schema.js +1 -1
  97. package/lib/mcp/tools/dataconnect/list_services.js +1 -1
  98. package/lib/mcp/tools/firestore/delete_document.js +1 -1
  99. package/lib/mcp/tools/firestore/get_documents.js +1 -1
  100. package/lib/mcp/tools/firestore/list_collections.js +1 -1
  101. package/lib/mcp/tools/firestore/query_collection.js +1 -1
  102. package/lib/mcp/tools/functions/get_logs.js +1 -1
  103. package/lib/mcp/tools/index.js +14 -4
  104. package/lib/mcp/tools/messaging/send_message.js +1 -1
  105. package/lib/mcp/tools/realtime_database/get_data.js +1 -1
  106. package/lib/mcp/tools/realtime_database/set_data.js +1 -1
  107. package/lib/mcp/tools/remoteconfig/get_template.js +1 -1
  108. package/lib/mcp/tools/remoteconfig/update_template.js +1 -1
  109. package/lib/mcp/tools/storage/get_download_url.js +1 -1
  110. package/lib/mcp/util/availability.js +22 -0
  111. package/lib/mcp/util/crashlytics/availability.js +81 -0
  112. package/lib/mcp/util/dataconnect/converter.js +5 -4
  113. package/lib/mcp/util/dataconnect/emulator.js +0 -1
  114. package/lib/mcp/util.js +26 -6
  115. package/lib/responseToError.js +7 -6
  116. package/package.json +1 -1
  117. package/schema/firebase-config.json +3 -0
  118. package/lib/dataconnect/appFinder.js +0 -103
package/lib/appUtils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extractAppIdentifiersAndroid = exports.extractAppIdentifierIos = exports.extractAppIdentifiersFlutter = exports.detectApps = exports.getPlatformsFromFolder = exports.appDescription = exports.Framework = exports.Platform = void 0;
3
+ exports.detectFiles = exports.extractAppIdentifiersAndroid = exports.extractAppIdentifierIos = exports.extractAppIdentifiersFlutter = exports.detectApps = exports.getPlatformsFromFolder = exports.appDescription = exports.Framework = exports.Platform = void 0;
4
4
  const fs = require("fs-extra");
5
5
  const path = require("path");
6
6
  const glob_1 = require("glob");
@@ -13,8 +13,8 @@ var Platform;
13
13
  })(Platform = exports.Platform || (exports.Platform = {}));
14
14
  var Framework;
15
15
  (function (Framework) {
16
- Framework["REACT"] = "REACT";
17
- Framework["ANGULAR"] = "ANGULAR";
16
+ Framework["REACT"] = "react";
17
+ Framework["ANGULAR"] = "angular";
18
18
  })(Framework = exports.Framework || (exports.Framework = {}));
19
19
  function appDescription(a) {
20
20
  return `${a.directory} (${a.platform.toLowerCase()})`;
@@ -119,8 +119,8 @@ async function packageJsonToWebApp(dirPath, packageJsonFile) {
119
119
  }
120
120
  const WEB_FRAMEWORKS = Object.values(Framework);
121
121
  const WEB_FRAMEWORKS_SIGNALS = {
122
- REACT: ["react", "next"],
123
- ANGULAR: ["@angular/core"],
122
+ react: ["react", "next"],
123
+ angular: ["@angular/core"],
124
124
  };
125
125
  async function detectAppIdsForPlatform(dirPath, platform) {
126
126
  let appIdFiles;
@@ -228,3 +228,4 @@ async function detectFiles(dirPath, filePattern) {
228
228
  };
229
229
  return (0, glob_1.glob)(`**/${filePattern}`, options);
230
230
  }
231
+ exports.detectFiles = detectFiles;
package/lib/command.js CHANGED
@@ -106,9 +106,8 @@ class Command {
106
106
  const trackSuccess = (0, track_1.trackGA4)("command_execution", {
107
107
  command_name: this.name,
108
108
  result: "success",
109
- duration,
110
109
  interactive: (0, utils_1.getInheritedOption)(options, "nonInteractive") ? "false" : "true",
111
- });
110
+ }, duration);
112
111
  if (!isEmulator) {
113
112
  await (0, utils_1.withTimeout)(5000, trackSuccess);
114
113
  }
@@ -154,20 +153,18 @@ class Command {
154
153
  async prepare(options) {
155
154
  options = options || {};
156
155
  options.project = (0, utils_1.getInheritedOption)(options, "project");
157
- if (!process.stdin.isTTY || (0, utils_1.getInheritedOption)(options, "nonInteractive")) {
156
+ if (!process.stdin.isTTY ||
157
+ (0, utils_1.getInheritedOption)(options, "nonInteractive") ||
158
+ (0, utils_1.getInheritedOption)(options, "json")) {
158
159
  options.nonInteractive = true;
159
160
  }
160
161
  if ((0, utils_1.getInheritedOption)(options, "interactive")) {
161
- options.interactive = true;
162
162
  options.nonInteractive = false;
163
163
  }
164
164
  if ((0, utils_1.getInheritedOption)(options, "debug")) {
165
165
  options.debug = true;
166
166
  }
167
- if ((0, utils_1.getInheritedOption)(options, "json")) {
168
- options.nonInteractive = true;
169
- }
170
- else if (!options.isMCP) {
167
+ if (!(0, utils_1.getInheritedOption)(options, "json") && !options.isMCP) {
171
168
  (0, logger_1.useConsoleLoggers)();
172
169
  }
173
170
  if ((0, utils_1.getInheritedOption)(options, "config")) {
@@ -8,7 +8,7 @@ const apps_1 = require("../management/apps");
8
8
  const requireAuth_1 = require("../requireAuth");
9
9
  const logger_1 = require("../logger");
10
10
  const projectUtils_1 = require("../projectUtils");
11
- const types_1 = require("../dataconnect/types");
11
+ const appUtils_1 = require("../appUtils");
12
12
  const experiments_1 = require("../experiments");
13
13
  function logUse(platform, filePath) {
14
14
  switch (platform) {
@@ -42,12 +42,12 @@ for information about adding your config file to your project.`);
42
42
  }
43
43
  function toAppPlatform(str) {
44
44
  switch (str.toUpperCase()) {
45
- case types_1.Platform.ANDROID:
46
- return types_1.Platform.ANDROID;
47
- case types_1.Platform.IOS:
48
- return types_1.Platform.IOS;
49
- case types_1.Platform.WEB:
50
- return types_1.Platform.WEB;
45
+ case appUtils_1.Platform.ANDROID:
46
+ return appUtils_1.Platform.ANDROID;
47
+ case appUtils_1.Platform.IOS:
48
+ return appUtils_1.Platform.IOS;
49
+ case appUtils_1.Platform.WEB:
50
+ return appUtils_1.Platform.WEB;
51
51
  }
52
52
  throw new Error(`Platform ${str} is not compatible with apps:configure`);
53
53
  }
@@ -0,0 +1,229 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const clc = require("colorette");
5
+ const command_1 = require("../command");
6
+ const projectUtils_1 = require("../projectUtils");
7
+ const load_1 = require("../dataconnect/load");
8
+ const requireAuth_1 = require("../requireAuth");
9
+ const constants_1 = require("../emulator/constants");
10
+ const apiv2_1 = require("../apiv2");
11
+ const dataplaneClient_1 = require("../dataconnect/dataplaneClient");
12
+ const dataplaneClient_2 = require("../dataconnect/dataplaneClient");
13
+ const names_1 = require("../dataconnect/names");
14
+ const error_1 = require("../error");
15
+ const node_fs_1 = require("node:fs");
16
+ const types_1 = require("../dataconnect/types");
17
+ const hub_1 = require("../emulator/hub");
18
+ const promises_1 = require("node:fs/promises");
19
+ const node_os_1 = require("node:os");
20
+ const node_path_1 = require("node:path");
21
+ const consumers_1 = require("node:stream/consumers");
22
+ const logger_1 = require("../logger");
23
+ const responseToError_1 = require("../responseToError");
24
+ let stdinUsedFor = undefined;
25
+ exports.command = new command_1.Command("dataconnect:execute [file] [operationName]")
26
+ .description("execute a Data Connect query or mutation. If FIREBASE_DATACONNECT_EMULATOR_HOST is set (such as during 'firebase emulator:exec', executes against the emulator instead.")
27
+ .option("--service <serviceId>", "The service ID to execute against (optional if there's only one service)")
28
+ .option("--location <locationId>", "The location ID to execute against (optional if there's only one service). Ignored by the emulator.")
29
+ .option("--vars, --variables <vars>", "Supply variables to the operation execution, which must be a JSON object whose keys are variable names. If vars begin with the character @, the rest is interpreted as a file name to read from, or - to read from stdin.")
30
+ .option("--no-debug-details", "Disables debug information in the response. Executions returns helpful errors or GQL extensions by default, which may expose too much for unprivilleged user or programs. If that's the case, this flag turns those output off.")
31
+ .action(async (file = "", operationName, options) => {
32
+ const emulatorHost = process.env[constants_1.Constants.FIREBASE_DATACONNECT_EMULATOR_HOST];
33
+ let projectId;
34
+ if (emulatorHost) {
35
+ projectId = (0, projectUtils_1.getProjectId)(options) || hub_1.EmulatorHub.MISSING_PROJECT_PLACEHOLDER;
36
+ }
37
+ else {
38
+ projectId = (0, projectUtils_1.needProjectId)(options);
39
+ }
40
+ let serviceName = undefined;
41
+ const serviceId = options.service;
42
+ const locationId = options.location;
43
+ if (!file && !operationName) {
44
+ if (process.stdin.isTTY) {
45
+ throw new error_1.FirebaseError("At least one of the [file] [operationName] arguments is required.");
46
+ }
47
+ file = "-";
48
+ }
49
+ let query;
50
+ if (file === "-") {
51
+ stdinUsedFor = "operation source code";
52
+ if (process.stdin.isTTY) {
53
+ process.stderr.write(`${clc.cyan("Reading GraphQL operation from stdin. EOF (CTRL+D) to finish and execute.")}${node_os_1.EOL}`);
54
+ }
55
+ query = await (0, consumers_1.text)(process.stdin);
56
+ }
57
+ else {
58
+ const stat = (0, node_fs_1.statSync)(file, { throwIfNoEntry: false });
59
+ if (stat === null || stat === void 0 ? void 0 : stat.isFile()) {
60
+ const opDisplay = operationName ? clc.bold(operationName) : "operation";
61
+ process.stderr.write(`${clc.cyan(`Executing ${opDisplay} in ${clc.bold(file)}`)}${node_os_1.EOL}`);
62
+ query = await (0, promises_1.readFile)(file, "utf-8");
63
+ }
64
+ else if (stat === null || stat === void 0 ? void 0 : stat.isDirectory()) {
65
+ query = await readQueryFromDir(file);
66
+ }
67
+ else {
68
+ if (operationName === undefined && (0, names_1.isGraphqlName)(file)) {
69
+ operationName = file;
70
+ file = "";
71
+ }
72
+ if (file) {
73
+ throw new error_1.FirebaseError(`${file}: no such file or directory`);
74
+ }
75
+ file = await pickConnectorDir();
76
+ query = await readQueryFromDir(file);
77
+ }
78
+ }
79
+ let apiClient;
80
+ if (emulatorHost) {
81
+ const url = new URL("http://placeholder");
82
+ url.host = emulatorHost;
83
+ apiClient = new apiv2_1.Client({
84
+ urlPrefix: url.toString(),
85
+ apiVersion: dataplaneClient_1.DATACONNECT_API_VERSION,
86
+ });
87
+ }
88
+ else {
89
+ await (0, requireAuth_1.requireAuth)(options);
90
+ apiClient = (0, dataplaneClient_2.dataconnectDataplaneClient)();
91
+ }
92
+ if (!serviceName) {
93
+ if (serviceId && (locationId || emulatorHost)) {
94
+ serviceName = `projects/${projectId}/locations/${locationId || "unused"}/services/${serviceId}`;
95
+ }
96
+ else {
97
+ serviceName = (await getServiceInfo()).serviceName;
98
+ }
99
+ }
100
+ if (!options.variables && !process.stdin.isTTY && !stdinUsedFor) {
101
+ options.variables = "@-";
102
+ }
103
+ const unparsedVars = await literalOrFile(options.variables, "--variables");
104
+ const response = await (0, dataplaneClient_1.executeGraphQL)(apiClient, serviceName, {
105
+ query,
106
+ operationName,
107
+ variables: parseJsonObject(unparsedVars, "--variables"),
108
+ });
109
+ let err = (0, responseToError_1.responseToError)(response, response.body);
110
+ if ((0, types_1.isGraphQLResponseError)(response.body)) {
111
+ const { status, message } = response.body.error;
112
+ if (!err) {
113
+ err = new error_1.FirebaseError(message, {
114
+ context: {
115
+ body: response.body,
116
+ response: response,
117
+ },
118
+ status: response.status,
119
+ });
120
+ }
121
+ if (status === "INVALID_ARGUMENT" && message.includes("operationName is required")) {
122
+ throw new error_1.FirebaseError(err.message + `\nHint: Append <operationName> as an argument to disambiguate.`, Object.assign(Object.assign({}, err), { original: err }));
123
+ }
124
+ }
125
+ if (err) {
126
+ throw err;
127
+ }
128
+ if (!(0, types_1.isGraphQLResponse)(response.body)) {
129
+ throw new error_1.FirebaseError("Got invalid response body with neither .data or .errors", {
130
+ context: {
131
+ body: response.body,
132
+ response: response,
133
+ },
134
+ status: response.status,
135
+ });
136
+ }
137
+ logger_1.logger.info(JSON.stringify(response.body, null, 2));
138
+ if (!response.body.data) {
139
+ throw new error_1.FirebaseError("GraphQL request error(s). See response body (above) for details.", {
140
+ context: {
141
+ body: response.body,
142
+ response: response,
143
+ },
144
+ status: response.status,
145
+ });
146
+ }
147
+ if (response.body.errors && response.body.errors.length > 0) {
148
+ throw new error_1.FirebaseError("Execution completed with error(s). See response body (above) for details.", {
149
+ context: {
150
+ body: response.body,
151
+ response: response,
152
+ },
153
+ status: response.status,
154
+ });
155
+ }
156
+ return response.body;
157
+ async function readQueryFromDir(dir) {
158
+ const opDisplay = operationName ? clc.bold(operationName) : "operation";
159
+ process.stderr.write(`${clc.cyan(`Executing ${opDisplay} in ${clc.bold(dir)}`)}${node_os_1.EOL}`);
160
+ const files = await (0, load_1.readGQLFiles)(dir);
161
+ const query = (0, load_1.squashGraphQL)({ files });
162
+ if (!query) {
163
+ throw new error_1.FirebaseError(`${dir} contains no GQL files or only empty ones`);
164
+ }
165
+ return query;
166
+ }
167
+ async function getServiceInfo() {
168
+ return (0, load_1.pickService)(projectId, options.config, serviceId || undefined).catch((e) => {
169
+ if (!(e instanceof error_1.FirebaseError)) {
170
+ return Promise.reject(e);
171
+ }
172
+ if (!serviceId) {
173
+ e = new error_1.FirebaseError(e.message +
174
+ `\nHint: Try specifying the ${clc.yellow("--service <serviceId>")} option.`, Object.assign(Object.assign({}, e), { original: e }));
175
+ }
176
+ return Promise.reject(e);
177
+ });
178
+ }
179
+ async function pickConnectorDir() {
180
+ const serviceInfo = await getServiceInfo();
181
+ serviceName = serviceInfo.serviceName;
182
+ switch (serviceInfo.connectorInfo.length) {
183
+ case 1: {
184
+ const connector = serviceInfo.connectorInfo[0];
185
+ return (0, node_path_1.relative)(process.cwd(), connector.directory);
186
+ }
187
+ case 0:
188
+ throw new error_1.FirebaseError(`No connector found.\n` +
189
+ "Hint: To execute an operation in a GraphQL file, run:\n" +
190
+ ` firebase dataconnect:execute ${clc.yellow("./path/to/file.gql OPERATION_NAME")}`);
191
+ default: {
192
+ const example = (0, node_path_1.relative)(process.cwd(), serviceInfo.connectorInfo[0].directory);
193
+ throw new error_1.FirebaseError(`A file or directory must be explicitly specified when there are multiple connectors.\n` +
194
+ "Hint: To execute an operation within a connector, try e.g.:\n" +
195
+ ` firebase dataconnect:execute ${clc.yellow(`${example} OPERATION_NAME`)}`);
196
+ }
197
+ }
198
+ }
199
+ });
200
+ function parseJsonObject(json, subject) {
201
+ let obj;
202
+ try {
203
+ obj = JSON.parse(json || "{}");
204
+ }
205
+ catch (e) {
206
+ throw new error_1.FirebaseError(`expected ${subject} to be valid JSON string, got: ${json}`);
207
+ }
208
+ if (typeof obj !== "object" || obj == null)
209
+ throw new error_1.FirebaseError(`Provided ${subject} is not an object`);
210
+ return obj;
211
+ }
212
+ async function literalOrFile(arg, subject) {
213
+ let str = arg;
214
+ if (!str) {
215
+ return "";
216
+ }
217
+ if (str.startsWith("@")) {
218
+ if (str === "@-") {
219
+ if (stdinUsedFor) {
220
+ throw new error_1.FirebaseError(`standard input can only be used for one of ${stdinUsedFor} and ${subject}.`);
221
+ }
222
+ str = await (0, consumers_1.text)(process.stdin);
223
+ }
224
+ else {
225
+ str = await (0, promises_1.readFile)(str.substring(1), "utf-8");
226
+ }
227
+ }
228
+ return str;
229
+ }
@@ -6,28 +6,80 @@ const command_1 = require("../command");
6
6
  const dataconnectEmulator_1 = require("../emulator/dataconnectEmulator");
7
7
  const projectUtils_1 = require("../projectUtils");
8
8
  const load_1 = require("../dataconnect/load");
9
- const logger_1 = require("../logger");
10
9
  const auth_1 = require("../auth");
11
10
  const utils_1 = require("../utils");
11
+ const config_1 = require("../config");
12
+ const dataconnectInit = require("../init/features/dataconnect");
13
+ const dataconnectSdkInit = require("../init/features/dataconnect/sdk");
14
+ const error_1 = require("../error");
15
+ const init_1 = require("./init");
16
+ const hub_1 = require("../emulator/hub");
12
17
  exports.command = new command_1.Command("dataconnect:sdk:generate")
13
18
  .description("generate typed SDKs for your Data Connect connectors")
14
19
  .option("--watch", "watch for changes to your connector GQL files and regenerate your SDKs when updates occur")
15
20
  .action(async (options) => {
16
- const projectId = (0, projectUtils_1.needProjectId)(options);
17
- const serviceInfos = await (0, load_1.loadAll)(projectId, options.config);
18
- const serviceInfosWithSDKs = serviceInfos.filter((serviceInfo) => serviceInfo.connectorInfo.some((c) => {
21
+ const projectId = (0, projectUtils_1.getProjectId)(options);
22
+ let justRanInit = false;
23
+ let config = options.config;
24
+ if (!config || !config.has("dataconnect")) {
25
+ if (options.nonInteractive) {
26
+ throw new error_1.FirebaseError(`No dataconnect project directory found. Please run ${clc.bold("firebase init dataconnect")} to set it up first.`);
27
+ }
28
+ (0, utils_1.logWarning)("No dataconnect project directory found.");
29
+ (0, utils_1.logBullet)(`Running ${clc.bold("firebase init dataconnect")} to setup a dataconnect project directory.`);
30
+ if (!config) {
31
+ const cwd = options.cwd || process.cwd();
32
+ config = new config_1.Config({}, { projectDir: cwd, cwd: cwd });
33
+ }
34
+ const setup = {
35
+ config: config.src,
36
+ projectId: projectId,
37
+ rcfile: options.rc.data,
38
+ featureInfo: {
39
+ dataconnectSource: "gen_sdk_init",
40
+ },
41
+ instructions: [],
42
+ };
43
+ await dataconnectInit.askQuestions(setup);
44
+ await dataconnectInit.actuate(setup, config, options);
45
+ await (0, init_1.postInitSaves)(setup, config);
46
+ justRanInit = true;
47
+ options.config = config;
48
+ }
49
+ let serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
50
+ if (!serviceInfosWithSDKs.length) {
51
+ if (justRanInit || options.nonInteractive) {
52
+ throw new error_1.FirebaseError(`No generated SDKs are configured during init. Please run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
53
+ }
54
+ (0, utils_1.logWarning)("No generated SDKs have been configured.");
55
+ (0, utils_1.logBullet)(`Running ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
56
+ const setup = {
57
+ config: config.src,
58
+ projectId: projectId,
59
+ rcfile: options.rc.data,
60
+ featureInfo: {
61
+ dataconnectSource: "gen_sdk_init_sdk",
62
+ },
63
+ instructions: [],
64
+ };
65
+ await dataconnectSdkInit.askQuestions(setup);
66
+ await dataconnectSdkInit.actuate(setup, config);
67
+ justRanInit = true;
68
+ serviceInfosWithSDKs = await loadAllWithSDKs(projectId, config);
69
+ }
70
+ await generateSDKsInAll(options, serviceInfosWithSDKs, justRanInit);
71
+ });
72
+ async function loadAllWithSDKs(projectId, config) {
73
+ const serviceInfos = await (0, load_1.loadAll)(projectId || hub_1.EmulatorHub.MISSING_PROJECT_PLACEHOLDER, config);
74
+ return serviceInfos.filter((serviceInfo) => serviceInfo.connectorInfo.some((c) => {
19
75
  var _a, _b, _c, _d;
20
76
  return (((_a = c.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.javascriptSdk) ||
21
77
  ((_b = c.connectorYaml.generate) === null || _b === void 0 ? void 0 : _b.kotlinSdk) ||
22
78
  ((_c = c.connectorYaml.generate) === null || _c === void 0 ? void 0 : _c.swiftSdk) ||
23
79
  ((_d = c.connectorYaml.generate) === null || _d === void 0 ? void 0 : _d.dartSdk));
24
80
  }));
25
- if (!serviceInfosWithSDKs.length) {
26
- logger_1.logger.warn("No generated SDKs have been declared in connector.yaml files.");
27
- logger_1.logger.warn(`Run ${clc.bold("firebase init dataconnect:sdk")} to configure a generated SDK.`);
28
- logger_1.logger.warn(`See https://firebase.google.com/docs/data-connect/web-sdk for more details of how to configure generated SDKs.`);
29
- return;
30
- }
81
+ }
82
+ async function generateSDKsInAll(options, serviceInfosWithSDKs, justRanInit) {
31
83
  async function generateSDK(serviceInfo) {
32
84
  return dataconnectEmulator_1.DataConnectEmulator.generate({
33
85
  configDir: serviceInfo.sourceDirectory,
@@ -39,10 +91,13 @@ exports.command = new command_1.Command("dataconnect:sdk:generate")
39
91
  await Promise.race(serviceInfosWithSDKs.map(generateSDK));
40
92
  }
41
93
  else {
94
+ if (justRanInit) {
95
+ return;
96
+ }
42
97
  for (const s of serviceInfosWithSDKs) {
43
98
  await generateSDK(s);
44
99
  }
45
100
  const services = serviceInfosWithSDKs.map((s) => s.dataConnectYaml.serviceId).join(", ");
46
101
  (0, utils_1.logLabeledSuccess)("dataconnect", `Successfully Generated SDKs for services: ${clc.bold(services)}`);
47
102
  }
48
- });
103
+ }
@@ -33,11 +33,6 @@ exports.command = new command_1.Command("firestore:backups:delete <backup>")
33
33
  catch (err) {
34
34
  throw new error_1.FirebaseError(`Failed to delete the backup ${backupName}`, { original: err });
35
35
  }
36
- if (options.json) {
37
- logger_1.logger.info(JSON.stringify(backup, undefined, 2));
38
- }
39
- else {
40
- logger_1.logger.info(clc.bold(`Successfully deleted ${clc.yellow(backupName)}`));
41
- }
36
+ logger_1.logger.info(clc.bold(`Successfully deleted ${clc.yellow(backupName)}`));
42
37
  return backup;
43
38
  });
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
- const logger_1 = require("../logger");
6
5
  const requirePermissions_1 = require("../requirePermissions");
7
6
  const types_1 = require("../emulator/types");
8
7
  const commandUtils_1 = require("../emulator/commandUtils");
@@ -12,14 +11,9 @@ exports.command = new command_1.Command("firestore:backups:get <backup>")
12
11
  .description("get a Cloud Firestore database backup")
13
12
  .before(requirePermissions_1.requirePermissions, ["datastore.backups.get"])
14
13
  .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
15
- .action(async (backupName, options) => {
14
+ .action(async (backupName) => {
16
15
  const backup = await (0, firestore_1.getBackup)(backupName);
17
16
  const printer = new pretty_print_1.PrettyPrint();
18
- if (options.json) {
19
- logger_1.logger.info(JSON.stringify(backup, undefined, 2));
20
- }
21
- else {
22
- printer.prettyPrintBackup(backup);
23
- }
17
+ printer.prettyPrintBackup(backup);
24
18
  return backup;
25
19
  });
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
- const logger_1 = require("../logger");
6
5
  const requirePermissions_1 = require("../requirePermissions");
7
6
  const types_1 = require("../emulator/types");
8
7
  const commandUtils_1 = require("../emulator/commandUtils");
@@ -20,15 +19,10 @@ exports.command = new command_1.Command("firestore:backups:list")
20
19
  const location = (_a = options.location) !== null && _a !== void 0 ? _a : "-";
21
20
  const listBackupsResponse = await (0, firestore_1.listBackups)(options.project, location);
22
21
  const backups = listBackupsResponse.backups || [];
23
- if (options.json) {
24
- logger_1.logger.info(JSON.stringify(listBackupsResponse, undefined, 2));
25
- }
26
- else {
27
- printer.prettyPrintBackups(backups);
28
- if (listBackupsResponse.unreachable && listBackupsResponse.unreachable.length > 0) {
29
- (0, utils_1.logWarning)("We were not able to reach the following locations: " +
30
- listBackupsResponse.unreachable.join(", "));
31
- }
22
+ printer.prettyPrintBackups(backups);
23
+ if (listBackupsResponse.unreachable && listBackupsResponse.unreachable.length > 0) {
24
+ (0, utils_1.logWarning)("We were not able to reach the following locations: " +
25
+ listBackupsResponse.unreachable.join(", "));
32
26
  }
33
27
  return backups;
34
28
  });
@@ -54,11 +54,6 @@ exports.command = new command_1.Command("firestore:backups:schedules:create")
54
54
  };
55
55
  }
56
56
  const backupSchedule = await (0, firestore_1.createBackupSchedule)(options.project, databaseId, retention, dailyRecurrence, weeklyRecurrence);
57
- if (options.json) {
58
- logger_1.logger.info(JSON.stringify(backupSchedule, undefined, 2));
59
- }
60
- else {
61
- logger_1.logger.info(clc.bold(`Successfully created ${printer.prettyBackupScheduleString(backupSchedule)}`));
62
- }
57
+ logger_1.logger.info(clc.bold(`Successfully created ${printer.prettyBackupScheduleString(backupSchedule)}`));
63
58
  return backupSchedule;
64
59
  });
@@ -35,11 +35,6 @@ exports.command = new command_1.Command("firestore:backups:schedules:delete <bac
35
35
  original: err,
36
36
  });
37
37
  }
38
- if (options.json) {
39
- logger_1.logger.info(JSON.stringify(backupSchedule, undefined, 2));
40
- }
41
- else {
42
- logger_1.logger.info(clc.bold(`Successfully deleted ${clc.yellow(backupScheduleName)}`));
43
- }
38
+ logger_1.logger.info(clc.bold(`Successfully deleted ${clc.yellow(backupScheduleName)}`));
44
39
  return backupSchedule;
45
40
  });
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
- const logger_1 = require("../logger");
6
5
  const requirePermissions_1 = require("../requirePermissions");
7
6
  const types_1 = require("../emulator/types");
8
7
  const commandUtils_1 = require("../emulator/commandUtils");
@@ -18,11 +17,6 @@ exports.command = new command_1.Command("firestore:backups:schedules:list")
18
17
  const printer = new pretty_print_1.PrettyPrint();
19
18
  const databaseId = (_a = options.database) !== null && _a !== void 0 ? _a : "(default)";
20
19
  const backupSchedules = await (0, firestore_1.listBackupSchedules)(options.project, databaseId);
21
- if (options.json) {
22
- logger_1.logger.info(JSON.stringify(backupSchedules, undefined, 2));
23
- }
24
- else {
25
- printer.prettyPrintBackupSchedules(backupSchedules, databaseId);
26
- }
20
+ printer.prettyPrintBackupSchedules(backupSchedules, databaseId);
27
21
  return backupSchedules;
28
22
  });
@@ -24,11 +24,6 @@ exports.command = new command_1.Command("firestore:backups:schedules:update <bac
24
24
  }
25
25
  const retention = (0, backupUtils_1.calculateRetention)(options.retention);
26
26
  const backupSchedule = await (0, firestore_1.updateBackupSchedule)(backupScheduleName, retention);
27
- if (options.json) {
28
- logger_1.logger.info(JSON.stringify(backupSchedule, undefined, 2));
29
- }
30
- else {
31
- logger_1.logger.info(clc.bold(`Successfully updated ${printer.prettyBackupScheduleString(backupSchedule)}`));
32
- }
27
+ logger_1.logger.info(clc.bold(`Successfully updated ${printer.prettyBackupScheduleString(backupSchedule)}`));
33
28
  return backupSchedule;
34
29
  });
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
5
  const fsi = require("../firestore/api");
6
- const logger_1 = require("../logger");
7
6
  const requirePermissions_1 = require("../requirePermissions");
8
7
  const types_1 = require("../emulator/types");
9
8
  const commandUtils_1 = require("../emulator/commandUtils");
@@ -54,20 +53,15 @@ exports.command = new command_1.Command("firestore:bulkdelete")
54
53
  return utils.reject("Command aborted.", { exit: 1 });
55
54
  }
56
55
  const op = await api.bulkDeleteDocuments(options.project, databaseId, collectionIds);
57
- if (options.json) {
58
- logger_1.logger.info(JSON.stringify(op, undefined, 2));
56
+ if (op.name) {
57
+ (0, utils_1.logSuccess)(`Successfully started bulk delete operation.`);
58
+ (0, utils_1.logBullet)(`Operation name: ` + clc.cyan(op.name));
59
+ (0, utils_1.logBullet)("You can monitor the operation's progress using the " +
60
+ clc.cyan(`gcloud firestore operations describe`) +
61
+ ` command.`);
59
62
  }
60
63
  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
- }
64
+ (0, utils_1.logLabeledError)(`Bulk Delete:`, `Failed to start a bulk delete operation.`);
71
65
  }
72
66
  return op;
73
67
  });