@nextrush/types 3.0.6 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,400 +1,388 @@
1
1
  # @nextrush/types
2
2
 
3
- > Shared TypeScript type definitions for the NextRush framework.
3
+ > 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.
4
4
 
5
- ## The Problem
5
+ [![npm version](https://img.shields.io/npm/v/@nextrush/types.svg)](https://www.npmjs.com/package/@nextrush/types)
6
+ [![downloads](https://img.shields.io/npm/dm/@nextrush/types.svg)](https://www.npmjs.com/package/@nextrush/types)
7
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/@nextrush/types.svg)](https://bundlephobia.com/package/@nextrush/types)
8
+ [![types](https://img.shields.io/npm/types/@nextrush/types.svg)](https://www.npmjs.com/package/@nextrush/types)
9
+ [![ESM only](https://img.shields.io/badge/module-ESM--only-blue.svg)](https://nodejs.org/api/esm.html)
10
+ [![license](https://img.shields.io/npm/l/@nextrush/types.svg)](https://github.com/0xTanzim/nextRush/blob/main/LICENSE)
6
11
 
7
- Backend frameworks often have types scattered across packages. This creates:
12
+ | | |
13
+ | --- | --- |
14
+ | **Purpose** | The single source of truth for NextRush's cross-package TypeScript contracts (`Context`, `Middleware`, `Router`, `Container`, `Extension`, adapter/runtime/stream shapes) |
15
+ | **Package type** | Core |
16
+ | **Status** | Stable ✅ |
17
+ | **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. |
18
+ | **Support tier** | Public — core (stable, semver-guarded) — see [ADR-0005](https://github.com/0xTanzim/nextRush/blob/main/docs/adr/ADR-0005-package-tiers-sealed-surface-deprecation.md) |
19
+ | **Maintenance** | Active |
20
+ | **Runtime** | Universal — Node · Bun · Deno · Edge |
21
+ | **Requires** | Node `>=22` · ESM-only · TypeScript `>=5.x` |
22
+ | **Introduced** | `v3.0.0` |
8
23
 
9
- - Circular dependencies between packages
10
- - Inconsistent interfaces across the ecosystem
11
- - Difficulty extending or augmenting types
24
+ ## Highlights
12
25
 
13
- ## How NextRush Approaches This
26
+ - **Zero runtime dependencies** — the root of the package graph; it imports nothing
27
+ - ✅ **Near-zero runtime footprint** — only four constant value exports (`HTTP_METHODS`, `HttpStatus`, `ContentType`, `ROUTE_METADATA`); every other export is a type, erased at build
28
+ - ✅ **ESM-only**, tree-shakable, side-effect-free (`"sideEffects": false`)
29
+ - ✅ **Fully typed** — strict TypeScript, zero `any`; structural stream interfaces avoid coupling to any runtime
30
+ - 📦 **Bundle:** ~1 KB min+gzip (the four constants)
14
31
 
15
- `@nextrush/types` is the **single source of truth** for all NextRush types:
32
+ <details>
33
+ <summary><strong>Table of contents</strong></summary>
16
34
 
17
- - **Zero dependencies**: Pure TypeScript definitions
18
- - **Zero runtime code**: Only exports types and constants
19
- - **Foundation package**: All NextRush packages depend on this
35
+ [The problem](#the-problem) · [When to use](#when-to-use) · [Installation](#installation) · [Quick start](#quick-start) · [Capabilities](#capabilities) · [Mental model](#mental-model) · [Common tasks](#common-tasks) · [API overview](#api-overview) · [Options](#options) · [Compatibility](#compatibility) · [Troubleshooting](#troubleshooting) · [FAQ](#faq) · [Package relationships](#package-relationships) · [Architecture](#architecture) · [Resources](#resources)
36
+
37
+ </details>
38
+
39
+ ---
40
+
41
+ ## The problem
42
+
43
+ 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.
44
+
45
+ ```ts
46
+ // TODAY, without a shared contract package — each layer redeclares the shape,
47
+ // and the definitions drift the moment one changes:
48
+
49
+ // in @nextrush/router
50
+ interface Context { params: Record<string, string>; /* … */ }
51
+
52
+ // in a middleware package
53
+ interface Context { params: object; body: any; /* … slightly different, and now `any` leaked */ }
54
+
55
+ // the router imports the middleware package (or vice versa) solely to reuse a type → a cycle waiting to happen
56
+ ```
57
+
58
+ `@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.
59
+
60
+ ## When to use
61
+
62
+ `@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.
63
+
64
+ **Use `@nextrush/types` if:**
65
+
66
+ - ✓ You're authoring a NextRush **package, adapter, or middleware** and need the shared `Context` / `Middleware` / `Router` / `Container` contracts without pulling in a heavier package
67
+ - ✓ You're building an **adapter** and need the `ServerAdapter` / `FetchAdapter` / `AdapterContext` conformance shapes
68
+ - ✓ You're writing a **framework-agnostic** utility that should type against the contract, not a concrete implementation
69
+ - ✓ You want the runtime constants `HttpStatus`, `HTTP_METHODS`, `ContentType`, or the `ROUTE_METADATA` symbol
70
+
71
+ **Reach for something else if:**
72
+
73
+ - ✗ You're building an application — you already get these types through [`nextrush`](../nextrush) / [`@nextrush/core`](../core); you rarely import from here directly
74
+ - ✗ You need HTTP **error classes** → use [`@nextrush/errors`](../errors) (it depends on these types)
75
+ - ✗ You need the router **implementation**, not its interface → use [`@nextrush/router`](../router)
76
+
77
+ ---
20
78
 
21
79
  ## Installation
22
80
 
23
81
  ```bash
24
82
  pnpm add @nextrush/types
83
+ # npm i @nextrush/types · yarn add @nextrush/types · bun add @nextrush/types
25
84
  ```
26
85
 
27
- ## Quick Start
86
+ > [!NOTE]
87
+ > Already using `nextrush`? The contracts you touch daily — `Context`, `Middleware`, `HttpStatus`,
88
+ > and friends — are reachable transitively; `createApp()` returns a typed `Context` without a direct
89
+ > import. Install `@nextrush/types` only when you're authoring a package/adapter/middleware and want
90
+ > to depend on the contract explicitly.
91
+
92
+ ## Quick start
28
93
 
29
- ```typescript
30
- import type { Context, Middleware, Plugin } from '@nextrush/types';
31
- import { HttpStatus, ContentType } from '@nextrush/types';
94
+ ```ts
95
+ import type { Context, Middleware } from '@nextrush/types';
96
+ import { HttpStatus } from '@nextrush/types';
32
97
 
33
- const middleware: Middleware = async (ctx, next) => {
34
- ctx.status = HttpStatus.OK;
98
+ // A framework-agnostic middleware typed against the shared contract.
99
+ // It reads and writes the SAME Context every NextRush package agrees on.
100
+ export const requestId: Middleware = async (ctx: Context, next) => {
101
+ ctx.set('X-Request-Id', crypto.randomUUID());
35
102
  await next();
103
+ if (!ctx.responded) {
104
+ ctx.status = HttpStatus.NO_CONTENT; // 204, from the shared constant
105
+ }
36
106
  };
37
107
  ```
38
108
 
39
- ## Context Types
40
-
41
- The `Context` interface is the heart of NextRush:
42
-
43
- ```typescript
44
- import type { Context, ContextState, RouteParams, QueryParams } from '@nextrush/types';
45
-
46
- const handler = async (ctx: Context) => {
47
- // Request (read-only)
48
- ctx.method; // HttpMethod
49
- ctx.url; // Full URL with query
50
- ctx.path; // Path without query
51
- ctx.query; // QueryParams
52
- ctx.headers; // IncomingHeaders
53
- ctx.ip; // Client IP
54
- ctx.params; // RouteParams (from router)
55
- ctx.body; // unknown (from body parser)
56
-
57
- // Response
58
- ctx.status = 201;
59
- ctx.json({ created: true });
60
- ctx.send('text');
61
- ctx.html('<h1>Hello</h1>');
62
- ctx.redirect('/new-url');
63
-
64
- // Headers
65
- ctx.set('X-Custom', 'value');
66
- ctx.get('Authorization');
67
-
68
- // Error helpers
69
- ctx.throw(404, 'Not found');
70
- ctx.assert(user, 404, 'User not found');
71
-
72
- // State (for middleware data)
73
- ctx.state.user = { id: '123' };
74
-
75
- // Middleware flow
76
- await ctx.next();
77
-
78
- // Raw access (platform-specific)
79
- ctx.raw.req; // Raw request
80
- ctx.raw.res; // Raw response
81
- ctx.runtime; // 'node' | 'bun' | 'deno' | 'edge'
82
- ctx.bodySource; // BodySource for parsers
83
- };
109
+ `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.
110
+
111
+ ## Capabilities
112
+
113
+ **Request/response contracts**
114
+ - **`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()`)
115
+ - **`Middleware` / `Next` / `RouteHandler`** — Koa-style middleware supporting both `(ctx)` and `(ctx, next)` signatures
116
+ - **HTTP primitives** `HttpMethod`, `HttpStatus`, `ContentType`, `IncomingHeaders`, `OutgoingHeaders`, `ParsedBody`, `ResponseBody`, `RawHttp`
117
+
118
+ **Framework contracts**
119
+ - **Router** `Router`, `Route`, `RouteMatch`, `RouterOptions`, `RoutePattern`, `RouteParam`
120
+ - **Route metadata** `RouteDefinition`, `RouteMetadata`, `RouteEntry`, and the `ROUTE_METADATA` contribution symbol (the source of truth for OpenAPI and future renderers)
121
+ - **Dependency injection** — `Container`, `Provider`, `Scope`, `Token`, `ServiceOptions` (the contract `@nextrush/di` implements)
122
+ - **Extensions** — `Extension`, `ExtensionContext`, `ExtensionHost` (the rare long-lived-service model)
123
+ - **Adapters** `ServerAdapter`, `FetchAdapter`, `AdapterContext`, `FetchContext`, `ServerAddress`, `ServerHandle`
124
+ - **Runtime & streaming** — `Runtime`, `RuntimeCapabilities`, `BodySource`, and the `TextStreamWriter` / `SSEStreamWriter` / `NDJSONStreamWriter` shapes
125
+ - **Standard Schema** — `StandardSchemaV1` (a vendored, dependency-free copy of the [Standard Schema](https://github.com/standard-schema/standard-schema) v1 contract) so Zod / Valibot / ArkType schemas are accepted structurally
126
+
127
+ **Developer experience**
128
+ - **Runtime-independent** — no `node:*`, no runtime globals; structural `NodeStreamLike` / `WebStreamLike` interfaces stand in for platform stream types
129
+ - **Fully typed** — strict TypeScript, zero `any`; tree-shakable and side-effect-free
130
+
131
+ ## Mental model
132
+
133
+ `@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.
134
+
135
+ ```text
136
+ ┌─ HTTP primitives (method · status · headers · body)
137
+ @nextrush/types ─┼─ Context contract ──▶ read by every handler & middleware
138
+ (shared shapes) ├─ Router / metadata contracts
139
+ ├─ DI · Extension · Logger contracts
140
+ └─ Adapter · Runtime · Stream contracts
141
+
142
+ └─ imported by every package · imports nothing itself (the graph root)
84
143
  ```
85
144
 
86
- ### Context Options
145
+ **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.
87
146
 
88
- ```typescript
89
- import type { ContextOptions } from '@nextrush/types';
147
+ > [!TIP]
148
+ > How these contracts relate, and why `types` sits at the bottom of the hierarchy (with Mermaid
149
+ > diagrams), is in [`ARCHITECTURE.md`](./ARCHITECTURE.md).
90
150
 
91
- // Used by adapters to create contexts
92
- const options: ContextOptions = {
93
- method: 'GET',
94
- url: '/users?page=1',
95
- headers: { 'content-type': 'application/json' },
96
- ip: '127.0.0.1',
97
- raw: { req, res },
151
+ ---
152
+
153
+ ## Common tasks
154
+
155
+ ### Type a middleware or route handler
156
+
157
+ ```ts
158
+ import type { Context, Middleware, RouteHandler } from '@nextrush/types';
159
+
160
+ // Both signatures are valid — `ctx.next()` (modern) or the `next` parameter (traditional).
161
+ const logger: Middleware = async (ctx, next) => {
162
+ const start = Date.now();
163
+ await next();
164
+ ctx.state.tookMs = Date.now() - start;
98
165
  };
99
- ```
100
166
 
101
- ## HTTP Types
167
+ const getUser: RouteHandler = (ctx: Context) => {
168
+ ctx.json({ id: ctx.params.id });
169
+ };
170
+ ```
102
171
 
103
- ### Methods
172
+ ### Use the HTTP constants and their value types
104
173
 
105
- ```typescript
106
- import type { HttpMethod, CommonHttpMethod } from '@nextrush/types';
107
- import { HTTP_METHODS } from '@nextrush/types';
174
+ ```ts
175
+ import { HttpStatus, HTTP_METHODS, ContentType } from '@nextrush/types';
176
+ import type { HttpStatusCode, HttpMethod, ContentTypeValue } from '@nextrush/types';
108
177
 
109
- const method: HttpMethod = 'GET';
110
- // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT'
178
+ const status: HttpStatusCode = HttpStatus.CREATED; // 201
179
+ const ct: ContentTypeValue = ContentType.JSON; // 'application/json'
111
180
 
112
- const common: CommonHttpMethod = 'POST';
113
- // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'
181
+ // HTTP_METHODS is a readonly tuple for iteration.
182
+ // Note: TRACE and CONNECT are in the `HttpMethod` type but intentionally
183
+ // excluded from this tuple (XST risk / proxy-only).
184
+ for (const method of HTTP_METHODS) {
185
+ const m: HttpMethod = method; // 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS'
186
+ }
187
+ ```
114
188
 
115
- // Iterate over methods
116
- for (const m of HTTP_METHODS) {
117
- console.log(m);
189
+ ### Type an extension (long-lived app-scoped service)
190
+
191
+ ```ts
192
+ import type { Extension, ExtensionContext } from '@nextrush/types';
193
+
194
+ // The optional generic carries the decorated shape so `app.<name>` infers.
195
+ export function clock(): Extension<{ now: () => number }> {
196
+ return {
197
+ name: 'clock',
198
+ setup(ctx: ExtensionContext) {
199
+ ctx.decorate('now', () => Date.now()); // throws on name collision
200
+ },
201
+ destroy() {
202
+ /* runs in reverse order at app.close() */
203
+ },
204
+ };
118
205
  }
119
206
  ```
120
207
 
121
- ### Status Codes
208
+ ### Accept any Standard Schema validator without an adapter
122
209
 
123
- ```typescript
124
- import { HttpStatus } from '@nextrush/types';
125
- import type { HttpStatusCode } from '@nextrush/types';
126
-
127
- ctx.status = HttpStatus.OK; // 200
128
- ctx.status = HttpStatus.CREATED; // 201
129
- ctx.status = HttpStatus.BAD_REQUEST; // 400
130
- ctx.status = HttpStatus.UNAUTHORIZED; // 401
131
- ctx.status = HttpStatus.FORBIDDEN; // 403
132
- ctx.status = HttpStatus.NOT_FOUND; // 404
133
- ctx.status = HttpStatus.INTERNAL_SERVER_ERROR; // 500
134
-
135
- // Type for status code values
136
- const status: HttpStatusCode = 200;
210
+ ```ts
211
+ import type { StandardSchemaV1, InferOutput } from '@nextrush/types';
212
+
213
+ // Works with Zod 3.24+, Valibot 1.0+, ArkType 2.0+ — anything exposing `~standard`.
214
+ async function parse<S extends StandardSchemaV1>(
215
+ schema: S,
216
+ input: unknown,
217
+ ): Promise<InferOutput<S>> {
218
+ const result = await schema['~standard'].validate(input);
219
+ if (result.issues) throw new Error(result.issues[0]?.message);
220
+ return result.value as InferOutput<S>;
221
+ }
137
222
  ```
138
223
 
139
- ### Content Types
224
+ ### Type an adapter against the conformance contract
140
225
 
141
- ```typescript
142
- import { ContentType } from '@nextrush/types';
143
- import type { ContentTypeValue } from '@nextrush/types';
226
+ ```ts
227
+ import type { ServerAdapter, ServerHandle } from '@nextrush/types';
144
228
 
145
- ctx.set('Content-Type', ContentType.JSON); // 'application/json'
146
- ctx.set('Content-Type', ContentType.HTML); // 'text/html'
147
- ctx.set('Content-Type', ContentType.TEXT); // 'text/plain'
148
- ctx.set('Content-Type', ContentType.FORM); // 'application/x-www-form-urlencoded'
149
- ctx.set('Content-Type', ContentType.MULTIPART); // 'multipart/form-data'
229
+ // `satisfies` pins the shape so `serve`/`createHandler` can't drift across adapters.
230
+ const nodeAdapter = {
231
+ async serve(app, options) {
232
+ /* */ return {} as ServerHandle;
233
+ },
234
+ createHandler(app, options) {
235
+ /* … */ return () => {};
236
+ },
237
+ } satisfies ServerAdapter;
150
238
  ```
151
239
 
152
- ### Headers
240
+ ## API overview
153
241
 
154
- ```typescript
155
- import type { IncomingHeaders, OutgoingHeaders } from '@nextrush/types';
242
+ Only four exports carry a runtime value; every other export is a **type** (erased at build). The sealed surface (ADR-0005):
156
243
 
157
- // Request headers (read-only)
158
- const incoming: IncomingHeaders = ctx.headers;
244
+ | Export | Signature | Since | Stability | Description |
245
+ | ------ | --------- | ----- | --------- | ----------- |
246
+ | `HttpStatus` | `Readonly<Record<string, number>>` | `3.0.0` | Stable ✅ | Named HTTP status codes (`HttpStatus.OK` → `200`). |
247
+ | `HTTP_METHODS` | `readonly HttpMethod[]` | `3.0.0` | Stable ✅ | Iterable tuple of routable methods (excludes `TRACE`/`CONNECT`). |
248
+ | `ContentType` | `Readonly<Record<string, string>>` | `3.0.0` | Stable ✅ | Common content-type strings (`ContentType.JSON`). |
249
+ | `ROUTE_METADATA` | `unique symbol` | `3.1.0` | Stable ✅ | `Symbol.for('nextrush.route.metadata')` — the route-metadata contribution key. |
159
250
 
160
- // Response headers (writable)
161
- const outgoing: OutgoingHeaders = {
162
- 'Content-Type': 'application/json',
163
- 'X-Request-Id': '123',
164
- };
165
- ```
251
+ ### Type exports by domain
166
252
 
167
- ### Body Types
253
+ Grouped by the module that owns them (all `import type`).
168
254
 
169
- ```typescript
170
- import type { ParsedBody, ResponseBody } from '@nextrush/types';
255
+ | Domain | Exports |
256
+ | ------ | ------- |
257
+ | **Context** (`context.ts`) | `Context` · `ContextOptions` · `ContextState` · `RouteParams` · `QueryParams` · `Middleware` · `Next` · `RouteHandler` |
258
+ | **HTTP** (`http.ts`) | `HttpMethod` · `CommonHttpMethod` · `HttpStatusCode` · `ContentTypeValue` · `IncomingHeaders` · `OutgoingHeaders` · `ParsedBody` · `ResponseBody` · `RawHttp` · `NodeStreamLike` · `WebStreamLike` |
259
+ | **Router** (`router.ts`) | `Router` · `Route` · `RouteMatch` · `RouterOptions` · `RoutePattern` · `RouteParam` |
260
+ | **Route metadata** (`route-metadata.ts`) | `RouteDefinition` · `RouteMetadata` · `RouteEntry` · `RouteMetaMarker` · `MetadataContribution` |
261
+ | **DI** (`container.ts`) | `Container` · `Provider` · `ClassProvider` · `FactoryProvider` · `ValueProvider` · `Constructor` · `Token` · `Scope` · `ServiceOptions` · `RegisterOptions` |
262
+ | **Extensions** (`extension.ts`) | `Extension` · `ExtensionContext` · `ExtensionHost` |
263
+ | **Adapters** (`adapter.ts`, `adapter-context.ts`) | `ServerAdapter` · `FetchAdapter` · `FetchHandler` · `HandlerOptions` · `FetchHandlerOptions` · `ServerAddress` · `ServerHandle` · `AdapterContext` · `FetchContext` · `AdapterContextFactory` |
264
+ | **Runtime** (`runtime.ts`) | `Runtime` · `RuntimeInfo` · `RuntimeCapabilities` · `BodySource` · `BodySourceOptions` |
265
+ | **Streaming** (`stream.ts`) | `SSEEvent` · `BaseStreamWriter` · `TextStreamWriter` · `SSEStreamWriter` · `NDJSONStreamWriter` · `StreamSource` · `StreamRun` |
266
+ | **Standard Schema** (`standard-schema.ts`) | `StandardSchemaV1` · `StandardSchemaProps` · `StandardSchemaResult` · `StandardSchemaIssue` · `StandardSchemaPathSegment` · `InferOutput` |
267
+ | **Logger** (`logger.ts`) | `Logger` |
171
268
 
172
- // Request body after parsing
173
- const body: ParsedBody = ctx.body;
174
- // string | Uint8Array | Record<string, unknown> | unknown[] | null | undefined
269
+ ## Options
175
270
 
176
- // Response body types
177
- const response: ResponseBody = { data: 'value' };
178
- // string | Uint8Array | ArrayBuffer | NodeStreamLike | WebStreamLike | Record<string, unknown> | unknown[] | null | undefined
179
- ```
271
+ 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.
180
272
 
181
- ## Middleware Types
273
+ ## Compatibility
182
274
 
183
- ```typescript
184
- import type { Middleware, Next, RouteHandler } from '@nextrush/types';
275
+ **Requirements**
185
276
 
186
- // Middleware function
187
- const middleware: Middleware = async (ctx, next) => {
188
- console.log('Before');
189
- await next();
190
- console.log('After');
191
- };
277
+ | Requirement | Version |
278
+ | ----------- | ------- |
279
+ | NextRush | `3.x` |
280
+ | Node.js | `>=22` |
281
+ | TypeScript | `>=5.x` |
192
282
 
193
- // Next function type
194
- const callNext: Next = async () => {
195
- // ...
196
- };
283
+ **Runtimes**
197
284
 
198
- // Route handler (alias for Middleware)
199
- const handler: RouteHandler = async (ctx) => {
200
- ctx.json({ ok: true });
201
- };
202
- ```
285
+ | Runtime | Supported | Notes |
286
+ | ------- | --------- | ----- |
287
+ | Node.js `>=22` | ✅ | ESM-only |
288
+ | 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. |
203
289
 
204
- ## Plugin Types
205
-
206
- ```typescript
207
- import type {
208
- Plugin,
209
- PluginFactory,
210
- PluginWithHooks,
211
- PluginMeta,
212
- ApplicationLike,
213
- } from '@nextrush/types';
214
-
215
- // Basic plugin
216
- const plugin: Plugin = {
217
- name: 'my-plugin',
218
- version: '1.0.0',
219
-
220
- install(app: ApplicationLike) {
221
- app.use(async (ctx, next) => {
222
- await next();
223
- });
224
- },
290
+ **Integration**
291
+ - **Peer dependencies:** none — this is the root of the package graph.
292
+ - **Works with:** every `@nextrush/*` package (they all depend on it) and any Standard Schema validator (Zod / Valibot / ArkType) structurally.
293
+ - **Incompatible with:** none.
225
294
 
226
- destroy() {
227
- // Cleanup on shutdown
228
- },
229
- };
295
+ > [!IMPORTANT]
296
+ > NextRush is **ESM-only, permanently** — no CommonJS build. On Node `>=22`, CommonJS consumers can
297
+ > `require()` this ESM package natively. See the
298
+ > [Module Format Policy](https://github.com/0xTanzim/nextRush#module-format-policy).
230
299
 
231
- // Plugin with lifecycle hooks
232
- const advancedPlugin: PluginWithHooks = {
233
- name: 'advanced',
234
- install(app) {},
300
+ ---
235
301
 
236
- onRequest(ctx) {},
237
- onResponse(ctx) {},
238
- onError(error, ctx) {},
239
- extendContext(ctx) {},
240
- };
302
+ ## Troubleshooting
241
303
 
242
- // Plugin factory pattern
243
- const createPlugin: PluginFactory<{ debug: boolean }> = (options) => ({
244
- name: 'configurable',
245
- install(app) {
246
- if (options?.debug) {
247
- // Debug mode
248
- }
249
- },
250
- });
304
+ <details>
305
+ <summary><strong>"<code>HttpStatus</code> cannot be used as a value because it was imported using <code>import type</code>"</strong></summary>
306
+
307
+ **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`.
308
+
309
+ ```ts
310
+ import { HttpStatus, HTTP_METHODS, ContentType, ROUTE_METADATA } from '@nextrush/types';
311
+ import type { Context, Middleware, Router } from '@nextrush/types';
251
312
  ```
252
313
 
253
- ## Router Types
254
-
255
- ```typescript
256
- import type {
257
- Router,
258
- Route,
259
- RouteMatch,
260
- RouterOptions,
261
- RoutePattern,
262
- RouteParam,
263
- } from '@nextrush/types';
264
-
265
- // Route definition
266
- const route: Route = {
267
- method: 'GET',
268
- path: '/users/:id',
269
- handler: async (ctx) => ctx.json({ id: ctx.params.id }),
270
- middleware: [],
271
- };
314
+ </details>
272
315
 
273
- // Route match result
274
- const match: RouteMatch = {
275
- handler: route.handler,
276
- params: { id: '123' },
277
- middleware: [],
278
- };
316
+ <details>
317
+ <summary><strong>My function isn't accepted where a <code>Middleware</code> is expected</strong></summary>
279
318
 
280
- // Router options
281
- const options: RouterOptions = {
282
- prefix: '/api/v1',
283
- caseSensitive: false,
284
- strict: false,
285
- };
319
+ **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()`.
320
+
321
+ ```ts
322
+ const mw: Middleware = async (ctx, next) => { await next(); };
286
323
  ```
287
324
 
288
- ## Runtime Types
325
+ </details>
289
326
 
290
- ```typescript
291
- import type { Runtime, RuntimeInfo, RuntimeCapabilities, BodySource } from '@nextrush/types';
327
+ <details>
328
+ <summary><strong>My Zod / Valibot schema isn't accepted as <code>StandardSchemaV1</code></strong></summary>
292
329
 
293
- // Supported runtimes
294
- const runtime: Runtime = 'node';
295
- // 'node' | 'bun' | 'deno' | 'deno-deploy' | 'cloudflare-workers' | 'vercel-edge' | 'edge' | 'unknown'
330
+ **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.
296
331
 
297
- // Runtime capabilities
298
- const caps: RuntimeCapabilities = {
299
- nodeStreams: true,
300
- webStreams: true,
301
- fileSystem: true,
302
- webSocket: true,
303
- fetch: true,
304
- cryptoSubtle: true,
305
- workers: true,
306
- };
332
+ </details>
307
333
 
308
- // Body source for parsers
309
- const bodySource: BodySource = ctx.bodySource;
310
- await bodySource.text(); // Read as string
311
- await bodySource.buffer(); // Read as Uint8Array
312
- await bodySource.json(); // Read as JSON
313
- bodySource.stream(); // Get underlying stream
314
- bodySource.consumed; // Check if already read
315
- bodySource.contentLength; // Content-Length header
316
- bodySource.contentType; // Content-Type header
317
- ```
334
+ <details>
335
+ <summary><strong>"Cannot find module '@nextrush/types' or its corresponding type declarations"</strong></summary>
318
336
 
319
- ## Raw HTTP Types
337
+ **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`.
320
338
 
321
- ```typescript
322
- import type { RawHttp } from '@nextrush/types';
339
+ </details>
323
340
 
324
- // Generic raw access
325
- const raw: RawHttp = ctx.raw;
326
- raw.req; // Platform request
327
- raw.res; // Platform response
341
+ ## FAQ
328
342
 
329
- // Type with generics for platform-specific typing
330
- import type { IncomingMessage, ServerResponse } from 'http';
331
- const nodeRaw: RawHttp<IncomingMessage, ServerResponse> = ctx.raw;
332
- ```
343
+ **Do I need to install `@nextrush/types` directly?**
344
+ 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.
345
+
346
+ **Why ESM-only?**
347
+ See the [Module Format Policy](https://github.com/0xTanzim/nextRush#module-format-policy).
348
+
349
+ **Does it work on Bun, Deno, and Edge?**
350
+ 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.
351
+
352
+ **Is this really "zero runtime"?**
353
+ 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.
354
+
355
+ ---
333
356
 
334
- ## API Reference
335
-
336
- ### Exports
337
-
338
- ```typescript
339
- import {
340
- // Constants (runtime values)
341
- HttpStatus,
342
- HTTP_METHODS,
343
- ContentType,
344
- } from '@nextrush/types';
345
-
346
- import type {
347
- // Context
348
- Context,
349
- ContextOptions,
350
- ContextState,
351
- RouteParams,
352
- QueryParams,
353
- Middleware,
354
- Next,
355
- RouteHandler,
356
-
357
- // HTTP
358
- HttpMethod,
359
- CommonHttpMethod,
360
- HttpStatusCode,
361
- ContentTypeValue,
362
- IncomingHeaders,
363
- OutgoingHeaders,
364
- ParsedBody,
365
- ResponseBody,
366
- RawHttp,
367
-
368
- // Plugin
369
- Plugin,
370
- PluginWithHooks,
371
- PluginFactory,
372
- PluginMeta,
373
- ApplicationLike,
374
-
375
- // Router
376
- Router,
377
- Route,
378
- RouteMatch,
379
- RouterOptions,
380
- RoutePattern,
381
- RouteParam,
382
-
383
- // Runtime
384
- Runtime,
385
- RuntimeInfo,
386
- RuntimeCapabilities,
387
- BodySource,
388
- BodySourceOptions,
389
- } from '@nextrush/types';
357
+ ## Package relationships
358
+
359
+ ```text
360
+ depends on (nothing — root of the graph, zero dependencies)
361
+ @nextrush/types ─────────────▶
362
+ depended on by @nextrush/errors · core · router · runtime · di · class · adapter-* · middleware
363
+ satisfied by Zod · Valibot · ArkType (structurally, via StandardSchemaV1)
390
364
  ```
391
365
 
392
- ## Package Size
366
+ - **Depends on:** nothing — `@nextrush/types` is the lowest layer and imports no other package (project-rules §1).
367
+ - **Depended on by:** [`@nextrush/errors`](../errors), [`@nextrush/core`](../core), [`@nextrush/router`](../router), [`@nextrush/runtime`](../runtime), [`@nextrush/di`](../di), [`@nextrush/class`](../class), the adapters, and the middleware packages — they all import the same contracts from here.
368
+ - **Satisfied by:** any [Standard Schema](https://github.com/standard-schema/standard-schema) validator (Zod / Valibot / ArkType) conforms to `StandardSchemaV1` structurally, with no adapter.
369
+ - **Alternative:** none — these are the framework's contracts.
370
+
371
+ ## Architecture
372
+
373
+ Maintaining or contributing to this package? The internal design — how the contracts are grouped
374
+ across modules, how `Context` composes the HTTP / runtime / stream types, why `types` sits at the
375
+ bottom of the hierarchy, and the architectural invariants that keep it dependency-free (with
376
+ diagrams) — is in **[`ARCHITECTURE.md`](./ARCHITECTURE.md)**. Design history:
377
+ [ADR-0005 (package tiers & sealed surface)](https://github.com/0xTanzim/nextRush/blob/main/docs/adr/ADR-0005-package-tiers-sealed-surface-deprecation.md).
378
+
379
+ ## Resources
393
380
 
394
- - **Bundle**: ~1 KB (mostly constants)
395
- - **Types**: ~22 KB
396
- - **Dependencies**: None
381
+ - 📖 **Learn** [Documentation](https://0xtanzim.github.io/nextRush/docs) · [Architecture](./ARCHITECTURE.md) · [RFCs](https://github.com/0xTanzim/nextRush/tree/main/docs/RFC)
382
+ - 📝 **Changelog** [CHANGELOG.md](./CHANGELOG.md)
383
+ - 🐛 **Report an issue** — [GitHub Issues](https://github.com/0xTanzim/nextRush/issues)
384
+ - 🤝 **Contribute** — [CONTRIBUTING.md](https://github.com/0xTanzim/nextRush/blob/main/CONTRIBUTING.md)
397
385
 
398
- ## License
386
+ ---
399
387
 
400
- MIT
388
+ MIT © [Tanzim Hossain](https://github.com/0xTanzim)