firebase-tools 10.2.2 → 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 (54) hide show
  1. package/lib/commands/ext-configure.js +58 -4
  2. package/lib/commands/ext-export.js +4 -9
  3. package/lib/commands/ext-install.js +63 -4
  4. package/lib/commands/ext-uninstall.js +9 -0
  5. package/lib/commands/ext-update.js +55 -2
  6. package/lib/config.js +6 -3
  7. package/lib/deploy/extensions/planner.js +6 -6
  8. package/lib/deploy/functions/backend.js +10 -1
  9. package/lib/deploy/functions/checkIam.js +4 -4
  10. package/lib/deploy/functions/prepare.js +2 -1
  11. package/lib/deploy/functions/release/fabricator.js +4 -4
  12. package/lib/deploy/functions/release/planner.js +34 -20
  13. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +6 -1
  14. package/lib/deploy/functions/runtimes/node/index.js +27 -0
  15. package/lib/deploy/functions/runtimes/node/parseTriggers.js +24 -8
  16. package/lib/deploy/functions/services/firebaseAlerts.js +30 -0
  17. package/lib/deploy/functions/services/index.js +9 -1
  18. package/lib/deploy/functions/services/storage.js +10 -4
  19. package/lib/deploy/functions/triggerRegionHelper.js +1 -1
  20. package/lib/emulator/constants.js +1 -0
  21. package/lib/emulator/controller.js +9 -7
  22. package/lib/emulator/extensions/validation.js +37 -2
  23. package/lib/emulator/extensionsEmulator.js +44 -9
  24. package/lib/emulator/functionsEmulator.js +13 -8
  25. package/lib/emulator/functionsEmulatorShared.js +17 -10
  26. package/lib/emulator/storage/apis/firebase.js +312 -335
  27. package/lib/emulator/storage/apis/gcloud.js +238 -113
  28. package/lib/emulator/storage/crc.js +5 -1
  29. package/lib/emulator/storage/errors.js +9 -0
  30. package/lib/emulator/storage/files.js +161 -304
  31. package/lib/emulator/storage/index.js +27 -73
  32. package/lib/emulator/storage/metadata.js +63 -49
  33. package/lib/emulator/storage/multipart.js +62 -0
  34. package/lib/emulator/storage/persistence.js +78 -0
  35. package/lib/emulator/storage/rules/config.js +33 -0
  36. package/lib/emulator/storage/rules/manager.js +81 -0
  37. package/lib/emulator/storage/rules/utils.js +48 -0
  38. package/lib/emulator/storage/server.js +2 -2
  39. package/lib/emulator/storage/upload.js +106 -0
  40. package/lib/extensions/emulator/optionsHelper.js +35 -3
  41. package/lib/extensions/extensionsHelper.js +19 -10
  42. package/lib/extensions/manifest.js +109 -13
  43. package/lib/extensions/paramHelper.js +5 -4
  44. package/lib/functions/env.js +4 -6
  45. package/lib/functions/events/v2.js +11 -0
  46. package/lib/gcp/cloudfunctions.js +18 -6
  47. package/lib/gcp/cloudfunctionsv2.js +30 -12
  48. package/lib/gcp/resourceManager.js +4 -4
  49. package/lib/serve/functions.js +2 -1
  50. package/lib/utils.js +14 -1
  51. package/npm-shrinkwrap.json +2 -2
  52. package/package.json +1 -1
  53. package/lib/deploy/extensions/params.js +0 -42
  54. package/lib/deploy/functions/eventTypes.js +0 -10
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UploadService = exports.NotCancellableError = exports.UploadNotActiveError = exports.UploadStatus = exports.UploadType = void 0;
4
+ const uuid_1 = require("uuid");
5
+ const errors_1 = require("./errors");
6
+ var UploadType;
7
+ (function (UploadType) {
8
+ UploadType[UploadType["MULTIPART"] = 0] = "MULTIPART";
9
+ UploadType[UploadType["RESUMABLE"] = 1] = "RESUMABLE";
10
+ })(UploadType = exports.UploadType || (exports.UploadType = {}));
11
+ var UploadStatus;
12
+ (function (UploadStatus) {
13
+ UploadStatus[UploadStatus["ACTIVE"] = 0] = "ACTIVE";
14
+ UploadStatus[UploadStatus["CANCELLED"] = 1] = "CANCELLED";
15
+ UploadStatus[UploadStatus["FINISHED"] = 2] = "FINISHED";
16
+ })(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
17
+ class UploadNotActiveError extends Error {
18
+ }
19
+ exports.UploadNotActiveError = UploadNotActiveError;
20
+ class NotCancellableError extends Error {
21
+ }
22
+ exports.NotCancellableError = NotCancellableError;
23
+ class UploadService {
24
+ constructor(_persistence) {
25
+ this._persistence = _persistence;
26
+ this.reset();
27
+ }
28
+ reset() {
29
+ this._uploads = new Map();
30
+ }
31
+ multipartUpload(request) {
32
+ const upload = this.startMultipartUpload(request, request.dataRaw.byteLength);
33
+ this._persistence.deleteFile(upload.path, true);
34
+ this._persistence.appendBytes(upload.path, request.dataRaw);
35
+ return upload;
36
+ }
37
+ startMultipartUpload(request, sizeInBytes) {
38
+ const id = (0, uuid_1.v4)();
39
+ const upload = {
40
+ id: (0, uuid_1.v4)(),
41
+ bucketId: request.bucketId,
42
+ objectId: request.objectId,
43
+ type: UploadType.MULTIPART,
44
+ path: this.getStagingFileName(id, request.bucketId, request.objectId),
45
+ status: UploadStatus.FINISHED,
46
+ metadata: JSON.parse(request.metadataRaw),
47
+ size: sizeInBytes,
48
+ authorization: request.authorization,
49
+ };
50
+ this._uploads.set(upload.id, upload);
51
+ return upload;
52
+ }
53
+ startResumableUpload(request) {
54
+ const id = (0, uuid_1.v4)();
55
+ const upload = {
56
+ id: id,
57
+ bucketId: request.bucketId,
58
+ objectId: request.objectId,
59
+ type: UploadType.RESUMABLE,
60
+ path: this.getStagingFileName(id, request.bucketId, request.objectId),
61
+ status: UploadStatus.ACTIVE,
62
+ metadata: JSON.parse(request.metadataRaw),
63
+ size: 0,
64
+ authorization: request.authorization,
65
+ };
66
+ this._uploads.set(upload.id, upload);
67
+ this._persistence.deleteFile(upload.path, true);
68
+ return upload;
69
+ }
70
+ continueResumableUpload(uploadId, dataRaw) {
71
+ const upload = this.getResumableUpload(uploadId);
72
+ if (upload.status !== UploadStatus.ACTIVE) {
73
+ throw new UploadNotActiveError();
74
+ }
75
+ this._persistence.appendBytes(upload.path, dataRaw);
76
+ upload.size += dataRaw.byteLength;
77
+ return upload;
78
+ }
79
+ getResumableUpload(uploadId) {
80
+ const upload = this._uploads.get(uploadId);
81
+ if (!upload || upload.type !== UploadType.RESUMABLE) {
82
+ throw new errors_1.NotFoundError();
83
+ }
84
+ return upload;
85
+ }
86
+ cancelResumableUpload(uploadId) {
87
+ const upload = this.getResumableUpload(uploadId);
88
+ if (upload.status === UploadStatus.FINISHED) {
89
+ throw new NotCancellableError();
90
+ }
91
+ upload.status = UploadStatus.CANCELLED;
92
+ return upload;
93
+ }
94
+ finalizeResumableUpload(uploadId) {
95
+ const upload = this.getResumableUpload(uploadId);
96
+ if (upload.status === UploadStatus.CANCELLED) {
97
+ throw new UploadNotActiveError();
98
+ }
99
+ upload.status = UploadStatus.FINISHED;
100
+ return upload;
101
+ }
102
+ getStagingFileName(uploadId, bucketId, objectId) {
103
+ return encodeURIComponent(`${uploadId}_b_${bucketId}_o_${objectId}`);
104
+ }
105
+ }
106
+ exports.UploadService = UploadService;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getParams = exports.getExtensionFunctionInfo = exports.buildOptions = void 0;
3
+ exports.getParams = exports.getSecretEnvVars = exports.getNonSecretEnv = exports.getExtensionFunctionInfo = exports.buildOptions = void 0;
4
4
  const fs = require("fs-extra");
5
5
  const _ = require("lodash");
6
6
  const path = require("path");
@@ -8,6 +8,7 @@ const paramHelper = require("../paramHelper");
8
8
  const specHelper = require("./specHelper");
9
9
  const localHelper = require("../localHelper");
10
10
  const triggerHelper = require("./triggerHelper");
11
+ const extensionsApi_1 = require("../extensionsApi");
11
12
  const extensionsHelper = require("../extensionsHelper");
12
13
  const config_1 = require("../../config");
13
14
  const error_1 = require("../../error");
@@ -35,9 +36,9 @@ async function buildOptions(options) {
35
36
  return options;
36
37
  }
37
38
  exports.buildOptions = buildOptions;
38
- async function getExtensionFunctionInfo(extensionDir, instanceId, params) {
39
+ async function getExtensionFunctionInfo(extensionDir, instanceId, paramValues) {
39
40
  const spec = await specHelper.readExtensionYaml(extensionDir);
40
- const functionResources = specHelper.getFunctionResourcesWithParamSubstitution(spec, params);
41
+ const functionResources = specHelper.getFunctionResourcesWithParamSubstitution(spec, paramValues);
41
42
  const extensionTriggers = functionResources
42
43
  .map((r) => triggerHelper.functionResourceToEmulatedTriggerDefintion(r))
43
44
  .map((trigger) => {
@@ -45,12 +46,43 @@ async function getExtensionFunctionInfo(extensionDir, instanceId, params) {
45
46
  return trigger;
46
47
  });
47
48
  const nodeMajorVersion = specHelper.getNodeVersion(functionResources);
49
+ const nonSecretEnv = getNonSecretEnv(spec.params, paramValues);
50
+ const secretEnvVariables = getSecretEnvVars(spec.params, paramValues);
48
51
  return {
49
52
  extensionTriggers,
50
53
  nodeMajorVersion,
54
+ nonSecretEnv,
55
+ secretEnvVariables,
51
56
  };
52
57
  }
53
58
  exports.getExtensionFunctionInfo = getExtensionFunctionInfo;
59
+ const isSecretParam = (p) => p.type === extensionsHelper.SpecParamType.SECRET || p.type === extensionsApi_1.ParamType.SECRET;
60
+ function getNonSecretEnv(params, paramValues) {
61
+ const getNonSecretEnv = Object.assign({}, paramValues);
62
+ const secretParams = params.filter(isSecretParam);
63
+ for (const p of secretParams) {
64
+ delete getNonSecretEnv[p.param];
65
+ }
66
+ return getNonSecretEnv;
67
+ }
68
+ exports.getNonSecretEnv = getNonSecretEnv;
69
+ function getSecretEnvVars(params, paramValues) {
70
+ const secretEnvVar = [];
71
+ const secretParams = params.filter(isSecretParam);
72
+ for (const s of secretParams) {
73
+ if (paramValues[s.param]) {
74
+ const [, projectId, , secret, , version] = paramValues[s.param].split("/");
75
+ secretEnvVar.push({
76
+ key: s.param,
77
+ secret,
78
+ projectId,
79
+ version,
80
+ });
81
+ }
82
+ }
83
+ return secretEnvVar;
84
+ }
85
+ exports.getSecretEnvVars = getSecretEnvVars;
54
86
  function getParams(options, extensionSpec) {
55
87
  const projectId = (0, projectUtils_1.needProjectId)(options);
56
88
  const userParams = paramHelper.readEnvFile(options.testParams);
@@ -14,6 +14,7 @@ const api_1 = require("../api");
14
14
  const archiveDirectory_1 = require("../archiveDirectory");
15
15
  const utils_1 = require("./utils");
16
16
  const functionsConfig_1 = require("../functionsConfig");
17
+ const adminSdkConfig_1 = require("../emulator/adminSdkConfig");
17
18
  const resolveSource_1 = require("./resolveSource");
18
19
  const error_1 = require("../error");
19
20
  const diagnose_1 = require("./diagnose");
@@ -29,6 +30,7 @@ const logger_1 = require("../logger");
29
30
  const utils_2 = require("../utils");
30
31
  const changelog_1 = require("./changelog");
31
32
  const getProjectNumber_1 = require("../getProjectNumber");
33
+ const constants_1 = require("../emulator/constants");
32
34
  var SpecParamType;
33
35
  (function (SpecParamType) {
34
36
  SpecParamType["SELECT"] = "select";
@@ -76,21 +78,28 @@ function getDBInstanceFromURL(databaseUrl = "") {
76
78
  return "";
77
79
  }
78
80
  exports.getDBInstanceFromURL = getDBInstanceFromURL;
79
- async function getFirebaseProjectParams(projectId) {
80
- const body = await (0, functionsConfig_1.getFirebaseConfig)({ project: projectId });
81
- const projectNumber = await (0, getProjectNumber_1.getProjectNumber)({ projectId });
81
+ async function getFirebaseProjectParams(projectId, emulatorMode = false) {
82
+ var _a, _b;
83
+ const body = emulatorMode
84
+ ? await (0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)
85
+ : await (0, functionsConfig_1.getFirebaseConfig)({ project: projectId });
86
+ const projectNumber = emulatorMode && constants_1.Constants.isDemoProject(projectId)
87
+ ? constants_1.Constants.FAKE_PROJECT_NUMBER
88
+ : await (0, getProjectNumber_1.getProjectNumber)({ projectId });
89
+ const databaseURL = (_a = body === null || body === void 0 ? void 0 : body.databaseURL) !== null && _a !== void 0 ? _a : `https://${projectId}.firebaseio.com`;
90
+ const storageBucket = (_b = body === null || body === void 0 ? void 0 : body.storageBucket) !== null && _b !== void 0 ? _b : `${projectId}.appspot.com`;
82
91
  const FIREBASE_CONFIG = JSON.stringify({
83
- projectId: body.projectId,
84
- databaseURL: body.databaseURL,
85
- storageBucket: body.storageBucket,
92
+ projectId,
93
+ databaseURL,
94
+ storageBucket,
86
95
  });
87
96
  return {
88
- PROJECT_ID: body.projectId,
97
+ PROJECT_ID: projectId,
89
98
  PROJECT_NUMBER: projectNumber,
90
- DATABASE_URL: body.databaseURL,
91
- STORAGE_BUCKET: body.storageBucket,
99
+ DATABASE_URL: databaseURL,
100
+ STORAGE_BUCKET: storageBucket,
92
101
  FIREBASE_CONFIG,
93
- DATABASE_INSTANCE: getDBInstanceFromURL(body.databaseURL),
102
+ DATABASE_INSTANCE: getDBInstanceFromURL(databaseURL),
94
103
  };
95
104
  }
96
105
  exports.getFirebaseProjectParams = getFirebaseProjectParams;
@@ -1,11 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeToManifest = void 0;
3
+ exports.showPreviewWarning = exports.showDeprecationWarning = exports.readInstanceParam = exports.getInstanceRef = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeToManifest = void 0;
4
4
  const clc = require("cli-color");
5
+ const path = require("path");
5
6
  const refs = require("./refs");
7
+ const config_1 = require("../config");
6
8
  const logger_1 = require("../logger");
7
9
  const prompt_1 = require("../prompt");
8
- async function writeToManifest(specs, config, options) {
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) {
9
16
  if (config.has("extensions") &&
10
17
  Object.keys(config.get("extensions")).length &&
11
18
  !options.nonInteractive &&
@@ -13,36 +20,125 @@ async function writeToManifest(specs, config, options) {
13
20
  const currentExtensions = Object.entries(config.get("extensions"))
14
21
  .map((i) => `${i[0]}: ${i[1]}`)
15
22
  .join("\n\t");
16
- const overwrite = await (0, prompt_1.promptOnce)({
17
- type: "list",
18
- message: `firebase.json already contains extensions:\n${currentExtensions}\nWould you like to overwrite or merge?`,
19
- choices: [
20
- { name: "Overwrite", value: true },
21
- { name: "Merge", value: false },
22
- ],
23
- });
24
- if (overwrite) {
25
- config.set("extensions", {});
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
+ }
26
35
  }
27
36
  }
28
37
  writeExtensionsToFirebaseJson(specs, config);
29
38
  await writeEnvFiles(specs, config, options.force);
30
39
  }
31
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;
32
76
  function writeExtensionsToFirebaseJson(specs, config) {
33
77
  const extensions = config.get("extensions", {});
34
78
  for (const s of specs) {
35
79
  extensions[s.instanceId] = refs.toExtensionVersionRef(s.ref);
36
80
  }
37
81
  config.set("extensions", extensions);
38
- logger_1.logger.info("Adding Extensions to " + clc.bold("firebase.json") + "...");
39
82
  config.writeProjectFile("firebase.json", config.src);
83
+ utils.logSuccess("Wrote extensions to " + clc.bold("firebase.json") + "...");
40
84
  }
41
85
  async function writeEnvFiles(specs, config, force) {
42
86
  for (const spec of specs) {
43
87
  const content = Object.entries(spec.params)
88
+ .sort((a, b) => {
89
+ return a[0].localeCompare(b[0]);
90
+ })
44
91
  .map((r) => `${r[0]}=${r[1]}`)
45
92
  .join("\n");
46
93
  await config.askWriteProjectFile(`extensions/${spec.instanceId}.env`, content, force);
47
94
  }
48
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;
@@ -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", {}));
@@ -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:");
@@ -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());
@@ -121,15 +121,13 @@ function parseStrict(data) {
121
121
  exports.parseStrict = parseStrict;
122
122
  function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
123
123
  const files = [".env"];
124
+ files.push(`.env.${projectId}`);
125
+ if (projectAlias) {
126
+ files.push(`.env.${projectAlias}`);
127
+ }
124
128
  if (isEmulator) {
125
129
  files.push(FUNCTIONS_EMULATOR_DOTENV);
126
130
  }
127
- else {
128
- files.push(`.env.${projectId}`);
129
- if (projectAlias && projectAlias.length) {
130
- files.push(`.env.${projectAlias}`);
131
- }
132
- }
133
131
  return files
134
132
  .map((f) => path.join(functionsSource, f))
135
133
  .filter(fs.existsSync)
@@ -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";
@@ -202,7 +202,7 @@ 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;
@@ -217,6 +217,11 @@ function endpointFromFunction(gcfFunction) {
217
217
  taskQueueTrigger: {},
218
218
  };
219
219
  }
220
+ else if ((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) {
221
+ trigger = {
222
+ callableTrigger: {},
223
+ };
224
+ }
220
225
  else if (gcfFunction.httpsTrigger) {
221
226
  trigger = { httpsTrigger: {} };
222
227
  uri = gcfFunction.httpsTrigger.url;
@@ -226,10 +231,13 @@ function endpointFromFunction(gcfFunction) {
226
231
  trigger = {
227
232
  eventTrigger: {
228
233
  eventType: gcfFunction.eventTrigger.eventType,
229
- eventFilters: {
230
- resource: gcfFunction.eventTrigger.resource,
231
- },
232
- 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),
233
241
  },
234
242
  };
235
243
  }
@@ -269,9 +277,13 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
269
277
  };
270
278
  proto.copyIfPresent(gcfFunction, endpoint, "labels");
271
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
+ }
272
284
  gcfFunction.eventTrigger = {
273
285
  eventType: endpoint.eventTrigger.eventType,
274
- resource: endpoint.eventTrigger.eventFilters.resource,
286
+ resource: resourceFilter.value,
275
287
  };
276
288
  gcfFunction.eventTrigger.failurePolicy = endpoint.eventTrigger.retry
277
289
  ? { retry: {} }
@@ -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,
@@ -179,14 +179,24 @@ function functionFromEndpoint(endpoint, source) {
179
179
  gcfFunction.eventTrigger = {
180
180
  eventType: endpoint.eventTrigger.eventType,
181
181
  };
182
- if (gcfFunction.eventTrigger.eventType === exports.PUBSUB_PUBLISH_EVENT) {
183
- 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
+ }
184
197
  }
185
198
  else {
186
- gcfFunction.eventTrigger.eventFilters = [];
187
- for (const [attribute, value] of Object.entries(endpoint.eventTrigger.eventFilters)) {
188
- gcfFunction.eventTrigger.eventFilters.push({ attribute, value });
189
- }
199
+ gcfFunction.eventTrigger.eventFilters = endpoint.eventTrigger.eventFilters;
190
200
  }
191
201
  proto.renameIfPresent(gcfFunction.eventTrigger, endpoint.eventTrigger, "triggerRegion", "region");
192
202
  if (endpoint.eventTrigger.retry) {
@@ -207,7 +217,7 @@ function functionFromEndpoint(endpoint, source) {
207
217
  }
208
218
  exports.functionFromEndpoint = functionFromEndpoint;
209
219
  function endpointFromFunction(gcfFunction) {
210
- var _a, _b;
220
+ var _a, _b, _c;
211
221
  const [, project, , region, , id] = gcfFunction.name.split("/");
212
222
  let trigger;
213
223
  if (((_a = gcfFunction.labels) === null || _a === void 0 ? void 0 : _a["deployment-scheduled"]) === "true") {
@@ -220,20 +230,28 @@ function endpointFromFunction(gcfFunction) {
220
230
  taskQueueTrigger: {},
221
231
  };
222
232
  }
233
+ else if (((_c = gcfFunction.labels) === null || _c === void 0 ? void 0 : _c["deployment-callable"]) === "true") {
234
+ trigger = {
235
+ callableTrigger: {},
236
+ };
237
+ }
223
238
  else if (gcfFunction.eventTrigger) {
224
239
  trigger = {
225
240
  eventTrigger: {
226
241
  eventType: gcfFunction.eventTrigger.eventType,
227
- eventFilters: {},
242
+ eventFilters: [],
228
243
  retry: false,
229
244
  },
230
245
  };
231
246
  if (gcfFunction.eventTrigger.pubsubTopic) {
232
- trigger.eventTrigger.eventFilters.resource = gcfFunction.eventTrigger.pubsubTopic;
247
+ trigger.eventTrigger.eventFilters.push({
248
+ attribute: "topic",
249
+ value: gcfFunction.eventTrigger.pubsubTopic,
250
+ });
233
251
  }
234
252
  else {
235
253
  for (const { attribute, value } of gcfFunction.eventTrigger.eventFilters || []) {
236
- trigger.eventTrigger.eventFilters[attribute] = value;
254
+ trigger.eventTrigger.eventFilters.push({ attribute, value });
237
255
  }
238
256
  }
239
257
  proto.renameIfPresent(trigger.eventTrigger, gcfFunction.eventTrigger, "region", "triggerRegion");
@@ -13,13 +13,13 @@ exports.firebaseRoles = {
13
13
  hostingAdmin: "roles/firebasehosting.admin",
14
14
  runViewer: "roles/run.viewer",
15
15
  };
16
- async function getIamPolicy(projectId) {
17
- const response = await apiClient.post(`/projects/${projectId}:getIamPolicy`);
16
+ async function getIamPolicy(projectIdOrNumber) {
17
+ const response = await apiClient.post(`/projects/${projectIdOrNumber}:getIamPolicy`);
18
18
  return response.body;
19
19
  }
20
20
  exports.getIamPolicy = getIamPolicy;
21
- async function setIamPolicy(projectId, newPolicy, updateMask = "") {
22
- const response = await apiClient.post(`/projects/${projectId}:setIamPolicy`, {
21
+ async function setIamPolicy(projectIdOrNumber, newPolicy, updateMask = "") {
22
+ const response = await apiClient.post(`/projects/${projectIdOrNumber}:setIamPolicy`, {
23
23
  policy: newPolicy,
24
24
  updateMask: updateMask,
25
25
  });
@@ -29,8 +29,9 @@ class FunctionsServer {
29
29
  functionsDir,
30
30
  nodeMajorVersion,
31
31
  env: {},
32
+ secretEnv: [],
32
33
  };
33
- const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: [this.backend], account }, partialArgs);
34
+ const args = Object.assign({ projectId, projectDir: options.config.projectDir, emulatableBackends: [this.backend], projectAlias: options.projectAlias, account }, partialArgs);
34
35
  if (options.host) {
35
36
  utils.assertIsStringOrUndefined(options.host);
36
37
  args.host = options.host;