Skip to content

tknf/matchbox

Repository files navigation

Matchbox

A modern CGI-style web framework built on Hono. Brings Apache-style conventions (.htaccess, .htpasswd) into the modern TypeScript/JSX ecosystem with file-based routing and familiar CGI variables.

Features

  • File-based routing - .cgi.tsx/.cgi.jsx files map directly to URL endpoints
  • CGI-style context - Familiar $_GET, $_POST, $_SESSION, $_SERVER, $_COOKIE, etc.
  • Apache compatibility - .htaccess for rewrites/redirects/headers, .htpasswd for Basic Auth
  • Modern tooling - Full TypeScript support, Vite integration, JSX rendering
  • Flexible configuration - Session management, custom middleware, security headers
  • Production ready - Comprehensive test coverage, security best practices

Installation

npm add @tknf/matchbox hono
# or
pnpm add @tknf/matchbox hono
# or
yarn add @tknf/matchbox hono

Quick Start

1. Configure Vite

Create vite.config.ts:

import devServer from "@hono/vite-dev-server";
import { defineConfig } from "vite";
import { MatchboxPlugin } from "@tknf/matchbox/plugin";

export default defineConfig({
  plugins: [
    MatchboxPlugin(),
    devServer({
      entry: "server.ts",
      exclude: [/^\/public\/.+/, /^\/favicon\.ico$/],
    }),
  ],
});

2. Create Server Entry

Create server.ts:

import { createCgi } from "@tknf/matchbox";

export default createCgi();

3. Create Your First Page

Create public/index.cgi.tsx:

import type { CgiContext } from "@tknf/matchbox";

export default function ({ $_SERVER, $_GET }: CgiContext) {
  return (
    <html>
      <head>
        <title>Matchbox</title>
      </head>
      <body>
        <h1>Hello from Matchbox!</h1>
        <p>Request Method: {$_SERVER.REQUEST_METHOD}</p>
        <p>Query Params: {JSON.stringify($_GET)}</p>
      </body>
    </html>
  );
}

4. Start Development Server

npm run dev

Visit http://localhost:5173 to see your page.

CGI Context

Every page function receives a CgiContext object with:

Request Data

  • $_GET - Query parameters
  • $_POST - Form data (POST/PUT)
  • $_FILES - Uploaded files
  • $_REQUEST - Combined $_GET + $_POST + $_COOKIE
  • $_COOKIE - Cookie values
  • $_SERVER - Server and request information
  • $_ENV - Environment variables
  • $_SESSION - Session data

Helper Functions

  • header(name, value) - Set response header
  • status(code) - Set HTTP status code
  • redirect(url, status?) - Redirect to another URL
  • cgiinfo() - HTML debug info block
  • get_modules() - List all available CGI modules
  • get_version() - Get Matchbox version string
  • log(message) - Custom logging
  • request_headers() - Get all request headers
  • response_headers() - Get current response headers

Example Usage

export default function (ctx: CgiContext) {
  const { $_GET, $_POST, $_SESSION, header, status, redirect } = ctx;

  // Handle form submission
  if ($_POST.username) {
    $_SESSION.user = $_POST.username;
    return redirect("/dashboard");
  }

  // Set custom headers
  header("X-Custom-Header", "value");
  status(200);

  return <div>Welcome</div>;
}

Configuration

Plugin Options

MatchboxPlugin({
  publicDir: "public",        // Directory for .cgi files (default: "public")
  config: {                   // Custom config object injected into pages
    siteName: "My Site",
    apiUrl: "https://api.example.com"
  }
});

Access config in your pages via context.config.

Runtime Options

createCgi({
  // Session cookie configuration
  sessionCookie: {
    name: "_SESSION_ID",      // Cookie name (default: "_SESSION_ID")
    path: "/",                // Cookie path (default: "/")
    domain: "example.com",    // Cookie domain
    secure: true,             // HTTPS only (default: false)
    sameSite: "Strict",       // CSRF protection: "Strict" | "Lax" | "None"
    maxAge: 3600,             // Session timeout in seconds
  },

  // URL trailing slash enforcement
  enforceTrailingSlash: true,

  // Custom middleware (runs before page handlers)
  middleware: [
    async (c, next) => {
      c.header("X-App", "matchbox");
      await next();
    },
  ],

  // Custom logger
  logger: (message, level) => {
    console.log(`[${level ?? "info"}] ${message}`);
  },
});

Apache-Style Features

Basic Authentication (.htpasswd)

Place a .htpasswd file in any directory under public/ to protect it:

# Generate with: htpasswd -c .htpasswd username
admin:$apr1$abc123$...
user:$apr1$xyz789$...

All files in that directory and subdirectories will require authentication.

URL Rewriting and Redirects (.htaccess)

Create .htaccess files to configure rewrites, redirects, headers, and error pages:

# Permanent redirect
Redirect 301 /old-page /new-page

# Conditional rewrite
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteRule ^(.*)$ https://example.com/$1 [R=301,L]

# Pattern-based rewrite
RewriteRule ^blog/(.+)$ /posts.cgi?slug=$1 [QSA,L]

# Forbidden
RewriteRule ^private$ - [F]

# Security headers
Header set X-Frame-Options "SAMEORIGIN"
Header set X-Content-Type-Options "nosniff"
Header set Strict-Transport-Security "max-age=31536000"

# Custom error pages
ErrorDocument 404 /errors/404.html
ErrorDocument 500 /errors/500.html

Supported RewriteRule Flags

  • [L] - Last rule, stop processing
  • [R] / [R=301] / [R=302] - Redirect with status code
  • [F] - Forbidden (403)
  • [G] - Gone (410)
  • [NC] - No Case (case-insensitive)
  • [QSA] - Query String Append
  • [QSD] - Query String Discard
  • [NE] - No Escape

Supported RewriteCond Variables

  • %{HTTP_HOST} - Request host
  • %{HTTP_USER_AGENT} - User agent
  • %{HTTP_REFERER} - Referer header
  • %{REQUEST_URI} - Request URI
  • %{REQUEST_METHOD} - HTTP method
  • %{QUERY_STRING} - Query string
  • %{REMOTE_ADDR} - Client IP
  • And more... (see documentation)

Examples

Check out the examples/ directory for complete working examples:

  • basic - Minimal setup with a simple page
  • htaccess-auth - Authentication and URL rewriting
  • custom-middleware - Custom middleware and logging

Documentation

Migration from v0.2.x

Version 0.3.0 introduced enhanced .htaccess parsing. If you're upgrading:

  • ✅ Existing .htaccess files work without changes
  • ✅ Both [R=301] and R=301 flag syntaxes are supported
  • ⚠️ Malformed directives now throw errors (previously silently ignored)

See the migration guide for details.

Development

# Install dependencies
pnpm install

# Run tests
pnpm test

# Build
pnpm run build

# Type check
pnpm run typecheck

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please read the contributing guidelines before submitting PRs.

Links