firebase-tools 10.2.0 → 10.3.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 (139) hide show
  1. package/lib/apiv2.js +3 -0
  2. package/lib/appdistribution/options-parser-util.js +1 -1
  3. package/lib/auth.js +3 -3
  4. package/lib/command.js +1 -1
  5. package/lib/commands/apps-android-sha-create.js +2 -2
  6. package/lib/commands/apps-sdkconfig.js +1 -1
  7. package/lib/commands/auth-import.js +1 -1
  8. package/lib/commands/database-rules-list.js +2 -2
  9. package/lib/commands/emulators-start.js +1 -1
  10. package/lib/commands/ext-configure.js +58 -4
  11. package/lib/commands/ext-dev-init.js +49 -49
  12. package/lib/commands/ext-export.js +7 -2
  13. package/lib/commands/ext-install.js +163 -104
  14. package/lib/commands/ext-uninstall.js +17 -8
  15. package/lib/commands/ext-update.js +64 -11
  16. package/lib/commands/functions-config-clone.js +1 -1
  17. package/lib/commands/functions-config-export.js +1 -1
  18. package/lib/commands/hosting-clone.js +3 -3
  19. package/lib/commands/remoteconfig-get.js +1 -1
  20. package/lib/config.js +6 -3
  21. package/lib/deploy/extensions/deploymentSummary.js +3 -3
  22. package/lib/deploy/extensions/planner.js +7 -6
  23. package/lib/deploy/extensions/tasks.js +1 -1
  24. package/lib/deploy/functions/backend.js +21 -5
  25. package/lib/deploy/functions/checkIam.js +5 -5
  26. package/lib/deploy/functions/containerCleaner.js +3 -3
  27. package/lib/deploy/functions/ensure.js +3 -3
  28. package/lib/deploy/functions/functionsDeployHelper.js +2 -2
  29. package/lib/deploy/functions/prepare.js +5 -3
  30. package/lib/deploy/functions/pricing.js +1 -1
  31. package/lib/deploy/functions/prompts.js +2 -2
  32. package/lib/deploy/functions/release/fabricator.js +7 -7
  33. package/lib/deploy/functions/release/index.js +1 -1
  34. package/lib/deploy/functions/release/planner.js +43 -26
  35. package/lib/deploy/functions/release/reporter.js +3 -0
  36. package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
  37. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  38. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +22 -12
  39. package/lib/deploy/functions/runtimes/golang/index.js +2 -2
  40. package/lib/deploy/functions/runtimes/node/index.js +53 -0
  41. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
  42. package/lib/deploy/functions/runtimes/node/parseTriggers.js +52 -15
  43. package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
  44. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  45. package/lib/deploy/functions/services/index.js +9 -1
  46. package/lib/deploy/functions/services/storage.js +10 -4
  47. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  48. package/lib/deploy/functions/validate.js +3 -3
  49. package/lib/deploy/hosting/client.js +9 -0
  50. package/lib/deploy/hosting/convertConfig.js +6 -0
  51. package/lib/deploy/hosting/deploy.js +2 -2
  52. package/lib/deploy/hosting/hashcache.js +21 -19
  53. package/lib/deploy/hosting/index.js +5 -5
  54. package/lib/deploy/hosting/prepare.js +25 -25
  55. package/lib/deploy/hosting/release.js +21 -24
  56. package/lib/deploy/hosting/uploader.js +5 -5
  57. package/lib/deploy/remoteconfig/functions.js +2 -2
  58. package/lib/emulator/auth/cloudFunctions.js +1 -1
  59. package/lib/emulator/auth/operations.js +1 -1
  60. package/lib/emulator/commandUtils.js +5 -1
  61. package/lib/emulator/constants.js +4 -0
  62. package/lib/emulator/controller.js +54 -24
  63. package/lib/emulator/download.js +18 -1
  64. package/lib/emulator/downloadableEmulators.js +30 -13
  65. package/lib/emulator/emulatorLogger.js +12 -1
  66. package/lib/emulator/extensions/validation.js +70 -0
  67. package/lib/emulator/extensionsEmulator.js +175 -0
  68. package/lib/emulator/functionsEmulator.js +106 -44
  69. package/lib/emulator/functionsEmulatorRuntime.js +44 -36
  70. package/lib/emulator/functionsEmulatorShared.js +17 -10
  71. package/lib/emulator/functionsEmulatorShell.js +1 -1
  72. package/lib/emulator/functionsEmulatorUtils.js +4 -4
  73. package/lib/emulator/functionsRuntimeWorker.js +2 -2
  74. package/lib/emulator/hub.js +4 -3
  75. package/lib/emulator/loggingEmulator.js +1 -1
  76. package/lib/emulator/pubsubEmulator.js +1 -1
  77. package/lib/emulator/registry.js +10 -2
  78. package/lib/emulator/storage/apis/firebase.js +314 -332
  79. package/lib/emulator/storage/apis/gcloud.js +241 -121
  80. package/lib/emulator/storage/crc.js +5 -1
  81. package/lib/emulator/storage/errors.js +9 -0
  82. package/lib/emulator/storage/files.js +159 -300
  83. package/lib/emulator/storage/index.js +27 -73
  84. package/lib/emulator/storage/metadata.js +65 -51
  85. package/lib/emulator/storage/multipart.js +62 -0
  86. package/lib/emulator/storage/persistence.js +78 -0
  87. package/lib/emulator/storage/rules/config.js +33 -0
  88. package/lib/emulator/storage/rules/manager.js +81 -0
  89. package/lib/emulator/storage/rules/runtime.js +8 -7
  90. package/lib/emulator/storage/rules/utils.js +48 -0
  91. package/lib/emulator/storage/server.js +2 -2
  92. package/lib/emulator/storage/upload.js +106 -0
  93. package/lib/emulator/types.js +3 -0
  94. package/lib/ensureApiEnabled.js +5 -1
  95. package/lib/error.js +1 -1
  96. package/lib/extensions/askUserForParam.js +1 -1
  97. package/lib/extensions/changelog.js +3 -1
  98. package/lib/extensions/checkProjectBilling.js +1 -1
  99. package/lib/extensions/displayExtensionInfo.js +1 -1
  100. package/lib/extensions/emulator/optionsHelper.js +56 -8
  101. package/lib/extensions/emulator/specHelper.js +10 -23
  102. package/lib/extensions/export.js +1 -51
  103. package/lib/extensions/extensionsApi.js +1 -1
  104. package/lib/extensions/extensionsHelper.js +32 -19
  105. package/lib/extensions/listExtensions.js +2 -0
  106. package/lib/extensions/manifest.js +144 -0
  107. package/lib/extensions/metricsUtils.js +4 -4
  108. package/lib/extensions/paramHelper.js +9 -8
  109. package/lib/extensions/refs.js +1 -1
  110. package/lib/extensions/secretsUtils.js +3 -3
  111. package/lib/functional.js +1 -1
  112. package/lib/functions/env.js +6 -7
  113. package/lib/functions/events/v2.js +11 -0
  114. package/lib/gcp/cloudfunctions.js +42 -11
  115. package/lib/gcp/cloudfunctionsv2.js +48 -17
  116. package/lib/gcp/cloudtasks.js +1 -1
  117. package/lib/gcp/docker.js +2 -2
  118. package/lib/gcp/resourceManager.js +4 -4
  119. package/lib/gcp/run.js +2 -2
  120. package/lib/gcp/storage.js +1 -0
  121. package/lib/hosting/api.js +1 -1
  122. package/lib/hosting/functionsProxy.js +15 -5
  123. package/lib/hosting/proxy.js +2 -2
  124. package/lib/init/features/account.js +1 -1
  125. package/lib/management/database.js +1 -1
  126. package/lib/previews.js +1 -1
  127. package/lib/responseToError.js +16 -7
  128. package/lib/serve/functions.js +2 -1
  129. package/lib/serve/hosting.js +1 -1
  130. package/lib/utils.js +15 -2
  131. package/npm-shrinkwrap.json +904 -412
  132. package/package.json +3 -3
  133. package/schema/firebase-config.json +32 -0
  134. package/templates/init/functions/javascript/package.lint.json +3 -3
  135. package/templates/init/functions/javascript/package.nolint.json +2 -2
  136. package/templates/init/functions/typescript/package.lint.json +7 -7
  137. package/templates/init/functions/typescript/package.nolint.json +3 -3
  138. package/lib/deploy/extensions/params.js +0 -39
  139. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeToManifest = void 0;
4
+ const clc = require("cli-color");
5
+ const path = require("path");
6
+ const refs = require("./refs");
7
+ const config_1 = require("../config");
8
+ const logger_1 = require("../logger");
9
+ const prompt_1 = require("../prompt");
10
+ const paramHelper_1 = require("./paramHelper");
11
+ const error_1 = require("../error");
12
+ const utils = require("../utils");
13
+ const extensionsHelper_1 = require("./extensionsHelper");
14
+ const ENV_DIRECTORY = "extensions";
15
+ async function writeToManifest(specs, config, options, allowOverwrite = false) {
16
+ if (config.has("extensions") &&
17
+ Object.keys(config.get("extensions")).length &&
18
+ !options.nonInteractive &&
19
+ !options.force) {
20
+ const currentExtensions = Object.entries(config.get("extensions"))
21
+ .map((i) => `${i[0]}: ${i[1]}`)
22
+ .join("\n\t");
23
+ if (allowOverwrite) {
24
+ const overwrite = await (0, prompt_1.promptOnce)({
25
+ type: "list",
26
+ message: `firebase.json already contains extensions:\n${currentExtensions}\nWould you like to overwrite or merge?`,
27
+ choices: [
28
+ { name: "Overwrite", value: true },
29
+ { name: "Merge", value: false },
30
+ ],
31
+ });
32
+ if (overwrite) {
33
+ config.set("extensions", {});
34
+ }
35
+ }
36
+ }
37
+ writeExtensionsToFirebaseJson(specs, config);
38
+ await writeEnvFiles(specs, config, options.force);
39
+ }
40
+ exports.writeToManifest = writeToManifest;
41
+ function removeFromManifest(instanceId, config) {
42
+ if (!instanceExists(instanceId, config)) {
43
+ throw new error_1.FirebaseError(`Extension instance ${instanceId} not found in firebase.json.`);
44
+ }
45
+ const extensions = config.get("extensions", {});
46
+ extensions[instanceId] = undefined;
47
+ config.set("extensions", extensions);
48
+ config.writeProjectFile("firebase.json", config.src);
49
+ logger_1.logger.info(`Removed extension instance ${instanceId} from firebase.json`);
50
+ config.deleteProjectFile(`extensions/${instanceId}.env`);
51
+ logger_1.logger.info(`Removed extension instance environment config extensions/${instanceId}.env`);
52
+ config.deleteProjectFile(`extensions/${instanceId}.env.local`);
53
+ logger_1.logger.info(`Removed extension instance environment config extensions/${instanceId}.env.local`);
54
+ }
55
+ exports.removeFromManifest = removeFromManifest;
56
+ function loadConfig(options) {
57
+ const existingConfig = config_1.Config.load(options, true);
58
+ if (!existingConfig) {
59
+ throw new error_1.FirebaseError("Not currently in a Firebase directory. Run `firebase init` to create a Firebase directory.");
60
+ }
61
+ return existingConfig;
62
+ }
63
+ exports.loadConfig = loadConfig;
64
+ function instanceExists(instanceId, config) {
65
+ return !!config.get("extensions", {})[instanceId];
66
+ }
67
+ exports.instanceExists = instanceExists;
68
+ function getInstanceRef(instanceId, config) {
69
+ if (!instanceExists(instanceId, config)) {
70
+ throw new error_1.FirebaseError(`Could not find extension instance ${instanceId} in firebase.json`);
71
+ }
72
+ const ref = config.get("extensions", {})[instanceId];
73
+ return refs.parse(ref);
74
+ }
75
+ exports.getInstanceRef = getInstanceRef;
76
+ function writeExtensionsToFirebaseJson(specs, config) {
77
+ const extensions = config.get("extensions", {});
78
+ for (const s of specs) {
79
+ extensions[s.instanceId] = refs.toExtensionVersionRef(s.ref);
80
+ }
81
+ config.set("extensions", extensions);
82
+ config.writeProjectFile("firebase.json", config.src);
83
+ utils.logSuccess("Wrote extensions to " + clc.bold("firebase.json") + "...");
84
+ }
85
+ async function writeEnvFiles(specs, config, force) {
86
+ for (const spec of specs) {
87
+ const content = Object.entries(spec.params)
88
+ .sort((a, b) => {
89
+ return a[0].localeCompare(b[0]);
90
+ })
91
+ .map((r) => `${r[0]}=${r[1]}`)
92
+ .join("\n");
93
+ await config.askWriteProjectFile(`extensions/${spec.instanceId}.env`, content, force);
94
+ }
95
+ }
96
+ function readInstanceParam(args) {
97
+ var _a;
98
+ const aliases = (_a = args.aliases) !== null && _a !== void 0 ? _a : [];
99
+ const filesToCheck = [
100
+ `${args.instanceId}.env`,
101
+ ...aliases.map((alias) => `${args.instanceId}.env.${alias}`),
102
+ ...(args.projectNumber ? [`${args.instanceId}.env.${args.projectNumber}`] : []),
103
+ ...(args.projectId ? [`${args.instanceId}.env.${args.projectId}`] : []),
104
+ ];
105
+ if (args.checkLocal) {
106
+ filesToCheck.push(`${args.instanceId}.env.local`);
107
+ }
108
+ let noFilesFound = true;
109
+ const combinedParams = {};
110
+ for (const fileToCheck of filesToCheck) {
111
+ try {
112
+ const params = readParamsFile(args.projectDir, fileToCheck);
113
+ logger_1.logger.debug(`Successfully read params from ${fileToCheck}`);
114
+ noFilesFound = false;
115
+ Object.assign(combinedParams, params);
116
+ }
117
+ catch (err) {
118
+ logger_1.logger.debug(`${err}`);
119
+ }
120
+ }
121
+ if (noFilesFound) {
122
+ throw new error_1.FirebaseError(`No params file found for ${args.instanceId}`);
123
+ }
124
+ return combinedParams;
125
+ }
126
+ exports.readInstanceParam = readInstanceParam;
127
+ function readParamsFile(projectDir, fileName) {
128
+ const paramPath = path.join(projectDir, ENV_DIRECTORY, fileName);
129
+ const params = (0, paramHelper_1.readEnvFile)(paramPath);
130
+ return params;
131
+ }
132
+ function showDeprecationWarning() {
133
+ utils.logLabeledWarning(extensionsHelper_1.logPrefix, "The behavior of ext:install, ext:update, ext:configure, and ext:uninstall will change in firebase-tools@11.0.0. " +
134
+ "Instead of deploying extensions directly, " +
135
+ "changes to extension instances will be written to firebase.json and ./extensions/*.env. " +
136
+ `Then ${clc.bold("firebase deploy (--only extensions)")} will deploy the changes to your Firebase project. ` +
137
+ `To access this behavior now, pass the ${clc.bold("--local")} flag.`);
138
+ }
139
+ exports.showDeprecationWarning = showDeprecationWarning;
140
+ function showPreviewWarning() {
141
+ utils.logLabeledWarning(extensionsHelper_1.logPrefix, "These changes will be reflected in your Firebase Emulator after restart. " +
142
+ `Run ${clc.bold("firebase deploy (--only extensions)")} to deploy the changes to your Firebase project. `);
143
+ }
144
+ exports.showPreviewWarning = showPreviewWarning;
@@ -7,19 +7,19 @@ function parseTimeseriesResponse(series) {
7
7
  const ret = [];
8
8
  for (const s of series) {
9
9
  const ref = buildRef(s);
10
- if (ref == undefined) {
10
+ if (ref === undefined) {
11
11
  continue;
12
12
  }
13
13
  let valueToday;
14
14
  let value7dAgo;
15
15
  let value28dAgo;
16
- if (s.points.length >= 28 && s.points[27].value.int64Value != undefined) {
16
+ if (s.points.length >= 28 && s.points[27].value.int64Value !== undefined) {
17
17
  value28dAgo = parseBucket(s.points[27].value.int64Value);
18
18
  }
19
- if (s.points.length >= 7 && s.points[6].value.int64Value != undefined) {
19
+ if (s.points.length >= 7 && s.points[6].value.int64Value !== undefined) {
20
20
  value7dAgo = parseBucket(s.points[6].value.int64Value);
21
21
  }
22
- if (s.points.length >= 1 && s.points[0].value.int64Value != undefined) {
22
+ if (s.points.length >= 1 && s.points[0].value.int64Value !== undefined) {
23
23
  valueToday = parseBucket(s.points[0].value.int64Value);
24
24
  }
25
25
  ret.push({
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readEnvFile = exports.getParamsFromFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = void 0;
3
+ exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = void 0;
4
4
  const _ = require("lodash");
5
5
  const path = require("path");
6
6
  const clc = require("cli-color");
@@ -19,6 +19,7 @@ function setNewDefaults(params, newDefaults) {
19
19
  });
20
20
  return params;
21
21
  }
22
+ exports.setNewDefaults = setNewDefaults;
22
23
  function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
23
24
  const specParams = _.cloneDeep(_.get(extensionInstance, "config.source.spec.params", []));
24
25
  const currentParams = _.cloneDeep(_.get(extensionInstance, "config.params", {}));
@@ -49,7 +50,7 @@ async function getParams(args) {
49
50
  const firebaseProjectParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
50
51
  params = await askUserForParam.ask(args.projectId, args.instanceId, args.paramSpecs, firebaseProjectParams, !!args.reconfiguring);
51
52
  }
52
- track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
53
+ void track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
53
54
  return params;
54
55
  }
55
56
  exports.getParams = getParams;
@@ -82,7 +83,7 @@ async function getParamsForUpdate(args) {
82
83
  instanceId: args.instanceId,
83
84
  });
84
85
  }
85
- track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
86
+ void track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
86
87
  return params;
87
88
  }
88
89
  exports.getParamsForUpdate = getParamsForUpdate;
@@ -91,9 +92,10 @@ async function promptForNewParams(args) {
91
92
  const comparer = (param1, param2) => {
92
93
  return param1.type === param2.type && param1.param === param2.param;
93
94
  };
94
- let paramsDiffDeletions = _.differenceWith(args.spec.params, _.get(args.newSpec, "params", []), comparer);
95
+ const oldParams = args.spec.params.filter((p) => Object.keys(args.currentParams).includes(p.param));
96
+ let paramsDiffDeletions = _.differenceWith(oldParams, args.newSpec.params, comparer);
95
97
  paramsDiffDeletions = (0, extensionsHelper_1.substituteParams)(paramsDiffDeletions, firebaseProjectParams);
96
- let paramsDiffAdditions = _.differenceWith(args.newSpec.params, _.get(args.spec, "params", []), comparer);
98
+ let paramsDiffAdditions = _.differenceWith(args.newSpec.params, oldParams, comparer);
97
99
  paramsDiffAdditions = (0, extensionsHelper_1.substituteParams)(paramsDiffAdditions, firebaseProjectParams);
98
100
  if (paramsDiffDeletions.length) {
99
101
  logger_1.logger.info("The following params will no longer be used:");
@@ -116,10 +118,10 @@ function getParamsFromFile(args) {
116
118
  let envParams;
117
119
  try {
118
120
  envParams = readEnvFile(args.paramsEnvPath);
119
- track("Extension Env File", "Present");
121
+ void track("Extension Env File", "Present");
120
122
  }
121
123
  catch (err) {
122
- track("Extension Env File", "Invalid");
124
+ void track("Extension Env File", "Invalid");
123
125
  throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
124
126
  }
125
127
  const params = (0, extensionsHelper_1.populateDefaultParams)(envParams, args.paramSpecs);
@@ -127,7 +129,6 @@ function getParamsFromFile(args) {
127
129
  logger_1.logger.info(`Using param values from ${args.paramsEnvPath}`);
128
130
  return params;
129
131
  }
130
- exports.getParamsFromFile = getParamsFromFile;
131
132
  function readEnvFile(envPath) {
132
133
  const buf = fs.readFileSync(path.resolve(envPath), "utf8");
133
134
  const result = env.parse(buf.toString().trim());
@@ -20,7 +20,7 @@ function parse(refOrName) {
20
20
  exports.parse = parse;
21
21
  function parseRef(ref) {
22
22
  const parts = refRegex.exec(ref);
23
- if (parts && (parts.length == 5 || parts.length == 7)) {
23
+ if (parts && (parts.length === 5 || parts.length === 7)) {
24
24
  const publisherId = parts[1];
25
25
  const extensionId = parts[2];
26
26
  const version = parts[4];
@@ -15,7 +15,7 @@ async function ensureSecretManagerApiEnabled(options) {
15
15
  }
16
16
  exports.ensureSecretManagerApiEnabled = ensureSecretManagerApiEnabled;
17
17
  function usesSecrets(spec) {
18
- return spec.params && !!spec.params.find((p) => p.type == extensionsApi.ParamType.SECRET);
18
+ return spec.params && !!spec.params.find((p) => p.type === extensionsApi.ParamType.SECRET);
19
19
  }
20
20
  exports.usesSecrets = usesSecrets;
21
21
  async function grantFirexServiceAgentSecretAdminRole(secret) {
@@ -38,7 +38,7 @@ async function getManagedSecrets(instance) {
38
38
  exports.getManagedSecrets = getManagedSecrets;
39
39
  function getActiveSecrets(spec, params) {
40
40
  return spec.params
41
- .map((p) => (p.type == extensionsApi.ParamType.SECRET ? params[p.param] : ""))
41
+ .map((p) => (p.type === extensionsApi.ParamType.SECRET ? params[p.param] : ""))
42
42
  .filter((pv) => !!pv);
43
43
  }
44
44
  exports.getActiveSecrets = getActiveSecrets;
@@ -50,7 +50,7 @@ function getSecretLabels(instanceId) {
50
50
  exports.getSecretLabels = getSecretLabels;
51
51
  function prettySecretName(secretResourceName) {
52
52
  const nameTokens = secretResourceName.split("/");
53
- if (nameTokens.length != 4 && nameTokens.length != 6) {
53
+ if (nameTokens.length !== 4 && nameTokens.length !== 6) {
54
54
  logger_1.logger.debug(`unable to parse secret secretResourceName: ${secretResourceName}`);
55
55
  return secretResourceName;
56
56
  }
package/lib/functional.js CHANGED
@@ -40,7 +40,7 @@ function reduceFlat(accum, next) {
40
40
  }
41
41
  exports.reduceFlat = reduceFlat;
42
42
  function* zip(left, right) {
43
- if (left.length != right.length) {
43
+ if (left.length !== right.length) {
44
44
  throw new Error("Cannot zip between two lists of differen lengths");
45
45
  }
46
46
  for (let i = 0; i < left.length; i++) {
@@ -12,6 +12,7 @@ const RESERVED_PREFIXES = ["X_GOOGLE_", "FIREBASE_", "EXT_"];
12
12
  const RESERVED_KEYS = [
13
13
  "FIREBASE_CONFIG",
14
14
  "CLOUD_RUNTIME_CONFIG",
15
+ "EVENTARC_CLOUD_EVENT_SOURCE",
15
16
  "ENTRY_POINT",
16
17
  "GCP_PROJECT",
17
18
  "GCLOUD_PROJECT",
@@ -120,15 +121,13 @@ function parseStrict(data) {
120
121
  exports.parseStrict = parseStrict;
121
122
  function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
122
123
  const files = [".env"];
124
+ files.push(`.env.${projectId}`);
125
+ if (projectAlias) {
126
+ files.push(`.env.${projectAlias}`);
127
+ }
123
128
  if (isEmulator) {
124
129
  files.push(FUNCTIONS_EMULATOR_DOTENV);
125
130
  }
126
- else {
127
- files.push(`.env.${projectId}`);
128
- if (projectAlias && projectAlias.length) {
129
- files.push(`.env.${projectAlias}`);
130
- }
131
- }
132
131
  return files
133
132
  .map((f) => path.join(functionsSource, f))
134
133
  .filter(fs.existsSync)
@@ -141,7 +140,7 @@ exports.hasUserEnvs = hasUserEnvs;
141
140
  function loadUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
142
141
  var _a;
143
142
  const envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
144
- if (envFiles.length == 0) {
143
+ if (envFiles.length === 0) {
145
144
  return {};
146
145
  }
147
146
  if (projectAlias) {
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
4
+ exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
5
+ exports.STORAGE_EVENTS = [
6
+ "google.cloud.storage.object.v1.finalized",
7
+ "google.cloud.storage.object.v1.archived",
8
+ "google.cloud.storage.object.v1.deleted",
9
+ "google.cloud.storage.object.v1.metadataUpdated",
10
+ ];
11
+ exports.FIREBASE_ALERTS_PUBLISH_EVENT = "google.firebase.firebasealerts.alerts.v1.published";
@@ -92,7 +92,7 @@ async function getIamPolicy(fnName) {
92
92
  }
93
93
  exports.getIamPolicy = getIamPolicy;
94
94
  async function setInvokerCreate(projectId, fnName, invoker) {
95
- if (invoker.length == 0) {
95
+ if (invoker.length === 0) {
96
96
  throw new error_1.FirebaseError("Invoker cannot be an empty array");
97
97
  }
98
98
  const invokerMembers = proto.getInvokerMembers(invoker, projectId);
@@ -108,7 +108,7 @@ async function setInvokerCreate(projectId, fnName, invoker) {
108
108
  exports.setInvokerCreate = setInvokerCreate;
109
109
  async function setInvokerUpdate(projectId, fnName, invoker) {
110
110
  var _a;
111
- if (invoker.length == 0) {
111
+ if (invoker.length === 0) {
112
112
  throw new error_1.FirebaseError("Invoker cannot be an empty array");
113
113
  }
114
114
  const invokerMembers = proto.getInvokerMembers(invoker, projectId);
@@ -202,10 +202,11 @@ async function listAllFunctions(projectId) {
202
202
  }
203
203
  exports.listAllFunctions = listAllFunctions;
204
204
  function endpointFromFunction(gcfFunction) {
205
- var _a, _b, _c;
205
+ var _a, _b, _c, _d;
206
206
  const [, project, , region, , id] = gcfFunction.name.split("/");
207
207
  let trigger;
208
208
  let uri;
209
+ let securityLevel;
209
210
  if ((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) {
210
211
  trigger = {
211
212
  scheduleTrigger: {},
@@ -216,18 +217,27 @@ function endpointFromFunction(gcfFunction) {
216
217
  taskQueueTrigger: {},
217
218
  };
218
219
  }
220
+ else if ((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) {
221
+ trigger = {
222
+ callableTrigger: {},
223
+ };
224
+ }
219
225
  else if (gcfFunction.httpsTrigger) {
220
226
  trigger = { httpsTrigger: {} };
221
227
  uri = gcfFunction.httpsTrigger.url;
228
+ securityLevel = gcfFunction.httpsTrigger.securityLevel;
222
229
  }
223
230
  else {
224
231
  trigger = {
225
232
  eventTrigger: {
226
233
  eventType: gcfFunction.eventTrigger.eventType,
227
- eventFilters: {
228
- resource: gcfFunction.eventTrigger.resource,
229
- },
230
- retry: !!((_c = gcfFunction.eventTrigger.failurePolicy) === null || _c === void 0 ? void 0 : _c.retry),
234
+ eventFilters: [
235
+ {
236
+ attribute: "resource",
237
+ value: gcfFunction.eventTrigger.resource,
238
+ },
239
+ ],
240
+ retry: !!((_d = gcfFunction.eventTrigger.failurePolicy) === null || _d === void 0 ? void 0 : _d.retry),
231
241
  },
232
242
  };
233
243
  }
@@ -240,12 +250,19 @@ function endpointFromFunction(gcfFunction) {
240
250
  if (uri) {
241
251
  endpoint.uri = uri;
242
252
  }
243
- proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
253
+ if (securityLevel) {
254
+ endpoint.securityLevel = securityLevel;
255
+ }
256
+ proto.copyIfPresent(endpoint, gcfFunction, "serviceAccountEmail", "availableMemoryMb", "timeout", "minInstances", "maxInstances", "ingressSettings", "labels", "environmentVariables", "secretEnvironmentVariables", "sourceUploadUrl");
257
+ if (gcfFunction.vpcConnector) {
258
+ endpoint.vpc = { connector: gcfFunction.vpcConnector };
259
+ proto.renameIfPresent(endpoint.vpc, gcfFunction, "egressSettings", "vpcConnectorEgressSettings");
260
+ }
244
261
  return endpoint;
245
262
  }
246
263
  exports.endpointFromFunction = endpointFromFunction;
247
264
  function functionFromEndpoint(endpoint, sourceUploadUrl) {
248
- if (endpoint.platform != "gcfv1") {
265
+ if (endpoint.platform !== "gcfv1") {
249
266
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
250
267
  }
251
268
  if (!runtimes.isValidRuntime(endpoint.runtime)) {
@@ -260,9 +277,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
260
277
  };
261
278
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
262
279
  if (backend.isEventTriggered(endpoint)) {
280
+ const resourceFilter = backend.findEventFilter(endpoint, "resource");
281
+ if (!resourceFilter) {
282
+ throw new error_1.FirebaseError("Invalid event trigger definition. Expected event filter with 'resource' attribute.");
283
+ }
263
284
  gcfFunction.eventTrigger = {
264
285
  eventType: endpoint.eventTrigger.eventType,
265
- resource: endpoint.eventTrigger.eventFilters.resource,
286
+ resource: resourceFilter.value,
266
287
  };
267
288
  gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
268
289
  ? { retry: {} }
@@ -282,8 +303,18 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
282
303
  }
283
304
  else {
284
305
  gcfFunction.httpsTrigger = {};
306
+ if (backend.isCallableTriggered(endpoint)) {
307
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callabled": "true" });
308
+ }
309
+ if (endpoint.securityLevel) {
310
+ gcfFunction.httpsTrigger.securityLevel = endpoint.securityLevel;
311
+ }
312
+ }
313
+ proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
314
+ if (endpoint.vpc) {
315
+ proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnector", "connector");
316
+ proto.renameIfPresent(gcfFunction, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
285
317
  }
286
- proto.copyIfPresent(gcfFunction, endpoint, "serviceAccountEmail", "timeout", "availableMemoryMb", "minInstances", "maxInstances", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables", "secretEnvironmentVariables");
287
318
  return gcfFunction;
288
319
  }
289
320
  exports.functionFromEndpoint = functionFromEndpoint;
@@ -1,11 +1,12 @@
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.PUBSUB_PUBLISH_EVENT = 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.megabytes = 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");
7
7
  const api_1 = require("../api");
8
8
  const logger_1 = require("../logger");
9
+ const v2_1 = require("../functions/events/v2");
9
10
  const backend = require("../deploy/functions/backend");
10
11
  const runtimes = require("../deploy/functions/runtimes");
11
12
  const proto = require("./proto");
@@ -16,7 +17,6 @@ const client = new apiv2_1.Client({
16
17
  auth: true,
17
18
  apiVersion: exports.API_VERSION,
18
19
  });
19
- exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
20
20
  const BYTES_PER_UNIT = {
21
21
  "": 1,
22
22
  k: 1e3,
@@ -106,7 +106,7 @@ async function listFunctionsInternal(projectId, region) {
106
106
  let pageToken = "";
107
107
  while (true) {
108
108
  const url = `projects/${projectId}/locations/${region}/functions`;
109
- const opts = pageToken == "" ? {} : { queryParams: { pageToken } };
109
+ const opts = pageToken === "" ? {} : { queryParams: { pageToken } };
110
110
  const res = await client.get(url, opts);
111
111
  functions.push(...(res.body.functions || []));
112
112
  for (const region of res.body.unreachable || []) {
@@ -122,9 +122,10 @@ async function listFunctionsInternal(projectId, region) {
122
122
  }
123
123
  }
124
124
  async function updateFunction(cloudFunction) {
125
+ const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "serviceConfig.environmentVariables");
125
126
  try {
126
127
  const queryParams = {
127
- updateMask: proto.fieldMasks(cloudFunction).join(","),
128
+ updateMask: fieldMasks.join(","),
128
129
  };
129
130
  const res = await client.patch(cloudFunction.name, cloudFunction, { queryParams });
130
131
  return res.body;
@@ -145,7 +146,7 @@ async function deleteFunction(cloudFunction) {
145
146
  }
146
147
  exports.deleteFunction = deleteFunction;
147
148
  function functionFromEndpoint(endpoint, source) {
148
- if (endpoint.platform != "gcfv2") {
149
+ if (endpoint.platform !== "gcfv2") {
149
150
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
150
151
  }
151
152
  if (!runtimes.isValidRuntime(endpoint.runtime)) {
@@ -165,28 +166,43 @@ function functionFromEndpoint(endpoint, source) {
165
166
  serviceConfig: {},
166
167
  };
167
168
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
168
- proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "vpcConnector", "vpcConnectorEgressSettings", "serviceAccountEmail", "ingressSettings");
169
+ proto.copyIfPresent(gcfFunction.serviceConfig, endpoint, "environmentVariables", "serviceAccountEmail", "ingressSettings");
169
170
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "availableMemory", "availableMemoryMb", (mb) => `${mb}M`);
170
171
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "timeoutSeconds", "timeout", proto.secondsFromDuration);
171
172
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "minInstanceCount", "minInstances");
172
173
  proto.renameIfPresent(gcfFunction.serviceConfig, endpoint, "maxInstanceCount", "maxInstances");
174
+ if (endpoint.vpc) {
175
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint.vpc, "vpcConnector", "connector");
176
+ proto.renameIfPresent(gcfFunction.serviceConfig, endpoint.vpc, "vpcConnectorEgressSettings", "egressSettings");
177
+ }
173
178
  if (backend.isEventTriggered(endpoint)) {
174
179
  gcfFunction.eventTrigger = {
175
180
  eventType: endpoint.eventTrigger.eventType,
176
181
  };
177
- if (gcfFunction.eventTrigger.eventType === exports.PUBSUB_PUBLISH_EVENT) {
178
- gcfFunction.eventTrigger.pubsubTopic = endpoint.eventTrigger.eventFilters.resource;
182
+ if (gcfFunction.eventTrigger.eventType === v2_1.PUBSUB_PUBLISH_EVENT) {
183
+ const pubsubFilter = backend.findEventFilter(endpoint, "topic");
184
+ if (!pubsubFilter) {
185
+ throw new error_1.FirebaseError("Invalid pubsub endpoint. Expected eventFilter with 'topic' attribute but found none.");
186
+ }
187
+ gcfFunction.eventTrigger.pubsubTopic = pubsubFilter.value;
188
+ for (const filter of endpoint.eventTrigger.eventFilters) {
189
+ if (filter.attribute === "topic") {
190
+ continue;
191
+ }
192
+ if (!gcfFunction.eventTrigger.eventFilters) {
193
+ gcfFunction.eventTrigger.eventFilters = [];
194
+ }
195
+ gcfFunction.eventTrigger.eventFilters.push(filter);
196
+ }
179
197
  }
180
198
  else {
181
- gcfFunction.eventTrigger.eventFilters = [];
182
- for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
183
- gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
184
- }
199
+ gcfFunction.eventTrigger.eventFilters = endpoint.eventTrigger.eventFilters;
185
200
  }
186
201
  proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
187
202
  if (endpoint.eventTrigger.retry) {
188
203
  logger_1.logger.warn("Cannot set a retry policy on Cloud Function", endpoint.id);
189
204
  }
205
+ gcfFunction.serviceConfig.environmentVariables = Object.assign(Object.assign({}, gcfFunction.serviceConfig.environmentVariables), { FUNCTION_SIGNATURE_TYPE: "cloudevent" });
190
206
  }
191
207
  else if (backend.isScheduleTriggered(endpoint)) {
192
208
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-scheduled": "true" });
@@ -194,11 +210,14 @@ function functionFromEndpoint(endpoint, source) {
194
210
  else if (backend.isTaskQueueTriggered(endpoint)) {
195
211
  gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-taskqueue": "true" });
196
212
  }
213
+ else if (backend.isCallableTriggered(endpoint)) {
214
+ gcfFunction.labels = Object.assign(Object.assign({}, gcfFunction.labels), { "deployment-callable": "true" });
215
+ }
197
216
  return gcfFunction;
198
217
  }
199
218
  exports.functionFromEndpoint = functionFromEndpoint;
200
219
  function endpointFromFunction(gcfFunction) {
201
- var _a, _b;
220
+ var _a, _b, _c;
202
221
  const [, project, , region, , id] = gcfFunction.name.split("/");
203
222
  let trigger;
204
223
  if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
@@ -211,20 +230,28 @@ function endpointFromFunction(gcfFunction) {
211
230
  taskQueueTrigger: {},
212
231
  };
213
232
  }
233
+ else if (((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) === "true") {
234
+ trigger = {
235
+ callableTrigger: {},
236
+ };
237
+ }
214
238
  else if (gcfFunction.eventTrigger) {
215
239
  trigger = {
216
240
  eventTrigger: {
217
241
  eventType: gcfFunction.eventTrigger.eventType,
218
- eventFilters: {},
242
+ eventFilters: [],
219
243
  retry: false,
220
244
  },
221
245
  };
222
246
  if (gcfFunction.eventTrigger.pubsubTopic) {
223
- trigger.eventTrigger.eventFilters.resource = gcfFunction.eventTrigger.pubsubTopic;
247
+ trigger.eventTrigger.eventFilters.push({
248
+ attribute: "topic",
249
+ value: gcfFunction.eventTrigger.pubsubTopic,
250
+ });
224
251
  }
225
252
  else {
226
253
  for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
227
- trigger.eventTrigger.eventFilters[attribute] = value;
254
+ trigger.eventTrigger.eventFilters.push({ attribute, value });
228
255
  }
229
256
  }
230
257
  proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
@@ -238,12 +265,16 @@ function endpointFromFunction(gcfFunction) {
238
265
  const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
239
266
  project,
240
267
  region }, trigger), { entryPoint: gcfFunction.buildConfig.entryPoint, runtime: gcfFunction.buildConfig.runtime, uri: gcfFunction.serviceConfig.uri });
241
- proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "vpcConnector", "vpcConnectorEgressSettings", "ingressSettings", "environmentVariables");
268
+ proto.copyIfPresent(endpoint, gcfFunction.serviceConfig, "serviceAccountEmail", "ingressSettings", "environmentVariables");
242
269
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "availableMemoryMb", "availableMemory", megabytes);
243
270
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "timeout", "timeoutSeconds", proto.durationFromSeconds);
244
271
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "minInstances", "minInstanceCount");
245
272
  proto.renameIfPresent(endpoint, gcfFunction.serviceConfig, "maxInstances", "maxInstanceCount");
246
273
  proto.copyIfPresent(endpoint, gcfFunction, "labels");
274
+ if (gcfFunction.serviceConfig.vpcConnector) {
275
+ endpoint.vpc = { connector: gcfFunction.serviceConfig.vpcConnector };
276
+ proto.renameIfPresent(endpoint.vpc, gcfFunction.serviceConfig, "egressSettings", "vpcConnectorEgressSettings");
277
+ }
247
278
  return endpoint;
248
279
  }
249
280
  exports.endpointFromFunction = endpointFromFunction;
@@ -102,7 +102,7 @@ async function setEnqueuer(name, invoker, assumeEmpty = false) {
102
102
  const invokerMembers = proto.getInvokerMembers(invoker, project);
103
103
  while (true) {
104
104
  const policy = {
105
- bindings: existing.bindings.filter((binding) => binding.role != ENQUEUER_ROLE),
105
+ bindings: existing.bindings.filter((binding) => binding.role !== ENQUEUER_ROLE),
106
106
  etag: existing.etag,
107
107
  version: existing.version,
108
108
  };
package/lib/gcp/docker.js CHANGED
@@ -58,7 +58,7 @@ class Client {
58
58
  if (!response.body) {
59
59
  return;
60
60
  }
61
- if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
61
+ if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) !== 0) {
62
62
  throw new error_1.FirebaseError(`Failed to delete tag ${tag} at path ${path}`, {
63
63
  children: response.body.errors,
64
64
  });
@@ -70,7 +70,7 @@ class Client {
70
70
  if (!response.body) {
71
71
  return;
72
72
  }
73
- if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) != 0) {
73
+ if (((_a = response.body.errors) === null || _a === void 0 ? void 0 : _a.length) !== 0) {
74
74
  throw new error_1.FirebaseError(`Failed to delete image ${digest} at path ${path}`, {
75
75
  children: response.body.errors,
76
76
  });