npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

woodland

v22.2.0

Published

Secure HTTP framework for Node.js. Express-compatible with built-in security, no performance tradeoff.

Downloads

3,038

Readme

Woodland

Secure HTTP framework for Node.js. Express-compatible with built-in security, no performance tradeoff.

npm version Node.js Version License Test Coverage

Quick Start

npm install woodland

ESM

import { createServer } from "node:http";
import { woodland } from "woodland";

const app = woodland();

app.get("/", (req, res) => res.send("Hello World!"));
app.get("/users/:id", (req, res) => res.json({ id: req.params.id }));

createServer(app.route).listen(3000, () => console.log("Server running at http://localhost:3000"));

CommonJS

const { createServer } = require("node:http");
const { woodland } = require("woodland");

const app = woodland();

app.get("/", (req, res) => res.send("Hello World!"));
app.get("/users/:id", (req, res) => res.json({ id: req.params.id }));

createServer(app.route).listen(3000, () => console.log("Server running at http://localhost:3000"));

Why Woodland?

Security-First Design:

  • Zero learning curve - Express-compatible patterns
  • Built-in security - CORS, path traversal, XSS prevention (no additional packages)
  • Secure by default - CORS deny-all, path traversal blocked, HTML escaping automatic
  • TypeScript first - Full type definitions included
  • No performance tradeoff - Security features add minimal overhead
  • Lightweight - Minimal dependencies (6 packages)
  • Dual module support - CommonJS and ESM
  • Production ready - Event emitters for custom monitoring, examples for graceful shutdown

Built-in Security Features:

  • CORS enforcement - Default deny-all, explicit allowlist required
  • Path traversal protection - Resolved paths validated against allowed directories
  • XSS prevention - Automatic HTML escaping for user output
  • IP validation - Protects against header spoofing
  • X-Content-Type-Options - Automatic nosniff header
  • Secure error handling - No sensitive data exposure

Common Patterns

REST API with Error Handling

const users = new Map();

app.get("/users", (req, res) => {
	try {
		const list = Array.from(users.values());
		res.json(list);
	} catch (error) {
		res.error(500, error.message);
	}
});

app.get("/users/:id", (req, res) => {
	try {
		const user = users.get(req.params.id);
		if (!user) {
			return res.error(404, "User not found");
		}
		res.json(user);
	} catch (error) {
		res.error(500, error.message);
	}
});

app.post("/users", async (req, res) => {
	try {
		const id = Date.now().toString();
		const user = { ...req.body, id };
		users.set(id, user);
		res.status(201).json(user);
	} catch (error) {
		res.error(400, error.message);
	}
});

Health Check Endpoint

app.get("/health", (req, res) => {
	res.json({
		status: "healthy",
		timestamp: new Date().toISOString(),
		uptime: process.uptime(),
	});
});

CORS

const app = woodland({
  origins: ["https://myapp.com", "http://localhost:3000"],
});
// Woodland handles preflight OPTIONS automatically

Static Files

const app = woodland({ autoIndex: true });
app.files("/static", "./public");

Middleware

// Global middleware
app.always((req, res, next) => {
	req.startTime = Date.now();
	next();
});

// Route-specific middleware
app.get("/protected", authenticate, handler);

// Error handler (register last with 4 params)
app.use((error, req, res, next) => {
	console.error(error);
	res.error(500, error.message);
});

Global Error Handling

Set app.error to intercept all unhandled errors before the error middleware chain:

const app = woodland();

app.error = (err, _req, res) => {
  console.error("Unhandled error:", err);
  res.status(500).send("Internal server error");
};

The handler receives 3 arguments (err, req, res) and must terminate the request itself. When set, the error middleware chain is skipped.

Configuration

const app = woodland({
	origins: process.env.ALLOWED_ORIGINS?.split(",") || [],
	autoIndex: process.env.AUTO_INDEX === "true",
	etags: true,
	cacheSize: 1000,
	cacheTTL: 10000,
	charset: "utf-8",
	logging: {
		enabled: true,
		level: process.env.LOG_LEVEL || "info",
	},
	time: true,
	silent: process.env.NODE_ENV === "production",
});

Response Helpers

res.json({ data: "value" });
res.send("Hello World");
res.redirect("/new-url");
res.header("x-custom", "value");
res.status(201);
res.error(404);

Request Properties

req.ip;         // Client IP address
req.params;     // URL parameters { id: "123" }
req.parsed;     // URL object
req.allow;      // Allowed methods
req.cors;       // CORS enabled
req.body;       // Request body
req.host;       // Hostname
req.valid;      // Request validity
req.app;        // Woodland application instance (provides access to app.error)

Event Handlers

app.on("connect", (req, res) => console.log(`Connection from ${req.ip}`));
app.on("finish", (req, res) => analytics.track({ method: req.method, status: res.statusCode }));
app.on("error", (req, res, err) => console.error(`Error ${res.statusCode}:`, err));
app.on("stream", (req, res) => console.log(`Streaming file`));

Graceful Shutdown

import { createServer } from "node:http";
import { woodland } from "woodland";

const app = woodland();
const server = createServer(app.route);

const gracefulShutdown = (signal) => {
	console.log(`Received ${signal}, shutting down gracefully...`);

	server.close(() => {
		console.log("Server closed");
		process.exit(0);
	});

	// Force shutdown after 30s
	setTimeout(() => {
		console.error("Forced shutdown");
		process.exit(1);
	}, 30000);
};

process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));

server.listen(3000);

CLI

# Serve directory (default: http://127.0.0.1:8000)
npx woodland

# Custom port
npx woodland --port=3000

# Custom IP
npx woodland --ip=0.0.0.0

Testing

npm test              # Run tests (334 tests, 100% line, 99.37% function, 95.90% branch coverage)
npm run coverage      # Generate coverage report
npm run benchmark     # Performance benchmarks
npm run lint          # Check linting

Documentation

Performance

Woodland delivers enterprise-grade security without sacrificing performance. Security features add minimal overhead.

| Framework | Security Approach | Mean Response Time | |-----------|------------------|-------------------| | Fastify | Requires plugins | 0.1491ms | | Woodland | Built-in | 0.1866ms | | Express | Requires middleware | 0.1956ms |

Security

Woodland implements multiple layers of protection:

  1. CORS Validation - Default deny-all policy
  2. Path Traversal Protection - Resolved paths validated
  3. Input Validation - IP addresses validated, URLs parsed securely
  4. Output Encoding - HTML escaping automatic
  5. Secure Error Handling - No internal paths exposed
  6. Header Injection Prevention - Type validation for headers
  7. Prototype Pollution Protection - Safe ETag generation

Production Setup:

import helmet from "helmet";
import rateLimit from "express-rate-limit";

app.always(helmet());
app.always(
	rateLimit({
		windowMs: 15 * 60 * 1000,  // 15 minutes
		max: 100,                   // Limit each IP to 100 requests
		standardHeaders: true,
		legacyHeaders: false,
	}),
);

Security Warning:

⚠️ Production Deployment: Always use a reverse proxy (nginx, Cloudflare) in production for SSL/TLS termination, DDoS protection, and additional security layers.

License

Copyright (c) 2026 Jason Mulligan

Licensed under the BSD-3-Clause license.

Contributing

Contributions are welcome!

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Support