firebase-tools 11.21.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.
@@ -54,7 +54,7 @@ async function detectFromPort(port, project, runtime, timeout = 30000) {
54
54
  });
55
55
  while (true) {
56
56
  try {
57
- res = await Promise.race([(0, node_fetch_1.default)(`http://localhost:${port}/__/functions.yaml`), timedOut]);
57
+ res = await Promise.race([(0, node_fetch_1.default)(`http://127.0.0.1:${port}/__/functions.yaml`), timedOut]);
58
58
  break;
59
59
  }
60
60
  catch (err) {
@@ -2,10 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getRuntimeDelegate = exports.getHumanFriendlyRuntimeName = exports.isValidRuntime = exports.isDeprecatedRuntime = void 0;
4
4
  const node = require("./node");
5
+ const python = require("./python");
5
6
  const validate = require("../validate");
6
7
  const error_1 = require("../../../error");
7
8
  const RUNTIMES = ["nodejs10", "nodejs12", "nodejs14", "nodejs16", "nodejs18"];
8
- const EXPERIMENTAL_RUNTIMES = [];
9
+ const EXPERIMENTAL_RUNTIMES = ["python310", "python311"];
9
10
  const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
10
11
  function isDeprecatedRuntime(runtime) {
11
12
  return DEPRECATED_RUNTIMES.includes(runtime);
@@ -23,12 +24,14 @@ const MESSAGE_FRIENDLY_RUNTIMES = {
23
24
  nodejs14: "Node.js 14",
24
25
  nodejs16: "Node.js 16",
25
26
  nodejs18: "Node.js 18",
27
+ python310: "Python 3.10",
28
+ python311: "Python 3.11 (Preview)",
26
29
  };
27
30
  function getHumanFriendlyRuntimeName(runtime) {
28
31
  return MESSAGE_FRIENDLY_RUNTIMES[runtime] || runtime;
29
32
  }
30
33
  exports.getHumanFriendlyRuntimeName = getHumanFriendlyRuntimeName;
31
- const factories = [node.tryCreateDelegate];
34
+ const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
32
35
  async function getRuntimeDelegate(context) {
33
36
  const { projectDir, sourceDir, runtime } = context;
34
37
  validate.functionsDirectoryExists(sourceDir, projectDir);
@@ -16,6 +16,7 @@ const discovery = require("../discovery");
16
16
  const validate = require("./validate");
17
17
  const versioning = require("./versioning");
18
18
  const parseTriggers = require("./parseTriggers");
19
+ const fsutils_1 = require("../../../../fsutils");
19
20
  const MIN_FUNCTIONS_SDK_VERSION = "3.20.0";
20
21
  async function tryCreateDelegate(context) {
21
22
  const packageJsonPath = path.join(context.sourceDir, "package.json");
@@ -39,6 +40,7 @@ class Delegate {
39
40
  this.runtime = runtime;
40
41
  this.name = "nodejs";
41
42
  this._sdkVersion = undefined;
43
+ this._bin = "";
42
44
  }
43
45
  get sdkVersion() {
44
46
  if (this._sdkVersion === undefined) {
@@ -46,6 +48,38 @@ class Delegate {
46
48
  }
47
49
  return this._sdkVersion;
48
50
  }
51
+ get bin() {
52
+ if (this._bin === "") {
53
+ this._bin = this.getNodeBinary();
54
+ }
55
+ return this._bin;
56
+ }
57
+ getNodeBinary() {
58
+ const requestedVersion = semver.coerce(this.runtime);
59
+ if (!requestedVersion) {
60
+ throw new error_1.FirebaseError(`Could not determine version of the requested runtime: ${this.runtime}`);
61
+ }
62
+ const hostVersion = process.versions.node;
63
+ const localNodePath = path.join(this.sourceDir, "node_modules/node");
64
+ const localNodeVersion = versioning.findModuleVersion("node", localNodePath);
65
+ if (localNodeVersion) {
66
+ if (semver.major(requestedVersion) === semver.major(localNodeVersion)) {
67
+ (0, utils_1.logLabeledSuccess)("functions", `Using node@${semver.major(localNodeVersion)} from local cache.`);
68
+ return localNodePath;
69
+ }
70
+ }
71
+ if (semver.major(requestedVersion) === semver.major(hostVersion)) {
72
+ (0, utils_1.logLabeledSuccess)("functions", `Using node@${semver.major(hostVersion)} from host.`);
73
+ return process.execPath;
74
+ }
75
+ if (!process.env.FIREPIT_VERSION) {
76
+ (0, utils_1.logLabeledWarning)("functions", `Your requested "node" version "${semver.major(requestedVersion)}" doesn't match your global version "${semver.major(hostVersion)}". Using node@${semver.major(hostVersion)} from host.`);
77
+ return process.execPath;
78
+ }
79
+ (0, utils_1.logLabeledWarning)("functions", `You've requested "node" version "${semver.major(requestedVersion)}", but the standalone Firebase CLI comes with bundled Node "${semver.major(hostVersion)}".`);
80
+ (0, utils_1.logLabeledSuccess)("functions", `To use a different Node.js version, consider removing the standalone Firebase CLI and switching to "firebase-tools" on npm.`);
81
+ return process.execPath;
82
+ }
49
83
  validate() {
50
84
  versioning.checkFunctionsSDKVersion(this.sdkVersion);
51
85
  const relativeDir = path.relative(this.projectDir, this.sourceDir);
@@ -57,35 +91,44 @@ class Delegate {
57
91
  watch() {
58
92
  return Promise.resolve(() => Promise.resolve());
59
93
  }
60
- serve(port, config, envs) {
94
+ serveAdmin(port, config, envs) {
61
95
  var _a;
62
- const env = Object.assign(Object.assign({}, envs), { PORT: port.toString(), FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH, NODE_ENV: process.env.NODE_ENV });
96
+ const env = Object.assign(Object.assign({}, envs), { PORT: port, FUNCTIONS_CONTROL_API: "true", HOME: process.env.HOME, PATH: process.env.PATH, NODE_ENV: process.env.NODE_ENV });
63
97
  if (Object.keys(config || {}).length) {
64
98
  env.CLOUD_RUNTIME_CONFIG = JSON.stringify(config);
65
99
  }
100
+ const sourceNodeModulesPath = path.join(this.sourceDir, "node_modules");
66
101
  const sdkPath = require.resolve("firebase-functions", { paths: [this.sourceDir] });
67
- const binPath = sdkPath.substring(0, sdkPath.lastIndexOf("node_modules") + 12);
68
- const childProcess = spawn(path.join(binPath, ".bin", "firebase-functions"), [this.sourceDir], {
69
- env,
70
- cwd: this.sourceDir,
71
- stdio: ["ignore", "pipe", "inherit"],
72
- });
73
- (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
74
- logger_1.logger.debug(chunk.toString());
75
- });
76
- return Promise.resolve(async () => {
77
- const p = new Promise((resolve, reject) => {
78
- childProcess.once("exit", resolve);
79
- childProcess.once("error", reject);
80
- });
81
- await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
82
- setTimeout(() => {
83
- if (!childProcess.killed) {
84
- childProcess.kill("SIGKILL");
85
- }
86
- }, 10000);
87
- return p;
88
- });
102
+ const sdkNodeModulesPath = sdkPath.substring(0, sdkPath.lastIndexOf("node_modules") + 12);
103
+ for (const nodeModulesPath of [sourceNodeModulesPath, sdkNodeModulesPath]) {
104
+ const binPath = path.join(nodeModulesPath, ".bin", "firebase-functions");
105
+ if ((0, fsutils_1.fileExistsSync)(binPath)) {
106
+ logger_1.logger.debug(`Found firebase-functions binary at '${binPath}'`);
107
+ const childProcess = spawn(binPath, [this.sourceDir], {
108
+ env,
109
+ cwd: this.sourceDir,
110
+ stdio: ["ignore", "pipe", "inherit"],
111
+ });
112
+ (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
113
+ logger_1.logger.debug(chunk.toString());
114
+ });
115
+ return Promise.resolve(async () => {
116
+ const p = new Promise((resolve, reject) => {
117
+ childProcess.once("exit", resolve);
118
+ childProcess.once("error", reject);
119
+ });
120
+ await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
121
+ setTimeout(() => {
122
+ if (!childProcess.killed) {
123
+ childProcess.kill("SIGKILL");
124
+ }
125
+ }, 10000);
126
+ return p;
127
+ });
128
+ }
129
+ }
130
+ throw new error_1.FirebaseError("Failed to find location of Firebase Functions SDK. " +
131
+ "Please file a bug on Github (https://github.com/firebase/firebase-tools/).");
89
132
  }
90
133
  async discoverBuild(config, env) {
91
134
  if (!semver.valid(this.sdkVersion)) {
@@ -99,9 +142,9 @@ class Delegate {
99
142
  }
100
143
  let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
101
144
  if (!discovered) {
102
- const getPort = (0, util_1.promisify)(portfinder.getPort);
103
- const port = await getPort();
104
- const kill = await this.serve(port, config, env);
145
+ const basePort = 8000 + (0, utils_1.randomInt)(0, 1000);
146
+ const port = await portfinder.getPortPromise({ port: basePort });
147
+ const kill = await this.serveAdmin(port.toString(), config, env);
105
148
  try {
106
149
  discovered = await discovery.detectFromPort(port, this.projectId, this.runtime);
107
150
  }
@@ -4,12 +4,13 @@ exports.checkFunctionsSDKVersion = exports.getLatestSDKVersion = exports.getFunc
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
6
  const clc = require("colorette");
7
- const semver = require("semver");
8
7
  const spawn = require("cross-spawn");
9
- const utils = require("../../../../utils");
8
+ const semver = require("semver");
10
9
  const logger_1 = require("../../../../logger");
11
10
  const track_1 = require("../../../../track");
11
+ const utils = require("../../../../utils");
12
12
  const MIN_SDK_VERSION = "2.0.0";
13
+ const NPM_COMMAND_TIMEOUT_MILLIES = 10000;
13
14
  exports.FUNCTIONS_SDK_VERSION_TOO_OLD_WARNING = clc.bold(clc.yellow("functions: ")) +
14
15
  "You must have a " +
15
16
  clc.bold("firebase-functions") +
@@ -53,6 +54,7 @@ function getLatestSDKVersion() {
53
54
  var _a;
54
55
  const child = spawn.sync("npm", ["show", "firebase-functions", "--json=true"], {
55
56
  encoding: "utf8",
57
+ timeout: NPM_COMMAND_TIMEOUT_MILLIES,
56
58
  });
57
59
  if (child.error) {
58
60
  logger_1.logger.debug("checkFunctionsSDKVersion was unable to fetch information from NPM", child.error.stack);
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Delegate = exports.tryCreateDelegate = void 0;
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const node_fetch_1 = require("node-fetch");
7
+ const util_1 = require("util");
8
+ const portfinder = require("portfinder");
9
+ const runtimes = require("..");
10
+ const discovery = require("../discovery");
11
+ const logger_1 = require("../../../../logger");
12
+ const python_1 = require("../../../../functions/python");
13
+ const error_1 = require("../../../../error");
14
+ const LATEST_VERSION = "python310";
15
+ async function tryCreateDelegate(context) {
16
+ const requirementsTextPath = path.join(context.sourceDir, "requirements.txt");
17
+ if (!(await (0, util_1.promisify)(fs.exists)(requirementsTextPath))) {
18
+ logger_1.logger.debug("Customer code is not Python code.");
19
+ return;
20
+ }
21
+ const runtime = context.runtime ? context.runtime : LATEST_VERSION;
22
+ if (!runtimes.isValidRuntime(runtime)) {
23
+ throw new error_1.FirebaseError(`Runtime ${runtime} is not a valid Python runtime`);
24
+ }
25
+ return Promise.resolve(new Delegate(context.projectId, context.sourceDir, runtime));
26
+ }
27
+ exports.tryCreateDelegate = tryCreateDelegate;
28
+ class Delegate {
29
+ constructor(projectId, sourceDir, runtime) {
30
+ this.projectId = projectId;
31
+ this.sourceDir = sourceDir;
32
+ this.runtime = runtime;
33
+ this.name = "python";
34
+ this._bin = "";
35
+ this._modulesDir = "";
36
+ }
37
+ get bin() {
38
+ if (this._bin === "") {
39
+ this._bin = this.getPythonBinary();
40
+ }
41
+ return this._bin;
42
+ }
43
+ async modulesDir() {
44
+ var _a;
45
+ if (!this._modulesDir) {
46
+ const child = (0, python_1.runWithVirtualEnv)([
47
+ this.bin,
48
+ "-c",
49
+ '"import firebase_functions; import os; print(os.path.dirname(firebase_functions.__file__))"',
50
+ ], this.sourceDir, {});
51
+ let out = "";
52
+ (_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
53
+ const chunkString = chunk.toString();
54
+ out = out + chunkString;
55
+ logger_1.logger.debug(`stdout: ${chunkString}`);
56
+ });
57
+ await new Promise((resolve, reject) => {
58
+ child.on("exit", resolve);
59
+ child.on("error", reject);
60
+ });
61
+ this._modulesDir = out.trim();
62
+ }
63
+ return this._modulesDir;
64
+ }
65
+ getPythonBinary() {
66
+ if (process.platform === "win32") {
67
+ return "python.exe";
68
+ }
69
+ if (this.runtime === "python310") {
70
+ return "python3.10";
71
+ }
72
+ else if (this.runtime === "python311") {
73
+ return "python3.11";
74
+ }
75
+ return "python";
76
+ }
77
+ validate() {
78
+ return Promise.resolve();
79
+ }
80
+ watch() {
81
+ return Promise.resolve(() => Promise.resolve());
82
+ }
83
+ async build() {
84
+ return Promise.resolve();
85
+ }
86
+ async serveAdmin(port, envs) {
87
+ const modulesDir = await this.modulesDir();
88
+ const envWithAdminPort = Object.assign(Object.assign({}, envs), { ADMIN_PORT: port.toString() });
89
+ const args = [this.bin, path.join(modulesDir, "private", "serving.py")];
90
+ logger_1.logger.debug(`Running admin server with args: ${JSON.stringify(args)} and env: ${JSON.stringify(envWithAdminPort)} in ${this.sourceDir}`);
91
+ const childProcess = (0, python_1.runWithVirtualEnv)(args, this.sourceDir, envWithAdminPort);
92
+ return Promise.resolve(async () => {
93
+ await (0, node_fetch_1.default)(`http://127.0.0.1:${port}/__/quitquitquit`);
94
+ const quitTimeout = setTimeout(() => {
95
+ if (!childProcess.killed) {
96
+ childProcess.kill("SIGKILL");
97
+ }
98
+ }, 10000);
99
+ clearTimeout(quitTimeout);
100
+ });
101
+ }
102
+ async discoverBuild(_configValues, envs) {
103
+ let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
104
+ if (!discovered) {
105
+ const adminPort = await portfinder.getPortPromise({
106
+ port: 8081,
107
+ });
108
+ const killProcess = await this.serveAdmin(adminPort, envs);
109
+ try {
110
+ discovered = await discovery.detectFromPort(adminPort, this.projectId, this.runtime);
111
+ }
112
+ finally {
113
+ await killProcess();
114
+ }
115
+ }
116
+ return discovered;
117
+ }
118
+ }
119
+ exports.Delegate = Delegate;
@@ -4562,6 +4562,10 @@ exports.default = {
4562
4562
  description: "Response message for GetRecaptchaParam.",
4563
4563
  properties: {
4564
4564
  kind: { type: "string" },
4565
+ producerProjectNumber: {
4566
+ description: "The producer project number used to generate PIA tokens",
4567
+ type: "string",
4568
+ },
4565
4569
  recaptchaSiteKey: {
4566
4570
  description: "The reCAPTCHA v2 site key used to invoke the reCAPTCHA service. Always present.",
4567
4571
  type: "string",
@@ -11,7 +11,6 @@ const registry_1 = require("./registry");
11
11
  const types_1 = require("./types");
12
12
  const constants_1 = require("./constants");
13
13
  const functionsEmulator_1 = require("./functionsEmulator");
14
- const functionsEmulatorUtils_1 = require("./functionsEmulatorUtils");
15
14
  const auth_1 = require("./auth");
16
15
  const databaseEmulator_1 = require("./databaseEmulator");
17
16
  const firestoreEmulator_1 = require("./firestoreEmulator");
@@ -152,7 +151,7 @@ function findExportMetadata(importPath) {
152
151
  }
153
152
  }
154
153
  async function startAll(options, showUI = true) {
155
- var _a, _b, _c, _d, _e, _f, _g;
154
+ var _a, _b, _c, _d, _e, _f, _g, _h;
156
155
  const targets = filterEmulatorTargets(options);
157
156
  options.targets = targets;
158
157
  const singleProjectModeEnabled = ((_a = options.config.src.emulators) === null || _a === void 0 ? void 0 : _a.singleProjectMode) === undefined ||
@@ -301,13 +300,14 @@ async function startAll(options, showUI = true) {
301
300
  utils.assertIsStringOrUndefined(options.extDevDir);
302
301
  for (const cfg of functionsCfg) {
303
302
  const functionsDir = path.join(projectDir, cfg.source);
303
+ const runtime = (_e = options.extDevRuntime) !== null && _e !== void 0 ? _e : cfg.runtime;
304
304
  emulatableBackends.push({
305
305
  functionsDir,
306
+ runtime,
306
307
  codebase: cfg.codebase,
307
308
  env: Object.assign({}, options.extDevEnv),
308
309
  secretEnv: [],
309
310
  predefinedTriggers: options.extDevTriggers,
310
- nodeMajorVersion: (0, functionsEmulatorUtils_1.parseRuntimeVersion)(options.extDevNodeVersion || cfg.runtime),
311
311
  });
312
312
  }
313
313
  }
@@ -316,7 +316,7 @@ async function startAll(options, showUI = true) {
316
316
  }
317
317
  if (emulatableBackends.length) {
318
318
  if (!listenForEmulator.functions || !listenForEmulator.eventarc) {
319
- listenForEmulator = await (0, portUtils_1.resolveHostAndAssignPorts)(Object.assign(Object.assign({}, listenForEmulator), { functions: (_e = listenForEmulator.functions) !== null && _e !== void 0 ? _e : getListenConfig(options, types_1.Emulators.FUNCTIONS), eventarc: (_f = listenForEmulator.eventarc) !== null && _f !== void 0 ? _f : getListenConfig(options, types_1.Emulators.EVENTARC) }));
319
+ listenForEmulator = await (0, portUtils_1.resolveHostAndAssignPorts)(Object.assign(Object.assign({}, listenForEmulator), { functions: (_f = listenForEmulator.functions) !== null && _f !== void 0 ? _f : getListenConfig(options, types_1.Emulators.FUNCTIONS), eventarc: (_g = listenForEmulator.eventarc) !== null && _g !== void 0 ? _g : getListenConfig(options, types_1.Emulators.EVENTARC) }));
320
320
  hubLogger.log("DEBUG", "late-assigned ports for functions and eventarc emulators", {
321
321
  user: listenForEmulator,
322
322
  });
@@ -377,7 +377,7 @@ async function startAll(options, showUI = true) {
377
377
  });
378
378
  }
379
379
  const config = options.config;
380
- const rulesLocalPath = (_g = config.src.firestore) === null || _g === void 0 ? void 0 : _g.rules;
380
+ const rulesLocalPath = (_h = config.src.firestore) === null || _h === void 0 ? void 0 : _h.rules;
381
381
  let rulesFileFound = false;
382
382
  if (rulesLocalPath) {
383
383
  const rules = config.path(rulesLocalPath);
@@ -143,14 +143,15 @@ class ExtensionsEmulator {
143
143
  const extensionDir = await this.ensureSourceCode(instance);
144
144
  const functionsDir = path.join(extensionDir, "functions");
145
145
  const env = Object.assign(this.autoPopulatedParams(instance), instance.params);
146
- const { extensionTriggers, nodeMajorVersion, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(instance, env);
146
+ const { extensionTriggers, runtime, nonSecretEnv, secretEnvVariables } = await (0, optionsHelper_1.getExtensionFunctionInfo)(instance, env);
147
147
  const emulatableBackend = {
148
148
  functionsDir,
149
+ runtime,
150
+ bin: process.execPath,
149
151
  env: nonSecretEnv,
150
152
  codebase: instance.instanceId,
151
153
  secretEnv: secretEnvVariables,
152
154
  predefinedTriggers: extensionTriggers,
153
- nodeMajorVersion: nodeMajorVersion,
154
155
  extensionInstanceId: instance.instanceId,
155
156
  };
156
157
  if (instance.ref) {