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

v4.0.1

Published

Shared TypeScript types for NextRush framework

Readme

@nextrush/types

The shared TypeScript contracts every NextRush package is built on — Context, Middleware, the router, DI, extension, adapter, runtime, and streaming shapes — with zero runtime dependencies.

npm version downloads bundle size types ESM only license

| | | | --- | --- | | Purpose | The single source of truth for NextRush's cross-package TypeScript contracts (Context, Middleware, Router, Container, Extension, adapter/runtime/stream shapes) | | Package type | Core | | Status | Stable ✅ | | Included in nextrush? | ✅ Yes — the contracts reach app code transitively (createApp() returns a typed Context). Install directly only when authoring a package that needs the shapes without a heavier dependency. | | 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

  • Zero runtime dependencies — the root of the package graph; it imports nothing
  • Near-zero runtime footprint — only four constant value exports (HTTP_METHODS, HttpStatus, ContentType, ROUTE_METADATA); every other export is a type, erased at build
  • ESM-only, tree-shakable, side-effect-free ("sideEffects": false)
  • Fully typed — strict TypeScript, zero any; structural stream interfaces avoid coupling to any runtime
  • 📦 Bundle: ~1 KB min+gzip (the four constants)

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

In a 30+ package framework, the same Context object is read by the core middleware engine, populated by the router, extended by adapters, and consumed by every piece of middleware. If each package declared its own version of that shape, they would drift, packages would import each other only to borrow a type, and the dependency graph would grow cycles.

// TODAY, without a shared contract package — each layer redeclares the shape,
// and the definitions drift the moment one changes:

// in @nextrush/router
interface Context { params: Record<string, string>; /* … */ }

// in a middleware package
interface Context { params: object; body: any; /* … slightly different, and now `any` leaked */ }

// the router imports the middleware package (or vice versa) solely to reuse a type → a cycle waiting to happen

@nextrush/types fixes this by owning the contracts in one place, at the bottom of the package hierarchy. Every higher package imports the same Context, Middleware, and Router shapes; nothing has to import sideways to share a type; and because the package sits below everything else, it can never introduce a cycle.

When to use

@nextrush/types is the foundation layer — you consume its contracts constantly, usually without importing it by name. createApp() already hands you a typed Context, and nextrush / @nextrush/core re-export the everyday shapes.

Use @nextrush/types if:

  • ✓ You're authoring a NextRush package, adapter, or middleware and need the shared Context / Middleware / Router / Container contracts without pulling in a heavier package
  • ✓ You're building an adapter and need the ServerAdapter / FetchAdapter / AdapterContext conformance shapes
  • ✓ You're writing a framework-agnostic utility that should type against the contract, not a concrete implementation
  • ✓ You want the runtime constants HttpStatus, HTTP_METHODS, ContentType, or the ROUTE_METADATA symbol

Reach for something else if:

  • ✗ You're building an application — you already get these types through nextrush / @nextrush/core; you rarely import from here directly
  • ✗ You need HTTP error classes → use @nextrush/errors (it depends on these types)
  • ✗ You need the router implementation, not its interface → use @nextrush/router

Installation

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

[!NOTE] Already using nextrush? The contracts you touch daily — Context, Middleware, HttpStatus, and friends — are reachable transitively; createApp() returns a typed Context without a direct import. Install @nextrush/types only when you're authoring a package/adapter/middleware and want to depend on the contract explicitly.

Quick start

import type { Context, Middleware } from '@nextrush/types';
import { HttpStatus } from '@nextrush/types';

// A framework-agnostic middleware typed against the shared contract.
// It reads and writes the SAME Context every NextRush package agrees on.
export const requestId: Middleware = async (ctx: Context, next) => {
  ctx.set('X-Request-Id', crypto.randomUUID());
  await next();
  if (!ctx.responded) {
    ctx.status = HttpStatus.NO_CONTENT; // 204, from the shared constant
  }
};

Middleware is imported with import type (it's erased at build); HttpStatus is a real value, so it's a plain import. That split — types versus the handful of runtime constants — is the whole shape of this package.

Capabilities

Request/response contracts

  • Context — the unified request/response object (method, url, path, query, params, body, headers, ip, plus json()/send()/html()/redirect(), throw()/assert(), set()/get(), next(), state, raw, runtime, bodySource, and stream()/sse()/ndjson())
  • Middleware / Next / RouteHandler — Koa-style middleware supporting both (ctx) and (ctx, next) signatures
  • HTTP primitivesHttpMethod, HttpStatus, ContentType, IncomingHeaders, OutgoingHeaders, ParsedBody, ResponseBody, RawHttp

Framework contracts

  • RouterRouter, Route, RouteMatch, RouterOptions, RoutePattern, RouteParam
  • Route metadataRouteDefinition, RouteMetadata, RouteEntry, and the ROUTE_METADATA contribution symbol (the source of truth for OpenAPI and future renderers)
  • Dependency injectionContainer, Provider, Scope, Token, ServiceOptions (the contract @nextrush/di implements)
  • ExtensionsExtension, ExtensionContext, ExtensionHost (the rare long-lived-service model)
  • AdaptersServerAdapter, FetchAdapter, AdapterContext, FetchContext, ServerAddress, ServerHandle
  • Runtime & streamingRuntime, RuntimeCapabilities, BodySource, and the TextStreamWriter / SSEStreamWriter / NDJSONStreamWriter shapes
  • Standard SchemaStandardSchemaV1 (a vendored, dependency-free copy of the Standard Schema v1 contract) so Zod / Valibot / ArkType schemas are accepted structurally

Developer experience

  • Runtime-independent — no node:*, no runtime globals; structural NodeStreamLike / WebStreamLike interfaces stand in for platform stream types
  • Fully typed — strict TypeScript, zero any; tree-shakable and side-effect-free

Mental model

@nextrush/types is a dictionary of shapes, not a library of behavior. It defines what a request, a route, a container, or an adapter looks like; every other package agrees to those shapes and provides the behavior.

                 ┌─ HTTP primitives   (method · status · headers · body)
@nextrush/types ─┼─ Context contract  ──▶ read by every handler & middleware
 (shared shapes) ├─ Router / metadata contracts
                 ├─ DI · Extension · Logger contracts
                 └─ Adapter · Runtime · Stream contracts
        │
        └─ imported by every package · imports nothing itself (the graph root)

Rule: a contract shared by two or more packages lives here; a shape used by exactly one package stays in that package. This is the one place allowed to be depended on by everything and to depend on nothing.

[!TIP] How these contracts relate, and why types sits at the bottom of the hierarchy (with Mermaid diagrams), is in ARCHITECTURE.md.


Common tasks

Type a middleware or route handler

import type { Context, Middleware, RouteHandler } from '@nextrush/types';

// Both signatures are valid — `ctx.next()` (modern) or the `next` parameter (traditional).
const logger: Middleware = async (ctx, next) => {
  const start = Date.now();
  await next();
  ctx.state.tookMs = Date.now() - start;
};

const getUser: RouteHandler = (ctx: Context) => {
  ctx.json({ id: ctx.params.id });
};

Use the HTTP constants and their value types

import { HttpStatus, HTTP_METHODS, ContentType } from '@nextrush/types';
import type { HttpStatusCode, HttpMethod, ContentTypeValue } from '@nextrush/types';

const status: HttpStatusCode = HttpStatus.CREATED;       // 201
const ct: ContentTypeValue = ContentType.JSON;           // 'application/json'

// HTTP_METHODS is a readonly tuple for iteration.
// Note: TRACE and CONNECT are in the `HttpMethod` type but intentionally
// excluded from this tuple (XST risk / proxy-only).
for (const method of HTTP_METHODS) {
  const m: HttpMethod = method; // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'
}

Type an extension (long-lived app-scoped service)

import type { Extension, ExtensionContext } from '@nextrush/types';

// The optional generic carries the decorated shape so `app.<name>` infers.
export function clock(): Extension<{ now: () => number }> {
  return {
    name: 'clock',
    setup(ctx: ExtensionContext) {
      ctx.decorate('now', () => Date.now()); // throws on name collision
    },
    destroy() {
      /* runs in reverse order at app.close() */
    },
  };
}

Accept any Standard Schema validator without an adapter

import type { StandardSchemaV1, InferOutput } from '@nextrush/types';

// Works with Zod 3.24+, Valibot 1.0+, ArkType 2.0+ — anything exposing `~standard`.
async function parse<S extends StandardSchemaV1>(
  schema: S,
  input: unknown,
): Promise<InferOutput<S>> {
  const result = await schema['~standard'].validate(input);
  if (result.issues) throw new Error(result.issues[0]?.message);
  return result.value as InferOutput<S>;
}

Type an adapter against the conformance contract

import type { ServerAdapter, ServerHandle } from '@nextrush/types';

// `satisfies` pins the shape so `serve`/`createHandler` can't drift across adapters.
const nodeAdapter = {
  async serve(app, options) {
    /* … */ return {} as ServerHandle;
  },
  createHandler(app, options) {
    /* … */ return () => {};
  },
} satisfies ServerAdapter;

API overview

Only four exports carry a runtime value; every other export is a type (erased at build). The sealed surface (ADR-0005):

| Export | Signature | Since | Stability | Description | | ------ | --------- | ----- | --------- | ----------- | | HttpStatus | Readonly<Record<string, number>> | 3.0.0 | Stable ✅ | Named HTTP status codes (HttpStatus.OK200). | | HTTP_METHODS | readonly HttpMethod[] | 3.0.0 | Stable ✅ | Iterable tuple of routable methods (excludes TRACE/CONNECT). | | ContentType | Readonly<Record<string, string>> | 3.0.0 | Stable ✅ | Common content-type strings (ContentType.JSON). | | ROUTE_METADATA | unique symbol | 3.1.0 | Stable ✅ | Symbol.for('nextrush.route.metadata') — the route-metadata contribution key. |

Type exports by domain

Grouped by the module that owns them (all import type).

| Domain | Exports | | ------ | ------- | | Context (context.ts) | Context · ContextOptions · ContextState · RouteParams · QueryParams · Middleware · Next · RouteHandler | | HTTP (http.ts) | HttpMethod · CommonHttpMethod · HttpStatusCode · ContentTypeValue · IncomingHeaders · OutgoingHeaders · ParsedBody · ResponseBody · RawHttp · NodeStreamLike · WebStreamLike | | Router (router.ts) | Router · Route · RouteMatch · RouterOptions · RoutePattern · RouteParam | | Route metadata (route-metadata.ts) | RouteDefinition · RouteMetadata · RouteEntry · RouteMetaMarker · MetadataContribution | | DI (container.ts) | Container · Provider · ClassProvider · FactoryProvider · ValueProvider · Constructor · Token · Scope · ServiceOptions · RegisterOptions | | Extensions (extension.ts) | Extension · ExtensionContext · ExtensionHost | | Adapters (adapter.ts, adapter-context.ts) | ServerAdapter · FetchAdapter · FetchHandler · HandlerOptions · FetchHandlerOptions · ServerAddress · ServerHandle · AdapterContext · FetchContext · AdapterContextFactory | | Runtime (runtime.ts) | Runtime · RuntimeInfo · RuntimeCapabilities · BodySource · BodySourceOptions | | Streaming (stream.ts) | SSEEvent · BaseStreamWriter · TextStreamWriter · SSEStreamWriter · NDJSONStreamWriter · StreamSource · StreamRun | | Standard Schema (standard-schema.ts) | StandardSchemaV1 · StandardSchemaProps · StandardSchemaResult · StandardSchemaIssue · StandardSchemaPathSegment · InferOutput | | Logger (logger.ts) | Logger |

Options

No configuration — @nextrush/types exports only type declarations and four constants. There is nothing to instantiate or configure; you import a contract and type against it.

Compatibility

Requirements

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

Runtimes

| Runtime | Supported | Notes | | ------- | --------- | ----- | | Node.js >=22 | ✅ | ESM-only | | Bun / Deno / Edge | ✅ / ✅ / ✅ | Contracts are compile-time; the only runtime values are plain constants. Stream types are structural (NodeStreamLike / WebStreamLike), so no runtime is coupled in. |

Integration

  • Peer dependencies: none — this is the root of the package graph.
  • Works with: every @nextrush/* package (they all depend on it) and any Standard Schema validator (Zod / Valibot / ArkType) structurally.
  • 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: HttpStatus, HTTP_METHODS, ContentType, and ROUTE_METADATA are runtime values, not types — with verbatimModuleSyntax, import type erases them. Fix: import the four constants with a plain import, and everything else with import type.

import { HttpStatus, HTTP_METHODS, ContentType, ROUTE_METADATA } from '@nextrush/types';
import type { Context, Middleware, Router } from '@nextrush/types';

Cause: Middleware is (ctx: Context, next: Next) => void | Promise<void>. A handler returning a value (e.g. (ctx) => ctx.json(...) where json returns non-void) or one with an incompatible next still fits, but returning a truthy value from a synchronous handler can mismatch. Fix: type the function as Middleware/RouteHandler so inference flows from the contract, and use async when awaiting next().

const mw: Middleware = async (ctx, next) => { await next(); };

Cause: the Standard Schema contract requires the ~standard property, which older validator versions don't expose. Fix: use a version that implements Standard Schema v1 — Zod 3.24+, Valibot 1.0+, or ArkType 2.0+. No adapter is needed; conformance is structural.

Cause: the package is ESM-only and ships .d.ts from dist; a moduleResolution that predates node16/nodenext/bundler won't resolve its exports map. Fix: set "moduleResolution": "nodenext" (or "bundler") and "module": "nodenext" in tsconfig.json, on Node >=22.

FAQ

Do I need to install @nextrush/types directly? Usually no. Application code gets Context, Middleware, and the HTTP constants transitively through nextrush / @nextrush/core. Install it directly only when you author a package, adapter, or middleware that should depend on the contracts explicitly.

Why ESM-only? See the Module Format Policy.

Does it work on Bun, Deno, and Edge? Yes. The contracts are compile-time and the only runtime values are plain constant objects and one symbol. Platform stream types are represented structurally (NodeStreamLike / WebStreamLike), so nothing runtime-specific is imported.

Is this really "zero runtime"? Zero runtime dependencies — yes, always. Zero runtime code — almost: it ships four constant value exports (HttpStatus, HTTP_METHODS, ContentType, ROUTE_METADATA, ~1 KB gzipped). Every interface and type alias is erased at build.


Package relationships

                 depends on          (nothing — root of the graph, zero dependencies)
@nextrush/types ─────────────▶
                 depended on by      @nextrush/errors · core · router · runtime · di · class · adapter-* · middleware
                 satisfied by        Zod · Valibot · ArkType   (structurally, via StandardSchemaV1)

Architecture

Maintaining or contributing to this package? The internal design — how the contracts are grouped across modules, how Context composes the HTTP / runtime / stream types, why types sits at the bottom of the hierarchy, and the architectural invariants that keep it dependency-free (with diagrams) — is in ARCHITECTURE.md. Design history: ADR-0005 (package tiers & sealed surface).

Resources


MIT © Tanzim Hossain