@nextrush/types 3.0.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/LICENSE +21 -0
- package/README.md +400 -0
- package/dist/index.d.ts +915 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,915 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nextrush/types - HTTP Type Definitions
|
|
3
|
+
*
|
|
4
|
+
* Core HTTP types used across the NextRush framework.
|
|
5
|
+
* These types provide a clean abstraction over HTTP primitives,
|
|
6
|
+
* designed to work across multiple runtimes (Node.js, Bun, Deno, Edge).
|
|
7
|
+
*
|
|
8
|
+
* @packageDocumentation
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Standard HTTP methods supported by NextRush
|
|
12
|
+
*/
|
|
13
|
+
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT';
|
|
14
|
+
/**
|
|
15
|
+
* Common HTTP methods for convenience
|
|
16
|
+
*/
|
|
17
|
+
type CommonHttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
18
|
+
/**
|
|
19
|
+
* HTTP methods as readonly tuple for iteration.
|
|
20
|
+
*
|
|
21
|
+
* Note: TRACE and CONNECT are intentionally excluded from this constant.
|
|
22
|
+
* - TRACE enables Cross-Site Tracing (XST) attacks and is disabled by most servers.
|
|
23
|
+
* - CONNECT is used for HTTP tunneling (proxies) and is not relevant to application routing.
|
|
24
|
+
* Both remain in the `HttpMethod` type for completeness (e.g., custom proxy adapters).
|
|
25
|
+
*/
|
|
26
|
+
declare const HTTP_METHODS: readonly ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
|
|
27
|
+
/**
|
|
28
|
+
* Incoming request headers (read-only)
|
|
29
|
+
*/
|
|
30
|
+
type IncomingHeaders = Readonly<Record<string, string | string[] | undefined>>;
|
|
31
|
+
/**
|
|
32
|
+
* Outgoing response headers (writable)
|
|
33
|
+
*/
|
|
34
|
+
type OutgoingHeaders = Record<string, string | number | string[]>;
|
|
35
|
+
/**
|
|
36
|
+
* HTTP status codes
|
|
37
|
+
*/
|
|
38
|
+
declare const HttpStatus: {
|
|
39
|
+
readonly OK: 200;
|
|
40
|
+
readonly CREATED: 201;
|
|
41
|
+
readonly ACCEPTED: 202;
|
|
42
|
+
readonly NO_CONTENT: 204;
|
|
43
|
+
readonly MOVED_PERMANENTLY: 301;
|
|
44
|
+
readonly FOUND: 302;
|
|
45
|
+
readonly SEE_OTHER: 303;
|
|
46
|
+
readonly NOT_MODIFIED: 304;
|
|
47
|
+
readonly TEMPORARY_REDIRECT: 307;
|
|
48
|
+
readonly PERMANENT_REDIRECT: 308;
|
|
49
|
+
readonly BAD_REQUEST: 400;
|
|
50
|
+
readonly UNAUTHORIZED: 401;
|
|
51
|
+
readonly PAYMENT_REQUIRED: 402;
|
|
52
|
+
readonly FORBIDDEN: 403;
|
|
53
|
+
readonly NOT_FOUND: 404;
|
|
54
|
+
readonly METHOD_NOT_ALLOWED: 405;
|
|
55
|
+
readonly NOT_ACCEPTABLE: 406;
|
|
56
|
+
readonly PROXY_AUTH_REQUIRED: 407;
|
|
57
|
+
readonly REQUEST_TIMEOUT: 408;
|
|
58
|
+
readonly CONFLICT: 409;
|
|
59
|
+
readonly GONE: 410;
|
|
60
|
+
readonly LENGTH_REQUIRED: 411;
|
|
61
|
+
readonly PRECONDITION_FAILED: 412;
|
|
62
|
+
readonly PAYLOAD_TOO_LARGE: 413;
|
|
63
|
+
readonly URI_TOO_LONG: 414;
|
|
64
|
+
readonly UNSUPPORTED_MEDIA_TYPE: 415;
|
|
65
|
+
readonly RANGE_NOT_SATISFIABLE: 416;
|
|
66
|
+
readonly EXPECTATION_FAILED: 417;
|
|
67
|
+
readonly IM_A_TEAPOT: 418;
|
|
68
|
+
readonly UNPROCESSABLE_ENTITY: 422;
|
|
69
|
+
readonly LOCKED: 423;
|
|
70
|
+
readonly FAILED_DEPENDENCY: 424;
|
|
71
|
+
readonly TOO_EARLY: 425;
|
|
72
|
+
readonly UPGRADE_REQUIRED: 426;
|
|
73
|
+
readonly PRECONDITION_REQUIRED: 428;
|
|
74
|
+
readonly TOO_MANY_REQUESTS: 429;
|
|
75
|
+
readonly REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
|
|
76
|
+
readonly UNAVAILABLE_FOR_LEGAL_REASONS: 451;
|
|
77
|
+
readonly INTERNAL_SERVER_ERROR: 500;
|
|
78
|
+
readonly NOT_IMPLEMENTED: 501;
|
|
79
|
+
readonly BAD_GATEWAY: 502;
|
|
80
|
+
readonly SERVICE_UNAVAILABLE: 503;
|
|
81
|
+
readonly GATEWAY_TIMEOUT: 504;
|
|
82
|
+
readonly HTTP_VERSION_NOT_SUPPORTED: 505;
|
|
83
|
+
readonly VARIANT_ALSO_NEGOTIATES: 506;
|
|
84
|
+
readonly INSUFFICIENT_STORAGE: 507;
|
|
85
|
+
readonly LOOP_DETECTED: 508;
|
|
86
|
+
readonly NOT_EXTENDED: 510;
|
|
87
|
+
readonly NETWORK_AUTH_REQUIRED: 511;
|
|
88
|
+
};
|
|
89
|
+
type HttpStatusCode = (typeof HttpStatus)[keyof typeof HttpStatus];
|
|
90
|
+
/**
|
|
91
|
+
* Supported request body types after parsing
|
|
92
|
+
*/
|
|
93
|
+
type ParsedBody = string | Uint8Array | Record<string, unknown> | unknown[] | null | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* Supported response body types
|
|
96
|
+
*
|
|
97
|
+
* @remarks
|
|
98
|
+
* Uses structural stream interfaces to avoid coupling to any
|
|
99
|
+
* specific runtime (Node.js, Bun, Deno, Edge).
|
|
100
|
+
* Uint8Array and ArrayBuffer provide Web API compatibility.
|
|
101
|
+
*/
|
|
102
|
+
type ResponseBody = string | Uint8Array | ArrayBuffer | NodeStreamLike | WebStreamLike | Record<string, unknown> | unknown[] | null | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Structural interface for Node.js-compatible readable streams.
|
|
105
|
+
* Matches Node.js `Readable` and `IncomingMessage` without importing `@types/node`.
|
|
106
|
+
*/
|
|
107
|
+
interface NodeStreamLike {
|
|
108
|
+
pipe(destination: unknown): unknown;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Structural interface for Web API ReadableStreams.
|
|
112
|
+
* Matches the global `ReadableStream<Uint8Array>` without requiring DOM lib types.
|
|
113
|
+
*/
|
|
114
|
+
interface WebStreamLike {
|
|
115
|
+
getReader(): unknown;
|
|
116
|
+
readonly locked: boolean;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Raw HTTP objects - generic to support multiple runtimes
|
|
120
|
+
*
|
|
121
|
+
* For Node.js: RawHttp<IncomingMessage, ServerResponse>
|
|
122
|
+
* For Bun: RawHttp<Request, Response>
|
|
123
|
+
* For Edge: RawHttp<Request, ResponseInit>
|
|
124
|
+
*
|
|
125
|
+
* @typeParam TReq - Raw request type for the runtime
|
|
126
|
+
* @typeParam TRes - Raw response type for the runtime
|
|
127
|
+
*/
|
|
128
|
+
interface RawHttp<TReq = unknown, TRes = unknown> {
|
|
129
|
+
readonly req: TReq;
|
|
130
|
+
readonly res: TRes;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Common content types
|
|
134
|
+
*/
|
|
135
|
+
declare const ContentType: {
|
|
136
|
+
readonly JSON: "application/json";
|
|
137
|
+
readonly HTML: "text/html";
|
|
138
|
+
readonly TEXT: "text/plain";
|
|
139
|
+
readonly XML: "application/xml";
|
|
140
|
+
readonly FORM: "application/x-www-form-urlencoded";
|
|
141
|
+
readonly MULTIPART: "multipart/form-data";
|
|
142
|
+
readonly OCTET_STREAM: "application/octet-stream";
|
|
143
|
+
};
|
|
144
|
+
type ContentTypeValue = (typeof ContentType)[keyof typeof ContentType];
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @nextrush/types - Runtime Type Definitions
|
|
148
|
+
*
|
|
149
|
+
* Types for cross-runtime support in NextRush.
|
|
150
|
+
*
|
|
151
|
+
* @packageDocumentation
|
|
152
|
+
*/
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Supported JavaScript runtimes
|
|
156
|
+
*
|
|
157
|
+
* @remarks
|
|
158
|
+
* NextRush detects the runtime environment and exposes it via `ctx.runtime`.
|
|
159
|
+
* This enables runtime-specific optimizations while maintaining a unified API.
|
|
160
|
+
*/
|
|
161
|
+
type Runtime = 'node' | 'bun' | 'deno' | 'deno-deploy' | 'cloudflare-workers' | 'vercel-edge' | 'edge' | 'unknown';
|
|
162
|
+
/**
|
|
163
|
+
* Runtime feature capabilities
|
|
164
|
+
*
|
|
165
|
+
* @remarks
|
|
166
|
+
* Different runtimes have different feature sets. Use this to check
|
|
167
|
+
* what's available before using runtime-specific features.
|
|
168
|
+
*/
|
|
169
|
+
interface RuntimeCapabilities {
|
|
170
|
+
/** Supports Node.js streams (Readable, Writable) */
|
|
171
|
+
nodeStreams: boolean;
|
|
172
|
+
/** Supports Web Streams API (ReadableStream, WritableStream) */
|
|
173
|
+
webStreams: boolean;
|
|
174
|
+
/** Supports file system operations */
|
|
175
|
+
fileSystem: boolean;
|
|
176
|
+
/** Supports WebSocket */
|
|
177
|
+
webSocket: boolean;
|
|
178
|
+
/** Supports native fetch API */
|
|
179
|
+
fetch: boolean;
|
|
180
|
+
/** Supports crypto.subtle API */
|
|
181
|
+
cryptoSubtle: boolean;
|
|
182
|
+
/** Supports Web Workers / Worker Threads */
|
|
183
|
+
workers: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Runtime information object
|
|
187
|
+
*/
|
|
188
|
+
interface RuntimeInfo {
|
|
189
|
+
/** Runtime identifier */
|
|
190
|
+
runtime: Runtime;
|
|
191
|
+
/** Runtime version (if available) */
|
|
192
|
+
version: string | undefined;
|
|
193
|
+
/** Runtime capabilities */
|
|
194
|
+
capabilities: RuntimeCapabilities;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Body source interface for cross-runtime body reading
|
|
198
|
+
*
|
|
199
|
+
* @remarks
|
|
200
|
+
* Different runtimes have different APIs for reading request bodies:
|
|
201
|
+
* - Node.js: `IncomingMessage` stream with `on('data')` events
|
|
202
|
+
* - Bun/Deno/Edge: Web `Request` with `text()`, `arrayBuffer()` methods
|
|
203
|
+
*
|
|
204
|
+
* `BodySource` provides a unified interface for body parsers to work
|
|
205
|
+
* across all runtimes without runtime-specific code.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```typescript
|
|
209
|
+
* // Works on any runtime
|
|
210
|
+
* async function parseJson(bodySource: BodySource): Promise<unknown> {
|
|
211
|
+
* const text = await bodySource.text();
|
|
212
|
+
* return JSON.parse(text);
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
interface BodySource {
|
|
217
|
+
/**
|
|
218
|
+
* Read the body as a UTF-8 string
|
|
219
|
+
*
|
|
220
|
+
* @returns Promise resolving to the body as string
|
|
221
|
+
* @throws Error if body has already been consumed
|
|
222
|
+
*/
|
|
223
|
+
text(): Promise<string>;
|
|
224
|
+
/**
|
|
225
|
+
* Read the body as a Uint8Array buffer
|
|
226
|
+
*
|
|
227
|
+
* @returns Promise resolving to the body as Uint8Array
|
|
228
|
+
* @throws Error if body has already been consumed
|
|
229
|
+
*/
|
|
230
|
+
buffer(): Promise<Uint8Array>;
|
|
231
|
+
/**
|
|
232
|
+
* Read the body as JSON
|
|
233
|
+
*
|
|
234
|
+
* @returns Promise resolving to parsed JSON
|
|
235
|
+
* @throws BadRequestError if body is not valid JSON
|
|
236
|
+
* @throws Error if body has already been consumed
|
|
237
|
+
*/
|
|
238
|
+
json<T = unknown>(): Promise<T>;
|
|
239
|
+
/**
|
|
240
|
+
* Get the body as a stream
|
|
241
|
+
*
|
|
242
|
+
* @remarks
|
|
243
|
+
* Returns the native stream type for the runtime:
|
|
244
|
+
* - Node.js: `Readable`
|
|
245
|
+
* - Web API: `ReadableStream<Uint8Array>`
|
|
246
|
+
*
|
|
247
|
+
* @returns The underlying stream
|
|
248
|
+
*/
|
|
249
|
+
stream(): NodeStreamLike | WebStreamLike;
|
|
250
|
+
/**
|
|
251
|
+
* Whether the body has been consumed
|
|
252
|
+
*
|
|
253
|
+
* @remarks
|
|
254
|
+
* Most runtimes only allow the body to be read once. After calling
|
|
255
|
+
* `text()`, `buffer()`, `json()`, or `stream()`, subsequent calls
|
|
256
|
+
* may throw an error.
|
|
257
|
+
*/
|
|
258
|
+
readonly consumed: boolean;
|
|
259
|
+
/**
|
|
260
|
+
* Content length from headers (if available)
|
|
261
|
+
*
|
|
262
|
+
* @remarks
|
|
263
|
+
* This is the value from the Content-Length header, which may not
|
|
264
|
+
* match the actual body size for chunked transfers.
|
|
265
|
+
*/
|
|
266
|
+
readonly contentLength: number | undefined;
|
|
267
|
+
/**
|
|
268
|
+
* Content type from headers (if available)
|
|
269
|
+
*/
|
|
270
|
+
readonly contentType: string | undefined;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Options for creating a BodySource
|
|
274
|
+
*/
|
|
275
|
+
interface BodySourceOptions {
|
|
276
|
+
/** Maximum body size in bytes (default: 1MB) */
|
|
277
|
+
limit?: number;
|
|
278
|
+
/** Encoding for text() method (default: 'utf-8') */
|
|
279
|
+
encoding?: 'utf-8' | 'utf8' | 'ascii' | 'latin1' | 'iso-8859-1' | 'utf-16le' | 'utf-16be';
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @nextrush/types - Context Type Definitions
|
|
284
|
+
*
|
|
285
|
+
* The Context object is the heart of NextRush's request/response handling.
|
|
286
|
+
* It provides a unified interface for accessing request data and sending responses.
|
|
287
|
+
*
|
|
288
|
+
* Design Philosophy:
|
|
289
|
+
* - ctx.body = request body (INPUT)
|
|
290
|
+
* - ctx.json() = send JSON response (OUTPUT)
|
|
291
|
+
* - ctx.next() = call next middleware
|
|
292
|
+
*
|
|
293
|
+
* @packageDocumentation
|
|
294
|
+
*/
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Route parameters extracted from URL path
|
|
298
|
+
* Example: /users/:id -> { id: '123' }
|
|
299
|
+
*/
|
|
300
|
+
type RouteParams = Record<string, string>;
|
|
301
|
+
/**
|
|
302
|
+
* Query string parameters
|
|
303
|
+
* Example: ?page=1&limit=10 -> { page: '1', limit: '10' }
|
|
304
|
+
*/
|
|
305
|
+
type QueryParams = Record<string, string | string[] | undefined>;
|
|
306
|
+
/**
|
|
307
|
+
* Request-scoped state object
|
|
308
|
+
* Use this to pass data between middleware
|
|
309
|
+
*/
|
|
310
|
+
type ContextState = Record<string | symbol, unknown>;
|
|
311
|
+
/**
|
|
312
|
+
* The Context interface - core of NextRush request handling
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* app.get('/users/:id', async (ctx) => {
|
|
317
|
+
* // Request data (input)
|
|
318
|
+
* const { id } = ctx.params;
|
|
319
|
+
* const { name } = ctx.body;
|
|
320
|
+
*
|
|
321
|
+
* // Response (output)
|
|
322
|
+
* ctx.status = 200;
|
|
323
|
+
* ctx.json({ id, name });
|
|
324
|
+
* });
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
interface Context {
|
|
328
|
+
/**
|
|
329
|
+
* HTTP method (GET, POST, PUT, DELETE, etc.)
|
|
330
|
+
* @readonly
|
|
331
|
+
*/
|
|
332
|
+
readonly method: HttpMethod;
|
|
333
|
+
/**
|
|
334
|
+
* Full request URL including query string
|
|
335
|
+
* @example '/users/123?include=posts'
|
|
336
|
+
* @readonly
|
|
337
|
+
*/
|
|
338
|
+
readonly url: string;
|
|
339
|
+
/**
|
|
340
|
+
* Request path without query string
|
|
341
|
+
* @example '/users/123'
|
|
342
|
+
* @readonly
|
|
343
|
+
*/
|
|
344
|
+
readonly path: string;
|
|
345
|
+
/**
|
|
346
|
+
* Parsed query string parameters
|
|
347
|
+
* @example { include: 'posts', limit: '10' }
|
|
348
|
+
* @readonly
|
|
349
|
+
*/
|
|
350
|
+
readonly query: QueryParams;
|
|
351
|
+
/**
|
|
352
|
+
* Request headers (read-only)
|
|
353
|
+
* Use ctx.get() for case-insensitive access
|
|
354
|
+
* @readonly
|
|
355
|
+
*/
|
|
356
|
+
readonly headers: IncomingHeaders;
|
|
357
|
+
/**
|
|
358
|
+
* Client IP address
|
|
359
|
+
* @readonly
|
|
360
|
+
*/
|
|
361
|
+
readonly ip: string;
|
|
362
|
+
/**
|
|
363
|
+
* Parsed request body (input).
|
|
364
|
+
*
|
|
365
|
+
* Populated by body-parser middleware (json, urlencoded, multipart).
|
|
366
|
+
* This is the **request body**, not the response body.
|
|
367
|
+
* Use `ctx.json()`, `ctx.send()`, or `ctx.html()` to set response output.
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```typescript
|
|
371
|
+
* app.post('/users', async (ctx) => {
|
|
372
|
+
* const { name, email } = ctx.body as CreateUserDto;
|
|
373
|
+
* });
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
body: unknown;
|
|
377
|
+
/**
|
|
378
|
+
* Route parameters from URL path
|
|
379
|
+
*
|
|
380
|
+
* @example
|
|
381
|
+
* ```typescript
|
|
382
|
+
* // Route: /users/:id/posts/:postId
|
|
383
|
+
* // URL: /users/123/posts/456
|
|
384
|
+
* ctx.params // { id: '123', postId: '456' }
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
params: RouteParams;
|
|
388
|
+
/**
|
|
389
|
+
* HTTP response status code
|
|
390
|
+
* @default 200
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* ctx.status = 201; // Created
|
|
395
|
+
* ctx.status = 404; // Not Found
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
status: number;
|
|
399
|
+
/**
|
|
400
|
+
* Send JSON response
|
|
401
|
+
* Automatically sets Content-Type: application/json
|
|
402
|
+
*
|
|
403
|
+
* @param data - Data to serialize as JSON
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* ctx.json({ users: [] });
|
|
408
|
+
* ctx.json({ error: 'Not found' });
|
|
409
|
+
* ```
|
|
410
|
+
*/
|
|
411
|
+
json(data: unknown): void;
|
|
412
|
+
/**
|
|
413
|
+
* Send response (text, buffer, or stream)
|
|
414
|
+
* Automatically detects content type
|
|
415
|
+
*
|
|
416
|
+
* @param data - Response data
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```typescript
|
|
420
|
+
* ctx.send('Hello World');
|
|
421
|
+
* ctx.send(Buffer.from('binary data'));
|
|
422
|
+
* ctx.send(fileStream);
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
send(data: ResponseBody): void;
|
|
426
|
+
/**
|
|
427
|
+
* Send HTML response
|
|
428
|
+
* Automatically sets Content-Type: text/html
|
|
429
|
+
*
|
|
430
|
+
* @param content - HTML string
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```typescript
|
|
434
|
+
* ctx.html('<h1>Hello World</h1>');
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
html(content: string): void;
|
|
438
|
+
/**
|
|
439
|
+
* Redirect to another URL
|
|
440
|
+
*
|
|
441
|
+
* @param url - Target URL
|
|
442
|
+
* @param status - HTTP status code (default: 302)
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* ```typescript
|
|
446
|
+
* ctx.redirect('/login');
|
|
447
|
+
* ctx.redirect('/new-page', 301); // Permanent redirect
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
redirect(url: string, status?: number): void;
|
|
451
|
+
/**
|
|
452
|
+
* Throw an HTTP error - stops execution and triggers error handler
|
|
453
|
+
*
|
|
454
|
+
* @param status - HTTP status code
|
|
455
|
+
* @param message - Error message (optional)
|
|
456
|
+
* @throws HttpError
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* ctx.throw(404, 'User not found');
|
|
461
|
+
* ctx.throw(401); // Uses default message "Unauthorized"
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
throw(status: number, message?: string): never;
|
|
465
|
+
/**
|
|
466
|
+
* Assert a condition, throw if falsy
|
|
467
|
+
*
|
|
468
|
+
* @param condition - Condition to check
|
|
469
|
+
* @param status - HTTP status code if assertion fails
|
|
470
|
+
* @param message - Error message (optional)
|
|
471
|
+
* @throws HttpError if condition is falsy
|
|
472
|
+
*
|
|
473
|
+
* @example
|
|
474
|
+
* ```typescript
|
|
475
|
+
* ctx.assert(user, 404, 'User not found');
|
|
476
|
+
* ctx.assert(user.isAdmin, 403, 'Admin access required');
|
|
477
|
+
* ctx.assert(ctx.body, 400); // Uses default "Bad Request"
|
|
478
|
+
* ```
|
|
479
|
+
*/
|
|
480
|
+
assert(condition: unknown, status: number, message?: string): asserts condition;
|
|
481
|
+
/**
|
|
482
|
+
* Set response header
|
|
483
|
+
*
|
|
484
|
+
* @param field - Header name
|
|
485
|
+
* @param value - Header value (string, number, or array for multi-value headers like Set-Cookie)
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* ```typescript
|
|
489
|
+
* ctx.set('X-Request-Id', '123');
|
|
490
|
+
* ctx.set('Cache-Control', 'no-cache');
|
|
491
|
+
* ctx.set('Set-Cookie', ['a=1; Path=/', 'b=2; Path=/']);
|
|
492
|
+
* ```
|
|
493
|
+
*/
|
|
494
|
+
set(field: string, value: string | number | string[]): void;
|
|
495
|
+
/**
|
|
496
|
+
* Get request header (case-insensitive)
|
|
497
|
+
*
|
|
498
|
+
* @param field - Header name
|
|
499
|
+
* @returns Header value or undefined
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* const auth = ctx.get('Authorization');
|
|
504
|
+
* const contentType = ctx.get('content-type');
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
get(field: string): string | undefined;
|
|
508
|
+
/**
|
|
509
|
+
* Call the next middleware in the chain
|
|
510
|
+
* Modern syntax: ctx.next() instead of next()
|
|
511
|
+
*
|
|
512
|
+
* @example
|
|
513
|
+
* ```typescript
|
|
514
|
+
* app.use(async (ctx) => {
|
|
515
|
+
* console.log('Before');
|
|
516
|
+
* await ctx.next();
|
|
517
|
+
* console.log('After');
|
|
518
|
+
* });
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
next(): Promise<void>;
|
|
522
|
+
/**
|
|
523
|
+
* Set the next middleware function
|
|
524
|
+
* @internal Used by middleware compose to wire up ctx.next()
|
|
525
|
+
*/
|
|
526
|
+
setNext?(fn: Next): void;
|
|
527
|
+
/**
|
|
528
|
+
* Whether a response has already been sent
|
|
529
|
+
*
|
|
530
|
+
* @remarks
|
|
531
|
+
* Useful for middleware that needs to check if downstream
|
|
532
|
+
* middleware has already sent a response.
|
|
533
|
+
*/
|
|
534
|
+
readonly responded: boolean;
|
|
535
|
+
/**
|
|
536
|
+
* Request-scoped state object
|
|
537
|
+
* Use to pass data between middleware
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* // Auth middleware
|
|
542
|
+
* app.use(async (ctx) => {
|
|
543
|
+
* ctx.state.user = await validateToken(ctx.get('Authorization'));
|
|
544
|
+
* await ctx.next();
|
|
545
|
+
* });
|
|
546
|
+
*
|
|
547
|
+
* // Route handler
|
|
548
|
+
* app.get('/profile', (ctx) => {
|
|
549
|
+
* const user = ctx.state.user;
|
|
550
|
+
* });
|
|
551
|
+
* ```
|
|
552
|
+
*/
|
|
553
|
+
state: ContextState;
|
|
554
|
+
/**
|
|
555
|
+
* Raw HTTP objects (platform-specific)
|
|
556
|
+
* Escape hatch for advanced use cases
|
|
557
|
+
*
|
|
558
|
+
* @remarks
|
|
559
|
+
* The type of `raw` depends on the adapter:
|
|
560
|
+
* - Node.js: `{ req: IncomingMessage, res: ServerResponse }`
|
|
561
|
+
* - Bun/Deno/Edge: `{ req: Request, res: Response | undefined }`
|
|
562
|
+
*
|
|
563
|
+
* @example
|
|
564
|
+
* ```typescript
|
|
565
|
+
* // Node.js adapter
|
|
566
|
+
* ctx.raw.req.socket.remoteAddress;
|
|
567
|
+
*
|
|
568
|
+
* // Web API adapter (Bun/Deno/Edge)
|
|
569
|
+
* ctx.raw.req.headers.get('authorization');
|
|
570
|
+
* ```
|
|
571
|
+
*/
|
|
572
|
+
readonly raw: RawHttp;
|
|
573
|
+
/**
|
|
574
|
+
* Current JavaScript runtime
|
|
575
|
+
*
|
|
576
|
+
* @remarks
|
|
577
|
+
* Useful for runtime-specific optimizations or conditional logic.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* ```typescript
|
|
581
|
+
* if (ctx.runtime === 'bun') {
|
|
582
|
+
* // Bun-specific optimization
|
|
583
|
+
* }
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
readonly runtime: Runtime;
|
|
587
|
+
/**
|
|
588
|
+
* Body source for cross-runtime body reading
|
|
589
|
+
*
|
|
590
|
+
* @remarks
|
|
591
|
+
* Used by body parsers to read request bodies in a runtime-agnostic way.
|
|
592
|
+
* Most applications should use `ctx.body` (set by body-parser middleware)
|
|
593
|
+
* instead of accessing `bodySource` directly.
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```typescript
|
|
597
|
+
* // In a custom body parser
|
|
598
|
+
* const text = await ctx.bodySource.text();
|
|
599
|
+
* const json = JSON.parse(text);
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
readonly bodySource: BodySource;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Next function type (traditional Koa-style)
|
|
606
|
+
*/
|
|
607
|
+
type Next = () => Promise<void>;
|
|
608
|
+
/**
|
|
609
|
+
* Middleware function type
|
|
610
|
+
* Supports both modern and traditional syntax
|
|
611
|
+
*
|
|
612
|
+
* Modern syntax (ctx.next()):
|
|
613
|
+
* ```typescript
|
|
614
|
+
* app.use(async (ctx) => {
|
|
615
|
+
* await ctx.next();
|
|
616
|
+
* });
|
|
617
|
+
* ```
|
|
618
|
+
*
|
|
619
|
+
* Traditional syntax (next parameter):
|
|
620
|
+
* ```typescript
|
|
621
|
+
* app.use(async (ctx, next) => {
|
|
622
|
+
* await next();
|
|
623
|
+
* });
|
|
624
|
+
* ```
|
|
625
|
+
*/
|
|
626
|
+
type Middleware = (ctx: Context, next: Next) => void | Promise<void>;
|
|
627
|
+
/**
|
|
628
|
+
* Route handler type (alias for middleware)
|
|
629
|
+
*/
|
|
630
|
+
type RouteHandler = Middleware;
|
|
631
|
+
/**
|
|
632
|
+
* Options for creating a new context
|
|
633
|
+
*/
|
|
634
|
+
interface ContextOptions {
|
|
635
|
+
/** HTTP method */
|
|
636
|
+
method: HttpMethod;
|
|
637
|
+
/** Request URL */
|
|
638
|
+
url: string;
|
|
639
|
+
/** Request headers */
|
|
640
|
+
headers?: IncomingHeaders;
|
|
641
|
+
/** Client IP */
|
|
642
|
+
ip?: string;
|
|
643
|
+
/** Raw HTTP objects */
|
|
644
|
+
raw: RawHttp;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* @nextrush/types - Plugin Type Definitions
|
|
649
|
+
*
|
|
650
|
+
* Plugins are the extension mechanism for NextRush.
|
|
651
|
+
* They allow adding functionality without bloating the core.
|
|
652
|
+
*
|
|
653
|
+
* @packageDocumentation
|
|
654
|
+
*/
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Minimal Application interface for plugin registration
|
|
658
|
+
* The full Application class is in @nextrush/core
|
|
659
|
+
*/
|
|
660
|
+
interface ApplicationLike {
|
|
661
|
+
/**
|
|
662
|
+
* Register middleware
|
|
663
|
+
*/
|
|
664
|
+
use(middleware: Middleware): this;
|
|
665
|
+
/**
|
|
666
|
+
* Get installed plugin by name
|
|
667
|
+
*/
|
|
668
|
+
getPlugin<T extends Plugin>(name: string): T | undefined;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Plugin interface for extending NextRush
|
|
672
|
+
*
|
|
673
|
+
* Plugins are installed via app.plugin() and can:
|
|
674
|
+
* - Add middleware
|
|
675
|
+
* - Extend context
|
|
676
|
+
* - Add lifecycle hooks
|
|
677
|
+
*
|
|
678
|
+
* @example
|
|
679
|
+
* ```typescript
|
|
680
|
+
* class LoggerPlugin implements Plugin {
|
|
681
|
+
* readonly name = 'logger';
|
|
682
|
+
* readonly version = '1.0.0';
|
|
683
|
+
*
|
|
684
|
+
* install(app: ApplicationLike) {
|
|
685
|
+
* app.use(async (ctx, next) => {
|
|
686
|
+
* const start = Date.now();
|
|
687
|
+
* await next();
|
|
688
|
+
* console.log(`${ctx.method} ${ctx.path} - ${Date.now() - start}ms`);
|
|
689
|
+
* });
|
|
690
|
+
* }
|
|
691
|
+
* }
|
|
692
|
+
* ```
|
|
693
|
+
*/
|
|
694
|
+
interface Plugin {
|
|
695
|
+
/**
|
|
696
|
+
* Unique plugin name
|
|
697
|
+
* Used for identification and getPlugin() lookup
|
|
698
|
+
*/
|
|
699
|
+
readonly name: string;
|
|
700
|
+
/**
|
|
701
|
+
* Plugin version (optional)
|
|
702
|
+
* Follows semver format
|
|
703
|
+
*/
|
|
704
|
+
readonly version?: string;
|
|
705
|
+
/**
|
|
706
|
+
* Install plugin into application
|
|
707
|
+
* Called when app.plugin() is invoked
|
|
708
|
+
*
|
|
709
|
+
* @param app - Application instance
|
|
710
|
+
*/
|
|
711
|
+
install(app: ApplicationLike): void | Promise<void>;
|
|
712
|
+
/**
|
|
713
|
+
* Cleanup when application shuts down (optional)
|
|
714
|
+
* Called during graceful shutdown
|
|
715
|
+
*/
|
|
716
|
+
destroy?(): void | Promise<void>;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Extended plugin interface with lifecycle hooks
|
|
720
|
+
* For advanced plugins that need deeper integration
|
|
721
|
+
*/
|
|
722
|
+
interface PluginWithHooks extends Plugin {
|
|
723
|
+
/**
|
|
724
|
+
* Called before each request
|
|
725
|
+
*/
|
|
726
|
+
onRequest?(ctx: Context): void | Promise<void>;
|
|
727
|
+
/**
|
|
728
|
+
* Called after each response
|
|
729
|
+
*/
|
|
730
|
+
onResponse?(ctx: Context): void | Promise<void>;
|
|
731
|
+
/**
|
|
732
|
+
* Called when an error occurs
|
|
733
|
+
*/
|
|
734
|
+
onError?(error: Error, ctx: Context): void | Promise<void>;
|
|
735
|
+
/**
|
|
736
|
+
* Extend the context object
|
|
737
|
+
* Add custom properties or methods
|
|
738
|
+
*/
|
|
739
|
+
extendContext?(ctx: Context): void;
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Plugin factory function type
|
|
743
|
+
* Allows creating plugins with options
|
|
744
|
+
*
|
|
745
|
+
* @example
|
|
746
|
+
* ```typescript
|
|
747
|
+
* const logger = createLoggerPlugin({ level: 'info' });
|
|
748
|
+
* app.plugin(logger);
|
|
749
|
+
* ```
|
|
750
|
+
*/
|
|
751
|
+
type PluginFactory<TOptions = unknown> = (options?: TOptions) => Plugin;
|
|
752
|
+
/**
|
|
753
|
+
* Plugin metadata for registration and discovery
|
|
754
|
+
*/
|
|
755
|
+
interface PluginMeta {
|
|
756
|
+
/** Plugin name */
|
|
757
|
+
name: string;
|
|
758
|
+
/** Plugin version */
|
|
759
|
+
version: string;
|
|
760
|
+
/** Plugin description */
|
|
761
|
+
description?: string;
|
|
762
|
+
/** Plugin author */
|
|
763
|
+
author?: string;
|
|
764
|
+
/** Plugin repository URL */
|
|
765
|
+
repository?: string;
|
|
766
|
+
/** Plugin dependencies (other plugin names) */
|
|
767
|
+
dependencies?: string[];
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* @nextrush/types - Router Type Definitions
|
|
772
|
+
*
|
|
773
|
+
* Types for the NextRush router system.
|
|
774
|
+
* The router uses a radix tree for efficient route matching.
|
|
775
|
+
*
|
|
776
|
+
* @packageDocumentation
|
|
777
|
+
*/
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Route definition
|
|
781
|
+
*/
|
|
782
|
+
interface Route {
|
|
783
|
+
/** HTTP method */
|
|
784
|
+
method: HttpMethod;
|
|
785
|
+
/** Route path pattern */
|
|
786
|
+
path: string;
|
|
787
|
+
/** Route handler */
|
|
788
|
+
handler: RouteHandler;
|
|
789
|
+
/** Optional route-level middleware */
|
|
790
|
+
middleware?: Middleware[];
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Matched route result
|
|
794
|
+
*/
|
|
795
|
+
interface RouteMatch {
|
|
796
|
+
/** Matched handler */
|
|
797
|
+
handler: RouteHandler;
|
|
798
|
+
/** Extracted route parameters */
|
|
799
|
+
params: Record<string, string>;
|
|
800
|
+
/** Router-level middleware stack (route-specific middleware is in the executor) */
|
|
801
|
+
middleware: Middleware[];
|
|
802
|
+
/** Pre-compiled executor for fast dispatch (internal) */
|
|
803
|
+
executor?: (ctx: Context) => Promise<void>;
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Router interface for route registration
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* ```typescript
|
|
810
|
+
* const router = createRouter();
|
|
811
|
+
*
|
|
812
|
+
* router.get('/users', listUsers);
|
|
813
|
+
* router.get('/users/:id', getUser);
|
|
814
|
+
* router.post('/users', createUser);
|
|
815
|
+
*
|
|
816
|
+
* app.use(router.routes());
|
|
817
|
+
* ```
|
|
818
|
+
*/
|
|
819
|
+
interface Router {
|
|
820
|
+
/**
|
|
821
|
+
* Register a GET route
|
|
822
|
+
*/
|
|
823
|
+
get(path: string, ...handlers: RouteHandler[]): this;
|
|
824
|
+
/**
|
|
825
|
+
* Register a POST route
|
|
826
|
+
*/
|
|
827
|
+
post(path: string, ...handlers: RouteHandler[]): this;
|
|
828
|
+
/**
|
|
829
|
+
* Register a PUT route
|
|
830
|
+
*/
|
|
831
|
+
put(path: string, ...handlers: RouteHandler[]): this;
|
|
832
|
+
/**
|
|
833
|
+
* Register a DELETE route
|
|
834
|
+
*/
|
|
835
|
+
delete(path: string, ...handlers: RouteHandler[]): this;
|
|
836
|
+
/**
|
|
837
|
+
* Register a PATCH route
|
|
838
|
+
*/
|
|
839
|
+
patch(path: string, ...handlers: RouteHandler[]): this;
|
|
840
|
+
/**
|
|
841
|
+
* Register a HEAD route
|
|
842
|
+
*/
|
|
843
|
+
head(path: string, ...handlers: RouteHandler[]): this;
|
|
844
|
+
/**
|
|
845
|
+
* Register an OPTIONS route
|
|
846
|
+
*/
|
|
847
|
+
options(path: string, ...handlers: RouteHandler[]): this;
|
|
848
|
+
/**
|
|
849
|
+
* Register a route for any HTTP method
|
|
850
|
+
*/
|
|
851
|
+
all(path: string, ...handlers: RouteHandler[]): this;
|
|
852
|
+
/**
|
|
853
|
+
* Register a route for specific method
|
|
854
|
+
*/
|
|
855
|
+
route(method: HttpMethod, path: string, ...handlers: RouteHandler[]): this;
|
|
856
|
+
/**
|
|
857
|
+
* Mount router middleware
|
|
858
|
+
*/
|
|
859
|
+
use(path: string, router: Router): this;
|
|
860
|
+
use(middleware: Middleware): this;
|
|
861
|
+
/**
|
|
862
|
+
* Register a redirect from one path to another
|
|
863
|
+
*
|
|
864
|
+
* @param from - Source path to redirect from
|
|
865
|
+
* @param to - Target path or URL to redirect to
|
|
866
|
+
* @param status - HTTP status code (default: 301)
|
|
867
|
+
*/
|
|
868
|
+
redirect(from: string, to: string, status?: 301 | 302 | 303 | 307 | 308): this;
|
|
869
|
+
/**
|
|
870
|
+
* Get routes middleware function
|
|
871
|
+
* Mount this on the application
|
|
872
|
+
*/
|
|
873
|
+
routes(): Middleware;
|
|
874
|
+
/**
|
|
875
|
+
* Match a route
|
|
876
|
+
*/
|
|
877
|
+
match(method: HttpMethod, path: string): RouteMatch | null;
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* Router configuration options
|
|
881
|
+
*/
|
|
882
|
+
interface RouterOptions {
|
|
883
|
+
/**
|
|
884
|
+
* Prefix for all routes
|
|
885
|
+
* @example '/api/v1'
|
|
886
|
+
*/
|
|
887
|
+
prefix?: string;
|
|
888
|
+
/**
|
|
889
|
+
* Whether to enable case-sensitive routing
|
|
890
|
+
* @default false
|
|
891
|
+
*/
|
|
892
|
+
caseSensitive?: boolean;
|
|
893
|
+
/**
|
|
894
|
+
* Whether to enable strict routing (trailing slashes matter)
|
|
895
|
+
* @default false
|
|
896
|
+
*/
|
|
897
|
+
strict?: boolean;
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Supported route pattern types
|
|
901
|
+
*/
|
|
902
|
+
type RoutePattern = `/${string}` | `${string}/:${string}` | `${string}/*` | `${string}/:${string}/*`;
|
|
903
|
+
/**
|
|
904
|
+
* Route parameter definition
|
|
905
|
+
*/
|
|
906
|
+
interface RouteParam {
|
|
907
|
+
/** Parameter name (without colon) */
|
|
908
|
+
name: string;
|
|
909
|
+
/** Whether the parameter is optional */
|
|
910
|
+
optional?: boolean;
|
|
911
|
+
/** Regex pattern for validation */
|
|
912
|
+
pattern?: RegExp;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
export { type ApplicationLike, type BodySource, type BodySourceOptions, type CommonHttpMethod, ContentType, type ContentTypeValue, type Context, type ContextOptions, type ContextState, HTTP_METHODS, type HttpMethod, HttpStatus, type HttpStatusCode, type IncomingHeaders, type Middleware, type Next, type NodeStreamLike, type OutgoingHeaders, type ParsedBody, type Plugin, type PluginFactory, type PluginMeta, type PluginWithHooks, type QueryParams, type RawHttp, type ResponseBody, type Route, type RouteHandler, type RouteMatch, type RouteParam, type RouteParams, type RoutePattern, type Router, type RouterOptions, type Runtime, type RuntimeCapabilities, type RuntimeInfo, type WebStreamLike };
|