firebase-tools 14.4.0 → 14.5.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 (62) hide show
  1. package/lib/apphosting/backend.js +2 -0
  2. package/lib/bin/mcp.js +1 -0
  3. package/lib/commands/init.js +0 -3
  4. package/lib/config.js +42 -24
  5. package/lib/dataconnect/cloudAICompanionClient.js +7 -2
  6. package/lib/dataconnect/cloudAICompanionTypes.js +2 -0
  7. package/lib/dataconnect/fileUtils.js +11 -4
  8. package/lib/dataconnect/schemaMigration.js +6 -7
  9. package/lib/deploy/apphosting/deploy.js +3 -0
  10. package/lib/deploy/apphosting/prepare.js +34 -28
  11. package/lib/deploy/apphosting/release.js +3 -0
  12. package/lib/deploy/firestore/deploy.js +47 -4
  13. package/lib/emulator/apphosting/serve.js +1 -1
  14. package/lib/emulator/downloadableEmulatorInfo.json +39 -18
  15. package/lib/emulator/downloadableEmulators.js +15 -59
  16. package/lib/extensions/manifest.js +0 -3
  17. package/lib/frameworks/angular/index.js +1 -1
  18. package/lib/frameworks/angular/utils.js +17 -6
  19. package/lib/gcp/apphosting.js +13 -1
  20. package/lib/gcp/run.js +19 -1
  21. package/lib/init/features/apphosting.js +3 -2
  22. package/lib/init/features/database.js +11 -19
  23. package/lib/init/features/dataconnect/index.js +27 -26
  24. package/lib/init/features/dataconnect/sdk.js +19 -6
  25. package/lib/init/features/emulators.js +4 -3
  26. package/lib/init/features/firestore/index.js +44 -34
  27. package/lib/init/features/firestore/indexes.js +12 -13
  28. package/lib/init/features/firestore/rules.js +8 -15
  29. package/lib/init/features/genkit/index.js +16 -9
  30. package/lib/init/features/hosting/index.js +9 -8
  31. package/lib/init/features/index.js +3 -2
  32. package/lib/init/features/storage.js +31 -8
  33. package/lib/init/index.js +5 -1
  34. package/lib/mcp/index.js +10 -6
  35. package/lib/mcp/tool.js +2 -1
  36. package/lib/mcp/tools/apphosting/fetch_logs.js +69 -0
  37. package/lib/mcp/tools/apphosting/index.js +6 -0
  38. package/lib/mcp/tools/apphosting/list_backends.js +51 -0
  39. package/lib/mcp/tools/core/index.js +2 -0
  40. package/lib/mcp/tools/core/init.js +26 -2
  41. package/lib/mcp/tools/core/list_apps.js +10 -5
  42. package/lib/mcp/tools/core/list_projects.js +45 -0
  43. package/lib/mcp/tools/dataconnect/generate_operation.js +1 -1
  44. package/lib/mcp/tools/firestore/query_collection.js +13 -7
  45. package/lib/mcp/tools/index.js +2 -0
  46. package/lib/mcp/tools/storage/get_download_url.js +1 -1
  47. package/lib/mcp/types.js +1 -0
  48. package/lib/mcp/util.js +137 -1
  49. package/lib/mcp/util.test.js +468 -0
  50. package/lib/prompt.js +1 -1
  51. package/lib/track.js +1 -1
  52. package/package.json +1 -1
  53. package/schema/connector-yaml.json +12 -0
  54. package/schema/extension-yaml.json +17 -4
  55. package/schema/firebase-config.json +3 -0
  56. package/standalone/package.json +1 -1
  57. package/templates/dataconnect-prompts/operation-generation-cursor-windsurf-rule.txt +273 -0
  58. package/templates/dataconnect-prompts/schema-generation-cursor-windsurf-rule.txt +653 -0
  59. package/templates/genkit/firebase.1.0.0.template +5 -0
  60. package/templates/init/firestore/firestore.rules +2 -0
  61. package/templates/init/functions/typescript/package.lint.json +1 -1
  62. package/templates/init/functions/typescript/package.nolint.json +1 -1
@@ -108,10 +108,12 @@ async function doSetup(projectId, webAppName, serviceAccount) {
108
108
  exports.doSetup = doSetup;
109
109
  async function doSetupSourceDeploy(projectId, backendId) {
110
110
  const location = await promptLocation(projectId, "Select a primary region to host your backend:\n");
111
+ const webAppSpinner = ora("Creating a new web app...\n").start();
111
112
  const webApp = await app_1.webApps.getOrCreateWebApp(projectId, null, backendId);
112
113
  if (!webApp) {
113
114
  (0, utils_1.logWarning)(`Firebase web app not set`);
114
115
  }
116
+ webAppSpinner.stop();
115
117
  const createBackendSpinner = ora("Creating your new backend...").start();
116
118
  const backend = await createBackend(projectId, location, backendId, null, undefined, webApp === null || webApp === void 0 ? void 0 : webApp.id);
117
119
  createBackendSpinner.succeed(`Successfully created backend!\n\t${backend.name}\n`);
package/lib/bin/mcp.js CHANGED
@@ -33,6 +33,7 @@ async function mcp() {
33
33
  console.log((0, index_js_1.markdownDocsOfTools)());
34
34
  return;
35
35
  }
36
+ process.env.IS_FIREBASE_MCP = "true";
36
37
  (0, logger_1.useFileLogger)();
37
38
  const activeFeatures = (values.only || "")
38
39
  .split(",")
@@ -186,12 +186,9 @@ async function initAction(feature, options) {
186
186
  }
187
187
  await (0, init_1.init)(setup, config, options);
188
188
  logger_1.logger.info();
189
- utils.logBullet("Writing configuration info to " + clc.bold("firebase.json") + "...");
190
189
  config.writeProjectFile("firebase.json", setup.config);
191
- utils.logBullet("Writing project information to " + clc.bold(".firebaserc") + "...");
192
190
  config.writeProjectFile(".firebaserc", setup.rcfile);
193
191
  if (!fsutils.fileExistsSync(config.path(".gitignore"))) {
194
- utils.logBullet("Writing gitignore file to " + clc.bold(".gitignore") + "...");
195
192
  config.writeProjectFile(".gitignore", GITIGNORE_TEMPLATE);
196
193
  }
197
194
  logger_1.logger.info();
package/lib/config.js CHANGED
@@ -156,11 +156,20 @@ class Config {
156
156
  }
157
157
  }
158
158
  writeProjectFile(p, content) {
159
- if (typeof content !== "string") {
160
- content = JSON.stringify(content, null, 2) + "\n";
159
+ const path = this.path(p);
160
+ fs.ensureFileSync(path);
161
+ fs.writeFileSync(path, stringifyContent(content), "utf8");
162
+ switch (p) {
163
+ case "firebase.json":
164
+ utils.logSuccess("Wrote configuration info to " + clc.bold("firebase.json"));
165
+ break;
166
+ case ".firebaserc":
167
+ utils.logSuccess("Wrote project information to " + clc.bold(".firebaserc"));
168
+ break;
169
+ default:
170
+ utils.logSuccess("Wrote " + clc.bold(p));
171
+ break;
161
172
  }
162
- fs.ensureFileSync(this.path(p));
163
- fs.writeFileSync(this.path(p), content, "utf8");
164
173
  }
165
174
  projectFileExists(p) {
166
175
  return fs.existsSync(this.path(p));
@@ -168,32 +177,35 @@ class Config {
168
177
  deleteProjectFile(p) {
169
178
  fs.removeSync(this.path(p));
170
179
  }
171
- async askWriteProjectFile(path, content, force, confirmByDefault) {
180
+ async confirmWriteProjectFile(path, content, confirmByDefault) {
172
181
  const writeTo = this.path(path);
173
- let next = true;
174
- if (typeof content !== "string") {
175
- content = JSON.stringify(content, null, 2) + "\n";
176
- }
177
- let existingContent;
178
- if (fsutils.fileExistsSync(writeTo)) {
179
- existingContent = fsutils.readFile(writeTo);
180
- }
181
- if (existingContent && existingContent !== content && !force) {
182
- next = await (0, prompt_1.confirm)({
183
- message: "File " + clc.underline(path) + " already exists. Overwrite?",
184
- default: !!confirmByDefault,
185
- });
182
+ if (!fsutils.fileExistsSync(writeTo)) {
183
+ return true;
186
184
  }
187
- if (existingContent === content) {
185
+ const existingContent = fsutils.readFile(writeTo);
186
+ const newContent = stringifyContent(content);
187
+ if (existingContent === newContent) {
188
188
  utils.logBullet(clc.bold(path) + " is unchanged");
189
+ return false;
189
190
  }
190
- else if (next) {
191
- this.writeProjectFile(path, content);
192
- utils.logSuccess("Wrote " + clc.bold(path));
193
- }
194
- else {
191
+ const shouldWrite = await (0, prompt_1.confirm)({
192
+ message: "File " + clc.underline(path) + " already exists. Overwrite?",
193
+ default: !!confirmByDefault,
194
+ });
195
+ if (!shouldWrite) {
195
196
  utils.logBullet("Skipping write of " + clc.bold(path));
197
+ return false;
196
198
  }
199
+ return true;
200
+ }
201
+ async askWriteProjectFile(path, content, force, confirmByDefault) {
202
+ if (!force) {
203
+ const shouldWrite = await this.confirmWriteProjectFile(path, content, confirmByDefault);
204
+ if (!shouldWrite) {
205
+ return;
206
+ }
207
+ }
208
+ this.writeProjectFile(path, content);
197
209
  }
198
210
  static load(options, allowMissing) {
199
211
  const pd = (0, detectProjectRoot_1.detectProjectRoot)(options);
@@ -241,3 +253,9 @@ Config.MATERIALIZE_TARGETS = [
241
253
  "dataconnect",
242
254
  "apphosting",
243
255
  ];
256
+ function stringifyContent(content) {
257
+ if (typeof content === "string") {
258
+ return content;
259
+ }
260
+ return JSON.stringify(content, null, 2) + "\n";
261
+ }
@@ -21,8 +21,13 @@ async function callCloudAICompanion(client, vscodeRequest, type) {
21
21
  const request = buildRequest(vscodeRequest, type);
22
22
  const { projectId } = getServiceParts(vscodeRequest.servicePath);
23
23
  const instance = toChatResourceName(projectId);
24
- const res = await client.post(`${instance}:completeTask`, request);
25
- return res;
24
+ try {
25
+ const res = await client.post(`${instance}:completeTask`, request);
26
+ return res.body;
27
+ }
28
+ catch (error) {
29
+ return { output: { messages: [] }, error: error };
30
+ }
26
31
  }
27
32
  exports.callCloudAICompanion = callCloudAICompanion;
28
33
  function buildRequest({ servicePath, naturalLanguageQuery, chatHistory }, type) {
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getFrameworksFromPackageJson = exports.frameworksMap = exports.SUPPORTED_FRAMEWORKS = exports.resolvePackageJson = exports.getPlatformFromFolder = exports.pickService = exports.readGQLFiles = exports.readConnectorYaml = exports.readDataConnectYaml = exports.readFirebaseJson = void 0;
4
4
  const fs = require("fs-extra");
5
5
  const path = require("path");
6
+ const clc = require("colorette");
6
7
  const error_1 = require("../error");
7
8
  const types_1 = require("./types");
8
9
  const utils_1 = require("../utils");
@@ -77,10 +78,16 @@ async function pickService(projectId, config, serviceId) {
77
78
  const serviceCfgs = readFirebaseJson(config);
78
79
  let serviceInfo;
79
80
  if (serviceCfgs.length === 0) {
80
- throw new error_1.FirebaseError("No Data Connect services found in firebase.json.");
81
+ throw new error_1.FirebaseError("No Data Connect services found in firebase.json." +
82
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add a Data Connect service.`);
81
83
  }
82
84
  else if (serviceCfgs.length === 1) {
83
85
  serviceInfo = await (0, load_1.load)(projectId, config, serviceCfgs[0].source);
86
+ if (serviceId && serviceId !== serviceInfo.dataConnectYaml.serviceId) {
87
+ throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${serviceInfo.dataConnectYaml.serviceId}.` +
88
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
89
+ }
90
+ return serviceInfo;
84
91
  }
85
92
  else {
86
93
  if (!serviceId) {
@@ -89,11 +96,11 @@ async function pickService(projectId, config, serviceId) {
89
96
  const infos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(projectId, config, c.source)));
90
97
  const maybe = infos.find((i) => i.dataConnectYaml.serviceId === serviceId);
91
98
  if (!maybe) {
92
- throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json.`);
99
+ throw new error_1.FirebaseError(`No service named ${serviceId} declared in firebase.json. Found ${infos.map((i) => i.dataConnectYaml.serviceId).join(", ")}.` +
100
+ `\nYou can run ${clc.bold("firebase init dataconnect")} to add this Data Connect service.`);
93
101
  }
94
- serviceInfo = maybe;
102
+ return maybe;
95
103
  }
96
- return serviceInfo;
97
104
  }
98
105
  exports.pickService = pickService;
99
106
  const WEB_INDICATORS = ["package.json", "package-lock.json", "node_modules"];
@@ -29,6 +29,7 @@ async function setupSchemaIfNecessary(instanceId, databaseId, options) {
29
29
  return schemaInfo.setupStatus;
30
30
  }
31
31
  async function diffSchema(options, schema, schemaValidation) {
32
+ (0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
32
33
  const { serviceName, instanceName, databaseId, instanceId } = getIdentifiers(schema);
33
34
  await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, false);
34
35
  let diffs = [];
@@ -36,15 +37,12 @@ async function diffSchema(options, schema, schemaValidation) {
36
37
  let validationMode = schemaValidation !== null && schemaValidation !== void 0 ? schemaValidation : "COMPATIBLE";
37
38
  setSchemaValidationMode(schema, validationMode);
38
39
  try {
39
- if (!schemaValidation) {
40
- (0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
41
- }
42
40
  await (0, client_1.upsertSchema)(schema, true);
43
41
  if (validationMode === "STRICT") {
44
- (0, utils_1.logLabeledSuccess)("dataconnect", `Database schema is up to date.`);
42
+ (0, utils_1.logLabeledSuccess)("dataconnect", `database schema of ${instanceId}:${databaseId} is up to date.`);
45
43
  }
46
44
  else {
47
- (0, utils_1.logLabeledSuccess)("dataconnect", `Database schema is compatible.`);
45
+ (0, utils_1.logLabeledSuccess)("dataconnect", `database schema of ${instanceId}:${databaseId} is compatible.`);
48
46
  }
49
47
  }
50
48
  catch (err) {
@@ -97,6 +95,7 @@ async function diffSchema(options, schema, schemaValidation) {
97
95
  }
98
96
  exports.diffSchema = diffSchema;
99
97
  async function migrateSchema(args) {
98
+ (0, utils_1.logLabeledBullet)("dataconnect", `generating required schema changes...`);
100
99
  const { options, schema, validateOnly, schemaValidation } = args;
101
100
  const { serviceName, instanceId, instanceName, databaseId } = getIdentifiers(schema);
102
101
  await ensureServiceIsConnectedToCloudSql(serviceName, instanceName, databaseId, true);
@@ -107,7 +106,7 @@ async function migrateSchema(args) {
107
106
  setSchemaValidationMode(schema, validationMode);
108
107
  try {
109
108
  await (0, client_1.upsertSchema)(schema, validateOnly);
110
- logger_1.logger.debug(`Database schema was up to date for ${instanceId}:${databaseId}`);
109
+ (0, utils_1.logLabeledBullet)("dataconnect", `database schema of ${instanceId}:${databaseId} is up to date.`);
111
110
  }
112
111
  catch (err) {
113
112
  if ((err === null || err === void 0 ? void 0 : err.status) !== 400) {
@@ -355,7 +354,7 @@ async function ensureServiceIsConnectedToCloudSql(serviceName, instanceId, datab
355
354
  (0, utils_1.logLabeledWarning)("dataconnect", `Not yet linked to the Cloud SQL instance.`);
356
355
  return;
357
356
  }
358
- (0, utils_1.logLabeledBullet)("dataconnect", `Linking the Cloud SQL instance...`);
357
+ (0, utils_1.logLabeledBullet)("dataconnect", `linking the Cloud SQL instance...`);
359
358
  currentSchema = {
360
359
  name: `${serviceName}/schemas/${types_1.SCHEMA_ID}`,
361
360
  source: {
@@ -9,6 +9,9 @@ const projectUtils_1 = require("../../projectUtils");
9
9
  const utils_1 = require("../../utils");
10
10
  const util_1 = require("./util");
11
11
  async function default_1(context, options) {
12
+ if (context.backendConfigs.size === 0) {
13
+ return;
14
+ }
12
15
  const projectId = (0, projectUtils_1.needProjectId)(options);
13
16
  options.projectNumber = await (0, getProjectNumber_1.getProjectNumber)(options);
14
17
  if (!context.backendConfigs) {
@@ -30,6 +30,7 @@ async function default_1(context, options) {
30
30
  const foundBackends = [];
31
31
  const notFoundBackends = [];
32
32
  const ambiguousBackends = [];
33
+ const skippedBackends = [];
33
34
  for (const cfg of configs) {
34
35
  const filteredBackends = backends.filter((backend) => (0, apphosting_1.parseBackendName)(backend.name).id === cfg.backendId);
35
36
  if (filteredBackends.length === 0) {
@@ -54,6 +55,7 @@ async function default_1(context, options) {
54
55
  for (const cfg of foundBackends) {
55
56
  const filteredBackends = backends.filter((backend) => (0, apphosting_1.parseBackendName)(backend.name).id === cfg.backendId);
56
57
  if (cfg.alwaysDeployFromSource === false) {
58
+ skippedBackends.push(cfg);
57
59
  continue;
58
60
  }
59
61
  const backend = filteredBackends[0];
@@ -69,9 +71,9 @@ async function default_1(context, options) {
69
71
  cfg.alwaysDeployFromSource = confirmDeploy;
70
72
  const configPath = path.join(options.projectRoot || "", "firebase.json");
71
73
  options.config.writeProjectFile(configPath, options.config.src);
72
- (0, utils_1.logLabeledBullet)("apphosting", `Your deployment preferences have been saved to firebase.json. On future invocations of "firebase deploy", your local source will be deployed to ${cfg.backendId}. You can edit this setting in your firebase.json at any time.`);
74
+ (0, utils_1.logLabeledBullet)("apphosting", `On future invocations of "firebase deploy", your local source will ${!confirmDeploy ? "not " : ""}be deployed to ${cfg.backendId}. You can edit this setting in your firebase.json at any time.`);
73
75
  if (!confirmDeploy) {
74
- (0, utils_1.logLabeledWarning)("apphosting", `Skipping deployment of backend ${cfg.backendId}`);
76
+ skippedBackends.push(cfg);
75
77
  continue;
76
78
  }
77
79
  }
@@ -79,33 +81,37 @@ async function default_1(context, options) {
79
81
  context.backendConfigs.set(cfg.backendId, cfg);
80
82
  context.backendLocations.set(cfg.backendId, location);
81
83
  }
82
- if (notFoundBackends.length === 0) {
83
- return;
84
- }
85
- if (options.force) {
86
- (0, utils_1.logLabeledWarning)("apphosting", `Skipping deployments of backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}; ` +
87
- "the backend(s) do not exist yet and we cannot create them for you because you must choose primary regions for each one. " +
88
- "Please run 'firebase deploy' without the --force flag, or 'firebase apphosting:backends:create' to create the backend, " +
89
- "then retry deployment.");
90
- return;
91
- }
92
- const confirmCreate = await (0, prompt_1.confirm)({
93
- default: true,
94
- message: `Did not find backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}. Do you want to create them (you'll have the option to select which to create in the next step)?`,
95
- });
96
- if (!confirmCreate) {
97
- return;
84
+ if (notFoundBackends.length > 0) {
85
+ if (options.force) {
86
+ (0, utils_1.logLabeledWarning)("apphosting", `Skipping deployments of backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}; ` +
87
+ "the backend(s) do not exist yet and we cannot create them for you because you must choose primary regions for each one. " +
88
+ "Please run 'firebase deploy' without the --force flag, or 'firebase apphosting:backends:create' to create the backend, " +
89
+ "then retry deployment.");
90
+ return;
91
+ }
92
+ const confirmCreate = await (0, prompt_1.confirm)({
93
+ default: true,
94
+ message: `Did not find backend(s) ${notFoundBackends.map((cfg) => cfg.backendId).join(", ")}. Do you want to create them (you'll have the option to select which to create in the next step)?`,
95
+ });
96
+ if (confirmCreate) {
97
+ const selected = await (0, prompt_1.checkbox)({
98
+ message: "Which backends do you want to create and deploy to?",
99
+ choices: notFoundBackends.map((cfg) => cfg.backendId),
100
+ });
101
+ const selectedBackends = selected.map((id) => notFoundBackends.find((backend) => backend.backendId === id));
102
+ for (const cfg of selectedBackends) {
103
+ (0, utils_1.logLabeledBullet)("apphosting", `Creating a new backend ${cfg.backendId}...`);
104
+ const { location } = await (0, backend_1.doSetupSourceDeploy)(projectId, cfg.backendId);
105
+ context.backendConfigs.set(cfg.backendId, cfg);
106
+ context.backendLocations.set(cfg.backendId, location);
107
+ }
108
+ }
109
+ else {
110
+ skippedBackends.push(...notFoundBackends);
111
+ }
98
112
  }
99
- const selected = await (0, prompt_1.checkbox)({
100
- message: "Which backends do you want to create and deploy to?",
101
- choices: notFoundBackends.map((cfg) => cfg.backendId),
102
- });
103
- const selectedBackends = selected.map((id) => notFoundBackends.find((backend) => backend.backendId === id));
104
- for (const cfg of selectedBackends) {
105
- (0, utils_1.logLabeledBullet)("apphosting", `Creating a new backend ${cfg.backendId}...`);
106
- const { location } = await (0, backend_1.doSetupSourceDeploy)(projectId, cfg.backendId);
107
- context.backendConfigs.set(cfg.backendId, cfg);
108
- context.backendLocations.set(cfg.backendId, location);
113
+ if (skippedBackends.length > 0) {
114
+ (0, utils_1.logLabeledWarning)("apphosting", `Skipping deployment of backend(s) ${skippedBackends.map((cfg) => cfg.backendId).join(", ")}.`);
109
115
  }
110
116
  return;
111
117
  }
@@ -7,6 +7,9 @@ const rollout_1 = require("../../apphosting/rollout");
7
7
  const projectUtils_1 = require("../../projectUtils");
8
8
  const utils_1 = require("../../utils");
9
9
  async function default_1(context, options) {
10
+ if (context.backendConfigs.size === 0) {
11
+ return;
12
+ }
10
13
  const projectId = (0, projectUtils_1.needProjectId)(options);
11
14
  const rollouts = [];
12
15
  const backendIds = [];
@@ -2,9 +2,42 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const clc = require("colorette");
4
4
  const api_1 = require("../../firestore/api");
5
+ const types = require("../../firestore/api-types");
5
6
  const logger_1 = require("../../logger");
6
7
  const utils = require("../../utils");
7
8
  const rulesDeploy_1 = require("../../rulesDeploy");
9
+ const utils_1 = require("../../utils");
10
+ const error_1 = require("../../error");
11
+ async function createDatabase(context, options) {
12
+ let firestoreCfg = options.config.data.firestore;
13
+ if (Array.isArray(firestoreCfg)) {
14
+ firestoreCfg = firestoreCfg[0];
15
+ }
16
+ if (!options.projectId) {
17
+ throw new error_1.FirebaseError("Project ID is required to create a Firestore database.");
18
+ }
19
+ if (!firestoreCfg || !firestoreCfg.database) {
20
+ throw new error_1.FirebaseError("Firestore database configuration is missing in firebase.json.");
21
+ }
22
+ const api = new api_1.FirestoreApi();
23
+ try {
24
+ await api.getDatabase(options.projectId, firestoreCfg.database);
25
+ }
26
+ catch (e) {
27
+ if (e.status === 404) {
28
+ utils.logLabeledBullet("firetore", `Creating the new Firestore database ${firestoreCfg.database}...`);
29
+ const createDatabaseReq = {
30
+ project: options.projectId,
31
+ databaseId: firestoreCfg.database,
32
+ locationId: firestoreCfg.location || "nam5",
33
+ type: types.DatabaseType.FIRESTORE_NATIVE,
34
+ deleteProtectionState: types.DatabaseDeleteProtectionState.DISABLED,
35
+ pointInTimeRecoveryEnablement: types.PointInTimeRecoveryEnablement.DISABLED,
36
+ };
37
+ await api.createDatabase(createDatabaseReq);
38
+ }
39
+ }
40
+ }
8
41
  async function deployRules(context) {
9
42
  var _a;
10
43
  const rulesDeploy = (_a = context === null || context === void 0 ? void 0 : context.firestore) === null || _a === void 0 ? void 0 : _a.rulesDeploy;
@@ -33,12 +66,22 @@ async function deployIndexes(context, options) {
33
66
  return;
34
67
  }
35
68
  const fieldOverrides = indexesRawSpec.fieldOverrides || [];
36
- await firestoreIndexes.deploy(options, indexes, fieldOverrides, databaseId).then(() => {
37
- utils.logSuccess(`${clc.bold(clc.green("firestore:"))} deployed indexes in ${clc.bold(indexesFileName)} successfully for ${databaseId} database`);
38
- });
69
+ try {
70
+ await firestoreIndexes.deploy(options, indexes, fieldOverrides, databaseId);
71
+ }
72
+ catch (err) {
73
+ if (err.status !== 404) {
74
+ throw err;
75
+ }
76
+ await (0, utils_1.sleep)(1000);
77
+ await firestoreIndexes.deploy(options, indexes, fieldOverrides, databaseId);
78
+ }
79
+ utils.logSuccess(`${clc.bold(clc.green("firestore:"))} deployed indexes in ${clc.bold(indexesFileName)} successfully for ${databaseId} database`);
39
80
  }));
40
81
  }
41
82
  async function default_1(context, options) {
42
- await Promise.all([deployRules(context), deployIndexes(context, options)]);
83
+ await createDatabase(context, options);
84
+ await deployRules(context);
85
+ await deployIndexes(context, options);
43
86
  }
44
87
  exports.default = default_1;
@@ -135,7 +135,7 @@ async function tripFirebasePostinstall(rootDirectory, env) {
135
135
  return;
136
136
  }
137
137
  const npmLsResults = JSON.parse(npmLs.stdout.toString().trim());
138
- const dependenciesToSearch = Object.values(npmLsResults.dependencies);
138
+ const dependenciesToSearch = Object.values(npmLsResults.dependencies || {});
139
139
  const firebaseUtilPaths = [];
140
140
  for (const dependency of dependenciesToSearch) {
141
141
  if (dependency.name === "@firebase/util" &&
@@ -3,58 +3,79 @@
3
3
  "version": "4.11.2",
4
4
  "expectedSize": 34495935,
5
5
  "expectedChecksum": "2fd771101c0e1f7898c04c9204f2ce63",
6
- "expectedChecksumSHA256": "b70d99344caf17c98b6f910fa8f6edf32a7c016cb1035e8915f70d38901eb97f"
6
+ "expectedChecksumSHA256": "b70d99344caf17c98b6f910fa8f6edf32a7c016cb1035e8915f70d38901eb97f",
7
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v4.11.2.jar",
8
+ "downloadPathRelativeToCacheDir": "firebase-database-emulator-v4.11.2.jar"
7
9
  },
8
10
  "firestore": {
9
11
  "version": "1.19.8",
10
12
  "expectedSize": 63634791,
11
13
  "expectedChecksum": "9b43a6daa590678de9b7df6d68260395",
12
- "expectedChecksumSHA256": "9d43599ed6151199e8d604dc87fac51218e49e5f3a48519b1ae560bbe5e3382d"
14
+ "expectedChecksumSHA256": "9d43599ed6151199e8d604dc87fac51218e49e5f3a48519b1ae560bbe5e3382d",
15
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.19.8.jar",
16
+ "downloadPathRelativeToCacheDir": "cloud-firestore-emulator-v1.19.8.jar"
13
17
  },
14
18
  "storage": {
15
19
  "version": "1.1.3",
16
20
  "expectedSize": 52892936,
17
21
  "expectedChecksum": "2ca11ec1193003bea89f806cc085fa25",
18
- "expectedChecksumSHA256": "0cd52db6f6271d62078f805220706377c849220b73bd68aa27078d977df9c900"
22
+ "expectedChecksumSHA256": "0cd52db6f6271d62078f805220706377c849220b73bd68aa27078d977df9c900",
23
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v1.1.3.jar",
24
+ "downloadPathRelativeToCacheDir": "cloud-storage-rules-runtime-v1.1.3.jar"
19
25
  },
20
26
  "ui": {
21
27
  "snapshot": {
22
28
  "version": "SNAPSHOT",
23
29
  "expectedSize": -1,
24
30
  "expectedChecksum": "",
25
- "expectedChecksumSHA256": ""
31
+ "expectedChecksumSHA256": "",
32
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-vSNAPSHOT.zip",
33
+ "downloadPathRelativeToCacheDir": "ui-vSNAPSHOT.zip",
34
+ "binaryPathRelativeToCacheDir": "ui-vSNAPSHOT/server/server.mjs"
26
35
  },
27
36
  "main": {
28
37
  "version": "1.15.0",
29
38
  "expectedSize": 3538469,
30
39
  "expectedChecksum": "2f13d5aea9524564c8b7adaa9cfa2128",
31
- "expectedChecksumSHA256": "97d8c4c574e3f20c4d690a2ce8373eef76ab024da73279a062dba8517f88cf9a"
40
+ "expectedChecksumSHA256": "97d8c4c574e3f20c4d690a2ce8373eef76ab024da73279a062dba8517f88cf9a",
41
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v1.15.0.zip",
42
+ "downloadPathRelativeToCacheDir": "ui-v1.15.0.zip",
43
+ "binaryPathRelativeToCacheDir": "ui-v1.15.0/server/server.mjs"
32
44
  }
33
45
  },
34
46
  "pubsub": {
35
47
  "version": "0.8.14",
36
48
  "expectedSize": 66786933,
37
49
  "expectedChecksum": "a9025b3e53fdeafd2969ccb3ba1e1d38",
38
- "expectedChecksumSHA256": "4fbeefd8ecb32b10e5ab522e5d8748e0c2b13891c471c0c327c04632dcc75a8d"
50
+ "expectedChecksumSHA256": "4fbeefd8ecb32b10e5ab522e5d8748e0c2b13891c471c0c327c04632dcc75a8d",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.14.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.14.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.14/pubsub-emulator/bin/cloud-pubsub-emulator"
39
54
  },
40
55
  "dataconnect": {
41
56
  "darwin": {
42
- "version": "2.6.1",
43
- "expectedSize": 27415296,
44
- "expectedChecksum": "5814d22b06b1321409f40693dfe47288",
45
- "expectedChecksumSHA256": "8df1cfac6ef747909b3c18b57ba52b70a11d53b9d0a22b197011d6550ceca133"
57
+ "version": "2.6.2",
58
+ "expectedSize": 27501312,
59
+ "expectedChecksum": "f4adceec52a29bbc8eabf39dc07460a8",
60
+ "expectedChecksumSHA256": "038b1a763d2afd487423b37841323554e13bb6973c69a35f993c315819506ff8",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.6.2",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2"
46
63
  },
47
64
  "win32": {
48
- "version": "2.6.1",
49
- "expectedSize": 27875328,
50
- "expectedChecksum": "21381052c2bf767ee84fb85975cfd4d5",
51
- "expectedChecksumSHA256": "6bebaa9575b460c4fa170b191d94722923a3c869bc1eca24c7e444428e6b7a70"
65
+ "version": "2.6.2",
66
+ "expectedSize": 27961856,
67
+ "expectedChecksum": "36c75d09d9def62891be1ba84424cc5d",
68
+ "expectedChecksumSHA256": "eb33efdf0374bfe0772f3adff7b2ab7eb3353f5e03e2a781623d10f61a92444e",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.6.2",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2.exe"
52
71
  },
53
72
  "linux": {
54
- "version": "2.6.1",
55
- "expectedSize": 27332760,
56
- "expectedChecksum": "0480a92f5af8e8441e680dc4e7995263",
57
- "expectedChecksumSHA256": "4f60baf4649c670227314302056f50b1c40ccc8d673b98d16935977508342a67"
73
+ "version": "2.6.2",
74
+ "expectedSize": 27414680,
75
+ "expectedChecksum": "deb1bad4fbccf4e3bbc437f8e2b34536",
76
+ "expectedChecksumSHA256": "549633c0e3ab26621da197b202e5712163c4be2d1f5d1e6c81bb33f20ccd1d7a",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.6.2",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.6.2"
58
79
  }
59
80
  }
60
81
  }
@@ -30,83 +30,39 @@ const dataconnectDetails = process.platform === "darwin"
30
30
  : EMULATOR_UPDATE_DETAILS.dataconnect.linux;
31
31
  exports.DownloadDetails = {
32
32
  database: {
33
- downloadPath: path.join(CACHE_DIR, `firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`),
33
+ downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.database.downloadPathRelativeToCacheDir),
34
34
  version: EMULATOR_UPDATE_DETAILS.database.version,
35
- opts: {
36
- cacheDir: CACHE_DIR,
37
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/firebase-database-emulator-v${EMULATOR_UPDATE_DETAILS.database.version}.jar`,
38
- expectedSize: EMULATOR_UPDATE_DETAILS.database.expectedSize,
39
- expectedChecksum: EMULATOR_UPDATE_DETAILS.database.expectedChecksum,
40
- namePrefix: "firebase-database-emulator",
41
- },
35
+ opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.database), { cacheDir: CACHE_DIR, namePrefix: "firebase-database-emulator" }),
42
36
  },
43
37
  firestore: {
44
- downloadPath: path.join(CACHE_DIR, `cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`),
38
+ downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.firestore.downloadPathRelativeToCacheDir),
45
39
  version: EMULATOR_UPDATE_DETAILS.firestore.version,
46
- opts: {
47
- cacheDir: CACHE_DIR,
48
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v${EMULATOR_UPDATE_DETAILS.firestore.version}.jar`,
49
- expectedSize: EMULATOR_UPDATE_DETAILS.firestore.expectedSize,
50
- expectedChecksum: EMULATOR_UPDATE_DETAILS.firestore.expectedChecksum,
51
- namePrefix: "cloud-firestore-emulator",
52
- },
40
+ opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.firestore), { cacheDir: CACHE_DIR, namePrefix: "cloud-firestore-emulator" }),
53
41
  },
54
42
  storage: {
55
- downloadPath: path.join(CACHE_DIR, `cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`),
43
+ downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.storage.downloadPathRelativeToCacheDir),
56
44
  version: EMULATOR_UPDATE_DETAILS.storage.version,
57
- opts: {
58
- cacheDir: CACHE_DIR,
59
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-storage-rules-runtime-v${EMULATOR_UPDATE_DETAILS.storage.version}.jar`,
60
- expectedSize: EMULATOR_UPDATE_DETAILS.storage.expectedSize,
61
- expectedChecksum: EMULATOR_UPDATE_DETAILS.storage.expectedChecksum,
62
- namePrefix: "cloud-storage-rules-emulator",
63
- },
45
+ opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.storage), { cacheDir: CACHE_DIR, namePrefix: "cloud-storage-rules-emulator" }),
64
46
  },
65
47
  ui: {
66
48
  version: emulatorUiDetails.version,
67
- downloadPath: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}.zip`),
49
+ downloadPath: path.join(CACHE_DIR, emulatorUiDetails.downloadPathRelativeToCacheDir),
68
50
  unzipDir: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}`),
69
- binaryPath: path.join(CACHE_DIR, `ui-v${emulatorUiDetails.version}`, "server", "server.mjs"),
70
- opts: {
71
- cacheDir: CACHE_DIR,
72
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/ui-v${emulatorUiDetails.version}.zip`,
73
- expectedSize: emulatorUiDetails.expectedSize,
74
- expectedChecksum: emulatorUiDetails.expectedChecksum,
75
- skipCache: experiments.isEnabled("emulatoruisnapshot"),
76
- skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"),
77
- namePrefix: "ui",
78
- },
51
+ binaryPath: path.join(CACHE_DIR, emulatorUiDetails.binaryPathRelativeToCacheDir),
52
+ opts: Object.assign(Object.assign({}, emulatorUiDetails), { cacheDir: CACHE_DIR, skipCache: experiments.isEnabled("emulatoruisnapshot"), skipChecksumAndSize: experiments.isEnabled("emulatoruisnapshot"), namePrefix: "ui" }),
79
53
  },
80
54
  pubsub: {
81
- downloadPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`),
55
+ downloadPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.pubsub.downloadPathRelativeToCacheDir),
82
56
  version: EMULATOR_UPDATE_DETAILS.pubsub.version,
83
57
  unzipDir: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`),
84
- binaryPath: path.join(CACHE_DIR, `pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}`, `pubsub-emulator/bin/cloud-pubsub-emulator${process.platform === "win32" ? ".bat" : ""}`),
85
- opts: {
86
- cacheDir: CACHE_DIR,
87
- remoteUrl: `https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-${EMULATOR_UPDATE_DETAILS.pubsub.version}.zip`,
88
- expectedSize: EMULATOR_UPDATE_DETAILS.pubsub.expectedSize,
89
- expectedChecksum: EMULATOR_UPDATE_DETAILS.pubsub.expectedChecksum,
90
- namePrefix: "pubsub-emulator",
91
- },
58
+ binaryPath: path.join(CACHE_DIR, EMULATOR_UPDATE_DETAILS.pubsub.binaryPathRelativeToCacheDir),
59
+ opts: Object.assign(Object.assign({}, EMULATOR_UPDATE_DETAILS.pubsub), { cacheDir: CACHE_DIR, namePrefix: "pubsub-emulator" }),
92
60
  },
93
61
  dataconnect: {
94
- downloadPath: path.join(CACHE_DIR, `dataconnect-emulator-${dataconnectDetails.version}${process.platform === "win32" ? ".exe" : ""}`),
62
+ downloadPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
95
63
  version: dataconnectDetails.version,
96
- binaryPath: path.join(CACHE_DIR, `dataconnect-emulator-${dataconnectDetails.version}${process.platform === "win32" ? ".exe" : ""}`),
97
- opts: {
98
- cacheDir: CACHE_DIR,
99
- remoteUrl: process.platform === "darwin"
100
- ? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v${dataconnectDetails.version}`
101
- : process.platform === "win32"
102
- ? `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v${dataconnectDetails.version}`
103
- : `https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v${dataconnectDetails.version}`,
104
- expectedSize: dataconnectDetails.expectedSize,
105
- expectedChecksum: dataconnectDetails.expectedChecksum,
106
- skipChecksumAndSize: false,
107
- namePrefix: "dataconnect-emulator",
108
- auth: false,
109
- },
64
+ binaryPath: path.join(CACHE_DIR, dataconnectDetails.downloadPathRelativeToCacheDir),
65
+ opts: Object.assign(Object.assign({}, dataconnectDetails), { cacheDir: CACHE_DIR, skipChecksumAndSize: false, namePrefix: "dataconnect-emulator", auth: false }),
110
66
  },
111
67
  };
112
68
  const EmulatorDetails = {