firebase-tools 11.20.0 → 11.22.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 (47) hide show
  1. package/lib/bin/firebase.js +1 -1
  2. package/lib/commands/appdistribution-distribute.js +3 -0
  3. package/lib/commands/ext-configure.js +2 -1
  4. package/lib/commands/ext-install.js +2 -2
  5. package/lib/deploy/extensions/planner.js +16 -9
  6. package/lib/deploy/extensions/tasks.js +6 -0
  7. package/lib/deploy/functions/prepareFunctionsUpload.js +13 -2
  8. package/lib/deploy/functions/runtimes/discovery/index.js +1 -1
  9. package/lib/deploy/functions/runtimes/index.js +5 -3
  10. package/lib/deploy/functions/runtimes/node/index.js +71 -26
  11. package/lib/deploy/functions/runtimes/node/versioning.js +4 -2
  12. package/lib/deploy/functions/runtimes/python/index.js +119 -0
  13. package/lib/emulator/auth/apiSpec.js +4 -0
  14. package/lib/emulator/auth/operations.js +3 -3
  15. package/lib/emulator/controller.js +5 -5
  16. package/lib/emulator/downloadableEmulators.js +3 -3
  17. package/lib/emulator/extensionsEmulator.js +3 -2
  18. package/lib/emulator/functionsEmulator.js +107 -72
  19. package/lib/emulator/functionsRuntimeWorker.js +4 -14
  20. package/lib/emulator/storage/apis/firebase.js +1 -0
  21. package/lib/emulator/storage/rules/manager.js +1 -1
  22. package/lib/emulator/storage/server.js +1 -1
  23. package/lib/emulator/storage/upload.js +3 -3
  24. package/lib/extensions/askUserForParam.js +32 -1
  25. package/lib/extensions/emulator/optionsHelper.js +3 -3
  26. package/lib/extensions/emulator/specHelper.js +17 -16
  27. package/lib/extensions/extensionsApi.js +16 -3
  28. package/lib/extensions/paramHelper.js +6 -1
  29. package/lib/firestore/checkDatabaseType.js +3 -3
  30. package/lib/frameworks/index.js +9 -8
  31. package/lib/frameworks/next/index.js +6 -4
  32. package/lib/frameworks/nuxt/index.js +18 -26
  33. package/lib/frameworks/nuxt/interfaces.js +2 -0
  34. package/lib/frameworks/nuxt/utils.js +13 -0
  35. package/lib/frameworks/nuxt2/index.js +91 -0
  36. package/lib/functional.js +10 -3
  37. package/lib/functions/env.js +1 -1
  38. package/lib/functions/python.js +21 -0
  39. package/lib/init/features/firestore/index.js +1 -3
  40. package/lib/serve/functions.js +1 -3
  41. package/npm-shrinkwrap.json +1805 -1171
  42. package/package.json +3 -3
  43. package/lib/deploy/functions/runtimes/golang/gomod.js +0 -74
  44. package/lib/deploy/functions/runtimes/golang/index.js +0 -106
  45. package/lib/init/features/functions/golang.js +0 -57
  46. package/templates/init/functions/golang/_gitignore +0 -12
  47. package/templates/init/functions/golang/functions.go +0 -38
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FunctionsEmulator = void 0;
3
+ exports.FunctionsEmulator = exports.TCPConn = exports.IPCConn = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const express = require("express");
@@ -8,6 +8,7 @@ const clc = require("colorette");
8
8
  const http = require("http");
9
9
  const jwt = require("jsonwebtoken");
10
10
  const cors = require("cors");
11
+ const semver = require("semver");
11
12
  const url_1 = require("url");
12
13
  const events_1 = require("events");
13
14
  const logger_1 = require("../logger");
@@ -15,6 +16,7 @@ const track_1 = require("../track");
15
16
  const constants_1 = require("./constants");
16
17
  const types_1 = require("./types");
17
18
  const chokidar = require("chokidar");
19
+ const portfinder = require("portfinder");
18
20
  const spawn = require("cross-spawn");
19
21
  const functionsEmulatorShared_1 = require("./functionsEmulatorShared");
20
22
  const registry_1 = require("./registry");
@@ -33,9 +35,34 @@ const functionsEnv = require("../functions/env");
33
35
  const v1_1 = require("../functions/events/v1");
34
36
  const build_1 = require("../deploy/functions/build");
35
37
  const env_1 = require("./env");
38
+ const python_1 = require("../functions/python");
36
39
  const EVENT_INVOKE = "functions:invoke";
37
40
  const EVENT_INVOKE_GA4 = "functions_invoke";
38
41
  const DATABASE_PATH_PATTERN = new RegExp("^projects/[^/]+/instances/([^/]+)/refs(/.*)$");
42
+ class IPCConn {
43
+ constructor(socketPath) {
44
+ this.socketPath = socketPath;
45
+ }
46
+ httpReqOpts() {
47
+ return {
48
+ socketPath: this.socketPath,
49
+ };
50
+ }
51
+ }
52
+ exports.IPCConn = IPCConn;
53
+ class TCPConn {
54
+ constructor(host, port) {
55
+ this.host = host;
56
+ this.port = port;
57
+ }
58
+ httpReqOpts() {
59
+ return {
60
+ host: this.host,
61
+ port: this.port,
62
+ };
63
+ }
64
+ }
65
+ exports.TCPConn = TCPConn;
39
66
  class FunctionsEmulator {
40
67
  constructor(args) {
41
68
  this.args = args;
@@ -168,7 +195,13 @@ class FunctionsEmulator {
168
195
  const record = this.getTriggerRecordByKey(this.getTriggerKey(trigger));
169
196
  const pool = this.workerPools[record.backend.codebase];
170
197
  if (!pool.readyForWork(trigger.id)) {
171
- await this.startRuntime(record.backend, trigger);
198
+ try {
199
+ await this.startRuntime(record.backend, trigger);
200
+ }
201
+ catch (e) {
202
+ this.logger.logLabeled("ERROR", `Failed to start runtime for ${trigger.id}: ${e}`);
203
+ return;
204
+ }
172
205
  }
173
206
  const worker = pool.getIdleWorker(trigger.id);
174
207
  const reqBody = JSON.stringify(body);
@@ -177,20 +210,13 @@ class FunctionsEmulator {
177
210
  "Content-Length": `${reqBody.length}`,
178
211
  };
179
212
  return new Promise((resolve, reject) => {
180
- const req = http.request({
181
- path: `/`,
182
- socketPath: worker.runtime.socketPath,
183
- headers: headers,
184
- }, resolve);
213
+ const req = http.request(Object.assign(Object.assign({}, worker.runtime.conn.httpReqOpts()), { path: `/`, headers: headers }), resolve);
185
214
  req.on("error", reject);
186
215
  req.write(reqBody);
187
216
  req.end();
188
217
  });
189
218
  }
190
219
  async start() {
191
- for (const backend of this.args.emulatableBackends) {
192
- backend.nodeBinary = this.getNodeBinary(backend);
193
- }
194
220
  const credentialEnv = await this.getCredentialsEnvironment();
195
221
  for (const e of this.args.emulatableBackends) {
196
222
  e.env = Object.assign(Object.assign({}, credentialEnv), e.env);
@@ -219,6 +245,7 @@ class FunctionsEmulator {
219
245
  /.+?[\\\/]node_modules[\\\/].+?/,
220
246
  /(^|[\/\\])\../,
221
247
  /.+\.log/,
248
+ /.+?[\\\/]venv[\\\/].+?/,
222
249
  ],
223
250
  persistent: true,
224
251
  });
@@ -257,21 +284,22 @@ class FunctionsEmulator {
257
284
  projectId: this.args.projectId,
258
285
  projectDir: this.args.projectDir,
259
286
  sourceDir: emulatableBackend.functionsDir,
287
+ runtime: emulatableBackend.runtime,
260
288
  };
261
- if (emulatableBackend.nodeMajorVersion) {
262
- runtimeDelegateContext.runtime = `nodejs${emulatableBackend.nodeMajorVersion}`;
263
- }
264
289
  const runtimeDelegate = await runtimes.getRuntimeDelegate(runtimeDelegateContext);
265
290
  logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
266
291
  await runtimeDelegate.validate();
267
292
  logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
268
293
  await runtimeDelegate.build();
294
+ emulatableBackend.runtime = runtimeDelegate.runtime;
295
+ emulatableBackend.bin = runtimeDelegate.bin;
269
296
  const firebaseConfig = this.getFirebaseConfig();
270
297
  const environment = Object.assign(Object.assign(Object.assign(Object.assign({}, this.getSystemEnvs()), this.getEmulatorEnvs()), { FIREBASE_CONFIG: firebaseConfig }), emulatableBackend.env);
271
298
  const userEnvOpt = {
272
299
  functionsSource: emulatableBackend.functionsDir,
273
300
  projectId: this.args.projectId,
274
301
  projectAlias: this.args.projectAlias,
302
+ isEmulator: true,
275
303
  };
276
304
  const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
277
305
  const discoveredBuild = await runtimeDelegate.discoverBuild(runtimeConfig, environment);
@@ -286,9 +314,7 @@ class FunctionsEmulator {
286
314
  }
287
315
  }
288
316
  async loadTriggers(emulatableBackend, force = false) {
289
- if (!emulatableBackend.nodeBinary) {
290
- throw new error_1.FirebaseError(`No node binary for ${emulatableBackend.functionsDir}. This should never happen.`);
291
- }
317
+ var _a;
292
318
  let triggerDefinitions = [];
293
319
  try {
294
320
  triggerDefinitions = await this.discoverTriggers(emulatableBackend);
@@ -381,13 +407,23 @@ class FunctionsEmulator {
381
407
  }
382
408
  }
383
409
  if (this.args.debugPort) {
384
- emulatableBackend.secretEnv = Object.values(triggerDefinitions.reduce((acc, curr) => {
385
- for (const secret of curr.secretEnvironmentVariables || []) {
386
- acc[secret.key] = secret;
410
+ if (!((_a = emulatableBackend.bin) === null || _a === void 0 ? void 0 : _a.startsWith("node"))) {
411
+ this.logger.log("WARN", "--inspect-functions only supported for Node.js runtimes.");
412
+ }
413
+ else {
414
+ emulatableBackend.secretEnv = Object.values(triggerDefinitions.reduce((acc, curr) => {
415
+ for (const secret of curr.secretEnvironmentVariables || []) {
416
+ acc[secret.key] = secret;
417
+ }
418
+ return acc;
419
+ }, {}));
420
+ try {
421
+ await this.startRuntime(emulatableBackend);
387
422
  }
388
- return acc;
389
- }, {}));
390
- await this.startRuntime(emulatableBackend);
423
+ catch (e) {
424
+ this.logger.logLabeled("ERROR", `Failed to start functions in ${emulatableBackend.functionsDir}: ${e}`);
425
+ }
426
+ }
391
427
  }
392
428
  }
393
429
  addEventarcTrigger(projectId, key, eventTrigger) {
@@ -632,43 +668,6 @@ class FunctionsEmulator {
632
668
  this.triggers = {};
633
669
  triggers.forEach((def) => this.addTriggerRecord(def, { backend, ignored: false }));
634
670
  }
635
- getNodeBinary(backend) {
636
- const pkg = require(path.join(backend.functionsDir, "package.json"));
637
- if ((!pkg.engines || !pkg.engines.node) && !backend.nodeMajorVersion) {
638
- this.logger.log("WARN", `Your functions directory ${backend.functionsDir} does not specify a Node version.\n ` +
639
- "- Learn more at https://firebase.google.com/docs/functions/manage-functions#set_runtime_options");
640
- return process.execPath;
641
- }
642
- const hostMajorVersion = process.versions.node.split(".")[0];
643
- const requestedMajorVersion = backend.nodeMajorVersion
644
- ? `${backend.nodeMajorVersion}`
645
- : pkg.engines.node;
646
- let localMajorVersion = "0";
647
- const localNodePath = path.join(backend.functionsDir, "node_modules/.bin/node");
648
- try {
649
- const localNodeOutput = spawn.sync(localNodePath, ["--version"]).stdout.toString();
650
- localMajorVersion = localNodeOutput.slice(1).split(".")[0];
651
- }
652
- catch (err) {
653
- }
654
- if (requestedMajorVersion === localMajorVersion) {
655
- this.logger.logLabeled("SUCCESS", "functions", `Using node@${requestedMajorVersion} from local cache.`);
656
- return localNodePath;
657
- }
658
- if (requestedMajorVersion === hostMajorVersion) {
659
- this.logger.logLabeled("SUCCESS", "functions", `Using node@${requestedMajorVersion} from host.`);
660
- }
661
- else {
662
- if (process.env.FIREPIT_VERSION) {
663
- this.logger.log("WARN", `You've requested "node" version "${requestedMajorVersion}", but the standalone Firebase CLI comes with bundled Node "${hostMajorVersion}".`);
664
- this.logger.log("INFO", `To use a different Node.js version, consider removing the standalone Firebase CLI and switching to "firebase-tools" on npm.`);
665
- }
666
- else {
667
- this.logger.log("WARN", `Your requested "node" version "${requestedMajorVersion}" doesn't match your global version "${hostMajorVersion}". Using node@${hostMajorVersion} from host.`);
668
- }
669
- }
670
- return process.execPath;
671
- }
672
671
  getRuntimeConfig(backend) {
673
672
  const configPath = `${backend.functionsDir}/.runtimeconfig.json`;
674
673
  try {
@@ -791,13 +790,11 @@ class FunctionsEmulator {
791
790
  }
792
791
  return secretEnvs;
793
792
  }
794
- async startRuntime(backend, trigger) {
795
- var _a;
796
- const emitter = new events_1.EventEmitter();
793
+ async startNode(backend, envs) {
797
794
  const args = [path.join(__dirname, "functionsEmulatorRuntime")];
798
795
  if (this.args.debugPort) {
799
- if (process.env.FIREPIT_VERSION && process.execPath === backend.nodeBinary) {
800
- this.logger.log("WARN", `To enable function inspection, please run "${process.execPath} is:npm i node@${backend.nodeMajorVersion} --save-dev" in your functions directory`);
796
+ if (process.env.FIREPIT_VERSION) {
797
+ this.logger.log("WARN", `To enable function inspection, please run "npm i node@${semver.coerce(backend.runtime || "18.0.0")} --save-dev" in your functions directory`);
801
798
  }
802
799
  else {
803
800
  const { host } = this.getInfo();
@@ -810,20 +807,51 @@ class FunctionsEmulator {
810
807
  "Cloud Functions for Firebase requires a node_modules folder to work correctly and is therefore incompatible with PnP. " +
811
808
  "See https://yarnpkg.com/getting-started/migration#step-by-step for more information.");
812
809
  }
813
- const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
814
- const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
810
+ const bin = backend.bin;
811
+ if (!bin) {
812
+ throw new Error(`No binary associated with ${backend.functionsDir}. ` +
813
+ "Make sure function runtime is configured correctly in firebase.json.");
814
+ }
815
815
  const socketPath = (0, functionsEmulatorShared_1.getTemporarySocketPath)();
816
- const childProcess = spawn(backend.nodeBinary, args, {
816
+ const childProcess = spawn(bin, args, {
817
817
  cwd: backend.functionsDir,
818
- env: Object.assign(Object.assign(Object.assign(Object.assign({ node: backend.nodeBinary }, process.env), runtimeEnv), secretEnvs), { PORT: socketPath }),
818
+ env: Object.assign(Object.assign(Object.assign({ node: backend.bin }, process.env), envs), { PORT: socketPath }),
819
819
  stdio: ["pipe", "pipe", "pipe", "ipc"],
820
820
  });
821
- const runtime = {
821
+ return Promise.resolve({
822
+ process: childProcess,
823
+ events: new events_1.EventEmitter(),
824
+ cwd: backend.functionsDir,
825
+ conn: new IPCConn(socketPath),
826
+ });
827
+ }
828
+ async startPython(backend, envs) {
829
+ const args = ["functions-framework"];
830
+ if (this.args.debugPort) {
831
+ this.logger.log("WARN", "--inspect-functions not supported for Python functions. Ignored.");
832
+ }
833
+ const port = await portfinder.getPortPromise({
834
+ port: 8081 + (0, utils_1.randomInt)(0, 1000),
835
+ });
836
+ const childProcess = (0, python_1.runWithVirtualEnv)(args, backend.functionsDir, Object.assign(Object.assign(Object.assign({}, process.env), envs), { PYTHONUNBUFFERED: "1", DEBUG: "False", HOST: "127.0.0.1", PORT: port.toString() }));
837
+ return {
822
838
  process: childProcess,
823
- events: emitter,
839
+ events: new events_1.EventEmitter(),
824
840
  cwd: backend.functionsDir,
825
- socketPath,
841
+ conn: new TCPConn("127.0.0.1", port),
826
842
  };
843
+ }
844
+ async startRuntime(backend, trigger) {
845
+ var _a;
846
+ const runtimeEnv = this.getRuntimeEnvs(backend, trigger);
847
+ const secretEnvs = await this.resolveSecretEnvs(backend, trigger);
848
+ let runtime;
849
+ if (backend.runtime.startsWith("python")) {
850
+ runtime = await this.startPython(backend, Object.assign(Object.assign({}, runtimeEnv), secretEnvs));
851
+ }
852
+ else {
853
+ runtime = await this.startNode(backend, Object.assign(Object.assign({}, runtimeEnv), secretEnvs));
854
+ }
827
855
  const extensionLogInfo = {
828
856
  instanceId: backend.extensionInstanceId,
829
857
  ref: (_a = backend.extensionVersion) === null || _a === void 0 ? void 0 : _a.ref,
@@ -927,7 +955,14 @@ class FunctionsEmulator {
927
955
  this.logger.log("DEBUG", `[functions] Got req.url=${req.url}, mapping to path=${path}`);
928
956
  const pool = this.workerPools[record.backend.codebase];
929
957
  if (!pool.readyForWork(trigger.id)) {
930
- await this.startRuntime(record.backend, trigger);
958
+ try {
959
+ await this.startRuntime(record.backend, trigger);
960
+ }
961
+ catch (e) {
962
+ this.logger.logLabeled("ERROR", `Failed to handle request for function ${trigger.id}`);
963
+ this.logger.logLabeled("ERROR", `Failed to start functions in ${record.backend.functionsDir}: ${e}`);
964
+ return;
965
+ }
931
966
  }
932
967
  const debugBundle = this.args.debugPort
933
968
  ? {
@@ -86,12 +86,7 @@ class RuntimeWorker {
86
86
  }
87
87
  };
88
88
  return new Promise((resolve) => {
89
- const proxy = http.request({
90
- method: req.method,
91
- path: req.path,
92
- headers: req.headers,
93
- socketPath: this.runtime.socketPath,
94
- }, (_resp) => {
89
+ const proxy = http.request(Object.assign(Object.assign({}, this.runtime.conn.httpReqOpts()), { method: req.method, path: req.path, headers: req.headers }), (_resp) => {
95
90
  resp.writeHead(_resp.statusCode || 200, _resp.headers);
96
91
  const piped = _resp.pipe(resp);
97
92
  piped.on("finish", () => {
@@ -137,16 +132,11 @@ class RuntimeWorker {
137
132
  }
138
133
  isSocketReady() {
139
134
  return new Promise((resolve, reject) => {
140
- const req = http
141
- .request({
142
- method: "GET",
143
- path: "/__/health",
144
- socketPath: this.runtime.socketPath,
145
- }, () => {
135
+ const req = http.request(Object.assign(Object.assign({}, this.runtime.conn.httpReqOpts()), { method: "GET", path: "/__/health" }), () => {
146
136
  this.readyForWork();
147
137
  resolve();
148
- })
149
- .end();
138
+ });
139
+ req.end();
150
140
  req.on("error", (error) => {
151
141
  reject(error);
152
142
  });
@@ -229,6 +229,7 @@ function createFirebaseEndpoints(emulator) {
229
229
  throw err;
230
230
  }
231
231
  res.header("X-Goog-Upload-Size-Received", upload.size.toString());
232
+ res.header("x-goog-upload-status", upload.status);
232
233
  return res.sendStatus(200);
233
234
  }
234
235
  if (uploadCommand === "cancel") {
@@ -52,7 +52,7 @@ class DefaultStorageRulesManager {
52
52
  this._logger.log("WARN", `${parsedIssue.description_.replace(/\.$/, "")} in ${parsedIssue.sourcePosition_.fileName_}:${parsedIssue.sourcePosition_.line_}`);
53
53
  }
54
54
  catch (_a) {
55
- this._logger.log("WARN", issue);
55
+ this._logger.logLabeled("WARN", "storage", issue);
56
56
  }
57
57
  });
58
58
  return issues;
@@ -24,8 +24,8 @@ function createApp(defaultProjectId, emulator) {
24
24
  exposedHeaders: [
25
25
  "content-type",
26
26
  "x-firebase-storage-version",
27
+ "X-Goog-Upload-Size-Received",
27
28
  "x-goog-upload-url",
28
- "x-goog-upload-status",
29
29
  "x-goog-upload-command",
30
30
  "x-gupload-uploadid",
31
31
  "x-goog-upload-header-content-length",
@@ -11,9 +11,9 @@ var UploadType;
11
11
  })(UploadType = exports.UploadType || (exports.UploadType = {}));
12
12
  var UploadStatus;
13
13
  (function (UploadStatus) {
14
- UploadStatus[UploadStatus["ACTIVE"] = 0] = "ACTIVE";
15
- UploadStatus[UploadStatus["CANCELLED"] = 1] = "CANCELLED";
16
- UploadStatus[UploadStatus["FINISHED"] = 2] = "FINISHED";
14
+ UploadStatus["ACTIVE"] = "active";
15
+ UploadStatus["CANCELLED"] = "cancelled";
16
+ UploadStatus["FINISHED"] = "final";
17
17
  })(UploadStatus = exports.UploadStatus || (exports.UploadStatus = {}));
18
18
  class UploadNotActiveError extends Error {
19
19
  }
@@ -13,6 +13,7 @@ const logger_1 = require("../logger");
13
13
  const prompt_1 = require("../prompt");
14
14
  const utils = require("../utils");
15
15
  const projectUtils_1 = require("../projectUtils");
16
+ const functional_1 = require("../functional");
16
17
  var SecretLocation;
17
18
  (function (SecretLocation) {
18
19
  SecretLocation[SecretLocation["CLOUD"] = 1] = "CLOUD";
@@ -67,8 +68,9 @@ async function ask(args) {
67
68
  }
68
69
  utils.logLabeledBullet(extensionsHelper_1.logPrefix, "answer the questions below to configure your extension:");
69
70
  const substituted = (0, extensionsHelper_1.substituteParams)(args.paramSpecs, args.firebaseProjectParams);
71
+ const [advancedParams, standardParams] = (0, functional_1.partition)(substituted, (p) => { var _a; return (_a = p.advanced) !== null && _a !== void 0 ? _a : false; });
70
72
  const result = {};
71
- const promises = substituted.map((paramSpec) => {
73
+ const promises = standardParams.map((paramSpec) => {
72
74
  return async () => {
73
75
  result[paramSpec.param] = await askForParam({
74
76
  projectId: args.projectId,
@@ -78,6 +80,35 @@ async function ask(args) {
78
80
  });
79
81
  };
80
82
  });
83
+ if (advancedParams.length) {
84
+ promises.push(async () => {
85
+ const shouldPrompt = await (0, prompt_1.promptOnce)({
86
+ type: "confirm",
87
+ message: "Do you want to configure any advanced parameters for this instance?",
88
+ default: false,
89
+ });
90
+ if (shouldPrompt) {
91
+ const advancedPromises = advancedParams.map((paramSpec) => {
92
+ return async () => {
93
+ result[paramSpec.param] = await askForParam({
94
+ projectId: args.projectId,
95
+ instanceId: args.instanceId,
96
+ paramSpec: paramSpec,
97
+ reconfiguring: args.reconfiguring,
98
+ });
99
+ };
100
+ });
101
+ await advancedPromises.reduce((prev, cur) => prev.then(cur), Promise.resolve());
102
+ }
103
+ else {
104
+ for (const paramSpec of advancedParams) {
105
+ if (paramSpec.required && paramSpec.default) {
106
+ result[paramSpec.param] = { baseValue: paramSpec.default };
107
+ }
108
+ }
109
+ }
110
+ });
111
+ }
81
112
  await promises.reduce((prev, cur) => prev.then(cur), Promise.resolve());
82
113
  logger_1.logger.info();
83
114
  return result;
@@ -32,7 +32,7 @@ async function buildOptions(options) {
32
32
  options.extDevEnv = params;
33
33
  const functionEmuTriggerDefs = functionResources.map((r) => triggerHelper.functionResourceToEmulatedTriggerDefintion(r));
34
34
  options.extDevTriggers = functionEmuTriggerDefs;
35
- options.extDevNodeVersion = specHelper.getNodeVersion(functionResources);
35
+ options.extDevRuntime = specHelper.getRuntime(functionResources);
36
36
  return options;
37
37
  }
38
38
  exports.buildOptions = buildOptions;
@@ -45,12 +45,12 @@ async function getExtensionFunctionInfo(instance, paramValues) {
45
45
  trigger.name = `ext-${instance.instanceId}-${trigger.name}`;
46
46
  return trigger;
47
47
  });
48
- const nodeMajorVersion = specHelper.getNodeVersion(functionResources);
48
+ const runtime = specHelper.getRuntime(functionResources);
49
49
  const nonSecretEnv = getNonSecretEnv(spec.params, paramValues);
50
50
  const secretEnvVariables = getSecretEnvVars(spec.params, paramValues);
51
51
  return {
52
52
  extensionTriggers,
53
- nodeMajorVersion,
53
+ runtime,
54
54
  nonSecretEnv,
55
55
  secretEnvVariables,
56
56
  };
@@ -1,13 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getNodeVersion = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readFileFromDirectory = exports.readPostinstall = exports.readExtensionYaml = void 0;
3
+ exports.getRuntime = exports.DEFAULT_RUNTIME = exports.getFunctionProperties = exports.getFunctionResourcesWithParamSubstitution = exports.readFileFromDirectory = exports.readPostinstall = exports.readExtensionYaml = void 0;
4
4
  const yaml = require("js-yaml");
5
5
  const path = require("path");
6
6
  const fs = require("fs-extra");
7
7
  const error_1 = require("../../error");
8
8
  const extensionsHelper_1 = require("../extensionsHelper");
9
9
  const utils_1 = require("../utils");
10
- const functionsEmulatorUtils_1 = require("../../emulator/functionsEmulatorUtils");
11
10
  const SPEC_FILE = "extension.yaml";
12
11
  const POSTINSTALL_FILE = "POSTINSTALL.md";
13
12
  const validFunctionTypes = [
@@ -67,24 +66,26 @@ function getFunctionProperties(resources) {
67
66
  return resources.map((r) => r.properties);
68
67
  }
69
68
  exports.getFunctionProperties = getFunctionProperties;
70
- function getNodeVersion(resources) {
69
+ exports.DEFAULT_RUNTIME = "nodejs14";
70
+ function getRuntime(resources) {
71
+ if (resources.length === 0) {
72
+ return exports.DEFAULT_RUNTIME;
73
+ }
71
74
  const invalidRuntimes = [];
72
- const versions = resources.map((r) => {
73
- if ((0, utils_1.getResourceRuntime)(r)) {
74
- const runtimeName = (0, utils_1.getResourceRuntime)(r);
75
- const runtime = (0, functionsEmulatorUtils_1.parseRuntimeVersion)(runtimeName);
76
- if (!runtime) {
77
- invalidRuntimes.push(runtimeName);
78
- }
79
- else {
80
- return runtime;
81
- }
75
+ const runtimes = resources.map((r) => {
76
+ const runtime = (0, utils_1.getResourceRuntime)(r);
77
+ if (!runtime) {
78
+ return exports.DEFAULT_RUNTIME;
79
+ }
80
+ if (!/^(nodejs)?([0-9]+)/.test(runtime)) {
81
+ invalidRuntimes.push(runtime);
82
+ return exports.DEFAULT_RUNTIME;
82
83
  }
83
- return 14;
84
+ return runtime;
84
85
  });
85
86
  if (invalidRuntimes.length) {
86
87
  throw new error_1.FirebaseError(`The following runtimes are not supported by the Emulator Suite: ${invalidRuntimes.join(", ")}. \n Only Node runtimes are supported.`);
87
88
  }
88
- return Math.max(...versions);
89
+ return runtimes.sort()[runtimes.length - 1];
89
90
  }
90
- exports.getNodeVersion = getNodeVersion;
91
+ exports.getRuntime = getRuntime;
@@ -34,9 +34,10 @@ async function createInstanceHelper(projectId, instanceId, config, validateOnly
34
34
  return pollRes;
35
35
  }
36
36
  async function createInstance(args) {
37
- var _a, _b;
37
+ var _a, _b, _c;
38
38
  const config = {
39
39
  params: args.params,
40
+ systemParams: (_a = args.systemParams) !== null && _a !== void 0 ? _a : {},
40
41
  allowedEventTypes: args.allowedEventTypes,
41
42
  eventarcChannel: args.eventarcChannel,
42
43
  };
@@ -44,12 +45,12 @@ async function createInstance(args) {
44
45
  throw new error_1.FirebaseError("ExtensionSource and ExtensionVersion both provided, but only one should be.");
45
46
  }
46
47
  else if (args.extensionSource) {
47
- config.source = { name: (_a = args.extensionSource) === null || _a === void 0 ? void 0 : _a.name };
48
+ config.source = { name: (_b = args.extensionSource) === null || _b === void 0 ? void 0 : _b.name };
48
49
  }
49
50
  else if (args.extensionVersionRef) {
50
51
  const ref = refs.parse(args.extensionVersionRef);
51
52
  config.extensionRef = refs.toExtensionRef(ref);
52
- config.extensionVersion = (_b = ref.version) !== null && _b !== void 0 ? _b : "";
53
+ config.extensionVersion = (_c = ref.version) !== null && _c !== void 0 ? _c : "";
53
54
  }
54
55
  else {
55
56
  throw new error_1.FirebaseError("No ExtensionVersion or ExtensionSource provided but one is required.");
@@ -128,6 +129,10 @@ async function configureInstance(args) {
128
129
  reqBody.data.config.eventarcChannel = args.eventarcChannel;
129
130
  }
130
131
  reqBody.updateMask += ",config.allowed_event_types,config.eventarc_channel";
132
+ if (args.systemParams) {
133
+ reqBody.data.config.systemParams = args.systemParams;
134
+ reqBody.updateMask += ",config.system_params";
135
+ }
131
136
  return patchInstance(reqBody);
132
137
  }
133
138
  exports.configureInstance = configureInstance;
@@ -143,6 +148,10 @@ async function updateInstance(args) {
143
148
  body.config.params = args.params;
144
149
  updateMask += ",config.params";
145
150
  }
151
+ if (args.systemParams) {
152
+ body.config.systemParams = args.systemParams;
153
+ updateMask += ",config.system_params";
154
+ }
146
155
  if (args.canEmitEvents) {
147
156
  if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
148
157
  throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
@@ -174,6 +183,10 @@ async function updateInstanceFromRegistry(args) {
174
183
  body.config.params = args.params;
175
184
  updateMask += ",config.params";
176
185
  }
186
+ if (args.systemParams) {
187
+ body.config.systemParams = args.systemParams;
188
+ updateMask += ",config.system_params";
189
+ }
177
190
  if (args.canEmitEvents) {
178
191
  if (args.allowedEventTypes === undefined || args.eventarcChannel === undefined) {
179
192
  throw new error_1.FirebaseError(`This instance is configured to emit events, but either allowed event types or eventarc channel is undefined.`);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
3
+ exports.isSystemParam = exports.readEnvFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = exports.setNewDefaults = exports.buildBindingOptionsWithBaseValue = exports.getBaseParamBindings = void 0;
4
4
  const path = require("path");
5
5
  const clc = require("colorette");
6
6
  const fs = require("fs-extra");
@@ -172,3 +172,8 @@ function readEnvFile(envPath) {
172
172
  return result.envs;
173
173
  }
174
174
  exports.readEnvFile = readEnvFile;
175
+ function isSystemParam(paramName) {
176
+ const regex = /^firebaseextensions\.[a-zA-Z0-9\.]*\//;
177
+ return regex.test(paramName);
178
+ }
179
+ exports.isSystemParam = isSystemParam;
@@ -6,9 +6,9 @@ const apiv2_1 = require("../apiv2");
6
6
  const logger_1 = require("../logger");
7
7
  async function checkDatabaseType(projectId) {
8
8
  try {
9
- const client = new apiv2_1.Client({ urlPrefix: api_1.appengineOrigin, apiVersion: "v1" });
10
- const resp = await client.get(`/apps/${projectId}`);
11
- return resp.body.databaseType;
9
+ const client = new apiv2_1.Client({ urlPrefix: api_1.firestoreOrigin, apiVersion: "v1" });
10
+ const resp = await client.get(`/projects/${projectId}/databases/(default)`);
11
+ return resp.body.type;
12
12
  }
13
13
  catch (err) {
14
14
  logger_1.logger.debug("error getting database type", err);
@@ -120,8 +120,8 @@ function findDependency(name, options = {}) {
120
120
  }
121
121
  exports.findDependency = findDependency;
122
122
  async function prepareFrameworks(targetNames, context, options, emulators = []) {
123
- var _a;
124
- var _b, _c, _d, _e;
123
+ var _a, _b;
124
+ var _c, _d, _e, _f;
125
125
  const nodeVersion = process.version;
126
126
  if (!semver.satisfies(nodeVersion, ">=16.0.0")) {
127
127
  throw new error_1.FirebaseError(`The frameworks awareness feature requires Node.JS >= 16 and npm >= 8 in order to work correctly, due to some of the downstream dependencies. Please upgrade your version of Node.JS, reinstall firebase-tools, and give it another go.`);
@@ -133,7 +133,7 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
133
133
  try {
134
134
  await (0, requireHostingSite_1.requireHostingSite)(options);
135
135
  }
136
- catch (_f) {
136
+ catch (_g) {
137
137
  options.site = project;
138
138
  }
139
139
  }
@@ -243,10 +243,11 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
243
243
  }
244
244
  }
245
245
  else {
246
- const { wantsBackend = false, rewrites = [], redirects = [], headers = [], } = (await build(getProjectPath())) || {};
246
+ const { wantsBackend = false, rewrites = [], redirects = [], headers = [], trailingSlash, } = (await build(getProjectPath())) || {};
247
247
  config.rewrites.push(...rewrites);
248
248
  config.redirects.push(...redirects);
249
249
  config.headers.push(...headers);
250
+ (_b = config.trailingSlash) !== null && _b !== void 0 ? _b : (config.trailingSlash = trailingSlash);
250
251
  if (await (0, fs_extra_1.pathExists)(hostingDist))
251
252
  await (0, promises_1.rm)(hostingDist, { recursive: true });
252
253
  await (0, fs_extra_1.mkdirp)(hostingDist);
@@ -319,11 +320,11 @@ async function prepareFrameworks(targetNames, context, options, emulators = [])
319
320
  packageJson.main = "server.js";
320
321
  delete packageJson.devDependencies;
321
322
  packageJson.dependencies || (packageJson.dependencies = {});
322
- (_b = packageJson.dependencies)["firebase-frameworks"] || (_b["firebase-frameworks"] = exports.FIREBASE_FRAMEWORKS_VERSION);
323
- (_c = packageJson.dependencies)["firebase-functions"] || (_c["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION);
324
- (_d = packageJson.dependencies)["firebase-admin"] || (_d["firebase-admin"] = exports.FIREBASE_ADMIN_VERSION);
323
+ (_c = packageJson.dependencies)["firebase-frameworks"] || (_c["firebase-frameworks"] = exports.FIREBASE_FRAMEWORKS_VERSION);
324
+ (_d = packageJson.dependencies)["firebase-functions"] || (_d["firebase-functions"] = exports.FIREBASE_FUNCTIONS_VERSION);
325
+ (_e = packageJson.dependencies)["firebase-admin"] || (_e["firebase-admin"] = exports.FIREBASE_ADMIN_VERSION);
325
326
  packageJson.engines || (packageJson.engines = {});
326
- (_e = packageJson.engines).node || (_e.node = exports.NODE_VERSION);
327
+ (_f = packageJson.engines).node || (_f.node = exports.NODE_VERSION);
327
328
  await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, "package.json"), JSON.stringify(packageJson, null, 2));
328
329
  await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, ".env"), `__FIREBASE_FRAMEWORKS_ENTRY__=${frameworksEntry}
329
330
  ${firebaseDefaults ? `__FIREBASE_DEFAULTS__=${JSON.stringify(firebaseDefaults)}\n` : ""}`);