firebase-tools 9.17.0 → 9.21.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 (95) hide show
  1. package/CHANGELOG.md +3 -7
  2. package/lib/api.js +1 -0
  3. package/lib/apiv2.js +5 -3
  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 +1 -1
  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-publish.js +11 -4
  15. package/lib/commands/ext-dev-unpublish.js +12 -4
  16. package/lib/commands/ext-install.js +115 -48
  17. package/lib/commands/ext-uninstall.js +6 -0
  18. package/lib/commands/ext-update.js +61 -18
  19. package/lib/commands/functions-config-export.js +115 -0
  20. package/lib/commands/functions-delete.js +45 -25
  21. package/lib/commands/functions-list.js +54 -0
  22. package/lib/commands/functions-log.js +5 -22
  23. package/lib/commands/hosting-channel-deploy.js +6 -4
  24. package/lib/commands/index.js +12 -0
  25. package/lib/deploy/functions/backend.js +118 -121
  26. package/lib/deploy/functions/checkIam.js +8 -8
  27. package/lib/deploy/functions/containerCleaner.js +5 -1
  28. package/lib/deploy/functions/deploy.js +11 -15
  29. package/lib/deploy/functions/functionsDeployHelper.js +3 -68
  30. package/lib/deploy/functions/prepare.js +67 -33
  31. package/lib/deploy/functions/pricing.js +17 -17
  32. package/lib/deploy/functions/prompts.js +24 -41
  33. package/lib/deploy/functions/release/executor.js +39 -0
  34. package/lib/deploy/functions/release/fabricator.js +362 -0
  35. package/lib/deploy/functions/release/index.js +69 -0
  36. package/lib/deploy/functions/release/planner.js +159 -0
  37. package/lib/deploy/functions/release/reporter.js +162 -0
  38. package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
  39. package/lib/deploy/functions/release/timer.js +14 -0
  40. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +102 -126
  41. package/lib/deploy/functions/runtimes/node/parseTriggers.js +34 -50
  42. package/lib/deploy/functions/triggerRegionHelper.js +40 -0
  43. package/lib/deploy/functions/validate.js +1 -24
  44. package/lib/downloadUtils.js +37 -0
  45. package/lib/emulator/auth/apiSpec.js +1788 -403
  46. package/lib/emulator/auth/handlers.js +6 -5
  47. package/lib/emulator/auth/operations.js +439 -40
  48. package/lib/emulator/auth/server.js +32 -11
  49. package/lib/emulator/auth/state.js +205 -5
  50. package/lib/emulator/auth/widget_ui.js +2 -2
  51. package/lib/emulator/download.js +2 -31
  52. package/lib/emulator/downloadableEmulators.js +7 -7
  53. package/lib/emulator/emulatorLogger.js +0 -3
  54. package/lib/emulator/events/types.js +16 -0
  55. package/lib/emulator/functionsEmulator.js +117 -20
  56. package/lib/emulator/functionsEmulatorRuntime.js +46 -121
  57. package/lib/emulator/functionsEmulatorShared.js +51 -7
  58. package/lib/emulator/functionsEmulatorShell.js +1 -1
  59. package/lib/emulator/pubsubEmulator.js +61 -40
  60. package/lib/emulator/storage/cloudFunctions.js +37 -7
  61. package/lib/extensions/askUserForConsent.js +16 -13
  62. package/lib/extensions/askUserForParam.js +72 -3
  63. package/lib/extensions/billingMigrationHelper.js +1 -11
  64. package/lib/extensions/changelog.js +2 -1
  65. package/lib/extensions/displayExtensionInfo.js +35 -33
  66. package/lib/extensions/emulator/optionsHelper.js +3 -3
  67. package/lib/extensions/emulator/triggerHelper.js +2 -32
  68. package/lib/extensions/extensionsApi.js +67 -94
  69. package/lib/extensions/extensionsHelper.js +49 -35
  70. package/lib/extensions/paramHelper.js +79 -36
  71. package/lib/extensions/refs.js +59 -0
  72. package/lib/extensions/secretsUtils.js +58 -0
  73. package/lib/extensions/updateHelper.js +21 -45
  74. package/lib/extensions/warnings.js +1 -7
  75. package/lib/functional.js +64 -0
  76. package/lib/functions/env.js +26 -13
  77. package/lib/functions/functionslog.js +40 -0
  78. package/lib/functions/runtimeConfigExport.js +137 -0
  79. package/lib/gcp/cloudfunctions.js +46 -38
  80. package/lib/gcp/cloudfunctionsv2.js +47 -47
  81. package/lib/gcp/cloudlogging.js +27 -21
  82. package/lib/gcp/cloudscheduler.js +22 -16
  83. package/lib/gcp/pubsub.js +1 -9
  84. package/lib/gcp/secretManager.js +111 -0
  85. package/lib/gcp/storage.js +16 -0
  86. package/lib/previews.js +1 -1
  87. package/lib/requireInteractive.js +12 -0
  88. package/lib/utils.js +30 -1
  89. package/package.json +5 -4
  90. package/lib/deploy/functions/deploymentPlanner.js +0 -113
  91. package/lib/deploy/functions/deploymentTimer.js +0 -23
  92. package/lib/deploy/functions/errorHandler.js +0 -75
  93. package/lib/deploy/functions/release.js +0 -116
  94. package/lib/deploy/functions/tasks.js +0 -324
  95. package/lib/functionsDelete.js +0 -60
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readParamsFile = exports.promptForNewParams = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = void 0;
3
+ exports.readEnvFile = exports.getParamsFromFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = void 0;
4
4
  const _ = require("lodash");
5
5
  const path = require("path");
6
6
  const clc = require("cli-color");
@@ -25,70 +25,113 @@ function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
25
25
  return setNewDefaults(specParams, currentParams);
26
26
  }
27
27
  exports.getParamsWithCurrentValuesAsDefaults = getParamsWithCurrentValuesAsDefaults;
28
- async function getParams(projectId, paramSpecs, envFilePath) {
29
- let commandLineParams;
30
- if (envFilePath) {
31
- try {
32
- const buf = fs.readFileSync(path.resolve(envFilePath), "utf8");
33
- commandLineParams = dotenv.parse(buf.toString().trim(), { debug: true });
34
- track("Extension Env File", "Present");
35
- }
36
- catch (err) {
37
- track("Extension Env File", "Invalid");
38
- throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
39
- }
28
+ async function getParams(args) {
29
+ let params;
30
+ if (args.nonInteractive && !args.paramsEnvPath) {
31
+ const paramsMessage = args.paramSpecs
32
+ .map((p) => {
33
+ return `\t${p.param}${p.required ? "" : " (Optional)"}`;
34
+ })
35
+ .join("\n");
36
+ throw new error_1.FirebaseError("In non-interactive mode but no `--params` flag found. " +
37
+ "To install this extension in non-interactive mode, set `--params` to a path to an .env file" +
38
+ " containing values for this extension's params:\n" +
39
+ paramsMessage);
40
+ }
41
+ else if (args.paramsEnvPath) {
42
+ params = getParamsFromFile({
43
+ projectId: args.projectId,
44
+ paramSpecs: args.paramSpecs,
45
+ noninteractive: args.nonInteractive,
46
+ paramsEnvPath: args.paramsEnvPath,
47
+ });
40
48
  }
41
49
  else {
42
- track("Extension Env File", "Not Present");
50
+ const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
51
+ params = await askUserForParam.ask(args.projectId, args.instanceId, args.paramSpecs, firebaseProjectParams, !!args.reconfiguring);
43
52
  }
44
- const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(projectId);
53
+ track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
54
+ return params;
55
+ }
56
+ exports.getParams = getParams;
57
+ async function getParamsForUpdate(args) {
45
58
  let params;
46
- if (commandLineParams) {
47
- params = extensionsHelper_1.populateDefaultParams(commandLineParams, paramSpecs);
48
- extensionsHelper_1.validateCommandLineParams(params, paramSpecs);
59
+ if (args.nonInteractive && !args.paramsEnvPath) {
60
+ const paramsMessage = args.newSpec.params
61
+ .map((p) => {
62
+ return `\t${p.param}${p.required ? "" : " (Optional)"}`;
63
+ })
64
+ .join("\n");
65
+ throw new error_1.FirebaseError("In non-interactive mode but no `--params` flag found. " +
66
+ "To update this extension in non-interactive mode, set `--params` to a path to an .env file" +
67
+ " containing values for this extension's params:\n" +
68
+ paramsMessage);
69
+ }
70
+ else if (args.paramsEnvPath) {
71
+ params = getParamsFromFile({
72
+ projectId: args.projectId,
73
+ paramSpecs: args.newSpec.params,
74
+ noninteractive: args.nonInteractive,
75
+ paramsEnvPath: args.paramsEnvPath,
76
+ });
49
77
  }
50
78
  else {
51
- params = await askUserForParam.ask(paramSpecs, firebaseProjectParams);
79
+ params = await promptForNewParams({
80
+ spec: args.spec,
81
+ newSpec: args.newSpec,
82
+ currentParams: args.currentParams,
83
+ projectId: args.projectId,
84
+ instanceId: args.instanceId,
85
+ });
52
86
  }
53
87
  track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
54
88
  return params;
55
89
  }
56
- exports.getParams = getParams;
57
- async function promptForNewParams(spec, newSpec, currentParams, projectId) {
58
- const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(projectId);
90
+ exports.getParamsForUpdate = getParamsForUpdate;
91
+ async function promptForNewParams(args) {
92
+ const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
59
93
  const comparer = (param1, param2) => {
60
94
  return param1.type === param2.type && param1.param === param2.param;
61
95
  };
62
- let paramsDiffDeletions = _.differenceWith(spec.params, _.get(newSpec, "params", []), comparer);
96
+ let paramsDiffDeletions = _.differenceWith(args.spec.params, _.get(args.newSpec, "params", []), comparer);
63
97
  paramsDiffDeletions = extensionsHelper_1.substituteParams(paramsDiffDeletions, firebaseProjectParams);
64
- let paramsDiffAdditions = _.differenceWith(newSpec.params, _.get(spec, "params", []), comparer);
98
+ let paramsDiffAdditions = _.differenceWith(args.newSpec.params, _.get(args.spec, "params", []), comparer);
65
99
  paramsDiffAdditions = extensionsHelper_1.substituteParams(paramsDiffAdditions, firebaseProjectParams);
66
100
  if (paramsDiffDeletions.length) {
67
101
  logger_1.logger.info("The following params will no longer be used:");
68
102
  paramsDiffDeletions.forEach((param) => {
69
- logger_1.logger.info(clc.red(`- ${param.param}: ${currentParams[param.param.toUpperCase()]}`));
70
- delete currentParams[param.param.toUpperCase()];
103
+ logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
104
+ delete args.currentParams[param.param.toUpperCase()];
71
105
  });
72
106
  }
73
107
  if (paramsDiffAdditions.length) {
74
108
  logger_1.logger.info("To update this instance, configure the following new parameters:");
75
109
  for (const param of paramsDiffAdditions) {
76
- const chosenValue = await askUserForParam.askForParam(param);
77
- currentParams[param.param] = chosenValue;
110
+ const chosenValue = await askUserForParam.askForParam(args.projectId, args.instanceId, param, false);
111
+ args.currentParams[param.param] = chosenValue;
78
112
  }
79
113
  }
80
- return currentParams;
114
+ return args.currentParams;
81
115
  }
82
116
  exports.promptForNewParams = promptForNewParams;
83
- function readParamsFile(envFilePath) {
117
+ function getParamsFromFile(args) {
118
+ let envParams;
84
119
  try {
85
- const buf = fs.readFileSync(path.resolve(envFilePath), "utf8");
86
- return dotenv.parse(buf.toString().trim(), { debug: true });
120
+ envParams = readEnvFile(args.paramsEnvPath);
121
+ track("Extension Env File", "Present");
87
122
  }
88
123
  catch (err) {
89
- throw new error_1.FirebaseError(`Error reading --test-params file: ${err.message}\n`, {
90
- original: err,
91
- });
124
+ track("Extension Env File", "Invalid");
125
+ throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
92
126
  }
127
+ const params = extensionsHelper_1.populateDefaultParams(envParams, args.paramSpecs);
128
+ extensionsHelper_1.validateCommandLineParams(params, args.paramSpecs);
129
+ logger_1.logger.info(`Using param values from ${args.paramsEnvPath}`);
130
+ return params;
131
+ }
132
+ exports.getParamsFromFile = getParamsFromFile;
133
+ function readEnvFile(envPath) {
134
+ const buf = fs.readFileSync(path.resolve(envPath), "utf8");
135
+ return dotenv.parse(buf.toString().trim(), { debug: true });
93
136
  }
94
- exports.readParamsFile = readParamsFile;
137
+ exports.readEnvFile = readEnvFile;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toExtensionVersionName = exports.toExtensionName = exports.toExtensionVersionRef = exports.toExtensionRef = exports.parse = void 0;
4
+ const semver = require("semver");
5
+ const error_1 = require("../error");
6
+ const refRegex = new RegExp(/^([^/@\n]+)\/{1}([^/@\n]+)(@{1}([^\n]+)|)$/);
7
+ function parse(refOrName) {
8
+ const ret = parseRef(refOrName) || parseName(refOrName);
9
+ if (!ret || !ret.publisherId || !ret.extensionId) {
10
+ throw new error_1.FirebaseError(`Unable to parse ${refOrName} as an extension ref`);
11
+ }
12
+ if (ret.version &&
13
+ !semver.valid(ret.version) &&
14
+ !semver.validRange(ret.version) &&
15
+ ret.version !== "latest") {
16
+ throw new error_1.FirebaseError(`Extension reference ${ret} contains an invalid version ${ret.version}.`);
17
+ }
18
+ return ret;
19
+ }
20
+ exports.parse = parse;
21
+ function parseRef(ref) {
22
+ const parts = refRegex.exec(ref);
23
+ if (parts && (parts.length == 5 || parts.length == 7)) {
24
+ const publisherId = parts[1];
25
+ const extensionId = parts[2];
26
+ const version = parts[4];
27
+ return { publisherId, extensionId, version };
28
+ }
29
+ }
30
+ function parseName(name) {
31
+ const parts = name.split("/");
32
+ return {
33
+ publisherId: parts[1],
34
+ extensionId: parts[3],
35
+ version: parts[5],
36
+ };
37
+ }
38
+ function toExtensionRef(ref) {
39
+ return `${ref.publisherId}/${ref.extensionId}`;
40
+ }
41
+ exports.toExtensionRef = toExtensionRef;
42
+ function toExtensionVersionRef(ref) {
43
+ if (!ref.version) {
44
+ throw new error_1.FirebaseError(`Ref does not have a version`);
45
+ }
46
+ return `${ref.publisherId}/${ref.extensionId}@${ref.version}`;
47
+ }
48
+ exports.toExtensionVersionRef = toExtensionVersionRef;
49
+ function toExtensionName(ref) {
50
+ return `publishers/${ref.publisherId}/extensions/${ref.extensionId}`;
51
+ }
52
+ exports.toExtensionName = toExtensionName;
53
+ function toExtensionVersionName(ref) {
54
+ if (!ref.version) {
55
+ throw new error_1.FirebaseError(`Ref does not have a version`);
56
+ }
57
+ return `publishers/${ref.publisherId}/extensions/${ref.extensionId}/versions/${ref.version}`;
58
+ }
59
+ exports.toExtensionVersionName = toExtensionVersionName;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prettySecretName = exports.getSecretLabels = exports.getManagedSecrets = exports.grantFirexServiceAgentSecretAdminRole = exports.usesSecrets = exports.ensureSecretManagerApiEnabled = void 0;
4
+ const getProjectNumber_1 = require("../getProjectNumber");
5
+ const utils = require("../utils");
6
+ const ensureApiEnabled_1 = require("../ensureApiEnabled");
7
+ const projectUtils_1 = require("../projectUtils");
8
+ const extensionsApi = require("./extensionsApi");
9
+ const secretManagerApi = require("../gcp/secretManager");
10
+ const logger_1 = require("../logger");
11
+ const SECRET_LABEL = "firebase-extensions-managed";
12
+ async function ensureSecretManagerApiEnabled(options) {
13
+ const projectId = projectUtils_1.needProjectId(options);
14
+ return await ensureApiEnabled_1.ensure(projectId, "secretmanager.googleapis.com", "extensions", options.markdown);
15
+ }
16
+ exports.ensureSecretManagerApiEnabled = ensureSecretManagerApiEnabled;
17
+ function usesSecrets(spec) {
18
+ return spec.params && !!spec.params.find((p) => p.type == extensionsApi.ParamType.SECRET);
19
+ }
20
+ exports.usesSecrets = usesSecrets;
21
+ async function grantFirexServiceAgentSecretAdminRole(secret) {
22
+ const projectNumber = await getProjectNumber_1.getProjectNumber({ projectId: secret.projectId });
23
+ const firexSaProjectId = utils.envOverride("FIREBASE_EXTENSIONS_SA_PROJECT_ID", "gcp-sa-firebasemods");
24
+ const saEmail = `service-${projectNumber}@${firexSaProjectId}.iam.gserviceaccount.com`;
25
+ return secretManagerApi.grantServiceAgentRole(secret, saEmail, "roles/secretmanager.admin");
26
+ }
27
+ exports.grantFirexServiceAgentSecretAdminRole = grantFirexServiceAgentSecretAdminRole;
28
+ async function getManagedSecrets(instance) {
29
+ return (await Promise.all(getActiveSecrets(instance).map(async (secretResourceName) => {
30
+ const secret = secretManagerApi.parseSecretResourceName(secretResourceName);
31
+ const labels = await secretManagerApi.getSecretLabels(secret.projectId, secret.name);
32
+ if (labels && labels[SECRET_LABEL]) {
33
+ return secretResourceName;
34
+ }
35
+ return Promise.resolve("");
36
+ }))).filter((secretId) => !!secretId);
37
+ }
38
+ exports.getManagedSecrets = getManagedSecrets;
39
+ function getActiveSecrets(instance) {
40
+ return instance.config.source.spec.params
41
+ .map((p) => p.type == extensionsApi.ParamType.SECRET && instance.config.params[p.param])
42
+ .filter((pv) => !!pv);
43
+ }
44
+ function getSecretLabels(instanceId) {
45
+ const labels = {};
46
+ labels[SECRET_LABEL] = instanceId;
47
+ return labels;
48
+ }
49
+ exports.getSecretLabels = getSecretLabels;
50
+ function prettySecretName(secretResourceName) {
51
+ const nameTokens = secretResourceName.split("/");
52
+ if (nameTokens.length != 4 && nameTokens.length != 6) {
53
+ logger_1.logger.debug(`unable to parse secret secretResourceName: ${secretResourceName}`);
54
+ return secretResourceName;
55
+ }
56
+ return nameTokens.slice(0, 4).join("/");
57
+ }
58
+ exports.prettySecretName = prettySecretName;
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.confirmUpdate = exports.inferUpdateSource = 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");
@@ -25,7 +25,7 @@ async function getExistingSourceOrigin(projectId, instanceId, extensionName, exi
25
25
  : extensionsHelper_1.SourceOrigin.LOCAL;
26
26
  }
27
27
  exports.getExistingSourceOrigin = getExistingSourceOrigin;
28
- async function showUpdateVersionInfo(instanceId, from, to, source) {
28
+ function showUpdateVersionInfo(instanceId, from, to, source) {
29
29
  if (source) {
30
30
  source = clc.bold(source);
31
31
  }
@@ -34,8 +34,7 @@ async function showUpdateVersionInfo(instanceId, from, to, source) {
34
34
  }
35
35
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(from)} to ${source} (${clc.bold(to)})`);
36
36
  if (semver.lt(to, from)) {
37
- 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.");
38
- 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.");
39
38
  }
40
39
  return;
41
40
  }
@@ -54,21 +53,12 @@ function warningUpdateToOtherSource(sourceOrigin) {
54
53
  logger_1.logger.info(marked(warning));
55
54
  }
56
55
  exports.warningUpdateToOtherSource = warningUpdateToOtherSource;
57
- async function displayChanges(spec, newSpec) {
58
- logger_1.logger.info("This update contains the following changes (in green and red). " +
59
- "If at any point you choose not to continue, the extension will not be updated and the changes will be discarded:\n");
60
- displayExtensionInfo_1.displayUpdateChangesNoInput(spec, newSpec);
61
- 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);
62
60
  }
63
61
  exports.displayChanges = displayChanges;
64
- async function retryUpdate() {
65
- return prompt_1.promptOnce({
66
- type: "confirm",
67
- message: "Are you sure you wish to continue with updating anyways?",
68
- default: false,
69
- });
70
- }
71
- exports.retryUpdate = retryUpdate;
72
62
  async function update(updateOptions) {
73
63
  const { projectId, instanceId, source, extRef, params } = updateOptions;
74
64
  if (extRef) {
@@ -80,7 +70,7 @@ async function update(updateOptions) {
80
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.`);
81
71
  }
82
72
  exports.update = update;
83
- async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec, existingSource) {
73
+ async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec) {
84
74
  displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
85
75
  let source;
86
76
  try {
@@ -90,13 +80,12 @@ async function updateFromLocalSource(projectId, instanceId, localSource, existin
90
80
  throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, localSource));
91
81
  }
92
82
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a local source.")}`);
93
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
83
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
94
84
  warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.LOCAL);
95
- await confirmUpdate();
96
85
  return source.name;
97
86
  }
98
87
  exports.updateFromLocalSource = updateFromLocalSource;
99
- async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec, existingSource) {
88
+ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec) {
100
89
  displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
101
90
  let source;
102
91
  try {
@@ -106,18 +95,17 @@ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpe
106
95
  throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, urlSource));
107
96
  }
108
97
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a URL source.")}`);
109
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
98
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
110
99
  warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.URL);
111
- await confirmUpdate();
112
100
  return source.name;
113
101
  }
114
102
  exports.updateFromUrlSource = updateFromUrlSource;
115
- async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec, existingSource) {
103
+ async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec) {
116
104
  let source;
117
- const refObj = extensionsApi.parseRef(extVersionRef);
118
- const version = refObj.version;
119
- const extensionRef = `${refObj.publisherId}/${refObj.extensionId}`;
120
- displayExtensionInfo_1.displayExtInfo(instanceId, refObj.publisherId, existingSpec, true);
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);
121
109
  const extension = await extensionsApi.getExtension(extensionRef);
122
110
  try {
123
111
  source = await extensionsApi.getExtensionVersion(extVersionRef);
@@ -138,7 +126,7 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
138
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.`);
139
127
  }
140
128
  }
141
- await showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
129
+ showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
142
130
  warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION);
143
131
  const releaseNotes = await changelog.getReleaseNotesForUpdate({
144
132
  extensionRef,
@@ -148,12 +136,11 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
148
136
  if (Object.keys(releaseNotes).length) {
149
137
  changelog.displayReleaseNotes(releaseNotes, existingSpec.version);
150
138
  }
151
- await confirmUpdate();
152
139
  return source.name;
153
140
  }
154
141
  exports.updateToVersionFromPublisherSource = updateToVersionFromPublisherSource;
155
- async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec, existingSource) {
156
- return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec, existingSource);
142
+ async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec) {
143
+ return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec);
157
144
  }
158
145
  exports.updateFromPublisherSource = updateFromPublisherSource;
159
146
  function inferUpdateSource(updateSource, existingRef) {
@@ -174,14 +161,3 @@ function inferUpdateSource(updateSource, existingRef) {
174
161
  return updateSource;
175
162
  }
176
163
  exports.inferUpdateSource = inferUpdateSource;
177
- async function confirmUpdate() {
178
- const continueUpdate = await prompt_1.promptOnce({
179
- type: "confirm",
180
- message: "Do you wish to continue with this update?",
181
- default: false,
182
- });
183
- if (!continueUpdate) {
184
- throw new error_1.FirebaseError(`Update cancelled.`);
185
- }
186
- }
187
- exports.confirmUpdate = confirmUpdate;
@@ -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;