firebase-tools 14.12.0 → 14.13.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 (80) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/dataconnect-services-list.js +5 -5
  3. package/lib/commands/dataconnect-sql-grant.js +5 -0
  4. package/lib/commands/dataconnect-sql-setup.js +1 -3
  5. package/lib/commands/firestore-databases-create.js +11 -0
  6. package/lib/crashlytics/buildToolsJarHelper.js +1 -2
  7. package/lib/crashlytics/getIssueDetails.js +41 -0
  8. package/lib/crashlytics/getSampleCrash.js +48 -0
  9. package/lib/dataconnect/client.js +23 -15
  10. package/lib/dataconnect/ensureApis.js +5 -9
  11. package/lib/dataconnect/fileUtils.js +5 -6
  12. package/lib/dataconnect/freeTrial.js +16 -39
  13. package/lib/dataconnect/provisionCloudSql.js +67 -70
  14. package/lib/dataconnect/schemaMigration.js +75 -47
  15. package/lib/deploy/dataconnect/deploy.js +9 -11
  16. package/lib/deploy/dataconnect/prepare.js +9 -12
  17. package/lib/deploy/dataconnect/release.js +13 -7
  18. package/lib/deploy/firestore/deploy.js +10 -0
  19. package/lib/deploy/functions/backend.js +8 -2
  20. package/lib/deploy/functions/build.js +23 -1
  21. package/lib/deploy/functions/ensure.js +1 -1
  22. package/lib/deploy/functions/functionsDeployHelper.js +8 -1
  23. package/lib/deploy/functions/prepare.js +6 -4
  24. package/lib/deploy/functions/prepareFunctionsUpload.js +3 -1
  25. package/lib/deploy/functions/pricing.js +12 -5
  26. package/lib/deploy/functions/release/fabricator.js +25 -3
  27. package/lib/emulator/controller.js +2 -1
  28. package/lib/emulator/downloadableEmulatorInfo.json +18 -18
  29. package/lib/emulator/functionsEmulator.js +9 -1
  30. package/lib/experiments.js +4 -0
  31. package/lib/extensions/extensionsHelper.js +4 -15
  32. package/lib/extensions/utils.js +1 -12
  33. package/lib/firestore/api-sort.js +96 -3
  34. package/lib/firestore/api-types.js +14 -1
  35. package/lib/firestore/api.js +85 -4
  36. package/lib/firestore/pretty-print.js +7 -0
  37. package/lib/firestore/validator.js +1 -1
  38. package/lib/functional.js +7 -1
  39. package/lib/functions/deprecationWarnings.js +4 -4
  40. package/lib/functions/projectConfig.js +25 -2
  41. package/lib/functions/secrets.js +3 -0
  42. package/lib/gcp/cloudfunctionsv2.js +3 -31
  43. package/lib/gcp/cloudscheduler.js +1 -1
  44. package/lib/gcp/cloudsql/cloudsqladmin.js +2 -14
  45. package/lib/gcp/cloudsql/connect.js +2 -2
  46. package/lib/gcp/cloudsql/permissionsSetup.js +13 -15
  47. package/lib/gcp/k8s.js +32 -0
  48. package/lib/gcp/runv2.js +178 -0
  49. package/lib/gemini/fdcExperience.js +5 -3
  50. package/lib/init/features/dataconnect/index.js +266 -162
  51. package/lib/init/features/dataconnect/sdk.js +32 -17
  52. package/lib/init/features/project.js +4 -0
  53. package/lib/management/studio.js +1 -1
  54. package/lib/mcp/index.js +75 -2
  55. package/lib/mcp/prompt.js +10 -0
  56. package/lib/mcp/prompts/core/deploy.js +58 -0
  57. package/lib/mcp/prompts/core/index.js +5 -0
  58. package/lib/mcp/prompts/index.js +45 -0
  59. package/lib/mcp/tools/core/get_sdk_config.js +10 -0
  60. package/lib/mcp/tools/core/init.js +7 -6
  61. package/lib/mcp/tools/crashlytics/get_issue_details.js +33 -0
  62. package/lib/mcp/tools/crashlytics/get_sample_crash.js +43 -0
  63. package/lib/mcp/tools/crashlytics/index.js +7 -1
  64. package/lib/mcp/tools/crashlytics/list_top_issues.js +2 -1
  65. package/lib/mcp/tools/database/get_data.js +49 -0
  66. package/lib/mcp/tools/database/get_rules.js +39 -0
  67. package/lib/mcp/tools/database/index.js +8 -0
  68. package/lib/mcp/tools/database/set_data.js +57 -0
  69. package/lib/mcp/tools/database/set_rules.js +41 -0
  70. package/lib/mcp/tools/database/validate_rules.js +41 -0
  71. package/lib/mcp/tools/index.js +4 -1
  72. package/lib/mcp/tools/rules/get_rules.js +1 -1
  73. package/lib/mcp/types.js +2 -0
  74. package/lib/mcp/util.js +2 -0
  75. package/lib/rtdb.js +10 -6
  76. package/lib/utils.js +24 -1
  77. package/package.json +1 -1
  78. package/schema/firebase-config.json +6 -0
  79. package/templates/init/firestore/firestore.indexes.json +26 -1
  80. package/lib/extensions/resolveSource.js +0 -24
@@ -136,6 +136,11 @@ class Fabricator {
136
136
  else if (endpoint.platform === "gcfv2") {
137
137
  await this.createV2Function(endpoint, scraperV2);
138
138
  }
139
+ else if (endpoint.platform === "run") {
140
+ throw new error_1.FirebaseError("Creating new Cloud Run functions is not supported yet.", {
141
+ exit: 1,
142
+ });
143
+ }
139
144
  else {
140
145
  (0, functional_1.assertExhaustive)(endpoint.platform);
141
146
  }
@@ -154,6 +159,9 @@ class Fabricator {
154
159
  else if (update.endpoint.platform === "gcfv2") {
155
160
  await this.updateV2Function(update.endpoint, scraperV2);
156
161
  }
162
+ else if (update.endpoint.platform === "run") {
163
+ throw new error_1.FirebaseError("Updating Cloud Run functions is not supported yet.", { exit: 1 });
164
+ }
157
165
  else {
158
166
  (0, functional_1.assertExhaustive)(update.endpoint.platform);
159
167
  }
@@ -162,11 +170,15 @@ class Fabricator {
162
170
  async deleteEndpoint(endpoint) {
163
171
  await this.deleteTrigger(endpoint);
164
172
  if (endpoint.platform === "gcfv1") {
165
- await this.deleteV1Function(endpoint);
173
+ return this.deleteV1Function(endpoint);
166
174
  }
167
- else {
168
- await this.deleteV2Function(endpoint);
175
+ else if (endpoint.platform === "gcfv2") {
176
+ return this.deleteV2Function(endpoint);
169
177
  }
178
+ else if (endpoint.platform === "run") {
179
+ throw new error_1.FirebaseError("Deleting Cloud Run functions is not supported yet.", { exit: 1 });
180
+ }
181
+ (0, functional_1.assertExhaustive)(endpoint.platform);
170
182
  }
171
183
  async createV1Function(endpoint, scraper) {
172
184
  var _a, _b;
@@ -471,6 +483,11 @@ class Fabricator {
471
483
  .catch(rethrowAs(endpoint, "set concurrency"));
472
484
  }
473
485
  async setTrigger(endpoint) {
486
+ if (endpoint.platform === "run") {
487
+ throw new error_1.FirebaseError("Setting triggers for Cloud Run functions is not supported yet.", {
488
+ exit: 1,
489
+ });
490
+ }
474
491
  if (backend.isScheduleTriggered(endpoint)) {
475
492
  if (endpoint.platform === "gcfv1") {
476
493
  await this.upsertScheduleV1(endpoint);
@@ -490,6 +507,11 @@ class Fabricator {
490
507
  }
491
508
  }
492
509
  async deleteTrigger(endpoint) {
510
+ if (endpoint.platform === "run") {
511
+ throw new error_1.FirebaseError("Deleting triggers for Cloud Run functions is not supported yet.", {
512
+ exit: 1,
513
+ });
514
+ }
493
515
  if (backend.isScheduleTriggered(endpoint)) {
494
516
  if (endpoint.platform === "gcfv1") {
495
517
  await this.deleteScheduleV1(endpoint);
@@ -116,7 +116,7 @@ function shouldStart(options, name) {
116
116
  return true;
117
117
  }
118
118
  catch (err) {
119
- emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("WARN", "functions", `The functions emulator is configured but there is no functions source directory. Have you run ${clc.bold("firebase init functions")}?`);
119
+ emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS).logLabeled("ERROR", "functions", `Failed to start Functions emulator: ${err.message}`);
120
120
  return false;
121
121
  }
122
122
  }
@@ -348,6 +348,7 @@ async function startAll(options, showUI = true, runningTestScript = false) {
348
348
  functionsDir,
349
349
  runtime,
350
350
  codebase: cfg.codebase,
351
+ prefix: cfg.prefix,
351
352
  env: Object.assign({}, options.extDevEnv),
352
353
  secretEnv: [],
353
354
  predefinedTriggers: options.extDevTriggers,
@@ -54,28 +54,28 @@
54
54
  },
55
55
  "dataconnect": {
56
56
  "darwin": {
57
- "version": "2.11.0",
58
- "expectedSize": 29234016,
59
- "expectedChecksum": "96da48708b8210f0d3d97099d777b322",
60
- "expectedChecksumSHA256": "10fe334f2c4145e4c9d27bc442bdc92b1252164d3daac47bb7555da42b6d7050",
61
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.0",
62
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0"
57
+ "version": "2.11.2",
58
+ "expectedSize": 29447008,
59
+ "expectedChecksum": "13bc7d3bb0a0bbfe601991361e4413c2",
60
+ "expectedChecksumSHA256": "e3b029eb461f0fe6f0c825c7a71d42c7a09c2b8ee4fac10c3e187d78fe5083f6",
61
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-macos-v2.11.2",
62
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2"
63
63
  },
64
64
  "win32": {
65
- "version": "2.11.0",
66
- "expectedSize": 29719040,
67
- "expectedChecksum": "df6b221af204a4a21163bcc73367f99e",
68
- "expectedChecksumSHA256": "af17c0d873b2b8f1919652c1ad9fd4aed417cd82bc35bbc5cc4a302fc90f9a03",
69
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.0",
70
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0.exe"
65
+ "version": "2.11.2",
66
+ "expectedSize": 29934592,
67
+ "expectedChecksum": "032a0749781fc338b446d753dd543bf5",
68
+ "expectedChecksumSHA256": "2d0498b3ef94b4e777d6fc1d526279376caf3842549f39f13a8ca327393bf810",
69
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-windows-v2.11.2",
70
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2.exe"
71
71
  },
72
72
  "linux": {
73
- "version": "2.11.0",
74
- "expectedSize": 29159608,
75
- "expectedChecksum": "38d6161f7e8f06ee89e5dc4094f1a9b6",
76
- "expectedChecksumSHA256": "3391efac3570164141b5d7f3e3d48b6426ec9c8cf9eafeabee3fa31678120218",
77
- "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.0",
78
- "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.0"
73
+ "version": "2.11.2",
74
+ "expectedSize": 29368504,
75
+ "expectedChecksum": "065a7523f881952040ac678a9a1e9323",
76
+ "expectedChecksumSHA256": "41ca6561cf77107b5d1d829233844d21c46a5e4bb823c150b1b32591dc2463e0",
77
+ "remoteUrl": "https://storage.googleapis.com/firemat-preview-drop/emulator/dataconnect-emulator-linux-v2.11.2",
78
+ "downloadPathRelativeToCacheDir": "dataconnect-emulator-2.11.2"
79
79
  }
80
80
  }
81
81
  }
@@ -28,8 +28,9 @@ const utils_1 = require("../utils");
28
28
  const adminSdkConfig_1 = require("./adminSdkConfig");
29
29
  const validate_1 = require("../deploy/functions/validate");
30
30
  const secretManager_1 = require("../gcp/secretManager");
31
- const runtimes = require("../deploy/functions/runtimes");
32
31
  const backend = require("../deploy/functions/backend");
32
+ const build = require("../deploy/functions/build");
33
+ const runtimes = require("../deploy/functions/runtimes");
33
34
  const functionsEnv = require("../functions/env");
34
35
  const v1_1 = require("../functions/events/v1");
35
36
  const build_1 = require("../deploy/functions/build");
@@ -82,6 +83,7 @@ class FunctionsEmulator {
82
83
  this.blockingFunctionsConfig = {};
83
84
  this.staticBackends = [];
84
85
  this.dynamicBackends = [];
86
+ this.watchers = [];
85
87
  this.debugMode = false;
86
88
  this.staticBackends = args.emulatableBackends;
87
89
  emulatorLogger_1.EmulatorLogger.setVerbosity(this.args.verbosity ? emulatorLogger_1.Verbosity[this.args.verbosity] : emulatorLogger_1.Verbosity["DEBUG"]);
@@ -271,6 +273,7 @@ class FunctionsEmulator {
271
273
  ],
272
274
  persistent: true,
273
275
  });
276
+ this.watchers.push(watcher);
274
277
  const debouncedLoadTriggers = (0, utils_1.debounce)(() => this.loadTriggers(backend), 1000);
275
278
  watcher.on("change", (filePath) => {
276
279
  this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
@@ -292,6 +295,10 @@ class FunctionsEmulator {
292
295
  for (const pool of Object.values(this.workerPools)) {
293
296
  pool.exit();
294
297
  }
298
+ for (const watcher of this.watchers) {
299
+ await watcher.close();
300
+ }
301
+ this.watchers = [];
295
302
  if (this.destroyServer) {
296
303
  await this.destroyServer();
297
304
  }
@@ -329,6 +336,7 @@ class FunctionsEmulator {
329
336
  await this.args.extensionsEmulator.addDynamicExtensions(emulatableBackend.codebase, discoveredBuild);
330
337
  await this.loadDynamicExtensionBackends();
331
338
  }
339
+ build.applyPrefix(discoveredBuild, emulatableBackend.prefix || "");
332
340
  const resolution = await (0, build_1.resolveBackend)({
333
341
  build: discoveredBuild,
334
342
  firebaseConfig: JSON.parse(firebaseConfig),
@@ -48,6 +48,10 @@ exports.ALL_EXPERIMENTS = experiments({
48
48
  default: true,
49
49
  public: true,
50
50
  },
51
+ runfunctions: {
52
+ shortDescription: "Functions created using the V2 API target Cloud Run Functions (not production ready)",
53
+ public: false,
54
+ },
51
55
  emulatoruisnapshot: {
52
56
  shortDescription: "Load pre-release versions of the emulator UI",
53
57
  },
@@ -7,7 +7,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
7
7
  function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
8
8
  };
9
9
  Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.checkExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteSecretParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOPULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
10
+ exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.checkExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteSecretParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOPULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
11
11
  const clc = require("colorette");
12
12
  const ora = require("ora");
13
13
  const semver = require("semver");
@@ -21,10 +21,8 @@ const unzip_1 = require("./../unzip");
21
21
  marked_1.marked.use((0, marked_terminal_1.markedTerminal)());
22
22
  const api_1 = require("../api");
23
23
  const archiveDirectory_1 = require("../archiveDirectory");
24
- const utils_1 = require("./utils");
25
24
  const functionsConfig_1 = require("../functionsConfig");
26
25
  const adminSdkConfig_1 = require("../emulator/adminSdkConfig");
27
- const resolveSource_1 = require("./resolveSource");
28
26
  const error_1 = require("../error");
29
27
  const diagnose_1 = require("./diagnose");
30
28
  const askUserForParam_1 = require("./askUserForParam");
@@ -37,7 +35,7 @@ const prompt_1 = require("../prompt");
37
35
  const refs = require("./refs");
38
36
  const localHelper_1 = require("./localHelper");
39
37
  const logger_1 = require("../logger");
40
- const utils_2 = require("../utils");
38
+ const utils_1 = require("../utils");
41
39
  const change_log_1 = require("./change-log");
42
40
  const getProjectNumber_1 = require("../getProjectNumber");
43
41
  const constants_1 = require("../emulator/constants");
@@ -61,7 +59,7 @@ var SourceOrigin;
61
59
  exports.logPrefix = "extensions";
62
60
  const VALID_LICENSES = ["apache-2.0"];
63
61
  exports.URL_REGEX = /^https:/;
64
- exports.EXTENSIONS_BUCKET_NAME = (0, utils_2.envOverride)("FIREBASE_EXTENSIONS_UPLOAD_BUCKET", "firebase-ext-eap-uploads");
62
+ exports.EXTENSIONS_BUCKET_NAME = (0, utils_1.envOverride)("FIREBASE_EXTENSIONS_UPLOAD_BUCKET", "firebase-ext-eap-uploads");
65
63
  const AUTOPOPULATED_PARAM_NAMES = [
66
64
  "PROJECT_ID",
67
65
  "STORAGE_BUCKET",
@@ -104,7 +102,7 @@ async function getFirebaseProjectParams(projectId, emulatorMode = false) {
104
102
  projectNumber = await (0, getProjectNumber_1.getProjectNumber)({ projectId });
105
103
  }
106
104
  catch (err) {
107
- (0, utils_2.logLabeledError)("extensions", `Unable to look up project number for ${projectId}.\n` +
105
+ (0, utils_1.logLabeledError)("extensions", `Unable to look up project number for ${projectId}.\n` +
108
106
  " If this is a real project, ensure that you are logged in and have access to it.\n" +
109
107
  " If this is a fake project, please use a project ID starting with 'demo-' to skip production calls.\n" +
110
108
  " Continuing with a fake project number - secrets and other features that require production access may behave unexpectedly.");
@@ -810,15 +808,6 @@ function displayReleaseNotes(args) {
810
808
  logger_1.logger.info(message);
811
809
  }
812
810
  exports.displayReleaseNotes = displayReleaseNotes;
813
- async function promptForOfficialExtension(message) {
814
- const officialExts = await (0, resolveSource_1.getExtensionRegistry)(true);
815
- return await (0, prompt_1.select)({
816
- message,
817
- choices: (0, utils_1.convertOfficialExtensionsToList)(officialExts),
818
- pageSize: Object.keys(officialExts).length,
819
- });
820
- }
821
- exports.promptForOfficialExtension = promptForOfficialExtension;
822
811
  async function promptForRepeatInstance(projectName, extensionName) {
823
812
  const message = `An extension with the ID '${clc.bold(extensionName)}' already exists in the project '${clc.bold(projectName)}'. What would you like to do?`;
824
813
  return await (0, prompt_1.select)({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getResourceRuntime = exports.formatTimestamp = exports.getRandomString = exports.convertOfficialExtensionsToList = exports.convertExtensionOptionToLabeledList = void 0;
3
+ exports.getResourceRuntime = exports.formatTimestamp = exports.getRandomString = exports.convertExtensionOptionToLabeledList = void 0;
4
4
  const types_1 = require("./types");
5
5
  function convertExtensionOptionToLabeledList(options) {
6
6
  return options.map((option) => {
@@ -12,17 +12,6 @@ function convertExtensionOptionToLabeledList(options) {
12
12
  });
13
13
  }
14
14
  exports.convertExtensionOptionToLabeledList = convertExtensionOptionToLabeledList;
15
- function convertOfficialExtensionsToList(officialExts) {
16
- const l = Object.entries(officialExts).map(([key, entry]) => {
17
- return {
18
- checked: false,
19
- value: `${entry.publisher}/${key}`,
20
- };
21
- });
22
- l.sort((a, b) => a.value.localeCompare(b.value));
23
- return l;
24
- }
25
- exports.convertOfficialExtensionsToList = convertOfficialExtensionsToList;
26
15
  function getRandomString(length) {
27
16
  const SUFFIX_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789";
28
17
  let result = "";
@@ -8,6 +8,19 @@ const QUERY_SCOPE_SEQUENCE = [
8
8
  API.QueryScope.COLLECTION,
9
9
  undefined,
10
10
  ];
11
+ const API_SCOPE_SEQUENCE = [
12
+ API.ApiScope.ANY_API,
13
+ API.ApiScope.DATASTORE_MODE_API,
14
+ API.ApiScope.MONGODB_COMPATIBLE_API,
15
+ undefined,
16
+ ];
17
+ const DENSITY_SEQUENCE = [
18
+ API.Density.DENSITY_UNSPECIFIED,
19
+ API.Density.SPARSE_ALL,
20
+ API.Density.SPARSE_ANY,
21
+ API.Density.DENSE,
22
+ undefined,
23
+ ];
11
24
  const ORDER_SEQUENCE = [API.Order.ASCENDING, API.Order.DESCENDING, undefined];
12
25
  const ARRAY_CONFIG_SEQUENCE = [API.ArrayConfig.CONTAINS, undefined];
13
26
  function compareSpecIndex(a, b) {
@@ -17,7 +30,23 @@ function compareSpecIndex(a, b) {
17
30
  if (a.queryScope !== b.queryScope) {
18
31
  return compareQueryScope(a.queryScope, b.queryScope);
19
32
  }
20
- return compareArrays(a.fields, b.fields, compareIndexField);
33
+ let cmp = compareArrays(a.fields, b.fields, compareIndexField);
34
+ if (cmp !== 0) {
35
+ return cmp;
36
+ }
37
+ cmp = compareApiScope(a.apiScope, b.apiScope);
38
+ if (cmp !== 0) {
39
+ return cmp;
40
+ }
41
+ cmp = compareDensity(a.density, b.density);
42
+ if (cmp !== 0) {
43
+ return cmp;
44
+ }
45
+ cmp = compareBoolean(a.multikey, b.multikey);
46
+ if (cmp !== 0) {
47
+ return cmp;
48
+ }
49
+ return compareBoolean(a.unique, b.unique);
21
50
  }
22
51
  exports.compareSpecIndex = compareSpecIndex;
23
52
  function compareApiIndex(a, b) {
@@ -31,7 +60,23 @@ function compareApiIndex(a, b) {
31
60
  if (a.queryScope !== b.queryScope) {
32
61
  return compareQueryScope(a.queryScope, b.queryScope);
33
62
  }
34
- return compareArrays(a.fields, b.fields, compareIndexField);
63
+ let cmp = compareArrays(a.fields, b.fields, compareIndexField);
64
+ if (cmp !== 0) {
65
+ return cmp;
66
+ }
67
+ cmp = compareApiScope(a.apiScope, b.apiScope);
68
+ if (cmp !== 0) {
69
+ return cmp;
70
+ }
71
+ cmp = compareDensity(a.density, b.density);
72
+ if (cmp !== 0) {
73
+ return cmp;
74
+ }
75
+ cmp = compareBoolean(a.multikey, b.multikey);
76
+ if (cmp !== 0) {
77
+ return cmp;
78
+ }
79
+ return compareBoolean(a.unique, b.unique);
35
80
  }
36
81
  exports.compareApiIndex = compareApiIndex;
37
82
  function compareApiDatabase(a, b) {
@@ -115,14 +160,62 @@ function compareFieldIndex(a, b) {
115
160
  if (a.arrayConfig !== b.arrayConfig) {
116
161
  return compareArrayConfig(a.arrayConfig, b.arrayConfig);
117
162
  }
118
- return 0;
163
+ let cmp = compareApiScope(a.apiScope, b.apiScope);
164
+ if (cmp !== 0) {
165
+ return cmp;
166
+ }
167
+ cmp = compareDensity(a.density, b.density);
168
+ if (cmp !== 0) {
169
+ return cmp;
170
+ }
171
+ cmp = compareBoolean(a.multikey, b.multikey);
172
+ if (cmp !== 0) {
173
+ return cmp;
174
+ }
175
+ return compareBoolean(a.unique, b.unique);
119
176
  }
120
177
  function compareQueryScope(a, b) {
121
178
  return QUERY_SCOPE_SEQUENCE.indexOf(a) - QUERY_SCOPE_SEQUENCE.indexOf(b);
122
179
  }
180
+ function compareApiScope(a, b) {
181
+ if (a === b) {
182
+ return 0;
183
+ }
184
+ if (a === undefined) {
185
+ return -1;
186
+ }
187
+ if (b === undefined) {
188
+ return 1;
189
+ }
190
+ return API_SCOPE_SEQUENCE.indexOf(a) - API_SCOPE_SEQUENCE.indexOf(b);
191
+ }
192
+ function compareDensity(a, b) {
193
+ if (a === b) {
194
+ return 0;
195
+ }
196
+ if (a === undefined) {
197
+ return -1;
198
+ }
199
+ if (b === undefined) {
200
+ return 1;
201
+ }
202
+ return DENSITY_SEQUENCE.indexOf(a) - DENSITY_SEQUENCE.indexOf(b);
203
+ }
123
204
  function compareOrder(a, b) {
124
205
  return ORDER_SEQUENCE.indexOf(a) - ORDER_SEQUENCE.indexOf(b);
125
206
  }
207
+ function compareBoolean(a, b) {
208
+ if (a === b) {
209
+ return 0;
210
+ }
211
+ if (a === undefined) {
212
+ return -1;
213
+ }
214
+ if (b === undefined) {
215
+ return 1;
216
+ }
217
+ return Number(a) - Number(b);
218
+ }
126
219
  function compareArrayConfig(a, b) {
127
220
  return ARRAY_CONFIG_SEQUENCE.indexOf(a) - ARRAY_CONFIG_SEQUENCE.indexOf(b);
128
221
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RecurrenceType = exports.DatabaseEdition = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.QueryScope = exports.Mode = void 0;
3
+ exports.RecurrenceType = exports.DatabaseEdition = exports.PointInTimeRecoveryEnablement = exports.PointInTimeRecoveryEnablementOption = exports.DatabaseDeleteProtectionState = exports.DatabaseDeleteProtectionStateOption = exports.DatabaseType = exports.StateTtl = exports.State = exports.ArrayConfig = exports.Order = exports.Density = exports.ApiScope = exports.QueryScope = exports.Mode = void 0;
4
4
  var Mode;
5
5
  (function (Mode) {
6
6
  Mode["ASCENDING"] = "ASCENDING";
@@ -12,6 +12,19 @@ var QueryScope;
12
12
  QueryScope["COLLECTION"] = "COLLECTION";
13
13
  QueryScope["COLLECTION_GROUP"] = "COLLECTION_GROUP";
14
14
  })(QueryScope = exports.QueryScope || (exports.QueryScope = {}));
15
+ var ApiScope;
16
+ (function (ApiScope) {
17
+ ApiScope["ANY_API"] = "ANY_API";
18
+ ApiScope["DATASTORE_MODE_API"] = "DATASTORE_MODE_API";
19
+ ApiScope["MONGODB_COMPATIBLE_API"] = "MONGODB_COMPATIBLE_API";
20
+ })(ApiScope = exports.ApiScope || (exports.ApiScope = {}));
21
+ var Density;
22
+ (function (Density) {
23
+ Density["DENSITY_UNSPECIFIED"] = "DENSITY_UNSPECIFIED";
24
+ Density["SPARSE_ALL"] = "SPARSE_ALL";
25
+ Density["SPARSE_ANY"] = "SPARSE_ANY";
26
+ Density["DENSE"] = "DENSE";
27
+ })(Density = exports.Density || (exports.Density = {}));
15
28
  var Order;
16
29
  (function (Order) {
17
30
  Order["ASCENDING"] = "ASCENDING";
@@ -6,6 +6,7 @@ const logger_1 = require("../logger");
6
6
  const utils = require("../utils");
7
7
  const validator = require("./validator");
8
8
  const types = require("./api-types");
9
+ const api_types_1 = require("./api-types");
9
10
  const sort = require("./api-sort");
10
11
  const util = require("./util");
11
12
  const prompt_1 = require("../prompt");
@@ -13,6 +14,7 @@ const api_1 = require("../api");
13
14
  const error_1 = require("../error");
14
15
  const apiv2_1 = require("../apiv2");
15
16
  const pretty_print_1 = require("./pretty-print");
17
+ const functional_1 = require("../functional");
16
18
  class FirestoreApi {
17
19
  constructor() {
18
20
  this.apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.firestoreOrigin)(), apiVersion: "v1" });
@@ -33,6 +35,7 @@ class FirestoreApi {
33
35
  });
34
36
  }
35
37
  async deploy(options, indexes, fieldOverrides, databaseId = "(default)") {
38
+ var _a;
36
39
  const spec = this.upgradeOldSpec({
37
40
  indexes,
38
41
  fieldOverrides,
@@ -42,8 +45,10 @@ class FirestoreApi {
42
45
  const fieldOverridesToDeploy = spec.fieldOverrides;
43
46
  const existingIndexes = await this.listIndexes(options.project, databaseId);
44
47
  const existingFieldOverrides = await this.listFieldOverrides(options.project, databaseId);
48
+ const database = await this.getDatabase(options.project, databaseId);
49
+ const edition = (_a = database.databaseEdition) !== null && _a !== void 0 ? _a : api_types_1.DatabaseEdition.STANDARD;
45
50
  const indexesToDelete = existingIndexes.filter((index) => {
46
- return !indexesToDeploy.some((spec) => this.indexMatchesSpec(index, spec));
51
+ return !indexesToDeploy.some((spec) => this.indexMatchesSpec(index, spec, edition));
47
52
  });
48
53
  const fieldOverridesToDelete = existingFieldOverrides.filter((field) => {
49
54
  return !fieldOverridesToDeploy.some((spec) => {
@@ -79,7 +84,7 @@ class FirestoreApi {
79
84
  }
80
85
  }
81
86
  for (const index of indexesToDeploy) {
82
- const exists = existingIndexes.some((x) => this.indexMatchesSpec(x, index));
87
+ const exists = existingIndexes.some((x) => this.indexMatchesSpec(x, index, edition));
83
88
  if (exists) {
84
89
  logger_1.logger.debug(`Skipping existing index: ${JSON.stringify(index)}`);
85
90
  }
@@ -160,6 +165,10 @@ class FirestoreApi {
160
165
  collectionGroup: util.parseIndexName(index.name).collectionGroupId,
161
166
  queryScope: index.queryScope,
162
167
  fields: index.fields,
168
+ apiScope: index.apiScope,
169
+ density: index.density,
170
+ multikey: index.multikey,
171
+ unique: index.unique,
163
172
  };
164
173
  });
165
174
  if (!fields) {
@@ -179,6 +188,10 @@ class FirestoreApi {
179
188
  order: firstField.order,
180
189
  arrayConfig: firstField.arrayConfig,
181
190
  queryScope: index.queryScope,
191
+ apiScope: index.apiScope,
192
+ density: index.density,
193
+ multikey: index.multikey,
194
+ unique: index.unique,
182
195
  };
183
196
  }),
184
197
  };
@@ -205,6 +218,19 @@ class FirestoreApi {
205
218
  validator.assertHas(index, "collectionGroup");
206
219
  validator.assertHas(index, "queryScope");
207
220
  validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
221
+ if (index.apiScope) {
222
+ validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
223
+ }
224
+ if (index.density) {
225
+ validator.assertEnum(index, "density", Object.keys(types.Density));
226
+ }
227
+ if (index.multikey) {
228
+ validator.assertType("multikey", index.multikey, "boolean");
229
+ }
230
+ if (index.unique !== undefined) {
231
+ validator.assertType("unique", index.unique, "boolean");
232
+ throw new error_1.FirebaseError("The `unique` index configuration is not supported yet.");
233
+ }
208
234
  validator.assertHas(index, "fields");
209
235
  index.fields.forEach((field) => {
210
236
  validator.assertHas(field, "fieldPath");
@@ -239,6 +265,18 @@ class FirestoreApi {
239
265
  if (index.queryScope) {
240
266
  validator.assertEnum(index, "queryScope", Object.keys(types.QueryScope));
241
267
  }
268
+ if (index.apiScope) {
269
+ validator.assertEnum(index, "apiScope", Object.keys(types.ApiScope));
270
+ }
271
+ if (index.density) {
272
+ validator.assertEnum(index, "density", Object.keys(types.Density));
273
+ }
274
+ if (index.multikey) {
275
+ validator.assertType("multikey", index.multikey, "boolean");
276
+ }
277
+ if (index.unique) {
278
+ validator.assertType("unique", index.unique, "boolean");
279
+ }
242
280
  });
243
281
  }
244
282
  async patchField(project, spec, databaseId = "(default)") {
@@ -246,6 +284,10 @@ class FirestoreApi {
246
284
  const indexes = spec.indexes.map((index) => {
247
285
  return {
248
286
  queryScope: index.queryScope,
287
+ apiScope: index.apiScope,
288
+ density: index.density,
289
+ multikey: index.multikey,
290
+ unique: index.unique,
249
291
  fields: [
250
292
  {
251
293
  fieldPath: spec.fieldPath,
@@ -282,13 +324,28 @@ class FirestoreApi {
282
324
  return this.apiClient.post(url, {
283
325
  fields: index.fields,
284
326
  queryScope: index.queryScope,
327
+ apiScope: index.apiScope,
328
+ density: index.density,
329
+ multikey: index.multikey,
330
+ unique: index.unique,
285
331
  });
286
332
  }
287
333
  deleteIndex(index) {
288
334
  const url = index.name;
289
335
  return this.apiClient.delete(`/${url}`);
290
336
  }
291
- indexMatchesSpec(index, spec) {
337
+ optionalApiScopeMatches(lhs, rhs) {
338
+ return (0, functional_1.optionalValueMatches)(lhs, rhs, types.ApiScope.ANY_API);
339
+ }
340
+ optionalDensityMatches(lhs, rhs, edition) {
341
+ const defaultValue = edition === api_types_1.DatabaseEdition.STANDARD ? types.Density.SPARSE_ALL : types.Density.DENSE;
342
+ return (0, functional_1.optionalValueMatches)(lhs, rhs, defaultValue);
343
+ }
344
+ optionalMultikeyMatches(lhs, rhs) {
345
+ const defaultValue = false;
346
+ return (0, functional_1.optionalValueMatches)(lhs, rhs, defaultValue);
347
+ }
348
+ indexMatchesSpec(index, spec, edition) {
292
349
  const collection = util.parseIndexName(index.name).collectionGroupId;
293
350
  if (collection !== spec.collectionGroup) {
294
351
  return false;
@@ -296,6 +353,15 @@ class FirestoreApi {
296
353
  if (index.queryScope !== spec.queryScope) {
297
354
  return false;
298
355
  }
356
+ if (!this.optionalApiScopeMatches(index.apiScope, spec.apiScope)) {
357
+ return false;
358
+ }
359
+ if (!this.optionalDensityMatches(index.density, spec.density, edition)) {
360
+ return false;
361
+ }
362
+ if (!this.optionalMultikeyMatches(index.multikey, spec.multikey)) {
363
+ return false;
364
+ }
299
365
  if (index.fields.length !== spec.fields.length) {
300
366
  return false;
301
367
  }
@@ -312,6 +378,9 @@ class FirestoreApi {
312
378
  if (iField.arrayConfig !== sField.arrayConfig) {
313
379
  return false;
314
380
  }
381
+ if (!utils.deepEqual(iField.vectorConfig, sField.vectorConfig)) {
382
+ return false;
383
+ }
315
384
  i++;
316
385
  }
317
386
  return true;
@@ -368,8 +437,19 @@ class FirestoreApi {
368
437
  const i = {
369
438
  collectionGroup: index.collectionGroup || index.collectionId,
370
439
  queryScope: index.queryScope || types.QueryScope.COLLECTION,
371
- fields: [],
372
440
  };
441
+ if (index.apiScope) {
442
+ i.apiScope = index.apiScope;
443
+ }
444
+ if (index.density) {
445
+ i.density = index.density;
446
+ }
447
+ if (index.multikey !== undefined) {
448
+ i.multikey = index.multikey;
449
+ }
450
+ if (index.unique !== undefined) {
451
+ i.unique = index.unique;
452
+ }
373
453
  if (index.fields) {
374
454
  i.fields = index.fields.map((field) => {
375
455
  const f = {
@@ -429,6 +509,7 @@ class FirestoreApi {
429
509
  const payload = {
430
510
  locationId: req.locationId,
431
511
  type: req.type,
512
+ databaseEdition: req.databaseEdition,
432
513
  deleteProtectionState: req.deleteProtectionState,
433
514
  pointInTimeRecoveryEnablement: req.pointInTimeRecoveryEnablement,
434
515
  cmekConfig: req.cmekConfig,
@@ -177,6 +177,13 @@ class PrettyPrint {
177
177
  }
178
178
  result += `(${field.fieldPath},${configString}) `;
179
179
  });
180
+ result += " -- ";
181
+ if (index.density !== undefined) {
182
+ result += clc.cyan(`Density:${index.density} `);
183
+ }
184
+ if (index.multikey !== undefined) {
185
+ result += clc.cyan(`Multikey:${index.multikey ? "YES" : "NO"}`);
186
+ }
180
187
  return result;
181
188
  }
182
189
  prettyBackupString(backup) {
@@ -26,7 +26,7 @@ exports.assertHasOneOf = assertHasOneOf;
26
26
  function assertEnum(obj, prop, valid) {
27
27
  const objString = clc.cyan(JSON.stringify(obj));
28
28
  if (valid.indexOf(obj[prop]) < 0) {
29
- throw new error_1.FirebaseError(`Field "${prop}" must be one of ${valid.join(", ")}: ${objString}`);
29
+ throw new error_1.FirebaseError(`Field "${prop}" must be one of ${valid.join(", ")}: ${objString}`);
30
30
  }
31
31
  }
32
32
  exports.assertEnum = assertEnum;