@nextrush/core 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.
@@ -0,0 +1,388 @@
1
+ import { Middleware, Context, Plugin, Next } from '@nextrush/types';
2
+ export { ContentType, Context, ContextState, HttpMethod, HttpStatus, HttpStatusCode, Middleware, Next, Plugin, PluginWithHooks, QueryParams, RouteHandler, RouteParams } from '@nextrush/types';
3
+ export { BadRequestError, ForbiddenError, HttpError, InternalServerError, NextRushError, NotFoundError, UnauthorizedError, createError as createHttpError } from '@nextrush/errors';
4
+
5
+ /**
6
+ * @nextrush/core - Application Class
7
+ *
8
+ * The Application class is the main entry point for NextRush.
9
+ * It manages middleware registration and plugin installation.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+
14
+ /**
15
+ * Error handler function type
16
+ */
17
+ type ErrorHandler = (error: Error, ctx: Context) => void | Promise<void>;
18
+ /**
19
+ * Logger interface for pluggable logging
20
+ */
21
+ interface Logger {
22
+ error(...args: unknown[]): void;
23
+ warn(...args: unknown[]): void;
24
+ info(...args: unknown[]): void;
25
+ debug(...args: unknown[]): void;
26
+ }
27
+ /**
28
+ * Application options
29
+ */
30
+ interface ApplicationOptions {
31
+ /**
32
+ * Environment mode
33
+ * @default 'development'
34
+ */
35
+ env?: 'development' | 'production' | 'test';
36
+ /**
37
+ * Whether to use proxy headers (X-Forwarded-For, etc.)
38
+ * @default false
39
+ */
40
+ proxy?: boolean;
41
+ /**
42
+ * Custom logger. Defaults to no-op (silent).
43
+ *
44
+ * @remarks
45
+ * Pass `console` for quick development logging:
46
+ * ```ts
47
+ * const app = createApp({ logger: console });
48
+ * ```
49
+ *
50
+ * For production, provide a structured logger (e.g. pino, winston)
51
+ * that implements the {@link Logger} interface.
52
+ *
53
+ * Adapters and internal hooks use `app.logger` — configuring it here
54
+ * ensures consistent logging across the framework.
55
+ */
56
+ logger?: Logger;
57
+ }
58
+ /**
59
+ * Listener callback type
60
+ */
61
+ type ListenCallback = () => void;
62
+ /**
63
+ * Routable interface - any object with routes() method
64
+ * This allows mounting Router instances without circular dependency
65
+ */
66
+ interface Routable {
67
+ routes(): Middleware;
68
+ }
69
+ /**
70
+ * The Application class
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const app = createApp();
75
+ *
76
+ * app.use(async (ctx) => {
77
+ * ctx.json({ message: 'Hello World' });
78
+ * });
79
+ *
80
+ * // Mount with an adapter
81
+ * listen(app, { port: 3000 });
82
+ * ```
83
+ */
84
+ declare class Application {
85
+ /**
86
+ * Middleware stack
87
+ */
88
+ private readonly middlewareStack;
89
+ /**
90
+ * Installed plugins
91
+ */
92
+ private readonly plugins;
93
+ /**
94
+ * Custom error handler
95
+ */
96
+ private _errorHandler;
97
+ /**
98
+ * Pluggable logger
99
+ */
100
+ readonly logger: Logger;
101
+ /**
102
+ * Application options
103
+ */
104
+ readonly options: ApplicationOptions;
105
+ /**
106
+ * Whether the app is running
107
+ */
108
+ private _isRunning;
109
+ constructor(options?: ApplicationOptions);
110
+ /**
111
+ * Check if running in production
112
+ */
113
+ get isProduction(): boolean;
114
+ /**
115
+ * Check if app is running
116
+ */
117
+ get isRunning(): boolean;
118
+ /**
119
+ * Get middleware count
120
+ */
121
+ get middlewareCount(): number;
122
+ /**
123
+ * Throws if the app is already running.
124
+ * Prevents configuration mutations after start().
125
+ */
126
+ private assertNotRunning;
127
+ /**
128
+ * Register middleware function(s)
129
+ *
130
+ * @param middleware - Middleware function or array of middleware
131
+ * @returns this for chaining
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * // Single middleware
136
+ * app.use(async (ctx) => {
137
+ * console.log(ctx.method, ctx.path);
138
+ * await ctx.next();
139
+ * });
140
+ *
141
+ * // Multiple middleware
142
+ * app.use(cors(), helmet(), json());
143
+ * ```
144
+ */
145
+ use(...middleware: Middleware[]): this;
146
+ /**
147
+ * Mount a router at a path prefix
148
+ *
149
+ * This is the Hono-style API for mounting routers directly on the app.
150
+ * The router's routes will only match requests that start with the given prefix.
151
+ *
152
+ * @param path - Path prefix for the router (e.g., '/api/users')
153
+ * @param router - Router instance to mount
154
+ * @returns this for chaining
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * // Create modular routers
159
+ * const users = createRouter();
160
+ * users.get('/', listUsers);
161
+ * users.get('/:id', getUser);
162
+ * users.post('/', createUser);
163
+ *
164
+ * const posts = createRouter();
165
+ * posts.get('/', listPosts);
166
+ * posts.get('/:id', getPost);
167
+ *
168
+ * // Mount directly on app - clean like Hono!
169
+ * const app = createApp();
170
+ * app.route('/api/users', users);
171
+ * app.route('/api/posts', posts);
172
+ *
173
+ * listen(app, 3000);
174
+ * ```
175
+ */
176
+ route(path: string, router: Routable): this;
177
+ /**
178
+ * Set the application error handler.
179
+ *
180
+ * Replaces any previously set handler. Only one error handler is active
181
+ * at a time. For additive error handling, compose logic within a single
182
+ * handler or use the `errorHandler()` middleware from `@nextrush/errors`.
183
+ *
184
+ * @param handler - Error handler function
185
+ * @returns this for chaining
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * app.setErrorHandler((error, ctx) => {
190
+ * console.error('Request failed:', error);
191
+ *
192
+ * if (error instanceof ValidationError) {
193
+ * ctx.status = 400;
194
+ * ctx.json({ error: error.message, details: error.details });
195
+ * return;
196
+ * }
197
+ *
198
+ * ctx.status = 500;
199
+ * ctx.json({ error: 'Internal Server Error' });
200
+ * });
201
+ * ```
202
+ */
203
+ setErrorHandler(handler: ErrorHandler): this;
204
+ /**
205
+ * Set custom error handler.
206
+ *
207
+ * @param handler - Error handler function
208
+ * @returns this for chaining
209
+ *
210
+ * @deprecated Use `setErrorHandler()` instead — `onError()` implies
211
+ * event subscription (additive), but this is a setter (replaces).
212
+ */
213
+ onError(handler: ErrorHandler): this;
214
+ /**
215
+ * Install a plugin.
216
+ *
217
+ * Handles both sync and async `install()` methods automatically.
218
+ * Returns `this` for sync plugins and `Promise<this>` for async ones.
219
+ *
220
+ * @param plugin - Plugin to install
221
+ * @returns this (sync) or Promise<this> (async)
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // Sync plugin
226
+ * app.plugin(loggerPlugin({ level: 'info' }));
227
+ *
228
+ * // Async plugin — just await it
229
+ * await app.plugin(databasePlugin({ uri: '...' }));
230
+ * ```
231
+ */
232
+ plugin(plugin: Plugin): this | Promise<this>;
233
+ /**
234
+ * Install a plugin asynchronously.
235
+ *
236
+ * @param plugin - Plugin to install
237
+ * @returns Promise that resolves when plugin is installed
238
+ *
239
+ * @deprecated Use `plugin()` instead — it handles both sync and async
240
+ * plugins automatically.
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * await app.plugin(new DatabasePlugin({ connectionString: '...' }));
245
+ * ```
246
+ */
247
+ pluginAsync(plugin: Plugin): Promise<this>;
248
+ /**
249
+ * Get an installed plugin by name
250
+ *
251
+ * @param name - Plugin name
252
+ * @returns Plugin instance or undefined
253
+ */
254
+ getPlugin<T extends Plugin>(name: string): T | undefined;
255
+ /**
256
+ * Check if a plugin is installed
257
+ */
258
+ hasPlugin(name: string): boolean;
259
+ /**
260
+ * Create the request handler callback
261
+ *
262
+ * This is the function that adapters call to handle each request.
263
+ * It composes all middleware and returns a function that processes context.
264
+ *
265
+ * **Important:** The middleware stack is snapshot at call time.
266
+ * Middleware or plugins added after `callback()` will NOT take effect
267
+ * on the returned handler. Call `callback()` again to get a
268
+ * handler that includes subsequent registrations.
269
+ *
270
+ * @returns Request handler function
271
+ */
272
+ callback(): (ctx: Context) => Promise<void>;
273
+ /**
274
+ * Handle errors - uses custom handler if set, otherwise default
275
+ */
276
+ private handleError;
277
+ /**
278
+ * Default error handler
279
+ *
280
+ * Uses the `expose` flag on errors (set by HttpError/NextRushError)
281
+ * to decide whether to include the error message in the response.
282
+ * 4xx errors expose messages by default; 5xx errors never do.
283
+ * This prevents leaking internal details (paths, SQL, stack info)
284
+ * regardless of environment.
285
+ */
286
+ private defaultErrorHandler;
287
+ /**
288
+ * Mark app as running and freeze configuration.
289
+ *
290
+ * Called by adapters when the server starts listening. After this call,
291
+ * `use()`, `route()`, `plugin()`, and `pluginAsync()` will throw.
292
+ * This prevents unsafe middleware mutations while requests are in flight.
293
+ *
294
+ * To re-enable registration (e.g. for testing), call `close()` first.
295
+ */
296
+ start(): void;
297
+ /**
298
+ * Graceful shutdown
299
+ * Destroys all plugins that have a destroy method.
300
+ * Uses Promise.allSettled to ensure all plugins are destroyed
301
+ * even if some throw errors.
302
+ *
303
+ * @returns Array of errors from plugins that failed to destroy (empty on success)
304
+ */
305
+ close(): Promise<Error[]>;
306
+ }
307
+ /**
308
+ * Create a new Application instance
309
+ *
310
+ * @param options - Application options
311
+ * @returns New Application instance
312
+ *
313
+ * @example
314
+ * ```typescript
315
+ * const app = createApp();
316
+ * const app = createApp({ env: 'production' });
317
+ * ```
318
+ */
319
+ declare function createApp(options?: ApplicationOptions): Application;
320
+
321
+ /**
322
+ * @nextrush/core - Middleware Composition
323
+ *
324
+ * Koa-style middleware composition with async/await support.
325
+ * This is the heart of the middleware pipeline.
326
+ *
327
+ * @packageDocumentation
328
+ */
329
+
330
+ /**
331
+ * Composed middleware handler - can be called with just context
332
+ */
333
+ type ComposedMiddleware = (ctx: Context, next?: Next) => Promise<void>;
334
+ /**
335
+ * Options for middleware composition
336
+ */
337
+ interface ComposeOptions {
338
+ /**
339
+ * Warn when a middleware sends a response AND calls next().
340
+ * Enabled by default in non-production environments.
341
+ * @default process.env.NODE_ENV !== 'production'
342
+ */
343
+ warnDoubleResponse?: boolean;
344
+ }
345
+ /**
346
+ * Compose multiple middleware functions into a single middleware.
347
+ *
348
+ * Executes middleware in order, each middleware can call `await next()`
349
+ * to pass control to the next middleware and wait for it to complete.
350
+ *
351
+ * @param middleware - Array of middleware functions to compose
352
+ * @param options - Composition options
353
+ * @returns Single composed middleware function
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * const composed = compose([
358
+ * async (ctx, next) => {
359
+ * console.log('1 - before');
360
+ * await next();
361
+ * console.log('1 - after');
362
+ * },
363
+ * async (ctx, next) => {
364
+ * console.log('2 - before');
365
+ * await next();
366
+ * console.log('2 - after');
367
+ * },
368
+ * ]);
369
+ *
370
+ * // Output:
371
+ * // 1 - before
372
+ * // 2 - before
373
+ * // 2 - after
374
+ * // 1 - after
375
+ * ```
376
+ */
377
+ declare function compose(middleware: Middleware[], options?: ComposeOptions): ComposedMiddleware;
378
+ /**
379
+ * Check if a function is a valid middleware
380
+ */
381
+ declare function isMiddleware(fn: unknown): fn is Middleware;
382
+ /**
383
+ * Flatten nested middleware arrays with type validation.
384
+ * Uses bounded depth (10 levels) to prevent V8 deoptimization on deeply nested arrays.
385
+ */
386
+ declare function flattenMiddleware(arr: (Middleware | Middleware[])[]): Middleware[];
387
+
388
+ export { Application, type ApplicationOptions, type ComposeOptions, type ComposedMiddleware, type ErrorHandler, type ListenCallback, type Logger, type Routable, compose, createApp, flattenMiddleware, isMiddleware };