firebase-tools 10.4.2 → 10.7.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 (108) hide show
  1. package/lib/bin/firebase.js +1 -1
  2. package/lib/command.js +4 -4
  3. package/lib/commands/deploy.js +1 -1
  4. package/lib/commands/emulators-start.js +13 -3
  5. package/lib/commands/ext-configure.js +15 -5
  6. package/lib/commands/ext-dev-emulators-start.js +5 -1
  7. package/lib/commands/ext-export.js +6 -5
  8. package/lib/commands/ext-install.js +28 -44
  9. package/lib/commands/ext-update.js +9 -1
  10. package/lib/commands/functions-delete.js +2 -5
  11. package/lib/commands/functions-secrets-destroy.js +23 -3
  12. package/lib/commands/functions-secrets-prune.js +15 -12
  13. package/lib/commands/functions-secrets-set.js +51 -4
  14. package/lib/commands/hosting-channel-deploy.js +2 -2
  15. package/lib/deploy/database/deploy.js +4 -0
  16. package/lib/deploy/database/index.js +1 -0
  17. package/lib/deploy/extensions/deploy.js +4 -4
  18. package/lib/deploy/extensions/deploymentSummary.js +8 -5
  19. package/lib/deploy/extensions/planner.js +36 -9
  20. package/lib/deploy/extensions/prepare.js +1 -1
  21. package/lib/deploy/extensions/secrets.js +2 -2
  22. package/lib/deploy/extensions/tasks.js +60 -21
  23. package/lib/deploy/functions/backend.js +17 -6
  24. package/lib/deploy/functions/build.js +162 -0
  25. package/lib/deploy/functions/checkIam.js +6 -5
  26. package/lib/deploy/functions/deploy.js +14 -15
  27. package/lib/deploy/functions/ensure.js +4 -4
  28. package/lib/deploy/functions/functionsDeployHelper.js +54 -23
  29. package/lib/deploy/functions/prepare.js +92 -39
  30. package/lib/deploy/functions/prepareFunctionsUpload.js +16 -21
  31. package/lib/deploy/functions/pricing.js +6 -3
  32. package/lib/deploy/functions/prompts.js +1 -7
  33. package/lib/deploy/functions/release/fabricator.js +44 -5
  34. package/lib/deploy/functions/release/index.js +31 -6
  35. package/lib/deploy/functions/release/planner.js +10 -8
  36. package/lib/deploy/functions/release/reporter.js +14 -11
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +12 -6
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +61 -13
  39. package/lib/deploy/functions/runtimes/node/index.js +1 -1
  40. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +3 -3
  41. package/lib/deploy/functions/runtimes/node/parseTriggers.js +29 -24
  42. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  43. package/lib/deploy/functions/services/auth.js +95 -0
  44. package/lib/deploy/functions/services/index.js +41 -21
  45. package/lib/deploy/functions/services/storage.js +1 -6
  46. package/lib/deploy/functions/validate.js +8 -5
  47. package/lib/deploy/hosting/args.js +2 -0
  48. package/lib/deploy/hosting/convertConfig.js +37 -8
  49. package/lib/deploy/hosting/deploy.js +3 -3
  50. package/lib/deploy/hosting/prepare.js +2 -2
  51. package/lib/deploy/hosting/release.js +6 -2
  52. package/lib/deploy/index.js +82 -93
  53. package/lib/deploy/remoteconfig/deploy.js +4 -0
  54. package/lib/deploy/remoteconfig/index.js +3 -1
  55. package/lib/emulator/auth/operations.js +26 -20
  56. package/lib/emulator/auth/state.js +79 -43
  57. package/lib/emulator/auth/utils.js +3 -25
  58. package/lib/emulator/commandUtils.js +72 -2
  59. package/lib/emulator/controller.js +14 -5
  60. package/lib/emulator/downloadableEmulators.js +47 -24
  61. package/lib/emulator/extensions/postinstall.js +41 -0
  62. package/lib/emulator/extensions/validation.js +2 -2
  63. package/lib/emulator/extensionsEmulator.js +85 -21
  64. package/lib/emulator/functionsEmulator.js +79 -7
  65. package/lib/emulator/functionsEmulatorShared.js +36 -21
  66. package/lib/emulator/registry.js +34 -12
  67. package/lib/emulator/shared/request.js +19 -0
  68. package/lib/emulator/storage/apis/firebase.js +32 -35
  69. package/lib/emulator/storage/apis/gcloud.js +84 -66
  70. package/lib/emulator/storage/files.js +56 -52
  71. package/lib/emulator/storage/index.js +23 -3
  72. package/lib/emulator/storage/metadata.js +18 -8
  73. package/lib/emulator/storage/rules/manager.js +7 -17
  74. package/lib/emulator/storage/rules/utils.js +11 -3
  75. package/lib/emulator/storage/server.js +38 -12
  76. package/lib/ensureApiEnabled.js +8 -4
  77. package/lib/extensions/askUserForParam.js +14 -11
  78. package/lib/extensions/changelog.js +1 -1
  79. package/lib/extensions/emulator/optionsHelper.js +9 -10
  80. package/lib/extensions/emulator/specHelper.js +7 -1
  81. package/lib/extensions/emulator/triggerHelper.js +11 -14
  82. package/lib/extensions/extensionsApi.js +2 -1
  83. package/lib/extensions/extensionsHelper.js +30 -24
  84. package/lib/extensions/manifest.js +28 -8
  85. package/lib/extensions/paramHelper.js +19 -13
  86. package/lib/extensions/provisioningHelper.js +2 -2
  87. package/lib/extensions/warnings.js +3 -3
  88. package/lib/functions/env.js +10 -2
  89. package/lib/functions/events/index.js +7 -0
  90. package/lib/functions/events/v1.js +6 -0
  91. package/lib/functions/projectConfig.js +24 -3
  92. package/lib/functions/runtimeConfigExport.js +10 -6
  93. package/lib/functions/secrets.js +99 -6
  94. package/lib/gcp/cloudfunctions.js +37 -18
  95. package/lib/gcp/cloudfunctionsv2.js +41 -25
  96. package/lib/gcp/cloudtasks.js +5 -3
  97. package/lib/gcp/identityPlatform.js +44 -0
  98. package/lib/gcp/secretManager.js +2 -2
  99. package/lib/metaprogramming.js +2 -0
  100. package/lib/previews.js +1 -1
  101. package/lib/serve/hosting.js +25 -12
  102. package/lib/serve/index.js +6 -0
  103. package/lib/track.js +15 -21
  104. package/lib/utils.js +30 -1
  105. package/npm-shrinkwrap.json +44 -2
  106. package/package.json +4 -1
  107. package/schema/firebase-config.json +6 -0
  108. package/lib/emulator/storage/list.js +0 -18
@@ -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.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,13 +20,33 @@ 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 });
31
+ }
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);
40
+ }
23
41
  }
24
42
  function validate(config) {
25
43
  if (config.length > 1) {
26
44
  throw new error_1.FirebaseError("More than one functions.source detected in firebase.json.");
27
45
  }
28
- return [validateSingle(config[0])];
46
+ const validated = validateSingle(config[0]);
47
+ assertUnique([validated], "source");
48
+ assertUnique([validated], "codebase");
49
+ return [validated];
29
50
  }
30
51
  exports.validate = validate;
31
52
  function normalizeAndValidate(config) {
@@ -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;
@@ -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,25 +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
- eventFilters: [
236
- {
237
- attribute: "resource",
238
- value: gcfFunction.eventTrigger.resource,
239
- },
240
- ],
241
- retry: !!((_e = gcfFunction.eventTrigger.failurePolicy) === null || _e === void 0 ? void 0 : _e.retry),
251
+ eventFilters: { resource: gcfFunction.eventTrigger.resource },
252
+ retry: !!((_f = gcfFunction.eventTrigger.failurePolicy) === null || _f === void 0 ? void 0 : _f.retry),
242
253
  },
243
254
  };
244
255
  }
256
+ if (gcfFunction.httpsTrigger) {
257
+ uri = gcfFunction.httpsTrigger.url;
258
+ securityLevel = gcfFunction.httpsTrigger.securityLevel;
259
+ }
245
260
  if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
246
261
  logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
247
262
  }
@@ -254,11 +269,13 @@ function endpointFromFunction(gcfFunction) {
254
269
  if (securityLevel) {
255
270
  endpoint.securityLevel = securityLevel;
256
271
  }
257
- proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
272
+ proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
273
+ proto.renameIfPresent(endpoint, gcfFunction, "timeoutSeconds", "timeout", proto.secondsFromDuration);
258
274
  if (gcfFunction.vpcConnector) {
259
275
  endpoint.vpc = { connector: gcfFunction.vpcConnector };
260
276
  proto.renameIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings");
261
277
  }
278
+ endpoint.codebase = ((_g = gcfFunction.labels) === null || _g === void 0 ? void 0 : _g[exports.CODEBASE_LABEL]) || projectConfig.DEFAULT_CODEBASE;
262
279
  return endpoint;
263
280
  }
264
281
  exports.endpointFromFunction = endpointFromFunction;
@@ -278,13 +295,9 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
278
295
  };
279
296
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
280
297
  if (backend.isEventTriggered(endpoint)) {
281
- const resourceFilter = backend.findEventFilter(endpoint, "resource");
282
- if (!resourceFilter) {
283
- throw new error_1.FirebaseError("Invalid event trigger definition. Expected event filter with 'resource' attribute.");
284
- }
285
298
  gcfFunction.eventTrigger = {
286
299
  eventType: endpoint.eventTrigger.eventType,
287
- resource: resourceFilter.value,
300
+ resource: endpoint.eventTrigger.eventFilters.resource,
288
301
  };
289
302
  gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
290
303
  ? { retry: {} }
@@ -302,6 +315,10 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
302
315
  gcfFunction.httpsTrigger = {};
303
316
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
304
317
  }
318
+ else if (backend.isBlockingTriggered(endpoint)) {
319
+ gcfFunction.httpsTrigger = {};
320
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.BLOCKING_LABEL]: BLOCKING_EVENT_TO_LABEL_KEY[endpoint.blockingTrigger.eventType] });
321
+ }
305
322
  else {
306
323
  gcfFunction.httpsTrigger = {};
307
324
  if (backend.isCallableTriggered(endpoint)) {
@@ -311,11 +328,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
311
328
  gcfFunction.httpsTrigger.securityLevel = endpoint.securityLevel;
312
329
  }
313
330
  }
314
- proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
331
+ proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
332
+ proto.renameIfPresent(gcfFunction, endpoint, "timeout", "timeoutSeconds", proto.durationFromSeconds);
315
333
  if (endpoint.vpc) {
316
334
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
317
335
  proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
318
336
  }
337
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { [exports.CODEBASE_LABEL]: endpoint.codebase || projectConfig.DEFAULT_CODEBASE });
319
338
  return gcfFunction;
320
339
  }
321
340
  exports.functionFromEndpoint = functionFromEndpoint;