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/node

v0.1.3

Published

Node HTTP host adapter for flow-state-dev — serve a long-lived FSD server in one call.

Readme

@flow-state-dev/node

A host adapter for running a flow-state-dev app as a long-lived Node process. A host adapter is the small piece of glue between your createFlowState config and a particular runtime — @flow-state-dev/vercel is the serverless one; this is the plain-Node one. serve(flowState) stands up an HTTP server in a single call.

It does the parts you'd otherwise hand-write: translating Node's http requests to the Web Request/Response the flow router speaks, streaming SSE through unbuffered, answering health checks, and shutting down cleanly on SIGTERM.

Use it to self-host on Railway, Render, Fly, a VPS, or anywhere a Node process runs. It is also the foundation for Node-runtime serverless: the same app serve() runs long-lived is exported as a portable Hono app you can wrap for AWS Lambda, Bun, or Deno (see Portable app for serverless). For Vercel + Next.js, reach for @flow-state-dev/vercel instead.

Under the hood serve() runs on @hono/node-server rather than a hand-rolled node:http bridge; the public API is unchanged.

Installation

pnpm add @flow-state-dev/node

serve(flowState)

import { createFlowState } from "@flow-state-dev/engine";
import { serve } from "@flow-state-dev/node";
import { flows } from "./flows.js";

const flowState = createFlowState({
  flows,
  stores: { default: { primary: /* your store adapter */ } },
});

// Binds process.env.PORT (then 3000) on 0.0.0.0, mounts the API under
// /api/flows, and serves /healthz. Resolves once the server is listening.
await serve(flowState);

serve resolves as soon as the port is bound — not when stores finish opening — so a platform health check passes during a cold start. /healthz returns 503 until the router's stores are ready, then 200.

SIGTERM/SIGINT trigger a graceful shutdown: the server stops accepting connections, drains in-flight requests (force-closing after the grace window), then disposes the router and the FlowState.

Serving a built FlowApiRouter directly

If you already hold a router (for example from createFlowApiRouter, or to manage store lifecycle yourself), pass it instead of a FlowState. It is treated as ready immediately, and close() disposes the router but leaves your stores alone.

import { createFlowApiRouter } from "@flow-state-dev/engine";
import { serve } from "@flow-state-dev/node";

const router = createFlowApiRouter({ registry, stores, runtimeConfig });
const handle = await serve(router, { port: 8080 });
// ... later
await handle.close();

Portable app for serverless

createServerApp(flowState) returns the portable Hono app that serve() wraps — the /healthz endpoint plus the flow router, as a Web Fetch handler. The same app runs long-lived here or on a serverless target, so you don't re-plumb the request bridge per platform.

import { createServerApp } from "@flow-state-dev/node/app";

const { app } = createServerApp(flowState);
// app.fetch is a Web Fetch handler: (Request) => Promise<Response>
//   Bun  → export default app
//   Deno → Deno.serve(app.fetch)

For AWS Lambda, the ./aws-lambda subpath wires the app to Lambda's response streaming (so SSE works), deployed behind a Function URL in RESPONSE_STREAM mode:

import { createLambdaHandler } from "@flow-state-dev/node/aws-lambda";
export const handler = createLambdaHandler(flowState);

See Host adapters for guidance on choosing between these, and Deploying to AWS Lambda for a worked example.

Network-bind authentication guard

assertNetworkBindIsAuthenticated(app, { host, allowUnauthenticated? }) refuses a network bind when any served flow runs on the framework default (unauthenticated) principal resolver — exposing it on a network interface would leave it open to anyone who can reach the port. It throws for a non-loopback host unless allowUnauthenticated is true. Loopback hosts pass unconditionally. isLoopbackHost(host) is the loopback predicate it uses.

The guard pairs with the engine's route-level authorization: a flow that satisfies it has its whole /api/flows surface protected — session, state, resource, and request-control routes included — not just its action routes.

import { serve, assertNetworkBindIsAuthenticated } from "@flow-state-dev/node";
import flowstate from "./fsdev.config";

const host = process.env.HOST ?? "0.0.0.0";

// Await the guard before serving — it rejects for a network host with an
// unauthenticated flow, so serving must not proceed until it resolves.
await assertNetworkBindIsAuthenticated(flowstate, { host });
await serve(flowstate, { host });

fsdev serve runs this before it binds. Call it yourself from a hand-written entrypoint that adds custom middleware, so a network deploy fails closed the same way. It resolves the runtime (getRuntime()) to inspect each flow's authentication, so on a non-loopback host store initialization happens before the port binds — a deliberate fail-fast if the app is misconfigured at boot.

Health checks and PaaS deployment

Point your platform's health check at healthPath (default /healthz). Pair this package with a long-lived host that attaches Postgres (and Redis, if you run durable background work). See the deployment guides for the full topology:

API reference

ServeOptions

| Option | Default | Description | | --- | --- | --- | | port | process.env.PORT then 3000 | Port to bind. | | host | "0.0.0.0" | Host to bind. Keep 0.0.0.0 on a PaaS. | | basePath | "/api/flows" | API mount prefix (matches createFlowApiRouter). | | healthPath | "/healthz" | Health endpoint. 200 once ready, 503 before. | | staticDir | — | Directory served for non-API routes, with index.html SPA fallback. | | devtoolConfig | — | DevTool connection config ({ userId?, bearerToken? }) injected into the served index.html as window.__FSD_DEVTOOL_CONFIG__. Dev-only, loopback-only: it can carry a bearer token, so serve() ignores it (with a warning) on a non-loopback host, and the injected document is served Cache-Control: no-store. Set by fsdev dev; omit for production. | | shutdownGraceMs | 10000 | Grace window before lingering connections are force-closed. |

ServeHandle

| Member | Description | | --- | --- | | server | The underlying node:http server. | | port | The bound port (useful when binding port 0). | | close() | Stop accepting connections, drain, then dispose the router and FlowState. Idempotent. |