firebase-tools 10.6.0 → 10.7.2

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 (97) 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/hosting-channel-deploy.js +2 -2
  10. package/lib/deploy/database/deploy.js +4 -0
  11. package/lib/deploy/database/index.js +1 -0
  12. package/lib/deploy/extensions/deploy.js +4 -4
  13. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  14. package/lib/deploy/extensions/planner.js +36 -9
  15. package/lib/deploy/extensions/prepare.js +1 -1
  16. package/lib/deploy/extensions/secrets.js +2 -2
  17. package/lib/deploy/extensions/tasks.js +60 -21
  18. package/lib/deploy/functions/backend.js +37 -2
  19. package/lib/deploy/functions/build.js +173 -0
  20. package/lib/deploy/functions/checkIam.js +11 -14
  21. package/lib/deploy/functions/containerCleaner.js +8 -7
  22. package/lib/deploy/functions/deploy.js +49 -28
  23. package/lib/deploy/functions/ensure.js +4 -4
  24. package/lib/deploy/functions/functionsDeployHelper.js +99 -24
  25. package/lib/deploy/functions/prepare.js +129 -71
  26. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  27. package/lib/deploy/functions/pricing.js +6 -3
  28. package/lib/deploy/functions/prompts.js +1 -7
  29. package/lib/deploy/functions/release/executor.js +1 -1
  30. package/lib/deploy/functions/release/fabricator.js +69 -25
  31. package/lib/deploy/functions/release/index.js +20 -6
  32. package/lib/deploy/functions/release/planner.js +18 -10
  33. package/lib/deploy/functions/release/reporter.js +14 -11
  34. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  35. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +50 -3
  36. package/lib/deploy/functions/runtimes/golang/index.js +3 -0
  37. package/lib/deploy/functions/runtimes/node/index.js +7 -0
  38. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  39. package/lib/deploy/functions/runtimes/node/parseTriggers.js +132 -6
  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/validate.js +33 -7
  44. package/lib/deploy/hosting/args.js +2 -0
  45. package/lib/deploy/hosting/convertConfig.js +39 -8
  46. package/lib/deploy/hosting/deploy.js +3 -3
  47. package/lib/deploy/hosting/prepare.js +2 -2
  48. package/lib/deploy/hosting/release.js +6 -2
  49. package/lib/deploy/index.js +82 -93
  50. package/lib/deploy/remoteconfig/deploy.js +4 -0
  51. package/lib/deploy/remoteconfig/index.js +3 -1
  52. package/lib/emulator/auth/cloudFunctions.js +6 -2
  53. package/lib/emulator/auth/operations.js +5 -1
  54. package/lib/emulator/auth/server.js +8 -1
  55. package/lib/emulator/auth/state.js +27 -24
  56. package/lib/emulator/auth/utils.js +3 -25
  57. package/lib/emulator/controller.js +17 -14
  58. package/lib/emulator/databaseEmulator.js +36 -3
  59. package/lib/emulator/downloadableEmulators.js +39 -23
  60. package/lib/emulator/extensions/validation.js +2 -2
  61. package/lib/emulator/extensionsEmulator.js +85 -21
  62. package/lib/emulator/functionsEmulator.js +89 -15
  63. package/lib/emulator/functionsEmulatorRuntime.js +1 -1
  64. package/lib/emulator/functionsEmulatorShared.js +25 -2
  65. package/lib/emulator/functionsEmulatorShell.js +2 -3
  66. package/lib/emulator/functionsEmulatorUtils.js +5 -1
  67. package/lib/emulator/pubsubEmulator.js +13 -9
  68. package/lib/emulator/registry.js +34 -12
  69. package/lib/emulator/storage/apis/firebase.js +33 -6
  70. package/lib/emulator/storage/apis/gcloud.js +6 -3
  71. package/lib/emulator/storage/files.js +9 -1
  72. package/lib/ensureApiEnabled.js +8 -4
  73. package/lib/extensions/changelog.js +1 -1
  74. package/lib/extensions/emulator/optionsHelper.js +4 -3
  75. package/lib/extensions/emulator/specHelper.js +7 -1
  76. package/lib/extensions/extensionsHelper.js +30 -24
  77. package/lib/extensions/manifest.js +27 -7
  78. package/lib/extensions/paramHelper.js +7 -5
  79. package/lib/extensions/provisioningHelper.js +2 -2
  80. package/lib/extensions/warnings.js +11 -4
  81. package/lib/functions/events/index.js +7 -0
  82. package/lib/functions/events/v1.js +6 -0
  83. package/lib/functions/projectConfig.js +32 -6
  84. package/lib/functionsShellCommandAction.js +1 -1
  85. package/lib/gcp/cloudfunctions.js +38 -5
  86. package/lib/gcp/cloudfunctionsv2.js +46 -7
  87. package/lib/gcp/identityPlatform.js +44 -0
  88. package/lib/gcp/secretManager.js +1 -1
  89. package/lib/metaprogramming.js +2 -0
  90. package/lib/previews.js +1 -1
  91. package/lib/serve/functions.js +16 -19
  92. package/lib/serve/hosting.js +25 -12
  93. package/lib/serve/index.js +6 -0
  94. package/lib/track.js +15 -21
  95. package/npm-shrinkwrap.json +256 -527
  96. package/package.json +6 -3
  97. 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.getNodeVersion = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readFileFromDirectory = exports.readExtensionYaml = void 0;
3
+ exports.getNodeVersion = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readFileFromDirectory = exports.readPostinstall = exports.readExtensionYaml = void 0;
4
4
  const yaml = require("js-yaml");
5
5
  const path = require("path");
6
6
  const fs = require("fs-extra");
@@ -8,6 +8,7 @@ const error_1 = require("../../error");
8
8
  const extensionsHelper_1 = require("../extensionsHelper");
9
9
  const functionsEmulatorUtils_1 = require("../../emulator/functionsEmulatorUtils");
10
10
  const SPEC_FILE = "extension.yaml";
11
+ const POSTINSTALL_FILE = "POSTINSTALL.md";
11
12
  const validFunctionTypes = [
12
13
  "firebaseextensions.v1beta.function",
13
14
  "firebaseextensions.v1beta.scheduledFunction",
@@ -29,6 +30,11 @@ async function readExtensionYaml(directory) {
29
30
  return wrappedSafeLoad(source);
30
31
  }
31
32
  exports.readExtensionYaml = readExtensionYaml;
33
+ async function readPostinstall(directory) {
34
+ const content = await readFileFromDirectory(directory, POSTINSTALL_FILE);
35
+ return content.source;
36
+ }
37
+ exports.readPostinstall = readPostinstall;
32
38
  function readFileFromDirectory(directory, file) {
33
39
  return new Promise((resolve, reject) => {
34
40
  fs.readFile(path.resolve(directory, file), "utf8", (err, data) => {
@@ -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
  })
@@ -8,9 +8,10 @@ const error_1 = require("../error");
8
8
  const logger_1 = require("../logger");
9
9
  const extensionsHelper_1 = require("./extensionsHelper");
10
10
  const askUserForParam = require("./askUserForParam");
11
- const track = require("../track");
11
+ const track_1 = require("../track");
12
12
  const env = require("../functions/env");
13
13
  const utils_1 = require("../utils");
14
+ const warnings_1 = require("./warnings");
14
15
  function getBaseParamBindings(params) {
15
16
  let ret = {};
16
17
  for (const [k, v] of Object.entries(params)) {
@@ -57,6 +58,7 @@ async function getParams(args) {
57
58
  paramsMessage);
58
59
  }
59
60
  else if (args.paramsEnvPath) {
61
+ (0, warnings_1.paramsFlagDeprecationWarning)();
60
62
  params = getParamsFromFile({
61
63
  paramSpecs: args.paramSpecs,
62
64
  paramsEnvPath: args.paramsEnvPath,
@@ -73,7 +75,7 @@ async function getParams(args) {
73
75
  });
74
76
  }
75
77
  const paramNames = Object.keys(params);
76
- void track("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
78
+ void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
77
79
  return params;
78
80
  }
79
81
  exports.getParams = getParams;
@@ -106,7 +108,7 @@ async function getParamsForUpdate(args) {
106
108
  });
107
109
  }
108
110
  const paramNames = Object.keys(params);
109
- void track("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
111
+ void (0, track_1.track)("Extension Params", paramNames.length ? "Not Present" : "Present", paramNames.length);
110
112
  return params;
111
113
  }
112
114
  exports.getParamsForUpdate = getParamsForUpdate;
@@ -150,10 +152,10 @@ function getParamsFromFile(args) {
150
152
  let envParams;
151
153
  try {
152
154
  envParams = readEnvFile(args.paramsEnvPath);
153
- void track("Extension Env File", "Present");
155
+ void (0, track_1.track)("Extension Env File", "Present");
154
156
  }
155
157
  catch (err) {
156
- void track("Extension Env File", "Invalid");
158
+ void (0, track_1.track)("Extension Env File", "Invalid");
157
159
  throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
158
160
  }
159
161
  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
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
3
+ exports.paramsFlagDeprecationWarning = exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
4
4
  const { marked } = require("marked");
5
5
  const clc = require("cli-color");
6
6
  const extensionsApi_1 = require("./extensionsApi");
@@ -11,6 +11,7 @@ const deploymentSummary_1 = require("../deploy/extensions/deploymentSummary");
11
11
  const planner_1 = require("../deploy/extensions/planner");
12
12
  const functional_1 = require("../functional");
13
13
  const utils = require("../utils");
14
+ const logger_1 = require("../logger");
14
15
  function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
15
16
  const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
16
17
  const warningMsg = `This extension is in preview and is built by a developer in the [Extensions Publisher Early Access Program](http://bit.ly/firex-provider). Its functionality might change in backward-incompatible ways. Since this extension isn't built by Firebase, reach out to ${publisherNameLink} with questions about this extension.`;
@@ -49,11 +50,11 @@ const toListEntry = (i) => {
49
50
  };
50
51
  async function displayWarningsForDeploy(instancesToCreate) {
51
52
  const trustedPublishers = await (0, resolveSource_1.getTrustedPublishers)();
52
- for (const i of instancesToCreate) {
53
+ const publishedExtensionInstances = instancesToCreate.filter((i) => i.ref);
54
+ for (const i of publishedExtensionInstances) {
53
55
  await (0, planner_1.getExtension)(i);
54
- await (0, planner_1.getExtensionVersion)(i);
55
56
  }
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 : ""); });
57
+ 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
58
  const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
58
59
  if (experimental.length) {
59
60
  const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
@@ -68,3 +69,9 @@ async function displayWarningsForDeploy(instancesToCreate) {
68
69
  return experimental.length > 0 || eapExtensions.length > 0;
69
70
  }
70
71
  exports.displayWarningsForDeploy = displayWarningsForDeploy;
72
+ function paramsFlagDeprecationWarning() {
73
+ logger_1.logger.warn("The --params flag is deprecated and will be removed in firebase-tools@11. " +
74
+ "Instead, use an extensions manifest and `firebase deploy --only extensions` to deploy extensions noninteractively. " +
75
+ "See https://firebase.google.com/docs/extensions/manifest for more details");
76
+ }
77
+ exports.paramsFlagDeprecationWarning = paramsFlagDeprecationWarning;
@@ -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;
@@ -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();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.API_VERSION = void 0;
3
+ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFunctions = exports.listFunctions = exports.deleteFunction = exports.updateFunction = exports.setInvokerUpdate = exports.setInvokerCreate = exports.getIamPolicy = exports.setIamPolicy = exports.createFunction = exports.generateUploadUrl = exports.BLOCKING_LABEL = exports.CODEBASE_LABEL = exports.API_VERSION = void 0;
4
4
  const clc = require("cli-color");
5
5
  const error_1 = require("../error");
6
6
  const logger_1 = require("../logger");
@@ -9,10 +9,21 @@ const backend = require("../deploy/functions/backend");
9
9
  const utils = require("../utils");
10
10
  const proto = require("./proto");
11
11
  const runtimes = require("../deploy/functions/runtimes");
12
+ const projectConfig = require("../functions/projectConfig");
12
13
  const apiv2_1 = require("../apiv2");
13
14
  const api_1 = require("../api");
14
15
  exports.API_VERSION = "v1";
16
+ exports.CODEBASE_LABEL = "firebase-functions-codebase";
15
17
  const client = new apiv2_1.Client({ urlPrefix: api_1.functionsOrigin, apiVersion: exports.API_VERSION });
18
+ exports.BLOCKING_LABEL = "deployment-blocking";
19
+ const BLOCKING_LABEL_KEY_TO_EVENT = {
20
+ "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
21
+ "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
22
+ };
23
+ const BLOCKING_EVENT_TO_LABEL_KEY = {
24
+ "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
25
+ "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
26
+ };
16
27
  function validateFunction(func) {
17
28
  proto.assertOneOf("Cloud Function", func, "sourceCode", "sourceArchiveUrl", "sourceRepository", "sourceUploadUrl");
18
29
  proto.assertOneOf("Cloud Function", func, "trigger", "httpsTrigger", "eventTrigger");
@@ -202,7 +213,7 @@ async function listAllFunctions(projectId) {
202
213
  }
203
214
  exports.listAllFunctions = listAllFunctions;
204
215
  function endpointFromFunction(gcfFunction) {
205
- var _a, _b, _c, _d, _e;
216
+ var _a, _b, _c, _d, _e, _f, _g;
206
217
  const [, project, , region, , id] = gcfFunction.name.split("/");
207
218
  let trigger;
208
219
  let uri;
@@ -223,20 +234,29 @@ function endpointFromFunction(gcfFunction) {
223
234
  callableTrigger: {},
224
235
  };
225
236
  }
237
+ else if ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[exports.BLOCKING_LABEL]) {
238
+ trigger = {
239
+ blockingTrigger: {
240
+ eventType: BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[exports.BLOCKING_LABEL]],
241
+ },
242
+ };
243
+ }
226
244
  else if (gcfFunction.httpsTrigger) {
227
245
  trigger = { httpsTrigger: {} };
228
- uri = gcfFunction.httpsTrigger.url;
229
- securityLevel = gcfFunction.httpsTrigger.securityLevel;
230
246
  }
231
247
  else {
232
248
  trigger = {
233
249
  eventTrigger: {
234
250
  eventType: gcfFunction.eventTrigger.eventType,
235
251
  eventFilters: { resource: gcfFunction.eventTrigger.resource },
236
- retry: !!((_e = gcfFunction.eventTrigger.failurePolicy) === null || _e === void 0 ? void 0 : _e.retry),
252
+ retry: !!((_f = gcfFunction.eventTrigger.failurePolicy) === null || _f === void 0 ? void 0 : _f.retry),
237
253
  },
238
254
  };
239
255
  }
256
+ if (gcfFunction.httpsTrigger) {
257
+ uri = gcfFunction.httpsTrigger.url;
258
+ securityLevel = gcfFunction.httpsTrigger.securityLevel;
259
+ }
240
260
  if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
241
261
  logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
242
262
  }
@@ -255,10 +275,12 @@ function endpointFromFunction(gcfFunction) {
255
275
  endpoint.vpc = { connector: gcfFunction.vpcConnector };
256
276
  proto.renameIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings");
257
277
  }
278
+ endpoint.codebase = ((_g = gcfFunction.labels) === null || _g === void 0 ? void 0 : _g[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
258
279
  return endpoint;
259
280
  }
260
281
  exports.endpointFromFunction = endpointFromFunction;
261
282
  function functionFromEndpoint(endpoint, sourceUploadUrl) {
283
+ var _a;
262
284
  if (endpoint.platform !== "gcfv1") {
263
285
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
264
286
  }
@@ -294,6 +316,10 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
294
316
  gcfFunction.httpsTrigger = {};
295
317
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
296
318
  }
319
+ else if (backend.isBlockingTriggered(endpoint)) {
320
+ gcfFunction.httpsTrigger = {};
321
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
322
+ }
297
323
  else {
298
324
  gcfFunction.httpsTrigger = {};
299
325
  if (backend.isCallableTriggered(endpoint)) {
@@ -309,6 +335,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
309
335
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
310
336
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
311
337
  }
338
+ const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
339
+ if (codebase !== projectConfig.DEFAULT_CODEBASE) {
340
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: codebase });
341
+ }
342
+ else {
343
+ (_a = gcfFunction.labels) === null || _a === void 0 ? true : delete _a[exports.CODEBASE_LABEL];
344
+ }
312
345
  return gcfFunction;
313
346
  }
314
347
  exports.functionFromEndpoint = functionFromEndpoint;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.megabytes = exports.API_VERSION = void 0;
3
+ exports.endpointFromFunction = exports.functionFromEndpoint = exports.deleteFunction = exports.updateFunction = exports.listAllFunctions = exports.listFunctions = exports.getFunction = exports.createFunction = exports.generateUploadUrl = exports.mebibytes = exports.BLOCKING_LABEL = exports.CODEBASE_LABEL = exports.API_VERSION = void 0;
4
4
  const clc = require("cli-color");
5
5
  const apiv2_1 = require("../apiv2");
6
6
  const error_1 = require("../error");
@@ -11,12 +11,23 @@ const backend = require("../deploy/functions/backend");
11
11
  const runtimes = require("../deploy/functions/runtimes");
12
12
  const proto = require("./proto");
13
13
  const utils = require("../utils");
14
+ const projectConfig = require("../functions/projectConfig");
14
15
  exports.API_VERSION = "v2alpha";
16
+ exports.CODEBASE_LABEL = "firebase-functions-codebase";
15
17
  const client = new apiv2_1.Client({
16
18
  urlPrefix: api_1.functionsV2Origin,
17
19
  auth: true,
18
20
  apiVersion: exports.API_VERSION,
19
21
  });
22
+ exports.BLOCKING_LABEL = "deployment-blocking";
23
+ const BLOCKING_LABEL_KEY_TO_EVENT = {
24
+ "before-create": "providers/cloud.auth/eventTypes/user.beforeCreate",
25
+ "before-sign-in": "providers/cloud.auth/eventTypes/user.beforeSignIn",
26
+ };
27
+ const BLOCKING_EVENT_TO_LABEL_KEY = {
28
+ "providers/cloud.auth/eventTypes/user.beforeCreate": "before-create",
29
+ "providers/cloud.auth/eventTypes/user.beforeSignIn": "before-sign-in",
30
+ };
20
31
  const BYTES_PER_UNIT = {
21
32
  "": 1,
22
33
  k: 1e3,
@@ -28,7 +39,7 @@ const BYTES_PER_UNIT = {
28
39
  Gi: 1 << 30,
29
40
  Ti: 1 << 40,
30
41
  };
31
- function megabytes(memory) {
42
+ function mebibytes(memory) {
32
43
  const re = /^([0-9]+(\.[0-9]*)?)(Ki|Mi|Gi|Ti|k|M|G|T|([eE]([0-9]+)))?$/;
33
44
  const matches = re.exec(memory);
34
45
  if (!matches) {
@@ -43,9 +54,9 @@ function megabytes(memory) {
43
54
  const suffix = matches[3] || "";
44
55
  bytes = quantity * BYTES_PER_UNIT[suffix];
45
56
  }
46
- return bytes / 1e6;
57
+ return bytes / (1 << 20);
47
58
  }
48
- exports.megabytes = megabytes;
59
+ exports.mebibytes = mebibytes;
49
60
  function functionsOpLogReject(funcName, type, err) {
50
61
  var _a, _b;
51
62
  if (((_b = (_a = err === null || err === void 0 ? void 0 : err.context) === null || _a === void 0 ? void 0 : _a.response) === null || _b === void 0 ? void 0 : _b.statusCode) === 429) {
@@ -146,6 +157,7 @@ async function deleteFunction(cloudFunction) {
146
157
  }
147
158
  exports.deleteFunction = deleteFunction;
148
159
  function functionFromEndpoint(endpoint, source) {
160
+ var _a;
149
161
  if (endpoint.platform !== "gcfv2") {
150
162
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
151
163
  }
@@ -167,7 +179,14 @@ function functionFromEndpoint(endpoint, source) {
167
179
  };
168
180
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
169
181
  proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "serviceAccountEmail", "ingressSettings", "timeoutSeconds");
170
- proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemory", "availableMemoryMb", (mb) => `${mb}M`);
182
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemory", "availableMemoryMb", (mb) => {
183
+ if (mb > 1024) {
184
+ return `${mb / 1024}Gi`;
185
+ }
186
+ else {
187
+ return `${mb}Mi`;
188
+ }
189
+ });
171
190
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "minInstanceCount", "minInstances");
172
191
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "maxInstanceCount", "maxInstances");
173
192
  if (endpoint.vpc) {
@@ -194,6 +213,7 @@ function functionFromEndpoint(endpoint, source) {
194
213
  }
195
214
  }
196
215
  proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
216
+ proto.copyIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "channel");
197
217
  if (endpoint.eventTrigger.retry) {
198
218
  logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
199
219
  }
@@ -208,11 +228,21 @@ function functionFromEndpoint(endpoint, source) {
208
228
  else if (backend.isCallableTriggered(endpoint)) {
209
229
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
210
230
  }
231
+ else if (backend.isBlockingTriggered(endpoint)) {
232
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
233
+ }
234
+ const codebase = endpoint.codebase || projectConfig.DEFAULT_CODEBASE;
235
+ if (codebase !== projectConfig.DEFAULT_CODEBASE) {
236
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: codebase });
237
+ }
238
+ else {
239
+ (_a = gcfFunction.labels) === null || _a === void 0 ? true : delete _a[exports.CODEBASE_LABEL];
240
+ }
211
241
  return gcfFunction;
212
242
  }
213
243
  exports.functionFromEndpoint = functionFromEndpoint;
214
244
  function endpointFromFunction(gcfFunction) {
215
- var _a, _b, _c;
245
+ var _a, _b, _c, _d, _e;
216
246
  const [, project, , region, , id] = gcfFunction.name.split("/");
217
247
  let trigger;
218
248
  if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
@@ -230,6 +260,13 @@ function endpointFromFunction(gcfFunction) {
230
260
  callableTrigger: {},
231
261
  };
232
262
  }
263
+ else if ((_d = gcfFunction.labels) === null || _d === void 0 ? void 0 : _d[exports.BLOCKING_LABEL]) {
264
+ trigger = {
265
+ blockingTrigger: {
266
+ eventType: BLOCKING_LABEL_KEY_TO_EVENT[gcfFunction.labels[exports.BLOCKING_LABEL]],
267
+ },
268
+ };
269
+ }
233
270
  else if (gcfFunction.eventTrigger) {
234
271
  trigger = {
235
272
  eventTrigger: {
@@ -246,6 +283,7 @@ function endpointFromFunction(gcfFunction) {
246
283
  trigger.eventTrigger.eventFilters[attribute] = value;
247
284
  }
248
285
  }
286
+ proto.copyIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "channel");
249
287
  proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
250
288
  }
251
289
  else {
@@ -258,7 +296,7 @@ function endpointFromFunction(gcfFunction) {
258
296
  project,
259
297
  region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, uri: gcfFunction.serviceConfig.uri });
260
298
  proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "ingressSettings", "environmentVariables", "timeoutSeconds");
261
- proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "availableMemoryMb", "availableMemory", megabytes);
299
+ proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "availableMemoryMb", "availableMemory", mebibytes);
262
300
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
263
301
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
264
302
  proto.copyIfPresent(endpoint, gcfFunction, "labels");
@@ -266,6 +304,7 @@ function endpointFromFunction(gcfFunction) {
266
304
  endpoint.vpc = { connector: gcfFunction.serviceConfig.vpcConnector };
267
305
  proto.renameIfPresent(endpoint.vpc, gcfFunction.serviceConfig, "egressSettings", "vpcConnectorEgressSettings");
268
306
  }
307
+ endpoint.codebase = ((_e = gcfFunction.labels) === null || _e === void 0 ? void 0 : _e[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
269
308
  return endpoint;
270
309
  }
271
310
  exports.endpointFromFunction = endpointFromFunction;