@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 +309 -321
- package/dist/index.d.ts +756 -92
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,400 +1,388 @@
|
|
|
1
1
|
# @nextrush/types
|
|
2
2
|
|
|
3
|
-
>
|
|
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
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@nextrush/types)
|
|
6
|
+
[](https://www.npmjs.com/package/@nextrush/types)
|
|
7
|
+
[](https://bundlephobia.com/package/@nextrush/types)
|
|
8
|
+
[](https://www.npmjs.com/package/@nextrush/types)
|
|
9
|
+
[](https://nodejs.org/api/esm.html)
|
|
10
|
+
[](https://github.com/0xTanzim/nextRush/blob/main/LICENSE)
|
|
6
11
|
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
- Inconsistent interfaces across the ecosystem
|
|
11
|
-
- Difficulty extending or augmenting types
|
|
24
|
+
## Highlights
|
|
12
25
|
|
|
13
|
-
|
|
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
|
-
|
|
32
|
+
<details>
|
|
33
|
+
<summary><strong>Table of contents</strong></summary>
|
|
16
34
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
30
|
-
import type { Context, Middleware
|
|
31
|
-
import { HttpStatus
|
|
94
|
+
```ts
|
|
95
|
+
import type { Context, Middleware } from '@nextrush/types';
|
|
96
|
+
import { HttpStatus } from '@nextrush/types';
|
|
32
97
|
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
167
|
+
const getUser: RouteHandler = (ctx: Context) => {
|
|
168
|
+
ctx.json({ id: ctx.params.id });
|
|
169
|
+
};
|
|
170
|
+
```
|
|
102
171
|
|
|
103
|
-
###
|
|
172
|
+
### Use the HTTP constants and their value types
|
|
104
173
|
|
|
105
|
-
```
|
|
106
|
-
import
|
|
107
|
-
import {
|
|
174
|
+
```ts
|
|
175
|
+
import { HttpStatus, HTTP_METHODS, ContentType } from '@nextrush/types';
|
|
176
|
+
import type { HttpStatusCode, HttpMethod, ContentTypeValue } from '@nextrush/types';
|
|
108
177
|
|
|
109
|
-
const
|
|
110
|
-
|
|
178
|
+
const status: HttpStatusCode = HttpStatus.CREATED; // 201
|
|
179
|
+
const ct: ContentTypeValue = ContentType.JSON; // 'application/json'
|
|
111
180
|
|
|
112
|
-
|
|
113
|
-
//
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
###
|
|
208
|
+
### Accept any Standard Schema validator without an adapter
|
|
122
209
|
|
|
123
|
-
```
|
|
124
|
-
import {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
###
|
|
224
|
+
### Type an adapter against the conformance contract
|
|
140
225
|
|
|
141
|
-
```
|
|
142
|
-
import {
|
|
143
|
-
import type { ContentTypeValue } from '@nextrush/types';
|
|
226
|
+
```ts
|
|
227
|
+
import type { ServerAdapter, ServerHandle } from '@nextrush/types';
|
|
144
228
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
240
|
+
## API overview
|
|
153
241
|
|
|
154
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
161
|
-
const outgoing: OutgoingHeaders = {
|
|
162
|
-
'Content-Type': 'application/json',
|
|
163
|
-
'X-Request-Id': '123',
|
|
164
|
-
};
|
|
165
|
-
```
|
|
251
|
+
### Type exports by domain
|
|
166
252
|
|
|
167
|
-
|
|
253
|
+
Grouped by the module that owns them (all `import type`).
|
|
168
254
|
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
173
|
-
const body: ParsedBody = ctx.body;
|
|
174
|
-
// string | Uint8Array | Record<string, unknown> | unknown[] | null | undefined
|
|
269
|
+
## Options
|
|
175
270
|
|
|
176
|
-
|
|
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
|
-
##
|
|
273
|
+
## Compatibility
|
|
182
274
|
|
|
183
|
-
|
|
184
|
-
import type { Middleware, Next, RouteHandler } from '@nextrush/types';
|
|
275
|
+
**Requirements**
|
|
185
276
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
};
|
|
277
|
+
| Requirement | Version |
|
|
278
|
+
| ----------- | ------- |
|
|
279
|
+
| NextRush | `3.x` |
|
|
280
|
+
| Node.js | `>=22` |
|
|
281
|
+
| TypeScript | `>=5.x` |
|
|
192
282
|
|
|
193
|
-
|
|
194
|
-
const callNext: Next = async () => {
|
|
195
|
-
// ...
|
|
196
|
-
};
|
|
283
|
+
**Runtimes**
|
|
197
284
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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
|
-
|
|
232
|
-
const advancedPlugin: PluginWithHooks = {
|
|
233
|
-
name: 'advanced',
|
|
234
|
-
install(app) {},
|
|
300
|
+
---
|
|
235
301
|
|
|
236
|
-
|
|
237
|
-
onResponse(ctx) {},
|
|
238
|
-
onError(error, ctx) {},
|
|
239
|
-
extendContext(ctx) {},
|
|
240
|
-
};
|
|
302
|
+
## Troubleshooting
|
|
241
303
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
325
|
+
</details>
|
|
289
326
|
|
|
290
|
-
|
|
291
|
-
|
|
327
|
+
<details>
|
|
328
|
+
<summary><strong>My Zod / Valibot schema isn't accepted as <code>StandardSchemaV1</code></strong></summary>
|
|
292
329
|
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
|
|
309
|
-
|
|
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
|
-
|
|
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
|
-
|
|
322
|
-
import type { RawHttp } from '@nextrush/types';
|
|
339
|
+
</details>
|
|
323
340
|
|
|
324
|
-
|
|
325
|
-
const raw: RawHttp = ctx.raw;
|
|
326
|
-
raw.req; // Platform request
|
|
327
|
-
raw.res; // Platform response
|
|
341
|
+
## FAQ
|
|
328
342
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
##
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
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
|
-
- **
|
|
395
|
-
- **
|
|
396
|
-
- **
|
|
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
|
-
|
|
386
|
+
---
|
|
399
387
|
|
|
400
|
-
MIT
|
|
388
|
+
MIT © [Tanzim Hossain](https://github.com/0xTanzim)
|