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
@@ -8,7 +8,7 @@ if (!semver.satisfies(nodeVersion, pkg.engines.node)) {
8
8
  console.error(`Firebase CLI v${pkg.version} is incompatible with Node.js ${nodeVersion} Please upgrade Node.js to version ${pkg.engines.node}`);
9
9
  process.exit(1);
10
10
  }
11
- const updateNotifierPkg = require("update-notifier");
11
+ const updateNotifierPkg = require("update-notifier-cjs");
12
12
  const clc = require("colorette");
13
13
  const TerminalRenderer = require("marked-terminal");
14
14
  const updateNotifier = updateNotifierPkg({ pkg });
@@ -90,6 +90,9 @@ exports.command = new command_1.Command("appdistribution:distribute <release-bin
90
90
  default:
91
91
  utils.logSuccess(`uploaded release ${release.displayVersion} (${release.buildVersion}) successfully!`);
92
92
  }
93
+ utils.logSuccess(`View this release in the Firebase console: ${release.firebaseConsoleUri}`);
94
+ utils.logSuccess(`Share this release with testers who have access: ${release.testingUri}`);
95
+ utils.logSuccess(`Download the release binary (link expires in 1 hour): ${release.binaryDownloadUri}`);
93
96
  releaseName = uploadResponse.release.name;
94
97
  }
95
98
  catch (err) {
@@ -32,6 +32,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
32
32
  .before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
33
33
  .before(extensionsHelper_1.diagnoseAndFixProject)
34
34
  .action(async (instanceId, options) => {
35
+ var _a;
35
36
  const projectId = (0, projectUtils_1.getProjectId)(options);
36
37
  if (options.nonInteractive) {
37
38
  throw new error_1.FirebaseError(`Command not supported in non-interactive mode, edit ./extensions/${instanceId}.env directly instead. ` +
@@ -56,7 +57,7 @@ exports.command = new command_1.Command("ext:configure <extensionInstanceId>")
56
57
  instanceId,
57
58
  projectDir: config.projectDir,
58
59
  });
59
- const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params, (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
60
+ const [immutableParams, tbdParams] = (0, functional_1.partition)(spec.params.concat((_a = spec.systemParams) !== null && _a !== void 0 ? _a : []), (param) => { var _a; return (_a = param.immutable) !== null && _a !== void 0 ? _a : false; });
60
61
  infoImmutableParams(immutableParams, oldParamValues);
61
62
  paramHelper.setNewDefaults(tbdParams, oldParamValues);
62
63
  const mutableParamsBindingOptions = await paramHelper.getParams({
@@ -120,7 +120,7 @@ async function infoExtensionVersion(args) {
120
120
  await (0, warnings_1.displayWarningPrompts)(ref.publisherId, extension.registryLaunchStage, args.extensionVersion);
121
121
  }
122
122
  async function installToManifest(options) {
123
- var _a;
123
+ var _a, _b;
124
124
  const { projectId, extensionName, extVersion, source, paramsEnvPath, nonInteractive, force } = options;
125
125
  const isLocalSource = (0, extensionsHelper_1.isLocalPath)(extensionName);
126
126
  const spec = (_a = extVersion === null || extVersion === void 0 ? void 0 : extVersion.spec) !== null && _a !== void 0 ? _a : source === null || source === void 0 ? void 0 : source.spec;
@@ -134,7 +134,7 @@ async function installToManifest(options) {
134
134
  }
135
135
  const paramBindingOptions = await paramHelper.getParams({
136
136
  projectId,
137
- paramSpecs: spec.params,
137
+ paramSpecs: spec.params.concat((_b = spec.systemParams) !== null && _b !== void 0 ? _b : []),
138
138
  nonInteractive,
139
139
  paramsEnvPath,
140
140
  instanceId,
@@ -8,7 +8,9 @@ const error_1 = require("../../error");
8
8
  const extensionsHelper_1 = require("../../extensions/extensionsHelper");
9
9
  const logger_1 = require("../../logger");
10
10
  const manifest_1 = require("../../extensions/manifest");
11
+ const paramHelper_1 = require("../../extensions/paramHelper");
11
12
  const specHelper_1 = require("../../extensions/emulator/specHelper");
13
+ const functional_1 = require("../../functional");
12
14
  async function getExtensionVersion(i) {
13
15
  if (!i.extensionVersion) {
14
16
  if (!i.ref) {
@@ -49,9 +51,11 @@ exports.getExtensionSpec = getExtensionSpec;
49
51
  async function have(projectId) {
50
52
  const instances = await extensionsApi.listInstances(projectId);
51
53
  return instances.map((i) => {
54
+ var _a;
52
55
  const dep = {
53
56
  instanceId: i.name.split("/").pop(),
54
57
  params: i.config.params,
58
+ systemParams: (_a = i.config.systemParams) !== null && _a !== void 0 ? _a : {},
55
59
  allowedEventTypes: i.config.allowedEventTypes,
56
60
  eventarcChannel: i.config.eventarcChannel,
57
61
  etag: i.etag,
@@ -71,7 +75,7 @@ async function want(args) {
71
75
  for (const e of Object.entries(args.extensions)) {
72
76
  try {
73
77
  const instanceId = e[0];
74
- const params = (0, manifest_1.readInstanceParam)({
78
+ const rawParams = (0, manifest_1.readInstanceParam)({
75
79
  projectDir: args.projectDir,
76
80
  instanceId,
77
81
  projectId: args.projectId,
@@ -80,18 +84,20 @@ async function want(args) {
80
84
  checkLocal: args.emulatorMode,
81
85
  });
82
86
  const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId, args.emulatorMode);
83
- const subbedParams = (0, extensionsHelper_1.substituteParams)(params, autoPopulatedParams);
84
- const allowedEventTypes = subbedParams.ALLOWED_EVENT_TYPES !== undefined
85
- ? subbedParams.ALLOWED_EVENT_TYPES.split(",").filter((e) => e !== "")
87
+ const subbedParams = (0, extensionsHelper_1.substituteParams)(rawParams, autoPopulatedParams);
88
+ const [systemParams, params] = (0, functional_1.partitionRecord)(subbedParams, paramHelper_1.isSystemParam);
89
+ const allowedEventTypes = params.ALLOWED_EVENT_TYPES !== undefined
90
+ ? params.ALLOWED_EVENT_TYPES.split(",").filter((e) => e !== "")
86
91
  : undefined;
87
- const eventarcChannel = subbedParams.EVENTARC_CHANNEL;
88
- delete subbedParams["EVENTARC_CHANNEL"];
89
- delete subbedParams["ALLOWED_EVENT_TYPES"];
92
+ const eventarcChannel = params.EVENTARC_CHANNEL;
93
+ delete params["EVENTARC_CHANNEL"];
94
+ delete params["ALLOWED_EVENT_TYPES"];
90
95
  if ((0, extensionsHelper_1.isLocalPath)(e[1])) {
91
96
  instanceSpecs.push({
92
97
  instanceId,
93
98
  localPath: e[1],
94
- params: subbedParams,
99
+ params,
100
+ systemParams,
95
101
  allowedEventTypes: allowedEventTypes,
96
102
  eventarcChannel: eventarcChannel,
97
103
  });
@@ -102,7 +108,8 @@ async function want(args) {
102
108
  instanceSpecs.push({
103
109
  instanceId,
104
110
  ref,
105
- params: subbedParams,
111
+ params,
112
+ systemParams,
106
113
  allowedEventTypes: allowedEventTypes,
107
114
  eventarcChannel: eventarcChannel,
108
115
  });
@@ -32,6 +32,7 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
32
32
  projectId,
33
33
  instanceId: instanceSpec.instanceId,
34
34
  params: instanceSpec.params,
35
+ systemParams: instanceSpec.systemParams,
35
36
  extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
36
37
  allowedEventTypes: instanceSpec.allowedEventTypes,
37
38
  eventarcChannel: instanceSpec.eventarcChannel,
@@ -44,6 +45,7 @@ function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
44
45
  projectId,
45
46
  instanceId: instanceSpec.instanceId,
46
47
  params: instanceSpec.params,
48
+ systemParams: instanceSpec.systemParams,
47
49
  extensionSource,
48
50
  allowedEventTypes: instanceSpec.allowedEventTypes,
49
51
  eventarcChannel: instanceSpec.eventarcChannel,
@@ -71,6 +73,7 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
71
73
  instanceId: instanceSpec.instanceId,
72
74
  extRef: refs.toExtensionVersionRef(instanceSpec.ref),
73
75
  params: instanceSpec.params,
76
+ systemParams: instanceSpec.systemParams,
74
77
  canEmitEvents: !!instanceSpec.allowedEventTypes,
75
78
  allowedEventTypes: instanceSpec.allowedEventTypes,
76
79
  eventarcChannel: instanceSpec.eventarcChannel,
@@ -84,6 +87,8 @@ function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = fal
84
87
  instanceId: instanceSpec.instanceId,
85
88
  extensionSource,
86
89
  validateOnly,
90
+ params: instanceSpec.params,
91
+ systemParams: instanceSpec.systemParams,
87
92
  canEmitEvents: !!instanceSpec.allowedEventTypes,
88
93
  allowedEventTypes: instanceSpec.allowedEventTypes,
89
94
  eventarcChannel: instanceSpec.eventarcChannel,
@@ -109,6 +114,7 @@ function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly =
109
114
  projectId,
110
115
  instanceId: instanceSpec.instanceId,
111
116
  params: instanceSpec.params,
117
+ systemParams: instanceSpec.systemParams,
112
118
  canEmitEvents: !!instanceSpec.allowedEventTypes,
113
119
  allowedEventTypes: instanceSpec.allowedEventTypes,
114
120
  eventarcChannel: instanceSpec.eventarcChannel,
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prepareFunctionsUpload = exports.getFunctionsConfig = void 0;
3
+ exports.convertToSortedKeyValueArray = exports.prepareFunctionsUpload = exports.getFunctionsConfig = void 0;
4
4
  const archiver = require("archiver");
5
5
  const clc = require("colorette");
6
6
  const filesize = require("filesize");
@@ -66,8 +66,9 @@ async function packageSource(sourceDir, config, runtimeConfig) {
66
66
  });
67
67
  }
68
68
  if (typeof runtimeConfig !== "undefined") {
69
+ const runtimeConfigHashString = JSON.stringify(convertToSortedKeyValueArray(runtimeConfig));
70
+ hashes.push(runtimeConfigHashString);
69
71
  const runtimeConfigString = JSON.stringify(runtimeConfig, null, 2);
70
- hashes.push(runtimeConfigString);
71
72
  archive.append(runtimeConfigString, {
72
73
  name: CONFIG_DEST_FILE,
73
74
  mode: 420,
@@ -94,3 +95,13 @@ async function prepareFunctionsUpload(sourceDir, config, runtimeConfig) {
94
95
  return packageSource(sourceDir, config, runtimeConfig);
95
96
  }
96
97
  exports.prepareFunctionsUpload = prepareFunctionsUpload;
98
+ function convertToSortedKeyValueArray(config) {
99
+ if (typeof config !== "object" || config === null)
100
+ return config;
101
+ return Object.keys(config)
102
+ .sort()
103
+ .map((key) => {
104
+ return { key, value: convertToSortedKeyValueArray(config[key]) };
105
+ });
106
+ }
107
+ exports.convertToSortedKeyValueArray = convertToSortedKeyValueArray;
@@ -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 = ["go113"];
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,13 +24,14 @@ const MESSAGE_FRIENDLY_RUNTIMES = {
23
24
  nodejs14: "Node.js 14",
24
25
  nodejs16: "Node.js 16",
25
26
  nodejs18: "Node.js 18",
26
- go113: "Go 1.13",
27
+ python310: "Python 3.10",
28
+ python311: "Python 3.11 (Preview)",
27
29
  };
28
30
  function getHumanFriendlyRuntimeName(runtime) {
29
31
  return MESSAGE_FRIENDLY_RUNTIMES[runtime] || runtime;
30
32
  }
31
33
  exports.getHumanFriendlyRuntimeName = getHumanFriendlyRuntimeName;
32
- const factories = [node.tryCreateDelegate];
34
+ const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
33
35
  async function getRuntimeDelegate(context) {
34
36
  const { projectDir, sourceDir, runtime } = context;
35
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,33 +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
  }
66
- const childProcess = spawn("./node_modules/.bin/firebase-functions", [this.sourceDir], {
67
- env,
68
- cwd: this.sourceDir,
69
- stdio: ["ignore", "pipe", "inherit"],
70
- });
71
- (_a = childProcess.stdout) === null || _a === void 0 ? void 0 : _a.on("data", (chunk) => {
72
- logger_1.logger.debug(chunk.toString());
73
- });
74
- return Promise.resolve(async () => {
75
- const p = new Promise((resolve, reject) => {
76
- childProcess.once("exit", resolve);
77
- childProcess.once("error", reject);
78
- });
79
- await (0, node_fetch_1.default)(`http://localhost:${port}/__/quitquitquit`);
80
- setTimeout(() => {
81
- if (!childProcess.killed) {
82
- childProcess.kill("SIGKILL");
83
- }
84
- }, 10000);
85
- return p;
86
- });
100
+ const sourceNodeModulesPath = path.join(this.sourceDir, "node_modules");
101
+ const sdkPath = require.resolve("firebase-functions", { paths: [this.sourceDir] });
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/).");
87
132
  }
88
133
  async discoverBuild(config, env) {
89
134
  if (!semver.valid(this.sdkVersion)) {
@@ -97,9 +142,9 @@ class Delegate {
97
142
  }
98
143
  let discovered = await discovery.detectFromYaml(this.sourceDir, this.projectId, this.runtime);
99
144
  if (!discovered) {
100
- const getPort = (0, util_1.promisify)(portfinder.getPort);
101
- const port = await getPort();
102
- 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);
103
148
  try {
104
149
  discovered = await discovery.detectFromPort(port, this.projectId, this.runtime);
105
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",
@@ -502,7 +502,7 @@ function createSessionCookie(state, reqBody) {
502
502
  const { payload } = parseIdToken(state, reqBody.idToken);
503
503
  const issuedAt = (0, utils_1.toUnixTimestamp)(new Date());
504
504
  const expiresAt = issuedAt + validDuration;
505
- const sessionCookie = (0, jsonwebtoken_1.sign)(Object.assign(Object.assign({}, payload), { iat: issuedAt, exp: expiresAt, iss: `https://session.firebase.google.com/${payload.aud}` }), "", {
505
+ const sessionCookie = (0, jsonwebtoken_1.sign)(Object.assign(Object.assign({}, payload), { iat: issuedAt, exp: expiresAt, iss: `https://session.firebase.google.com/${payload.aud}` }), "fake-secret", {
506
506
  algorithm: "none",
507
507
  });
508
508
  return { sessionCookie };
@@ -1552,7 +1552,7 @@ function generateJwt(user, { projectId, signInProvider, expiresInSeconds, extraC
1552
1552
  tenant: tenantId,
1553
1553
  sign_in_attributes: signInAttributes,
1554
1554
  } });
1555
- const jwtStr = (0, jsonwebtoken_1.sign)(customPayloadFields, "", {
1555
+ const jwtStr = (0, jsonwebtoken_1.sign)(customPayloadFields, "fake-secret", {
1556
1556
  algorithm: "none",
1557
1557
  expiresIn: expiresInSeconds,
1558
1558
  subject: user.localId,
@@ -2129,7 +2129,7 @@ function generateBlockingFunctionJwt(state, event, url, timeoutMs, user, options
2129
2129
  if (state.shouldForwardCredentialToBlockingFunction("refreshToken")) {
2130
2130
  jwt.oauth_refresh_token = oauthTokens.oauthRefreshToken;
2131
2131
  }
2132
- const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "", {
2132
+ const jwtStr = (0, jsonwebtoken_1.sign)(jwt, "fake-secret", {
2133
2133
  algorithm: "none",
2134
2134
  });
2135
2135
  return jwtStr;
@@ -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);
@@ -28,9 +28,9 @@ const EMULATOR_UPDATE_DETAILS = {
28
28
  expectedChecksum: "4f41d24a3c0f3b55ea22804a424cc0ee",
29
29
  },
30
30
  storage: {
31
- version: "1.1.2",
32
- expectedSize: 47028740,
33
- expectedChecksum: "983b4415b1e72b109864f1b8e7ea7546",
31
+ version: "1.1.3",
32
+ expectedSize: 52892936,
33
+ expectedChecksum: "2ca11ec1193003bea89f806cc085fa25",
34
34
  },
35
35
  ui: experiments.isEnabled("emulatoruisnapshot")
36
36
  ? { version: "SNAPSHOT", expectedSize: -1, expectedChecksum: "" }
@@ -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) {