firebase-tools 10.5.0 → 10.7.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 (105) hide show
  1. package/lib/command.js +4 -4
  2. package/lib/commands/deploy.js +1 -1
  3. package/lib/commands/emulators-start.js +7 -2
  4. package/lib/commands/ext-configure.js +15 -5
  5. package/lib/commands/ext-export.js +6 -5
  6. package/lib/commands/ext-install.js +28 -44
  7. package/lib/commands/ext-update.js +9 -1
  8. package/lib/commands/functions-delete.js +7 -3
  9. package/lib/commands/functions-secrets-destroy.js +23 -3
  10. package/lib/commands/functions-secrets-prune.js +15 -12
  11. package/lib/commands/functions-secrets-set.js +51 -4
  12. package/lib/commands/hosting-channel-deploy.js +2 -2
  13. package/lib/deploy/database/deploy.js +4 -0
  14. package/lib/deploy/database/index.js +1 -0
  15. package/lib/deploy/extensions/deploy.js +4 -4
  16. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  17. package/lib/deploy/extensions/planner.js +36 -9
  18. package/lib/deploy/extensions/prepare.js +1 -1
  19. package/lib/deploy/extensions/secrets.js +2 -2
  20. package/lib/deploy/extensions/tasks.js +60 -21
  21. package/lib/deploy/functions/backend.js +37 -6
  22. package/lib/deploy/functions/build.js +162 -0
  23. package/lib/deploy/functions/checkIam.js +10 -6
  24. package/lib/deploy/functions/deploy.js +49 -28
  25. package/lib/deploy/functions/ensure.js +4 -4
  26. package/lib/deploy/functions/functionsDeployHelper.js +99 -24
  27. package/lib/deploy/functions/prepare.js +130 -62
  28. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  29. package/lib/deploy/functions/pricing.js +6 -3
  30. package/lib/deploy/functions/prompts.js +1 -7
  31. package/lib/deploy/functions/release/fabricator.js +70 -28
  32. package/lib/deploy/functions/release/index.js +41 -6
  33. package/lib/deploy/functions/release/planner.js +19 -12
  34. package/lib/deploy/functions/release/reporter.js +14 -11
  35. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  36. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  37. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  40. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  41. package/lib/deploy/functions/services/auth.js +95 -0
  42. package/lib/deploy/functions/services/index.js +41 -21
  43. package/lib/deploy/functions/services/storage.js +1 -6
  44. package/lib/deploy/functions/validate.js +32 -6
  45. package/lib/deploy/hosting/args.js +2 -0
  46. package/lib/deploy/hosting/convertConfig.js +39 -8
  47. package/lib/deploy/hosting/deploy.js +3 -3
  48. package/lib/deploy/hosting/prepare.js +2 -2
  49. package/lib/deploy/hosting/release.js +6 -2
  50. package/lib/deploy/index.js +82 -93
  51. package/lib/deploy/remoteconfig/deploy.js +4 -0
  52. package/lib/deploy/remoteconfig/index.js +3 -1
  53. package/lib/emulator/auth/operations.js +5 -0
  54. package/lib/emulator/auth/utils.js +3 -25
  55. package/lib/emulator/controller.js +17 -14
  56. package/lib/emulator/downloadableEmulators.js +39 -23
  57. package/lib/emulator/extensions/postinstall.js +41 -0
  58. package/lib/emulator/extensions/validation.js +2 -2
  59. package/lib/emulator/extensionsEmulator.js +85 -21
  60. package/lib/emulator/functionsEmulator.js +88 -10
  61. package/lib/emulator/functionsEmulatorShared.js +37 -21
  62. package/lib/emulator/functionsEmulatorShell.js +2 -3
  63. package/lib/emulator/pubsubEmulator.js +13 -9
  64. package/lib/emulator/registry.js +34 -12
  65. package/lib/emulator/storage/apis/firebase.js +13 -8
  66. package/lib/emulator/storage/apis/gcloud.js +15 -9
  67. package/lib/emulator/storage/files.js +14 -3
  68. package/lib/emulator/storage/index.js +9 -1
  69. package/lib/emulator/storage/metadata.js +18 -8
  70. package/lib/emulator/storage/rules/manager.js +7 -17
  71. package/lib/emulator/storage/server.js +38 -12
  72. package/lib/ensureApiEnabled.js +8 -4
  73. package/lib/extensions/askUserForParam.js +14 -11
  74. package/lib/extensions/changelog.js +1 -1
  75. package/lib/extensions/emulator/optionsHelper.js +9 -10
  76. package/lib/extensions/emulator/specHelper.js +7 -1
  77. package/lib/extensions/emulator/triggerHelper.js +11 -14
  78. package/lib/extensions/extensionsApi.js +2 -1
  79. package/lib/extensions/extensionsHelper.js +30 -24
  80. package/lib/extensions/manifest.js +28 -8
  81. package/lib/extensions/paramHelper.js +19 -13
  82. package/lib/extensions/provisioningHelper.js +2 -2
  83. package/lib/extensions/warnings.js +3 -3
  84. package/lib/functions/env.js +10 -2
  85. package/lib/functions/events/index.js +7 -0
  86. package/lib/functions/events/v1.js +6 -0
  87. package/lib/functions/projectConfig.js +32 -6
  88. package/lib/functions/runtimeConfigExport.js +10 -6
  89. package/lib/functions/secrets.js +99 -6
  90. package/lib/functionsShellCommandAction.js +1 -1
  91. package/lib/gcp/cloudfunctions.js +44 -18
  92. package/lib/gcp/cloudfunctionsv2.js +48 -25
  93. package/lib/gcp/cloudtasks.js +5 -3
  94. package/lib/gcp/identityPlatform.js +44 -0
  95. package/lib/gcp/secretManager.js +2 -2
  96. package/lib/metaprogramming.js +2 -0
  97. package/lib/previews.js +1 -1
  98. package/lib/serve/functions.js +16 -19
  99. package/lib/serve/hosting.js +25 -12
  100. package/lib/serve/index.js +6 -0
  101. package/lib/track.js +15 -21
  102. package/lib/utils.js +30 -1
  103. package/npm-shrinkwrap.json +44 -2
  104. package/package.json +4 -1
  105. package/schema/firebase-config.json +6 -0
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
3
+ exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
4
4
  const _ = require("lodash");
5
5
  const clc = require("cli-color");
6
6
  const ora = require("ora");
@@ -305,7 +305,7 @@ async function publishExtensionVersionFromLocalSource(args) {
305
305
  "Please create one and add an entry for this version. " +
306
306
  marked("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
307
307
  }
308
- if (!notes && extension) {
308
+ if (!notes && !semver.prerelease(extensionSpec.version) && extension) {
309
309
  throw new error_1.FirebaseError(`No entry for version ${extensionSpec.version} found in CHANGELOG.md. ` +
310
310
  "Please add one so users know what has changed in this version. " +
311
311
  marked("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
@@ -363,32 +363,26 @@ async function publishExtensionVersionFromLocalSource(args) {
363
363
  }
364
364
  exports.publishExtensionVersionFromLocalSource = publishExtensionVersionFromLocalSource;
365
365
  async function createSourceFromLocation(projectId, sourceUri) {
366
+ const extensionRoot = "/";
366
367
  let packageUri;
367
- let extensionRoot;
368
368
  let objectPath = "";
369
- if (!sourceUri.startsWith("https:")) {
370
- const uploadSpinner = ora(" Archiving and uploading extension source code");
371
- try {
372
- uploadSpinner.start();
373
- objectPath = await archiveAndUploadSource(sourceUri, exports.EXTENSIONS_BUCKET_NAME);
374
- uploadSpinner.succeed(" Uploaded extension source code");
375
- packageUri = api_1.storageOrigin + objectPath + "?alt=media";
376
- extensionRoot = "/";
377
- }
378
- catch (err) {
379
- uploadSpinner.fail();
380
- throw new error_1.FirebaseError(`Failed to archive and upload extension source, ${err}`, {
381
- original: err,
382
- });
383
- }
369
+ const spinner = ora(" Archiving and uploading extension source code");
370
+ try {
371
+ spinner.start();
372
+ objectPath = await archiveAndUploadSource(sourceUri, exports.EXTENSIONS_BUCKET_NAME);
373
+ spinner.succeed(" Uploaded extension source code");
374
+ packageUri = api_1.storageOrigin + objectPath + "?alt=media";
375
+ const res = await (0, extensionsApi_1.createSource)(projectId, packageUri, extensionRoot);
376
+ logger_1.logger.debug("Created new Extension Source %s", res.name);
377
+ await deleteUploadedSource(objectPath);
378
+ return res;
384
379
  }
385
- else {
386
- [packageUri, extensionRoot] = sourceUri.split("#");
380
+ catch (err) {
381
+ spinner.fail();
382
+ throw new error_1.FirebaseError(`Failed to archive and upload extension source from ${sourceUri}, ${err}`, {
383
+ original: err,
384
+ });
387
385
  }
388
- const res = await (0, extensionsApi_1.createSource)(projectId, packageUri, extensionRoot);
389
- logger_1.logger.debug("Created new Extension Source %s", res.name);
390
- await deleteUploadedSource(objectPath);
391
- return res;
392
386
  }
393
387
  exports.createSourceFromLocation = createSourceFromLocation;
394
388
  async function deleteUploadedSource(objectPath) {
@@ -535,3 +529,15 @@ async function diagnoseAndFixProject(options) {
535
529
  }
536
530
  }
537
531
  exports.diagnoseAndFixProject = diagnoseAndFixProject;
532
+ function canonicalizeRefInput(extensionName) {
533
+ if (extensionName.split("/").length < 2) {
534
+ const [extensionID, version] = extensionName.split("@");
535
+ extensionName = `firebase/${extensionID}@${version || "latest"}`;
536
+ }
537
+ const ref = refs.parse(extensionName);
538
+ if (!ref.version) {
539
+ extensionName = `${extensionName}@latest`;
540
+ }
541
+ return extensionName;
542
+ }
543
+ exports.canonicalizeRefInput = canonicalizeRefInput;
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
3
+ exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.getInstanceTarget = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
4
4
  const clc = require("cli-color");
5
5
  const path = require("path");
6
6
  const refs = require("./refs");
7
7
  const config_1 = require("../config");
8
+ const planner_1 = require("../deploy/extensions/planner");
8
9
  const logger_1 = require("../logger");
9
10
  const prompt_1 = require("../prompt");
10
11
  const paramHelper_1 = require("./paramHelper");
@@ -42,11 +43,12 @@ async function writeToManifest(specs, config, options, allowOverwrite = false) {
42
43
  exports.writeToManifest = writeToManifest;
43
44
  async function writeLocalSecrets(specs, config, force) {
44
45
  for (const spec of specs) {
45
- if (!spec.paramSpecs) {
46
+ const extensionSpec = await (0, planner_1.getExtensionSpec)(spec);
47
+ if (!extensionSpec.params) {
46
48
  continue;
47
49
  }
48
50
  const writeBuffer = {};
49
- const locallyOverridenSecretParams = spec.paramSpecs.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
51
+ const locallyOverridenSecretParams = extensionSpec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
50
52
  for (const paramSpec of locallyOverridenSecretParams) {
51
53
  const key = paramSpec.param;
52
54
  const localValue = spec.params[key].local;
@@ -97,18 +99,35 @@ function instanceExists(instanceId, config) {
97
99
  return !!config.get("extensions", {})[instanceId];
98
100
  }
99
101
  exports.instanceExists = instanceExists;
100
- function getInstanceRef(instanceId, config) {
102
+ function getInstanceTarget(instanceId, config) {
101
103
  if (!instanceExists(instanceId, config)) {
102
104
  throw new error_1.FirebaseError(`Could not find extension instance ${instanceId} in firebase.json`);
103
105
  }
104
- const ref = config.get("extensions", {})[instanceId];
105
- return refs.parse(ref);
106
+ return config.get("extensions", {})[instanceId];
107
+ }
108
+ exports.getInstanceTarget = getInstanceTarget;
109
+ function getInstanceRef(instanceId, config) {
110
+ const source = getInstanceTarget(instanceId, config);
111
+ if ((0, extensionsHelper_1.isLocalPath)(source)) {
112
+ throw new error_1.FirebaseError(`Extension instance ${instanceId} doesn't have a ref because it is from a local source`);
113
+ }
114
+ return refs.parse(source);
106
115
  }
107
116
  exports.getInstanceRef = getInstanceRef;
108
117
  function writeExtensionsToFirebaseJson(specs, config) {
109
118
  const extensions = config.get("extensions", {});
110
119
  for (const s of specs) {
111
- extensions[s.instanceId] = refs.toExtensionVersionRef(s.ref);
120
+ let target;
121
+ if (s.ref) {
122
+ target = refs.toExtensionVersionRef(s.ref);
123
+ }
124
+ else if (s.localPath) {
125
+ target = s.localPath;
126
+ }
127
+ else {
128
+ throw new error_1.FirebaseError(`Unable to resolve ManifestInstanceSpec, make sure you provide either extension ref or a local path to extension source code`);
129
+ }
130
+ extensions[s.instanceId] = target;
112
131
  }
113
132
  config.set("extensions", extensions);
114
133
  config.writeProjectFile("firebase.json", config.src);
@@ -117,6 +136,7 @@ function writeExtensionsToFirebaseJson(specs, config) {
117
136
  async function writeEnvFiles(specs, config, force) {
118
137
  for (const spec of specs) {
119
138
  const content = Object.entries(spec.params)
139
+ .filter((r) => r[1].baseValue !== "")
120
140
  .sort((a, b) => {
121
141
  return a[0].localeCompare(b[0]);
122
142
  })
@@ -170,7 +190,7 @@ function showDeprecationWarning() {
170
190
  }
171
191
  exports.showDeprecationWarning = showDeprecationWarning;
172
192
  function showPreviewWarning() {
173
- utils.logLabeledWarning(extensionsHelper_1.logPrefix, "These changes will be reflected in your Firebase Emulator after restart. " +
193
+ utils.logLabeledWarning(extensionsHelper_1.logPrefix, `See these changes in your Firebase Emulator by running "firebase emulators:start". ` +
174
194
  `Run ${clc.bold("firebase deploy (--only extensions)")} to deploy the changes to your Firebase project. `);
175
195
  }
176
196
  exports.showPreviewWarning = showPreviewWarning;
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
4
- const _ = require("lodash");
5
4
  const path = require("path");
6
5
  const clc = require("cli-color");
7
6
  const fs = require("fs-extra");
@@ -9,8 +8,9 @@ const error_1 = require("../error");
9
8
  const logger_1 = require("../logger");
10
9
  const extensionsHelper_1 = require("./extensionsHelper");
11
10
  const askUserForParam = require("./askUserForParam");
12
- const track = require("../track");
11
+ const track_1 = require("../track");
13
12
  const env = require("../functions/env");
13
+ const utils_1 = require("../utils");
14
14
  function getBaseParamBindings(params) {
15
15
  let ret = {};
16
16
  for (const [k, v] of Object.entries(params)) {
@@ -37,8 +37,9 @@ function setNewDefaults(params, newDefaults) {
37
37
  }
38
38
  exports.setNewDefaults = setNewDefaults;
39
39
  function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
40
- const specParams = _.cloneDeep(_.get(extensionInstance, "config.source.spec.params", []));
41
- const currentParams = _.cloneDeep(_.get(extensionInstance, "config.params", {}));
40
+ var _a, _b, _c, _d;
41
+ const specParams = (0, utils_1.cloneDeep)(((_c = (_b = (_a = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _a === void 0 ? void 0 : _a.source) === null || _b === void 0 ? void 0 : _b.spec) === null || _c === void 0 ? void 0 : _c.params) || []);
42
+ const currentParams = (0, utils_1.cloneDeep)(((_d = extensionInstance === null || extensionInstance === void 0 ? void 0 : extensionInstance.config) === null || _d === void 0 ? void 0 : _d.params) || {});
42
43
  return setNewDefaults(specParams, currentParams);
43
44
  }
44
45
  exports.getParamsWithCurrentValuesAsDefaults = getParamsWithCurrentValuesAsDefaults;
@@ -71,7 +72,8 @@ async function getParams(args) {
71
72
  reconfiguring: !!args.reconfiguring,
72
73
  });
73
74
  }
74
- void track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
75
+ const paramNames = Object.keys(params);
76
+ void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
75
77
  return params;
76
78
  }
77
79
  exports.getParams = getParams;
@@ -103,27 +105,31 @@ async function getParamsForUpdate(args) {
103
105
  instanceId: args.instanceId,
104
106
  });
105
107
  }
106
- void track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
108
+ const paramNames = Object.keys(params);
109
+ void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
107
110
  return params;
108
111
  }
109
112
  exports.getParamsForUpdate = getParamsForUpdate;
110
113
  async function promptForNewParams(args) {
111
114
  const newParamBindingOptions = buildBindingOptionsWithBaseValue(args.currentParams);
112
115
  const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
113
- const comparer = (param1, param2) => {
116
+ const sameParam = (param1) => (param2) => {
114
117
  return param1.type === param2.type && param1.param === param2.param;
115
118
  };
119
+ const paramDiff = (left, right) => {
120
+ return left.filter((aLeft) => !right.find(sameParam(aLeft)));
121
+ };
116
122
  const oldParams = args.spec.params.filter((p) => Object.keys(args.currentParams).includes(p.param));
117
- let paramsDiffDeletions = _.differenceWith(oldParams, args.newSpec.params, comparer);
123
+ let paramsDiffDeletions = paramDiff(oldParams, args.newSpec.params);
118
124
  paramsDiffDeletions = (0, extensionsHelper_1.substituteParams)(paramsDiffDeletions, firebaseProjectParams);
119
- let paramsDiffAdditions = _.differenceWith(args.newSpec.params, oldParams, comparer);
125
+ let paramsDiffAdditions = paramDiff(args.newSpec.params, oldParams);
120
126
  paramsDiffAdditions = (0, extensionsHelper_1.substituteParams)(paramsDiffAdditions, firebaseProjectParams);
121
127
  if (paramsDiffDeletions.length) {
122
128
  logger_1.logger.info("The following params will no longer be used:");
123
- paramsDiffDeletions.forEach((param) => {
129
+ for (const param of paramsDiffDeletions) {
124
130
  logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
125
131
  delete newParamBindingOptions[param.param.toUpperCase()];
126
- });
132
+ }
127
133
  }
128
134
  if (paramsDiffAdditions.length) {
129
135
  logger_1.logger.info("To update this instance, configure the following new parameters:");
@@ -144,10 +150,10 @@ function getParamsFromFile(args) {
144
150
  let envParams;
145
151
  try {
146
152
  envParams = readEnvFile(args.paramsEnvPath);
147
- void track("Extension Env File", "Present");
153
+ void (0, track_1.track)("Extension Env File", "Present");
148
154
  }
149
155
  catch (err) {
150
- void track("Extension Env File", "Invalid");
156
+ void (0, track_1.track)("Extension Env File", "Invalid");
151
157
  throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
152
158
  }
153
159
  const params = (0, extensionsHelper_1.populateDefaultParams)(envParams, args.paramSpecs);
@@ -19,8 +19,8 @@ async function checkProductsProvisioned(projectId, spec) {
19
19
  exports.checkProductsProvisioned = checkProductsProvisioned;
20
20
  async function bulkCheckProductsProvisioned(projectId, instanceSpecs) {
21
21
  const usedProducts = await Promise.all(instanceSpecs.map(async (i) => {
22
- const extensionVersion = await (0, planner_1.getExtensionVersion)(i);
23
- return getUsedProducts(extensionVersion.spec);
22
+ const extensionSpec = await (0, planner_1.getExtensionSpec)(i);
23
+ return getUsedProducts(extensionSpec);
24
24
  }));
25
25
  await checkProducts(projectId, [...(0, functional_1.flattenArray)(usedProducts)]);
26
26
  }
@@ -49,11 +49,11 @@ const toListEntry = (i) => {
49
49
  };
50
50
  async function displayWarningsForDeploy(instancesToCreate) {
51
51
  const trustedPublishers = await (0, resolveSource_1.getTrustedPublishers)();
52
- for (const i of instancesToCreate) {
52
+ const publishedExtensionInstances = instancesToCreate.filter((i) => i.ref);
53
+ for (const i of publishedExtensionInstances) {
53
54
  await (0, planner_1.getExtension)(i);
54
- await (0, planner_1.getExtensionVersion)(i);
55
55
  }
56
- const [eapExtensions, nonEapExtensions] = (0, functional_1.partition)(instancesToCreate, (i) => { var _a, _b; return !trustedPublishers.includes((_b = (_a = i.ref) === null || _a === void 0 ? void 0 : _a.publisherId) !== null && _b !== void 0 ? _b : ""); });
56
+ const [eapExtensions, nonEapExtensions] = (0, functional_1.partition)(publishedExtensionInstances, (i) => { var _a, _b; return !trustedPublishers.includes((_b = (_a = i.ref) === null || _a === void 0 ? void 0 : _a.publisherId) !== null && _b !== void 0 ? _b : ""); });
57
57
  const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
58
58
  if (experimental.length) {
59
59
  const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
@@ -42,6 +42,15 @@ const LINE_RE = new RegExp("^" +
42
42
  "\\s*" +
43
43
  "(?:#[^\\n]*)?" +
44
44
  "$", "gms");
45
+ const ESCAPE_SEQUENCES_TO_CHARACTERS = {
46
+ "\\n": "\n",
47
+ "\\r": "\r",
48
+ "\\t": "\t",
49
+ "\\v": "\v",
50
+ "\\\\": "\\",
51
+ "\\'": "'",
52
+ '\\"': '"',
53
+ };
45
54
  function parse(data) {
46
55
  const envs = {};
47
56
  const errors = [];
@@ -54,8 +63,7 @@ function parse(data) {
54
63
  if ((quotesMatch = /^(["'])(.*)\1$/ms.exec(v)) != null) {
55
64
  v = quotesMatch[2];
56
65
  if (quotesMatch[1] === '"') {
57
- v = v.replace("\\n", "\n").replace("\\r", "\r").replace("\\t", "\t").replace("\\v", "\v");
58
- v = v.replace(/\\([\\'"])/g, "$1");
66
+ v = v.replace(/\\[nrtv\\'"]/g, (match) => ESCAPE_SEQUENCES_TO_CHARACTERS[match]);
59
67
  }
60
68
  }
61
69
  envs[k] = v;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.v2 = exports.v1 = void 0;
4
+ const v1 = require("./v1");
5
+ exports.v1 = v1;
6
+ const v2 = require("./v2");
7
+ exports.v2 = v2;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AUTH_BLOCKING_EVENTS = exports.BEFORE_SIGN_IN_EVENT = exports.BEFORE_CREATE_EVENT = void 0;
4
+ exports.BEFORE_CREATE_EVENT = "providers/cloud.auth/eventTypes/user.beforeCreate";
5
+ exports.BEFORE_SIGN_IN_EVENT = "providers/cloud.auth/eventTypes/user.beforeSignIn";
6
+ exports.AUTH_BLOCKING_EVENTS = [exports.BEFORE_CREATE_EVENT, exports.BEFORE_SIGN_IN_EVENT];
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeAndValidate = exports.validate = exports.normalize = void 0;
3
+ exports.configForCodebase = exports.normalizeAndValidate = exports.validate = exports.normalize = exports.DEFAULT_CODEBASE = void 0;
4
4
  const error_1 = require("../error");
5
+ exports.DEFAULT_CODEBASE = "default";
5
6
  function normalize(config) {
6
7
  if (!config) {
7
8
  throw new error_1.FirebaseError("No valid functions configuration detected in firebase.json");
@@ -19,16 +20,41 @@ function validateSingle(config) {
19
20
  if (!config.source) {
20
21
  throw new error_1.FirebaseError("functions.source must be specified");
21
22
  }
22
- return Object.assign(Object.assign({}, config), { source: config.source });
23
+ if (!config.codebase) {
24
+ config.codebase = exports.DEFAULT_CODEBASE;
25
+ }
26
+ if (config.codebase.length > 63 || !/^[a-z0-9_-]+$/.test(config.codebase)) {
27
+ throw new error_1.FirebaseError("Invalid codebase name. Codebase must be less than 63 characters and " +
28
+ "can contain only lowercase letters, numeric characters, underscores, and dashes.");
29
+ }
30
+ return Object.assign(Object.assign({}, config), { source: config.source, codebase: config.codebase });
23
31
  }
24
- function validate(config) {
25
- if (config.length > 1) {
26
- throw new error_1.FirebaseError("More than one functions.source detected in firebase.json.");
32
+ function assertUnique(config, property) {
33
+ const values = new Set();
34
+ for (const single of config) {
35
+ const value = single[property];
36
+ if (values.has(value)) {
37
+ throw new error_1.FirebaseError(`functions.${property} must be unique but '${value}' was used more than once.`);
38
+ }
39
+ values.add(value);
27
40
  }
28
- return [validateSingle(config[0])];
41
+ }
42
+ function validate(config) {
43
+ const validated = config.map((cfg) => validateSingle(cfg));
44
+ assertUnique(validated, "source");
45
+ assertUnique(validated, "codebase");
46
+ return validated;
29
47
  }
30
48
  exports.validate = validate;
31
49
  function normalizeAndValidate(config) {
32
50
  return validate(normalize(config));
33
51
  }
34
52
  exports.normalizeAndValidate = normalizeAndValidate;
53
+ function configForCodebase(config, codebase) {
54
+ const codebaseCfg = config.find((c) => c.codebase === codebase);
55
+ if (!codebaseCfg) {
56
+ throw new error_1.FirebaseError(`No functions config found for codebase ${codebase}`);
57
+ }
58
+ return codebaseCfg;
59
+ }
60
+ exports.configForCodebase = configForCodebase;
@@ -115,13 +115,17 @@ function hydrateEnvs(pInfos, prefix) {
115
115
  return errMsg;
116
116
  }
117
117
  exports.hydrateEnvs = hydrateEnvs;
118
+ const CHARACTERS_TO_ESCAPE_SEQUENCES = {
119
+ "\n": "\\n",
120
+ "\r": "\\r",
121
+ "\t": "\\t",
122
+ "\v": "\\v",
123
+ "\\": "\\\\",
124
+ '"': '\\"',
125
+ "'": "\\'",
126
+ };
118
127
  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");
128
+ return s.replace(/[\n\r\t\v\\"']/g, (ch) => CHARACTERS_TO_ESCAPE_SEQUENCES[ch]);
125
129
  }
126
130
  function toDotenvFormat(envs, header = "") {
127
131
  const lines = envs.map(({ newKey, value }) => `${newKey}="${escape(value)}"`);
@@ -1,11 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pruneSecrets = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.labels = exports.isFirebaseManaged = void 0;
3
+ exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.inUse = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.labels = exports.isFirebaseManaged = void 0;
4
+ const utils = require("../utils");
5
+ const poller = require("../operation-poller");
6
+ const gcf = require("../gcp/cloudfunctions");
4
7
  const secretManager_1 = require("../gcp/secretManager");
5
8
  const error_1 = require("../error");
6
9
  const utils_1 = require("../utils");
7
10
  const prompt_1 = require("../prompt");
8
11
  const env_1 = require("./env");
12
+ const logger_1 = require("../logger");
13
+ const api_1 = require("../api");
14
+ const functional_1 = require("../functional");
9
15
  const FIREBASE_MANGED = "firebase-managed";
10
16
  function isFirebaseManaged(secret) {
11
17
  return Object.keys(secret.labels || []).includes(FIREBASE_MANGED);
@@ -17,8 +23,7 @@ function labels() {
17
23
  exports.labels = labels;
18
24
  function toUpperSnakeCase(key) {
19
25
  return key
20
- .replace("-", "_")
21
- .replace(".", "_")
26
+ .replace(/[.-]/g, "_")
22
27
  .replace(/([a-z])([A-Z])/g, "$1_$2")
23
28
  .toUpperCase();
24
29
  }
@@ -80,19 +85,40 @@ function of(endpoints) {
80
85
  return endpoints.reduce((envs, endpoint) => [...envs, ...(endpoint.secretEnvironmentVariables || [])], []);
81
86
  }
82
87
  exports.of = of;
88
+ function inUse(projectInfo, secret, endpoint) {
89
+ const { projectId, projectNumber } = projectInfo;
90
+ for (const sev of of([endpoint])) {
91
+ if ((sev.projectId === projectId || sev.projectId === projectNumber) &&
92
+ sev.secret === secret.name) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+ exports.inUse = inUse;
83
99
  async function pruneSecrets(projectInfo, endpoints) {
84
100
  const { projectId, projectNumber } = projectInfo;
85
101
  const pruneKey = (name, version) => `${name}@${version}`;
86
102
  const prunedSecrets = new Set();
87
103
  const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANGED}=true`);
88
104
  for (const secret of haveSecrets) {
89
- const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `state: ENABLED`);
105
+ const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `NOT state: DESTROYED`);
90
106
  for (const version of versions) {
91
107
  prunedSecrets.add(pruneKey(secret.name, version.versionId));
92
108
  }
93
109
  }
94
- const sevs = of(endpoints).filter((sev) => sev.projectId === projectId || sev.projectId === projectNumber);
95
- for (const sev of sevs) {
110
+ const secrets = [];
111
+ for (const secret of of(endpoints)) {
112
+ if (!secret.version) {
113
+ throw new error_1.FirebaseError(`Secret ${secret.secret} version is unexpectedly empty.`);
114
+ }
115
+ if (secret.projectId === projectId || secret.projectId === projectNumber) {
116
+ if (secret.version) {
117
+ secrets.push(Object.assign(Object.assign({}, secret), { version: secret.version }));
118
+ }
119
+ }
120
+ }
121
+ for (const sev of secrets) {
96
122
  let name = sev.secret;
97
123
  if (name.includes("/")) {
98
124
  const secret = (0, secretManager_1.parseSecretResourceName)(name);
@@ -110,3 +136,70 @@ async function pruneSecrets(projectInfo, endpoints) {
110
136
  .map(([secret, version]) => ({ projectId, version, secret, key: secret }));
111
137
  }
112
138
  exports.pruneSecrets = pruneSecrets;
139
+ async function pruneAndDestroySecrets(projectInfo, endpoints) {
140
+ const { projectId, projectNumber } = projectInfo;
141
+ logger_1.logger.debug("Pruning secrets to find unused secret versions...");
142
+ const unusedSecrets = await module.exports.pruneSecrets({ projectId, projectNumber }, endpoints);
143
+ if (unusedSecrets.length === 0) {
144
+ return { destroyed: [], erred: [] };
145
+ }
146
+ const destroyed = [];
147
+ const erred = [];
148
+ const msg = unusedSecrets.map((s) => `${s.secret}@${s.version}`);
149
+ logger_1.logger.debug(`Found unused secret versions: ${msg}. Destroying them...`);
150
+ const destroyResults = await utils.allSettled(unusedSecrets.map(async (sev) => {
151
+ await (0, secretManager_1.destroySecretVersion)(sev.projectId, sev.secret, sev.version);
152
+ return sev;
153
+ }));
154
+ for (const result of destroyResults) {
155
+ if (result.status === "fulfilled") {
156
+ destroyed.push(result.value);
157
+ }
158
+ else {
159
+ erred.push(result.reason);
160
+ }
161
+ }
162
+ return { destroyed, erred };
163
+ }
164
+ exports.pruneAndDestroySecrets = pruneAndDestroySecrets;
165
+ async function updateEndpointSecret(projectInfo, secretVersion, endpoint) {
166
+ const { projectId, projectNumber } = projectInfo;
167
+ if (!inUse(projectInfo, secretVersion.secret, endpoint)) {
168
+ return endpoint;
169
+ }
170
+ const updatedSevs = [];
171
+ for (const sev of of([endpoint])) {
172
+ const updatedSev = Object.assign({}, sev);
173
+ if ((updatedSev.projectId === projectId || updatedSev.projectId === projectNumber) &&
174
+ updatedSev.secret === secretVersion.secret.name) {
175
+ updatedSev.version = secretVersion.versionId;
176
+ }
177
+ updatedSevs.push(updatedSev);
178
+ }
179
+ if (endpoint.platform === "gcfv1") {
180
+ const fn = gcf.functionFromEndpoint(endpoint, "");
181
+ const op = await gcf.updateFunction({
182
+ name: fn.name,
183
+ runtime: fn.runtime,
184
+ entryPoint: fn.entryPoint,
185
+ secretEnvironmentVariables: updatedSevs,
186
+ });
187
+ const gcfV1PollerOptions = {
188
+ apiOrigin: api_1.functionsOrigin,
189
+ apiVersion: gcf.API_VERSION,
190
+ masterTimeout: 25 * 60 * 1000,
191
+ maxBackoff: 10000,
192
+ pollerName: `update-${endpoint.region}-${endpoint.id}`,
193
+ operationResourceName: op.name,
194
+ };
195
+ const cfn = await poller.pollOperation(gcfV1PollerOptions);
196
+ return gcf.endpointFromFunction(cfn);
197
+ }
198
+ else if (endpoint.platform === "gcfv2") {
199
+ throw new error_1.FirebaseError(`Unsupported platform ${endpoint.platform}`);
200
+ }
201
+ else {
202
+ (0, functional_1.assertExhaustive)(endpoint.platform);
203
+ }
204
+ }
205
+ exports.updateEndpointSecret = updateEndpointSecret;
@@ -55,7 +55,7 @@ const actionFunction = async (options) => {
55
55
  })
56
56
  .then(() => {
57
57
  const instance = serveFunctions.get();
58
- const emulator = new shell.FunctionsEmulatorShell(instance, serveFunctions.getBackend());
58
+ const emulator = new shell.FunctionsEmulatorShell(instance);
59
59
  if (emulator.emulatedFunctions && emulator.emulatedFunctions.length === 0) {
60
60
  logger_1.logger.info("No functions emulated.");
61
61
  process.exit();