firebase-tools 9.16.6 → 9.20.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 (82) hide show
  1. package/CHANGELOG.md +1 -3
  2. package/lib/api.js +1 -0
  3. package/lib/apiv2.js +1 -1
  4. package/lib/appdistribution/client.js +84 -72
  5. package/lib/appdistribution/distribution.js +8 -26
  6. package/lib/appdistribution/options-parser-util.js +51 -0
  7. package/lib/command.js +8 -6
  8. package/lib/commands/appdistribution-distribute.js +74 -91
  9. package/lib/commands/appdistribution-testers-add.js +18 -0
  10. package/lib/commands/appdistribution-testers-remove.js +32 -0
  11. package/lib/commands/crashlytics-symbols-upload.js +146 -0
  12. package/lib/commands/ext-configure.js +9 -1
  13. package/lib/commands/ext-dev-extension-delete.js +2 -1
  14. package/lib/commands/ext-dev-init.js +18 -9
  15. package/lib/commands/ext-dev-publish.js +11 -4
  16. package/lib/commands/ext-dev-unpublish.js +2 -1
  17. package/lib/commands/ext-install.js +115 -48
  18. package/lib/commands/ext-uninstall.js +6 -0
  19. package/lib/commands/ext-update.js +67 -43
  20. package/lib/commands/functions-config-export.js +115 -0
  21. package/lib/commands/functions-delete.js +44 -35
  22. package/lib/commands/functions-list.js +54 -0
  23. package/lib/commands/functions-log.js +5 -22
  24. package/lib/commands/hosting-channel-deploy.js +6 -4
  25. package/lib/commands/index.js +12 -0
  26. package/lib/deploy/functions/backend.js +47 -12
  27. package/lib/deploy/functions/containerCleaner.js +5 -1
  28. package/lib/deploy/functions/deploy.js +7 -5
  29. package/lib/deploy/functions/prepare.js +9 -7
  30. package/lib/deploy/functions/prompts.js +3 -21
  31. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +2 -1
  32. package/lib/deploy/functions/runtimes/index.js +2 -1
  33. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +4 -3
  34. package/lib/deploy/functions/runtimes/node/parseTriggers.js +14 -9
  35. package/lib/deploy/functions/triggerRegionHelper.js +32 -0
  36. package/lib/downloadUtils.js +37 -0
  37. package/lib/emulator/auth/apiSpec.js +1758 -404
  38. package/lib/emulator/auth/handlers.js +6 -5
  39. package/lib/emulator/auth/operations.js +429 -40
  40. package/lib/emulator/auth/server.js +18 -11
  41. package/lib/emulator/auth/state.js +186 -5
  42. package/lib/emulator/auth/widget_ui.js +2 -2
  43. package/lib/emulator/download.js +2 -31
  44. package/lib/emulator/downloadableEmulators.js +7 -7
  45. package/lib/emulator/emulatorLogger.js +0 -3
  46. package/lib/emulator/events/types.js +16 -0
  47. package/lib/emulator/functionsEmulator.js +102 -17
  48. package/lib/emulator/functionsEmulatorRuntime.js +46 -121
  49. package/lib/emulator/functionsEmulatorShared.js +51 -7
  50. package/lib/emulator/functionsEmulatorShell.js +1 -1
  51. package/lib/emulator/pubsubEmulator.js +61 -40
  52. package/lib/extensions/askUserForConsent.js +16 -13
  53. package/lib/extensions/askUserForParam.js +72 -3
  54. package/lib/extensions/billingMigrationHelper.js +1 -11
  55. package/lib/extensions/changelog.js +93 -0
  56. package/lib/extensions/displayExtensionInfo.js +38 -38
  57. package/lib/extensions/emulator/optionsHelper.js +3 -3
  58. package/lib/extensions/emulator/triggerHelper.js +2 -32
  59. package/lib/extensions/extensionsApi.js +69 -95
  60. package/lib/extensions/extensionsHelper.js +75 -50
  61. package/lib/extensions/paramHelper.js +79 -36
  62. package/lib/extensions/refs.js +59 -0
  63. package/lib/extensions/resolveSource.js +2 -20
  64. package/lib/extensions/secretsUtils.js +58 -0
  65. package/lib/extensions/updateHelper.js +39 -105
  66. package/lib/extensions/warnings.js +1 -7
  67. package/lib/functional.js +64 -0
  68. package/lib/functions/env.js +26 -13
  69. package/lib/functions/functionslog.js +40 -0
  70. package/lib/functions/listFunctions.js +10 -0
  71. package/lib/functions/runtimeConfigExport.js +137 -0
  72. package/lib/gcp/cloudfunctions.js +84 -9
  73. package/lib/gcp/cloudfunctionsv2.js +99 -7
  74. package/lib/gcp/cloudlogging.js +27 -21
  75. package/lib/gcp/secretManager.js +111 -0
  76. package/lib/gcp/storage.js +16 -0
  77. package/lib/previews.js +1 -1
  78. package/lib/requireInteractive.js +12 -0
  79. package/package.json +5 -4
  80. package/schema/firebase-config.json +2 -1
  81. package/templates/extensions/CHANGELOG.md +7 -0
  82. package/templates/init/hosting/index.html +10 -10
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inferUpdateSource = exports.updateFromRegistryFile = exports.updateToVersionFromRegistryFile = exports.updateFromPublisherSource = exports.updateToVersionFromPublisherSource = exports.updateFromUrlSource = exports.updateFromLocalSource = exports.update = exports.retryUpdate = exports.displayChanges = exports.warningUpdateToOtherSource = exports.getExistingSourceOrigin = void 0;
3
+ exports.inferUpdateSource = exports.updateFromPublisherSource = exports.updateToVersionFromPublisherSource = exports.updateFromUrlSource = exports.updateFromLocalSource = exports.update = exports.displayChanges = exports.warningUpdateToOtherSource = exports.getExistingSourceOrigin = void 0;
4
4
  const clc = require("cli-color");
5
5
  const semver = require("semver");
6
+ const marked = require("marked");
6
7
  const error_1 = require("../error");
7
8
  const logger_1 = require("../logger");
8
9
  const resolveSource = require("./resolveSource");
9
10
  const extensionsApi = require("./extensionsApi");
10
- const prompt_1 = require("../prompt");
11
- const marked = require("marked");
11
+ const refs = require("./refs");
12
12
  const extensionsHelper_1 = require("./extensionsHelper");
13
13
  const utils = require("../utils");
14
14
  const displayExtensionInfo_1 = require("./displayExtensionInfo");
15
+ const changelog = require("./changelog");
15
16
  function invalidSourceErrMsgTemplate(instanceId, source) {
16
17
  return `Unable to update from the source \`${clc.bold(source)}\`. To update this instance, you can either:\n
17
18
  - Run \`${clc.bold("firebase ext:update " + instanceId)}\` to update from the published source.\n
@@ -19,26 +20,12 @@ function invalidSourceErrMsgTemplate(instanceId, source) {
19
20
  }
20
21
  async function getExistingSourceOrigin(projectId, instanceId, extensionName, existingSource) {
21
22
  const instance = await extensionsApi.getInstance(projectId, instanceId);
22
- if (instance && instance.config.extensionRef) {
23
- return extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION;
24
- }
25
- let existingSourceOrigin;
26
- try {
27
- const registryEntry = await resolveSource.resolveRegistryEntry(extensionName);
28
- if (resolveSource.isOfficialSource(registryEntry, existingSource)) {
29
- existingSourceOrigin = extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION;
30
- }
31
- else {
32
- existingSourceOrigin = extensionsHelper_1.SourceOrigin.LOCAL;
33
- }
34
- }
35
- catch (_a) {
36
- existingSourceOrigin = extensionsHelper_1.SourceOrigin.LOCAL;
37
- }
38
- return existingSourceOrigin;
23
+ return instance && instance.config.extensionRef
24
+ ? extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION
25
+ : extensionsHelper_1.SourceOrigin.LOCAL;
39
26
  }
40
27
  exports.getExistingSourceOrigin = getExistingSourceOrigin;
41
- async function showUpdateVersionInfo(instanceId, from, to, source) {
28
+ function showUpdateVersionInfo(instanceId, from, to, source) {
42
29
  if (source) {
43
30
  source = clc.bold(source);
44
31
  }
@@ -47,19 +34,13 @@ async function showUpdateVersionInfo(instanceId, from, to, source) {
47
34
  }
48
35
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(from)} to ${source} (${clc.bold(to)})`);
49
36
  if (semver.lt(to, from)) {
50
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, "The version you are updating to is less than the current version for this extension. This extension may not be backwards compatible.");
51
- return await displayExtensionInfo_1.getConsent("version", "Do you wish to continue?");
37
+ utils.logLabeledWarning(extensionsHelper_1.logPrefix, "The version you are updating to is less than the current version for this extension. This extension may not be backwards compatible.");
52
38
  }
53
39
  return;
54
40
  }
55
- async function warningUpdateToOtherSource(sourceOrigin) {
41
+ function warningUpdateToOtherSource(sourceOrigin) {
56
42
  let targetText;
57
- if ([
58
- extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION,
59
- extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION,
60
- extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION,
61
- extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION_VERSION,
62
- ].includes(sourceOrigin)) {
43
+ if ([extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(sourceOrigin)) {
63
44
  targetText = "published extension";
64
45
  }
65
46
  else if (sourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
@@ -70,31 +51,14 @@ async function warningUpdateToOtherSource(sourceOrigin) {
70
51
  }
71
52
  const warning = `All the instance's resources and logic will be overwritten to use the source code and files from the ${targetText}.\n`;
72
53
  logger_1.logger.info(marked(warning));
73
- const continueUpdate = await prompt_1.promptOnce({
74
- type: "confirm",
75
- message: "Do you wish to continue with this update?",
76
- default: false,
77
- });
78
- if (!continueUpdate) {
79
- throw new error_1.FirebaseError(`Update cancelled.`, { exit: 2 });
80
- }
81
54
  }
82
55
  exports.warningUpdateToOtherSource = warningUpdateToOtherSource;
83
- async function displayChanges(spec, newSpec, isOfficial = true) {
84
- logger_1.logger.info("This update contains the following changes (in green and red). " +
85
- "If at any point you choose not to continue, the extension will not be updated and the changes will be discarded:\n");
86
- displayExtensionInfo_1.displayUpdateChangesNoInput(spec, newSpec, isOfficial);
87
- await displayExtensionInfo_1.displayUpdateChangesRequiringConfirmation(spec, newSpec);
56
+ async function displayChanges(args) {
57
+ utils.logLabeledBullet("extensions", "This update contains the following changes:");
58
+ displayExtensionInfo_1.displayUpdateChangesNoInput(args.spec, args.newSpec);
59
+ await displayExtensionInfo_1.displayUpdateChangesRequiringConfirmation(args);
88
60
  }
89
61
  exports.displayChanges = displayChanges;
90
- async function retryUpdate() {
91
- return prompt_1.promptOnce({
92
- type: "confirm",
93
- message: "Are you sure you wish to continue with updating anyways?",
94
- default: false,
95
- });
96
- }
97
- exports.retryUpdate = retryUpdate;
98
62
  async function update(updateOptions) {
99
63
  const { projectId, instanceId, source, extRef, params } = updateOptions;
100
64
  if (extRef) {
@@ -106,7 +70,7 @@ async function update(updateOptions) {
106
70
  throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
107
71
  }
108
72
  exports.update = update;
109
- async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec, existingSource) {
73
+ async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec) {
110
74
  displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
111
75
  let source;
112
76
  try {
@@ -116,12 +80,12 @@ async function updateFromLocalSource(projectId, instanceId, localSource, existin
116
80
  throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, localSource));
117
81
  }
118
82
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a local source.")}`);
119
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
120
- await warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.LOCAL);
83
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
84
+ warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.LOCAL);
121
85
  return source.name;
122
86
  }
123
87
  exports.updateFromLocalSource = updateFromLocalSource;
124
- async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec, existingSource) {
88
+ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec) {
125
89
  displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
126
90
  let source;
127
91
  try {
@@ -131,17 +95,18 @@ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpe
131
95
  throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, urlSource));
132
96
  }
133
97
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a URL source.")}`);
134
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
135
- await warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.URL);
98
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
99
+ warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.URL);
136
100
  return source.name;
137
101
  }
138
102
  exports.updateFromUrlSource = updateFromUrlSource;
139
- async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec, existingSource) {
103
+ async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec) {
140
104
  let source;
141
- const refObj = extensionsApi.parseRef(extVersionRef);
142
- const version = refObj.version;
143
- displayExtensionInfo_1.displayExtInfo(instanceId, refObj.publisherId, existingSpec, true);
144
- const extension = await extensionsApi.getExtension(`${refObj.publisherId}/${refObj.extensionId}`);
105
+ const ref = refs.parse(extVersionRef);
106
+ const version = ref.version;
107
+ const extensionRef = refs.toExtensionRef(ref);
108
+ displayExtensionInfo_1.displayExtInfo(instanceId, ref.publisherId, existingSpec, true);
109
+ const extension = await extensionsApi.getExtension(extensionRef);
145
110
  try {
146
111
  source = await extensionsApi.getExtensionVersion(extVersionRef);
147
112
  }
@@ -149,66 +114,35 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
149
114
  throw new error_1.FirebaseError(`Could not find source '${clc.bold(extVersionRef)}' because (${clc.bold(version)}) is not a published version. To update, use the latest version of this extension (${clc.bold(extension.latestVersion)}).`);
150
115
  }
151
116
  let registryEntry;
152
- let sourceOrigin;
153
117
  try {
154
118
  registryEntry = await resolveSource.resolveRegistryEntry(existingSpec.name);
155
- sourceOrigin =
156
- registryEntry.publisher === refObj.publisherId
157
- ? extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION
158
- : extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION;
159
119
  }
160
120
  catch (err) {
161
- sourceOrigin = extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION;
121
+ logger_1.logger.debug(`Unable to fetch registry.json entry for ${existingSpec.name}`);
162
122
  }
163
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold(`You are updating this extension instance to a(n) ${sourceOrigin}.`)}`);
164
123
  if (registryEntry) {
165
124
  const minVer = resolveSource.getMinRequiredVersion(registryEntry);
166
125
  if (minVer && semver.gt(minVer, source.spec.version)) {
167
126
  throw new error_1.FirebaseError(`The version you are trying to update to (${clc.bold(source.spec.version)}) is less than the minimum version required (${clc.bold(minVer)}) to use this extension.`);
168
127
  }
169
128
  }
170
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
171
- await warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION);
172
- if (registryEntry) {
173
- await resolveSource.promptForUpdateWarnings(registryEntry, existingSpec.version, source.spec.version);
129
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
130
+ warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION);
131
+ const releaseNotes = await changelog.getReleaseNotesForUpdate({
132
+ extensionRef,
133
+ fromVersion: existingSpec.version,
134
+ toVersion: source.spec.version,
135
+ });
136
+ if (Object.keys(releaseNotes).length) {
137
+ changelog.displayReleaseNotes(releaseNotes, existingSpec.version);
174
138
  }
175
139
  return source.name;
176
140
  }
177
141
  exports.updateToVersionFromPublisherSource = updateToVersionFromPublisherSource;
178
- async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec, existingSource) {
179
- return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec, existingSource);
142
+ async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec) {
143
+ return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec);
180
144
  }
181
145
  exports.updateFromPublisherSource = updateFromPublisherSource;
182
- async function updateToVersionFromRegistryFile(projectId, instanceId, existingSpec, existingSource, version) {
183
- if (version !== "latest" && !semver.valid(version)) {
184
- throw new error_1.FirebaseError(`cannot update to invalid version ${version}`);
185
- }
186
- let registryEntry;
187
- try {
188
- registryEntry = await resolveSource.resolveRegistryEntry(existingSpec.name);
189
- }
190
- catch (err) {
191
- throw new error_1.FirebaseError(`Cannot find the latest version of this extension. To update this instance to a local source or URL source, run "firebase ext:update ${instanceId} <localSourceOrURL>".`);
192
- }
193
- displayExtensionInfo_1.displayExtInfo(instanceId, registryEntry.publisher, existingSpec, true);
194
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, clc.bold("You are updating this extension instance to an official source."));
195
- const minVer = resolveSource.getMinRequiredVersion(registryEntry);
196
- if (minVer) {
197
- if (version !== "latest" && semver.gt(minVer, version)) {
198
- throw new error_1.FirebaseError(`The version you are trying to upgrade to (${clc.bold(version)}) is less than the minimum version required (${clc.bold(minVer)}) to use this extension.`);
199
- }
200
- }
201
- const targetVersion = resolveSource.getTargetVersion(registryEntry, version);
202
- await showUpdateVersionInfo(instanceId, existingSpec.version, targetVersion);
203
- await warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION);
204
- await resolveSource.promptForUpdateWarnings(registryEntry, existingSpec.version, targetVersion);
205
- return resolveSource.resolveSourceUrl(registryEntry, existingSpec.name, targetVersion);
206
- }
207
- exports.updateToVersionFromRegistryFile = updateToVersionFromRegistryFile;
208
- async function updateFromRegistryFile(projectId, instanceId, existingSpec, existingSource) {
209
- return updateToVersionFromRegistryFile(projectId, instanceId, existingSpec, existingSource, "latest");
210
- }
211
- exports.updateFromRegistryFile = updateFromRegistryFile;
212
146
  function inferUpdateSource(updateSource, existingRef) {
213
147
  if (!updateSource) {
214
148
  return `${existingRef}@latest`;
@@ -7,7 +7,6 @@ const extensionsApi_1 = require("./extensionsApi");
7
7
  const displayExtensionInfo_1 = require("./displayExtensionInfo");
8
8
  const extensionsHelper_1 = require("./extensionsHelper");
9
9
  const resolveSource_1 = require("./resolveSource");
10
- const prompt_1 = require("../prompt");
11
10
  const utils = require("../utils");
12
11
  function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
13
12
  const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
@@ -32,12 +31,7 @@ async function displayWarningPrompts(publisherId, launchStage, extensionVersion)
32
31
  displayExperimentalWarning();
33
32
  }
34
33
  else {
35
- return true;
34
+ return;
36
35
  }
37
- return await prompt_1.promptOnce({
38
- type: "confirm",
39
- message: "Do you acknowledge the status of this extension?",
40
- default: true,
41
- });
42
36
  }
43
37
  exports.displayWarningPrompts = displayWarningPrompts;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.partition = exports.assertExhaustive = exports.zipIn = exports.zip = exports.reduceFlat = exports.flatten = exports.flattenArray = exports.flattenObject = void 0;
4
+ function* flattenObject(obj) {
5
+ function* helper(path, obj) {
6
+ for (const [k, v] of Object.entries(obj)) {
7
+ if (typeof v !== "object" || v === null) {
8
+ yield [[...path, k].join("."), v];
9
+ }
10
+ else {
11
+ yield* helper([...path, k], v);
12
+ }
13
+ }
14
+ }
15
+ yield* helper([], obj);
16
+ }
17
+ exports.flattenObject = flattenObject;
18
+ function* flattenArray(arr) {
19
+ for (const val of arr) {
20
+ if (Array.isArray(val)) {
21
+ yield* flattenArray(val);
22
+ }
23
+ else {
24
+ yield val;
25
+ }
26
+ }
27
+ }
28
+ exports.flattenArray = flattenArray;
29
+ function flatten(objOrArr) {
30
+ if (Array.isArray(objOrArr)) {
31
+ return flattenArray(objOrArr);
32
+ }
33
+ else {
34
+ return flattenObject(objOrArr);
35
+ }
36
+ }
37
+ exports.flatten = flatten;
38
+ function reduceFlat(accum, next) {
39
+ return [...(accum || []), ...flatten([next])];
40
+ }
41
+ exports.reduceFlat = reduceFlat;
42
+ function* zip(left, right) {
43
+ if (left.length != right.length) {
44
+ throw new Error("Cannot zip between two lists of differen lengths");
45
+ }
46
+ for (let i = 0; i < left.length; i++) {
47
+ yield [left[i], right[i]];
48
+ }
49
+ }
50
+ exports.zip = zip;
51
+ exports.zipIn = (other) => (elem, ndx) => {
52
+ return [elem, other[ndx]];
53
+ };
54
+ function assertExhaustive(val) {
55
+ throw new Error(`Never has a value (${val}). This should be impossible`);
56
+ }
57
+ exports.assertExhaustive = assertExhaustive;
58
+ function partition(arr, callbackFn) {
59
+ return arr.reduce((acc, elem) => {
60
+ acc[callbackFn(elem) ? 0 : 1].push(elem);
61
+ return acc;
62
+ }, [[], []]);
63
+ }
64
+ exports.partition = partition;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.hasUserEnvs = exports.validateKey = exports.parse = void 0;
3
+ exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.hasUserEnvs = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
4
4
  const clc = require("cli-color");
5
5
  const fs = require("fs");
6
6
  const path = require("path");
@@ -8,6 +8,7 @@ const error_1 = require("../error");
8
8
  const logger_1 = require("../logger");
9
9
  const previews_1 = require("../previews");
10
10
  const utils_1 = require("../utils");
11
+ const FUNCTIONS_EMULATOR_DOTENV = ".env.local";
11
12
  const RESERVED_KEYS = [
12
13
  "FIREBASE_CONFIG",
13
14
  "CLOUD_RUNTIME_CONFIG",
@@ -35,7 +36,7 @@ const LINE_RE = new RegExp("^" +
35
36
  "(" +
36
37
  "\\s*'(?:\\\\'|[^'])*'|" +
37
38
  '\\s*"(?:\\\\"|[^"])*"|' +
38
- "[^\\#\\r\\n]+" +
39
+ "[^#\\r\\n]+" +
39
40
  ")?" +
40
41
  "\\s*" +
41
42
  "(?:#[^\\n]*)?" +
@@ -71,17 +72,23 @@ function parse(data) {
71
72
  }
72
73
  exports.parse = parse;
73
74
  class KeyValidationError extends Error {
75
+ constructor(key, message) {
76
+ super(`Failed to validate key ${key}: ${message}`);
77
+ this.key = key;
78
+ this.message = message;
79
+ }
74
80
  }
81
+ exports.KeyValidationError = KeyValidationError;
75
82
  function validateKey(key) {
76
83
  if (RESERVED_KEYS.includes(key)) {
77
- throw new KeyValidationError(`Key ${key} is reserved for internal use.`);
84
+ throw new KeyValidationError(key, `Key ${key} is reserved for internal use.`);
78
85
  }
79
86
  if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
80
- throw new KeyValidationError(`Key ${key} must start with an uppercase ASCII letter or underscore` +
87
+ throw new KeyValidationError(key, `Key ${key} must start with an uppercase ASCII letter or underscore` +
81
88
  ", and then consist of uppercase ASCII letters, digits, and underscores.");
82
89
  }
83
90
  if (key.startsWith("X_GOOGLE_") || key.startsWith("FIREBASE_")) {
84
- throw new KeyValidationError(`Key ${key} starts with a reserved prefix (X_GOOGLE_ or FIREBASE_)`);
91
+ throw new KeyValidationError(key, `Key ${key} starts with a reserved prefix (X_GOOGLE_ or FIREBASE_)`);
85
92
  }
86
93
  }
87
94
  exports.validateKey = validateKey;
@@ -110,26 +117,32 @@ function parseStrict(data) {
110
117
  }
111
118
  return envs;
112
119
  }
113
- function findEnvfiles(functionsSource, projectId, projectAlias) {
114
- const files = [".env", `.env.${projectId}`];
115
- if (projectAlias && projectAlias.length) {
116
- files.push(`.env.${projectAlias}`);
120
+ function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
121
+ const files = [".env"];
122
+ if (isEmulator) {
123
+ files.push(FUNCTIONS_EMULATOR_DOTENV);
124
+ }
125
+ else {
126
+ files.push(`.env.${projectId}`);
127
+ if (projectAlias && projectAlias.length) {
128
+ files.push(`.env.${projectAlias}`);
129
+ }
117
130
  }
118
131
  return files
119
132
  .map((f) => path.join(functionsSource, f))
120
133
  .filter(fs.existsSync)
121
134
  .map((p) => path.basename(p));
122
135
  }
123
- function hasUserEnvs({ functionsSource, projectId, projectAlias }) {
124
- return findEnvfiles(functionsSource, projectId, projectAlias).length > 0;
136
+ function hasUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
137
+ return findEnvfiles(functionsSource, projectId, projectAlias, isEmulator).length > 0;
125
138
  }
126
139
  exports.hasUserEnvs = hasUserEnvs;
127
- function loadUserEnvs({ functionsSource, projectId, projectAlias, }) {
140
+ function loadUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
128
141
  var _a;
129
142
  if (!previews_1.previews.dotenv) {
130
143
  return {};
131
144
  }
132
- const envFiles = findEnvfiles(functionsSource, projectId, projectAlias);
145
+ const envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
133
146
  if (envFiles.length == 0) {
134
147
  return {};
135
148
  }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.logEntries = exports.getApiFilter = void 0;
4
+ const logger_1 = require("../logger");
5
+ const previews_1 = require("../previews");
6
+ function getApiFilter(functionList) {
7
+ const baseFilter = previews_1.previews.functionsv2
8
+ ? 'resource.type="cloud_function" OR ' +
9
+ '(resource.type="cloud_run_revision" AND ' +
10
+ 'labels."goog-managed-by"="cloudfunctions")'
11
+ : 'resource.type="cloud_function"';
12
+ if (functionList) {
13
+ const apiFuncFilters = functionList.split(",").map((fn) => {
14
+ return previews_1.previews.functionsv2
15
+ ? `resource.labels.function_name="${fn}" ` + `OR resource.labels.service_name="${fn}"`
16
+ : `resource.labels.function_name="${fn}"`;
17
+ });
18
+ return baseFilter + `\n(${apiFuncFilters.join(" OR ")})`;
19
+ }
20
+ return baseFilter;
21
+ }
22
+ exports.getApiFilter = getApiFilter;
23
+ function logEntries(entries) {
24
+ if (!entries || entries.length === 0) {
25
+ logger_1.logger.info("No log entries found.");
26
+ return;
27
+ }
28
+ for (let i = entries.length - 1; i >= 0; i--) {
29
+ const entry = entries[i];
30
+ const timestamp = entry.timestamp || "---";
31
+ const severity = (entry.severity || "?").substring(0, 1);
32
+ const name = entry.resource.labels.function_name || entry.resource.labels.service_name;
33
+ const message = entry.textPayload ||
34
+ JSON.stringify(entry.jsonPayload) ||
35
+ JSON.stringify(entry.protoPayload) ||
36
+ "";
37
+ logger_1.logger.info(`${timestamp} ${severity} ${name}: ${message}`);
38
+ }
39
+ }
40
+ exports.logEntries = logEntries;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listFunctions = void 0;
4
+ const backend = require("../deploy/functions/backend");
5
+ async function listFunctions(context) {
6
+ const functionSpecs = (await backend.existingBackend(context, true)).cloudFunctions;
7
+ functionSpecs.sort(backend.compareFunctions);
8
+ return { functions: functionSpecs };
9
+ }
10
+ exports.listFunctions = listFunctions;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDotenvFilename = exports.toDotenvFormat = exports.hydrateEnvs = exports.configToEnv = exports.convertKey = exports.hydrateConfigs = exports.getProjectInfos = void 0;
4
+ const clc = require("cli-color");
5
+ const env = require("./env");
6
+ const functionsConfig = require("../functionsConfig");
7
+ const error_1 = require("../error");
8
+ const logger_1 = require("../logger");
9
+ const projectUtils_1 = require("../projectUtils");
10
+ const rc_1 = require("../rc");
11
+ const utils_1 = require("../utils");
12
+ const functional_1 = require("../functional");
13
+ function getProjectInfos(options) {
14
+ const result = {};
15
+ const rc = rc_1.loadRC(options);
16
+ if (rc.projects) {
17
+ for (const [alias, projectId] of Object.entries(rc.projects)) {
18
+ if (Object.keys(result).includes(projectId)) {
19
+ utils_1.logWarning(`Multiple aliases found for ${clc.bold(projectId)}. ` +
20
+ `Preferring alias (${clc.bold(result[projectId])}) over (${clc.bold(alias)}).`);
21
+ continue;
22
+ }
23
+ result[projectId] = alias;
24
+ }
25
+ }
26
+ const projectId = projectUtils_1.getProjectId(options);
27
+ if (projectId && !Object.keys(result).includes(projectId)) {
28
+ result[projectId] = projectId;
29
+ }
30
+ return Object.entries(result).map(([k, v]) => {
31
+ const result = { projectId: k };
32
+ if (k !== v) {
33
+ result.alias = v;
34
+ }
35
+ return result;
36
+ });
37
+ }
38
+ exports.getProjectInfos = getProjectInfos;
39
+ async function hydrateConfigs(pInfos) {
40
+ const hydrate = pInfos.map((info) => {
41
+ return functionsConfig
42
+ .materializeAll(info.projectId)
43
+ .then((config) => {
44
+ info.config = config;
45
+ return;
46
+ })
47
+ .catch((err) => {
48
+ logger_1.logger.debug(`Failed to fetch runtime config for project ${info.projectId}: ${err.message}`);
49
+ });
50
+ });
51
+ await Promise.all(hydrate);
52
+ }
53
+ exports.hydrateConfigs = hydrateConfigs;
54
+ function convertKey(configKey, prefix) {
55
+ const baseKey = configKey
56
+ .toUpperCase()
57
+ .replace(/\./g, "_")
58
+ .replace(/-/g, "_");
59
+ let envKey = baseKey;
60
+ try {
61
+ env.validateKey(envKey);
62
+ }
63
+ catch (err) {
64
+ if (err instanceof env.KeyValidationError) {
65
+ envKey = prefix + envKey;
66
+ env.validateKey(envKey);
67
+ }
68
+ }
69
+ return envKey;
70
+ }
71
+ exports.convertKey = convertKey;
72
+ function configToEnv(configs, prefix) {
73
+ const success = [];
74
+ const errors = [];
75
+ for (const [configKey, value] of functional_1.flatten(configs)) {
76
+ try {
77
+ const envKey = convertKey(configKey, prefix);
78
+ success.push({ origKey: configKey, newKey: envKey, value: value });
79
+ }
80
+ catch (err) {
81
+ if (err instanceof env.KeyValidationError) {
82
+ errors.push({
83
+ origKey: configKey,
84
+ newKey: err.key,
85
+ err: err.message,
86
+ value: value,
87
+ });
88
+ }
89
+ else {
90
+ throw new error_1.FirebaseError("Unexpected error while converting config", {
91
+ exit: 2,
92
+ original: err,
93
+ });
94
+ }
95
+ }
96
+ }
97
+ return { success, errors };
98
+ }
99
+ exports.configToEnv = configToEnv;
100
+ function hydrateEnvs(pInfos, prefix) {
101
+ let errMsg = "";
102
+ for (const pInfo of pInfos) {
103
+ const { success, errors } = configToEnv(pInfo.config, prefix);
104
+ if (errors.length > 0) {
105
+ const msg = `${pInfo.projectId} ` +
106
+ `${pInfo.alias ? "(" + pInfo.alias + ")" : ""}:\n` +
107
+ errors.map((err) => `\t${err.origKey} => ${clc.bold(err.newKey)} (${err.err})`).join("\n") +
108
+ "\n";
109
+ errMsg += msg;
110
+ }
111
+ else {
112
+ pInfo.envs = success;
113
+ }
114
+ }
115
+ return errMsg;
116
+ }
117
+ exports.hydrateEnvs = hydrateEnvs;
118
+ function escape(s) {
119
+ const result = s
120
+ .replace("\n", "\\n")
121
+ .replace("\r", "\\r")
122
+ .replace("\t", "\\t")
123
+ .replace("\v", "\\v");
124
+ return result.replace(/(['"])/g, "\\$1");
125
+ }
126
+ function toDotenvFormat(envs, header = "") {
127
+ const lines = envs.map(({ newKey, value }) => `${newKey}="${escape(value)}"`);
128
+ const maxLineLen = Math.max(...lines.map((l) => l.length));
129
+ return (`${header}\n` +
130
+ lines.map((line, idx) => `${line.padEnd(maxLineLen)} # from ${envs[idx].origKey}`).join("\n"));
131
+ }
132
+ exports.toDotenvFormat = toDotenvFormat;
133
+ function generateDotenvFilename(pInfo) {
134
+ var _a;
135
+ return `.env.${(_a = pInfo.alias) !== null && _a !== void 0 ? _a : pInfo.projectId}`;
136
+ }
137
+ exports.generateDotenvFilename = generateDotenvFilename;