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

@flow-state-dev/vercel

v0.1.3

Published

Vercel deployment adapter for flow-state-dev.

Readme

@flow-state-dev/vercel

Vercel deployment adapter for flow-state-dev. Wraps a flow-state-dev router into Next.js App Router handlers with Vercel-specific SSE shaping and runtime configuration.

Specific to Vercel + Next.js. For a long-lived Node server (Railway, Render, Fly, Docker, a VPS), use @flow-state-dev/node's serve(). For other serverless platforms (AWS Lambda, Bun, Deno), use the portable app from @flow-state-dev/node. See Host adapters.

SSE heartbeats are provided by @flow-state-dev/engine for every live and GET-attach stream. Configure them via createFlowApiRouter({ defaultSseHeartbeatMs }) or per-flow defineFlow({ request: { sseHeartbeatMs } }).

Quick Start

pnpm add @flow-state-dev/vercel

Two files to deploy an FSD app to Vercel.

1. FlowState (lib/flowstate.ts) — the runtime config:

import { createFlowState, inMemoryStores } from "@flow-state-dev/engine";
import { vercelPostgresStores } from "@flow-state-dev/vercel/store";
import myFlow from "@/flows/my-flow/flow";

export const flowstate = createFlowState({
  flows: { myFlow },
  models: { default: "openai/gpt-5.4-mini" },
  stores: {
    prod: { primary: vercelPostgresStores() },
    dev: { primary: inMemoryStores() },
  },
  defaultProfile: "dev",
});

2. Catch-all route (app/api/flows/[...path]/route.ts):

import { flowstate } from "@/lib/flowstate";
import { createVercelNextHandler } from "@flow-state-dev/vercel/next";

export const { GET, POST, PATCH, DELETE } = createVercelNextHandler(flowstate);

// Next.js reads these statically — must be literal declarations, not re-exports.
export const runtime = "nodejs";
export const maxDuration = 300;
export const dynamic = "force-dynamic";

SSE streams get the right headers, heartbeats prevent proxy timeouts, and maxDuration is set to 300 seconds. createVercelNextHandler resolves the router lazily on the first request, so async store init works with no top-level await.

For background work that must survive the function freezing after the response (scheduled dispatches, post-202 execution), wire Vercel's after at construction with createFlowState({ onBackgroundWork: (p) => after(() => p) }) from next/server. It's a createFlowState option, not a handler option, because the router is built inside createFlowState.

@flow-state-dev/vercel/store

vercelPostgresStores() returns a StoreAdapter for the primary capability slot, Postgres tuned for Vercel and Neon. It bakes in the Vercel pool defaults (vercelPgPoolOptions), swaps in Neon's WebSocket Client for .neon.tech URLs, skips schema init (migrations run out-of-band at build), and uses the polling tail fallback. No process.env.VERCEL checks or URL sniffing in your code — declare it as a profile slot.

import { vercelPostgresStores } from "@flow-state-dev/vercel/store";

createFlowState({
  flows: { myFlow },
  stores: { prod: { primary: vercelPostgresStores() } },
});

The connection string defaults to FSD_DB_URL then DATABASE_URL. Pass { connectionString } to override.

@flow-state-dev/vercel/next

createVercelNextHandler(flowstate) mounts a FlowState onto a catch-all route with Vercel's SSE header shaping. Pass the FlowState handle, not flowstate.getRouter() — the handler resolves the router lazily itself.

import { createVercelNextHandler } from "@flow-state-dev/vercel/next";

export const { GET, POST, PATCH, DELETE } = createVercelNextHandler(flowstate);

Requires Next.js 15+. For non-Vercel Next deployments, use createNextHandler from @flow-state-dev/next instead.

What it does

  • Handles Next.js 15 async params — unwraps Promise<{ path }> so you don't have to.
  • SSE response shaping — adds Cache-Control: no-cache, no-transform, X-Accel-Buffering: no to prevent Vercel's edge layer from buffering streamed tokens.
  • Heartbeat keep-alive — injects periodic : ping SSE comments (default every 15s) to defeat intermediate proxy idle timeouts.
  • AbortSignal wiring — request cancellation propagates into flow execution.

Lazy router initialization

If your store setup is async (e.g. Postgres connection pool), pass a factory function instead of a pre-built router:

// [... path]/route.ts
import { createVercelHandler } from "@flow-state-dev/vercel";
import { getRouter } from "@/lib/server";

// getRouter returns Promise<FlowApiRouter> — called once, cached internally.
export const { GET, POST, PATCH, DELETE } = createVercelHandler(getRouter);
// ...runtime config...
// route.ts (bare)
import { createVercelBareHandler } from "@flow-state-dev/vercel";
import { getRouter } from "@/lib/server";

export const { GET, POST } = createVercelBareHandler(getRouter);

Configuration

createVercelHandler(router, {
  onAbort: (req) => { ... },     // Client disconnect callback
  waitUntil: (p) => { ... },     // Keep function alive for background work
});

Route config values

Next.js reads runtime, maxDuration, and dynamic via static analysis. They must be literal export const declarations in your route file — re-exports from another module won't work.

| Field | Recommended value | Purpose | |--------|---------|---------| | runtime | "nodejs" | Vercel runtime (use "edge" only with edge-safe stores) | | maxDuration | 300 | Max function execution time in seconds | | dynamic | "force-dynamic" | Prevents Next.js from caching SSE routes |

API

createVercelHandler(app, options?)

Creates Next.js App Router GET, POST, PATCH, DELETE handlers for [...path] catch-all routes.

app: Either a FlowApiRouter (from createFlowApiRouter) or a () => FlowApiRouter | Promise<FlowApiRouter> factory. The factory is called at most once and cached.

Returns: { GET, POST, PATCH, DELETE } — export these directly from your route file.

createVercelBareHandler(app, options?)

Creates handlers for the bare /api/flows route (no path segments). Same app input as above.

Returns: { GET, POST } — export from the sibling route.ts.

Postgres on Vercel

Vercel keeps Node function instances warm across requests. Auto-suspending databases (Neon, Supabase direct-connect, RDS with auto-pause) drop TCP sockets after ~5 minutes idle. A default pg.Pool caches those dead sockets and emits "Connection terminated unexpectedly" on the next cold request.

@flow-state-dev/vercel/pg exports vercelPgPoolOptions, a pg.PoolConfig that closes that race (short idle timeout, longer connection timeout, max: 1, allowExitOnIdle). Feed it through @flow-state-dev/store-postgres' poolOptions passthrough, gated on process.env.VERCEL so local dev is unaffected:

import { createPostgresStores } from "@flow-state-dev/store-postgres";
import { vercelPgPoolOptions } from "@flow-state-dev/vercel/pg";

export const stores = await createPostgresStores({
  connectionString: process.env.DATABASE_URL,
  poolOptions: process.env.VERCEL ? vercelPgPoolOptions : undefined
});

The subpath is zero-runtime — it uses import type for pg, so importing it doesn't add pg to your bundle if you aren't using the Postgres adapter.

For first-request cold-start latency (typical 1–3s after wake-up), swap in Neon's WebSocket Client using pg.PoolConfig.Client. See the @flow-state-dev/store-postgres README for the recipe.

Scheduled actions

@flow-state-dev/vercel/schedules ships two helpers for wiring Vercel Cron to the scheduled-actions transport.

createGetToPostCronShim turns the GET hit Vercel Cron sends into the POST the framework dispatch endpoint expects:

import { createGetToPostCronShim } from "@flow-state-dev/vercel/schedules";

export const GET = createGetToPostCronShim({
  flowKind: "billing",
  scheduleId: "monthly-invoices"
});

createScheduleTickHandler runs once per cron beat, claims due rows from a ScheduleIndex, and dispatches each with bounded concurrency:

import { createScheduleTickHandler } from "@flow-state-dev/vercel/schedules";
import { scheduleIndex } from "@/lib/schedule-index";

export const GET = createScheduleTickHandler({
  flowKind: "reminders",
  index: scheduleIndex
});

Both helpers authenticate inbound requests via constant-time bearer compare against CRON_SECRET and forward the same bearer to the dispatch endpoint. Runtime deps stay zero — only @flow-state-dev/scheduled type imports cross the boundary.

See Scheduled actions on Vercel Cron for the full setup.

Scripts

pnpm build       # Build the package
pnpm typecheck   # Type-check
pnpm test        # Run tests