You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 7-8.RSVP
Socket
Socket
Sign inDemoInstall

serve

Package Overview
Dependencies
Maintainers
9
Versions
152
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 14.0.1 to 14.1.0

331

build/main.js
#!/usr/bin/env node
// source/main.ts
import { cwd as getPwd, exit, env as env2, stdout } from "node:process";
import path from "node:path";
import chalk3 from "chalk";
import chalk4 from "chalk";
import boxen from "boxen";
import clipboard from "clipboardy";
import checkForUpdate from "update-check";

@@ -13,3 +13,3 @@ // package.json

name: "serve",
version: "14.0.1",
version: "14.1.0",
description: "Static file serving and directory listing",

@@ -39,3 +39,5 @@ keywords: [

"test:tsc": "tsc --project tsconfig.json",
test: "pnpm test:tsc",
"test:unit": "vitest run --config config/vitest.ts",
"test:watch": "vitest watch --config config/vitest.ts",
test: "pnpm test:tsc && pnpm test:unit",
"lint:code": "eslint --max-warnings 0 source/**/*.ts",

@@ -52,2 +54,3 @@ "lint:style": "prettier --check --ignore-path .gitignore .",

boxen: "7.0.0",
c8: "7.11.3",
chalk: "5.0.1",

@@ -58,3 +61,3 @@ "chalk-template": "0.4.0",

"is-port-reachable": "4.0.0",
"serve-handler": "6.1.3",
"serve-handler": "6.1.5",
"update-check": "1.5.4"

@@ -67,2 +70,3 @@ },

eslint: "8.19.0",
got: "12.1.0",
husky: "8.0.1",

@@ -73,3 +77,4 @@ "lint-staged": "13.0.3",

tsx: "3.7.1",
typescript: "4.6.4"
typescript: "4.6.4",
vitest: "0.18.0"
},

@@ -98,3 +103,7 @@ tsup: {

"source/**/*.ts": [
"eslint --max-warnings 0 --fix"
"eslint --max-warnings 0 --fix",
"vitest related --run"
],
tests: [
"vitest --run"
]

@@ -106,6 +115,13 @@ }

import { promisify } from "node:util";
var resolve = (promise) => promise.then((data) => [void 0, data]).catch((error2) => [error2, void 0]);
var resolve = async (promiseLike) => {
try {
const data = await promiseLike;
return [void 0, data];
} catch (error2) {
return [error2, void 0];
}
};
// source/utilities/server.ts
import http from "node:http";
import http2 from "node:http";
import https from "node:https";

@@ -116,31 +132,7 @@ import { readFile } from "node:fs/promises";

import isPortReachable from "is-port-reachable";
import chalk2 from "chalk";
// source/utilities/http.ts
import { parse } from "node:url";
import { networkInterfaces as getNetworkInterfaces } from "node:os";
var networkInterfaces = getNetworkInterfaces();
var parseEndpoint = (uriOrPort) => {
if (!isNaN(Number(uriOrPort)))
return [uriOrPort];
const endpoint = uriOrPort;
const url = parse(endpoint);
switch (url.protocol) {
case "pipe:": {
const pipe = endpoint.replace(/^pipe:/, "");
if (!pipe.startsWith("\\\\.\\"))
throw new Error(`Invalid Windows named pipe endpoint: ${endpoint}`);
return [pipe];
}
case "unix:":
if (!url.pathname)
throw new Error(`Invalid UNIX domain socket endpoint: ${endpoint}`);
return [url.pathname];
case "tcp:":
url.port = url.port ?? "3000";
url.hostname = url.hostname ?? "localhost";
return [parseInt(url.port, 10), url.hostname];
default:
throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol ?? "undefined"}`);
}
};
var registerCloseListener = (fn) => {

@@ -170,2 +162,11 @@ let run = false;

// source/utilities/logger.ts
import chalk from "chalk";
var http = (...message) => console.info(chalk.bgBlue.bold(" HTTP "), ...message);
var info = (...message) => console.info(chalk.bgMagenta.bold(" INFO "), ...message);
var warn = (...message) => console.error(chalk.bgYellow.bold(" WARN "), ...message);
var error = (...message) => console.error(chalk.bgRed.bold(" ERROR "), ...message);
var log = console.log;
var logger = { http, info, warn, error, log };
// source/utilities/server.ts

@@ -176,2 +177,8 @@ var compress = promisify(compression());

const run = async () => {
const requestTime = new Date();
const formattedTime = `${requestTime.toLocaleDateString()} ${requestTime.toLocaleTimeString()}`;
const ipAddress = request.socket.remoteAddress?.replace("::ffff:", "") ?? "unknown";
const requestUrl = `${request.method ?? "GET"} ${request.url ?? "/"}`;
if (!args2["--no-request-logging"])
logger.http(chalk2.dim(formattedTime), chalk2.yellow(ipAddress), chalk2.cyan(requestUrl));
if (args2["--cors"])

@@ -182,2 +189,5 @@ response.setHeader("Access-Control-Allow-Origin", "*");

await handler(request, response, config2);
const responseTime = Date.now() - requestTime.getTime();
if (!args2["--no-request-logging"])
logger.http(chalk2.dim(formattedTime), chalk2.yellow(ipAddress), chalk2[response.statusCode < 400 ? "green" : "red"](`Returned ${response.statusCode} in ${responseTime} ms`));
};

@@ -188,11 +198,21 @@ run().catch((error2) => {

};
const useSsl = args2["--ssl-cert"] && args2["--ssl-key"];
const httpMode = useSsl ? "https" : "http";
const sslCert = args2["--ssl-cert"];
const sslKey = args2["--ssl-key"];
const sslPass = args2["--ssl-pass"];
const serverConfig = httpMode === "https" && args2["--ssl-cert"] && args2["--ssl-key"] ? {
key: await readFile(args2["--ssl-key"]),
cert: await readFile(args2["--ssl-cert"]),
passphrase: sslPass ? await readFile(sslPass, "utf8") : ""
} : {};
const server = httpMode === "https" ? https.createServer(serverConfig, serverHandler) : http.createServer(serverHandler);
const isPFXFormat = sslCert && /[.](?<extension>pfx|p12)$/.exec(sslCert) !== null;
const useSsl = sslCert && (sslKey || sslPass || isPFXFormat);
let serverConfig = {};
if (useSsl && sslCert && sslKey) {
serverConfig = {
key: await readFile(sslKey),
cert: await readFile(sslCert),
passphrase: sslPass ? await readFile(sslPass, "utf8") : ""
};
} else if (useSsl && sslCert && isPFXFormat) {
serverConfig = {
pfx: await readFile(sslCert),
passphrase: sslPass ? await readFile(sslPass, "utf8") : ""
};
}
const server = useSsl ? https.createServer(serverConfig, serverHandler) : http2.createServer(serverHandler);
const getServerDetails = () => {

@@ -214,4 +234,5 @@ registerCloseListener(() => server.close());

const ip = getNetworkAddress();
local = `${httpMode}://${address}:${details.port}`;
network = ip ? `${httpMode}://${ip}:${details.port}` : void 0;
const protocol = useSsl ? "https" : "http";
local = `${protocol}://${address}:${details.port}`;
network = ip ? `${protocol}://${ip}:${details.port}` : void 0;
}

@@ -227,17 +248,17 @@ return {

});
if (typeof endpoint[0] === "number" && !isNaN(endpoint[0]) && endpoint[0] !== 0) {
const port = endpoint[0];
if (typeof endpoint.port === "number" && !isNaN(endpoint.port) && endpoint.port !== 0) {
const port = endpoint.port;
const isClosed = await isPortReachable(port, {
host: endpoint[1] ?? "localhost"
host: endpoint.host ?? "localhost"
});
if (isClosed)
return startServer([0], config2, args2, port);
return startServer({ port: 0 }, config2, args2, port);
}
return new Promise((resolve2, _reject) => {
if (endpoint.length === 1 && typeof endpoint[0] === "number")
server.listen(endpoint[0], () => resolve2(getServerDetails()));
else if (endpoint.length === 1 && typeof endpoint[0] === "string")
server.listen(endpoint[0], () => resolve2(getServerDetails()));
else if (endpoint.length === 2 && typeof endpoint[0] === "number" && typeof endpoint[1] === "string")
server.listen(endpoint[0], endpoint[1], () => resolve2(getServerDetails()));
if (typeof endpoint.port !== "undefined" && typeof endpoint.host === "undefined")
server.listen(endpoint.port, () => resolve2(getServerDetails()));
else if (typeof endpoint.port === "undefined" && typeof endpoint.host !== "undefined")
server.listen(endpoint.host, () => resolve2(getServerDetails()));
else if (typeof endpoint.port !== "undefined" && typeof endpoint.host !== "undefined")
server.listen(endpoint.port, endpoint.host, () => resolve2(getServerDetails()));
});

@@ -247,33 +268,9 @@ };

// source/utilities/cli.ts
import chalk from "chalk-template";
import { parse as parseUrl } from "node:url";
import { env } from "node:process";
import chalk3 from "chalk";
import chalkTemplate from "chalk-template";
import parseArgv from "arg";
var options = {
"--help": Boolean,
"--version": Boolean,
"--listen": [parseEndpoint],
"--single": Boolean,
"--debug": Boolean,
"--config": String,
"--no-clipboard": Boolean,
"--no-compression": Boolean,
"--no-etag": Boolean,
"--symlinks": Boolean,
"--cors": Boolean,
"--no-port-switching": Boolean,
"--ssl-cert": String,
"--ssl-key": String,
"--ssl-pass": String,
"-h": "--help",
"-v": "--version",
"-l": "--listen",
"-s": "--single",
"-d": "--debug",
"-c": "--config",
"-n": "--no-clipboard",
"-u": "--no-compression",
"-S": "--symlinks",
"-C": "--cors",
"-p": "--listen"
};
var helpText = chalk`
import checkForUpdate from "update-check";
var helpText = chalkTemplate`
{bold.cyan serve} - Static file serving and directory listing

@@ -304,8 +301,10 @@

-s, --single Rewrite all not-found requests to \`index.html\`
-d, --debug Show debugging information
-s, --single Rewrite all not-found requests to \`index.html\`
-c, --config Specify custom path to \`serve.json\`
-L, --no-request-logging Do not log any request information to the console.
-C, --cors Enable CORS, sets \`Access-Control-Allow-Origin\` to \`*\`

@@ -322,4 +321,6 @@

--ssl-cert Optional path to an SSL/TLS certificate to serve with HTTPS
{grey Supported formats: PEM (default) and PKCS12 (PFX)}
--ssl-key Optional path to the SSL/TLS certificate\'s private key
{grey Applicable only for PEM certificates}

@@ -351,4 +352,69 @@ --ssl-pass Optional path to the SSL/TLS certificate\'s passphrase

`;
var getHelpText = () => helpText;
var parseEndpoint = (uriOrPort) => {
if (!isNaN(Number(uriOrPort)))
return { port: Number(uriOrPort) };
const endpoint = uriOrPort;
const url = parseUrl(endpoint);
switch (url.protocol) {
case "pipe:": {
const pipe = endpoint.replace(/^pipe:/, "");
if (!pipe.startsWith("\\\\.\\"))
throw new Error(`Invalid Windows named pipe endpoint: ${endpoint}`);
return { host: pipe };
}
case "unix:":
if (!url.pathname)
throw new Error(`Invalid UNIX domain socket endpoint: ${endpoint}`);
return { host: url.pathname };
case "tcp:":
url.port = url.port ?? "3000";
url.hostname = url.hostname ?? "localhost";
return {
port: Number(url.port),
host: url.hostname
};
default:
throw new Error(`Unknown --listen endpoint scheme (protocol): ${url.protocol ?? "undefined"}`);
}
};
var options = {
"--help": Boolean,
"--version": Boolean,
"--listen": [parseEndpoint],
"--single": Boolean,
"--debug": Boolean,
"--config": String,
"--no-clipboard": Boolean,
"--no-compression": Boolean,
"--no-etag": Boolean,
"--symlinks": Boolean,
"--cors": Boolean,
"--no-port-switching": Boolean,
"--ssl-cert": String,
"--ssl-key": String,
"--ssl-pass": String,
"-h": "--help",
"-v": "--version",
"-l": "--listen",
"-s": "--single",
"-d": "--debug",
"-c": "--config",
"-n": "--no-clipboard",
"-u": "--no-compression",
"-S": "--symlinks",
"-C": "--cors",
"-p": "--listen"
};
var parseArguments = () => parseArgv(options);
var getHelpText = () => helpText;
var checkForUpdates = async (manifest) => {
if (env.NO_UPDATE_CHECK)
return;
const [error2, update] = await resolve(checkForUpdate(manifest));
if (error2)
throw error2;
if (!update)
return;
logger.log(chalk3.bgRed.white(" UPDATE "), `The latest version of \`serve\` is ${update.latest}`);
};

@@ -363,3 +429,3 @@ // source/utilities/config.ts

import schema from "@zeit/schemas/deployment/config-static.js";
var loadConfiguration = async (cwd2, entry2, args2) => {
var loadConfiguration = async (presentDirectory2, directoryToServe2, args2) => {
const files = ["serve.json", "now.json", "package.json"];

@@ -370,3 +436,3 @@ if (args2["--config"])

for (const file of files) {
const location = resolvePath(entry2, file);
const location = resolvePath(directoryToServe2, file);
const [error2, rawContents] = await resolve(readFile2(location, "utf8"));

@@ -397,7 +463,9 @@ if (error2) {

Object.assign(config2, parsedJson);
if (file === "now.json" || file === "package.json")
logger.warn("The config files `now.json` and `package.json` are deprecated. Please use `serve.json`.");
break;
}
if (entry2) {
if (directoryToServe2) {
const staticDirectory = config2.public;
config2.public = resolveRelativePath(cwd2, staticDirectory ? resolvePath(entry2, staticDirectory) : entry2);
config2.public = resolveRelativePath(presentDirectory2, staticDirectory ? resolvePath(directoryToServe2, staticDirectory) : directoryToServe2);
}

@@ -420,53 +488,36 @@ if (Object.keys(config2).length !== 0) {

// source/utilities/logger.ts
import chalk2 from "chalk";
var info = (...message) => console.error(chalk2.magenta("INFO:", ...message));
var warn = (...message) => console.error(chalk2.yellow("WARNING:", ...message));
var error = (...message) => console.error(chalk2.red("ERROR:", ...message));
var log = console.log;
var logger = { info, warn, error, log };
// source/main.ts
var printUpdateNotification = async (debugMode) => {
const [error2, update] = await resolve(checkForUpdate(package_default));
if (error2) {
const suffix = debugMode ? ":" : " (use `--debug` to see full error).";
logger.warn(`Checking for updates failed${suffix}`);
if (debugMode)
logger.error(error2.message);
}
if (!update)
return;
logger.log(chalk3.bgRed.white(" UPDATE "), `The latest version of \`serve\` is ${update.latest}.`);
};
var args;
try {
args = parseArguments();
} catch (error2) {
logger.error(error2.message);
process.exit(1);
var [parseError, args] = await resolve(parseArguments());
if (parseError || !args) {
logger.error(parseError.message);
exit(1);
}
if (process.env.NO_UPDATE_CHECK !== "1")
await printUpdateNotification(args["--debug"]);
var [updateError] = await resolve(checkForUpdates(package_default));
if (updateError) {
const suffix = args["--debug"] ? ":" : " (use `--debug` to see full error)";
logger.warn(`Checking for updates failed${suffix}`);
if (args["--debug"])
logger.error(updateError.message);
}
if (args["--version"]) {
logger.log(package_default.version);
process.exit(0);
exit(0);
}
if (args["--help"]) {
logger.log(getHelpText());
process.exit(0);
exit(0);
}
if (!args["--listen"])
args["--listen"] = [
[process.env.PORT ? parseInt(process.env.PORT, 10) : 3e3]
];
args["--listen"] = [{ port: parseInt(env2.PORT ?? "3000", 10) }];
if (args._.length > 1) {
logger.error("Please provide one path argument at maximum");
process.exit(1);
exit(1);
}
if (args["--config"] === "now.json" || args["--config"] === "package.json")
logger.warn("The config files `now.json` and `package.json` are deprecated. Please use `serve.json`.");
var cwd = process.cwd();
var entry = args._[0] ? path.resolve(args._[0]) : cwd;
var config = await loadConfiguration(cwd, entry, args);
var presentDirectory = getPwd();
var directoryToServe = args._[0] ? path.resolve(args._[0]) : presentDirectory;
var [configError, config] = await resolve(loadConfiguration(presentDirectory, directoryToServe, args));
if (configError || !config) {
logger.error(configError.message);
exit(1);
}
if (args["--single"]) {

@@ -486,22 +537,22 @@ const { rewrites } = config;

const copyAddress = !args["--no-clipboard"];
if (!process.stdout.isTTY || process.env.NODE_ENV === "production") {
const suffix = local ? ` at ${local}.` : ".";
if (!stdout.isTTY || env2.NODE_ENV === "production") {
const suffix = local ? ` at ${local}` : "";
logger.info(`Accepting connections${suffix}`);
continue;
}
let message = chalk3.green("Serving!");
let message = chalk4.green("Serving!");
if (local) {
const prefix = network ? "- " : "";
const space = network ? " " : " ";
const space = network ? " " : " ";
message += `
${chalk3.bold(`${prefix}Local:`)}${space}${local}`;
${chalk4.bold(`${prefix}Local:`)}${space}${local}`;
}
if (network)
message += `
${chalk3.bold("- On Your Network:")} ${network}`;
${chalk4.bold("- Network:")} ${network}`;
if (previous)
message += chalk3.red(`
message += chalk4.red(`
This port was picked because ${chalk3.underline(previous.toString())} is in use.`);
This port was picked because ${chalk4.underline(previous.toString())} is in use.`);
if (copyAddress && local) {

@@ -512,3 +563,3 @@ try {

${chalk3.grey("Copied local address to clipboard!")}`;
${chalk4.grey("Copied local address to clipboard!")}`;
} catch (error2) {

@@ -530,4 +581,4 @@ logger.error(`Cannot copy server address to clipboard: ${error2.message}.`);

logger.warn("Force-closing all open sockets...");
process.exit(0);
exit(0);
});
});
{
"name": "serve",
"version": "14.0.1",
"version": "14.1.0",
"description": "Static file serving and directory listing",

@@ -23,14 +23,2 @@ "keywords": [

},
"scripts": {
"develop": "tsx watch ./source/main.ts",
"start": "node ./build/main.js",
"compile": "tsup ./source/main.ts",
"test:tsc": "tsc --project tsconfig.json",
"test": "pnpm test:tsc",
"lint:code": "eslint --max-warnings 0 source/**/*.ts",
"lint:style": "prettier --check --ignore-path .gitignore .",
"lint": "pnpm lint:code && pnpm lint:style",
"format": "prettier --write --ignore-path .gitignore .",
"prepare": "husky install config/husky && pnpm compile"
},
"dependencies": {

@@ -41,2 +29,3 @@ "@zeit/schemas": "2.21.0",

"boxen": "7.0.0",
"c8": "7.11.3",
"chalk": "5.0.1",

@@ -47,3 +36,3 @@ "chalk-template": "0.4.0",

"is-port-reachable": "4.0.0",
"serve-handler": "6.1.3",
"serve-handler": "6.1.5",
"update-check": "1.5.4"

@@ -56,2 +45,3 @@ },

"eslint": "8.19.0",
"got": "12.1.0",
"husky": "8.0.1",

@@ -62,3 +52,4 @@ "lint-staged": "13.0.3",

"tsx": "3.7.1",
"typescript": "4.6.4"
"typescript": "4.6.4",
"vitest": "0.18.0"
},

@@ -87,5 +78,22 @@ "tsup": {

"source/**/*.ts": [
"eslint --max-warnings 0 --fix"
"eslint --max-warnings 0 --fix",
"vitest related --run"
],
"tests": [
"vitest --run"
]
},
"scripts": {
"develop": "tsx watch ./source/main.ts",
"start": "node ./build/main.js",
"compile": "tsup ./source/main.ts",
"test:tsc": "tsc --project tsconfig.json",
"test:unit": "vitest run --config config/vitest.ts",
"test:watch": "vitest watch --config config/vitest.ts",
"test": "pnpm test:tsc && pnpm test:unit",
"lint:code": "eslint --max-warnings 0 source/**/*.ts",
"lint:style": "prettier --check --ignore-path .gitignore .",
"lint": "pnpm lint:code && pnpm lint:style",
"format": "prettier --write --ignore-path .gitignore ."
}
}
}

@@ -29,2 +29,4 @@ ![Serve Logo](https://raw.githubusercontent.com/vercel/serve/main/media/banner.png)

> `serve` v14 onwards requires Node v14 to run. Please use `serve` v13 if you cannot upgrade to Node v14.
The quickest way to get started is to just run `npx serve` in your project's directory.

@@ -64,3 +66,3 @@

The core of `serve` is [serve-handler](https://github.com/vercel/serve-handler), which can be used as middleware in existing HTTP servers:
The core of `serve` is [`serve-handler`](https://github.com/vercel/serve-handler), which can be used as middleware in existing HTTP servers:

@@ -82,5 +84,3 @@ ```js

> **Note**
>
> You can also replace `http.createServer` with [micro](https://github.com/vercel/micro).
> You can also replace `http.createServer` with [`micro`](https://github.com/vercel/micro).

@@ -95,3 +95,3 @@ ## Issues and Contributing

This project used to be called "list" and "micro-list". But thanks to [TJ Holowaychuk](https://github.com/tj) handing us the new name, it's now called "serve" (which is much more definite).
This project used to be called `list` and `micro-list`. But thanks to [TJ Holowaychuk](https://github.com/tj) handing us the new name, it's now called `serve` (which is much more definite).

@@ -98,0 +98,0 @@ ## Author

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc