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

bini-server

v1.0.10

Published

Production server for bini-router apps.

Readme

bini-server

npm version license node bundle size downloads

Production server for bini-router apps.
Zero-dependency, secure-by-default, production-grade server for your static sites and API routes.


✨ Features

Core Features

  • 🗂️ Static file serving — Streams dist/ with proper MIME types, ETag, and cache headers
  • 🌐 API routes — Serves /api/* from src/app/api/ (Hono apps + plain functions)
  • 🔀 SPA fallback — Unknown routes serve dist/index.html automatically
  • 🏷️ ETag support304 Not Modified responses for unchanged static files
  • Lazy route loading — API routes scanned only on first request for fast startup

Security & Performance

  • 🛡️ CORS — Enabled by default, configurable via CORS_ENABLED (supports BINI_*, VITE_*, no prefix)
  • 🔒 Body limits — Configurable request body size limit (default 10MB)
  • ⏱️ Timeouts — Configurable body read + handler timeouts (default 30s each)
  • 🚫 Path traversal protection — Guards against .. and // in URLs
  • 💾 Module cache — Caches imported handlers with mtime invalidation
  • 🔌 Port auto-increment — Starts at 3000, auto-increments if busy

Developer Experience

  • 🌿 Auto env loading.env files detected and listed at startup
  • ⌨️ Interactive shortcutsh for help, o to open browser, q to quit
  • 🖥️ Cross-platform — Works on Windows, macOS, and Linux
  • 🪄 Graceful shutdown — Handles SIGTERM + SIGINT with timeout fallback
  • 📦 Zero dependencies — Only uses Node.js built-in modules
  • 🔧 Flexible config — Supports BINI_*, VITE_*, or no prefix env vars

📋 Requirements

  • Node.js ≥ 20.19.0
  • A bini-router project with a built dist/
  • API handlers in src/app/api/ (if using API routes)

📦 Install

npm install bini-server
# or
pnpm add bini-server
# or
yarn add bini-server

🚀 Usage

1. Add to package.json

{
  "scripts": {
    "build": "vite build",
    "start": "bini-server"
  }
}

2. Build and Start

npm run build   # Build your app
npm start       # Serve in production

3. Terminal Output

  ß Bini.js  (production)
  ➜  Environments: .env, .env.local
  ➜  Local:   http://localhost:3000/
  ➜  Network: http://192.168.1.5:3000/
  ➜  press h + enter to show help

⌨️ Keyboard Shortcuts

While the server is running, type a key and press enter:

| Key | Action | |-----|--------| | h | Show available shortcuts | | o | Open your app in the default browser | | q | Quit the server |

Keyboard shortcuts are automatically disabled in non-interactive environments (like Render, CI/CD).


🌿 Environment Variables

Auto-Detected .env Files

At startup, bini-server automatically detects and loads:

  1. .env.local
  2. .env.[NODE_ENV].local (e.g., .env.production.local)
  3. .env.[NODE_ENV] (e.g., .env.production)
  4. .env

All detected files are listed in the startup banner.

Server Configuration

All environment variables support three naming conventions:

| Convention | Example | Priority | |------------|---------|----------| | BINI_* | BINI_PORT=3000 | Highest | | VITE_* | VITE_PORT=3000 | Medium | | No prefix | PORT=3000 | Lowest |

| Variable | Default | Description | |----------|---------|-------------| | PORT | 3000 | HTTP port to listen on | | CORS_ENABLED | true | Enable/disable CORS on API routes | | API_DIR | src/app/api | Path to API handlers directory | | DIST_DIR | dist | Path to static files directory | | BODY_TIMEOUT_SECS | 30 | Max seconds to read request body | | HANDLER_TIMEOUT_SECS | 30 | Max seconds for handler to respond | | BODY_SIZE_LIMIT | 10485760 | Max request body size in bytes (10MB) |

Examples

# .env file
PORT=8080
CORS_ENABLED=false
API_DIR=src/api
BODY_SIZE_LIMIT=5242880  # 5MB

# Or inline
PORT=3001 BINI_CORS_ENABLED=false bini-server

# Or with VITE prefix
VITE_PORT=3000 VITE_CORS_ENABLED=false bini-server

📁 Project Structure

my-app/
├── dist/                    # Built static files (required)
│   ├── index.html
│   ├── assets/
│   └── ...
├── src/
│   ├── app/
│   │   ├── api/            # API handlers (optional)
│   │   │   ├── users.ts
│   │   │   └── posts/
│   │   │       ├── index.ts
│   │   │       └── [id].ts
│   │   └── layout.tsx
│   └── main.tsx
├── .env                     # Environment variables
├── package.json
└── vite.config.ts

🌐 API Routes

Supported Formats

// 1. Hono App (Recommended)
import { Hono } from 'hono';
const app = new Hono();
app.get('/users', (c) => c.json({ users: [] }));
export default app;

// 2. Plain Function
export default (req: Request) => {
  return Response.json({ message: 'Hello' });
};

Supported Extensions

Only .ts and .js files are supported for API routes (Next.js convention).

Dynamic Routes

src/app/api/
  users/
    [id].ts      → /api/users/:id
  posts/
    [...slug].ts → /api/posts/*

Route Parameters

// src/app/api/users/[id].ts
export default (req: Request) => {
  const params = JSON.parse(req.headers.get('x-bini-params') || '{}');
  // params.id → '123'
  return Response.json({ id: params.id });
};

CORS

CORS is enabled by default with these headers:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS,HEAD
Access-Control-Allow-Headers: Content-Type,Authorization,X-Request-ID

Disable with CORS_ENABLED=false, BINI_CORS_ENABLED=false, or VITE_CORS_ENABLED=false.


🗂️ Static File Serving

Supported MIME Types

All common file types are served with correct MIME types:

  • HTML, CSS, JavaScript, JSON
  • Images: PNG, JPEG, GIF, SVG, WebP, AVIF, ICO
  • Fonts: WOFF, WOFF2, TTF, EOT
  • Documents: TXT, XML
  • Web manifests

Cache Headers

| File Type | Cache Policy | |-----------|--------------| | Assets (/assets/*) | public, max-age=31536000, immutable (1 year) | | All other files | no-cache |

ETag Support

Automatically generates ETags from file size + mtimeMs:

  • Sends ETag header on first request
  • Handles If-None-Match for 304 Not Modified responses
  • Uses MD5 hash (16 chars) for efficient caching

🚢 Deployment

Important: Ship Your src/ Folder

bini-server runs API handlers directly from src/app/api/ — they are not compiled into dist/. When deploying, ensure your server has access to both dist/ and src/app/api/.

  • VPS/pm2: Deploy the full project directory
  • Railway/Render/Fly.io: Automatic (clones your repository)
  • Docker: Copy both dist/ and src/ directories

VPS / Dedicated Server

npm run build
npm start

# With pm2 (recommended)
npm install -g pm2
pm2 start "npm start" --name my-app
pm2 save
pm2 startup

Platform as a Service

| Platform | Start Command | Notes | |----------|---------------|-------| | Railway | npm start | PORT injected automatically | | Render | npm start | PORT injected automatically | | Fly.io | npm start | See fly.toml example below | | Heroku | npm start | PORT injected automatically |

Docker

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]

Fly.io

# fly.toml
[processes]
  app = "npm start"

🔧 vs vite preview

| Feature | vite preview | bini-server | |---------|---------------|---------------| | Serves dist/ | ✅ | ✅ | | API routes | ✅ | ✅ | | SPA fallback | ✅ | ✅ | | Auto env loading | ✅ | ✅ | | ETag / 304 support | ❌ | ✅ | | Body timeout | ❌ | ✅ (30s) | | Body size limit | ❌ | ✅ (10MB) | | Handler timeout | ❌ | ✅ (30s) | | Graceful shutdown | ❌ | ✅ | | Module cache | ❌ | ✅ | | Configurable dirs | ❌ | ✅ | | CORS control | ❌ | ✅ | | Zero dependencies | ❌ | ✅ | | Production use | ⚠️ Not recommended | ✅ Production-ready |


🛡️ Security

| Feature | Default | Configurable | |---------|---------|--------------| | CORS | Enabled | ✅ via CORS_ENABLED | | Body size limit | 10MB | ✅ via BODY_SIZE_LIMIT | | Request timeout | 30s | ✅ via BODY_TIMEOUT_SECS | | Handler timeout | 30s | ✅ via HANDLER_TIMEOUT_SECS | | Path traversal | Blocked | ✅ (guard in place) |


🧪 Testing Your Server

# Check static files
curl http://localhost:3000/

# Check API routes
curl http://localhost:3000/api/hello

# Check ETag
curl -I http://localhost:3000/styles.css

# Test 304 Not Modified
curl -I http://localhost:3000/styles.css \
  -H "If-None-Match: [etag_from_previous_request]"

# Test CORS
curl -X OPTIONS http://localhost:3000/api/hello \
  -H "Origin: http://example.com"

⚙️ Configuration Examples

Development (All security disabled)

CORS_ENABLED=true
BODY_TIMEOUT_SECS=0
HANDLER_TIMEOUT_SECS=0
BODY_SIZE_LIMIT=0
NODE_ENV=development

Production (Secure defaults)

CORS_ENABLED=true
BODY_TIMEOUT_SECS=30
HANDLER_TIMEOUT_SECS=30
BODY_SIZE_LIMIT=10485760
NODE_ENV=production

Internal API (No CORS)

CORS_ENABLED=false
BODY_SIZE_LIMIT=5242880  # 5MB

File Upload Service

CORS_ENABLED=true
BODY_SIZE_LIMIT=1073741824  # 1GB
BODY_TIMEOUT_SECS=300  # 5 minutes

📚 API Reference

Environment Variables Priority

  1. BINI_* (highest)
  2. VITE_* (medium)
  3. No prefix (lowest)

Returned HTTP Status Codes

| Code | Description | |------|-------------| | 200 | Success | | 204 | OPTIONS preflight success | | 304 | Not Modified (ETag match) | | 400 | Bad Request URL | | 404 | Route not found | | 408 | Request timeout | | 413 | Payload too large | | 500 | Internal server error |

Supported HTTP Methods

  • GET, POST, PUT, PATCH, DELETE
  • OPTIONS (CORS preflight)
  • HEAD (with ETag support)

🤝 Contributing

  1. Fork the repository
  2. Create your feature branch
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

📝 License

MIT © Binidu Ranasinghe


🔗 Links


Built with ❤️ by Binidu Ranasinghe