firebase-tools 10.7.1 → 10.9.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 (63) hide show
  1. package/lib/commands/ext-configure.js +26 -15
  2. package/lib/commands/ext-export.js +14 -5
  3. package/lib/commands/ext-install.js +31 -2
  4. package/lib/commands/ext-update.js +17 -10
  5. package/lib/commands/functions-list.js +12 -20
  6. package/lib/commands/functions-secrets-set.js +1 -13
  7. package/lib/deploy/extensions/planner.js +12 -0
  8. package/lib/deploy/extensions/tasks.js +13 -0
  9. package/lib/deploy/functions/backend.js +47 -14
  10. package/lib/deploy/functions/build.js +28 -9
  11. package/lib/deploy/functions/checkIam.js +65 -53
  12. package/lib/deploy/functions/containerCleaner.js +8 -7
  13. package/lib/deploy/functions/functionsDeployHelper.js +1 -1
  14. package/lib/deploy/functions/prepare.js +42 -15
  15. package/lib/deploy/functions/pricing.js +2 -2
  16. package/lib/deploy/functions/release/executor.js +1 -1
  17. package/lib/deploy/functions/release/fabricator.js +66 -11
  18. package/lib/deploy/functions/release/index.js +0 -21
  19. package/lib/deploy/functions/runtimes/discovery/index.js +2 -1
  20. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +13 -1
  21. package/lib/deploy/functions/runtimes/golang/index.js +3 -0
  22. package/lib/deploy/functions/runtimes/node/index.js +23 -20
  23. package/lib/deploy/functions/runtimes/node/parseTriggers.js +108 -1
  24. package/lib/deploy/functions/services/storage.js +6 -12
  25. package/lib/deploy/functions/validate.js +80 -16
  26. package/lib/deploy/index.js +2 -1
  27. package/lib/emulator/auth/cloudFunctions.js +6 -2
  28. package/lib/emulator/auth/operations.js +0 -1
  29. package/lib/emulator/auth/server.js +8 -1
  30. package/lib/emulator/auth/state.js +27 -24
  31. package/lib/emulator/controller.js +10 -5
  32. package/lib/emulator/databaseEmulator.js +36 -3
  33. package/lib/emulator/downloadableEmulators.js +18 -34
  34. package/lib/emulator/extensionsEmulator.js +4 -1
  35. package/lib/emulator/functionsEmulator.js +6 -7
  36. package/lib/emulator/functionsEmulatorRuntime.js +1 -1
  37. package/lib/emulator/functionsEmulatorShared.js +3 -0
  38. package/lib/emulator/functionsEmulatorUtils.js +5 -1
  39. package/lib/emulator/storage/apis/firebase.js +26 -4
  40. package/lib/extensions/askUserForEventsConfig.js +97 -0
  41. package/lib/extensions/export.js +7 -0
  42. package/lib/extensions/extensionsApi.js +47 -7
  43. package/lib/extensions/manifest.js +1 -1
  44. package/lib/extensions/paramHelper.js +2 -0
  45. package/lib/extensions/updateHelper.js +7 -1
  46. package/lib/extensions/warnings.js +11 -4
  47. package/lib/frameworks/index.js +111 -0
  48. package/lib/functions/functionslog.js +4 -9
  49. package/lib/gcp/cloudfunctions.js +1 -1
  50. package/lib/gcp/cloudfunctionsv2.js +14 -9
  51. package/lib/gcp/serviceusage.js +24 -0
  52. package/lib/hosting/normalizedHostingConfigs.js +3 -0
  53. package/lib/previews.js +1 -1
  54. package/lib/serve/index.js +2 -1
  55. package/lib/throttler/throttler.js +2 -1
  56. package/npm-shrinkwrap.json +315 -534
  57. package/package.json +4 -4
  58. package/templates/extensions/javascript/package.lint.json +5 -5
  59. package/templates/extensions/javascript/package.nolint.json +3 -3
  60. package/templates/extensions/typescript/package.lint.json +8 -7
  61. package/templates/extensions/typescript/package.nolint.json +2 -1
  62. package/templates/init/functions/typescript/package.lint.json +1 -0
  63. package/templates/init/functions/typescript/package.nolint.json +5 -5
@@ -65,7 +65,7 @@ class ExtensionsEmulator {
65
65
  if (!this.hasValidSource({ path: instance.localPath, extTarget: instance.localPath })) {
66
66
  throw new error_1.FirebaseError(`Tried to emulate local extension at ${instance.localPath}, but it was missing required files.`);
67
67
  }
68
- return instance.localPath;
68
+ return path.resolve(instance.localPath);
69
69
  }
70
70
  else if (instance.ref) {
71
71
  const ref = (0, refs_1.toExtensionVersionRef)(instance.ref);
@@ -152,6 +152,7 @@ class ExtensionsEmulator {
152
152
  return emulatableBackend;
153
153
  }
154
154
  autoPopulatedParams(instance) {
155
+ var _a;
155
156
  const projectId = this.args.projectId;
156
157
  return {
157
158
  PROJECT_ID: projectId !== null && projectId !== void 0 ? projectId : "",
@@ -159,6 +160,8 @@ class ExtensionsEmulator {
159
160
  DATABASE_INSTANCE: projectId !== null && projectId !== void 0 ? projectId : "",
160
161
  DATABASE_URL: `https://${projectId}.firebaseio.com`,
161
162
  STORAGE_BUCKET: `${projectId}.appspot.com`,
163
+ ALLOWED_EVENT_TYPES: instance.allowedEventTypes ? instance.allowedEventTypes.join(",") : "",
164
+ EVENTARC_CHANNEL: (_a = instance.eventarcChannel) !== null && _a !== void 0 ? _a : "",
162
165
  };
163
166
  }
164
167
  async checkAndWarnAPIs(instances) {
@@ -223,7 +223,6 @@ class FunctionsEmulator {
223
223
  return Promise.resolve();
224
224
  }
225
225
  async connect() {
226
- const loadTriggerPromises = [];
227
226
  for (const backend of this.args.emulatableBackends) {
228
227
  this.logger.logLabeled("BULLET", "functions", `Watching "${backend.functionsDir}" for Cloud Functions...`);
229
228
  const watcher = chokidar.watch(backend.functionsDir, {
@@ -239,9 +238,8 @@ class FunctionsEmulator {
239
238
  this.logger.log("DEBUG", `File ${filePath} changed, reloading triggers`);
240
239
  return debouncedLoadTriggers();
241
240
  });
242
- loadTriggerPromises.push(this.loadTriggers(backend, true));
241
+ await this.loadTriggers(backend, true);
243
242
  }
244
- await Promise.all(loadTriggerPromises);
245
243
  await this.performPostLoadOperations();
246
244
  return;
247
245
  }
@@ -669,7 +667,10 @@ class FunctionsEmulator {
669
667
  envs.FUNCTIONS_EMULATOR = "true";
670
668
  envs.TZ = "UTC";
671
669
  envs.FIREBASE_DEBUG_MODE = "true";
672
- envs.FIREBASE_DEBUG_FEATURES = JSON.stringify({ skipTokenVerification: true });
670
+ envs.FIREBASE_DEBUG_FEATURES = JSON.stringify({
671
+ skipTokenVerification: true,
672
+ enableCors: true,
673
+ });
673
674
  const firestoreEmulator = this.getEmulatorInfo(types_1.Emulators.FIRESTORE);
674
675
  if (firestoreEmulator != null) {
675
676
  envs[constants_1.Constants.FIRESTORE_EMULATOR_HOST] = (0, functionsEmulatorShared_1.formatHost)(firestoreEmulator);
@@ -853,11 +854,9 @@ class FunctionsEmulator {
853
854
  }
854
855
  async reloadTriggers() {
855
856
  this.triggerGeneration++;
856
- const loadTriggerPromises = [];
857
857
  for (const backend of this.args.emulatableBackends) {
858
- loadTriggerPromises.push(this.loadTriggers(backend));
858
+ await this.loadTriggers(backend);
859
859
  }
860
- await Promise.all(loadTriggerPromises);
861
860
  await this.performPostLoadOperations();
862
861
  return;
863
862
  }
@@ -213,7 +213,7 @@ function initializeNetworkFiltering(frb) {
213
213
  })
214
214
  .filter((v) => v);
215
215
  const href = (hrefs.length && hrefs[0]) || "";
216
- if (href && !history[href] && !href.startsWith("http://localhost")) {
216
+ if (href && !history[href] && !(0, functionsEmulatorUtils_1.isLocalHost)(href)) {
217
217
  history[href] = true;
218
218
  if (href.indexOf("googleapis.com") !== -1) {
219
219
  new types_1.EmulatorLog("SYSTEM", "googleapis-network-access", "", {
@@ -106,6 +106,9 @@ function emulatedFunctionsFromEndpoints(endpoints) {
106
106
  options: endpoint.blockingTrigger.options || {},
107
107
  };
108
108
  }
109
+ else if (backend.isTaskQueueTriggered(endpoint)) {
110
+ def.httpsTrigger = {};
111
+ }
109
112
  else {
110
113
  }
111
114
  regionDefinitions.push(def);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
3
+ exports.isLocalHost = exports.compareVersionStrings = exports.parseVersionString = exports.parseRuntimeVersion = exports.removePathSegments = exports.trimSlashes = exports.isValidWildcardMatch = exports.extractParamsFromPath = void 0;
4
4
  const wildcardRegex = new RegExp("{[^/{}]*}");
5
5
  const wildcardKeyRegex = new RegExp("^{(.+)}$");
6
6
  function extractParamsFromPath(wildcardPath, snapshotPath) {
@@ -85,3 +85,7 @@ function compareVersionStrings(a, b) {
85
85
  return 0;
86
86
  }
87
87
  exports.compareVersionStrings = compareVersionStrings;
88
+ function isLocalHost(href) {
89
+ return !!href.match(/^(http(s)?:\/\/)?(localhost|127.0.0.1|\[::1])/);
90
+ }
91
+ exports.isLocalHost = isLocalHost;
@@ -118,7 +118,7 @@ function createFirebaseEndpoints(emulator) {
118
118
  return res.json(new metadata_1.OutgoingFirebaseMetadata(metadata));
119
119
  });
120
120
  firebaseStorageAPI.get("/b/:bucketId/o", async (req, res) => {
121
- var _a, _b, _c, _d, _e;
121
+ var _a, _b, _c, _d;
122
122
  const maxResults = (_a = req.query.maxResults) === null || _a === void 0 ? void 0 : _a.toString();
123
123
  let listResponse;
124
124
  try {
@@ -144,10 +144,12 @@ function createFirebaseEndpoints(emulator) {
144
144
  }
145
145
  return res.status(200).json({
146
146
  nextPageToken: listResponse.nextPageToken,
147
- prefixes: (_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : [],
148
- items: (_e = (_d = listResponse.items) === null || _d === void 0 ? void 0 : _d.map((item) => {
147
+ prefixes: ((_c = listResponse.prefixes) !== null && _c !== void 0 ? _c : []).filter(isValidPrefix),
148
+ items: ((_d = listResponse.items) !== null && _d !== void 0 ? _d : [])
149
+ .filter((item) => isValidNonEncodedPathString(item.name))
150
+ .map((item) => {
149
151
  return { name: item.name, bucket: item.bucket };
150
- })) !== null && _e !== void 0 ? _e : [],
152
+ }),
151
153
  });
152
154
  });
153
155
  const handleUpload = async (req, res) => {
@@ -467,3 +469,23 @@ function setObjectHeaders(res, metadata, headerOverride = { "Content-Encoding":
467
469
  res.setHeader("Content-Language", metadata.contentLanguage);
468
470
  }
469
471
  }
472
+ function isValidPrefix(prefix) {
473
+ return isValidNonEncodedPathString(removeAtMostOneTrailingSlash(prefix));
474
+ }
475
+ function isValidNonEncodedPathString(path) {
476
+ if (path.startsWith("/")) {
477
+ path = path.substring(1);
478
+ }
479
+ if (!path) {
480
+ return false;
481
+ }
482
+ for (const pathSegment of path.split("/")) {
483
+ if (!pathSegment) {
484
+ return false;
485
+ }
486
+ }
487
+ return true;
488
+ }
489
+ function removeAtMostOneTrailingSlash(path) {
490
+ return path.replace(/\/$/, "");
491
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.askForEventArcLocation = exports.askShouldCollectEventsConfig = exports.askForAllowedEventTypes = exports.askForEventsConfig = exports.checkAllowedEventTypesResponse = void 0;
4
+ const prompt_1 = require("../prompt");
5
+ const extensionsApi = require("../extensions/extensionsApi");
6
+ const utils = require("../utils");
7
+ const clc = require("cli-color");
8
+ const logger_1 = require("../logger");
9
+ const { marked } = require("marked");
10
+ function checkAllowedEventTypesResponse(response, validEvents) {
11
+ const validEventTypes = validEvents.map((e) => e.type);
12
+ if (response.length === 0) {
13
+ return false;
14
+ }
15
+ for (const e of response) {
16
+ if (!validEventTypes.includes(e)) {
17
+ utils.logWarning(`Unexpected event type '${e}' was configured to be emitted. This event type is not part of the extension spec.`);
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ exports.checkAllowedEventTypesResponse = checkAllowedEventTypesResponse;
24
+ async function askForEventsConfig(events, projectId, instanceId) {
25
+ var _a, _b;
26
+ logger_1.logger.info(`\n${clc.bold("Enable Events")}: ${marked("If you enable events, you can write custom event handlers ([https://firebase.google.com/docs/extensions/install-extensions#eventarc](https://firebase.google.com/docs/extensions/install-extensions#eventarc)) that respond to these events.\n\nYou can always enable or disable events later. Events will be emitted via Eventarc. Fees apply ([https://cloud.google.com/eventarc/pricing](https://cloud.google.com/eventarc/pricing)).")}`);
27
+ if (!(await askShouldCollectEventsConfig())) {
28
+ return undefined;
29
+ }
30
+ let existingInstance;
31
+ try {
32
+ existingInstance = instanceId
33
+ ? await extensionsApi.getInstance(projectId, instanceId)
34
+ : undefined;
35
+ }
36
+ catch (_c) {
37
+ }
38
+ const preselectedTypes = (_a = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.allowedEventTypes) !== null && _a !== void 0 ? _a : [];
39
+ const oldLocation = (_b = existingInstance === null || existingInstance === void 0 ? void 0 : existingInstance.config.eventarcChannel) === null || _b === void 0 ? void 0 : _b.split("/")[3];
40
+ const location = await askForEventArcLocation(oldLocation);
41
+ const channel = `projects/${projectId}/locations/${location}/channels/firebase`;
42
+ const allowedEventTypes = await askForAllowedEventTypes(events, preselectedTypes);
43
+ return { channel, allowedEventTypes };
44
+ }
45
+ exports.askForEventsConfig = askForEventsConfig;
46
+ async function askForAllowedEventTypes(eventDescriptors, preselectedTypes) {
47
+ let valid = false;
48
+ let response = [];
49
+ const eventTypes = eventDescriptors.map((e, index) => ({
50
+ checked: false,
51
+ name: `${index + 1}. ${e.type}\n ${e.description}`,
52
+ value: e.type,
53
+ }));
54
+ while (!valid) {
55
+ response = await (0, prompt_1.promptOnce)({
56
+ name: "selectedEventTypesInput",
57
+ type: "checkbox",
58
+ default: preselectedTypes !== null && preselectedTypes !== void 0 ? preselectedTypes : [],
59
+ message: `Please select the events [${eventTypes.length} types total] that this extension is permitted to emit. ` +
60
+ "You can implement your own handlers that trigger when these events are emitted to customize the extension's behavior. ",
61
+ choices: eventTypes,
62
+ pageSize: 20,
63
+ });
64
+ valid = checkAllowedEventTypesResponse(response, eventDescriptors);
65
+ }
66
+ return response.filter((e) => e !== "");
67
+ }
68
+ exports.askForAllowedEventTypes = askForAllowedEventTypes;
69
+ async function askShouldCollectEventsConfig() {
70
+ return (0, prompt_1.promptOnce)({
71
+ type: "confirm",
72
+ name: "shouldCollectEvents",
73
+ message: `Would you like to enable events?`,
74
+ default: false,
75
+ });
76
+ }
77
+ exports.askShouldCollectEventsConfig = askShouldCollectEventsConfig;
78
+ async function askForEventArcLocation(preselectedLocation) {
79
+ let valid = false;
80
+ const allowedRegions = ["us-central1", "us-west1", "europe-west4", "asia-northeast1"];
81
+ let location = "";
82
+ while (!valid) {
83
+ location = await (0, prompt_1.promptOnce)({
84
+ name: "input",
85
+ type: "list",
86
+ default: preselectedLocation !== null && preselectedLocation !== void 0 ? preselectedLocation : "us-central1",
87
+ message: "Which location would you like the Eventarc channel to live in? We recommend using the default option. A channel location that differs from the extension's Cloud Functions location can incur egress cost.",
88
+ choices: allowedRegions.map((e) => ({ checked: false, value: e })),
89
+ });
90
+ valid = allowedRegions.includes(location);
91
+ if (!valid) {
92
+ utils.logWarning(`Unexpected EventArc region '${location}' was specified. Allowed regions: ${allowedRegions.join(", ")}`);
93
+ }
94
+ }
95
+ return location;
96
+ }
97
+ exports.askForEventArcLocation = askForEventArcLocation;
@@ -45,6 +45,7 @@ function displayExportInfo(withRef, withoutRef) {
45
45
  }
46
46
  exports.displayExportInfo = displayExportInfo;
47
47
  function displaySpecs(specs) {
48
+ var _a;
48
49
  for (let i = 0; i < specs.length; i++) {
49
50
  const spec = specs[i];
50
51
  logger_1.logger.info(`${i + 1}. ${(0, deploymentSummary_1.humanReadable)(spec)}`);
@@ -52,6 +53,12 @@ function displaySpecs(specs) {
52
53
  for (const p of Object.entries(spec.params)) {
53
54
  logger_1.logger.info(`\t${p[0]}=${p[1]}`);
54
55
  }
56
+ if ((_a = spec.allowedEventTypes) === null || _a === void 0 ? void 0 : _a.length) {
57
+ logger_1.logger.info(`\tALLOWED_EVENTS=${spec.allowedEventTypes}`);
58
+ }
59
+ if (spec.eventarcChannel) {
60
+ logger_1.logger.info(`\tEVENTARC_CHANNEL=${spec.eventarcChannel}`);
61
+ }
55
62
  logger_1.logger.info("");
56
63
  }
57
64
  }
@@ -58,6 +58,8 @@ async function createInstance(args) {
58
58
  var _a, _b;
59
59
  const config = {
60
60
  params: args.params,
61
+ allowedEventTypes: args.allowedEventTypes,
62
+ eventarcChannel: args.eventarcChannel,
61
63
  };
62
64
  if (args.extensionSource && args.extensionVersionRef) {
63
65
  throw new error_1.FirebaseError("ExtensionSource and ExtensionVersion both provided, but only one should be.");
@@ -73,6 +75,12 @@ async function createInstance(args) {
73
75
  else {
74
76
  throw new error_1.FirebaseError("No ExtensionVersion or ExtensionSource provided but one is required.");
75
77
  }
78
+ if (args.allowedEventTypes) {
79
+ config.allowedEventTypes = args.allowedEventTypes;
80
+ }
81
+ if (args.eventarcChannel) {
82
+ config.eventarcChannel = args.eventarcChannel;
83
+ }
76
84
  return createInstanceHelper(args.projectId, args.instanceId, config, args.validateOnly);
77
85
  }
78
86
  exports.createInstance = createInstance;
@@ -88,8 +96,16 @@ async function deleteInstance(projectId, instanceId) {
88
96
  }
89
97
  exports.deleteInstance = deleteInstance;
90
98
  async function getInstance(projectId, instanceId) {
91
- const res = await apiClient.get(`/projects/${projectId}/instances/${instanceId}`);
92
- return res.body;
99
+ try {
100
+ const res = await apiClient.get(`/projects/${projectId}/instances/${instanceId}`);
101
+ return res.body;
102
+ }
103
+ catch (err) {
104
+ if (err.status === 404) {
105
+ throw new error_1.FirebaseError(`Extension instance '${clc.bold(instanceId)}' not found in project '${clc.bold(projectId)}'.`, { status: 404 });
106
+ }
107
+ throw err;
108
+ }
93
109
  }
94
110
  exports.getInstance = getInstance;
95
111
  async function listInstances(projectId) {
@@ -114,7 +130,7 @@ async function listInstances(projectId) {
114
130
  exports.listInstances = listInstances;
115
131
  async function configureInstance(args) {
116
132
  var _a;
117
- const res = await patchInstance({
133
+ const reqBody = {
118
134
  projectId: args.projectId,
119
135
  instanceId: args.instanceId,
120
136
  updateMask: "config.params",
@@ -124,8 +140,16 @@ async function configureInstance(args) {
124
140
  params: args.params,
125
141
  },
126
142
  },
127
- });
128
- return res;
143
+ };
144
+ if (args.canEmitEvents) {
145
+ if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
146
+ throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
147
+ }
148
+ reqBody.data.config.allowedEventTypes = args.allowedEventTypes;
149
+ reqBody.data.config.eventarcChannel = args.eventarcChannel;
150
+ }
151
+ reqBody.updateMask += ",config.allowed_event_types,config.eventarc_channel";
152
+ return patchInstance(reqBody);
129
153
  }
130
154
  exports.configureInstance = configureInstance;
131
155
  async function updateInstance(args) {
@@ -140,7 +164,15 @@ async function updateInstance(args) {
140
164
  body.config.params = args.params;
141
165
  updateMask += ",config.params";
142
166
  }
143
- return await patchInstance({
167
+ if (args.canEmitEvents) {
168
+ if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
169
+ throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
170
+ }
171
+ body.config.allowedEventTypes = args.allowedEventTypes;
172
+ body.config.eventarcChannel = args.eventarcChannel;
173
+ }
174
+ updateMask += ",config.allowed_event_types,config.eventarc_channel";
175
+ return patchInstance({
144
176
  projectId: args.projectId,
145
177
  instanceId: args.instanceId,
146
178
  updateMask,
@@ -163,7 +195,15 @@ async function updateInstanceFromRegistry(args) {
163
195
  body.config.params = args.params;
164
196
  updateMask += ",config.params";
165
197
  }
166
- return await patchInstance({
198
+ if (args.canEmitEvents) {
199
+ if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
200
+ throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
201
+ }
202
+ body.config.allowedEventTypes = args.allowedEventTypes;
203
+ body.config.eventarcChannel = args.eventarcChannel;
204
+ }
205
+ updateMask += ",config.allowed_event_types,config.eventarc_channel";
206
+ return patchInstance({
167
207
  projectId: args.projectId,
168
208
  instanceId: args.instanceId,
169
209
  updateMask,
@@ -48,7 +48,7 @@ async function writeLocalSecrets(specs, config, force) {
48
48
  continue;
49
49
  }
50
50
  const writeBuffer = {};
51
- const locallyOverridenSecretParams = extensionSpec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET && spec.params[p.param].local);
51
+ const locallyOverridenSecretParams = extensionSpec.params.filter((p) => { var _a; return p.type === extensionsApi_1.ParamType.SECRET && ((_a = spec.params[p.param]) === null || _a === void 0 ? void 0 : _a.local); });
52
52
  for (const paramSpec of locallyOverridenSecretParams) {
53
53
  const key = paramSpec.param;
54
54
  const localValue = spec.params[key].local;
@@ -11,6 +11,7 @@ const askUserForParam = require("./askUserForParam");
11
11
  const track_1 = require("../track");
12
12
  const env = require("../functions/env");
13
13
  const utils_1 = require("../utils");
14
+ const warnings_1 = require("./warnings");
14
15
  function getBaseParamBindings(params) {
15
16
  let ret = {};
16
17
  for (const [k, v] of Object.entries(params)) {
@@ -57,6 +58,7 @@ async function getParams(args) {
57
58
  paramsMessage);
58
59
  }
59
60
  else if (args.paramsEnvPath) {
61
+ (0, warnings_1.paramsFlagDeprecationWarning)();
60
62
  params = getParamsFromFile({
61
63
  paramSpecs: args.paramSpecs,
62
64
  paramsEnvPath: args.paramsEnvPath,
@@ -59,13 +59,16 @@ async function displayChanges(args) {
59
59
  }
60
60
  exports.displayChanges = displayChanges;
61
61
  async function update(updateOptions) {
62
- const { projectId, instanceId, source, extRef, params } = updateOptions;
62
+ const { projectId, instanceId, source, extRef, params, canEmitEvents, allowedEventTypes, eventarcChannel, } = updateOptions;
63
63
  if (extRef) {
64
64
  return await extensionsApi.updateInstanceFromRegistry({
65
65
  projectId,
66
66
  instanceId,
67
67
  extRef,
68
68
  params,
69
+ canEmitEvents,
70
+ allowedEventTypes,
71
+ eventarcChannel,
69
72
  });
70
73
  }
71
74
  else if (source) {
@@ -74,6 +77,9 @@ async function update(updateOptions) {
74
77
  instanceId,
75
78
  extensionSource: source,
76
79
  params,
80
+ canEmitEvents,
81
+ allowedEventTypes,
82
+ eventarcChannel,
77
83
  });
78
84
  }
79
85
  throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
3
+ exports.paramsFlagDeprecationWarning = exports.displayWarningsForDeploy = exports.displayWarningPrompts = void 0;
4
4
  const { marked } = require("marked");
5
5
  const clc = require("cli-color");
6
6
  const extensionsApi_1 = require("./extensionsApi");
@@ -11,6 +11,7 @@ const deploymentSummary_1 = require("../deploy/extensions/deploymentSummary");
11
11
  const planner_1 = require("../deploy/extensions/planner");
12
12
  const functional_1 = require("../functional");
13
13
  const utils = require("../utils");
14
+ const logger_1 = require("../logger");
14
15
  function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
15
16
  const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
16
17
  const warningMsg = `This extension is in preview and is built by a developer in the [Extensions Publisher Early Access Program](http://bit.ly/firex-provider). Its functionality might change in backward-incompatible ways. Since this extension isn't built by Firebase, reach out to ${publisherNameLink} with questions about this extension.`;
@@ -57,14 +58,20 @@ async function displayWarningsForDeploy(instancesToCreate) {
57
58
  const experimental = nonEapExtensions.filter((i) => i.extension.registryLaunchStage === extensionsApi_1.RegistryLaunchStage.EXPERIMENTAL);
58
59
  if (experimental.length) {
59
60
  const humanReadableList = experimental.map((i) => `\t${(0, deploymentSummary_1.humanReadable)(i)}`).join("\n");
60
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n`));
61
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`The following are instances of ${clc.bold("experimental")} extensions.They may not be production-ready. Their functionality may change in backward-incompatible ways before their official release, or they may be discontinued.\n${humanReadableList}\n`, { gfm: false }));
61
62
  }
62
63
  if (eapExtensions.length) {
63
64
  const humanReadableList = eapExtensions.map(toListEntry).join("\n");
64
- utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider. Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
65
+ utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`These extensions are in preview and are built by a developer in the Extensions Publisher Early Access Program (http://bit.ly/firex-provider). Their functionality might change in backwards-incompatible ways. Since these extensions aren't built by Firebase, reach out to their publisher with questions about them.` +
65
66
  ` They are provided “AS IS”, without any warranty, express or implied, from Google.` +
66
- ` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}`));
67
+ ` Google disclaims all liability for any damages, direct or indirect, resulting from the use of these extensions\n${humanReadableList}`, { gfm: false }));
67
68
  }
68
69
  return experimental.length > 0 || eapExtensions.length > 0;
69
70
  }
70
71
  exports.displayWarningsForDeploy = displayWarningsForDeploy;
72
+ function paramsFlagDeprecationWarning() {
73
+ logger_1.logger.warn("The --params flag is deprecated and will be removed in firebase-tools@11. " +
74
+ "Instead, use an extensions manifest and `firebase deploy --only extensions` to deploy extensions noninteractively. " +
75
+ "See https://firebase.google.com/docs/extensions/manifest for more details");
76
+ }
77
+ exports.paramsFlagDeprecationWarning = paramsFlagDeprecationWarning;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareFrameworks = exports.shortSiteName = void 0;
4
+ const path_1 = require("path");
5
+ const process_1 = require("process");
6
+ const projectUtils_1 = require("../projectUtils");
7
+ const normalizedHostingConfigs_1 = require("../hosting/normalizedHostingConfigs");
8
+ const api_1 = require("../hosting/api");
9
+ const apps_1 = require("../management/apps");
10
+ const fs_1 = require("fs");
11
+ const prompt_1 = require("../prompt");
12
+ const { writeFile } = fs_1.promises;
13
+ const shortSiteName = (site) => (site === null || site === void 0 ? void 0 : site.name) && site.name.split("/").pop();
14
+ exports.shortSiteName = shortSiteName;
15
+ const prepareFrameworks = async (targetNames, context, options) => {
16
+ const project = (0, projectUtils_1.needProjectId)(context);
17
+ const configs = (0, normalizedHostingConfigs_1.normalizedHostingConfigs)(Object.assign({ site: project }, options), { resolveTargets: true });
18
+ options.normalizedHostingConfigs = configs;
19
+ if (configs.length === 0)
20
+ return;
21
+ for (const config of configs) {
22
+ const { source, site, public: publicDir } = config;
23
+ if (!source)
24
+ continue;
25
+ const dist = (0, path_1.join)(".firebase", site);
26
+ const hostingDist = (0, path_1.join)(dist, "hosting");
27
+ const functionsDist = (0, path_1.join)(dist, "functions");
28
+ if (publicDir)
29
+ throw new Error(`hosting.public and hosting.source cannot both be set in firebase.json`);
30
+ const getProjectPath = (...args) => (0, path_1.join)(process.cwd(), source, ...args);
31
+ const functionName = `ssr${site.replace(/-/g, "")}`;
32
+ const { build } = require("firebase-frameworks/tools");
33
+ const { usingCloudFunctions, rewrites, redirects, headers, usesFirebaseConfig } = await build({
34
+ dist,
35
+ project,
36
+ site,
37
+ function: {
38
+ name: functionName,
39
+ region: "us-central1",
40
+ },
41
+ }, getProjectPath);
42
+ config.public = hostingDist;
43
+ if (usingCloudFunctions) {
44
+ if (context.hostingChannel) {
45
+ const message = "Cannot preview changes to the backend, you will only see changes to the static content on this channel.";
46
+ if (!options.nonInteractive) {
47
+ const continueDeploy = await (0, prompt_1.promptOnce)({
48
+ type: "confirm",
49
+ default: true,
50
+ message: `${message} Would you like to continue with the deploy?`,
51
+ });
52
+ if (!continueDeploy)
53
+ (0, process_1.exit)(1);
54
+ }
55
+ else {
56
+ console.error(message);
57
+ }
58
+ }
59
+ else {
60
+ const functionConfig = {
61
+ source: functionsDist,
62
+ codebase: `firebase-frameworks-${site}`,
63
+ };
64
+ if (targetNames.includes("functions")) {
65
+ const combinedFunctionsConfig = [functionConfig].concat(options.config.get("functions") || []);
66
+ options.config.set("functions", combinedFunctionsConfig);
67
+ }
68
+ else {
69
+ targetNames.unshift("functions");
70
+ options.config.set("functions", functionConfig);
71
+ }
72
+ }
73
+ config.rewrites = [
74
+ ...(config.rewrites || []),
75
+ ...rewrites,
76
+ {
77
+ source: "**",
78
+ function: functionName,
79
+ },
80
+ ];
81
+ let firebaseProjectConfig = null;
82
+ if (usesFirebaseConfig) {
83
+ const sites = await (0, api_1.listSites)(project);
84
+ const selectedSite = sites.find((it) => (0, exports.shortSiteName)(it) === site);
85
+ if (selectedSite) {
86
+ const { appId } = selectedSite;
87
+ if (appId) {
88
+ firebaseProjectConfig = await (0, apps_1.getAppConfig)(appId, apps_1.AppPlatform.WEB);
89
+ }
90
+ else {
91
+ console.warn(`No Firebase app associated with site ${site}, unable to provide authenticated server context`);
92
+ }
93
+ }
94
+ }
95
+ writeFile((0, path_1.join)(functionsDist, ".env"), `FRAMEWORKS_FIREBASE_PROJECT_CONFIG="${JSON.stringify(firebaseProjectConfig).replace(/"/g, '\\"')}"`);
96
+ }
97
+ else {
98
+ config.rewrites = [
99
+ ...(config.rewrites || []),
100
+ ...rewrites,
101
+ {
102
+ source: "**",
103
+ destination: "/index.html",
104
+ },
105
+ ];
106
+ }
107
+ config.redirects = [...(config.redirects || []), ...redirects];
108
+ config.headers = [...(config.headers || []), ...headers];
109
+ }
110
+ };
111
+ exports.prepareFrameworks = prepareFrameworks;
@@ -2,18 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.logEntries = exports.getApiFilter = void 0;
4
4
  const logger_1 = require("../logger");
5
- const previews_1 = require("../previews");
6
5
  function getApiFilter(functionList) {
7
- const baseFilter = previews_1.previews.functionsv2
8
- ? 'resource.type="cloud_function" OR ' +
9
- '(resource.type="cloud_run_revision" AND ' +
10
- 'labels."goog-managed-by"="cloudfunctions")'
11
- : 'resource.type="cloud_function"';
6
+ const baseFilter = 'resource.type="cloud_function" OR ' +
7
+ '(resource.type="cloud_run_revision" AND ' +
8
+ 'labels."goog-managed-by"="cloudfunctions")';
12
9
  if (functionList) {
13
10
  const apiFuncFilters = functionList.split(",").map((fn) => {
14
- return previews_1.previews.functionsv2
15
- ? `resource.labels.function_name="${fn}" ` + `OR resource.labels.service_name="${fn}"`
16
- : `resource.labels.function_name="${fn}"`;
11
+ return `resource.labels.function_name="${fn}" ` + `OR resource.labels.service_name="${fn}"`;
17
12
  });
18
13
  return baseFilter + `\n(${apiFuncFilters.join(" OR ")})`;
19
14
  }
@@ -145,7 +145,7 @@ async function setInvokerUpdate(projectId, fnName, invoker) {
145
145
  exports.setInvokerUpdate = setInvokerUpdate;
146
146
  async function updateFunction(cloudFunction) {
147
147
  const endpoint = `/${cloudFunction.name}`;
148
- const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
148
+ const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables", "secretEnvironmentVariables");
149
149
  try {
150
150
  const headers = {};
151
151
  if (previews_1.previews.artifactregistry) {