firebase-tools 11.27.0 → 11.29.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 (38) hide show
  1. package/lib/commands/database-import.js +12 -1
  2. package/lib/commands/ext-configure.js +2 -2
  3. package/lib/commands/ext-install.js +8 -3
  4. package/lib/commands/ext-update.js +6 -1
  5. package/lib/commands/firestore-databases-create.js +45 -0
  6. package/lib/commands/firestore-databases-delete.js +39 -0
  7. package/lib/commands/firestore-databases-get.js +25 -0
  8. package/lib/commands/firestore-databases-list.js +24 -0
  9. package/lib/commands/firestore-databases-update.js +42 -0
  10. package/lib/commands/firestore-indexes-list.js +2 -2
  11. package/lib/commands/firestore-locations.js +24 -0
  12. package/lib/commands/index.js +7 -0
  13. package/lib/commands/init.js +5 -0
  14. package/lib/commands/open.js +10 -0
  15. package/lib/database/import.js +4 -6
  16. package/lib/deploy/firestore/deploy.js +2 -2
  17. package/lib/deploy/functions/build.js +8 -11
  18. package/lib/deploy/functions/params.js +2 -2
  19. package/lib/deploy/functions/validate.js +4 -15
  20. package/lib/emulator/downloadableEmulators.js +3 -3
  21. package/lib/experiments.js +1 -6
  22. package/lib/extensions/extensionsApi.js +9 -6
  23. package/lib/extensions/extensionsHelper.js +5 -37
  24. package/lib/extensions/manifest.js +23 -1
  25. package/lib/firestore/{indexes-sort.js → api-sort.js} +10 -2
  26. package/lib/firestore/{indexes-api.js → api-types.js} +16 -1
  27. package/lib/firestore/{indexes.js → api.js} +122 -16
  28. package/lib/frameworks/index.js +12 -5
  29. package/lib/functions/secrets.js +4 -4
  30. package/lib/gcp/eventarc.js +1 -1
  31. package/lib/init/features/extensions/index.js +16 -0
  32. package/lib/init/features/firestore/indexes.js +2 -2
  33. package/lib/init/features/index.js +3 -1
  34. package/lib/init/index.js +1 -0
  35. package/lib/prompt.js +19 -1
  36. package/npm-shrinkwrap.json +2 -2
  37. package/package.json +1 -1
  38. /package/lib/firestore/{indexes-spec.js → api-spec.js} +0 -0
@@ -16,12 +16,17 @@ const database_1 = require("../management/database");
16
16
  const api_1 = require("../database/api");
17
17
  const requireDatabaseInstance_1 = require("../requireDatabaseInstance");
18
18
  const requirePermissions_1 = require("../requirePermissions");
19
+ const MAX_CHUNK_SIZE_MB = 10;
20
+ const MAX_PAYLOAD_SIZE_MB = 256;
21
+ const CONCURRENCY_LIMIT = 5;
19
22
  exports.command = new command_1.Command("database:import <path> [infile]")
20
23
  .description("non-atomically import the contents of a JSON file to the specified path in Realtime Database")
21
24
  .withForce()
22
25
  .option("--instance <instance>", "use the database <instance>.firebaseio.com (if omitted, use default database instance)")
23
26
  .option("--disable-triggers", "suppress any Cloud functions triggered by this operation, default to true", true)
24
27
  .option("--filter <dataPath>", "import only data at this path in the JSON file (if omitted, import entire file)")
28
+ .option("--chunk-size <mb>", "max chunk size in megabytes, default to 10 MB")
29
+ .option("--concurrency <val>", "concurrency limit, default to 5")
25
30
  .before(requirePermissions_1.requirePermissions, ["firebasedatabase.instances.update"])
26
31
  .before(requireDatabaseInstance_1.requireDatabaseInstance)
27
32
  .before(database_1.populateInstanceDetails)
@@ -33,6 +38,10 @@ exports.command = new command_1.Command("database:import <path> [infile]")
33
38
  if (!infile) {
34
39
  throw new error_1.FirebaseError("No file supplied");
35
40
  }
41
+ const chunkMegabytes = options.chunkSize ? parseInt(options.chunkSize, 10) : MAX_CHUNK_SIZE_MB;
42
+ if (chunkMegabytes > MAX_PAYLOAD_SIZE_MB) {
43
+ throw new error_1.FirebaseError("Max chunk size cannot exceed 256 MB");
44
+ }
36
45
  const projectId = (0, projectUtils_1.needProjectId)(options);
37
46
  const origin = (0, api_1.realtimeOriginOrEmulatorOrCustomUrl)(options.instanceDetails.databaseUrl);
38
47
  const dbPath = utils.getDatabaseUrl(origin, options.instance, path);
@@ -51,7 +60,9 @@ exports.command = new command_1.Command("database:import <path> [infile]")
51
60
  }
52
61
  const inStream = fs.createReadStream(infile);
53
62
  const dataPath = options.filter || "";
54
- const importer = new import_1.default(dbUrl, inStream, dataPath);
63
+ const chunkBytes = chunkMegabytes * 1024 * 1024;
64
+ const concurrency = options.concurrency ? parseInt(options.concurrency, 10) : CONCURRENCY_LIMIT;
65
+ const importer = new import_1.default(dbUrl, inStream, dataPath, chunkBytes, concurrency);
55
66
  let responses;
56
67
  try {
57
68
  responses = await importer.execute();
@@ -32,7 +32,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
32
32
  .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
33
33
  .before(extensionsHelper_1.diagnoseAndFixProject)
34
34
  .action(async (instanceId, options) => {
35
- var _a;
35
+ var _a, _b;
36
36
  const projectId = (0, projectUtils_1.getProjectId)(options);
37
37
  if (options.nonInteractive) {
38
38
  throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead. ` +
@@ -57,7 +57,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
57
57
  instanceId,
58
58
  projectDir: config.projectDir,
59
59
  });
60
- const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params.concat((_a = spec.systemParams) !== null && _a !== void 0 ? _a : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
60
+ const [immutableParams, tbdParams] = (0, functional_1.partition)(((_a = spec.params) !== null && _a !== void 0 ? _a : []).concat((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
61
61
  infoImmutableParams(immutableParams, oldParamValues);
62
62
  paramHelper.setNewDefaults(tbdParams, oldParamValues);
63
63
  const mutableParamsBindingOptions = await paramHelper.getParams({
@@ -12,9 +12,11 @@ const error_1 = require("../error");
12
12
  const projectUtils_1 = require("../projectUtils");
13
13
  const extensionsApi = require("../extensions/extensionsApi");
14
14
  const refs = require("../extensions/refs");
15
+ const secretsUtils = require("../extensions/secretsUtils");
15
16
  const warnings_1 = require("../extensions/warnings");
16
17
  const paramHelper = require("../extensions/paramHelper");
17
18
  const extensionsHelper_1 = require("../extensions/extensionsHelper");
19
+ const prompt_1 = require("../prompt");
18
20
  const utils_1 = require("../extensions/utils");
19
21
  const requirePermissions_1 = require("../requirePermissions");
20
22
  const utils = require("../utils");
@@ -74,7 +76,7 @@ exports.command = new command_1.Command("ext:install [extensionName]")
74
76
  extensionVersion,
75
77
  });
76
78
  }
77
- if (!(await (0, extensionsHelper_1.confirm)({
79
+ if (!(await (0, prompt_1.confirm)({
78
80
  nonInteractive: options.nonInteractive,
79
81
  force: options.force,
80
82
  default: true,
@@ -119,13 +121,16 @@ async function infoExtensionVersion(args) {
119
121
  await (0, warnings_1.displayWarningPrompts)(ref.publisherId, args.extensionVersion);
120
122
  }
121
123
  async function installToManifest(options) {
122
- var _a, _b;
124
+ var _a, _b, _c;
123
125
  const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
124
126
  const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
125
127
  const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
126
128
  if (!spec) {
127
129
  throw new error_1.FirebaseError(`Could not find the extension.yaml for ${extensionName}. Please make sure this is a valid extension and try again.`);
128
130
  }
131
+ if (secretsUtils.usesSecrets(spec)) {
132
+ await secretsUtils.ensureSecretManagerApiEnabled(options);
133
+ }
129
134
  const config = manifest.loadConfig(options);
130
135
  let instanceId = spec.name;
131
136
  while (manifest.instanceExists(instanceId, config)) {
@@ -133,7 +138,7 @@ async function installToManifest(options) {
133
138
  }
134
139
  const paramBindingOptions = await paramHelper.getParams({
135
140
  projectId,
136
- paramSpecs: spec.params.concat((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []),
141
+ paramSpecs: ((_b = spec.params) !== null && _b !== void 0 ? _b : []).concat((_c = spec.systemParams) !== null && _c !== void 0 ? _c : []),
137
142
  nonInteractive,
138
143
  paramsEnvPath,
139
144
  instanceId,
@@ -11,8 +11,10 @@ const extensionsApi = require("../extensions/extensionsApi");
11
11
  const extensionsHelper_1 = require("../extensions/extensionsHelper");
12
12
  const paramHelper = require("../extensions/paramHelper");
13
13
  const updateHelper_1 = require("../extensions/updateHelper");
14
+ const secretsUtils = require("../extensions/secretsUtils");
14
15
  const refs = require("../extensions/refs");
15
16
  const projectUtils_1 = require("../projectUtils");
17
+ const prompt_1 = require("../prompt");
16
18
  const requirePermissions_1 = require("../requirePermissions");
17
19
  const utils = require("../utils");
18
20
  const experiments = require("../experiments");
@@ -60,7 +62,7 @@ exports.command = new command_1.Command("ext:update <extensionInstanceId> [updat
60
62
  return;
61
63
  }
62
64
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(oldExtensionVersion.ref)} to version ${clc.bold(newExtensionVersion.ref)}.`);
63
- if (!(await (0, extensionsHelper_1.confirm)({
65
+ if (!(await (0, prompt_1.confirm)({
64
66
  nonInteractive: options.nonInteractive,
65
67
  force: options.force,
66
68
  default: false,
@@ -68,6 +70,9 @@ exports.command = new command_1.Command("ext:update <extensionInstanceId> [updat
68
70
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Update aborted.");
69
71
  return;
70
72
  }
73
+ if (secretsUtils.usesSecrets(newExtensionVersion.spec)) {
74
+ await secretsUtils.ensureSecretManagerApiEnabled(options);
75
+ }
71
76
  const oldParamValues = manifest.readInstanceParam({
72
77
  instanceId,
73
78
  projectDir: config.projectDir,
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const clc = require("colorette");
6
+ const fsi = require("../firestore/api");
7
+ const types = require("../firestore/api-types");
8
+ const logger_1 = require("../logger");
9
+ const requirePermissions_1 = require("../requirePermissions");
10
+ const types_1 = require("../emulator/types");
11
+ const commandUtils_1 = require("../emulator/commandUtils");
12
+ exports.command = new command_1.Command("firestore:databases:create <database>")
13
+ .description("Create a database in your Firebase project.")
14
+ .option("--location <locationId>", "Region to create database, for example 'nam5'. Run 'firebase firestore:locations' to get a list of eligible locations. (required)")
15
+ .option("--delete-protection <deleteProtectionState>", "Whether or not to prevent deletion of database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'")
16
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.create"])
17
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
18
+ .action(async (database, options) => {
19
+ const api = new fsi.FirestoreApi();
20
+ if (!options.location) {
21
+ logger_1.logger.error("Missing required flag --location. See firebase firestore:databases:create --help for more info.");
22
+ return;
23
+ }
24
+ const type = types.DatabaseType.FIRESTORE_NATIVE;
25
+ if (options.deleteProtection &&
26
+ options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.ENABLED &&
27
+ options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.DISABLED) {
28
+ logger_1.logger.error("Invalid value for flag --delete-protection. See firebase firestore:databases:create --help for more info.");
29
+ return;
30
+ }
31
+ const deleteProtectionState = options.deleteProtection === types.DatabaseDeleteProtectionStateOption.ENABLED
32
+ ? types.DatabaseDeleteProtectionState.ENABLED
33
+ : types.DatabaseDeleteProtectionState.DISABLED;
34
+ const databaseResp = await api.createDatabase(options.project, database, options.location, type, deleteProtectionState);
35
+ if (options.json) {
36
+ logger_1.logger.info(JSON.stringify(databaseResp, undefined, 2));
37
+ }
38
+ else {
39
+ logger_1.logger.info(clc.bold(`Successfully created ${api.prettyDatabaseString(databaseResp)}`));
40
+ logger_1.logger.info("Please be sure to configure Firebase rules in your Firebase config file for\n" +
41
+ "the new database. By default, created databases will have closed rules that\n" +
42
+ "block any incoming third-party traffic.");
43
+ }
44
+ return databaseResp;
45
+ });
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const clc = require("colorette");
6
+ const fsi = require("../firestore/api");
7
+ const prompt_1 = require("../prompt");
8
+ const logger_1 = require("../logger");
9
+ const requirePermissions_1 = require("../requirePermissions");
10
+ const types_1 = require("../emulator/types");
11
+ const commandUtils_1 = require("../emulator/commandUtils");
12
+ const error_1 = require("../error");
13
+ exports.command = new command_1.Command("firestore:databases:delete <database>")
14
+ .description("Delete a database in your Cloud Firestore project. Database delete protection state must be disabled. To do so, use the update command: firebase firestore:databases:update <database> --delete-protection DISABLED")
15
+ .option("--force", "Attempt to delete database without prompting for confirmation.")
16
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.delete"])
17
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
18
+ .action(async (database, options) => {
19
+ const api = new fsi.FirestoreApi();
20
+ if (!options.force) {
21
+ const confirmMessage = `You are about to delete projects/${options.project}/databases/${database}. Do you wish to continue?`;
22
+ const consent = await (0, prompt_1.promptOnce)({
23
+ type: "confirm",
24
+ message: confirmMessage,
25
+ default: false,
26
+ });
27
+ if (!consent) {
28
+ throw new error_1.FirebaseError("Delete database canceled.");
29
+ }
30
+ }
31
+ const databaseResp = await api.deleteDatabase(options.project, database);
32
+ if (options.json) {
33
+ logger_1.logger.info(JSON.stringify(databaseResp, undefined, 2));
34
+ }
35
+ else {
36
+ logger_1.logger.info(clc.bold(`Successfully deleted ${api.prettyDatabaseString(databaseResp)}`));
37
+ }
38
+ return databaseResp;
39
+ });
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const requirePermissions_1 = require("../requirePermissions");
8
+ const types_1 = require("../emulator/types");
9
+ const commandUtils_1 = require("../emulator/commandUtils");
10
+ exports.command = new command_1.Command("firestore:databases:get [database]")
11
+ .description("Get database in your Cloud Firestore project.")
12
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.get"])
13
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
14
+ .action(async (database, options) => {
15
+ const api = new fsi.FirestoreApi();
16
+ const databaseId = database || "(default)";
17
+ const databaseResp = await api.getDatabase(options.project, databaseId);
18
+ if (options.json) {
19
+ logger_1.logger.info(JSON.stringify(databaseResp, undefined, 2));
20
+ }
21
+ else {
22
+ api.prettyPrintDatabase(databaseResp);
23
+ }
24
+ return databaseResp;
25
+ });
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const requirePermissions_1 = require("../requirePermissions");
8
+ const types_1 = require("../emulator/types");
9
+ const commandUtils_1 = require("../emulator/commandUtils");
10
+ exports.command = new command_1.Command("firestore:databases:list")
11
+ .description("List databases in your Cloud Firestore project.")
12
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.list"])
13
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
14
+ .action(async (options) => {
15
+ const api = new fsi.FirestoreApi();
16
+ const databases = await api.listDatabases(options.project);
17
+ if (options.json) {
18
+ logger_1.logger.info(JSON.stringify(databases, undefined, 2));
19
+ }
20
+ else {
21
+ api.prettyPrintDatabases(databases);
22
+ }
23
+ return databases;
24
+ });
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const clc = require("colorette");
6
+ const fsi = require("../firestore/api");
7
+ const types = require("../firestore/api-types");
8
+ const logger_1 = require("../logger");
9
+ const requirePermissions_1 = require("../requirePermissions");
10
+ const types_1 = require("../emulator/types");
11
+ const commandUtils_1 = require("../emulator/commandUtils");
12
+ exports.command = new command_1.Command("firestore:databases:update <database>")
13
+ .description("Update a database in your Firebase project. Must specify at least one property to update.")
14
+ .option("--json", "Prints raw json response of the create API call if specified")
15
+ .option("--delete-protection <deleteProtectionState>", "Whether or not to prevent deletion of database, for example 'ENABLED' or 'DISABLED'. Default is 'DISABLED'")
16
+ .before(requirePermissions_1.requirePermissions, ["datastore.databases.update"])
17
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
18
+ .action(async (database, options) => {
19
+ const api = new fsi.FirestoreApi();
20
+ if (!options.type && !options.deleteProtection) {
21
+ logger_1.logger.error("Missing properties to update. See firebase firestore:databases:update --help for more info.");
22
+ return;
23
+ }
24
+ const type = types.DatabaseType.FIRESTORE_NATIVE;
25
+ if (options.deleteProtection &&
26
+ options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.ENABLED &&
27
+ options.deleteProtection !== types.DatabaseDeleteProtectionStateOption.DISABLED) {
28
+ logger_1.logger.error("Invalid value for flag --delete-protection. See firebase firestore:databases:update --help for more info.");
29
+ return;
30
+ }
31
+ const deleteProtectionState = options.deleteProtection === types.DatabaseDeleteProtectionStateOption.ENABLED
32
+ ? types.DatabaseDeleteProtectionState.ENABLED
33
+ : types.DatabaseDeleteProtectionState.DISABLED;
34
+ const databaseResp = await api.updateDatabase(options.project, database, type, deleteProtectionState);
35
+ if (options.json) {
36
+ logger_1.logger.info(JSON.stringify(databaseResp, undefined, 2));
37
+ }
38
+ else {
39
+ logger_1.logger.info(clc.bold(`Successfully updated ${api.prettyDatabaseString(databaseResp)}`));
40
+ }
41
+ return databaseResp;
42
+ });
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const command_1 = require("../command");
5
5
  const clc = require("colorette");
6
- const fsi = require("../firestore/indexes");
6
+ const fsi = require("../firestore/api");
7
7
  const logger_1 = require("../logger");
8
8
  const requirePermissions_1 = require("../requirePermissions");
9
9
  const types_1 = require("../emulator/types");
@@ -17,7 +17,7 @@ exports.command = new command_1.Command("firestore:indexes")
17
17
  .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
18
18
  .action(async (options) => {
19
19
  var _a;
20
- const indexApi = new fsi.FirestoreIndexes();
20
+ const indexApi = new fsi.FirestoreApi();
21
21
  const databaseId = (_a = options.database) !== null && _a !== void 0 ? _a : "(default)";
22
22
  const indexes = await indexApi.listIndexes(options.project, databaseId);
23
23
  const fieldOverrides = await indexApi.listFieldOverrides(options.project, databaseId);
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const fsi = require("../firestore/api");
6
+ const logger_1 = require("../logger");
7
+ const requirePermissions_1 = require("../requirePermissions");
8
+ const types_1 = require("../emulator/types");
9
+ const commandUtils_1 = require("../emulator/commandUtils");
10
+ exports.command = new command_1.Command("firestore:locations")
11
+ .description("List possible locations for your Cloud Firestore project.")
12
+ .before(requirePermissions_1.requirePermissions, ["datastore.locations.list"])
13
+ .before(commandUtils_1.warnEmulatorNotSupported, types_1.Emulators.FIRESTORE)
14
+ .action(async (options) => {
15
+ const api = new fsi.FirestoreApi();
16
+ const locations = await api.locations(options.project);
17
+ if (options.json) {
18
+ logger_1.logger.info(JSON.stringify(locations, undefined, 2));
19
+ }
20
+ else {
21
+ api.prettyPrintLocations(locations);
22
+ }
23
+ return locations;
24
+ });
@@ -102,6 +102,13 @@ function load(client) {
102
102
  client.firestore = {};
103
103
  client.firestore.delete = loadCommand("firestore-delete");
104
104
  client.firestore.indexes = loadCommand("firestore-indexes-list");
105
+ client.firestore.locations = loadCommand("firestore-locations");
106
+ client.firestore.databases = {};
107
+ client.firestore.databases.list = loadCommand("firestore-databases-list");
108
+ client.firestore.databases.get = loadCommand("firestore-databases-get");
109
+ client.firestore.databases.create = loadCommand("firestore-databases-create");
110
+ client.firestore.databases.update = loadCommand("firestore-databases-update");
111
+ client.firestore.databases.delete = loadCommand("firestore-databases-delete");
105
112
  client.functions = {};
106
113
  client.functions.config = {};
107
114
  client.functions.config.clone = loadCommand("functions-config-clone");
@@ -62,6 +62,11 @@ const choices = [
62
62
  name: "Remote Config: Configure a template file for Remote Config",
63
63
  checked: false,
64
64
  },
65
+ {
66
+ value: "extensions",
67
+ name: "Extensions: Set up an empty Extensions manifest",
68
+ checked: false,
69
+ },
65
70
  ];
66
71
  const featureNames = choices.map((choice) => choice.value);
67
72
  const DESCRIPTION = `Interactively configure the current directory as a Firebase project or initialize new features in an already configured Firebase project directory.
@@ -24,6 +24,16 @@ const LINKS = [
24
24
  { name: "Firestore: Data", arg: "firestore", consolePath: "/firestore/data" },
25
25
  { name: "Firestore: Rules", arg: "firestore:rules", consolePath: "/firestore/rules" },
26
26
  { name: "Firestore: Indexes", arg: "firestore:indexes", consolePath: "/firestore/indexes" },
27
+ {
28
+ name: "Firestore: Databases List",
29
+ arg: "firestore:databases:list",
30
+ consolePath: "/firestore/databases/list",
31
+ },
32
+ {
33
+ name: "Firestore: Locations",
34
+ arg: "firestore:locations",
35
+ consolePath: "/firestore/locations",
36
+ },
27
37
  { name: "Firestore: Usage", arg: "firestore:usage", consolePath: "/firestore/usage" },
28
38
  { name: "Functions", arg: "functions", consolePath: "/functions/list" },
29
39
  { name: "Functions Log", arg: "functions:log" },
@@ -8,16 +8,14 @@ const StreamObject = require("stream-json/streamers/StreamObject");
8
8
  const apiv2_1 = require("../apiv2");
9
9
  const error_1 = require("../error");
10
10
  const pLimit = require("p-limit");
11
- const MAX_CHUNK_SIZE = 1024 * 1024 * 10;
12
- const CONCURRENCY_LIMIT = 5;
13
11
  class DatabaseImporter {
14
- constructor(dbUrl, inStream, dataPath, chunkSize = MAX_CHUNK_SIZE) {
12
+ constructor(dbUrl, inStream, dataPath, chunkBytes, concurrency) {
15
13
  this.dbUrl = dbUrl;
16
14
  this.inStream = inStream;
17
15
  this.dataPath = dataPath;
18
- this.chunkSize = chunkSize;
19
- this.limit = pLimit(CONCURRENCY_LIMIT);
16
+ this.chunkBytes = chunkBytes;
20
17
  this.client = new apiv2_1.Client({ urlPrefix: dbUrl.origin, auth: true });
18
+ this.limit = pLimit(concurrency);
21
19
  }
22
20
  async execute() {
23
21
  await this.checkLocationIsEmpty();
@@ -107,7 +105,7 @@ class DatabaseImporter {
107
105
  chunks.push(child);
108
106
  }
109
107
  }
110
- if (hasChunkedChild || size >= this.chunkSize) {
108
+ if (hasChunkedChild || size >= this.chunkBytes) {
111
109
  return { chunks, size };
112
110
  }
113
111
  else {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const clc = require("colorette");
4
- const indexes_1 = require("../../firestore/indexes");
4
+ const api_1 = require("../../firestore/api");
5
5
  const logger_1 = require("../../logger");
6
6
  const utils = require("../../utils");
7
7
  const rulesDeploy_1 = require("../../rulesDeploy");
@@ -20,7 +20,7 @@ async function deployIndexes(context, options) {
20
20
  }
21
21
  const indexesContext = (_a = context === null || context === void 0 ? void 0 : context.firestore) === null || _a === void 0 ? void 0 : _a.indexes;
22
22
  utils.logBullet(clc.bold(clc.cyan("firestore: ")) + "deploying indexes...");
23
- const firestoreIndexes = new indexes_1.FirestoreIndexes();
23
+ const firestoreIndexes = new api_1.FirestoreApi();
24
24
  await Promise.all(indexesContext.map(async (indexContext) => {
25
25
  const { databaseId, indexesFileName, indexesRawSpec } = indexContext;
26
26
  if (!indexesRawSpec) {
@@ -5,7 +5,6 @@ const backend = require("./backend");
5
5
  const proto = require("../../gcp/proto");
6
6
  const api = require("../../.../../api");
7
7
  const params = require("./params");
8
- const experiments = require("../../experiments");
9
8
  const error_1 = require("../../error");
10
9
  const functional_1 = require("../../functional");
11
10
  const env_1 = require("../../functions/env");
@@ -61,18 +60,16 @@ exports.AllIngressSettings = [
61
60
  ];
62
61
  async function resolveBackend(build, firebaseConfig, userEnvOpt, userEnvs, nonInteractive) {
63
62
  let paramValues = {};
64
- if (experiments.isEnabled("functionsparams")) {
65
- paramValues = await params.resolveParams(build.params, firebaseConfig, envWithTypes(build.params, userEnvs), nonInteractive);
66
- const toWrite = {};
67
- for (const paramName of Object.keys(paramValues)) {
68
- const paramValue = paramValues[paramName];
69
- if (Object.prototype.hasOwnProperty.call(userEnvs, paramName) || paramValue.internal) {
70
- continue;
71
- }
72
- toWrite[paramName] = paramValue.toString();
63
+ paramValues = await params.resolveParams(build.params, firebaseConfig, envWithTypes(build.params, userEnvs), nonInteractive);
64
+ const toWrite = {};
65
+ for (const paramName of Object.keys(paramValues)) {
66
+ const paramValue = paramValues[paramName];
67
+ if (Object.prototype.hasOwnProperty.call(userEnvs, paramName) || paramValue.internal) {
68
+ continue;
73
69
  }
74
- (0, env_1.writeUserEnvs)(toWrite, userEnvOpt);
70
+ toWrite[paramName] = paramValue.toString();
75
71
  }
72
+ (0, env_1.writeUserEnvs)(toWrite, userEnvOpt);
76
73
  return { backend: toBackend(build, paramValues), envs: paramValues };
77
74
  }
78
75
  exports.resolveBackend = resolveBackend;
@@ -8,6 +8,7 @@ const functional_1 = require("../../functional");
8
8
  const secretManager = require("../../gcp/secretManager");
9
9
  const storage_1 = require("../../gcp/storage");
10
10
  const cel_1 = require("./cel");
11
+ const secrets_1 = require("../../functions/secrets");
11
12
  function dependenciesCEL(expr) {
12
13
  const deps = [];
13
14
  const paramCapture = /{{ params\.(\w+) }}/g;
@@ -221,8 +222,7 @@ async function handleSecret(secretParam, projectId) {
221
222
  type: "password",
222
223
  message: `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${secretParam.name}. Enter a value for ${secretParam.label || secretParam.name}:`,
223
224
  });
224
- const secretLabel = { "firebase-hosting-managed": "yes" };
225
- await secretManager.createSecret(projectId, secretParam.name, secretLabel);
225
+ await secretManager.createSecret(projectId, secretParam.name, (0, secrets_1.labels)());
226
226
  await secretManager.addVersion(projectId, secretParam.name, secretValue);
227
227
  return secretValue;
228
228
  }
@@ -136,24 +136,13 @@ function functionsDirectoryExists(sourceDir, projectDir) {
136
136
  }
137
137
  exports.functionsDirectoryExists = functionsDirectoryExists;
138
138
  function functionIdsAreValid(functions) {
139
- const v1FunctionName = /^[a-zA-Z][a-zA-Z0-9_-]{0,62}$/;
140
- const invalidV1Ids = functions.filter((fn) => {
141
- return fn.platform === "gcfv1" && !v1FunctionName.test(fn.id);
142
- });
143
- if (invalidV1Ids.length !== 0) {
144
- const msg = `${invalidV1Ids.map((f) => f.id).join(", ")} function name(s) can only contain letters, ` +
139
+ const functionName = /^[a-zA-Z][a-zA-Z0-9_-]{0,62}$/;
140
+ const invalidIds = functions.filter((fn) => !functionName.test(fn.id));
141
+ if (invalidIds.length !== 0) {
142
+ const msg = `${invalidIds.map((f) => f.id).join(", ")} function name(s) can only contain letters, ` +
145
143
  `numbers, hyphens, and not exceed 62 characters in length`;
146
144
  throw new error_1.FirebaseError(msg);
147
145
  }
148
- const v2FunctionName = /^[a-z][a-z0-9-]{0,62}$/;
149
- const invalidV2Ids = functions.filter((fn) => {
150
- return fn.platform === "gcfv2" && !v2FunctionName.test(fn.id);
151
- });
152
- if (invalidV2Ids.length !== 0) {
153
- const msg = `${invalidV2Ids.map((f) => f.id).join(", ")} v2 function name(s) can only contain lower ` +
154
- `case letters, numbers, hyphens, and not exceed 62 characters in length`;
155
- throw new error_1.FirebaseError(msg);
156
- }
157
146
  }
158
147
  exports.functionIdsAreValid = functionIdsAreValid;
159
148
  async function secretsAreValid(projectId, wantBackend) {
@@ -23,9 +23,9 @@ const EMULATOR_UPDATE_DETAILS = {
23
23
  expectedChecksum: "311609538bd65666eb724ef47c2e6466",
24
24
  },
25
25
  firestore: {
26
- version: "1.16.2",
27
- expectedSize: 64601019,
28
- expectedChecksum: "83f379a5b3d367503a860497fea3a936",
26
+ version: "1.17.2",
27
+ expectedSize: 64907894,
28
+ expectedChecksum: "bc56ccf2419be3242e7b53def0a51566",
29
29
  },
30
30
  storage: {
31
31
  version: "1.1.3",
@@ -44,18 +44,13 @@ exports.ALL_EXPERIMENTS = experiments({
44
44
  "of how that image was created.",
45
45
  public: true,
46
46
  },
47
- functionsparams: {
48
- shortDescription: "Adds support for paramaterizing functions deployments",
49
- default: true,
50
- },
51
47
  emulatoruisnapshot: {
52
48
  shortDescription: "Load pre-release versions of the emulator UI",
53
49
  },
54
50
  webframeworks: {
55
51
  shortDescription: "Native support for popular web frameworks",
56
52
  fullDescription: "Adds support for popular web frameworks such as Next.js " +
57
- "Angular, React, Svelte, and Vite-compatible frameworks. Firebase is " +
58
- "committed to support these platforms long-term, but a manual migration " +
53
+ "Angular, React, Svelte, and Vite-compatible frameworks. A manual migration " +
59
54
  "may be required when the non-experimental support for these frameworks " +
60
55
  "is released",
61
56
  docsUri: "https://firebase.google.com/docs/hosting/frameworks-overview",
@@ -222,9 +222,10 @@ async function patchInstance(args) {
222
222
  });
223
223
  return pollRes;
224
224
  }
225
- function populateResourceProperties(spec) {
225
+ function populateSpec(spec) {
226
+ var _a, _b;
226
227
  if (spec) {
227
- spec.resources.forEach((r) => {
228
+ for (const r of spec.resources) {
228
229
  try {
229
230
  if (r.propertiesYaml) {
230
231
  r.properties = yaml.safeLoad(r.propertiesYaml);
@@ -233,7 +234,9 @@ function populateResourceProperties(spec) {
233
234
  catch (err) {
234
235
  logger_1.logger.debug(`[ext] failed to parse resource properties yaml: ${err}`);
235
236
  }
236
- });
237
+ }
238
+ spec.params = (_a = spec.params) !== null && _a !== void 0 ? _a : [];
239
+ spec.systemParams = (_b = spec.systemParams) !== null && _b !== void 0 ? _b : [];
237
240
  }
238
241
  }
239
242
  async function createSource(projectId, packageUri, extensionRoot) {
@@ -248,7 +251,7 @@ async function createSource(projectId, packageUri, extensionRoot) {
248
251
  masterTimeout: 600000,
249
252
  });
250
253
  if (pollRes.spec) {
251
- populateResourceProperties(pollRes.spec);
254
+ populateSpec(pollRes.spec);
252
255
  }
253
256
  return pollRes;
254
257
  }
@@ -256,7 +259,7 @@ exports.createSource = createSource;
256
259
  async function getSource(sourceName) {
257
260
  const res = await apiClient.get(`/${sourceName}`);
258
261
  if (res.body.spec) {
259
- populateResourceProperties(res.body.spec);
262
+ populateSpec(res.body.spec);
260
263
  }
261
264
  return res.body;
262
265
  }
@@ -269,7 +272,7 @@ async function getExtensionVersion(extensionVersionRef) {
269
272
  try {
270
273
  const res = await apiClient.get(`/${refs.toExtensionVersionName(ref)}`);
271
274
  if (res.body.spec) {
272
- populateResourceProperties(res.body.spec);
275
+ populateSpec(res.body.spec);
273
276
  }
274
277
  return res.body;
275
278
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.publishExtensionVersionFromRemoteRepo = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
3
+ exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.publishExtensionVersionFromRemoteRepo = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
4
4
  const clc = require("colorette");
5
5
  const ora = require("ora");
6
6
  const semver = require("semver");
@@ -388,23 +388,8 @@ async function publishExtensionVersionFromRemoteRepo(args) {
388
388
  throw new error_1.FirebaseError("Repo URI is required but not currently set.");
389
389
  }
390
390
  }
391
- if ((extension === null || extension === void 0 ? void 0 : extension.repoUri) && extension.repoUri !== repoUri) {
392
- throw new error_1.FirebaseError(`Repo URI '${clc.bold(args.repoUri)}' does not match repo URI '${clc.bold(extension.repoUri)}' already associated with Extension ${clc.bold(extensionRef)}. Repo URI cannot be changed.`);
393
- }
394
- if (!(extension === null || extension === void 0 ? void 0 : extension.repoUri)) {
395
- logger_1.logger.info(`\n${clc.red("Warning:")} You are about to associate repo URI ${clc.bold(repoUri)} with Extension ${clc.bold(extensionRef)}. This cannot be changed. All future verifiable versions must be published from this repo. ` +
396
- `You can continue publishing unverifiable versions from local source.`);
397
- const confirmed = await confirm({
398
- nonInteractive: args.nonInteractive,
399
- force: args.force,
400
- default: false,
401
- });
402
- if (!confirmed) {
403
- return;
404
- }
405
- }
406
- else {
407
- logger_1.logger.info(`Extension ${clc.bold(extensionRef)} is published from ${clc.bold(extension === null || extension === void 0 ? void 0 : extension.repoUri)}.`);
391
+ if (extension === null || extension === void 0 ? void 0 : extension.repoUri) {
392
+ logger_1.logger.info(`Extension ${clc.bold(extensionRef)} is published from ${clc.bold(extension === null || extension === void 0 ? void 0 : extension.repoUri)}. Use --repo to change this repo.`);
408
393
  }
409
394
  let extensionRoot = args.extensionRoot;
410
395
  let defaultRoot = "/";
@@ -487,7 +472,7 @@ async function publishExtensionVersionFromRemoteRepo(args) {
487
472
  });
488
473
  const sourceUri = path.join(repoUri, "tree", sourceRef, extensionRoot);
489
474
  displayReleaseNotes(extensionRef, extensionSpec.version, notes, sourceUri);
490
- const confirmed = await confirm({
475
+ const confirmed = await (0, prompt_1.confirm)({
491
476
  nonInteractive: args.nonInteractive,
492
477
  force: args.force,
493
478
  default: false,
@@ -535,7 +520,7 @@ async function publishExtensionVersionFromLocalSource(args) {
535
520
  });
536
521
  const extensionRef = `${args.publisherId}/${args.extensionId}`;
537
522
  displayReleaseNotes(extensionRef, extensionSpec.version, notes);
538
- const confirmed = await confirm({
523
+ const confirmed = await (0, prompt_1.confirm)({
539
524
  nonInteractive: args.nonInteractive,
540
525
  force: args.force,
541
526
  default: false,
@@ -725,23 +710,6 @@ function getSourceOrigin(sourceOrVersion) {
725
710
  throw new error_1.FirebaseError(`Could not find source '${clc.bold(sourceOrVersion)}'. Check to make sure the source is correct, and then please try again.`);
726
711
  }
727
712
  exports.getSourceOrigin = getSourceOrigin;
728
- async function confirm(args) {
729
- if (!args.nonInteractive && !args.force) {
730
- const message = `Do you wish to continue?`;
731
- return await (0, prompt_1.promptOnce)({
732
- type: "confirm",
733
- message,
734
- default: args.default,
735
- });
736
- }
737
- else if (args.nonInteractive && !args.force) {
738
- throw new error_1.FirebaseError("Pass the --force flag to use this command in non-interactive mode");
739
- }
740
- else {
741
- return true;
742
- }
743
- }
744
- exports.confirm = confirm;
745
713
  async function diagnoseAndFixProject(options) {
746
714
  const projectId = (0, projectUtils_1.getProjectId)(options);
747
715
  if (!projectId) {
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.showPostDeprecationNotice = exports.readInstanceParam = exports.getInstanceRef = exports.getInstanceTarget = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
3
+ exports.showPostDeprecationNotice = exports.readInstanceParam = exports.writeExtensionsToFirebaseJson = exports.getInstanceRef = exports.getInstanceTarget = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeEmptyManifest = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
4
4
  const clc = require("colorette");
5
5
  const path = require("path");
6
+ const fs = require("fs-extra");
6
7
  const refs = require("./refs");
7
8
  const config_1 = require("../config");
8
9
  const planner_1 = require("../deploy/extensions/planner");
@@ -41,6 +42,26 @@ async function writeToManifest(specs, config, options, allowOverwrite = false) {
41
42
  await writeLocalSecrets(specs, config, options.force);
42
43
  }
43
44
  exports.writeToManifest = writeToManifest;
45
+ async function writeEmptyManifest(config, options) {
46
+ if (!fs.existsSync(config.path("extensions"))) {
47
+ fs.mkdirSync(config.path("extensions"));
48
+ }
49
+ if (config.has("extensions") && Object.keys(config.get("extensions")).length) {
50
+ const currentExtensions = Object.entries(config.get("extensions"))
51
+ .map((i) => `${i[0]}: ${i[1]}`)
52
+ .join("\n\t");
53
+ if (!(await (0, prompt_1.confirm)({
54
+ message: `firebase.json already contains extensions:\n${currentExtensions}\nWould you like to overwrite them?`,
55
+ nonInteractive: options.nonInteractive,
56
+ force: options.force,
57
+ default: false,
58
+ }))) {
59
+ return;
60
+ }
61
+ }
62
+ config.set("extensions", {});
63
+ }
64
+ exports.writeEmptyManifest = writeEmptyManifest;
44
65
  async function writeLocalSecrets(specs, config, force) {
45
66
  for (const spec of specs) {
46
67
  const extensionSpec = await (0, planner_1.getExtensionSpec)(spec);
@@ -133,6 +154,7 @@ function writeExtensionsToFirebaseJson(specs, config) {
133
154
  config.writeProjectFile("firebase.json", config.src);
134
155
  utils.logSuccess("Wrote extensions to " + clc.bold("firebase.json") + "...");
135
156
  }
157
+ exports.writeExtensionsToFirebaseJson = writeExtensionsToFirebaseJson;
136
158
  async function writeEnvFiles(specs, config, force) {
137
159
  for (const spec of specs) {
138
160
  const content = Object.entries(spec.params)
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compareFieldOverride = exports.compareApiField = exports.compareApiIndex = exports.compareSpecIndex = void 0;
4
- const API = require("./indexes-api");
3
+ exports.compareFieldOverride = exports.compareApiField = exports.compareLocation = exports.compareApiDatabase = exports.compareApiIndex = exports.compareSpecIndex = void 0;
4
+ const API = require("./api-types");
5
5
  const util = require("./util");
6
6
  const QUERY_SCOPE_SEQUENCE = [
7
7
  API.QueryScope.COLLECTION_GROUP,
@@ -34,6 +34,14 @@ function compareApiIndex(a, b) {
34
34
  return compareArrays(a.fields, b.fields, compareIndexField);
35
35
  }
36
36
  exports.compareApiIndex = compareApiIndex;
37
+ function compareApiDatabase(a, b) {
38
+ return a.name > b.name ? 1 : -1;
39
+ }
40
+ exports.compareApiDatabase = compareApiDatabase;
41
+ function compareLocation(a, b) {
42
+ return a.locationId > b.locationId ? 1 : -1;
43
+ }
44
+ exports.compareLocation = compareLocation;
37
45
  function compareApiField(a, b) {
38
46
  const aName = util.parseFieldName(a.name);
39
47
  const bName = util.parseFieldName(b.name);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
3
+ exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
4
4
  var Mode;
5
5
  (function (Mode) {
6
6
  Mode["ASCENDING"] = "ASCENDING";
@@ -33,3 +33,18 @@ var StateTtl;
33
33
  StateTtl["ACTIVE"] = "ACTIVE";
34
34
  StateTtl["NEEDS_REPAIR"] = "NEEDS_REPAIR";
35
35
  })(StateTtl = exports.StateTtl || (exports.StateTtl = {}));
36
+ var DatabaseType;
37
+ (function (DatabaseType) {
38
+ DatabaseType["DATASTORE_MODE"] = "DATASTORE_MODE";
39
+ DatabaseType["FIRESTORE_NATIVE"] = "FIRESTORE_NATIVE";
40
+ })(DatabaseType = exports.DatabaseType || (exports.DatabaseType = {}));
41
+ var DatabaseDeleteProtectionStateOption;
42
+ (function (DatabaseDeleteProtectionStateOption) {
43
+ DatabaseDeleteProtectionStateOption["ENABLED"] = "ENABLED";
44
+ DatabaseDeleteProtectionStateOption["DISABLED"] = "DISABLED";
45
+ })(DatabaseDeleteProtectionStateOption = exports.DatabaseDeleteProtectionStateOption || (exports.DatabaseDeleteProtectionStateOption = {}));
46
+ var DatabaseDeleteProtectionState;
47
+ (function (DatabaseDeleteProtectionState) {
48
+ DatabaseDeleteProtectionState["ENABLED"] = "DELETE_PROTECTION_ENABLED";
49
+ DatabaseDeleteProtectionState["DISABLED"] = "DELETE_PROTECTION_DISABLED";
50
+ })(DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionState || (exports.DatabaseDeleteProtectionState = {}));
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FirestoreIndexes = void 0;
3
+ exports.FirestoreApi = void 0;
4
4
  const clc = require("colorette");
5
5
  const logger_1 = require("../logger");
6
6
  const utils = require("../utils");
7
7
  const validator = require("./validator");
8
- const API = require("./indexes-api");
9
- const sort = require("./indexes-sort");
8
+ const types = require("./api-types");
9
+ const sort = require("./api-sort");
10
10
  const util = require("./util");
11
11
  const prompt_1 = require("../prompt");
12
12
  const api_1 = require("../api");
13
+ const error_1 = require("../error");
13
14
  const apiv2_1 = require("../apiv2");
14
- class FirestoreIndexes {
15
+ class FirestoreApi {
15
16
  constructor() {
16
17
  this.apiClient = new apiv2_1.Client({ urlPrefix: api_1.firestoreOrigin, apiVersion: "v1" });
17
18
  }
@@ -193,6 +194,44 @@ class FirestoreIndexes {
193
194
  logger_1.logger.info(this.prettyIndexString(index));
194
195
  });
195
196
  }
197
+ prettyPrintDatabases(databases) {
198
+ if (databases.length === 0) {
199
+ logger_1.logger.info("No databases found.");
200
+ return;
201
+ }
202
+ const sortedDatabases = databases.sort(sort.compareApiDatabase);
203
+ const Table = require("cli-table");
204
+ const table = new Table({
205
+ head: ["Database Name"],
206
+ colWidths: [Math.max(...sortedDatabases.map((database) => database.name.length + 5), 20)],
207
+ });
208
+ table.push(...sortedDatabases.map((database) => [this.prettyDatabaseString(database)]));
209
+ logger_1.logger.info(table.toString());
210
+ }
211
+ prettyPrintDatabase(database) {
212
+ const Table = require("cli-table");
213
+ const table = new Table({
214
+ head: ["Field", "Value"],
215
+ colWidths: [25, Math.max(50, 5 + database.name.length)],
216
+ });
217
+ table.push(["Name", clc.yellow(database.name)], ["Create Time", clc.yellow(database.createTime)], ["Last Update Time", clc.yellow(database.updateTime)], ["Type", clc.yellow(database.type)], ["Location", clc.yellow(database.locationId)], ["Delete Protection State", clc.yellow(database.deleteProtectionState)]);
218
+ logger_1.logger.info(table.toString());
219
+ }
220
+ prettyPrintLocations(locations) {
221
+ if (locations.length === 0) {
222
+ logger_1.logger.info("No Locations Available");
223
+ return;
224
+ }
225
+ const Table = require("cli-table");
226
+ const table = new Table({
227
+ head: ["Display Name", "LocationId"],
228
+ colWidths: [20, 30],
229
+ });
230
+ table.push(...locations
231
+ .sort(sort.compareLocation)
232
+ .map((location) => [location.displayName, location.locationId]));
233
+ logger_1.logger.info(table.toString());
234
+ }
196
235
  printFieldOverrides(fields) {
197
236
  if (fields.length === 0) {
198
237
  logger_1.logger.info("None");
@@ -217,16 +256,16 @@ class FirestoreIndexes {
217
256
  validateIndex(index) {
218
257
  validator.assertHas(index, "collectionGroup");
219
258
  validator.assertHas(index, "queryScope");
220
- validator.assertEnum(index, "queryScope", Object.keys(API.QueryScope));
259
+ validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
221
260
  validator.assertHas(index, "fields");
222
261
  index.fields.forEach((field) => {
223
262
  validator.assertHas(field, "fieldPath");
224
263
  validator.assertHasOneOf(field, ["order", "arrayConfig"]);
225
264
  if (field.order) {
226
- validator.assertEnum(field, "order", Object.keys(API.Order));
265
+ validator.assertEnum(field, "order", Object.keys(types.Order));
227
266
  }
228
267
  if (field.arrayConfig) {
229
- validator.assertEnum(field, "arrayConfig", Object.keys(API.ArrayConfig));
268
+ validator.assertEnum(field, "arrayConfig", Object.keys(types.ArrayConfig));
230
269
  }
231
270
  });
232
271
  }
@@ -240,13 +279,13 @@ class FirestoreIndexes {
240
279
  field.indexes.forEach((index) => {
241
280
  validator.assertHasOneOf(index, ["arrayConfig", "order"]);
242
281
  if (index.arrayConfig) {
243
- validator.assertEnum(index, "arrayConfig", Object.keys(API.ArrayConfig));
282
+ validator.assertEnum(index, "arrayConfig", Object.keys(types.ArrayConfig));
244
283
  }
245
284
  if (index.order) {
246
- validator.assertEnum(index, "order", Object.keys(API.Order));
285
+ validator.assertEnum(index, "order", Object.keys(types.Order));
247
286
  }
248
287
  if (index.queryScope) {
249
- validator.assertEnum(index, "queryScope", Object.keys(API.QueryScope));
288
+ validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
250
289
  }
251
290
  });
252
291
  }
@@ -376,7 +415,7 @@ class FirestoreIndexes {
376
415
  result.indexes = spec.indexes.map((index) => {
377
416
  const i = {
378
417
  collectionGroup: index.collectionGroup || index.collectionId,
379
- queryScope: index.queryScope || API.QueryScope.COLLECTION,
418
+ queryScope: index.queryScope || types.QueryScope.COLLECTION,
380
419
  fields: [],
381
420
  };
382
421
  if (index.fields) {
@@ -390,8 +429,8 @@ class FirestoreIndexes {
390
429
  else if (field.arrayConfig) {
391
430
  f.arrayConfig = field.arrayConfig;
392
431
  }
393
- else if (field.mode === API.Mode.ARRAY_CONTAINS) {
394
- f.arrayConfig = API.ArrayConfig.CONTAINS;
432
+ else if (field.mode === types.Mode.ARRAY_CONTAINS) {
433
+ f.arrayConfig = types.ArrayConfig.CONTAINS;
395
434
  }
396
435
  else {
397
436
  f.order = field.mode;
@@ -403,14 +442,78 @@ class FirestoreIndexes {
403
442
  });
404
443
  return result;
405
444
  }
445
+ async listDatabases(project) {
446
+ const url = `/projects/${project}/databases`;
447
+ const res = await this.apiClient.get(url);
448
+ const databases = res.body.databases;
449
+ if (!databases) {
450
+ return [];
451
+ }
452
+ return databases;
453
+ }
454
+ async locations(project) {
455
+ const url = `/projects/${project}/locations`;
456
+ const res = await this.apiClient.get(url);
457
+ const locations = res.body.locations;
458
+ if (!locations) {
459
+ return [];
460
+ }
461
+ return locations;
462
+ }
463
+ async getDatabase(project, databaseId) {
464
+ const url = `/projects/${project}/databases/${databaseId}`;
465
+ const res = await this.apiClient.get(url);
466
+ const database = res.body;
467
+ if (!database) {
468
+ throw new error_1.FirebaseError("Not found");
469
+ }
470
+ return database;
471
+ }
472
+ async createDatabase(project, databaseId, locationId, type, deleteProtectionState) {
473
+ const url = `/projects/${project}/databases`;
474
+ const payload = {
475
+ type,
476
+ locationId,
477
+ deleteProtectionState,
478
+ };
479
+ const options = { queryParams: { databaseId: databaseId } };
480
+ const res = await this.apiClient.post(url, payload, options);
481
+ const database = res.body.response;
482
+ if (!database) {
483
+ throw new error_1.FirebaseError("Not found");
484
+ }
485
+ return database;
486
+ }
487
+ async updateDatabase(project, databaseId, type, deleteProtectionState) {
488
+ const url = `/projects/${project}/databases/${databaseId}`;
489
+ const payload = {
490
+ type,
491
+ deleteProtectionState,
492
+ };
493
+ const res = await this.apiClient.patch(url, payload);
494
+ const database = res.body.response;
495
+ if (!database) {
496
+ throw new error_1.FirebaseError("Not found");
497
+ }
498
+ return database;
499
+ }
500
+ async deleteDatabase(project, databaseId) {
501
+ const url = `/projects/${project}/databases/${databaseId}`;
502
+ const res = await this.apiClient.delete(url);
503
+ const database = res.body.response;
504
+ if (!database) {
505
+ throw new error_1.FirebaseError("Not found");
506
+ }
507
+ return database;
508
+ }
406
509
  prettyIndexString(index, includeState = true) {
407
510
  let result = "";
408
511
  if (index.state && includeState) {
409
512
  const stateMsg = `[${index.state}] `;
410
- if (index.state === API.State.READY) {
513
+ if (index.state === types.State.READY) {
411
514
  result += clc.green(stateMsg);
412
515
  }
413
- else if (index.state === API.State.CREATING) {
516
+ else if (index.state === types.State.CREATING) {
414
517
  result += clc.yellow(stateMsg);
415
518
  }
416
519
  else {
@@ -429,6 +532,9 @@ class FirestoreIndexes {
429
532
  });
430
533
  return result;
431
534
  }
535
+ prettyDatabaseString(database) {
536
+ return clc.yellow(database.name);
537
+ }
432
538
  prettyFieldString(field) {
433
539
  let result = "";
434
540
  const parsedName = util.parseFieldName(field.name);
@@ -456,4 +562,4 @@ class FirestoreIndexes {
456
562
  return result;
457
563
  }
458
564
  }
459
- exports.FirestoreIndexes = FirestoreIndexes;
565
+ exports.FirestoreApi = FirestoreApi;
@@ -52,8 +52,15 @@ const DEFAULT_FIND_DEP_OPTIONS = {
52
52
  };
53
53
  exports.WebFrameworks = Object.fromEntries((0, fs_1.readdirSync)(__dirname)
54
54
  .filter((path) => (0, fs_1.statSync)((0, path_1.join)(__dirname, path)).isDirectory())
55
- .map((path) => [path, require((0, path_1.join)(__dirname, path))])
56
- .filter(([, obj]) => obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support));
55
+ .map((path) => {
56
+ try {
57
+ return [path, require((0, path_1.join)(__dirname, path))];
58
+ }
59
+ catch (e) {
60
+ return [];
61
+ }
62
+ })
63
+ .filter(([, obj]) => obj && obj.name && obj.discover && obj.build && obj.type !== undefined && obj.support));
57
64
  function relativeRequire(dir, mod) {
58
65
  try {
59
66
  const path = require.resolve(mod, { paths: [dir] });
@@ -113,11 +120,11 @@ function scanDependencyTree(searchingFor, dependencies = {}) {
113
120
  function getNodeModuleBin(name, cwd) {
114
121
  var _a;
115
122
  const cantFindExecutable = new error_1.FirebaseError(`Could not find the ${name} executable.`);
116
- const npmBin = (_a = (0, cross_spawn_1.sync)("npm", ["bin"], { cwd }).stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
117
- if (!npmBin) {
123
+ const npmRoot = (_a = (0, cross_spawn_1.sync)("npm", ["root"], { cwd }).stdout) === null || _a === void 0 ? void 0 : _a.toString().trim();
124
+ if (!npmRoot) {
118
125
  throw cantFindExecutable;
119
126
  }
120
- const path = (0, path_1.join)(npmBin, name);
127
+ const path = (0, path_1.join)(npmRoot, ".bin", name);
121
128
  if (!(0, fsutils_1.fileExistsSync)(path)) {
122
129
  throw cantFindExecutable;
123
130
  }
@@ -12,13 +12,13 @@ const env_1 = require("./env");
12
12
  const logger_1 = require("../logger");
13
13
  const api_1 = require("../api");
14
14
  const functional_1 = require("../functional");
15
- const FIREBASE_MANGED = "firebase-managed";
15
+ const FIREBASE_MANAGED = "firebase-managed";
16
16
  function isFirebaseManaged(secret) {
17
- return Object.keys(secret.labels || []).includes(FIREBASE_MANGED);
17
+ return Object.keys(secret.labels || []).includes(FIREBASE_MANAGED);
18
18
  }
19
19
  exports.isFirebaseManaged = isFirebaseManaged;
20
20
  function labels() {
21
- return { [FIREBASE_MANGED]: "true" };
21
+ return { [FIREBASE_MANAGED]: "true" };
22
22
  }
23
23
  exports.labels = labels;
24
24
  function toUpperSnakeCase(key) {
@@ -107,7 +107,7 @@ async function pruneSecrets(projectInfo, endpoints) {
107
107
  const { projectId, projectNumber } = projectInfo;
108
108
  const pruneKey = (name, version) => `${name}@${version}`;
109
109
  const prunedSecrets = new Set();
110
- const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANGED}=true`);
110
+ const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANAGED}=true`);
111
111
  for (const secret of haveSecrets) {
112
112
  const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `NOT state: DESTROYED`);
113
113
  for (const version of versions) {
@@ -12,7 +12,7 @@ const client = new apiv2_1.Client({
12
12
  apiVersion: exports.API_VERSION,
13
13
  });
14
14
  async function getChannel(name) {
15
- const res = await client.get(name);
15
+ const res = await client.get(name, { resolveOnHTTPError: true });
16
16
  if (res.status === 404) {
17
17
  return undefined;
18
18
  }
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doSetup = void 0;
4
+ const requirePermissions_1 = require("../../../requirePermissions");
5
+ const ensureApiEnabled_1 = require("../../../ensureApiEnabled");
6
+ const manifest = require("../../../extensions/manifest");
7
+ async function doSetup(setup, config, options) {
8
+ var _a, _b;
9
+ const projectId = (_b = (_a = setup === null || setup === void 0 ? void 0 : setup.rcfile) === null || _a === void 0 ? void 0 : _a.projects) === null || _b === void 0 ? void 0 : _b.default;
10
+ if (projectId) {
11
+ await (0, requirePermissions_1.requirePermissions)(Object.assign(Object.assign({}, options), { project: projectId }));
12
+ await Promise.all([(0, ensureApiEnabled_1.ensure)(projectId, "firebaseextensions.googleapis.com", "unused", true)]);
13
+ }
14
+ return manifest.writeEmptyManifest(config, options);
15
+ }
16
+ exports.doSetup = doSetup;
@@ -4,11 +4,11 @@ exports.initIndexes = void 0;
4
4
  const clc = require("colorette");
5
5
  const fs = require("fs");
6
6
  const error_1 = require("../../../error");
7
- const iv2 = require("../../../firestore/indexes");
7
+ const api = require("../../../firestore/api");
8
8
  const fsutils = require("../../../fsutils");
9
9
  const prompt_1 = require("../../../prompt");
10
10
  const logger_1 = require("../../../logger");
11
- const indexes = new iv2.FirestoreIndexes();
11
+ const indexes = new api.FirestoreApi();
12
12
  const INDEXES_TEMPLATE = fs.readFileSync(__dirname + "/../../../../templates/init/firestore/firestore.indexes.json", "utf8");
13
13
  function initIndexes(setup, config) {
14
14
  logger_1.logger.info();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hostingGithub = exports.remoteconfig = exports.project = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = exports.account = void 0;
3
+ exports.hostingGithub = exports.remoteconfig = exports.project = exports.extensions = exports.emulators = exports.storage = exports.hosting = exports.functions = exports.firestore = exports.database = 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");
@@ -15,6 +15,8 @@ var storage_1 = require("./storage");
15
15
  Object.defineProperty(exports, "storage", { enumerable: true, get: function () { return storage_1.doSetup; } });
16
16
  var emulators_1 = require("./emulators");
17
17
  Object.defineProperty(exports, "emulators", { enumerable: true, get: function () { return emulators_1.doSetup; } });
18
+ var extensions_1 = require("./extensions");
19
+ Object.defineProperty(exports, "extensions", { enumerable: true, get: function () { return extensions_1.doSetup; } });
18
20
  var project_1 = require("./project");
19
21
  Object.defineProperty(exports, "project", { enumerable: true, get: function () { return project_1.doSetup; } });
20
22
  var remoteconfig_1 = require("./remoteconfig");
package/lib/init/index.js CHANGED
@@ -14,6 +14,7 @@ const featureFns = new Map([
14
14
  ["hosting", features.hosting],
15
15
  ["storage", features.storage],
16
16
  ["emulators", features.emulators],
17
+ ["extensions", features.extensions],
17
18
  ["project", features.project],
18
19
  ["remoteconfig", features.remoteconfig],
19
20
  ["hosting:github", features.hostingGithub],
package/lib/prompt.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.promptOnce = exports.prompt = void 0;
3
+ exports.confirm = exports.promptOnce = exports.prompt = void 0;
4
4
  const inquirer = require("inquirer");
5
5
  const error_1 = require("./error");
6
6
  async function prompt(options, questions) {
@@ -30,3 +30,21 @@ async function promptOnce(question, options = {}) {
30
30
  return options[question.name];
31
31
  }
32
32
  exports.promptOnce = promptOnce;
33
+ async function confirm(args) {
34
+ var _a;
35
+ if (!args.nonInteractive && !args.force) {
36
+ const message = (_a = args.message) !== null && _a !== void 0 ? _a : `Do you wish to continue?`;
37
+ return await promptOnce({
38
+ type: "confirm",
39
+ message,
40
+ default: args.default,
41
+ });
42
+ }
43
+ else if (args.nonInteractive && !args.force) {
44
+ throw new error_1.FirebaseError("Pass the --force flag to use this command in non-interactive mode");
45
+ }
46
+ else {
47
+ return true;
48
+ }
49
+ }
50
+ exports.confirm = confirm;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "11.27.0",
3
+ "version": "11.29.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "firebase-tools",
9
- "version": "11.27.0",
9
+ "version": "11.29.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@google-cloud/pubsub": "^3.0.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "11.27.0",
3
+ "version": "11.29.0",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
File without changes