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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@foxen/middleware

v1.4.0

Published

Middleware support for Foxen - middleware.ts and proxy.ts handling

Readme

@foxen/middleware

Handles loading and executing middleware.ts (and proxy.ts) files, providing full Next.js middleware compatibility.

Installation

bun add @foxen/middleware

Usage

Basic Usage

import { loadMiddleware, executeMiddleware } from "@foxen/middleware";
import { NextRequest } from "@foxen/core";

// Load middleware from project
const middleware = await loadMiddleware({
    projectRoot: process.cwd(),
});

// Execute for a request
const request = new NextRequest("https://example.com/api/users");
const result = await executeMiddleware(request, middleware);

if (result.response) {
    // Middleware returned a response (redirect, error, etc.)
    return result.response;
}

// Continue to route handler
// Headers from middleware are in result.headers

Checking Path Matching

import {
    loadMiddleware,
    shouldRunMiddleware,
    executeMiddleware,
} from "@foxen/middleware";

const middleware = await loadMiddleware({ projectRoot: "." });

if (middleware) {
    const pathname = new URL(request.url).pathname;

    // Check if middleware should run for this path
    if (shouldRunMiddleware(pathname, middleware.matchers)) {
        const result = await executeMiddleware(request, middleware.handler);
        // ...
    }
}

Writing Middleware

// middleware.ts
import { NextRequest, NextResponse } from "@foxen/core";

export function middleware(request: NextRequest) {
    // Check auth
    const token = request.cookies.get("token");
    if (!token) {
        return NextResponse.redirect(new URL("/login", request.url));
    }

    // Add headers
    const response = NextResponse.next();
    response.headers.set("X-Custom-Header", "value");
    return response;
}

export const config = {
    matcher: ["/api/:path*", "/dashboard/:path*"],
};

Middleware Response Types

Middleware can return different types of responses:

// Continue to handler (with optional header modifications)
return NextResponse.next();

// Redirect
return NextResponse.redirect(new URL("/new-path", request.url));

// Rewrite (transparent URL change)
return NextResponse.rewrite(new URL("/actual-path", request.url));

// Return response directly
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });

// Return nothing (implicit next())
return;

Matcher Configuration

Simple Patterns

export const config = {
    matcher: "/api/:path*", // Single pattern
};

export const config = {
    matcher: ["/api/:path*", "/admin/:path*"], // Multiple patterns
};

Complex Matchers

export const config = {
    matcher: [
        {
            source: "/api/:path*",
            has: [{ type: "header", key: "authorization" }],
        },
        {
            source: "/admin/:path*",
            missing: [{ type: "cookie", key: "admin-session" }],
        },
    ],
};

Pattern Syntax

| Pattern | Description | Example | |---------|-------------|---------| | /path | Exact match | /api/users | | /path/:param | Named parameter | /api/users/:id | | /path/:param* | Catch-all | /api/:path* | | /path/:param? | Optional parameter | /api/users/:id? | | /(group)/path | Route group (ignored) | /(auth)/login |

API Reference

Loading

| Function | Description | |----------|-------------| | loadMiddleware(options) | Load middleware from project | | normalizeMatchers(config) | Normalize matcher configuration | | middlewareFileExists(projectRoot) | Check if middleware file exists |

Matching

| Function | Description | |----------|-------------| | shouldRunMiddleware(pathname, matchers) | Check if middleware should run | | pathToRegex(pattern) | Convert pattern to regex | | compileMatchers(matchers) | Compile matchers for fast matching | | testPathMatch(pathname, pattern) | Test single path pattern |

Execution

| Function | Description | |----------|-------------| | executeMiddleware(request, handler) | Execute middleware handler | | parseMiddlewareResponse(response) | Parse middleware response type | | applyMiddlewareHeaders(response, headers) | Apply headers from middleware |

Interrupt Handling

Foxen routes can throw interrupts from @foxen/navigation (for example, redirect() or notFound()). Use the foxnInterruptHandler plugin to translate those interrupts into HTTP responses when building your own Elysia apps.

import { Elysia } from "elysia";
import { foxnInterruptHandler } from "@foxen/middleware";
import { redirect } from "@foxen/navigation";

const app = new Elysia()
    .use(foxnInterruptHandler({
        onUnauthorized: () => new Response("nope", { status: 401 }),
    }))
    .get("/api/secure", () => {
        redirect("/login");
    });

The handler accepts FoxnInterruptResponseOptions, allowing custom overrides for redirect/not-found/ unauthorized/forbidden interrupts and default headers that should be applied to every generated response.

Event

| Function | Description | |----------|-------------| | NextFetchEvent | Fetch event class | | createNextFetchEvent(request) | Create fetch event for middleware |

Types

// Middleware handler function
type MiddlewareHandler = (
    request: NextRequest,
    event?: NextFetchEvent,
) => NextResponse | Response | void | Promise<NextResponse | Response | void>;

// Middleware configuration
interface MiddlewareConfig {
    matcher?: string | string[] | MiddlewareMatcher[];
}

// Complex matcher
interface MiddlewareMatcher {
    source: string;
    has?: RouteCondition[];
    missing?: RouteCondition[];
}

// Route condition
interface RouteCondition {
    type: "header" | "cookie" | "query" | "host";
    key: string;
    value?: string;
}

// Execution result
interface MiddlewareResult {
    response?: Response;
    headers: Headers;
    rewriteUrl?: string;
    redirectUrl?: string;
    continue: boolean;
}

Examples

Authentication Middleware

// middleware.ts
import { NextRequest, NextResponse } from "@foxen/core";
import { verifyJWT } from "./lib/jwt";

export async function middleware(request: NextRequest) {
    const token = request.cookies.get("token")?.value;

    if (!token) {
        return NextResponse.redirect(new URL("/login", request.url));
    }

    try {
        const user = await verifyJWT(token);
        const response = NextResponse.next();
        response.headers.set("X-User-ID", user.id);
        return response;
    } catch {
        return NextResponse.redirect(new URL("/login", request.url));
    }
}

export const config = {
    matcher: ["/api/((?!public).*)", "/dashboard/:path*"],
};

Rate Limiting Middleware

// middleware.ts
import { NextRequest, NextResponse } from "@foxen/core";

const rateLimit = new Map<string, number[]>();

export function middleware(request: NextRequest) {
    const ip = request.ip ?? "unknown";
    const now = Date.now();
    const windowMs = 60000; // 1 minute
    const max = 100; // Max requests per window

    const requests = rateLimit.get(ip) ?? [];
    const windowStart = now - windowMs;
    const recentRequests = requests.filter((t) => t > windowStart);

    if (recentRequests.length >= max) {
        return NextResponse.json(
            { error: "Too many requests" },
            { status: 429 },
        );
    }

    recentRequests.push(now);
    rateLimit.set(ip, recentRequests);

    return NextResponse.next();
}

export const config = {
    matcher: "/api/:path*",
};

License

MIT