firebase-tools 10.2.1 → 10.3.1

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 (125) hide show
  1. package/lib/appdistribution/options-parser-util.js +1 -1
  2. package/lib/auth.js +3 -3
  3. package/lib/command.js +1 -1
  4. package/lib/commands/apps-android-sha-create.js +2 -2
  5. package/lib/commands/apps-sdkconfig.js +1 -1
  6. package/lib/commands/database-rules-list.js +2 -2
  7. package/lib/commands/emulators-start.js +1 -1
  8. package/lib/commands/ext-configure.js +67 -7
  9. package/lib/commands/ext-dev-init.js +49 -49
  10. package/lib/commands/ext-export.js +7 -2
  11. package/lib/commands/ext-install.js +173 -109
  12. package/lib/commands/ext-uninstall.js +17 -8
  13. package/lib/commands/ext-update.js +67 -12
  14. package/lib/commands/functions-config-export.js +1 -1
  15. package/lib/commands/hosting-clone.js +3 -3
  16. package/lib/commands/remoteconfig-get.js +1 -1
  17. package/lib/config.js +6 -3
  18. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  19. package/lib/deploy/extensions/planner.js +7 -6
  20. package/lib/deploy/extensions/tasks.js +1 -1
  21. package/lib/deploy/functions/backend.js +21 -5
  22. package/lib/deploy/functions/checkIam.js +5 -5
  23. package/lib/deploy/functions/containerCleaner.js +3 -3
  24. package/lib/deploy/functions/ensure.js +3 -3
  25. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  26. package/lib/deploy/functions/prepare.js +5 -3
  27. package/lib/deploy/functions/pricing.js +1 -1
  28. package/lib/deploy/functions/prompts.js +2 -2
  29. package/lib/deploy/functions/release/fabricator.js +7 -7
  30. package/lib/deploy/functions/release/index.js +1 -1
  31. package/lib/deploy/functions/release/planner.js +43 -26
  32. package/lib/deploy/functions/release/reporter.js +3 -0
  33. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  34. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  36. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  37. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +64 -20
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  42. package/lib/deploy/functions/services/index.js +9 -1
  43. package/lib/deploy/functions/services/storage.js +10 -4
  44. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  45. package/lib/deploy/functions/validate.js +3 -3
  46. package/lib/deploy/hosting/deploy.js +2 -2
  47. package/lib/deploy/hosting/hashcache.js +21 -19
  48. package/lib/deploy/hosting/uploader.js +5 -5
  49. package/lib/deploy/remoteconfig/functions.js +2 -2
  50. package/lib/emulator/auth/cloudFunctions.js +1 -1
  51. package/lib/emulator/auth/operations.js +1 -1
  52. package/lib/emulator/constants.js +4 -0
  53. package/lib/emulator/controller.js +54 -24
  54. package/lib/emulator/download.js +18 -1
  55. package/lib/emulator/downloadableEmulators.js +1 -1
  56. package/lib/emulator/emulatorLogger.js +12 -1
  57. package/lib/emulator/extensions/validation.js +70 -0
  58. package/lib/emulator/extensionsEmulator.js +175 -0
  59. package/lib/emulator/functionsEmulator.js +95 -43
  60. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  61. package/lib/emulator/functionsEmulatorShared.js +30 -11
  62. package/lib/emulator/functionsEmulatorShell.js +1 -1
  63. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  64. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  65. package/lib/emulator/hub.js +4 -3
  66. package/lib/emulator/loggingEmulator.js +1 -1
  67. package/lib/emulator/pubsubEmulator.js +1 -1
  68. package/lib/emulator/registry.js +10 -2
  69. package/lib/emulator/storage/apis/firebase.js +314 -332
  70. package/lib/emulator/storage/apis/gcloud.js +241 -121
  71. package/lib/emulator/storage/crc.js +5 -1
  72. package/lib/emulator/storage/errors.js +9 -0
  73. package/lib/emulator/storage/files.js +159 -300
  74. package/lib/emulator/storage/index.js +27 -73
  75. package/lib/emulator/storage/metadata.js +65 -51
  76. package/lib/emulator/storage/multipart.js +62 -0
  77. package/lib/emulator/storage/persistence.js +78 -0
  78. package/lib/emulator/storage/rules/config.js +33 -0
  79. package/lib/emulator/storage/rules/manager.js +81 -0
  80. package/lib/emulator/storage/rules/runtime.js +8 -7
  81. package/lib/emulator/storage/rules/utils.js +48 -0
  82. package/lib/emulator/storage/server.js +2 -2
  83. package/lib/emulator/storage/upload.js +106 -0
  84. package/lib/emulator/types.js +3 -0
  85. package/lib/ensureApiEnabled.js +5 -1
  86. package/lib/error.js +1 -1
  87. package/lib/extensions/askUserForParam.js +31 -25
  88. package/lib/extensions/changelog.js +3 -1
  89. package/lib/extensions/checkProjectBilling.js +1 -1
  90. package/lib/extensions/displayExtensionInfo.js +1 -1
  91. package/lib/extensions/emulator/optionsHelper.js +56 -8
  92. package/lib/extensions/emulator/specHelper.js +10 -23
  93. package/lib/extensions/export.js +1 -51
  94. package/lib/extensions/extensionsApi.js +1 -1
  95. package/lib/extensions/extensionsHelper.js +32 -19
  96. package/lib/extensions/manifest.js +144 -0
  97. package/lib/extensions/metricsUtils.js +4 -4
  98. package/lib/extensions/paramHelper.js +34 -12
  99. package/lib/extensions/refs.js +1 -1
  100. package/lib/extensions/secretsUtils.js +3 -3
  101. package/lib/functional.js +1 -1
  102. package/lib/functions/env.js +6 -7
  103. package/lib/functions/events/v2.js +11 -0
  104. package/lib/gcp/cloudfunctions.js +43 -11
  105. package/lib/gcp/cloudfunctionsv2.js +48 -17
  106. package/lib/gcp/cloudtasks.js +1 -1
  107. package/lib/gcp/docker.js +2 -2
  108. package/lib/gcp/resourceManager.js +4 -4
  109. package/lib/gcp/run.js +2 -2
  110. package/lib/hosting/api.js +1 -1
  111. package/lib/hosting/proxy.js +2 -2
  112. package/lib/init/features/account.js +1 -1
  113. package/lib/management/database.js +1 -1
  114. package/lib/previews.js +1 -1
  115. package/lib/serve/functions.js +2 -1
  116. package/lib/utils.js +15 -2
  117. package/npm-shrinkwrap.json +786 -393
  118. package/package.json +1 -1
  119. package/schema/firebase-config.json +5 -0
  120. package/templates/init/functions/javascript/package.lint.json +3 -3
  121. package/templates/init/functions/javascript/package.nolint.json +2 -2
  122. package/templates/init/functions/typescript/package.lint.json +7 -7
  123. package/templates/init/functions/typescript/package.nolint.json +3 -3
  124. package/lib/deploy/extensions/params.js +0 -39
  125. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -27,9 +27,173 @@ const utils = require("../utils");
27
27
  const track_1 = require("../track");
28
28
  const logger_1 = require("../logger");
29
29
  const previews_1 = require("../previews");
30
+ const manifest = require("../extensions/manifest");
31
+ const paramHelper_1 = require("../extensions/paramHelper");
30
32
  marked.setOptions({
31
33
  renderer: new TerminalRenderer(),
32
34
  });
35
+ exports.default = new command_1.Command("ext:install [extensionName]")
36
+ .description("install an official extension if [extensionName] or [extensionName@version] is provided; " +
37
+ (previews_1.previews.extdev
38
+ ? "install a local extension if [localPathOrUrl] or [url#root] is provided; install a published extension (not authored by Firebase) if [publisherId/extensionId] is provided "
39
+ : "") +
40
+ "or run with `-i` to see all available extensions.")
41
+ .withForce()
42
+ .option("--params <paramsFile>", "name of params variables file with .env format.")
43
+ .option("--local", "save to firebase.json rather than directly install to a Firebase project")
44
+ .before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
45
+ .before(extensionsHelper_1.ensureExtensionsApiEnabled)
46
+ .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
47
+ .before(extensionsHelper_1.diagnoseAndFixProject)
48
+ .action(async (extensionName, options) => {
49
+ var _a;
50
+ const projectId = (0, projectUtils_1.needProjectId)(options);
51
+ const paramsEnvPath = ((_a = options.params) !== null && _a !== void 0 ? _a : "");
52
+ let learnMore = false;
53
+ if (!extensionName) {
54
+ if (options.interactive) {
55
+ learnMore = true;
56
+ extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
57
+ " Select an extension, then press Enter to learn more.");
58
+ }
59
+ else {
60
+ throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
61
+ `Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
62
+ }
63
+ }
64
+ let source;
65
+ let extVersion;
66
+ if ((0, extensionsHelper_1.isUrlPath)(extensionName)) {
67
+ void (0, track_1.track)("Extension Install", "Install by url path", options.interactive ? 1 : 0);
68
+ }
69
+ if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
70
+ void (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
71
+ if (options.local) {
72
+ throw new error_1.FirebaseError("Installing a local source locally is not supported yet, please use ext:dev:emulator commands");
73
+ }
74
+ source = await infoInstallBySource(projectId, extensionName);
75
+ }
76
+ else {
77
+ void (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
78
+ extVersion = await infoInstallByReference(extensionName, options.interactive);
79
+ }
80
+ if (!(await (0, extensionsHelper_1.confirm)({
81
+ nonInteractive: options.nonInteractive,
82
+ force: options.force,
83
+ default: true,
84
+ }))) {
85
+ return;
86
+ }
87
+ if (!source && !extVersion) {
88
+ throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
89
+ }
90
+ const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
91
+ if (!spec) {
92
+ throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
93
+ }
94
+ if (learnMore) {
95
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
96
+ `${spec.description}\n` +
97
+ `View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
98
+ }
99
+ if (options.local) {
100
+ try {
101
+ return installToManifest({
102
+ paramsEnvPath,
103
+ projectId,
104
+ extensionName,
105
+ source,
106
+ extVersion,
107
+ nonInteractive: options.nonInteractive,
108
+ force: options.force,
109
+ });
110
+ }
111
+ catch (err) {
112
+ if (!(err instanceof error_1.FirebaseError)) {
113
+ throw new error_1.FirebaseError(`Error occurred saving the extension to manifest: ${err.message}`, {
114
+ original: err,
115
+ });
116
+ }
117
+ throw err;
118
+ }
119
+ }
120
+ try {
121
+ return installExtension({
122
+ paramsEnvPath,
123
+ projectId,
124
+ extensionName,
125
+ source,
126
+ extVersion,
127
+ nonInteractive: options.nonInteractive,
128
+ force: options.force,
129
+ });
130
+ }
131
+ catch (err) {
132
+ if (!(err instanceof error_1.FirebaseError)) {
133
+ throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
134
+ original: err,
135
+ });
136
+ }
137
+ throw err;
138
+ }
139
+ });
140
+ async function infoInstallBySource(projectId, extensionName) {
141
+ let source;
142
+ try {
143
+ source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
144
+ }
145
+ catch (err) {
146
+ throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
147
+ `and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
148
+ }
149
+ (0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
150
+ return source;
151
+ }
152
+ async function infoInstallByReference(extensionName, interactive) {
153
+ if (extensionName.split("/").length < 2) {
154
+ const [extensionID, version] = extensionName.split("@");
155
+ extensionName = `firebase/${extensionID}@${version || "latest"}`;
156
+ }
157
+ const ref = refs.parse(extensionName);
158
+ const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
159
+ if (!ref.version) {
160
+ void (0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
161
+ extensionName = `${extensionName}@latest`;
162
+ }
163
+ const extVersion = await extensionsApi.getExtensionVersion(extensionName);
164
+ (0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
165
+ await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
166
+ return extVersion;
167
+ }
168
+ async function installToManifest(options) {
169
+ const { projectId, extensionName, extVersion, paramsEnvPath, nonInteractive, force } = options;
170
+ const spec = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec;
171
+ if (!spec) {
172
+ throw new error_1.FirebaseError(`Could not find the extension.yaml for ${extensionName}. Please make sure this is a valid extension and try again.`);
173
+ }
174
+ const config = manifest.loadConfig(options);
175
+ let instanceId = spec.name;
176
+ while (manifest.instanceExists(instanceId, config)) {
177
+ instanceId = await (0, extensionsHelper_1.promptForValidInstanceId)(`${spec.name}-${(0, utils_1.getRandomString)(4)}`);
178
+ }
179
+ const paramBindingOptions = await paramHelper.getParams({
180
+ projectId,
181
+ paramSpecs: spec.params,
182
+ nonInteractive,
183
+ paramsEnvPath,
184
+ instanceId,
185
+ });
186
+ const params = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
187
+ const ref = refs.parse(extVersion.ref);
188
+ await manifest.writeToManifest([
189
+ {
190
+ instanceId,
191
+ ref,
192
+ params,
193
+ },
194
+ ], config, { nonInteractive, force: force !== null && force !== void 0 ? force : false });
195
+ manifest.showPreviewWarning();
196
+ }
33
197
  async function installExtension(options) {
34
198
  const { projectId, extensionName, source, extVersion, paramsEnvPath, nonInteractive, force } = options;
35
199
  const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
@@ -97,17 +261,19 @@ async function installExtension(options) {
97
261
  else {
98
262
  choice = "installNew";
99
263
  }
100
- let params;
264
+ let paramBindingOptions;
265
+ let paramBindings;
101
266
  switch (choice) {
102
267
  case "installNew":
103
268
  instanceId = await (0, extensionsHelper_1.promptForValidInstanceId)(`${instanceId}-${(0, utils_1.getRandomString)(4)}`);
104
- params = await paramHelper.getParams({
269
+ paramBindingOptions = await paramHelper.getParams({
105
270
  projectId,
106
271
  paramSpecs: spec.params,
107
272
  nonInteractive,
108
273
  paramsEnvPath,
109
274
  instanceId,
110
275
  });
276
+ paramBindings = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
111
277
  spinner.text = "Installing your extension instance. This usually takes 3 to 5 minutes...";
112
278
  spinner.start();
113
279
  await extensionsApi.createInstance({
@@ -115,20 +281,21 @@ async function installExtension(options) {
115
281
  instanceId,
116
282
  extensionSource: source,
117
283
  extensionVersionRef: extVersion === null || extVersion === void 0 ? void 0 : extVersion.ref,
118
- params,
284
+ params: paramBindings,
119
285
  });
120
286
  spinner.stop();
121
287
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `Successfully installed your instance of ${clc.bold(spec.displayName || spec.name)}! ` +
122
288
  `Its Instance ID is ${clc.bold(instanceId)}.`);
123
289
  break;
124
290
  case "updateExisting":
125
- params = await paramHelper.getParams({
291
+ paramBindingOptions = await paramHelper.getParams({
126
292
  projectId,
127
293
  paramSpecs: spec.params,
128
294
  nonInteractive,
129
295
  paramsEnvPath,
130
296
  instanceId,
131
297
  });
298
+ paramBindings = (0, paramHelper_1.getBaseParamBindings)(paramBindingOptions);
132
299
  spinner.text = "Updating your extension instance. This usually takes 3 to 5 minutes...";
133
300
  spinner.start();
134
301
  await (0, updateHelper_1.update)({
@@ -136,7 +303,7 @@ async function installExtension(options) {
136
303
  instanceId,
137
304
  source,
138
305
  extRef: extVersion === null || extVersion === void 0 ? void 0 : extVersion.ref,
139
- params,
306
+ params: paramBindings,
140
307
  });
141
308
  spinner.stop();
142
309
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `Successfully updated your instance of ${clc.bold(spec.displayName || spec.name)}! ` +
@@ -149,6 +316,7 @@ async function installExtension(options) {
149
316
  `which may include some required post-installation tasks: ${utils.consoleUrl(projectId, `/extensions/instances/${instanceId}?tab=usage`)}`));
150
317
  logger_1.logger.info(marked("You can run `firebase ext` to view available Firebase Extensions commands, " +
151
318
  "including those to update, reconfigure, or delete your installed extension."));
319
+ manifest.showDeprecationWarning();
152
320
  }
153
321
  catch (err) {
154
322
  if (spinner.isSpinning) {
@@ -162,107 +330,3 @@ async function installExtension(options) {
162
330
  });
163
331
  }
164
332
  }
165
- async function infoInstallBySource(projectId, extensionName) {
166
- let source;
167
- try {
168
- source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, extensionName);
169
- }
170
- catch (err) {
171
- throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}', ` +
172
- `and encountered the following error when trying to create an instance of extension '${clc.bold(extensionName)}':\n ${err.message}`);
173
- }
174
- (0, displayExtensionInfo_1.displayExtInfo)(extensionName, "", source.spec);
175
- return source;
176
- }
177
- async function infoInstallByReference(extensionName, interactive) {
178
- if (extensionName.split("/").length < 2) {
179
- const [extensionID, version] = extensionName.split("@");
180
- extensionName = `firebase/${extensionID}@${version || "latest"}`;
181
- }
182
- const ref = refs.parse(extensionName);
183
- const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
184
- if (!ref.version) {
185
- (0, track_1.track)("Extension Install", "Install by Extension Version Ref", interactive ? 1 : 0);
186
- extensionName = `${extensionName}@latest`;
187
- }
188
- const extVersion = await extensionsApi.getExtensionVersion(extensionName);
189
- (0, displayExtensionInfo_1.displayExtInfo)(extensionName, ref.publisherId, extVersion.spec, true);
190
- await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, extVersion);
191
- return extVersion;
192
- }
193
- exports.default = new command_1.Command("ext:install [extensionName]")
194
- .description("install an official extension if [extensionName] or [extensionName@version] is provided; " +
195
- (previews_1.previews.extdev
196
- ? "install a local extension if [localPathOrUrl] or [url#root] is provided; install a published extension (not authored by Firebase) if [publisherId/extensionId] is provided "
197
- : "") +
198
- "or run with `-i` to see all available extensions.")
199
- .withForce()
200
- .option("--params <paramsFile>", "name of params variables file with .env format.")
201
- .before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.create"])
202
- .before(extensionsHelper_1.ensureExtensionsApiEnabled)
203
- .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
204
- .before(extensionsHelper_1.diagnoseAndFixProject)
205
- .action(async (extensionName, options) => {
206
- const projectId = (0, projectUtils_1.needProjectId)(options);
207
- const paramsEnvPath = options.params;
208
- let learnMore = false;
209
- if (!extensionName) {
210
- if (options.interactive) {
211
- learnMore = true;
212
- extensionName = await (0, extensionsHelper_1.promptForOfficialExtension)("Which official extension do you wish to install?\n" +
213
- " Select an extension, then press Enter to learn more.");
214
- }
215
- else {
216
- throw new error_1.FirebaseError(`Unable to find published extension '${clc.bold(extensionName)}'. ` +
217
- `Run ${clc.bold("firebase ext:install -i")} to select from the list of all available published extensions.`);
218
- }
219
- }
220
- let source;
221
- let extVersion;
222
- if ((0, extensionsHelper_1.isLocalOrURLPath)(extensionName)) {
223
- (0, track_1.track)("Extension Install", "Install by Source", options.interactive ? 1 : 0);
224
- source = await infoInstallBySource(projectId, extensionName);
225
- }
226
- else {
227
- (0, track_1.track)("Extension Install", "Install by Extension Ref", options.interactive ? 1 : 0);
228
- extVersion = await infoInstallByReference(extensionName, options.interactive);
229
- }
230
- if (!(await (0, extensionsHelper_1.confirm)({
231
- nonInteractive: options.nonInteractive,
232
- force: options.force,
233
- default: true,
234
- }))) {
235
- return;
236
- }
237
- if (!source && !extVersion) {
238
- throw new error_1.FirebaseError("Could not find a source. Please specify a valid source to continue.");
239
- }
240
- const spec = (source === null || source === void 0 ? void 0 : source.spec) || (extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec);
241
- if (!spec) {
242
- throw new error_1.FirebaseError(`Could not find the extension.yaml for extension '${clc.bold(extensionName)}'. Please make sure this is a valid extension and try again.`);
243
- }
244
- if (learnMore) {
245
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, `You selected: ${clc.bold(spec.displayName)}.\n` +
246
- `${spec.description}\n` +
247
- `View details: https://firebase.google.com/products/extensions/${spec.name}\n`);
248
- }
249
- try {
250
- return installExtension({
251
- paramsEnvPath,
252
- projectId,
253
- extensionName,
254
- source,
255
- extVersion,
256
- nonInteractive: options.nonInteractive,
257
- force: options.force,
258
- });
259
- }
260
- catch (err) {
261
- if (!(err instanceof error_1.FirebaseError)) {
262
- throw new error_1.FirebaseError(`Error occurred installing the extension: ${err.message}`, {
263
- original: err,
264
- });
265
- }
266
- throw err;
267
- }
268
- });
@@ -16,25 +16,25 @@ const prompt_1 = require("../prompt");
16
16
  const requirePermissions_1 = require("../requirePermissions");
17
17
  const utils = require("../utils");
18
18
  const logger_1 = require("../logger");
19
+ const manifest = require("../extensions/manifest");
19
20
  marked.setOptions({
20
21
  renderer: new TerminalRenderer(),
21
22
  });
22
- function consoleUninstallOnly(projectId, instanceId) {
23
- const instanceURL = `https://console.firebase.google.com/project/${projectId}/extensions/instances/${instanceId}`;
24
- const consoleUninstall = "This extension has additional uninstall checks that are not currently supported by the CLI, and can only be uninstalled through the Firebase Console. " +
25
- `Please visit **[${instanceURL}](${instanceURL})** to uninstall this extension.`;
26
- logger_1.logger.info("\n");
27
- utils.logLabeledWarning(extensionsHelper_1.logPrefix, marked(consoleUninstall));
28
- return Promise.resolve();
29
- }
30
23
  exports.default = new command_1.Command("ext:uninstall <extensionInstanceId>")
31
24
  .description("uninstall an extension that is installed in your Firebase project by instance ID")
32
25
  .withForce()
26
+ .option("--local", "remove from firebase.json rather than directly uninstall from a Firebase project")
33
27
  .before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.delete"])
34
28
  .before(extensionsHelper_1.ensureExtensionsApiEnabled)
35
29
  .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
36
30
  .before(extensionsHelper_1.diagnoseAndFixProject)
37
31
  .action(async (instanceId, options) => {
32
+ if (options.local) {
33
+ const config = manifest.loadConfig(options);
34
+ manifest.removeFromManifest(instanceId, config);
35
+ manifest.showPreviewWarning();
36
+ return;
37
+ }
38
38
  const projectId = (0, projectUtils_1.needProjectId)(options);
39
39
  let instance;
40
40
  try {
@@ -101,4 +101,13 @@ exports.default = new command_1.Command("ext:uninstall <extensionInstanceId>")
101
101
  return utils.reject(`Error occurred uninstalling extension ${instanceId}`, { original: err });
102
102
  }
103
103
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `uninstalled ${clc.bold(instanceId)}`);
104
+ manifest.showDeprecationWarning();
104
105
  });
106
+ function consoleUninstallOnly(projectId, instanceId) {
107
+ const instanceURL = `https://console.firebase.google.com/project/${projectId}/extensions/instances/${instanceId}`;
108
+ const consoleUninstall = "This extension has additional uninstall checks that are not currently supported by the CLI, and can only be uninstalled through the Firebase Console. " +
109
+ `Please visit **[${instanceURL}](${instanceURL})** to uninstall this extension.`;
110
+ logger_1.logger.info("\n");
111
+ utils.logLabeledWarning(extensionsHelper_1.logPrefix, marked(consoleUninstall));
112
+ return Promise.resolve();
113
+ }
@@ -22,18 +22,10 @@ const projectUtils_1 = require("../projectUtils");
22
22
  const requirePermissions_1 = require("../requirePermissions");
23
23
  const utils = require("../utils");
24
24
  const previews_1 = require("../previews");
25
+ const manifest = require("../extensions/manifest");
25
26
  marked.setOptions({
26
27
  renderer: new TerminalRenderer(),
27
28
  });
28
- function isValidUpdate(existingSourceOrigin, newSourceOrigin) {
29
- if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION) {
30
- return [extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(newSourceOrigin);
31
- }
32
- else if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
33
- return [extensionsHelper_1.SourceOrigin.LOCAL, extensionsHelper_1.SourceOrigin.URL].includes(newSourceOrigin);
34
- }
35
- return false;
36
- }
37
29
  exports.default = new command_1.Command("ext:update <extensionInstanceId> [updateSource]")
38
30
  .description(previews_1.previews.extdev
39
31
  ? "update an existing extension instance to the latest version or from a local or URL source"
@@ -47,10 +39,62 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
47
39
  .before(extensionsHelper_1.diagnoseAndFixProject)
48
40
  .withForce()
49
41
  .option("--params <paramsFile>", "name of params variables file with .env format.")
42
+ .option("--local", "save the update to firebase.json rather than directly update an existing Extension instance on a Firebase project")
50
43
  .action(async (instanceId, updateSource, options) => {
44
+ var _a, _b;
45
+ const projectId = (0, projectUtils_1.needProjectId)(options);
46
+ if (options.local) {
47
+ const config = manifest.loadConfig(options);
48
+ const oldRef = manifest.getInstanceRef(instanceId, config);
49
+ const oldExtensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(oldRef));
50
+ updateSource = (0, updateHelper_1.inferUpdateSource)(updateSource, refs.toExtensionRef(oldRef));
51
+ const newSourceOrigin = (0, extensionsHelper_1.getSourceOrigin)(updateSource);
52
+ if (![extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(newSourceOrigin)) {
53
+ throw new error_1.FirebaseError(`Only updating to a published extension version is allowed`);
54
+ }
55
+ const newExtensionVersion = await extensionsApi.getExtensionVersion(updateSource);
56
+ if (oldExtensionVersion.ref === newExtensionVersion.ref) {
57
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold(instanceId)} is already up to date. Its version is ${clc.bold(newExtensionVersion.ref)}.`);
58
+ return;
59
+ }
60
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(oldExtensionVersion.ref)} to version ${clc.bold(newExtensionVersion.ref)}.`);
61
+ if (!(await (0, extensionsHelper_1.confirm)({
62
+ nonInteractive: options.nonInteractive,
63
+ force: options.force,
64
+ default: false,
65
+ }))) {
66
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, "Update aborted.");
67
+ return;
68
+ }
69
+ const oldParamValues = manifest.readInstanceParam({
70
+ instanceId,
71
+ projectDir: config.projectDir,
72
+ });
73
+ const newParamBindingOptions = await paramHelper.getParamsForUpdate({
74
+ spec: oldExtensionVersion.spec,
75
+ newSpec: newExtensionVersion.spec,
76
+ currentParams: oldParamValues,
77
+ projectId,
78
+ paramsEnvPath: ((_a = options.params) !== null && _a !== void 0 ? _a : ""),
79
+ nonInteractive: options.nonInteractive,
80
+ instanceId,
81
+ });
82
+ const newParamBindings = paramHelper.getBaseParamBindings(newParamBindingOptions);
83
+ await manifest.writeToManifest([
84
+ {
85
+ instanceId,
86
+ ref: refs.parse(newExtensionVersion.ref),
87
+ params: newParamBindings,
88
+ },
89
+ ], config, {
90
+ nonInteractive: options.nonInteractive,
91
+ force: true,
92
+ });
93
+ manifest.showPreviewWarning();
94
+ return;
95
+ }
51
96
  const spinner = ora(`Updating ${clc.bold(instanceId)}. This usually takes 3 to 5 minutes...`);
52
97
  try {
53
- const projectId = (0, projectUtils_1.needProjectId)(options);
54
98
  let existingInstance;
55
99
  try {
56
100
  existingInstance = await extensionsApi.getInstance(projectId, instanceId);
@@ -152,15 +196,16 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
152
196
  }
153
197
  }
154
198
  const oldParamValues = Object.assign({}, existingParams);
155
- const newParams = await paramHelper.getParamsForUpdate({
199
+ const newParamBindings = await paramHelper.getParamsForUpdate({
156
200
  spec: existingSpec,
157
201
  newSpec,
158
202
  currentParams: existingParams,
159
203
  projectId,
160
- paramsEnvPath: options.params,
204
+ paramsEnvPath: ((_b = options.params) !== null && _b !== void 0 ? _b : ""),
161
205
  nonInteractive: options.nonInteractive,
162
206
  instanceId,
163
207
  });
208
+ const newParams = paramHelper.getBaseParamBindings(newParamBindings);
164
209
  spinner.start();
165
210
  const updateOptions = {
166
211
  projectId,
@@ -179,6 +224,7 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
179
224
  spinner.stop();
180
225
  utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `successfully updated ${clc.bold(instanceId)}.`);
181
226
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`You can view your updated instance in the Firebase console: ${utils.consoleUrl(projectId, `/extensions/instances/${instanceId}?tab=usage`)}`));
227
+ manifest.showDeprecationWarning();
182
228
  }
183
229
  catch (err) {
184
230
  if (spinner.isSpinning) {
@@ -192,3 +238,12 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
192
238
  throw err;
193
239
  }
194
240
  });
241
+ function isValidUpdate(existingSourceOrigin, newSourceOrigin) {
242
+ if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION) {
243
+ return [extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(newSourceOrigin);
244
+ }
245
+ else if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
246
+ return [extensionsHelper_1.SourceOrigin.LOCAL, extensionsHelper_1.SourceOrigin.URL].includes(newSourceOrigin);
247
+ }
248
+ return false;
249
+ }
@@ -96,7 +96,7 @@ exports.default = new command_1.Command("functions:config:export")
96
96
  throw new error_1.FirebaseError("Exceeded max attempts to fix invalid config keys.");
97
97
  }
98
98
  const errMsg = configExport.hydrateEnvs(pInfos, prefix);
99
- if (errMsg.length == 0) {
99
+ if (errMsg.length === 0) {
100
100
  break;
101
101
  }
102
102
  prefix = await promptForPrefix(errMsg);
@@ -32,8 +32,8 @@ exports.default = new command_1.Command("hosting:clone <source> <targetChannel>"
32
32
  if (sourceChannelId) {
33
33
  sourceChannelId = (0, api_1.normalizeName)(sourceChannelId);
34
34
  }
35
- const equalSiteIds = sourceSiteId == targetSiteId;
36
- const equalChannelIds = sourceChannelId == targetChannelId;
35
+ const equalSiteIds = sourceSiteId === targetSiteId;
36
+ const equalChannelIds = sourceChannelId === targetChannelId;
37
37
  if (equalSiteIds && equalChannelIds) {
38
38
  throw new error_1.FirebaseError(`Source and destination cannot be equal. Please pick a different source or desination.`);
39
39
  }
@@ -67,7 +67,7 @@ exports.default = new command_1.Command("hosting:clone <source> <targetChannel>"
67
67
  }
68
68
  }
69
69
  const currentTargetVersionName = (_d = (_c = tChannel.release) === null || _c === void 0 ? void 0 : _c.version) === null || _d === void 0 ? void 0 : _d.name;
70
- if (equalSiteIds && sourceVersionName == currentTargetVersionName) {
70
+ if (equalSiteIds && sourceVersionName === currentTargetVersionName) {
71
71
  utils.logSuccess(`Channels ${(0, cli_color_1.bold)(sourceChannelId)} and ${(0, cli_color_1.bold)(targetChannel)} are serving identical versions. No need to clone.`);
72
72
  return;
73
73
  }
@@ -15,7 +15,7 @@ const error_1 = require("../error");
15
15
  const tableHead = ["Entry Name", "Value"];
16
16
  const MAX_DISPLAY_ITEMS = 20;
17
17
  function checkValidOptionalNumber(versionNumber) {
18
- if (!versionNumber || typeof Number(versionNumber) == "number") {
18
+ if (!versionNumber || typeof Number(versionNumber) === "number") {
19
19
  return versionNumber;
20
20
  }
21
21
  throw new error_1.FirebaseError(`Could not interpret "${versionNumber}" as a valid number.`);
package/lib/config.js CHANGED
@@ -17,12 +17,12 @@ const logger_1 = require("./logger");
17
17
  const loadCJSON = require("./loadCJSON");
18
18
  const parseBoltRules = require("./parseBoltRules");
19
19
  class Config {
20
- constructor(src, options) {
20
+ constructor(src, options = {}) {
21
21
  this.data = {};
22
22
  this.defaults = {};
23
23
  this.notes = {};
24
- this.options = options || {};
25
- this.projectDir = options.projectDir || (0, detectProjectRoot_1.detectProjectRoot)(options);
24
+ this.options = options;
25
+ this.projectDir = this.options.projectDir || (0, detectProjectRoot_1.detectProjectRoot)(this.options);
26
26
  this._src = src;
27
27
  if (this._src.firebase) {
28
28
  this.defaults.project = this._src.firebase;
@@ -144,6 +144,9 @@ class Config {
144
144
  fs.ensureFileSync(this.path(p));
145
145
  fs.writeFileSync(this.path(p), content, "utf8");
146
146
  }
147
+ deleteProjectFile(p) {
148
+ fs.removeSync(this.path(p));
149
+ }
147
150
  askWriteProjectFile(p, content, force) {
148
151
  const writeTo = this.path(p);
149
152
  let next;
@@ -7,8 +7,8 @@ const humanReadable = (dep) => `${clc.bold(dep.instanceId)} (${dep.ref ? `${refs
7
7
  exports.humanReadable = humanReadable;
8
8
  const humanReadableUpdate = (from, to) => {
9
9
  var _a, _b, _c, _d, _e;
10
- if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId) == ((_b = to.ref) === null || _b === void 0 ? void 0 : _b.publisherId) &&
11
- ((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId) == ((_d = to.ref) === null || _d === void 0 ? void 0 : _d.extensionId)) {
10
+ if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId) === ((_b = to.ref) === null || _b === void 0 ? void 0 : _b.publisherId) &&
11
+ ((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId) === ((_d = to.ref) === null || _d === void 0 ? void 0 : _d.extensionId)) {
12
12
  return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_e = to.ref) === null || _e === void 0 ? void 0 : _e.version})`;
13
13
  }
14
14
  else {
@@ -28,7 +28,7 @@ exports.createsSummary = createsSummary;
28
28
  function updatesSummary(toUpdate, have) {
29
29
  const instancesToUpdate = toUpdate
30
30
  .map((to) => {
31
- const from = have.find((exists) => exists.instanceId == to.instanceId);
31
+ const from = have.find((exists) => exists.instanceId === to.instanceId);
32
32
  return humanReadableUpdate(from, to);
33
33
  })
34
34
  .join("\n");
@@ -2,12 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveVersion = exports.want = exports.have = exports.getExtension = exports.getExtensionVersion = void 0;
4
4
  const semver = require("semver");
5
- const error_1 = require("../../error");
6
5
  const extensionsApi = require("../../extensions/extensionsApi");
7
- const extensionsHelper_1 = require("../../extensions/extensionsHelper");
8
6
  const refs = require("../../extensions/refs");
9
- const params_1 = require("./params");
7
+ const error_1 = require("../../error");
8
+ const extensionsHelper_1 = require("../../extensions/extensionsHelper");
10
9
  const logger_1 = require("../../logger");
10
+ const manifest_1 = require("../../extensions/manifest");
11
11
  async function getExtensionVersion(i) {
12
12
  if (!i.extensionVersion) {
13
13
  if (!i.ref) {
@@ -52,14 +52,15 @@ async function want(args) {
52
52
  const instanceId = e[0];
53
53
  const ref = refs.parse(e[1]);
54
54
  ref.version = await resolveVersion(ref);
55
- const params = (0, params_1.readParams)({
55
+ const params = (0, manifest_1.readInstanceParam)({
56
56
  projectDir: args.projectDir,
57
57
  instanceId,
58
58
  projectId: args.projectId,
59
59
  projectNumber: args.projectNumber,
60
60
  aliases: args.aliases,
61
+ checkLocal: args.emulatorMode,
61
62
  });
62
- const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
63
+ const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
63
64
  const subbedParams = (0, extensionsHelper_1.substituteParams)(params, autoPopulatedParams);
64
65
  instanceSpecs.push({
65
66
  instanceId,
@@ -80,7 +81,7 @@ async function want(args) {
80
81
  }
81
82
  exports.want = want;
82
83
  async function resolveVersion(ref) {
83
- if (!ref.version || ref.version == "latest") {
84
+ if (!ref.version || ref.version === "latest") {
84
85
  return "latest";
85
86
  }
86
87
  const extensionRef = refs.toExtensionRef(ref);
@@ -5,7 +5,7 @@ const clc = require("cli-color");
5
5
  const extensionsApi = require("../../extensions/extensionsApi");
6
6
  const refs = require("../../extensions/refs");
7
7
  const utils = require("../../utils");
8
- const isRetryable = (err) => err.status == 429 || err.status == 409;
8
+ const isRetryable = (err) => err.status === 429 || err.status === 409;
9
9
  function extensionsDeploymentHandler(errorHandler) {
10
10
  return async (task) => {
11
11
  var _a, _b, _c, _d;