@nextrush/core 4.0.0-beta.0 → 4.0.0-beta.2
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/dist/index.d.ts +40 -7
- package/dist/index.js +77 -47
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger, Router, Container, Middleware, RouteEntry, Context, Extension, Next } from '@nextrush/types';
|
|
1
|
+
import { Logger, ProxyTrust, Router, Container, Middleware, RouteEntry, Context, Extension, Next } from '@nextrush/types';
|
|
2
2
|
export { ContentType, Context, ContextState, Extension, ExtensionContext, ExtensionHost, HttpMethod, HttpStatus, HttpStatusCode, Logger, Middleware, Next, QueryParams, RouteEntry, RouteHandler, RouteParams, Router } from '@nextrush/types';
|
|
3
3
|
export { BadRequestError, ForbiddenError, HttpError, InternalServerError, NextRushError, NotFoundError, UnauthorizedError } from '@nextrush/errors';
|
|
4
4
|
|
|
@@ -44,10 +44,17 @@ interface ApplicationOptions {
|
|
|
44
44
|
*/
|
|
45
45
|
env?: 'development' | 'production' | 'test';
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
47
|
+
* Proxy-trust specification for `ctx.ip` resolution (RFC-030, SEC-01).
|
|
48
|
+
*
|
|
49
|
+
* @remarks
|
|
50
|
+
* `false` trusts nothing (the socket peer is always `ctx.ip`); a `number`
|
|
51
|
+
* trusts exactly that many reverse-proxy hops; a `string[]` of CIDR
|
|
52
|
+
* ranges/IPs trusts only a direct peer inside that set. `true` and `0`
|
|
53
|
+
* throw at construction — see {@link validateProxyTrust}.
|
|
54
|
+
*
|
|
48
55
|
* @default false
|
|
49
56
|
*/
|
|
50
|
-
proxy?:
|
|
57
|
+
proxy?: ProxyTrust;
|
|
51
58
|
/**
|
|
52
59
|
* Custom logger. Defaults to no-op (silent).
|
|
53
60
|
*
|
|
@@ -80,6 +87,22 @@ type ListenCallback = () => void;
|
|
|
80
87
|
*/
|
|
81
88
|
interface Routable {
|
|
82
89
|
routes(): Middleware;
|
|
90
|
+
/**
|
|
91
|
+
* Test whether `path` falls under this router's mount prefix, using the
|
|
92
|
+
* SAME normalization the router itself matches with (case folding per its
|
|
93
|
+
* `caseSensitive` option, structural normalization) — never a raw
|
|
94
|
+
* `startsWith` comparison, which would apply a different rule than the
|
|
95
|
+
* router's own dispatch (RFC-029, SEC-02/SEC-15). Optional so a minimal
|
|
96
|
+
* `Routable` (e.g. a test double) still mounts; {@link createPrefixMount}
|
|
97
|
+
* falls back to a literal prefix test when this is absent.
|
|
98
|
+
*
|
|
99
|
+
* @param path - The full, still-query-stripped request path being tested.
|
|
100
|
+
* @param prefix - The normalized mount prefix (leading `/`, no trailing `/`).
|
|
101
|
+
* @returns The path's remainder past the prefix (e.g. `/users` for
|
|
102
|
+
* `/Admin/Users` mounted at `/admin`), or `undefined` if `path` is not
|
|
103
|
+
* under `prefix` per this router's own normalization.
|
|
104
|
+
*/
|
|
105
|
+
matchesMountPrefix?(path: string, prefix: string): string | undefined;
|
|
83
106
|
}
|
|
84
107
|
/**
|
|
85
108
|
* The Application class
|
|
@@ -100,8 +123,13 @@ interface Routable {
|
|
|
100
123
|
* ```
|
|
101
124
|
*/
|
|
102
125
|
declare class Application {
|
|
103
|
-
/** Middleware stack */
|
|
104
126
|
private readonly middlewareStack;
|
|
127
|
+
/**
|
|
128
|
+
* Boot-time security audit checks contributed by tagged middleware
|
|
129
|
+
* (`security-boundaries` capability, task 8.1). Collected as `use()`
|
|
130
|
+
* registers each middleware; run once by `_boot()`, production-only.
|
|
131
|
+
*/
|
|
132
|
+
private readonly securityAudits;
|
|
105
133
|
/** Registered extensions, in registration order (setup runs at ready()) */
|
|
106
134
|
private readonly extensions;
|
|
107
135
|
/** Registered extension names — enforces uniqueness */
|
|
@@ -119,11 +147,8 @@ declare class Application {
|
|
|
119
147
|
private _closeStarted;
|
|
120
148
|
/** Names decorated onto the app — enforces collision detection */
|
|
121
149
|
private readonly decorations;
|
|
122
|
-
/** Custom error handler */
|
|
123
150
|
private _errorHandler;
|
|
124
|
-
/** Pluggable logger */
|
|
125
151
|
readonly logger: Logger;
|
|
126
|
-
/** Application options */
|
|
127
152
|
readonly options: ApplicationOptions;
|
|
128
153
|
/** Whether the app is running (server listening) */
|
|
129
154
|
private _isRunning;
|
|
@@ -241,6 +266,14 @@ declare class Application {
|
|
|
241
266
|
* memoized by {@link ready}.
|
|
242
267
|
*/
|
|
243
268
|
private _boot;
|
|
269
|
+
/**
|
|
270
|
+
* Run every registered middleware's boot-time security audit check
|
|
271
|
+
* (`security-boundaries` capability, task 8.1/8.2). `throw`-level verdicts
|
|
272
|
+
* fail boot immediately; `warn`-level verdicts log once each via the app
|
|
273
|
+
* logger and never block boot. Production-only — `_boot()` calls this
|
|
274
|
+
* conditionally so a dev/test app never pays the cost or sees the noise.
|
|
275
|
+
*/
|
|
276
|
+
private _runSecurityAudits;
|
|
244
277
|
/**
|
|
245
278
|
* Attach a value to the app under `name`. Extension-author primitive, invoked
|
|
246
279
|
* through {@link ExtensionContext.decorate} — there is intentionally no public
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { SECURITY_AUDIT } from '@nextrush/types';
|
|
2
|
+
export { ContentType, HttpStatus } from '@nextrush/types';
|
|
1
3
|
import { getErrorStatus, NextRushError, getHttpStatusMessage } from '@nextrush/errors';
|
|
2
4
|
export { BadRequestError, ForbiddenError, HttpError, InternalServerError, NextRushError, NotFoundError, UnauthorizedError } from '@nextrush/errors';
|
|
3
|
-
|
|
5
|
+
|
|
6
|
+
// src/application.ts
|
|
4
7
|
|
|
5
8
|
// src/middleware.ts
|
|
6
9
|
var MULTIPLE_NEXT_MESSAGE = "next() called multiple times";
|
|
10
|
+
var RESOLVED = Promise.resolve();
|
|
7
11
|
function emitDoubleResponseWarning(index) {
|
|
8
12
|
console.warn(
|
|
9
13
|
`[nextrush] Middleware at index ${String(index)} called next() after the response was already committed. Downstream middleware may attempt to write to an already-finished response. Either await next() to delegate downstream, or send a response without calling next().`
|
|
@@ -23,7 +27,7 @@ function compose(middleware, options) {
|
|
|
23
27
|
const len = stack.length;
|
|
24
28
|
if (len === 0) {
|
|
25
29
|
return function composedMiddleware(_ctx, next) {
|
|
26
|
-
return next ? next() :
|
|
30
|
+
return next ? next() : RESOLVED;
|
|
27
31
|
};
|
|
28
32
|
}
|
|
29
33
|
if (len === 1) {
|
|
@@ -39,13 +43,14 @@ function compose(middleware, options) {
|
|
|
39
43
|
if (warnDoubleResponse && ctx.responded) {
|
|
40
44
|
emitDoubleResponseWarning(0);
|
|
41
45
|
}
|
|
42
|
-
return next ? next() :
|
|
46
|
+
return next ? next() : RESOLVED;
|
|
43
47
|
};
|
|
44
48
|
if (ctx.setNext) {
|
|
45
49
|
ctx.setNext(nextFn);
|
|
46
50
|
}
|
|
47
51
|
try {
|
|
48
|
-
|
|
52
|
+
const result = only(ctx, nextFn);
|
|
53
|
+
return result === void 0 ? RESOLVED : Promise.resolve(result);
|
|
49
54
|
} catch (err) {
|
|
50
55
|
return Promise.reject(err instanceof Error ? err : new Error(String(err)));
|
|
51
56
|
}
|
|
@@ -66,7 +71,7 @@ function compose(middleware, options) {
|
|
|
66
71
|
fn = next;
|
|
67
72
|
}
|
|
68
73
|
if (!fn) {
|
|
69
|
-
return
|
|
74
|
+
return RESOLVED;
|
|
70
75
|
}
|
|
71
76
|
const nextFn = () => {
|
|
72
77
|
if (warnDoubleResponse && ctx.responded) {
|
|
@@ -78,7 +83,8 @@ function compose(middleware, options) {
|
|
|
78
83
|
ctx.setNext(nextFn);
|
|
79
84
|
}
|
|
80
85
|
try {
|
|
81
|
-
|
|
86
|
+
const result = fn(ctx, nextFn);
|
|
87
|
+
return result === void 0 ? RESOLVED : Promise.resolve(result);
|
|
82
88
|
} catch (err) {
|
|
83
89
|
return Promise.reject(err instanceof Error ? err : new Error(String(err)));
|
|
84
90
|
}
|
|
@@ -118,20 +124,26 @@ function writeDefaultErrorResponse(error, ctx, opts) {
|
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
// src/route-mount.ts
|
|
127
|
+
var SLASH_CHAR_CODE = 47;
|
|
121
128
|
var ORIGINAL_PATH = /* @__PURE__ */ Symbol.for("nextrush.originalPath");
|
|
122
129
|
var ROUTE_PREFIX = /* @__PURE__ */ Symbol.for("nextrush.routePrefix");
|
|
123
|
-
function
|
|
130
|
+
function resolveMountedPath(currentPath, normalizedPrefix, matchesMountPrefix) {
|
|
131
|
+
if (matchesMountPrefix) {
|
|
132
|
+
return matchesMountPrefix(currentPath, normalizedPrefix);
|
|
133
|
+
}
|
|
124
134
|
const prefixLen = normalizedPrefix.length;
|
|
135
|
+
if (!currentPath.startsWith(normalizedPrefix)) return void 0;
|
|
136
|
+
const hasCharAfterPrefix = prefixLen < currentPath.length;
|
|
137
|
+
if (hasCharAfterPrefix && currentPath.charCodeAt(prefixLen) !== SLASH_CHAR_CODE) return void 0;
|
|
138
|
+
return currentPath.slice(prefixLen) || "/";
|
|
139
|
+
}
|
|
140
|
+
function createPrefixMount(normalizedPrefix, routerMiddleware, matchesMountPrefix) {
|
|
125
141
|
return async (ctx, next) => {
|
|
126
142
|
const currentPath = ctx.path;
|
|
127
|
-
|
|
143
|
+
const adjustedPath = resolveMountedPath(currentPath, normalizedPrefix, matchesMountPrefix);
|
|
144
|
+
if (adjustedPath === void 0) {
|
|
128
145
|
return next();
|
|
129
146
|
}
|
|
130
|
-
const hasCharAfterPrefix = prefixLen < currentPath.length;
|
|
131
|
-
if (hasCharAfterPrefix && currentPath.charCodeAt(prefixLen) !== 47) {
|
|
132
|
-
return next();
|
|
133
|
-
}
|
|
134
|
-
const adjustedPath = currentPath.slice(prefixLen) || "/";
|
|
135
147
|
ctx.path = adjustedPath;
|
|
136
148
|
ctx.state[ORIGINAL_PATH] = currentPath;
|
|
137
149
|
ctx.state[ROUTE_PREFIX] = normalizedPrefix;
|
|
@@ -150,6 +162,19 @@ function createPrefixMount(normalizedPrefix, routerMiddleware) {
|
|
|
150
162
|
}
|
|
151
163
|
|
|
152
164
|
// src/application.ts
|
|
165
|
+
function validateProxyTrust(proxy) {
|
|
166
|
+
if (proxy === true) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
"createApp({ proxy: true }) is no longer valid \u2014 trusting every proxy header lets a remote client forge its own IP. Use `proxy: <hopCount>` (e.g. `proxy: 1` for one reverse proxy) or `proxy: ['<cidr>', ...]` (a list of trusted proxy IPs/ranges) instead."
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (proxy === 0) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
"createApp({ proxy: 0 }) trusts zero hops, which is the same as `proxy: false` but never falls back cleanly \u2014 use `proxy: false` to disable proxy trust, or a positive hop count to trust that many proxies."
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
return proxy;
|
|
177
|
+
}
|
|
153
178
|
var TeardownTimeoutError = class extends Error {
|
|
154
179
|
constructor(unitName, timeoutMs) {
|
|
155
180
|
super(`Teardown unit "${unitName}" did not complete within ${String(timeoutMs)}ms`);
|
|
@@ -184,8 +209,13 @@ var NOOP_LOGGER = {
|
|
|
184
209
|
}
|
|
185
210
|
};
|
|
186
211
|
var Application = class {
|
|
187
|
-
/** Middleware stack */
|
|
188
212
|
middlewareStack = [];
|
|
213
|
+
/**
|
|
214
|
+
* Boot-time security audit checks contributed by tagged middleware
|
|
215
|
+
* (`security-boundaries` capability, task 8.1). Collected as `use()`
|
|
216
|
+
* registers each middleware; run once by `_boot()`, production-only.
|
|
217
|
+
*/
|
|
218
|
+
securityAudits = [];
|
|
189
219
|
/** Registered extensions, in registration order (setup runs at ready()) */
|
|
190
220
|
extensions = [];
|
|
191
221
|
/** Registered extension names — enforces uniqueness */
|
|
@@ -203,11 +233,8 @@ var Application = class {
|
|
|
203
233
|
_closeStarted = false;
|
|
204
234
|
/** Names decorated onto the app — enforces collision detection */
|
|
205
235
|
decorations = /* @__PURE__ */ new Set();
|
|
206
|
-
/** Custom error handler */
|
|
207
236
|
_errorHandler = null;
|
|
208
|
-
/** Pluggable logger */
|
|
209
237
|
logger;
|
|
210
|
-
/** Application options */
|
|
211
238
|
options;
|
|
212
239
|
/** Whether the app is running (server listening) */
|
|
213
240
|
_isRunning = false;
|
|
@@ -224,9 +251,10 @@ var Application = class {
|
|
|
224
251
|
/** The app-owned DI container (optional). Exposed to extensions and registrars. */
|
|
225
252
|
container;
|
|
226
253
|
constructor(options = {}) {
|
|
254
|
+
const proxy = validateProxyTrust(options.proxy ?? false);
|
|
227
255
|
this.options = {
|
|
228
256
|
env: options.env ?? "development",
|
|
229
|
-
proxy
|
|
257
|
+
proxy
|
|
230
258
|
};
|
|
231
259
|
this.logger = options.logger ?? NOOP_LOGGER;
|
|
232
260
|
this.router = options.router;
|
|
@@ -272,9 +300,6 @@ var Application = class {
|
|
|
272
300
|
);
|
|
273
301
|
}
|
|
274
302
|
}
|
|
275
|
-
// ===========================================================================
|
|
276
|
-
// Middleware Registration
|
|
277
|
-
// ===========================================================================
|
|
278
303
|
/**
|
|
279
304
|
* Register middleware function(s)
|
|
280
305
|
*
|
|
@@ -288,12 +313,13 @@ var Application = class {
|
|
|
288
313
|
throw new TypeError("Middleware must be a function");
|
|
289
314
|
}
|
|
290
315
|
this.middlewareStack.push(mw);
|
|
316
|
+
const audit = mw[SECURITY_AUDIT];
|
|
317
|
+
if (typeof audit === "function") {
|
|
318
|
+
this.securityAudits.push(audit);
|
|
319
|
+
}
|
|
291
320
|
}
|
|
292
321
|
return this;
|
|
293
322
|
}
|
|
294
|
-
// ===========================================================================
|
|
295
|
-
// Router Mounting (Hono-style)
|
|
296
|
-
// ===========================================================================
|
|
297
323
|
/**
|
|
298
324
|
* Mount a router at a path prefix.
|
|
299
325
|
*
|
|
@@ -312,12 +338,11 @@ var Application = class {
|
|
|
312
338
|
if (!normalizedPrefix.startsWith("/")) {
|
|
313
339
|
normalizedPrefix = "/" + normalizedPrefix;
|
|
314
340
|
}
|
|
315
|
-
this.middlewareStack.push(
|
|
341
|
+
this.middlewareStack.push(
|
|
342
|
+
createPrefixMount(normalizedPrefix, routerMiddleware, router.matchesMountPrefix?.bind(router))
|
|
343
|
+
);
|
|
316
344
|
return this;
|
|
317
345
|
}
|
|
318
|
-
// ===========================================================================
|
|
319
|
-
// Routing (delegates to the app-owned router)
|
|
320
|
-
// ===========================================================================
|
|
321
346
|
requireRouter() {
|
|
322
347
|
if (!this.router) {
|
|
323
348
|
throw new Error(
|
|
@@ -375,9 +400,6 @@ var Application = class {
|
|
|
375
400
|
this.requireRouter().all(path, ...entries);
|
|
376
401
|
return this;
|
|
377
402
|
}
|
|
378
|
-
// ===========================================================================
|
|
379
|
-
// Error Handling
|
|
380
|
-
// ===========================================================================
|
|
381
403
|
/**
|
|
382
404
|
* Set the application error handler. Replaces any previously set handler.
|
|
383
405
|
*
|
|
@@ -389,9 +411,7 @@ var Application = class {
|
|
|
389
411
|
this._errorHandler = handler;
|
|
390
412
|
return this;
|
|
391
413
|
}
|
|
392
|
-
//
|
|
393
|
-
// Extension System (see RFC-NEXTRUSH-PLUGIN-SYSTEM)
|
|
394
|
-
// ===========================================================================
|
|
414
|
+
// Extension System. See docs/RFC/class-runtime/005-plugin-system.md
|
|
395
415
|
/**
|
|
396
416
|
* Register an extension. Queues it — `setup()` runs later, at `ready()`,
|
|
397
417
|
* in registration order. Synchronous and chainable.
|
|
@@ -496,9 +516,31 @@ var Application = class {
|
|
|
496
516
|
this.middlewareStack.push(this.router.routes());
|
|
497
517
|
this._routerMounted = true;
|
|
498
518
|
}
|
|
519
|
+
if (this.isProduction) {
|
|
520
|
+
this._runSecurityAudits();
|
|
521
|
+
}
|
|
499
522
|
this._isReady = true;
|
|
500
523
|
return this;
|
|
501
524
|
}
|
|
525
|
+
/**
|
|
526
|
+
* Run every registered middleware's boot-time security audit check
|
|
527
|
+
* (`security-boundaries` capability, task 8.1/8.2). `throw`-level verdicts
|
|
528
|
+
* fail boot immediately; `warn`-level verdicts log once each via the app
|
|
529
|
+
* logger and never block boot. Production-only — `_boot()` calls this
|
|
530
|
+
* conditionally so a dev/test app never pays the cost or sees the noise.
|
|
531
|
+
*/
|
|
532
|
+
_runSecurityAudits() {
|
|
533
|
+
for (const audit of this.securityAudits) {
|
|
534
|
+
const verdict = audit();
|
|
535
|
+
if (verdict.level === "ok") {
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
if (verdict.level === "throw") {
|
|
539
|
+
throw new Error(`[security-boundaries] ${verdict.message}`);
|
|
540
|
+
}
|
|
541
|
+
this.logger.warn(`[security-boundaries] ${verdict.message}`);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
502
544
|
/**
|
|
503
545
|
* Attach a value to the app under `name`. Extension-author primitive, invoked
|
|
504
546
|
* through {@link ExtensionContext.decorate} — there is intentionally no public
|
|
@@ -532,9 +574,6 @@ var Application = class {
|
|
|
532
574
|
hasDecorator(name) {
|
|
533
575
|
return this.decorations.has(name);
|
|
534
576
|
}
|
|
535
|
-
// ===========================================================================
|
|
536
|
-
// Request Handler
|
|
537
|
-
// ===========================================================================
|
|
538
577
|
/**
|
|
539
578
|
* Create the request handler callback.
|
|
540
579
|
*
|
|
@@ -553,13 +592,7 @@ var Application = class {
|
|
|
553
592
|
const fn = compose(this.middlewareStack, {
|
|
554
593
|
warnDoubleResponse: !this.isProduction
|
|
555
594
|
});
|
|
556
|
-
return
|
|
557
|
-
try {
|
|
558
|
-
await fn(ctx);
|
|
559
|
-
} catch (error) {
|
|
560
|
-
await this.handleError(error, ctx);
|
|
561
|
-
}
|
|
562
|
-
};
|
|
595
|
+
return (ctx) => fn(ctx).then(void 0, (error) => this.handleError(error, ctx));
|
|
563
596
|
}
|
|
564
597
|
/**
|
|
565
598
|
* Handle errors - uses custom handler if set, otherwise default
|
|
@@ -590,9 +623,6 @@ var Application = class {
|
|
|
590
623
|
isProduction: this.isProduction
|
|
591
624
|
});
|
|
592
625
|
}
|
|
593
|
-
// ===========================================================================
|
|
594
|
-
// Lifecycle
|
|
595
|
-
// ===========================================================================
|
|
596
626
|
/**
|
|
597
627
|
* Mark app as running and freeze configuration.
|
|
598
628
|
*
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/middleware.ts","../src/error-handler.ts","../src/route-mount.ts","../src/application.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @nextrush/core - Middleware Composition\n *\n * Koa-style middleware composition with async/await support.\n * This is the heart of the middleware pipeline.\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware, Next } from '@nextrush/types';\n\n/**\n * Composed middleware handler - can be called with just context\n */\nexport type ComposedMiddleware = (ctx: Context, next?: Next) => Promise<void>;\n\n/**\n * Rejection message shared by BOTH the general path and the single-middleware\n * fast path (design D4). Downstream code/tests assert on this exact string, so\n * it is defined once to prevent the two paths from drifting.\n */\nconst MULTIPLE_NEXT_MESSAGE = 'next() called multiple times';\n\n/**\n * Emit the double-response warning for the middleware at `index`.\n *\n * Shared by the general path and the fast path so the text (including the\n * index reference) cannot diverge between them (design D4). Fires only when\n * `warnDoubleResponse` is enabled AND the context has already committed a\n * response — the caller supplies both conditions.\n */\nfunction emitDoubleResponseWarning(index: number): void {\n console.warn(\n `[nextrush] Middleware at index ${String(index)} called next() after the response was already committed. ` +\n 'Downstream middleware may attempt to write to an already-finished response. ' +\n 'Either await next() to delegate downstream, or send a response without calling next().'\n );\n}\n\n/**\n * Options for middleware composition\n */\nexport interface ComposeOptions {\n /**\n * Warn when a middleware sends a response AND calls next().\n *\n * @remarks\n * Opt-in and defaults to `false` — `@nextrush/core` is a runtime-agnostic\n * package and must not read `process.env` (global-rules §2, audit C-4). The\n * `Application` enables this in non-production by passing the flag from its\n * own `env` option.\n *\n * @default false\n */\n warnDoubleResponse?: boolean;\n}\n\n/**\n * Compose multiple middleware functions into a single middleware.\n *\n * Executes middleware in order, each middleware can call `await next()`\n * to pass control to the next middleware and wait for it to complete.\n *\n * @param middleware - Array of middleware functions to compose\n * @param options - Composition options\n * @returns Single composed middleware function\n *\n * @example\n * ```typescript\n * const composed = compose([\n * async (ctx, next) => {\n * console.log('1 - before');\n * await next();\n * console.log('1 - after');\n * },\n * async (ctx, next) => {\n * console.log('2 - before');\n * await next();\n * console.log('2 - after');\n * },\n * ]);\n *\n * // Output:\n * // 1 - before\n * // 2 - before\n * // 2 - after\n * // 1 - after\n * ```\n */\nexport function compose(middleware: Middleware[], options?: ComposeOptions): ComposedMiddleware {\n // Validate middleware array\n if (!Array.isArray(middleware)) {\n throw new TypeError('Middleware stack must be an array');\n }\n\n for (const fn of middleware) {\n if (typeof fn !== 'function') {\n throw new TypeError('Middleware must be a function');\n }\n }\n\n const warnDoubleResponse = options?.warnDoubleResponse ?? false;\n\n // Snapshot middleware array at compose time\n const stack = [...middleware];\n const len = stack.length;\n\n // FAST PATH: No middleware — just call next()\n if (len === 0) {\n return function composedMiddleware(_ctx: Context, next?: Next): Promise<void> {\n return next ? next() : Promise.resolve();\n };\n }\n\n // FAST PATH: Exactly one middleware — the overwhelmingly common application\n // shape (a single mounted router). Avoids allocating the recursive `dispatch`\n // closure and the per-call index comparison of the general path while\n // preserving every observable semantic (design D2/D3/D7):\n // - a PER-INVOCATION guard (`called`) declared inside the returned function,\n // never hoisted, so concurrent requests cannot corrupt each other;\n // - the SAME guarded thunk is passed as the `next` argument AND wired to\n // `ctx.setNext`, so a double-call is caught across either surface;\n // - a synchronous throw becomes a rejected promise and non-`Error` throws\n // are wrapped, identical to the general path.\n if (len === 1) {\n const only = stack[0];\n if (only) {\n return function composedSingle(ctx: Context, next?: Next): Promise<void> {\n let called = false; // PER-INVOCATION — must never be hoisted out of here\n const nextFn = (): Promise<void> => {\n if (called) {\n return Promise.reject(new Error(MULTIPLE_NEXT_MESSAGE));\n }\n called = true;\n if (warnDoubleResponse && ctx.responded) {\n emitDoubleResponseWarning(0);\n }\n return next ? next() : Promise.resolve();\n };\n\n // Wire ctx.next() to the SAME thunk passed as the argument (design D3).\n if (ctx.setNext) {\n ctx.setNext(nextFn);\n }\n\n try {\n return Promise.resolve(only(ctx, nextFn));\n } catch (err: unknown) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n }\n }\n\n /**\n * Composed middleware function\n * Uses index-based dispatch to avoid per-request closure chains\n * while preserving double-next detection per call.\n */\n return function composedMiddleware(ctx: Context, next?: Next): Promise<void> {\n // Per-request index tracker — only state needed\n let index = -1;\n\n function dispatch(i: number): Promise<void> {\n if (i <= index) {\n return Promise.reject(new Error(MULTIPLE_NEXT_MESSAGE));\n }\n\n index = i;\n\n let fn: Middleware | Next | undefined;\n if (i < len) {\n fn = stack[i];\n } else if (i === len) {\n fn = next;\n }\n\n if (!fn) {\n return Promise.resolve();\n }\n\n const nextFn = (): Promise<void> => {\n if (warnDoubleResponse && ctx.responded) {\n emitDoubleResponseWarning(i);\n }\n return dispatch(i + 1);\n };\n\n // Wire up ctx.next() if the context supports it\n if (ctx.setNext) {\n ctx.setNext(nextFn);\n }\n\n try {\n return Promise.resolve(fn(ctx, nextFn));\n } catch (err: unknown) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n return dispatch(0);\n };\n}\n\n/**\n * Check if a function is a valid middleware\n */\nexport function isMiddleware(fn: unknown): fn is Middleware {\n return typeof fn === 'function';\n}\n\n/**\n * Flatten nested middleware arrays with type validation.\n * Uses bounded depth (10 levels) to prevent V8 deoptimization on deeply nested arrays.\n */\nexport function flattenMiddleware(arr: (Middleware | Middleware[])[]): Middleware[] {\n const flattened = arr.flat(10);\n for (const fn of flattened) {\n if (typeof fn !== 'function') {\n throw new TypeError(`Invalid middleware: expected function, got ${typeof fn}`);\n }\n }\n return flattened;\n}\n","/**\n * @nextrush/core - Default Error Response\n *\n * The framework's default error serialization, extracted from `Application`\n * (audit C-5). Produces the same JSON shape as `@nextrush/errors`'\n * `errorHandler()` so there is ONE error contract framework-wide (audit C-1):\n * a `NextRushError` serializes via its own `toJSON()`; a plain error becomes a\n * safe coded 500.\n *\n * @packageDocumentation\n */\n\nimport { getErrorStatus, getHttpStatusMessage, NextRushError } from '@nextrush/errors';\nimport type { Context, Logger } from '@nextrush/types';\n\n/** Options for {@link writeDefaultErrorResponse}. */\nexport interface DefaultErrorOptions {\n logger: Logger;\n isProduction: boolean;\n}\n\n/**\n * Write the default error response to `ctx`.\n *\n * @remarks\n * 5xx are always logged; 4xx only outside production (audit C-7). The internal\n * message of a non-exposed error is never sent to the client.\n */\nexport function writeDefaultErrorResponse(\n error: Error,\n ctx: Context,\n opts: DefaultErrorOptions\n): void {\n const status = getErrorStatus(error);\n\n if (status >= 500 || !opts.isProduction) {\n opts.logger.error('Request error:', error);\n }\n\n ctx.status = status;\n\n if (error instanceof NextRushError) {\n ctx.json(error.toJSON());\n return;\n }\n\n const expose = status < 500;\n ctx.json({\n error: expose ? error.name : getHttpStatusMessage(status),\n message: expose ? error.message : 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n status,\n });\n}\n","/**\n * @nextrush/core - Router Prefix Mount\n *\n * Builds the middleware that mounts a router at a path prefix, rewriting\n * `ctx.path` for the duration of the sub-router and restoring it afterward.\n * Extracted from `Application.route()` (audit C-5).\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware } from '@nextrush/types';\n\n/** @internal Symbol keys for mount state — avoids polluting the user's ctx.state namespace */\nconst ORIGINAL_PATH = Symbol.for('nextrush.originalPath');\nconst ROUTE_PREFIX = Symbol.for('nextrush.routePrefix');\n\n/**\n * Create a middleware that mounts `routerMiddleware` at `normalizedPrefix`.\n *\n * @remarks\n * On a matching request it strips the prefix from `ctx.path`, runs the mounted\n * middleware, and restores the original path in a `finally` — including across\n * the downstream `next()` boundary, so middleware after the mount sees the\n * original path. Callers pass an already-normalized prefix (leading `/`, no\n * trailing `/`).\n *\n * @param normalizedPrefix - The mount prefix (e.g. `/api/users`).\n * @param routerMiddleware - The mounted router's `routes()` middleware.\n */\nexport function createPrefixMount(\n normalizedPrefix: string,\n routerMiddleware: Middleware\n): Middleware {\n const prefixLen = normalizedPrefix.length;\n\n return async (ctx: Context, next): Promise<void> => {\n const currentPath = ctx.path;\n\n // Fast prefix check\n if (!currentPath.startsWith(normalizedPrefix)) {\n return next();\n }\n\n // Check prefix boundary (avoid /api/usersxxx matching /api/users)\n const hasCharAfterPrefix = prefixLen < currentPath.length;\n if (hasCharAfterPrefix && currentPath.charCodeAt(prefixLen) !== 47) {\n // 47 = '/'\n return next();\n }\n\n // Direct path manipulation (no Proxy - fast!)\n const adjustedPath = currentPath.slice(prefixLen) || '/';\n (ctx as { path: string }).path = adjustedPath;\n\n // Store original for recovery (Symbol keys avoid ctx.state pollution)\n (ctx.state as Record<symbol, unknown>)[ORIGINAL_PATH] = currentPath;\n (ctx.state as Record<symbol, unknown>)[ROUTE_PREFIX] = normalizedPrefix;\n\n try {\n await routerMiddleware(ctx, async () => {\n (ctx as { path: string }).path = currentPath;\n await next();\n (ctx as { path: string }).path = adjustedPath;\n });\n } finally {\n (ctx as { path: string }).path = currentPath;\n (ctx.state as Record<symbol, unknown>)[ORIGINAL_PATH] = undefined;\n (ctx.state as Record<symbol, unknown>)[ROUTE_PREFIX] = undefined;\n }\n };\n}\n","/**\n * @nextrush/core - Application Class\n *\n * The Application class is the main entry point for NextRush.\n * It manages middleware registration and the extension lifecycle.\n *\n * See docs/RFC/class-runtime/005-plugin-system.md for the extension model.\n *\n * @packageDocumentation\n */\n\nimport type {\n Container,\n Context,\n Extension,\n ExtensionContext,\n Logger,\n Middleware,\n RouteEntry,\n Router,\n} from '@nextrush/types';\nimport { compose } from './middleware';\nimport { writeDefaultErrorResponse } from './error-handler';\nimport { createPrefixMount } from './route-mount';\n\n/**\n * Error handler function type\n */\nexport type ErrorHandler = (error: Error, ctx: Context) => void | Promise<void>;\n\n/**\n * Options for {@link Application.close}.\n *\n * @see RFC-022 (`docs/RFC/class-runtime/022-bounded-teardown-lifecycle.md`) / ADR-0012.\n */\nexport interface CloseOptions {\n /**\n * Bound, in milliseconds, on total teardown time. Every teardown unit\n * (extension `destroy()`) races against this budget independently — a\n * unit that does not finish in time is reported as a {@link TeardownTimeoutError}\n * in the returned array, and never blocks `close()` past the budget.\n *\n * @remarks Omitted = unbounded, identical to calling `close()` with no\n * arguments today (`Promise.allSettled`, no timeout).\n */\n timeout?: number;\n}\n\n/**\n * Reported when a teardown unit (extension `destroy()`) does not settle\n * within the budget passed to {@link Application.close}. Named by the unit\n * so operators can see exactly which extension leaked past the shutdown\n * window (RFC-022 §8.5).\n */\nexport class TeardownTimeoutError extends Error {\n constructor(unitName: string, timeoutMs: number) {\n super(`Teardown unit \"${unitName}\" did not complete within ${String(timeoutMs)}ms`);\n this.name = 'TeardownTimeoutError';\n }\n}\n\n/**\n * One named, independently-runnable teardown unit — the uniform shape shared\n * by extension `destroy()`, class `onShutdown()` (bridged externally), and\n * `onClose` hooks (RFC-022 §7.3: \"teardown becomes a flat list of\n * independently-isolated, budget-raced units\").\n */\ninterface TeardownUnit {\n readonly name: string;\n run(): void | Promise<void>;\n}\n\n/**\n * Run one teardown unit, isolated from every other unit's failure, optionally\n * racing it against `timeoutMs`. Resolves to `null` on success or an `Error`\n * describing the failure/timeout — never throws and never leaves the caller\n * waiting past the budget.\n */\nasync function runTeardownUnit(unit: TeardownUnit, timeoutMs?: number): Promise<Error | null> {\n const settle = Promise.resolve()\n .then(() => unit.run())\n .then((): Error | null => null)\n .catch((err: unknown): Error => (err instanceof Error ? err : new Error(String(err))));\n\n if (timeoutMs === undefined) {\n return settle;\n }\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<Error>((resolve) => {\n timer = setTimeout(() => {\n resolve(new TeardownTimeoutError(unit.name, timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([settle, timedOut]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** No-op logger — default when no logger is configured */\nconst NOOP_LOGGER: Logger = {\n error(..._args) {\n void _args;\n },\n warn(..._args) {\n void _args;\n },\n info(..._args) {\n void _args;\n },\n debug(..._args) {\n void _args;\n },\n};\n\n/**\n * Application options\n */\nexport interface ApplicationOptions {\n /**\n * Environment mode\n * @default 'development'\n */\n env?: 'development' | 'production' | 'test';\n\n /**\n * Whether to use proxy headers (X-Forwarded-For, etc.)\n * @default false\n */\n proxy?: boolean;\n\n /**\n * Custom logger. Defaults to no-op (silent).\n *\n * @remarks\n * Pass `console` for quick development logging. For production, provide a\n * structured logger (pino, winston, `@nextrush/logger`) implementing {@link Logger}.\n */\n logger?: Logger;\n\n /**\n * A router the app owns. Route methods (`app.get`, `app.post`, …) delegate to\n * it, and it is mounted last at `ready()`. The `nextrush` meta-package's\n * `createApp` injects one automatically; pass it explicitly when using\n * `@nextrush/core` directly.\n */\n router?: Router;\n\n /**\n * A per-app DI container. Exposed to extensions via `ExtensionContext.container`\n * and used by class-based registrars (e.g. `registerControllers`). Injected by\n * `nextrush/class`; omitted for functional, DI-free apps.\n */\n container?: Container;\n}\n\n/**\n * Listener callback type\n */\nexport type ListenCallback = () => void;\n\n/**\n * Routable interface - any object with routes() method.\n * Allows mounting Router instances without a circular dependency.\n */\nexport interface Routable {\n routes(): Middleware;\n}\n\n/**\n * The Application class\n *\n * @example\n * ```typescript\n * const app = createApp();\n *\n * app.use(async (ctx) => {\n * ctx.json({ message: 'Hello World' });\n * });\n *\n * // Extensions (rare — long-lived services) are booted at ready(), which\n * // adapters call automatically before start().\n * app.extend(events());\n *\n * listen(app, { port: 8080 });\n * ```\n */\nexport class Application {\n /** Middleware stack */\n private readonly middlewareStack: Middleware[] = [];\n\n /** Registered extensions, in registration order (setup runs at ready()) */\n private readonly extensions: Extension<unknown>[] = [];\n\n /** Registered extension names — enforces uniqueness */\n private readonly extensionNames = new Set<string>();\n\n /**\n * Combined teardown-unit registration order: extension `destroy()`s and\n * {@link onClose} hooks interleaved in the exact order they were registered,\n * so `_shutdown()` can run the whole set in one reverse-of-registration pass\n * (RFC-022 §7.3 — \"teardown becomes a flat list of independently-isolated,\n * budget-raced units\"). Populated lazily by {@link extend} (for extensions\n * declaring `destroy()`) and {@link onClose}.\n */\n private readonly teardownUnits: TeardownUnit[] = [];\n\n /** Whether {@link close} has started — {@link onClose} is a pre-shutdown-only registration point (RFC-022 §8.6). */\n private _closeStarted = false;\n\n /** Names decorated onto the app — enforces collision detection */\n private readonly decorations = new Set<string>();\n\n /** Custom error handler */\n private _errorHandler: ErrorHandler | null = null;\n\n /** Pluggable logger */\n readonly logger: Logger;\n\n /** Application options */\n readonly options: ApplicationOptions;\n\n /** Whether the app is running (server listening) */\n private _isRunning = false;\n\n /** Whether the app has been booted (extensions set up, config frozen) */\n private _isReady = false;\n\n /** Whether ready() mounted the app-owned router (so close() can undo it) */\n private _routerMounted = false;\n\n /** In-flight boot promise — memoized so concurrent ready() calls share one boot (H-1) */\n private _readyPromise: Promise<this> | null = null;\n\n /** In-flight shutdown promise — memoized so concurrent close() calls share one (H-3) */\n private _closePromise: Promise<Error[]> | null = null;\n\n /** The app-owned router (optional). Route methods delegate to it. */\n readonly router?: Router;\n\n /** The app-owned DI container (optional). Exposed to extensions and registrars. */\n readonly container?: Container;\n\n constructor(options: ApplicationOptions = {}) {\n this.options = {\n env: options.env ?? 'development',\n proxy: options.proxy ?? false,\n };\n this.logger = options.logger ?? NOOP_LOGGER;\n this.router = options.router;\n this.container = options.container;\n }\n\n /** Check if running in production */\n get isProduction(): boolean {\n return this.options.env === 'production';\n }\n\n /** Check if app is running */\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n /**\n * Whether a graceful shutdown is currently in progress — `true` from the\n * moment {@link close} begins tearing down until it completes (F-12:\n * shutdown observability, RFC-022). A readiness check or operator tooling\n * can read this to reflect an in-progress drain.\n */\n get isDraining(): boolean {\n return this._closeStarted;\n }\n\n /** Check if app has been booted via ready() */\n get isReady(): boolean {\n return this._isReady;\n }\n\n /** Get middleware count */\n get middlewareCount(): number {\n return this.middlewareStack.length;\n }\n\n /** Get registered extension count */\n get extensionCount(): number {\n return this.extensions.length;\n }\n\n /**\n * Throws if the app configuration is frozen (after ready() or start()).\n * Prevents unsafe mutations once the app has booted or is serving traffic.\n */\n private assertConfigurable(method: string): void {\n if (this._isReady || this._isRunning) {\n throw new Error(\n `Cannot call ${method}() after the app has booted (ready()) or started — ` +\n `configuration is frozen`\n );\n }\n }\n\n // ===========================================================================\n // Middleware Registration\n // ===========================================================================\n\n /**\n * Register middleware function(s)\n *\n * @param middleware - Middleware function(s)\n * @returns this for chaining\n */\n use(...middleware: Middleware[]): this {\n this.assertConfigurable('use');\n for (const mw of middleware) {\n if (typeof mw !== 'function') {\n throw new TypeError('Middleware must be a function');\n }\n this.middlewareStack.push(mw);\n }\n return this;\n }\n\n // ===========================================================================\n // Router Mounting (Hono-style)\n // ===========================================================================\n\n /**\n * Mount a router at a path prefix.\n *\n * @param path - Path prefix (e.g. '/api/users')\n * @param router - Router instance to mount\n * @returns this for chaining\n */\n route(path: string, router: Routable): this {\n this.assertConfigurable('route');\n // Root mount optimization: skip all prefix processing\n if (path === '/' || path === '') {\n this.middlewareStack.push(router.routes());\n return this;\n }\n\n const routerMiddleware = router.routes();\n // Normalize prefix: ensure leading '/', strip trailing '/'\n let normalizedPrefix = path.endsWith('/') ? path.slice(0, -1) : path;\n if (!normalizedPrefix.startsWith('/')) {\n normalizedPrefix = '/' + normalizedPrefix;\n }\n\n this.middlewareStack.push(createPrefixMount(normalizedPrefix, routerMiddleware));\n return this;\n }\n\n // ===========================================================================\n // Routing (delegates to the app-owned router)\n // ===========================================================================\n\n private requireRouter(): Router {\n if (!this.router) {\n throw new Error(\n 'No router configured. Create your app with `createApp()` from `nextrush` ' +\n '(batteries-included), or pass one: `createApp({ router: createRouter() })`.'\n );\n }\n return this.router;\n }\n\n /** Register a GET route on the app-owned router. */\n get(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('get');\n this.requireRouter().get(path, ...entries);\n return this;\n }\n\n /** Register a POST route on the app-owned router. */\n post(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('post');\n this.requireRouter().post(path, ...entries);\n return this;\n }\n\n /** Register a PUT route on the app-owned router. */\n put(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('put');\n this.requireRouter().put(path, ...entries);\n return this;\n }\n\n /** Register a PATCH route on the app-owned router. */\n patch(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('patch');\n this.requireRouter().patch(path, ...entries);\n return this;\n }\n\n /** Register a DELETE route on the app-owned router. */\n delete(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('delete');\n this.requireRouter().delete(path, ...entries);\n return this;\n }\n\n /** Register a HEAD route on the app-owned router. */\n head(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('head');\n this.requireRouter().head(path, ...entries);\n return this;\n }\n\n /**\n * Register a route for all HTTP methods on the app-owned router.\n *\n * @remarks\n * There is intentionally no `app.options()` verb method — it would collide\n * with the `app.options` configuration property. Register OPTIONS routes via\n * `app.all()`, the router directly, or let CORS middleware handle preflight.\n */\n all(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('all');\n this.requireRouter().all(path, ...entries);\n return this;\n }\n\n // ===========================================================================\n // Error Handling\n // ===========================================================================\n\n /**\n * Set the application error handler. Replaces any previously set handler.\n *\n * @param handler - Error handler function\n * @returns this for chaining\n */\n setErrorHandler(handler: ErrorHandler): this {\n this.assertConfigurable('setErrorHandler');\n this._errorHandler = handler;\n return this;\n }\n\n // ===========================================================================\n // Extension System (see RFC-NEXTRUSH-PLUGIN-SYSTEM)\n // ===========================================================================\n\n /**\n * Register an extension. Queues it — `setup()` runs later, at `ready()`,\n * in registration order. Synchronous and chainable.\n *\n * @param extension - Extension to register. If it declares a decorated\n * shape via `Extension<TDecorated>`, the return type carries that shape —\n * `app.extend(events<MyEvents>()).events` is statically known, no\n * `declare module` augmentation required.\n * @returns this, intersected with the extension's declared decorated shape\n *\n * @example\n * ```typescript\n * const extended = app.extend(events<MyEvents>());\n * await app.ready(); // adapters call this automatically — runs setup()/decorate()\n * extended.events.emit('user:created', { id: '1' }); // available only AFTER ready()\n * ```\n */\n extend<TDecorated = Record<string, never>>(\n extension: Extension<TDecorated>\n ): this & TDecorated {\n this.assertConfigurable('extend');\n // Runtime guard for externally-supplied input: the type says non-null with a\n // required setup(), but user/plugin code can still violate that at runtime.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive validation of external input\n if (!extension || typeof extension.setup !== 'function') {\n throw new TypeError('Extension must have a setup() method');\n }\n if (this.extensionNames.has(extension.name)) {\n throw new Error(\n `Extension \"${extension.name}\" is already registered. Use a different extension ` +\n `name, or check for a duplicate app.extend() call.`\n );\n }\n this.extensionNames.add(extension.name);\n this.extensions.push(extension);\n if (typeof extension.destroy === 'function') {\n const destroyableExtension = extension as Extension<TDecorated> & {\n destroy: () => void | Promise<void>;\n };\n this.teardownUnits.push({\n name: extension.name,\n run: () => destroyableExtension.destroy(),\n });\n }\n return this as this & TDecorated;\n }\n\n /**\n * Boot the application: run every registered extension's `setup()` once, in\n * registration order, awaiting async setups. Idempotent — safe to call twice.\n * Adapters call this automatically before `start()`.\n *\n * After `ready()` resolves, the configuration is frozen (`use`/`route`/`extend`\n * throw).\n *\n * @returns this\n * @throws if an extension declares a `needs` dependency not yet registered\n */\n async ready(): Promise<this> {\n if (this._isReady) {\n return this;\n }\n\n // Memoize the in-flight boot so concurrent `await app.ready()` calls share a\n // single setup() pass (H-1). Without this, two racing callers both clear the\n // `_isReady` guard, run every extension's setup() twice, and the second\n // decorate() throws a collision. On failure, clear the memo so a retry can\n // re-attempt boot.\n this._readyPromise ??= this._boot().catch((err: unknown) => {\n this._readyPromise = null;\n throw err;\n });\n return this._readyPromise;\n }\n\n /**\n * Run the boot sequence once (extension setup + router mount). Guarded and\n * memoized by {@link ready}.\n */\n private async _boot(): Promise<this> {\n // extension SOMEWHERE (regardless of order) — catches a typo'd or\n // never-registered dependency name with a distinct message from the\n // order-sensitive check below, before running any setup().\n const allNames = new Set(this.extensions.map((e) => e.name));\n for (const extension of this.extensions) {\n if (extension.needs) {\n for (const dep of extension.needs) {\n if (!allNames.has(dep)) {\n throw new Error(\n `Extension \"${extension.name}\" needs \"${dep}\", but no extension named ` +\n `\"${dep}\" was ever registered.`\n );\n }\n }\n }\n }\n\n const setupDone = new Set<string>();\n\n for (const extension of this.extensions) {\n // Declare-and-assert dependency check (no auto-sort; registration order is the order)\n if (extension.needs) {\n for (const dep of extension.needs) {\n if (!setupDone.has(dep)) {\n throw new Error(\n `Extension \"${extension.name}\" needs \"${dep}\", but \"${dep}\" was not ` +\n `registered before it. Register the \"${dep}\" extension before \"${extension.name}\".`\n );\n }\n }\n }\n\n const ctx: ExtensionContext = {\n app: this,\n logger: this.logger,\n container: this.container,\n env: this.options.env ?? 'development',\n name: extension.name,\n decorate: (name: string, value: unknown): void => {\n this.decorate(name, value);\n },\n };\n\n await extension.setup(ctx);\n setupDone.add(extension.name);\n }\n\n // Mount the app-owned router LAST, so routes run after all middleware\n // (both user- and extension-registered).\n if (this.router) {\n this.middlewareStack.push(this.router.routes());\n this._routerMounted = true;\n }\n\n this._isReady = true;\n return this;\n }\n\n /**\n * Attach a value to the app under `name`. Extension-author primitive, invoked\n * through {@link ExtensionContext.decorate} — there is intentionally no public\n * `app.decorate()` (RFC §6.1). Throws on collision.\n *\n * @internal\n */\n private decorate(name: string, value: unknown): void {\n if (this.decorations.has(name)) {\n throw new Error(\n `Decoration \"${name}\" already exists on the application. Choose a different ` +\n `name, or check for a duplicate ctx.decorate() call across your extensions.`\n );\n }\n if (name in this) {\n throw new Error(\n `Decoration \"${name}\" collides with an existing Application member — choose another name`\n );\n }\n Object.defineProperty(this, name, {\n value,\n enumerable: true,\n writable: false,\n configurable: true, // allow close() to clean up\n });\n this.decorations.add(name);\n }\n\n /**\n * Whether a decoration already occupies `name`.\n */\n hasDecorator(name: string): boolean {\n return this.decorations.has(name);\n }\n\n // ===========================================================================\n // Request Handler\n // ===========================================================================\n\n /**\n * Create the request handler callback.\n *\n * The middleware stack is snapshot at call time — call `ready()` before\n * `callback()` so extension-registered middleware is included. Adapters do\n * this automatically.\n *\n * @returns Request handler function\n */\n callback(): (ctx: Context) => Promise<void> {\n if (!this._isReady && this.extensions.length > 0) {\n this.logger.warn(\n `callback() was called before ready(), but ${String(this.extensions.length)} extension(s) ` +\n `were registered via extend() — their setup() will NOT run, and anything they ` +\n `would decorate (e.g. app.events) will be missing. Call \\`await app.ready()\\` ` +\n `before \\`callback()\\`. Adapters (listen/serve) do this automatically.`\n );\n }\n\n const fn = compose(this.middlewareStack, {\n warnDoubleResponse: !this.isProduction,\n });\n\n return async (ctx: Context): Promise<void> => {\n try {\n await fn(ctx);\n } catch (error) {\n await this.handleError(error, ctx);\n }\n };\n }\n\n /**\n * Handle errors - uses custom handler if set, otherwise default\n */\n private async handleError(error: unknown, ctx: Context): Promise<void> {\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (this._errorHandler) {\n try {\n await this._errorHandler(err, ctx);\n return;\n } catch (handlerError) {\n this.logger.error('Error handler threw:', handlerError);\n }\n }\n\n // The default handler can itself throw (e.g. ctx.json when the response is\n // already committed). Swallow-and-log so the request settles instead of\n // rejecting out of callback() into the adapter as an unhandled error (H-2).\n try {\n this.defaultErrorHandler(err, ctx);\n } catch (fatal) {\n this.logger.error('Default error handler threw:', fatal);\n }\n }\n\n /**\n * Default error handler — delegates to the shared error serializer so the\n * default response matches `@nextrush/errors`' `errorHandler()` (audit C-1).\n */\n private defaultErrorHandler(error: Error, ctx: Context): void {\n writeDefaultErrorResponse(error, ctx, {\n logger: this.logger,\n isProduction: this.isProduction,\n });\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n /**\n * Mark app as running and freeze configuration.\n *\n * Called by adapters when the server starts listening (after `ready()`).\n */\n start(): void {\n this._isRunning = true;\n }\n\n /**\n * Register a teardown hook for a resource outside the extension system\n * (stateful middleware, a manually-attached service, …). Run during\n * {@link close} under the SAME bounded/isolated guarantee as extension\n * `destroy()` — one throwing/hanging hook never strands the others — in one\n * combined reverse-of-registration order together with every `extend()`ed\n * extension's `destroy()` (RFC-022 §8.1/§8.2).\n *\n * @param hook - Teardown callback. May be sync or async; a thrown/rejected\n * hook is isolated and its error collected in {@link close}'s returned array.\n *\n * @remarks Registration is pre-shutdown only: a hook registered after\n * `close()` has already started is not run in that shutdown (RFC-022 §8.6).\n *\n * @example\n * ```typescript\n * const store = new MemoryStore(options);\n * app.onClose(() => store.shutdown());\n * ```\n */\n onClose(hook: () => void | Promise<void>): void {\n if (this._closeStarted) {\n return;\n }\n this.teardownUnits.push({ name: 'onClose hook', run: hook });\n }\n\n /**\n * Graceful shutdown. Destroys extensions in reverse registration order,\n * each isolated from the others' failures — one failing/hanging `destroy()`\n * never strands the rest (RFC-022 / ADR-0012).\n *\n * @param options - Optional teardown budget. Omitted = unbounded, byte-identical\n * to today's behavior (`Promise.allSettled`, no timeout). When `timeout` is\n * given, every teardown unit races against it independently; a unit that\n * does not finish in time is reported as a {@link TeardownTimeoutError} in\n * the returned array instead of blocking `close()` past the budget.\n * @returns Array of errors from units that failed or timed out (empty on success)\n */\n async close(options?: CloseOptions): Promise<Error[]> {\n // Memoize the in-flight shutdown so concurrent close() calls (e.g. two\n // signal handlers) destroy each extension exactly once (H-3).\n this._closePromise ??= this._shutdown(options);\n return this._closePromise;\n }\n\n /** Run the shutdown sequence once. Guarded and memoized by {@link close}. */\n private async _shutdown(options?: CloseOptions): Promise<Error[]> {\n this._isRunning = false;\n this._closeStarted = true;\n // F-12: surface the draining transition so operators/readiness checks can\n // observe an in-progress shutdown instead of it being a silent black box.\n this.logger.info('Application is draining: graceful shutdown starting');\n\n if (!this._isReady && this.extensions.length > 0) {\n this.logger.warn(\n `close() was called before ready(), but ${String(this.extensions.length)} extension(s) ` +\n `were registered via extend() — their setup() never ran, yet destroy() will still ` +\n `be called on them now. If destroy() assumes setup()-established state, this may ` +\n `throw or behave incorrectly. Call \\`await app.ready()\\` before \\`close()\\`.`\n );\n }\n\n const timeoutMs = options?.timeout;\n // Reverse of registration order across BOTH extension destroy() calls and\n // onClose hooks — one uniform, combined teardown-unit list (RFC-022 §7.3),\n // so a hook registered between two extend() calls runs at the correct\n // point in the reversed sequence, not as a separate pass.\n const units = [...this.teardownUnits].reverse();\n\n const errors = (\n await Promise.all(units.map((unit) => runTeardownUnit(unit, timeoutMs)))\n ).filter((e): e is Error => e !== null);\n\n // F-12: report the teardown outcome at completion — which unit(s) failed\n // or timed out — rather than exiting silently. TeardownTimeoutError's own\n // message already names the offending unit (§8.5).\n for (const error of errors) {\n this.logger.error('Teardown unit failed during shutdown:', error);\n }\n\n // Clean up decorations so the instance can be re-booted (e.g. in tests)\n for (const name of this.decorations) {\n Reflect.deleteProperty(this, name);\n }\n this.decorations.clear();\n this.extensions.length = 0;\n this.extensionNames.clear();\n this.teardownUnits.length = 0;\n this._isReady = false;\n // Reset the boot/shutdown memos so the instance can be cleanly re-booted\n // (re-boot in tests / hot reload) — a fresh ready()/close() must re-run.\n this._readyPromise = null;\n this._closePromise = null;\n this._closeStarted = false;\n\n // Undo the router mount that ready() appended, so a subsequent ready()\n // (re-boot in tests / hot reload) does not stack a second router (audit C-2).\n if (this._routerMounted) {\n this.middlewareStack.pop();\n this._routerMounted = false;\n }\n\n return errors;\n }\n}\n\n/**\n * Create a new Application instance\n *\n * @param options - Application options\n * @returns New Application instance\n */\nexport function createApp(options?: ApplicationOptions): Application {\n return new Application(options);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/middleware.ts","../src/error-handler.ts","../src/route-mount.ts","../src/application.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @nextrush/core - Middleware Composition\n *\n * Koa-style middleware composition with async/await support.\n * This is the heart of the middleware pipeline.\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware, Next } from '@nextrush/types';\n\n/**\n * Composed middleware handler - can be called with just context\n */\nexport type ComposedMiddleware = (ctx: Context, next?: Next) => Promise<void>;\n\n/**\n * Rejection message shared by BOTH the general path and the single-middleware\n * fast path (design D4). Downstream code/tests assert on this exact string, so\n * it is defined once to prevent the two paths from drifting.\n */\nconst MULTIPLE_NEXT_MESSAGE = 'next() called multiple times';\n\n/**\n * Shared already-resolved promise, returned wherever this module would\n * otherwise construct a fresh one. Mirrors the router's existing `RESOLVED` /\n * `NOOP_NEXT` sentinels.\n *\n * @see openspec/changes/elide-resolved-promise-allocation/design.md\n */\nconst RESOLVED: Promise<void> = Promise.resolve();\n\n/**\n * Emit the double-response warning for the middleware at `index`.\n *\n * Shared by the general path and the fast path so the text (including the\n * index reference) cannot diverge between them (design D4). Fires only when\n * `warnDoubleResponse` is enabled AND the context has already committed a\n * response — the caller supplies both conditions.\n */\nfunction emitDoubleResponseWarning(index: number): void {\n console.warn(\n `[nextrush] Middleware at index ${String(index)} called next() after the response was already committed. ` +\n 'Downstream middleware may attempt to write to an already-finished response. ' +\n 'Either await next() to delegate downstream, or send a response without calling next().'\n );\n}\n\n/**\n * Options for middleware composition\n */\nexport interface ComposeOptions {\n /**\n * Warn when a middleware sends a response AND calls next().\n *\n * @remarks\n * Opt-in and defaults to `false` — `@nextrush/core` is a runtime-agnostic\n * package and must not read `process.env` (global-rules §2, audit C-4). The\n * `Application` enables this in non-production by passing the flag from its\n * own `env` option.\n *\n * @default false\n */\n warnDoubleResponse?: boolean;\n}\n\n/**\n * Compose multiple middleware functions into a single middleware.\n *\n * Executes middleware in order, each middleware can call `await next()`\n * to pass control to the next middleware and wait for it to complete.\n *\n * @param middleware - Array of middleware functions to compose\n * @param options - Composition options\n * @returns Single composed middleware function\n *\n * @example\n * ```typescript\n * const composed = compose([\n * async (ctx, next) => {\n * console.log('1 - before');\n * await next();\n * console.log('1 - after');\n * },\n * async (ctx, next) => {\n * console.log('2 - before');\n * await next();\n * console.log('2 - after');\n * },\n * ]);\n *\n * // Output:\n * // 1 - before\n * // 2 - before\n * // 2 - after\n * // 1 - after\n * ```\n */\nexport function compose(middleware: Middleware[], options?: ComposeOptions): ComposedMiddleware {\n // Validate middleware array\n if (!Array.isArray(middleware)) {\n throw new TypeError('Middleware stack must be an array');\n }\n\n for (const fn of middleware) {\n if (typeof fn !== 'function') {\n throw new TypeError('Middleware must be a function');\n }\n }\n\n const warnDoubleResponse = options?.warnDoubleResponse ?? false;\n\n // Snapshot middleware array at compose time\n const stack = [...middleware];\n const len = stack.length;\n\n // FAST PATH: No middleware — just call next()\n if (len === 0) {\n return function composedMiddleware(_ctx: Context, next?: Next): Promise<void> {\n return next ? next() : RESOLVED;\n };\n }\n\n // FAST PATH: Exactly one middleware — the overwhelmingly common application\n // shape (a single mounted router). Avoids allocating the recursive `dispatch`\n // closure and the per-call index comparison of the general path while\n // preserving every observable semantic (design D2/D3/D7):\n // - a PER-INVOCATION guard (`called`) declared inside the returned function,\n // never hoisted, so concurrent requests cannot corrupt each other;\n // - the SAME guarded thunk is passed as the `next` argument AND wired to\n // `ctx.setNext`, so a double-call is caught across either surface;\n // - a synchronous throw becomes a rejected promise and non-`Error` throws\n // are wrapped, identical to the general path.\n if (len === 1) {\n const only = stack[0];\n if (only) {\n return function composedSingle(ctx: Context, next?: Next): Promise<void> {\n let called = false; // PER-INVOCATION — must never be hoisted out of here\n const nextFn = (): Promise<void> => {\n if (called) {\n return Promise.reject(new Error(MULTIPLE_NEXT_MESSAGE));\n }\n called = true;\n if (warnDoubleResponse && ctx.responded) {\n emitDoubleResponseWarning(0);\n }\n return next ? next() : RESOLVED;\n };\n\n // Wire ctx.next() to the SAME thunk passed as the argument (design D3).\n if (ctx.setNext) {\n ctx.setNext(nextFn);\n }\n\n try {\n // Only `undefined` short-circuits to the sentinel. A thenable MUST\n // stay on the `Promise.resolve` path so its work is adopted, and a\n // falsy-but-defined value must keep its own resolved value.\n const result = only(ctx, nextFn);\n return result === undefined ? RESOLVED : Promise.resolve(result);\n } catch (err: unknown) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n }\n }\n\n /**\n * Composed middleware function\n * Uses index-based dispatch to avoid per-request closure chains\n * while preserving double-next detection per call.\n */\n return function composedMiddleware(ctx: Context, next?: Next): Promise<void> {\n // Per-request index tracker — only state needed\n let index = -1;\n\n function dispatch(i: number): Promise<void> {\n if (i <= index) {\n return Promise.reject(new Error(MULTIPLE_NEXT_MESSAGE));\n }\n\n index = i;\n\n let fn: Middleware | Next | undefined;\n if (i < len) {\n fn = stack[i];\n } else if (i === len) {\n fn = next;\n }\n\n if (!fn) {\n return RESOLVED;\n }\n\n const nextFn = (): Promise<void> => {\n if (warnDoubleResponse && ctx.responded) {\n emitDoubleResponseWarning(i);\n }\n return dispatch(i + 1);\n };\n\n // Wire up ctx.next() if the context supports it\n if (ctx.setNext) {\n ctx.setNext(nextFn);\n }\n\n try {\n // See the fast path's note: `undefined` only, so thenables are adopted.\n const result = fn(ctx, nextFn);\n return result === undefined ? RESOLVED : Promise.resolve(result);\n } catch (err: unknown) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n return dispatch(0);\n };\n}\n\n/**\n * Check if a function is a valid middleware\n */\nexport function isMiddleware(fn: unknown): fn is Middleware {\n return typeof fn === 'function';\n}\n\n/**\n * Flatten nested middleware arrays with type validation.\n * Uses bounded depth (10 levels) to prevent V8 deoptimization on deeply nested arrays.\n */\nexport function flattenMiddleware(arr: (Middleware | Middleware[])[]): Middleware[] {\n const flattened = arr.flat(10);\n for (const fn of flattened) {\n if (typeof fn !== 'function') {\n throw new TypeError(`Invalid middleware: expected function, got ${typeof fn}`);\n }\n }\n return flattened;\n}\n","/**\n * @nextrush/core - Default Error Response\n *\n * The framework's default error serialization, extracted from `Application`\n * (audit C-5). Produces the same JSON shape as `@nextrush/errors`'\n * `errorHandler()` so there is ONE error contract framework-wide (audit C-1):\n * a `NextRushError` serializes via its own `toJSON()`; a plain error becomes a\n * safe coded 500.\n *\n * @packageDocumentation\n */\n\nimport { getErrorStatus, getHttpStatusMessage, NextRushError } from '@nextrush/errors';\nimport type { Context, Logger } from '@nextrush/types';\n\n/** Options for {@link writeDefaultErrorResponse}. */\nexport interface DefaultErrorOptions {\n logger: Logger;\n isProduction: boolean;\n}\n\n/**\n * Write the default error response to `ctx`.\n *\n * @remarks\n * 5xx are always logged; 4xx only outside production (audit C-7). The internal\n * message of a non-exposed error is never sent to the client.\n */\nexport function writeDefaultErrorResponse(\n error: Error,\n ctx: Context,\n opts: DefaultErrorOptions\n): void {\n const status = getErrorStatus(error);\n\n if (status >= 500 || !opts.isProduction) {\n opts.logger.error('Request error:', error);\n }\n\n ctx.status = status;\n\n if (error instanceof NextRushError) {\n ctx.json(error.toJSON());\n return;\n }\n\n const expose = status < 500;\n ctx.json({\n error: expose ? error.name : getHttpStatusMessage(status),\n message: expose ? error.message : 'Internal Server Error',\n code: 'INTERNAL_ERROR',\n status,\n });\n}\n","/**\n * @nextrush/core - Router Prefix Mount\n *\n * Builds the middleware that mounts a router at a path prefix, rewriting\n * `ctx.path` for the duration of the sub-router and restoring it afterward.\n * Extracted from `Application.route()` (audit C-5).\n *\n * @packageDocumentation\n */\n\nimport type { Context, Middleware } from '@nextrush/types';\n\nconst SLASH_CHAR_CODE = 0x2f; // '/'.charCodeAt(0)\n\n/** @internal Symbol keys for mount state — avoids polluting the user's ctx.state namespace */\nconst ORIGINAL_PATH = Symbol.for('nextrush.originalPath');\nconst ROUTE_PREFIX = Symbol.for('nextrush.routePrefix');\n\n/**\n * Test whether `currentPath` falls under `normalizedPrefix`, delegating to\n * the mounted router's own `matchesMountPrefix` when it implements one\n * (RFC-029, task 3.8) so the mount boundary uses the SAME case-folding and\n * structural normalization the router's own dispatch does — never a rule of\n * its own. A `Routable` with no such method (e.g. a minimal test double)\n * falls back to the literal, case-sensitive prefix + boundary check this\n * function previously did inline.\n */\nfunction resolveMountedPath(\n currentPath: string,\n normalizedPrefix: string,\n matchesMountPrefix?: (path: string, prefix: string) => string | undefined\n): string | undefined {\n if (matchesMountPrefix) {\n return matchesMountPrefix(currentPath, normalizedPrefix);\n }\n\n const prefixLen = normalizedPrefix.length;\n if (!currentPath.startsWith(normalizedPrefix)) return undefined;\n\n const hasCharAfterPrefix = prefixLen < currentPath.length;\n if (hasCharAfterPrefix && currentPath.charCodeAt(prefixLen) !== SLASH_CHAR_CODE) return undefined;\n\n return currentPath.slice(prefixLen) || '/';\n}\n\n/**\n * Create a middleware that mounts `routerMiddleware` at `normalizedPrefix`.\n *\n * @remarks\n * On a matching request it strips the prefix from `ctx.path`, runs the mounted\n * middleware, and restores the original path in a `finally` — including across\n * the downstream `next()` boundary, so middleware after the mount sees the\n * original path. Callers pass an already-normalized prefix (leading `/`, no\n * trailing `/`).\n *\n * @param normalizedPrefix - The mount prefix (e.g. `/api/users`).\n * @param routerMiddleware - The mounted router's `routes()` middleware.\n * @param matchesMountPrefix - The mounted router's own prefix test, when it\n * implements {@link import('./application').Routable.matchesMountPrefix} —\n * see {@link resolveMountedPath}.\n */\nexport function createPrefixMount(\n normalizedPrefix: string,\n routerMiddleware: Middleware,\n matchesMountPrefix?: (path: string, prefix: string) => string | undefined\n): Middleware {\n return async (ctx: Context, next): Promise<void> => {\n const currentPath = ctx.path;\n\n const adjustedPath = resolveMountedPath(currentPath, normalizedPrefix, matchesMountPrefix);\n if (adjustedPath === undefined) {\n return next();\n }\n\n // Direct path manipulation (no Proxy - fast!)\n (ctx as { path: string }).path = adjustedPath;\n\n // Store original for recovery (Symbol keys avoid ctx.state pollution)\n (ctx.state as Record<symbol, unknown>)[ORIGINAL_PATH] = currentPath;\n (ctx.state as Record<symbol, unknown>)[ROUTE_PREFIX] = normalizedPrefix;\n\n try {\n await routerMiddleware(ctx, async () => {\n (ctx as { path: string }).path = currentPath;\n await next();\n (ctx as { path: string }).path = adjustedPath;\n });\n } finally {\n (ctx as { path: string }).path = currentPath;\n (ctx.state as Record<symbol, unknown>)[ORIGINAL_PATH] = undefined;\n (ctx.state as Record<symbol, unknown>)[ROUTE_PREFIX] = undefined;\n }\n };\n}\n","/**\n * @nextrush/core - Application Class\n *\n * The Application class is the main entry point for NextRush.\n * It manages middleware registration and the extension lifecycle.\n *\n * See docs/RFC/class-runtime/005-plugin-system.md for the extension model.\n *\n * @packageDocumentation\n */\n\nimport type {\n Container,\n Context,\n Extension,\n ExtensionContext,\n Logger,\n Middleware,\n ProxyTrust,\n RouteEntry,\n Router,\n SecurityAuditCheck,\n} from '@nextrush/types';\nimport { SECURITY_AUDIT } from '@nextrush/types';\nimport { compose } from './middleware';\nimport { writeDefaultErrorResponse } from './error-handler';\nimport { createPrefixMount } from './route-mount';\n\n/**\n * Rejects the two unsafe `proxy` configurations at boot (RFC-030 §8.5):\n * `true` (equivalent to \"trust everything,\" the SEC-01 vulnerability this\n * type exists to close) and `0` (a hop count that can never be satisfied,\n * so it always falls back — almost certainly not what was intended).\n */\nfunction validateProxyTrust(proxy: ProxyTrust): ProxyTrust {\n if ((proxy as unknown) === true) {\n throw new Error(\n \"createApp({ proxy: true }) is no longer valid — trusting every proxy header lets a \" +\n 'remote client forge its own IP. Use `proxy: <hopCount>` (e.g. `proxy: 1` for one ' +\n \"reverse proxy) or `proxy: ['<cidr>', ...]` (a list of trusted proxy IPs/ranges) instead.\"\n );\n }\n if (proxy === 0) {\n throw new Error(\n 'createApp({ proxy: 0 }) trusts zero hops, which is the same as `proxy: false` but ' +\n 'never falls back cleanly — use `proxy: false` to disable proxy trust, or a positive ' +\n 'hop count to trust that many proxies.'\n );\n }\n return proxy;\n}\n\n/**\n * Error handler function type\n */\nexport type ErrorHandler = (error: Error, ctx: Context) => void | Promise<void>;\n\n/**\n * Options for {@link Application.close}.\n *\n * @see RFC-022 (`docs/RFC/class-runtime/022-bounded-teardown-lifecycle.md`) / ADR-0012.\n */\nexport interface CloseOptions {\n /**\n * Bound, in milliseconds, on total teardown time. Every teardown unit\n * (extension `destroy()`) races against this budget independently — a\n * unit that does not finish in time is reported as a {@link TeardownTimeoutError}\n * in the returned array, and never blocks `close()` past the budget.\n *\n * @remarks Omitted = unbounded, identical to calling `close()` with no\n * arguments today (`Promise.allSettled`, no timeout).\n */\n timeout?: number;\n}\n\n/**\n * Reported when a teardown unit (extension `destroy()`) does not settle\n * within the budget passed to {@link Application.close}. Named by the unit\n * so operators can see exactly which extension leaked past the shutdown\n * window (RFC-022 §8.5).\n */\nexport class TeardownTimeoutError extends Error {\n constructor(unitName: string, timeoutMs: number) {\n super(`Teardown unit \"${unitName}\" did not complete within ${String(timeoutMs)}ms`);\n this.name = 'TeardownTimeoutError';\n }\n}\n\n/**\n * One named, independently-runnable teardown unit — the uniform shape shared\n * by extension `destroy()`, class `onShutdown()` (bridged externally), and\n * `onClose` hooks (RFC-022 §7.3: \"teardown becomes a flat list of\n * independently-isolated, budget-raced units\").\n */\ninterface TeardownUnit {\n readonly name: string;\n run(): void | Promise<void>;\n}\n\n/**\n * Run one teardown unit, isolated from every other unit's failure, optionally\n * racing it against `timeoutMs`. Resolves to `null` on success or an `Error`\n * describing the failure/timeout — never throws and never leaves the caller\n * waiting past the budget.\n */\nasync function runTeardownUnit(unit: TeardownUnit, timeoutMs?: number): Promise<Error | null> {\n const settle = Promise.resolve()\n .then(() => unit.run())\n .then((): Error | null => null)\n .catch((err: unknown): Error => (err instanceof Error ? err : new Error(String(err))));\n\n if (timeoutMs === undefined) {\n return settle;\n }\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timedOut = new Promise<Error>((resolve) => {\n timer = setTimeout(() => {\n resolve(new TeardownTimeoutError(unit.name, timeoutMs));\n }, timeoutMs);\n });\n\n try {\n return await Promise.race([settle, timedOut]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** No-op logger — default when no logger is configured */\nconst NOOP_LOGGER: Logger = {\n error(..._args) {\n void _args;\n },\n warn(..._args) {\n void _args;\n },\n info(..._args) {\n void _args;\n },\n debug(..._args) {\n void _args;\n },\n};\n\n/**\n * Application options\n */\nexport interface ApplicationOptions {\n /**\n * Environment mode\n * @default 'development'\n */\n env?: 'development' | 'production' | 'test';\n\n /**\n * Proxy-trust specification for `ctx.ip` resolution (RFC-030, SEC-01).\n *\n * @remarks\n * `false` trusts nothing (the socket peer is always `ctx.ip`); a `number`\n * trusts exactly that many reverse-proxy hops; a `string[]` of CIDR\n * ranges/IPs trusts only a direct peer inside that set. `true` and `0`\n * throw at construction — see {@link validateProxyTrust}.\n *\n * @default false\n */\n proxy?: ProxyTrust;\n\n /**\n * Custom logger. Defaults to no-op (silent).\n *\n * @remarks\n * Pass `console` for quick development logging. For production, provide a\n * structured logger (pino, winston, `@nextrush/logger`) implementing {@link Logger}.\n */\n logger?: Logger;\n\n /**\n * A router the app owns. Route methods (`app.get`, `app.post`, …) delegate to\n * it, and it is mounted last at `ready()`. The `nextrush` meta-package's\n * `createApp` injects one automatically; pass it explicitly when using\n * `@nextrush/core` directly.\n */\n router?: Router;\n\n /**\n * A per-app DI container. Exposed to extensions via `ExtensionContext.container`\n * and used by class-based registrars (e.g. `registerControllers`). Injected by\n * `nextrush/class`; omitted for functional, DI-free apps.\n */\n container?: Container;\n}\n\n/**\n * Listener callback type\n */\nexport type ListenCallback = () => void;\n\n/**\n * Routable interface - any object with routes() method.\n * Allows mounting Router instances without a circular dependency.\n */\nexport interface Routable {\n routes(): Middleware;\n /**\n * Test whether `path` falls under this router's mount prefix, using the\n * SAME normalization the router itself matches with (case folding per its\n * `caseSensitive` option, structural normalization) — never a raw\n * `startsWith` comparison, which would apply a different rule than the\n * router's own dispatch (RFC-029, SEC-02/SEC-15). Optional so a minimal\n * `Routable` (e.g. a test double) still mounts; {@link createPrefixMount}\n * falls back to a literal prefix test when this is absent.\n *\n * @param path - The full, still-query-stripped request path being tested.\n * @param prefix - The normalized mount prefix (leading `/`, no trailing `/`).\n * @returns The path's remainder past the prefix (e.g. `/users` for\n * `/Admin/Users` mounted at `/admin`), or `undefined` if `path` is not\n * under `prefix` per this router's own normalization.\n */\n matchesMountPrefix?(path: string, prefix: string): string | undefined;\n}\n\n/**\n * The Application class\n *\n * @example\n * ```typescript\n * const app = createApp();\n *\n * app.use(async (ctx) => {\n * ctx.json({ message: 'Hello World' });\n * });\n *\n * // Extensions (rare — long-lived services) are booted at ready(), which\n * // adapters call automatically before start().\n * app.extend(events());\n *\n * listen(app, { port: 8080 });\n * ```\n */\nexport class Application {\n private readonly middlewareStack: Middleware[] = [];\n\n /**\n * Boot-time security audit checks contributed by tagged middleware\n * (`security-boundaries` capability, task 8.1). Collected as `use()`\n * registers each middleware; run once by `_boot()`, production-only.\n */\n private readonly securityAudits: SecurityAuditCheck[] = [];\n\n /** Registered extensions, in registration order (setup runs at ready()) */\n private readonly extensions: Extension<unknown>[] = [];\n\n /** Registered extension names — enforces uniqueness */\n private readonly extensionNames = new Set<string>();\n\n /**\n * Combined teardown-unit registration order: extension `destroy()`s and\n * {@link onClose} hooks interleaved in the exact order they were registered,\n * so `_shutdown()` can run the whole set in one reverse-of-registration pass\n * (RFC-022 §7.3 — \"teardown becomes a flat list of independently-isolated,\n * budget-raced units\"). Populated lazily by {@link extend} (for extensions\n * declaring `destroy()`) and {@link onClose}.\n */\n private readonly teardownUnits: TeardownUnit[] = [];\n\n /** Whether {@link close} has started — {@link onClose} is a pre-shutdown-only registration point (RFC-022 §8.6). */\n private _closeStarted = false;\n\n /** Names decorated onto the app — enforces collision detection */\n private readonly decorations = new Set<string>();\n\n private _errorHandler: ErrorHandler | null = null;\n\n readonly logger: Logger;\n\n readonly options: ApplicationOptions;\n\n /** Whether the app is running (server listening) */\n private _isRunning = false;\n\n /** Whether the app has been booted (extensions set up, config frozen) */\n private _isReady = false;\n\n /** Whether ready() mounted the app-owned router (so close() can undo it) */\n private _routerMounted = false;\n\n /** In-flight boot promise — memoized so concurrent ready() calls share one boot (H-1) */\n private _readyPromise: Promise<this> | null = null;\n\n /** In-flight shutdown promise — memoized so concurrent close() calls share one (H-3) */\n private _closePromise: Promise<Error[]> | null = null;\n\n /** The app-owned router (optional). Route methods delegate to it. */\n readonly router?: Router;\n\n /** The app-owned DI container (optional). Exposed to extensions and registrars. */\n readonly container?: Container;\n\n constructor(options: ApplicationOptions = {}) {\n const proxy = validateProxyTrust(options.proxy ?? false);\n this.options = {\n env: options.env ?? 'development',\n proxy,\n };\n this.logger = options.logger ?? NOOP_LOGGER;\n this.router = options.router;\n this.container = options.container;\n }\n\n /** Check if running in production */\n get isProduction(): boolean {\n return this.options.env === 'production';\n }\n\n /** Check if app is running */\n get isRunning(): boolean {\n return this._isRunning;\n }\n\n /**\n * Whether a graceful shutdown is currently in progress — `true` from the\n * moment {@link close} begins tearing down until it completes (F-12:\n * shutdown observability, RFC-022). A readiness check or operator tooling\n * can read this to reflect an in-progress drain.\n */\n get isDraining(): boolean {\n return this._closeStarted;\n }\n\n /** Check if app has been booted via ready() */\n get isReady(): boolean {\n return this._isReady;\n }\n\n /** Get middleware count */\n get middlewareCount(): number {\n return this.middlewareStack.length;\n }\n\n /** Get registered extension count */\n get extensionCount(): number {\n return this.extensions.length;\n }\n\n /**\n * Throws if the app configuration is frozen (after ready() or start()).\n * Prevents unsafe mutations once the app has booted or is serving traffic.\n */\n private assertConfigurable(method: string): void {\n if (this._isReady || this._isRunning) {\n throw new Error(\n `Cannot call ${method}() after the app has booted (ready()) or started — ` +\n `configuration is frozen`\n );\n }\n }\n\n /**\n * Register middleware function(s)\n *\n * @param middleware - Middleware function(s)\n * @returns this for chaining\n */\n use(...middleware: Middleware[]): this {\n this.assertConfigurable('use');\n for (const mw of middleware) {\n if (typeof mw !== 'function') {\n throw new TypeError('Middleware must be a function');\n }\n this.middlewareStack.push(mw);\n const audit = (mw as Partial<Record<typeof SECURITY_AUDIT, SecurityAuditCheck>>)[\n SECURITY_AUDIT\n ];\n if (typeof audit === 'function') {\n this.securityAudits.push(audit);\n }\n }\n return this;\n }\n\n /**\n * Mount a router at a path prefix.\n *\n * @param path - Path prefix (e.g. '/api/users')\n * @param router - Router instance to mount\n * @returns this for chaining\n */\n route(path: string, router: Routable): this {\n this.assertConfigurable('route');\n // Root mount optimization: skip all prefix processing\n if (path === '/' || path === '') {\n this.middlewareStack.push(router.routes());\n return this;\n }\n\n const routerMiddleware = router.routes();\n // Normalize prefix: ensure leading '/', strip trailing '/'\n let normalizedPrefix = path.endsWith('/') ? path.slice(0, -1) : path;\n if (!normalizedPrefix.startsWith('/')) {\n normalizedPrefix = '/' + normalizedPrefix;\n }\n\n this.middlewareStack.push(\n createPrefixMount(normalizedPrefix, routerMiddleware, router.matchesMountPrefix?.bind(router))\n );\n return this;\n }\n\n private requireRouter(): Router {\n if (!this.router) {\n throw new Error(\n 'No router configured. Create your app with `createApp()` from `nextrush` ' +\n '(batteries-included), or pass one: `createApp({ router: createRouter() })`.'\n );\n }\n return this.router;\n }\n\n /** Register a GET route on the app-owned router. */\n get(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('get');\n this.requireRouter().get(path, ...entries);\n return this;\n }\n\n /** Register a POST route on the app-owned router. */\n post(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('post');\n this.requireRouter().post(path, ...entries);\n return this;\n }\n\n /** Register a PUT route on the app-owned router. */\n put(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('put');\n this.requireRouter().put(path, ...entries);\n return this;\n }\n\n /** Register a PATCH route on the app-owned router. */\n patch(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('patch');\n this.requireRouter().patch(path, ...entries);\n return this;\n }\n\n /** Register a DELETE route on the app-owned router. */\n delete(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('delete');\n this.requireRouter().delete(path, ...entries);\n return this;\n }\n\n /** Register a HEAD route on the app-owned router. */\n head(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('head');\n this.requireRouter().head(path, ...entries);\n return this;\n }\n\n /**\n * Register a route for all HTTP methods on the app-owned router.\n *\n * @remarks\n * There is intentionally no `app.options()` verb method — it would collide\n * with the `app.options` configuration property. Register OPTIONS routes via\n * `app.all()`, the router directly, or let CORS middleware handle preflight.\n */\n all(path: string, ...entries: RouteEntry[]): this {\n this.assertConfigurable('all');\n this.requireRouter().all(path, ...entries);\n return this;\n }\n\n /**\n * Set the application error handler. Replaces any previously set handler.\n *\n * @param handler - Error handler function\n * @returns this for chaining\n */\n setErrorHandler(handler: ErrorHandler): this {\n this.assertConfigurable('setErrorHandler');\n this._errorHandler = handler;\n return this;\n }\n\n // Extension System. See docs/RFC/class-runtime/005-plugin-system.md\n\n /**\n * Register an extension. Queues it — `setup()` runs later, at `ready()`,\n * in registration order. Synchronous and chainable.\n *\n * @param extension - Extension to register. If it declares a decorated\n * shape via `Extension<TDecorated>`, the return type carries that shape —\n * `app.extend(events<MyEvents>()).events` is statically known, no\n * `declare module` augmentation required.\n * @returns this, intersected with the extension's declared decorated shape\n *\n * @example\n * ```typescript\n * const extended = app.extend(events<MyEvents>());\n * await app.ready(); // adapters call this automatically — runs setup()/decorate()\n * extended.events.emit('user:created', { id: '1' }); // available only AFTER ready()\n * ```\n */\n extend<TDecorated = Record<string, never>>(\n extension: Extension<TDecorated>\n ): this & TDecorated {\n this.assertConfigurable('extend');\n // Runtime guard for externally-supplied input: the type says non-null with a\n // required setup(), but user/plugin code can still violate that at runtime.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- defensive validation of external input\n if (!extension || typeof extension.setup !== 'function') {\n throw new TypeError('Extension must have a setup() method');\n }\n if (this.extensionNames.has(extension.name)) {\n throw new Error(\n `Extension \"${extension.name}\" is already registered. Use a different extension ` +\n `name, or check for a duplicate app.extend() call.`\n );\n }\n this.extensionNames.add(extension.name);\n this.extensions.push(extension);\n if (typeof extension.destroy === 'function') {\n const destroyableExtension = extension as Extension<TDecorated> & {\n destroy: () => void | Promise<void>;\n };\n this.teardownUnits.push({\n name: extension.name,\n run: () => destroyableExtension.destroy(),\n });\n }\n return this as this & TDecorated;\n }\n\n /**\n * Boot the application: run every registered extension's `setup()` once, in\n * registration order, awaiting async setups. Idempotent — safe to call twice.\n * Adapters call this automatically before `start()`.\n *\n * After `ready()` resolves, the configuration is frozen (`use`/`route`/`extend`\n * throw).\n *\n * @returns this\n * @throws if an extension declares a `needs` dependency not yet registered\n */\n async ready(): Promise<this> {\n if (this._isReady) {\n return this;\n }\n\n // Memoize the in-flight boot so concurrent `await app.ready()` calls share a\n // single setup() pass (H-1). Without this, two racing callers both clear the\n // `_isReady` guard, run every extension's setup() twice, and the second\n // decorate() throws a collision. On failure, clear the memo so a retry can\n // re-attempt boot.\n this._readyPromise ??= this._boot().catch((err: unknown) => {\n this._readyPromise = null;\n throw err;\n });\n return this._readyPromise;\n }\n\n /**\n * Run the boot sequence once (extension setup + router mount). Guarded and\n * memoized by {@link ready}.\n */\n private async _boot(): Promise<this> {\n // extension SOMEWHERE (regardless of order) — catches a typo'd or\n // never-registered dependency name with a distinct message from the\n // order-sensitive check below, before running any setup().\n const allNames = new Set(this.extensions.map((e) => e.name));\n for (const extension of this.extensions) {\n if (extension.needs) {\n for (const dep of extension.needs) {\n if (!allNames.has(dep)) {\n throw new Error(\n `Extension \"${extension.name}\" needs \"${dep}\", but no extension named ` +\n `\"${dep}\" was ever registered.`\n );\n }\n }\n }\n }\n\n const setupDone = new Set<string>();\n\n for (const extension of this.extensions) {\n // Declare-and-assert dependency check (no auto-sort; registration order is the order)\n if (extension.needs) {\n for (const dep of extension.needs) {\n if (!setupDone.has(dep)) {\n throw new Error(\n `Extension \"${extension.name}\" needs \"${dep}\", but \"${dep}\" was not ` +\n `registered before it. Register the \"${dep}\" extension before \"${extension.name}\".`\n );\n }\n }\n }\n\n const ctx: ExtensionContext = {\n app: this,\n logger: this.logger,\n container: this.container,\n env: this.options.env ?? 'development',\n name: extension.name,\n decorate: (name: string, value: unknown): void => {\n this.decorate(name, value);\n },\n };\n\n await extension.setup(ctx);\n setupDone.add(extension.name);\n }\n\n // Mount the app-owned router LAST, so routes run after all middleware\n // (both user- and extension-registered).\n if (this.router) {\n this.middlewareStack.push(this.router.routes());\n this._routerMounted = true;\n }\n\n if (this.isProduction) {\n this._runSecurityAudits();\n }\n\n this._isReady = true;\n return this;\n }\n\n /**\n * Run every registered middleware's boot-time security audit check\n * (`security-boundaries` capability, task 8.1/8.2). `throw`-level verdicts\n * fail boot immediately; `warn`-level verdicts log once each via the app\n * logger and never block boot. Production-only — `_boot()` calls this\n * conditionally so a dev/test app never pays the cost or sees the noise.\n */\n private _runSecurityAudits(): void {\n for (const audit of this.securityAudits) {\n const verdict = audit();\n if (verdict.level === 'ok') {\n continue;\n }\n if (verdict.level === 'throw') {\n throw new Error(`[security-boundaries] ${verdict.message}`);\n }\n this.logger.warn(`[security-boundaries] ${verdict.message}`);\n }\n }\n\n /**\n * Attach a value to the app under `name`. Extension-author primitive, invoked\n * through {@link ExtensionContext.decorate} — there is intentionally no public\n * `app.decorate()` (RFC §6.1). Throws on collision.\n *\n * @internal\n */\n private decorate(name: string, value: unknown): void {\n if (this.decorations.has(name)) {\n throw new Error(\n `Decoration \"${name}\" already exists on the application. Choose a different ` +\n `name, or check for a duplicate ctx.decorate() call across your extensions.`\n );\n }\n if (name in this) {\n throw new Error(\n `Decoration \"${name}\" collides with an existing Application member — choose another name`\n );\n }\n Object.defineProperty(this, name, {\n value,\n enumerable: true,\n writable: false,\n configurable: true, // allow close() to clean up\n });\n this.decorations.add(name);\n }\n\n /**\n * Whether a decoration already occupies `name`.\n */\n hasDecorator(name: string): boolean {\n return this.decorations.has(name);\n }\n\n /**\n * Create the request handler callback.\n *\n * The middleware stack is snapshot at call time — call `ready()` before\n * `callback()` so extension-registered middleware is included. Adapters do\n * this automatically.\n *\n * @returns Request handler function\n */\n callback(): (ctx: Context) => Promise<void> {\n if (!this._isReady && this.extensions.length > 0) {\n this.logger.warn(\n `callback() was called before ready(), but ${String(this.extensions.length)} extension(s) ` +\n `were registered via extend() — their setup() will NOT run, and anything they ` +\n `would decorate (e.g. app.events) will be missing. Call \\`await app.ready()\\` ` +\n `before \\`callback()\\`. Adapters (listen/serve) do this automatically.`\n );\n }\n\n const fn = compose(this.middlewareStack, {\n warnDoubleResponse: !this.isProduction,\n });\n\n return (ctx: Context): Promise<void> =>\n fn(ctx).then(undefined, (error: unknown) => this.handleError(error, ctx));\n }\n\n /**\n * Handle errors - uses custom handler if set, otherwise default\n */\n private async handleError(error: unknown, ctx: Context): Promise<void> {\n const err = error instanceof Error ? error : new Error(String(error));\n\n if (this._errorHandler) {\n try {\n await this._errorHandler(err, ctx);\n return;\n } catch (handlerError) {\n this.logger.error('Error handler threw:', handlerError);\n }\n }\n\n // The default handler can itself throw (e.g. ctx.json when the response is\n // already committed). Swallow-and-log so the request settles instead of\n // rejecting out of callback() into the adapter as an unhandled error (H-2).\n try {\n this.defaultErrorHandler(err, ctx);\n } catch (fatal) {\n this.logger.error('Default error handler threw:', fatal);\n }\n }\n\n /**\n * Default error handler — delegates to the shared error serializer so the\n * default response matches `@nextrush/errors`' `errorHandler()` (audit C-1).\n */\n private defaultErrorHandler(error: Error, ctx: Context): void {\n writeDefaultErrorResponse(error, ctx, {\n logger: this.logger,\n isProduction: this.isProduction,\n });\n }\n\n /**\n * Mark app as running and freeze configuration.\n *\n * Called by adapters when the server starts listening (after `ready()`).\n */\n start(): void {\n this._isRunning = true;\n }\n\n /**\n * Register a teardown hook for a resource outside the extension system\n * (stateful middleware, a manually-attached service, …). Run during\n * {@link close} under the SAME bounded/isolated guarantee as extension\n * `destroy()` — one throwing/hanging hook never strands the others — in one\n * combined reverse-of-registration order together with every `extend()`ed\n * extension's `destroy()` (RFC-022 §8.1/§8.2).\n *\n * @param hook - Teardown callback. May be sync or async; a thrown/rejected\n * hook is isolated and its error collected in {@link close}'s returned array.\n *\n * @remarks Registration is pre-shutdown only: a hook registered after\n * `close()` has already started is not run in that shutdown (RFC-022 §8.6).\n *\n * @example\n * ```typescript\n * const store = new MemoryStore(options);\n * app.onClose(() => store.shutdown());\n * ```\n */\n onClose(hook: () => void | Promise<void>): void {\n if (this._closeStarted) {\n return;\n }\n this.teardownUnits.push({ name: 'onClose hook', run: hook });\n }\n\n /**\n * Graceful shutdown. Destroys extensions in reverse registration order,\n * each isolated from the others' failures — one failing/hanging `destroy()`\n * never strands the rest (RFC-022 / ADR-0012).\n *\n * @param options - Optional teardown budget. Omitted = unbounded, byte-identical\n * to today's behavior (`Promise.allSettled`, no timeout). When `timeout` is\n * given, every teardown unit races against it independently; a unit that\n * does not finish in time is reported as a {@link TeardownTimeoutError} in\n * the returned array instead of blocking `close()` past the budget.\n * @returns Array of errors from units that failed or timed out (empty on success)\n */\n async close(options?: CloseOptions): Promise<Error[]> {\n // Memoize the in-flight shutdown so concurrent close() calls (e.g. two\n // signal handlers) destroy each extension exactly once (H-3).\n this._closePromise ??= this._shutdown(options);\n return this._closePromise;\n }\n\n /** Run the shutdown sequence once. Guarded and memoized by {@link close}. */\n private async _shutdown(options?: CloseOptions): Promise<Error[]> {\n this._isRunning = false;\n this._closeStarted = true;\n // F-12: surface the draining transition so operators/readiness checks can\n // observe an in-progress shutdown instead of it being a silent black box.\n this.logger.info('Application is draining: graceful shutdown starting');\n\n if (!this._isReady && this.extensions.length > 0) {\n this.logger.warn(\n `close() was called before ready(), but ${String(this.extensions.length)} extension(s) ` +\n `were registered via extend() — their setup() never ran, yet destroy() will still ` +\n `be called on them now. If destroy() assumes setup()-established state, this may ` +\n `throw or behave incorrectly. Call \\`await app.ready()\\` before \\`close()\\`.`\n );\n }\n\n const timeoutMs = options?.timeout;\n // Reverse of registration order across BOTH extension destroy() calls and\n // onClose hooks — one uniform, combined teardown-unit list (RFC-022 §7.3),\n // so a hook registered between two extend() calls runs at the correct\n // point in the reversed sequence, not as a separate pass.\n const units = [...this.teardownUnits].reverse();\n\n const errors = (\n await Promise.all(units.map((unit) => runTeardownUnit(unit, timeoutMs)))\n ).filter((e): e is Error => e !== null);\n\n // F-12: report the teardown outcome at completion — which unit(s) failed\n // or timed out — rather than exiting silently. TeardownTimeoutError's own\n // message already names the offending unit (§8.5).\n for (const error of errors) {\n this.logger.error('Teardown unit failed during shutdown:', error);\n }\n\n // Clean up decorations so the instance can be re-booted (e.g. in tests)\n for (const name of this.decorations) {\n Reflect.deleteProperty(this, name);\n }\n this.decorations.clear();\n this.extensions.length = 0;\n this.extensionNames.clear();\n this.teardownUnits.length = 0;\n this._isReady = false;\n // Reset the boot/shutdown memos so the instance can be cleanly re-booted\n // (re-boot in tests / hot reload) — a fresh ready()/close() must re-run.\n this._readyPromise = null;\n this._closePromise = null;\n this._closeStarted = false;\n\n // Undo the router mount that ready() appended, so a subsequent ready()\n // (re-boot in tests / hot reload) does not stack a second router (audit C-2).\n if (this._routerMounted) {\n this.middlewareStack.pop();\n this._routerMounted = false;\n }\n\n return errors;\n }\n}\n\n/**\n * Create a new Application instance\n *\n * @param options - Application options\n * @returns New Application instance\n */\nexport function createApp(options?: ApplicationOptions): Application {\n return new Application(options);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextrush/core",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.2",
|
|
4
4
|
"description": "Core functionality for NextRush framework - Application, Context, Middleware",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@nextrush/errors": "4.0.0-beta.
|
|
50
|
-
"@nextrush/types": "4.0.0-beta.
|
|
49
|
+
"@nextrush/errors": "4.0.0-beta.2",
|
|
50
|
+
"@nextrush/types": "4.0.0-beta.2"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^25.9.5",
|