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

@nextrush/runtime

v4.0.1

Published

Runtime detection and cross-runtime abstractions for NextRush

Downloads

974

Readme

@nextrush/runtime

The shared cross-runtime layer NextRush adapters are built on — runtime detection, capability negotiation, and the Fetch-API request/response primitives every adapter would otherwise re-implement.

npm version downloads bundle size types ESM only license

| | | | --- | --- | | Purpose | Detect the JavaScript runtime, negotiate its capabilities, and provide the cross-runtime primitives (body reading, response building, client-IP resolution, startup errors) every NextRush adapter shares | | Package type | Core — cross-runtime foundation | | Status | Stable ✅ | | Included in nextrush? | Indirectly — the adapter that powers nextrush depends on it, so its code ships transitively. Its detection/capability API is not re-exported from the meta barrel; install @nextrush/runtime directly to call detectRuntime() and friends. | | Support tier | Public — core (stable, semver-guarded) — see ADR-0005 | | Maintenance | Active | | Runtime | Universal — Node · Bun · Deno · Edge | | Requires | Node >=22 · ESM-only · TypeScript >=5.x | | Introduced | v3.0.0 |

Highlights

  • Near-zero dependencies — depends only on @nextrush/types (contracts) and @nextrush/errors (the ServerStartError base); no third-party runtime deps
  • One implementation, every runtime — Node, Bun, Deno, and Edge share one body reader, one response builder, and one client-IP policy instead of four that drift
  • Capability negotiation, not runtime branching — features are decided by a probed capability matrix, never by if (runtime === 'x')
  • ESM-only, tree-shakable, side-effect-free ("sideEffects": false)
  • Fully typed — strict TypeScript, zero any; hardened against prototype pollution, header injection, and parameter flooding

The problem · When to use · Installation · Quick start · Capabilities · Mental model · Common tasks · API overview · Options · Compatibility · Troubleshooting · FAQ · Package relationships · Architecture · Resources


The problem

NextRush runs the same application on Node, Bun, Deno, and every edge runtime, behind a thin per-platform adapter. Each adapter needs the same handful of things: read a request body within a size limit, build a Fetch Response with header-safety checks, resolve the client IP from proxy headers, and turn a bind failure into a useful error. Left to each adapter, those routines drift — and they did.

// TODAY, without a shared runtime layer — each adapter forks the same logic,
// and the four copies disagree the moment one is patched:

// in the Node adapter
function clientIp(req) {
  return req.headers['x-forwarded-for']?.split(',')[0] ?? req.socket.remoteAddress;
} // no format validation — a spoofed header flows straight through

// in the Bun adapter
function clientIp(req) {
  return req.headers.get('x-real-ip') ?? '';
} // different header, different precedence, different result for the same request

// startup failure: Node rejects the raw EADDRINUSE, Bun rewrites the message,
// Deno does neither — three shapes for one failure.

@nextrush/runtime fixes this by owning those primitives in one place, below the adapters. There is exactly one client-IP precedence, one body-size guard, one response builder, and one ServerStartError — so Node, Bun, Deno, and Edge resolve the same request identically. On top of that it answers the question the primitives depend on: what can this runtime actually do? — via a capability matrix that is probed, not hardcoded per runtime name.

When to use

@nextrush/runtime is infrastructure. If you are building an application you almost never import it directly — you talk to createApp() and an adapter, and this layer works underneath. You reach for it when you are extending the framework across runtimes.

Use @nextrush/runtime if:

  • ✓ You're authoring an adapter and need the shared body reader, response builder, client-IP policy, or startup-error normalizer so your adapter matches the others
  • ✓ You need to detect the current runtime (detectRuntime(), isNode(), isEdge()) or its capabilities (getRuntimeCapabilities()) to gate a feature
  • ✓ You want the named capability profiles (NodeProfile, CloudflareProfile, …) for documentation, defaults, or a diagnostics view
  • ✓ You need the shared timeout / shutdown / keep-alive constants so your adapter's defaults can't drift from the others

Reach for something else if:

  • ✗ You want to start a serverlisten() / serve() live in the platform adapters, not here → use @nextrush/adapter-node (or bun / deno / edge)
  • ✗ You want the request/response contracts (Context, Runtime, BodySource types) → they're declared in @nextrush/types; this package implements against them
  • ✗ You're building an application — you get cross-runtime behavior for free through nextrush + an adapter

Installation

pnpm add @nextrush/runtime
# npm i @nextrush/runtime · yarn add @nextrush/runtime · bun add @nextrush/runtime

[!NOTE] Already using nextrush? This package's code ships transitively (your adapter depends on it), but its detection and capability API is not re-exported from the nextrush barrel. Install @nextrush/runtime directly only when you need to call detectRuntime(), read capabilities, or build an adapter.

Quick start

import {
  detectRuntime,
  getRuntimeCapabilities,
} from '@nextrush/runtime';

const runtime = detectRuntime();        // 'node' | 'bun' | 'deno' | 'edge' | ...
const caps = getRuntimeCapabilities();  // capability matrix for THIS runtime

// Decide a feature by capability, never by runtime name:
if (caps.webStreams) {
  // safe to stream the response with a ReadableStream
} else {
  // buffer and send instead
}

// Check for transport capabilities:
if (caps.secureServing) {
  // the adapter can serve over TLS — offer HTTPS configuration
}
if (caps.http2) {
  // the adapter negotiates HTTP/2 via ALPN when TLS is present
}

Detection is memoized after the first call, and the feature decision keys off caps.webStreams — not runtime === 'node' — so a capable-but-unrecognized runtime still gets the streaming path.

Capabilities

Runtime detection

  • detectRuntime() / getRuntime() — identify Node, Bun, Deno, Deno Deploy, Cloudflare Workers, Vercel Edge, generic edge, or 'unknown'; getRuntime() memoizes the result
  • detectEdgeRuntime() — granular edge platform flags (isCloudflare / isVercel / isNetlify / isGenericEdge)
  • PredicatesisNode() · isBun() · isDeno() · isEdge() · isRuntime(r)
  • getRuntimeVersion() / getRuntimeInfo() — version string and the combined { runtime, version, capabilities } view

Capability negotiation

  • capabilitiesFor(runtime) — the pure capability matrix for a runtime; unknown/future runtimes are answered by feature-probing, never an all-false blank
  • getRuntimeCapabilities() — the matrix for the current runtime
  • Named profilesNodeProfile · BunProfile · DenoProfile · DenoDeployProfile · CloudflareProfile · VercelEdgeProfile · EdgeProfile · LambdaProfile, plus capabilityProfileFor(runtime)

Cross-runtime request/response primitives

  • Body readingWebBodySource / EmptyBodySource / AbstractBodySource and createWebBodySource() with a DEFAULT_BODY_LIMIT; throws BodyTooLargeError / BodyConsumedError
  • Response buildingWebResponseBuilder for Fetch-API adapters, plus assertHeaderSafe(), isBodylessResponse(), and jsonErrorResponse() for a uniform framework-error Content-Type (F-05)
  • Shared Context shellWebContextBase (F-08): the Bun/Deno/Edge contexts extend this one class for their response methods, lazy raw/signal, streaming, and get/next/throw/assert, instead of triplicating it
  • Client IP — one policy: resolveClientIp() / getClientIp() / getEdgeClientIp(), with structural isValidClientIp()
  • Headers & queryheadersToRecord() (prototype-pollution-safe, keeps multi-value set-cookie) and parseQueryString() (single-pass, DoS-limited)
  • CancellationcombineAbortSignal() merges client-disconnect and timeout into one ctx.signal

Server startup errors

  • ServerStartError / normalizeStartupError() — one typed error (EADDRINUSE / EACCES / EADDRNOTAVAIL / UNKNOWN) so every adapter surfaces bind failures identically

Shared constants

  • DEFAULT_TIMEOUT_MS · DEFAULT_SHUTDOWN_TIMEOUT_MS · DEFAULT_KEEP_ALIVE_TIMEOUT_MS · METHODS_WITHOUT_BODY — the single source of truth for adapter defaults

Mental model

@nextrush/runtime answers two questions and provides one toolbox. First, which runtime is this and what can it do? Second, how do I read a body / build a response / resolve an IP the same way everywhere? Adapters ask both.

                         detectRuntime()  ------->  'node'|'bun'|'deno'|'edge'|...
  adapter startup ---->  capabilitiesFor() ------>  { webStreams, fetch, cryptoSubtle, secureServing, http2, ... }
  (once, cached)         NodeProfile / ...  ----->  named capability data (defaults & docs)

                         parseQueryString(qs) ---->  safe QueryParams (no proto pollution)
  per request     ---->  WebBodySource / Builder ->  read body (size-limited) / build Response
  (web adapters)         resolveClientIp(headers)->  one IP precedence, all adapters
                         combineAbortSignal(sig) ->  timeout <-> client-disconnect

Rule: decide features by capability (caps.webStreams), never by runtime identity (runtime === 'node'). Profiles are data to read, not a license to branch behavior on a name.

[!TIP] The startup sequence (how an adapter uses detection, then the per-request primitives) and the internal module map — with Mermaid diagrams — are in ARCHITECTURE.md.


Common tasks

Detect the runtime and gate a feature by capability

import { getRuntime, getRuntimeCapabilities } from '@nextrush/runtime';

const runtime = getRuntime(); // memoized

const { fileSystem, webSocket } = getRuntimeCapabilities();
if (fileSystem) {
  // safe to read/write files on this runtime
}
if (webSocket) {
  // WebSocket upgrade supported
}

Read a request body with a size limit

import { createWebBodySource, BodyTooLargeError } from '@nextrush/runtime';

const source = createWebBodySource(request.body, { limit: 1_000_000 }); // 1 MB
try {
  const text = await source.text();
} catch (err) {
  if (err instanceof BodyTooLargeError) {
    // respond 413 Payload Too Large
  }
}

Resolve the client IP with one shared policy

import { resolveClientIp } from '@nextrush/runtime';

const ip = resolveClientIp((name) => request.headers.get(name) ?? undefined, {
  trustProxy: true,       // only trust proxy headers when you sit behind one
  directIp: socketRemote, // the socket address, used when trustProxy is false
});
// precedence when trusted: cf-connecting-ip -> x-forwarded-for[0] -> x-real-ip,
// each format-validated; a malformed header is skipped, not trusted.

Normalize a server startup failure

import { normalizeStartupError } from '@nextrush/runtime';

try {
  await bindServer({ port: 8080, host: '0.0.0.0' });
} catch (raw) {
  // Same typed error + actionable message on Node, Bun, and Deno:
  throw normalizeStartupError(raw, { port: 8080, host: '0.0.0.0' });
  // -> ServerStartError { code: 'EADDRINUSE', status: 500, expose: false }
}

API overview

The sealed public surface (ADR-0005), guarded by __tests__/public-surface.test.ts. Key value exports:

| Export | Signature | Since | Stability | Description | | ------ | --------- | ----- | --------- | ----------- | | detectRuntime | () => Runtime | 3.0.0 | Stable ✅ | Identify the current runtime (uncached). | | getRuntime | () => Runtime | 3.0.0 | Stable ✅ | Memoized detectRuntime() for the hot path. | | getRuntimeCapabilities | () => RuntimeCapabilities | 3.0.0 | Stable ✅ | Capability matrix for the current runtime. | | capabilitiesFor | (r: Runtime) => RuntimeCapabilities | 3.0.0 | Stable ✅ | Pure capability matrix; probes for unknown runtimes. | | getRuntimeInfo | () => RuntimeInfo | 3.0.0 | Stable ✅ | { runtime, version, capabilities }. | | detectEdgeRuntime | () => EdgeRuntimeInfo | 3.0.0 | Stable ✅ | Granular edge-platform flags. | | isNode / isBun / isDeno / isEdge | () => boolean | 3.0.0 | Stable ✅ | Runtime predicates (isEdge covers CF/Vercel/generic). | | isRuntime | (r: Runtime) => boolean | 3.0.0 | Stable ✅ | Predicate for a specific runtime. | | parseQueryString | (qs: string) => QueryParams | 3.0.0 | Stable ✅ | Single-pass, DoS-limited, prototype-pollution-safe parser. | | createWebBodySource | (body, opts?) => WebBodySource | 3.1.0 | Stable ✅ | Size-limited Fetch-API body reader. | | WebResponseBuilder | class | 3.1.0 | Stable ✅ | Shared Fetch-Response builder for web adapters. | | jsonErrorResponse | (status, message) => Response | 3.1.0 | Stable ✅ | Uniform-charset framework error response (F-05). | | WebContextBase | class | 3.1.0 | Stable ✅ | Shared Web Context shell for Bun/Deno/Edge (F-08). | | resolveClientIp | (get, opts) => string | 3.1.0 | Stable ✅ | The one client-IP precedence + validation policy. | | combineAbortSignal | (base: AbortSignal) => CombinedAbort | 3.1.0 | Stable ✅ | Merge client-disconnect and timeout into one signal. | | normalizeStartupError | (err, ctx) => ServerStartError | 3.1.0 | Stable ✅ | Uniform bind-failure error across adapters. |

Exports by domain

| Domain | Exports | | ------ | ------- | | Detection (detection.ts) | detectRuntime · getRuntime · getRuntimeVersion · getRuntimeInfo · detectEdgeRuntime · isNode · isBun · isDeno · isEdge · isRuntime · resetRuntimeCache · capabilitiesFor · getRuntimeCapabilities · type EdgeRuntimeInfo | | Capability profiles (profiles.ts) | NodeProfile · BunProfile · DenoProfile · DenoDeployProfile · CloudflareProfile · VercelEdgeProfile · EdgeProfile · LambdaProfile · capabilityProfileFor · type CapabilityProfile | | Body source (body-source.ts) | AbstractBodySource · WebBodySource · EmptyBodySource · createWebBodySource · createEmptyBodySource · BodyConsumedError · BodyTooLargeError · DEFAULT_BODY_LIMIT | | Response builder (response-builder.ts) | WebResponseBuilder · assertHeaderSafe · isBodylessResponse · jsonErrorResponse | | Web Context base (web-context-base.ts) | WebContextBase · types WebRawHttp · WebStreamRunners | | Headers (headers.ts) | headersToRecord · resolveClientIp · getClientIp · getEdgeClientIp · isValidClientIp · types ClientIpOptions · HeaderLookup | | Query (query.ts) | parseQueryString | | Request signal (request-signal.ts) | combineAbortSignal · type CombinedAbort | | Startup errors (server-error.ts) | ServerStartError · normalizeStartupError · type ServerStartErrorCode | | Constants (constants.ts) | DEFAULT_TIMEOUT_MS · DEFAULT_SHUTDOWN_TIMEOUT_MS · DEFAULT_KEEP_ALIVE_TIMEOUT_MS · METHODS_WITHOUT_BODY | | Re-exported types (from @nextrush/types) | Runtime · RuntimeInfo · RuntimeCapabilities · BodySource · BodySourceOptions |

Options

No configuration object — @nextrush/runtime exports functions and classes, not a configurable middleware. There is nothing to instantiate globally; behavior is controlled per call through documented option objects:

| Option object | Used by | Key fields | | ------------- | ------- | ---------- | | ClientIpOptions | resolveClientIp | trustProxy (⚠️ security-sensitive) · directIp · cloudflare? | | BodySourceOptions | createWebBodySource | limit (bytes; defaults to DEFAULT_BODY_LIMIT) |

[!WARNING] trustProxy decides whether x-forwarded-for / x-real-ip are believed. Enable it only when the process sits behind a proxy you control — otherwise a client can spoof its own IP. This is the single security-relevant switch in the package.

Compatibility

Requirements

| Requirement | Version | | ----------- | ------- | | NextRush | 3.x | | Node.js | >=22 | | TypeScript | >=5.x |

Runtimes

| Runtime | Supported | Notes | | ------- | --------- | ----- | | Node.js >=22 | ✅ | Detected via process.versions.node. | | Bun / Deno / Edge | ✅ / ✅ / ✅ | Detected by feature-probing global objects (Bun, Deno, navigator.userAgent, Web Request); unknown runtimes fall back to a probed capability matrix, not an all-false blank. |

Integration

  • Peer dependencies: none — depends on @nextrush/types and @nextrush/errors directly.
  • Works with: every @nextrush/adapter-* package (they build on these primitives).
  • Incompatible with: none.

[!IMPORTANT] NextRush is ESM-only, permanently — no CommonJS build. On Node >=22, CommonJS consumers can require() this ESM package natively. See the Module Format Policy.


Troubleshooting

Cause: the environment exposes none of the probed signals (no Bun / Deno global, no process.versions.node, no Web Request). Fix: you can still call getRuntimeCapabilities() — for 'unknown' it feature-probes the available globals rather than reporting everything as unsupported, so a capable runtime keeps working. If you expected a known runtime, verify the global your platform sets is present at the point of the call.

Cause: trustProxy is false (the safe default), so proxy headers are ignored and directIp is returned; behind a proxy the socket address is the proxy, not the client. Fix: pass trustProxy: true to the adapter/resolveClientIp only when you actually sit behind a trusted proxy. The precedence is then cf-connecting-ipx-forwarded-for[0]x-real-ip, each format-validated.

Cause: a BodySource stream can be consumed once; a second text() / json() / arrayBuffer() call (e.g. two middleware both reading the body) fails. Fix: read the body once and share the parsed result via ctx.state, or buffer it in a body-parser middleware that later readers reuse.

Cause: getRuntime() and detectEdgeRuntime() cache their result on first call. Fix: call resetRuntimeCache() between tests that swap the runtime globals. It is exported for exactly this (marked @internal — test-only, not an application API).

FAQ

Where is listen() / how do I start a server? Not here. @nextrush/runtime provides the cross-runtime primitives; the server lifecycle (listen / serve) lives in each platform adapter — @nextrush/adapter-node, -bun, -deno, -edge. Application code gets listen through nextrush.

Why ESM-only? See the Module Format Policy.

Does it work on Bun, Deno, and Edge? Yes — detecting and serving those runtimes identically is the whole point. Detection probes each platform's globals; the primitives (body, response, IP, query) are written against Web-standard Request / Response / ReadableStream.

Why detect the runtime at all if I shouldn't branch on it? Detection drives adapter selection and diagnostics/defaults, not feature behavior. Feature decisions go through getRuntimeCapabilities() — a probed matrix — so an unrecognized runtime still gets every capability it actually supports.


Package relationships

                    depends on          @nextrush/types  ·  @nextrush/errors
@nextrush/runtime ------------------->
                    depended on by      @nextrush/adapter-{node,bun,deno,edge,serverless}
                    used alongside      @nextrush/adapters/conformance (parity tests)
  • Depends on: @nextrush/types (the Runtime / RuntimeCapabilities / BodySource contracts) and @nextrush/errors (the NextRushError base for ServerStartError).
  • Depended on by: the platform adapters — adapter-node, adapter-bun, adapter-deno, adapter-edge, and adapter-serverless — which build their request/response handling on these primitives.
  • Usually used next: an adapter — you rarely use @nextrush/runtime without one.
  • Alternative: none — this is the framework's cross-runtime layer.

Architecture

Maintaining or contributing to this package? The internal design — how detection is cached, why capability decisions are separated from runtime identity, how the Fetch-API primitives keep the four adapters in parity, the invariants, and the trade-offs (with diagrams) — is in ARCHITECTURE.md. Design history: the cross-runtime capability-negotiation contract (RFC/ADR-R6) and the adapter-parity audit fixes (F-08, F-11, F-15, F-16, R-2..R-10).

Resources


MIT © Tanzim Hossain