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

@surfjs/next

v0.5.2

Published

Next.js adapter for Surf.js — App Router + Pages Router support

Readme

@surfjs/next

Next.js adapter for Surf.js — App Router, Pages Router, and edge-compatible.

npm License: MIT GitHub

Website · Docs · GitHub


Mount a Surf command API in your Next.js app in minutes. Supports App Router (route handlers, edge runtime), Pages Router (API routes), and the standard /.well-known/surf.json discovery path via a Next.js middleware rewrite.

Part of the Surf.js ecosystem. See @surfjs/core for the server-side library.

Installation

npm install @surfjs/next @surfjs/core
# or
pnpm add @surfjs/next @surfjs/core

Quick Start (App Router)

1. Create a catch-all route handler

// app/api/surf/[...slug]/route.ts
import { createSurf } from '@surfjs/core';
import { createSurfRouteHandler } from '@surfjs/next';

const surf = await createSurf({
  name: 'My App',
  commands: {
    hello: {
      description: 'Say hello',
      params: { name: { type: 'string', required: true } },
      run: ({ name }) => ({ message: `Hello, ${name}!` }),
    },
  },
});

export const { GET, POST } = createSurfRouteHandler(surf);

This single handler serves all Surf routes:

| Method | Path | Description | |--------|------|-------------| | GET | /api/surf/.well-known/surf.json | Surf manifest | | POST | /api/surf/surf/execute | Execute a command | | POST | /api/surf/surf/pipeline | Execute a pipeline | | POST | /api/surf/surf/session/start | Start a session | | POST | /api/surf/surf/session/end | End a session |

2. Enable standard discovery (recommended)

Add the middleware rewrite so agents can find your manifest at /.well-known/surf.json:

// middleware.ts
import { surfMiddleware } from '@surfjs/next/middleware';

export default surfMiddleware();

// Only run on the discovery path
export const config = { matcher: ['/.well-known/surf.json'] };

This rewrites GET /.well-known/surf.jsonGET /api/surf/.well-known/surf.json — no extra route needed.

3. Done

Your app is now Surf-enabled:

curl https://myapp.com/.well-known/surf.json
curl -X POST https://myapp.com/api/surf/surf/execute \
  -H "Content-Type: application/json" \
  -d '{"command":"hello","params":{"name":"Claude"}}'

Pages Router

Create a catch-all API route:

// pages/api/surf/[...slug].ts
import { createSurf } from '@surfjs/core';
import { createSurfApiHandler } from '@surfjs/next/pages';

const surf = await createSurf({
  name: 'My App',
  commands: {
    hello: {
      description: 'Say hello',
      params: { name: { type: 'string', required: true } },
      run: ({ name }) => ({ message: `Hello, ${name}!` }),
    },
  },
});

export default createSurfApiHandler(surf);

Add the same middleware.ts as above for standard discovery.


Custom Base Path

If your Surf route is not at /api/surf, pass basePath:

// App Router — mounted at /actions/surf/[...slug]/route.ts
export const { GET, POST } = createSurfRouteHandler(surf, {
  basePath: '/actions/surf',
});
// middleware.ts — rewrite to matching base path
import { surfMiddleware } from '@surfjs/next/middleware';

export default surfMiddleware({ basePath: '/actions/surf' });
export const config = { matcher: ['/.well-known/surf.json'] };

Edge Runtime

The App Router handler (createSurfRouteHandler) uses only Web Standard APIs — no Node.js. Opt into the edge runtime with:

// app/api/surf/[...slug]/route.ts
export const runtime = 'edge';

export const { GET, POST } = createSurfRouteHandler(surf);

Note: Edge runtime does not support @surfjs/core's WebSocket transport. For WebSocket support, use the Node.js runtime.


Authentication

import { createSurf, bearerVerifier } from '@surfjs/core';
import { createSurfRouteHandler } from '@surfjs/next';

const surf = await createSurf({
  name: 'My App',
  auth: { type: 'bearer', description: 'API key' },
  authVerifier: bearerVerifier(['my-secret-token']),
  commands: {
    private: {
      description: 'Protected command',
      auth: 'required',
      run: (params, ctx) => ({ userId: ctx.claims }),
    },
  },
});

export const { GET, POST } = createSurfRouteHandler(surf);

The handler automatically extracts Bearer tokens from the Authorization header and passes them to your authVerifier.


Composing with Existing Middleware

The surfMiddleware utility composes cleanly with other Next.js middleware:

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { surfMiddleware } from '@surfjs/next/middleware';

const surf = surfMiddleware();

export function middleware(request: NextRequest) {
  // Run Surf rewrite for the discovery path
  if (request.nextUrl.pathname === '/.well-known/surf.json') {
    return surf(request);
  }
  // Your existing middleware logic
  return NextResponse.next();
}

export const config = { matcher: ['/.well-known/surf.json', '/api/:path*'] };

API Reference

createSurfRouteHandler(surf, options?) — App Router

Returns { GET, POST } route handlers for a Next.js catch-all route.

| Option | Type | Default | Description | |--------|------|---------|-------------| | basePath | string | '/api/surf' | The base path where the route is mounted |

createSurfApiHandler(surf, options?) — Pages Router

Returns a single API route handler for a Next.js catch-all API route.

| Option | Type | Default | Description | |--------|------|---------|-------------| | basePath | string | '/api/surf' | The base path where the route is mounted |

surfMiddleware(options?) — Next.js Middleware

Returns a Next.js middleware function that rewrites /.well-known/surf.json to your Surf API route.

Import from @surfjs/next/middleware.

| Option | Type | Default | Description | |--------|------|---------|-------------| | basePath | string | '/api/surf' | The Surf API route base path to rewrite to |

Utilities

These are re-exported from @surfjs/next for advanced use:

| Export | Description | |--------|-------------| | extractAuth(header) | Extract Bearer token from Authorization header | | extractIp(forwarded, realIp) | Extract client IP from headers | | extractSessionId(body) | Extract sessionId from a request body | | getErrorStatus(code) | Map a SurfErrorCode to an HTTP status code |


Features

  • App Router — route handlers with edge runtime support
  • Pages Router — classic API routes with Node.js runtime
  • Standard discovery/.well-known/surf.json via middleware rewrite
  • SSE streaming — for long-running commands
  • Session management — start, execute, end sessions
  • Pipeline execution — multi-command round-trips
  • Auth extraction — Bearer token from Authorization header
  • IP extractionx-forwarded-for / x-real-ip
  • ETag caching — efficient manifest responses
  • CORS headers — cross-origin agent access
  • Error mapping — consistent SurfErrorCode → HTTP status

Full Docs

surf.codes/docs/adapters/next

License

MIT