firebase-tools 14.21.0 → 14.23.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 (40) hide show
  1. package/README.md +13 -5
  2. package/lib/apiv2.js +12 -2
  3. package/lib/appdistribution/client.js +62 -16
  4. package/lib/appdistribution/types.js +1 -12
  5. package/lib/appdistribution/yaml_helper.js +69 -0
  6. package/lib/commands/appdistribution-groups-list.js +3 -5
  7. package/lib/commands/appdistribution-testcases-export.js +32 -0
  8. package/lib/commands/appdistribution-testcases-import.js +34 -0
  9. package/lib/commands/appdistribution-testers-list.js +3 -5
  10. package/lib/commands/deploy.js +6 -4
  11. package/lib/commands/hosting-sites-create.js +4 -3
  12. package/lib/commands/index.js +8 -5
  13. package/lib/commands/init.js +5 -7
  14. package/lib/deploy/functions/params.js +7 -11
  15. package/lib/emulator/auth/operations.js +7 -1
  16. package/lib/emulator/downloadableEmulatorInfo.json +25 -25
  17. package/lib/emulator/functionsEmulatorRuntime.js +8 -0
  18. package/lib/emulator/taskQueue.js +5 -0
  19. package/lib/experiments.js +0 -6
  20. package/lib/firestore/api.js +22 -4
  21. package/lib/frameworks/angular/index.js +1 -1
  22. package/lib/frameworks/flutter/index.js +1 -1
  23. package/lib/frameworks/next/index.js +1 -1
  24. package/lib/frameworks/nuxt/index.js +1 -1
  25. package/lib/frameworks/vite/index.js +5 -2
  26. package/lib/hosting/interactive.js +14 -19
  27. package/lib/init/features/hosting/github.js +6 -6
  28. package/lib/init/features/hosting/index.js +89 -86
  29. package/lib/init/features/index.js +3 -2
  30. package/lib/init/index.js +5 -1
  31. package/lib/management/provisioning/errorHandler.js +54 -0
  32. package/lib/management/provisioning/provision.js +2 -5
  33. package/lib/mcp/resources/guides/init_backend.js +3 -26
  34. package/lib/mcp/resources/guides/init_hosting.js +15 -10
  35. package/lib/mcp/tools/core/init.js +27 -0
  36. package/lib/mcp/tools/functions/index.js +2 -1
  37. package/lib/mcp/tools/functions/list_functions.js +48 -0
  38. package/lib/utils.js +4 -1
  39. package/package.json +2 -2
  40. package/schema/firebase-config.json +0 -1
@@ -44,38 +44,38 @@
44
44
  }
45
45
  },
46
46
  "pubsub": {
47
- "version": "0.8.14",
48
- "expectedSize": 66786933,
49
- "expectedChecksum": "a9025b3e53fdeafd2969ccb3ba1e1d38",
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"
47
+ "version": "0.8.17",
48
+ "expectedSize": 65162324,
49
+ "expectedChecksum": "a88ec6424e49af459b5c8a3657d69c06",
50
+ "expectedChecksumSHA256": "72a49d14ee6cd4c1a0fee4a46f520fe4d7465396a155c61abc0103905047edd3",
51
+ "remoteUrl": "https://storage.googleapis.com/firebase-preview-drop/emulator/pubsub-emulator-0.8.17.zip",
52
+ "downloadPathRelativeToCacheDir": "pubsub-emulator-0.8.17.zip",
53
+ "binaryPathRelativeToCacheDir": "pubsub-emulator-0.8.17/pubsub-emulator/bin/cloud-pubsub-emulator"
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.15.1",
58
- "expectedSize": 29946720,
59
- "expectedChecksum": "cc8d5dd053cc71adad0f640ac2627018",
60
- "expectedChecksumSHA256": "a3749396507678bc546987ef047a9d0c25145064503a9adc777229f1bc9b8c6f",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.15.1",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.1"
57
+ "version": "2.16.0",
58
+ "expectedSize": 29963104,
59
+ "expectedChecksum": "ee4d81abfba3576c7c6c8082313acfd0",
60
+ "expectedChecksumSHA256": "8aed1dc6cc8c35ab23fdf10e519e9cf3d93c8a0ccb0ff7d5af3ddc41a4847e3e",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-amd64-v2.16.0",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.16.0"
63
63
  },
64
64
  "win32": {
65
- "version": "2.15.1",
66
- "expectedSize": 30440960,
67
- "expectedChecksum": "57390c392101a13952ff2aea74b62787",
68
- "expectedChecksumSHA256": "905a9ab2200189dc31f23b5454d21803b0c672442f0ee4bb7db8286d159a86e0",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.15.1",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.1.exe"
65
+ "version": "2.16.0",
66
+ "expectedSize": 30456320,
67
+ "expectedChecksum": "546019a713be28620a3b43d49fc9d4b0",
68
+ "expectedChecksumSHA256": "ec1de720e173f0a613fbbb51767c0288121aa2f80e45e6bad0f3d2aff12689ec",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-amd64-v2.16.0",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.16.0.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.15.1",
74
- "expectedSize": 29868216,
75
- "expectedChecksum": "cc6ef676d8c5c3a3f74c0bf037083cf0",
76
- "expectedChecksumSHA256": "1dfe085c467637ebf6a71cc6c8e8bc11fac85202056b960633607627bd647a6d",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.15.1",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.15.1"
73
+ "version": "2.16.0",
74
+ "expectedSize": 29888696,
75
+ "expectedChecksum": "5ab8da956043a48b43199f07b94ac48c",
76
+ "expectedChecksumSHA256": "1a420f3d2672627eae2d9b0b6747b833517cfc08f7e80ae3a79389788691b67a",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-amd64-v2.16.0",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.16.0"
79
79
  }
80
80
  }
81
81
  }
@@ -240,6 +240,10 @@ function initializeNetworkFiltering() {
240
240
  }
241
241
  async function initializeFirebaseFunctionsStubs() {
242
242
  const firebaseFunctionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
243
+ if ((0, functionsEmulatorUtils_1.compareVersionStrings)(firebaseFunctionsResolution.version, "7.0.0") >= 0) {
244
+ logDebug("Detected firebase-functions v7+, skipping legacy stubs.");
245
+ return;
246
+ }
243
247
  const firebaseFunctionsRoot = (0, functionsEmulatorShared_1.findModuleRoot)("firebase-functions", firebaseFunctionsResolution.resolution);
244
248
  const httpsProviderResolution = path.join(firebaseFunctionsRoot, "lib/providers/https");
245
249
  const httpsProviderV1Resolution = path.join(firebaseFunctionsRoot, "lib/v1/providers/https");
@@ -451,6 +455,10 @@ function warnAboutStorageProd() {
451
455
  }
452
456
  async function initializeFunctionsConfigHelper() {
453
457
  const functionsResolution = await assertResolveDeveloperNodeModule("firebase-functions");
458
+ if ((0, functionsEmulatorUtils_1.compareVersionStrings)(functionsResolution.version, "7.0.0") >= 0) {
459
+ logDebug("Detected firebase-functions v7+, skipping config helper.");
460
+ return;
461
+ }
454
462
  const localFunctionsModule = require(functionsResolution.resolution);
455
463
  logDebug("Checked functions.config()", {
456
464
  config: localFunctionsModule.config(),
@@ -4,6 +4,7 @@ exports.TaskQueue = exports.TaskStatus = exports.Queue = void 0;
4
4
  const abort_controller_1 = require("abort-controller");
5
5
  const emulatorLogger_1 = require("./emulatorLogger");
6
6
  const types_1 = require("./types");
7
+ const error_1 = require("../error");
7
8
  const node_fetch_1 = require("node-fetch");
8
9
  class Node {
9
10
  constructor(data) {
@@ -198,6 +199,10 @@ class TaskQueue {
198
199
  }
199
200
  const controller = new abort_controller_1.default();
200
201
  const signal = controller.signal;
202
+ signal.reason = "";
203
+ signal.throwIfAborted = () => {
204
+ throw new error_1.FirebaseError("Aborted");
205
+ };
201
206
  const request = (0, node_fetch_1.default)(emulatedTask.task.httpRequest.url, {
202
207
  method: "POST",
203
208
  headers: headers,
@@ -118,12 +118,6 @@ exports.ALL_EXPERIMENTS = experiments({
118
118
  shortDescription: "Adds experimental App Testing feature",
119
119
  public: true,
120
120
  },
121
- ailogic: {
122
- shortDescription: "Enable Firebase AI Logic feature for existing apps",
123
- fullDescription: "Enables the AI Logic initialization feature that provisions AI Logic for existing Firebase apps.",
124
- public: true,
125
- default: false,
126
- },
127
121
  });
128
122
  function isValidExperiment(name) {
129
123
  return Object.keys(exports.ALL_EXPERIMENTS).includes(name);
@@ -22,16 +22,34 @@ class FirestoreApi {
22
22
  this.printer = new pretty_print_1.PrettyPrint();
23
23
  }
24
24
  static processIndex(index) {
25
- var _a, _b, _c;
26
- const fields = index.fields;
25
+ var _a;
26
+ let fields = index.fields;
27
+ const suffixOrder = FirestoreApi.lastIndexFieldOrder(fields);
28
+ const nameSuffix = { fieldPath: "__name__", order: suffixOrder };
27
29
  const lastField = (_a = index.fields) === null || _a === void 0 ? void 0 : _a[index.fields.length - 1];
30
+ if (lastField.vectorConfig) {
31
+ const vectorField = lastField;
32
+ fields = fields.slice(0, -1);
33
+ if (fields.length === 0 || (fields === null || fields === void 0 ? void 0 : fields[fields.length - 1].fieldPath) !== "__name__") {
34
+ fields.push(nameSuffix);
35
+ }
36
+ fields.push(vectorField);
37
+ return Object.assign(Object.assign({}, index), { fields });
38
+ }
28
39
  if ((lastField === null || lastField === void 0 ? void 0 : lastField.fieldPath) !== "__name__") {
29
- const defaultDirection = (_c = (_b = index.fields) === null || _b === void 0 ? void 0 : _b[index.fields.length - 1]) === null || _c === void 0 ? void 0 : _c.order;
30
- const nameSuffix = { fieldPath: "__name__", order: defaultDirection };
31
40
  fields.push(nameSuffix);
32
41
  }
33
42
  return Object.assign(Object.assign({}, index), { fields });
34
43
  }
44
+ static lastIndexFieldOrder(fields) {
45
+ let lastIndexFieldOrder = types.Order.ASCENDING;
46
+ for (const field of fields) {
47
+ if (field.order) {
48
+ lastIndexFieldOrder = field.order;
49
+ }
50
+ }
51
+ return lastIndexFieldOrder;
52
+ }
35
53
  async deploy(options, indexes, fieldOverrides, databaseId = "(default)") {
36
54
  var _a;
37
55
  const spec = this.upgradeOldSpec({
@@ -26,7 +26,7 @@ async function discover(dir) {
26
26
  }
27
27
  exports.discover = discover;
28
28
  function init(setup, config) {
29
- (0, child_process_1.execSync)(`npx --yes -p @angular/cli@"${exports.supportedRange}" ng new ${setup.projectId} --directory ${setup.hosting.source} --skip-git`, {
29
+ (0, child_process_1.execSync)(`npx --yes -p @angular/cli@"${exports.supportedRange}" ng new ${setup.projectId} --directory ${setup.featureInfo.hosting.source} --skip-git`, {
30
30
  stdio: "inherit",
31
31
  cwd: config.projectDir,
32
32
  });
@@ -34,7 +34,7 @@ function init(setup, config) {
34
34
  `--project-name=${projectName}`,
35
35
  "--overwrite",
36
36
  "--platforms=web",
37
- setup.hosting.source,
37
+ setup.featureInfo.hosting.source,
38
38
  ], { stdio: "inherit", cwd: config.projectDir });
39
39
  if (result.status !== 0)
40
40
  throw new error_1.FirebaseError("We were not able to create your flutter app, create the application yourself https://docs.flutter.dev/get-started/test-drive?tab=terminal before trying again.");
@@ -214,7 +214,7 @@ async function init(setup, config) {
214
214
  ],
215
215
  });
216
216
  (0, child_process_1.execSync)(`npx --yes create-next-app@"${exports.supportedRange}" -e hello-world ` +
217
- `${setup.hosting.source} --use-npm --${language}`, { stdio: "inherit", cwd: config.projectDir });
217
+ `${setup.featureInfo.hosting.source} --use-npm --${language}`, { stdio: "inherit", cwd: config.projectDir });
218
218
  }
219
219
  exports.init = init;
220
220
  async function ɵcodegenPublicDirectory(sourceDir, destDir, _, context) {
@@ -93,7 +93,7 @@ async function getConfig(cwd) {
93
93
  }
94
94
  exports.getConfig = getConfig;
95
95
  function init(setup, config) {
96
- (0, child_process_1.execSync)(`npx --yes nuxi@"${exports.supportedRange}" init ${setup.hosting.source}`, {
96
+ (0, child_process_1.execSync)(`npx --yes nuxi@"${exports.supportedRange}" init ${setup.featureInfo.hosting.source}`, {
97
97
  stdio: "inherit",
98
98
  cwd: config.projectDir,
99
99
  });
@@ -25,11 +25,14 @@ async function init(setup, config, baseTemplate = "vanilla") {
25
25
  { name: "TypeScript", value: `${baseTemplate}-ts` },
26
26
  ],
27
27
  });
28
- (0, child_process_1.execSync)(`npm create vite@"${exports.supportedRange}" ${setup.hosting.source} --yes -- --template ${template}`, {
28
+ (0, child_process_1.execSync)(`npm create vite@"${exports.supportedRange}" ${setup.featureInfo.hosting.source} --yes -- --template ${template}`, {
29
29
  stdio: "inherit",
30
30
  cwd: config.projectDir,
31
31
  });
32
- (0, child_process_1.execSync)(`npm install`, { stdio: "inherit", cwd: (0, path_1.join)(config.projectDir, setup.hosting.source) });
32
+ (0, child_process_1.execSync)(`npm install`, {
33
+ stdio: "inherit",
34
+ cwd: (0, path_1.join)(config.projectDir, setup.featureInfo.hosting.source),
35
+ });
33
36
  }
34
37
  exports.init = init;
35
38
  const viteDiscoverWithNpmDependency = (dep) => async (dir) => await discover(dir, undefined, dep);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.interactiveCreateHostingSite = void 0;
3
+ exports.pickHostingSiteName = void 0;
4
4
  const error_1 = require("../error");
5
5
  const utils_1 = require("../utils");
6
6
  const projectUtils_1 = require("../projectUtils");
@@ -9,11 +9,11 @@ const prompt_1 = require("../prompt");
9
9
  const nameSuggestion = new RegExp("try something like `(.+)`");
10
10
  const prompt = "Please provide an unique, URL-friendly id for your site. Your site's URL will be <site-id>.web.app. " +
11
11
  'We recommend using letters, numbers, and hyphens (e.g. "{project-id}-{random-hash}"):';
12
- async function interactiveCreateHostingSite(siteId, appId, options) {
12
+ async function pickHostingSiteName(siteId, options) {
13
13
  const projectId = (0, projectUtils_1.needProjectId)(options);
14
14
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
15
15
  let id = siteId;
16
- let newSite;
16
+ let nameConfirmed = false;
17
17
  let suggestion;
18
18
  if (!id) {
19
19
  const attempt = await trySiteID(projectNumber, projectId);
@@ -24,7 +24,7 @@ async function interactiveCreateHostingSite(siteId, appId, options) {
24
24
  suggestion = attempt.suggestion;
25
25
  }
26
26
  }
27
- while (!newSite) {
27
+ while (!nameConfirmed) {
28
28
  if (!id || suggestion) {
29
29
  id = await (0, prompt_1.input)({
30
30
  message: prompt,
@@ -32,24 +32,16 @@ async function interactiveCreateHostingSite(siteId, appId, options) {
32
32
  default: suggestion,
33
33
  });
34
34
  }
35
- try {
36
- newSite = await (0, api_1.createSite)(projectNumber, id, appId);
37
- }
38
- catch (err) {
39
- if (!(err instanceof error_1.FirebaseError)) {
40
- throw err;
41
- }
42
- if (options.nonInteractive) {
43
- throw err;
44
- }
35
+ const attempt = await trySiteID(projectNumber, id, options.nonInteractive);
36
+ nameConfirmed = attempt.available;
37
+ suggestion = attempt.suggestion;
38
+ if (!nameConfirmed)
45
39
  id = "";
46
- suggestion = getSuggestionFromError(err);
47
- }
48
40
  }
49
- return newSite;
41
+ return id;
50
42
  }
51
- exports.interactiveCreateHostingSite = interactiveCreateHostingSite;
52
- async function trySiteID(projectNumber, id) {
43
+ exports.pickHostingSiteName = pickHostingSiteName;
44
+ async function trySiteID(projectNumber, id, nonInteractive = false) {
53
45
  try {
54
46
  await (0, api_1.createSite)(projectNumber, id, "", true);
55
47
  return { available: true };
@@ -58,6 +50,9 @@ async function trySiteID(projectNumber, id) {
58
50
  if (!(err instanceof error_1.FirebaseError)) {
59
51
  throw err;
60
52
  }
53
+ if (nonInteractive) {
54
+ throw err;
55
+ }
61
56
  const suggestion = getSuggestionFromError(err);
62
57
  return { available: false, suggestion };
63
58
  }
@@ -29,11 +29,11 @@ const HOSTING_GITHUB_ACTION_NAME = "FirebaseExtended/action-hosting-deploy@v0";
29
29
  const SERVICE_ACCOUNT_MAX_KEY_NUMBER = 10;
30
30
  const githubApiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.githubApiOrigin)(), auth: false });
31
31
  async function initGitHub(setup) {
32
- var _a, _b, _c, _d, _e;
32
+ var _a, _b, _c, _d, _e, _f, _g;
33
33
  if (!setup.projectId) {
34
34
  return (0, utils_1.reject)("Could not determine Project ID, can't set up GitHub workflow.", { exit: 1 });
35
35
  }
36
- if (!setup.config.hosting) {
36
+ if (!setup.config.hosting && !((_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.hosting)) {
37
37
  return (0, utils_1.reject)(`Didn't find a Hosting config in firebase.json. Run ${(0, colorette_1.bold)("firebase init hosting")} instead.`);
38
38
  }
39
39
  logger_1.logger.info();
@@ -67,10 +67,10 @@ async function initGitHub(setup) {
67
67
  (0, utils_1.logSuccess)(`Uploaded service account JSON to GitHub as secret ${(0, colorette_1.bold)(githubSecretName)}.`);
68
68
  (0, utils_1.logBullet)(`You can manage your secrets at https://github.com/${repo}/settings/secrets.`);
69
69
  logger_1.logger.info();
70
- if (!Array.isArray(setup.config.hosting) && setup.config.hosting.predeploy) {
70
+ if (!Array.isArray(setup.config.hosting) && ((_b = setup.config.hosting) === null || _b === void 0 ? void 0 : _b.predeploy)) {
71
71
  (0, utils_1.logBullet)(`You have a predeploy script configured in firebase.json.`);
72
72
  }
73
- const { script } = await promptForBuildScript((_a = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _a === void 0 ? void 0 : _a.useWebFrameworks);
73
+ const { script } = await promptForBuildScript((_c = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _c === void 0 ? void 0 : _c.useWebFrameworks);
74
74
  const ymlDeployDoc = loadYMLDeploy();
75
75
  let shouldWriteYMLHostingFile = true;
76
76
  let shouldWriteYMLDeployFile = false;
@@ -81,7 +81,7 @@ async function initGitHub(setup) {
81
81
  shouldWriteYMLHostingFile = overwrite;
82
82
  }
83
83
  if (shouldWriteYMLHostingFile) {
84
- writeChannelActionYMLFile(YML_FULL_PATH_PULL_REQUEST, githubSecretName, setup.projectId, script, (_b = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _b === void 0 ? void 0 : _b.useWebFrameworks, (_c = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _c === void 0 ? void 0 : _c.source);
84
+ writeChannelActionYMLFile(YML_FULL_PATH_PULL_REQUEST, githubSecretName, setup.projectId, script, (_d = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _d === void 0 ? void 0 : _d.useWebFrameworks, (_e = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _e === void 0 ? void 0 : _e.source);
85
85
  logger_1.logger.info();
86
86
  (0, utils_1.logSuccess)(`Created workflow file ${(0, colorette_1.bold)(YML_FULL_PATH_PULL_REQUEST)}`);
87
87
  }
@@ -101,7 +101,7 @@ async function initGitHub(setup) {
101
101
  shouldWriteYMLDeployFile = true;
102
102
  }
103
103
  if (shouldWriteYMLDeployFile) {
104
- writeDeployToProdActionYMLFile(YML_FULL_PATH_MERGE, branch, githubSecretName, setup.projectId, script, (_d = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _d === void 0 ? void 0 : _d.useWebFrameworks, (_e = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _e === void 0 ? void 0 : _e.source);
104
+ writeDeployToProdActionYMLFile(YML_FULL_PATH_MERGE, branch, githubSecretName, setup.projectId, script, (_f = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _f === void 0 ? void 0 : _f.useWebFrameworks, (_g = setup === null || setup === void 0 ? void 0 : setup.hosting) === null || _g === void 0 ? void 0 : _g.source);
105
105
  logger_1.logger.info();
106
106
  (0, utils_1.logSuccess)(`Created workflow file ${(0, colorette_1.bold)(YML_FULL_PATH_MERGE)}`);
107
107
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.doSetup = void 0;
3
+ exports.actuate = exports.askQuestions = void 0;
4
4
  const clc = require("colorette");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const path_1 = require("path");
@@ -15,13 +15,16 @@ const getDefaultHostingSite_1 = require("../../../getDefaultHostingSite");
15
15
  const utils_1 = require("../../../utils");
16
16
  const interactive_1 = require("../../../hosting/interactive");
17
17
  const templates_1 = require("../../../templates");
18
+ const error_1 = require("../../../error");
19
+ const api_1 = require("../../../hosting/api");
18
20
  const INDEX_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/index.html");
19
21
  const MISSING_TEMPLATE = (0, templates_1.readTemplateSync)("init/hosting/404.html");
20
22
  const DEFAULT_IGNORES = ["firebase.json", "**/.*", "**/node_modules/**"];
21
- async function doSetup(setup, config, options) {
22
- var _a, _b, _c, _d;
23
- var _e, _f, _g;
24
- setup.hosting = {};
23
+ async function askQuestions(setup, config, options) {
24
+ var _a, _b, _c, _d, _e, _f;
25
+ var _g, _h, _j, _k, _l;
26
+ setup.featureInfo = setup.featureInfo || {};
27
+ setup.featureInfo.hosting = {};
25
28
  if (setup.projectId) {
26
29
  let hasHostingSite = true;
27
30
  try {
@@ -33,95 +36,72 @@ async function doSetup(setup, config, options) {
33
36
  }
34
37
  hasHostingSite = false;
35
38
  }
36
- if (!hasHostingSite) {
37
- const confirmCreate = await (0, prompt_1.confirm)({
39
+ if (!hasHostingSite &&
40
+ (await (0, prompt_1.confirm)({
38
41
  message: "A Firebase Hosting site is required to deploy. Would you like to create one now?",
39
42
  default: true,
40
- });
41
- if (confirmCreate) {
42
- const createOptions = {
43
- projectId: setup.projectId,
44
- nonInteractive: options.nonInteractive,
45
- };
46
- const newSite = await (0, interactive_1.interactiveCreateHostingSite)("", "", createOptions);
47
- logger_1.logger.info();
48
- (0, utils_1.logSuccess)(`Firebase Hosting site ${(0, utils_1.last)(newSite.name.split("/"))} created!`);
49
- logger_1.logger.info();
50
- }
43
+ }))) {
44
+ const createOptions = {
45
+ projectId: setup.projectId,
46
+ nonInteractive: options.nonInteractive,
47
+ };
48
+ setup.featureInfo.hosting.newSiteId = await (0, interactive_1.pickHostingSiteName)("", createOptions);
51
49
  }
52
50
  }
53
51
  let discoveredFramework = experiments.isEnabled("webframeworks")
54
52
  ? await (0, frameworks_1.discover)(config.projectDir, false)
55
53
  : undefined;
56
54
  if (experiments.isEnabled("webframeworks")) {
57
- if (discoveredFramework) {
58
- const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
59
- (_a = (_e = setup.hosting).useDiscoveredFramework) !== null && _a !== void 0 ? _a : (_e.useDiscoveredFramework = await (0, prompt_1.confirm)({
60
- message: `Detected an existing ${name} codebase in the current directory, should we use this?`,
55
+ if (discoveredFramework &&
56
+ (await (0, prompt_1.confirm)({
57
+ message: `Detected an existing ${frameworks_1.WebFrameworks[discoveredFramework.framework].name} codebase in the current directory, do you want to use this?`,
61
58
  default: true,
62
- }));
63
- }
64
- if (setup.hosting.useDiscoveredFramework) {
65
- setup.hosting.source = ".";
66
- setup.hosting.useWebFrameworks = true;
59
+ }))) {
60
+ setup.featureInfo.hosting.source = ".";
61
+ setup.featureInfo.hosting.useWebFrameworks = true;
62
+ setup.featureInfo.hosting.useDiscoveredFramework = true;
63
+ setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
67
64
  }
68
65
  else {
69
- setup.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
66
+ setup.featureInfo.hosting.useWebFrameworks = await (0, prompt_1.confirm)(`Do you want to use a web framework? (${clc.bold("experimental")})`);
70
67
  }
71
68
  }
72
- if (setup.hosting.useWebFrameworks) {
73
- (_b = (_f = setup.hosting).source) !== null && _b !== void 0 ? _b : (_f.source = await (0, prompt_1.input)({
69
+ if (setup.featureInfo.hosting.useWebFrameworks) {
70
+ (_a = (_g = setup.featureInfo.hosting).source) !== null && _a !== void 0 ? _a : (_g.source = await (0, prompt_1.input)({
74
71
  message: "What folder would you like to use for your web application's root directory?",
75
72
  default: "hosting",
76
73
  }));
77
- if (setup.hosting.source !== ".")
78
- delete setup.hosting.useDiscoveredFramework;
79
- discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.hosting.source));
74
+ discoveredFramework = await (0, frameworks_1.discover)((0, path_1.join)(config.projectDir, setup.featureInfo.hosting.source));
80
75
  if (discoveredFramework) {
81
76
  const name = frameworks_1.WebFrameworks[discoveredFramework.framework].name;
82
- (_c = (_g = setup.hosting).useDiscoveredFramework) !== null && _c !== void 0 ? _c : (_g.useDiscoveredFramework = await (0, prompt_1.confirm)({
83
- message: `Detected an existing ${name} codebase in ${setup.hosting.source}, should we use this?`,
77
+ (_b = (_h = setup.featureInfo.hosting).useDiscoveredFramework) !== null && _b !== void 0 ? _b : (_h.useDiscoveredFramework = await (0, prompt_1.confirm)({
78
+ message: `Detected an existing ${name} codebase in ${setup.featureInfo.hosting.source}, should we use this?`,
84
79
  default: true,
85
80
  }));
81
+ if (setup.featureInfo.hosting.useDiscoveredFramework)
82
+ setup.featureInfo.hosting.webFramework = discoveredFramework.framework;
86
83
  }
87
- if (setup.hosting.useDiscoveredFramework && discoveredFramework) {
88
- setup.hosting.webFramework = discoveredFramework.framework;
89
- }
90
- else {
91
- const choices = [];
92
- for (const value in frameworks_1.WebFrameworks) {
93
- if (frameworks_1.WebFrameworks[value]) {
94
- const { name, init } = frameworks_1.WebFrameworks[value];
95
- if (init)
96
- choices.push({ name, value });
97
- }
84
+ const choices = [];
85
+ for (const value in frameworks_1.WebFrameworks) {
86
+ if (frameworks_1.WebFrameworks[value]) {
87
+ const { name, init } = frameworks_1.WebFrameworks[value];
88
+ if (init)
89
+ choices.push({ name, value });
98
90
  }
99
- const defaultChoice = (_d = choices.find(({ value }) => value === (discoveredFramework === null || discoveredFramework === void 0 ? void 0 : discoveredFramework.framework))) === null || _d === void 0 ? void 0 : _d.value;
100
- setup.hosting.whichFramework =
101
- setup.hosting.whichFramework ||
102
- (await (0, prompt_1.select)({
103
- message: "Please choose the framework:",
104
- default: defaultChoice,
105
- choices,
106
- }));
107
- if (discoveredFramework)
108
- (0, node_fs_1.rmSync)(setup.hosting.source, { recursive: true });
109
- await frameworks_1.WebFrameworks[setup.hosting.whichFramework].init(setup, config);
110
91
  }
111
- setup.hosting.region =
112
- setup.hosting.region ||
92
+ const defaultChoice = (_c = choices.find(({ value }) => value === (discoveredFramework === null || discoveredFramework === void 0 ? void 0 : discoveredFramework.framework))) === null || _c === void 0 ? void 0 : _c.value;
93
+ (_d = (_j = setup.featureInfo.hosting).webFramework) !== null && _d !== void 0 ? _d : (_j.webFramework = await (0, prompt_1.select)({
94
+ message: "Please choose the framework:",
95
+ default: defaultChoice,
96
+ choices,
97
+ }));
98
+ setup.featureInfo.hosting.region =
99
+ setup.featureInfo.hosting.region ||
113
100
  (await (0, prompt_1.select)({
114
101
  message: "In which region would you like to host server-side content, if applicable?",
115
102
  default: constants_1.DEFAULT_REGION,
116
103
  choices: constants_1.ALLOWED_SSR_REGIONS.filter((region) => region.recommended),
117
104
  }));
118
- setup.config.hosting = {
119
- source: setup.hosting.source,
120
- ignore: DEFAULT_IGNORES,
121
- frameworksBackend: {
122
- region: setup.hosting.region,
123
- },
124
- };
125
105
  }
126
106
  else {
127
107
  logger_1.logger.info();
@@ -129,35 +109,58 @@ async function doSetup(setup, config, options) {
129
109
  logger_1.logger.info(`will contain Hosting assets to be uploaded with ${clc.bold("firebase deploy")}. If you`);
130
110
  logger_1.logger.info("have a build process for your assets, use your build's output directory.");
131
111
  logger_1.logger.info();
132
- setup.hosting.public =
133
- setup.hosting.public ||
134
- (await (0, prompt_1.input)({
135
- message: "What do you want to use as your public directory?",
136
- default: "public",
137
- }));
138
- setup.hosting.spa =
139
- setup.hosting.spa ||
140
- (await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
112
+ (_e = (_k = setup.featureInfo.hosting).public) !== null && _e !== void 0 ? _e : (_k.public = await (0, prompt_1.input)({
113
+ message: "What do you want to use as your public directory?",
114
+ default: "public",
115
+ }));
116
+ (_f = (_l = setup.featureInfo.hosting).spa) !== null && _f !== void 0 ? _f : (_l.spa = await (0, prompt_1.confirm)("Configure as a single-page app (rewrite all urls to /index.html)?"));
117
+ }
118
+ if (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?")) {
119
+ return (0, github_1.initGitHub)(setup);
120
+ }
121
+ }
122
+ exports.askQuestions = askQuestions;
123
+ async function actuate(setup, config, options) {
124
+ var _a;
125
+ const hostingInfo = (_a = setup.featureInfo) === null || _a === void 0 ? void 0 : _a.hosting;
126
+ if (!hostingInfo) {
127
+ throw new error_1.FirebaseError("Could not find hosting info in setup.featureInfo.hosting. This should not happen.", { exit: 2 });
128
+ }
129
+ if (hostingInfo.newSiteId && setup.projectId) {
130
+ await (0, api_1.createSite)(setup.projectId, hostingInfo.newSiteId);
131
+ logger_1.logger.info();
132
+ (0, utils_1.logSuccess)(`Firebase Hosting site ${hostingInfo.newSiteId} created!`);
133
+ logger_1.logger.info();
134
+ }
135
+ if (hostingInfo.webFramework) {
136
+ if (!hostingInfo.useDiscoveredFramework) {
137
+ if (hostingInfo.source && (0, node_fs_1.existsSync)(hostingInfo.source)) {
138
+ (0, node_fs_1.rmSync)(hostingInfo.source, { recursive: true });
139
+ }
140
+ await frameworks_1.WebFrameworks[hostingInfo.webFramework].init(setup, config);
141
+ }
141
142
  setup.config.hosting = {
142
- public: setup.hosting.public,
143
+ source: hostingInfo.source,
143
144
  ignore: DEFAULT_IGNORES,
145
+ frameworksBackend: {
146
+ region: hostingInfo.region,
147
+ },
144
148
  };
145
149
  }
146
- setup.hosting.github =
147
- setup.hosting.github || (await (0, prompt_1.confirm)("Set up automatic builds and deploys with GitHub?"));
148
- if (!setup.hosting.useWebFrameworks) {
149
- if (setup.hosting.spa) {
150
+ else {
151
+ setup.config.hosting = {
152
+ public: hostingInfo.public,
153
+ ignore: DEFAULT_IGNORES,
154
+ };
155
+ if (hostingInfo.spa) {
150
156
  setup.config.hosting.rewrites = [{ source: "**", destination: "/index.html" }];
151
157
  }
152
158
  else {
153
- await config.askWriteProjectFile(`${setup.hosting.public}/404.html`, MISSING_TEMPLATE);
159
+ await config.askWriteProjectFile(`${hostingInfo.public}/404.html`, MISSING_TEMPLATE, !!options.force);
154
160
  }
155
161
  const c = new apiv2_1.Client({ urlPrefix: "https://www.gstatic.com", auth: false });
156
162
  const response = await c.get("/firebasejs/releases.json");
157
- await config.askWriteProjectFile(`${setup.hosting.public}/index.html`, INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version));
158
- }
159
- if (setup.hosting.github) {
160
- return (0, github_1.initGitHub)(setup);
163
+ await config.askWriteProjectFile(`${hostingInfo.public}/index.html`, INDEX_TEMPLATE.replace(/{{VERSION}}/g, response.body.current.version), !!options.force);
161
164
  }
162
165
  }
163
- exports.doSetup = doSetup;
166
+ exports.actuate = actuate;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.aiLogicActuate = exports.aiLogicAskQuestions = exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hosting = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
3
+ exports.aiLogicActuate = exports.aiLogicAskQuestions = exports.aitools = exports.apptestingAcutate = exports.apptestingAskQuestions = exports.genkit = exports.apphosting = exports.dataconnectSdkActuate = exports.dataconnectSdkAskQuestions = exports.dataconnectActuate = exports.dataconnectAskQuestions = exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storageActuate = exports.storageAskQuestions = exports.hostingActuate = exports.hostingAskQuestions = exports.functions = exports.firestoreActuate = exports.firestoreAskQuestions = exports.databaseActuate = exports.databaseAskQuestions = exports.account = void 0;
4
4
  var account_1 = require("./account");
5
5
  Object.defineProperty(exports, "account", { enumerable: true, get: function () { return account_1.doSetup; } });
6
6
  var database_1 = require("./database");
@@ -12,7 +12,8 @@ Object.defineProperty(exports, "firestoreActuate", { enumerable: true, get: func
12
12
  var functions_1 = require("./functions");
13
13
  Object.defineProperty(exports, "functions", { enumerable: true, get: function () { return functions_1.doSetup; } });
14
14
  var hosting_1 = require("./hosting");
15
- Object.defineProperty(exports, "hosting", { enumerable: true, get: function () { return hosting_1.doSetup; } });
15
+ Object.defineProperty(exports, "hostingAskQuestions", { enumerable: true, get: function () { return hosting_1.askQuestions; } });
16
+ Object.defineProperty(exports, "hostingActuate", { enumerable: true, get: function () { return hosting_1.actuate; } });
16
17
  var storage_1 = require("./storage");
17
18
  Object.defineProperty(exports, "storageAskQuestions", { enumerable: true, get: function () { return storage_1.askQuestions; } });
18
19
  Object.defineProperty(exports, "storageActuate", { enumerable: true, get: function () { return storage_1.actuate; } });
package/lib/init/index.js CHANGED
@@ -30,7 +30,11 @@ const featuresList = [
30
30
  actuate: features.dataconnectSdkActuate,
31
31
  },
32
32
  { name: "functions", doSetup: features.functions },
33
- { name: "hosting", doSetup: features.hosting },
33
+ {
34
+ name: "hosting",
35
+ askQuestions: features.hostingAskQuestions,
36
+ actuate: features.hostingActuate,
37
+ },
34
38
  {
35
39
  name: "storage",
36
40
  askQuestions: features.storageAskQuestions,