@nextrush/class 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2371 @@
1
+ import { InternalServerError, BadRequestError, ForbiddenError } from '@nextrush/errors';
2
+ export { HttpError } from '@nextrush/errors';
3
+ import { readdir } from 'node:fs/promises';
4
+ import { resolve, join, sep, extname } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import 'reflect-metadata';
7
+ import { markInjectable, createContainer, container, hasServiceMetadata, getServiceScope, getOptionalParams, DIError } from '@nextrush/di';
8
+ export { Repository, Service, container, createContainer, inject } from '@nextrush/di';
9
+ import { ROUTE_METADATA } from '@nextrush/types';
10
+
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
14
+ var __esm = (fn, res) => function __init() {
15
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
16
+ };
17
+ var __export = (target, all) => {
18
+ for (var name in all)
19
+ __defProp(target, name, { get: all[name], enumerable: true });
20
+ };
21
+
22
+ // src/metadata/metadata-keys.ts
23
+ function isValidHttpMethod(method) {
24
+ return [
25
+ "GET",
26
+ "POST",
27
+ "PUT",
28
+ "DELETE",
29
+ "PATCH",
30
+ "HEAD",
31
+ "OPTIONS",
32
+ "ALL"
33
+ ].includes(method);
34
+ }
35
+ function isValidParamSource(source) {
36
+ return [
37
+ "body",
38
+ "query",
39
+ "param",
40
+ "header",
41
+ "ctx",
42
+ "req",
43
+ "res",
44
+ "custom"
45
+ ].includes(source);
46
+ }
47
+ function isGuardClass(guard) {
48
+ if (typeof guard !== "function") {
49
+ return false;
50
+ }
51
+ const proto = guard.prototype;
52
+ if (!proto || typeof proto !== "object") {
53
+ return false;
54
+ }
55
+ return typeof proto.canActivate === "function";
56
+ }
57
+ var DECORATOR_METADATA_KEYS;
58
+ var init_metadata_keys = __esm({
59
+ "src/metadata/metadata-keys.ts"() {
60
+ DECORATOR_METADATA_KEYS = {
61
+ CONTROLLER: /* @__PURE__ */ Symbol.for("nextrush:controller"),
62
+ MODULE: /* @__PURE__ */ Symbol.for("nextrush:module"),
63
+ ROUTES: /* @__PURE__ */ Symbol.for("nextrush:routes"),
64
+ PARAMS: /* @__PURE__ */ Symbol.for("nextrush:params"),
65
+ MIDDLEWARE: /* @__PURE__ */ Symbol.for("nextrush:middleware"),
66
+ GUARDS: /* @__PURE__ */ Symbol.for("nextrush:guards"),
67
+ INTERCEPTORS: /* @__PURE__ */ Symbol.for("nextrush:interceptors"),
68
+ FILTERS: /* @__PURE__ */ Symbol.for("nextrush:filters"),
69
+ CATCH: /* @__PURE__ */ Symbol.for("nextrush:catch"),
70
+ RESPONSE_HEADERS: /* @__PURE__ */ Symbol.for("nextrush:response-headers"),
71
+ REDIRECT: /* @__PURE__ */ Symbol.for("nextrush:redirect"),
72
+ HTTP_CODE: /* @__PURE__ */ Symbol.for("nextrush:http-code")
73
+ };
74
+ __name(isValidHttpMethod, "isValidHttpMethod");
75
+ __name(isValidParamSource, "isValidParamSource");
76
+ __name(isGuardClass, "isGuardClass");
77
+ }
78
+ });
79
+
80
+ // src/lifecycle/lifecycle-types.ts
81
+ function isOnInit(value) {
82
+ return typeof value === "object" && value !== null && typeof value.onInit === "function";
83
+ }
84
+ function isOnShutdown(value) {
85
+ return typeof value === "object" && value !== null && typeof value.onShutdown === "function";
86
+ }
87
+ var init_lifecycle_types = __esm({
88
+ "src/lifecycle/lifecycle-types.ts"() {
89
+ __name(isOnInit, "isOnInit");
90
+ __name(isOnShutdown, "isOnShutdown");
91
+ }
92
+ });
93
+
94
+ // src/types.ts
95
+ var init_types = __esm({
96
+ "src/types.ts"() {
97
+ init_metadata_keys();
98
+ }
99
+ });
100
+
101
+ // src/reflection/reflection.ts
102
+ function getConstructorParamTypes(target) {
103
+ const DESIGN_PARAMTYPES = "design:paramtypes";
104
+ return Reflect.getMetadata(DESIGN_PARAMTYPES, target) ?? [];
105
+ }
106
+ function getMetadata(key, target, propertyKey) {
107
+ if (propertyKey !== void 0) {
108
+ return Reflect.getMetadata(key, target, propertyKey);
109
+ }
110
+ return Reflect.getMetadata(key, target);
111
+ }
112
+ function getOwnMetadata(key, target) {
113
+ return Reflect.getOwnMetadata(key, target);
114
+ }
115
+ function defineMetadata(key, value, target, propertyKey) {
116
+ if (propertyKey !== void 0) {
117
+ Reflect.defineMetadata(key, value, target, propertyKey);
118
+ } else {
119
+ Reflect.defineMetadata(key, value, target);
120
+ }
121
+ }
122
+ function hasOwnMetadata(key, target) {
123
+ return Reflect.hasOwnMetadata(key, target);
124
+ }
125
+ var init_reflection = __esm({
126
+ "src/reflection/reflection.ts"() {
127
+ __name(getConstructorParamTypes, "getConstructorParamTypes");
128
+ __name(getMetadata, "getMetadata");
129
+ __name(getOwnMetadata, "getOwnMetadata");
130
+ __name(defineMetadata, "defineMetadata");
131
+ __name(hasOwnMetadata, "hasOwnMetadata");
132
+ }
133
+ });
134
+
135
+ // src/metadata/metadata.ts
136
+ function isController(target) {
137
+ return getOwnMetadata(DECORATOR_METADATA_KEYS.CONTROLLER, target) !== void 0;
138
+ }
139
+ function getControllerMetadata(target) {
140
+ const meta = getOwnMetadata(DECORATOR_METADATA_KEYS.CONTROLLER, target);
141
+ return meta ? {
142
+ ...meta
143
+ } : void 0;
144
+ }
145
+ function getRouteMetadata(target) {
146
+ const routes = getOwnMetadata(DECORATOR_METADATA_KEYS.ROUTES, target);
147
+ return routes ? [
148
+ ...routes
149
+ ] : [];
150
+ }
151
+ function getParamMetadata(target, methodName) {
152
+ const allParams = getOwnMetadata(DECORATOR_METADATA_KEYS.PARAMS, target);
153
+ const params = allParams?.get(String(methodName));
154
+ return params ? [
155
+ ...params
156
+ ] : [];
157
+ }
158
+ function getAllParamMetadata(target) {
159
+ const params = getOwnMetadata(DECORATOR_METADATA_KEYS.PARAMS, target);
160
+ if (!params) return /* @__PURE__ */ new Map();
161
+ const copy = /* @__PURE__ */ new Map();
162
+ for (const [key, value] of params) {
163
+ copy.set(key, [
164
+ ...value
165
+ ]);
166
+ }
167
+ return copy;
168
+ }
169
+ function getControllerDefinition(target) {
170
+ const controller = getControllerMetadata(target);
171
+ if (!controller) {
172
+ return void 0;
173
+ }
174
+ return {
175
+ target,
176
+ controller,
177
+ routes: getRouteMetadata(target),
178
+ params: getAllParamMetadata(target)
179
+ };
180
+ }
181
+ function getResponseHeaders(target, methodName) {
182
+ const map = getOwnMetadata(DECORATOR_METADATA_KEYS.RESPONSE_HEADERS, target);
183
+ return [
184
+ ...map?.get(methodName) ?? []
185
+ ];
186
+ }
187
+ function getRedirectMetadata(target, methodName) {
188
+ const map = getOwnMetadata(DECORATOR_METADATA_KEYS.REDIRECT, target);
189
+ return map?.get(methodName);
190
+ }
191
+ function getHttpCode(target, methodName) {
192
+ const map = getOwnMetadata(DECORATOR_METADATA_KEYS.HTTP_CODE, target);
193
+ return map?.get(methodName);
194
+ }
195
+ var init_metadata = __esm({
196
+ "src/metadata/metadata.ts"() {
197
+ init_types();
198
+ init_reflection();
199
+ __name(isController, "isController");
200
+ __name(getControllerMetadata, "getControllerMetadata");
201
+ __name(getRouteMetadata, "getRouteMetadata");
202
+ __name(getParamMetadata, "getParamMetadata");
203
+ __name(getAllParamMetadata, "getAllParamMetadata");
204
+ __name(getControllerDefinition, "getControllerDefinition");
205
+ __name(getResponseHeaders, "getResponseHeaders");
206
+ __name(getRedirectMetadata, "getRedirectMetadata");
207
+ __name(getHttpCode, "getHttpCode");
208
+ }
209
+ });
210
+ var ControllerError, NotAControllerError, NoRoutesError, DiscoveryError, ControllerResolutionError, ParameterInjectionError, MissingParameterError, RouteRegistrationError, GuardRejectionError, NotAModuleError;
211
+ var init_errors = __esm({
212
+ "src/errors.ts"() {
213
+ ControllerError = class extends InternalServerError {
214
+ static {
215
+ __name(this, "ControllerError");
216
+ }
217
+ constructor(message, code, options) {
218
+ super(message, {
219
+ code,
220
+ ...options
221
+ });
222
+ this.name = "ControllerError";
223
+ }
224
+ };
225
+ NotAControllerError = class extends ControllerError {
226
+ static {
227
+ __name(this, "NotAControllerError");
228
+ }
229
+ constructor(className) {
230
+ super(`Class "${className}" is not a controller.
231
+
232
+ To make it a controller, add the @Controller decorator:
233
+
234
+ import { Controller } from '@nextrush/class';
235
+
236
+ @Controller('/path')
237
+ class ${className} {
238
+ // ...
239
+ }
240
+ `, "NOT_A_CONTROLLER");
241
+ this.name = "NotAControllerError";
242
+ }
243
+ };
244
+ NoRoutesError = class extends ControllerError {
245
+ static {
246
+ __name(this, "NoRoutesError");
247
+ }
248
+ constructor(className) {
249
+ super(`Controller "${className}" has no routes defined.
250
+
251
+ Add route decorators to your controller methods:
252
+
253
+ import { Controller, Get, Post } from '@nextrush/class';
254
+
255
+ @Controller('/users')
256
+ class ${className} {
257
+ @Get()
258
+ findAll() { }
259
+
260
+ @Post()
261
+ create(@Body() data: CreateDto) { }
262
+ }
263
+ `, "NO_ROUTES");
264
+ this.name = "NoRoutesError";
265
+ }
266
+ };
267
+ DiscoveryError = class extends ControllerError {
268
+ static {
269
+ __name(this, "DiscoveryError");
270
+ }
271
+ filePath;
272
+ constructor(filePath, reason, cause) {
273
+ super(`Failed to discover controllers in "${filePath}".
274
+
275
+ Reason: ${reason}
276
+
277
+ Possible fixes:
278
+ 1. Ensure the file exists and is accessible
279
+ 2. Check for syntax errors in the file
280
+ 3. Verify the file exports controller classes
281
+ `, "DISCOVERY_ERROR", {
282
+ cause
283
+ });
284
+ this.name = "DiscoveryError";
285
+ this.filePath = filePath;
286
+ }
287
+ };
288
+ ControllerResolutionError = class extends ControllerError {
289
+ static {
290
+ __name(this, "ControllerResolutionError");
291
+ }
292
+ controllerName;
293
+ constructor(controllerName, cause) {
294
+ super(`Failed to resolve controller "${controllerName}" from DI container.
295
+
296
+ Possible causes:
297
+ 1. Controller is not registered in the DI container
298
+ 2. Controller has unresolvable dependencies
299
+ 3. Circular dependency detected
300
+
301
+ Note: @Controller automatically registers with DI - no @Service() needed!
302
+
303
+ import { Controller } from '@nextrush/class';
304
+
305
+ @Controller('/path')
306
+ class ${controllerName} {
307
+ constructor(private readonly service: SomeService) { }
308
+ }
309
+ `, "CONTROLLER_RESOLUTION_ERROR", {
310
+ cause
311
+ });
312
+ this.name = "ControllerResolutionError";
313
+ this.controllerName = controllerName;
314
+ }
315
+ };
316
+ ParameterInjectionError = class extends BadRequestError {
317
+ static {
318
+ __name(this, "ParameterInjectionError");
319
+ }
320
+ controllerName;
321
+ methodName;
322
+ paramIndex;
323
+ constructor(controllerName, methodName, paramIndex, reason) {
324
+ super(`Invalid parameter at index ${paramIndex} for "${controllerName}.${methodName}": ${reason}`, {
325
+ code: "PARAMETER_INJECTION_ERROR",
326
+ details: {
327
+ controller: controllerName,
328
+ method: methodName,
329
+ parameterIndex: paramIndex,
330
+ reason
331
+ }
332
+ });
333
+ this.name = "ParameterInjectionError";
334
+ this.controllerName = controllerName;
335
+ this.methodName = methodName;
336
+ this.paramIndex = paramIndex;
337
+ }
338
+ };
339
+ MissingParameterError = class extends BadRequestError {
340
+ static {
341
+ __name(this, "MissingParameterError");
342
+ }
343
+ controllerName;
344
+ methodName;
345
+ paramName;
346
+ source;
347
+ constructor(controllerName, methodName, paramName, source, messageOverride) {
348
+ super(messageOverride ?? `Required ${source} parameter "${paramName}" is missing`, {
349
+ code: "MISSING_PARAMETER",
350
+ details: {
351
+ parameter: paramName,
352
+ source,
353
+ controller: controllerName,
354
+ method: methodName
355
+ }
356
+ });
357
+ this.name = "MissingParameterError";
358
+ this.controllerName = controllerName;
359
+ this.methodName = methodName;
360
+ this.paramName = paramName;
361
+ this.source = source;
362
+ }
363
+ };
364
+ RouteRegistrationError = class extends ControllerError {
365
+ static {
366
+ __name(this, "RouteRegistrationError");
367
+ }
368
+ controllerName;
369
+ method;
370
+ path;
371
+ constructor(controllerName, method, path, reason, cause) {
372
+ super(`Failed to register route ${method} ${path} from controller "${controllerName}".
373
+
374
+ Reason: ${reason}
375
+ `, "ROUTE_REGISTRATION_ERROR", {
376
+ cause
377
+ });
378
+ this.name = "RouteRegistrationError";
379
+ this.controllerName = controllerName;
380
+ this.method = method;
381
+ this.path = path;
382
+ }
383
+ };
384
+ GuardRejectionError = class extends ForbiddenError {
385
+ static {
386
+ __name(this, "GuardRejectionError");
387
+ }
388
+ guardName;
389
+ constructor(guardName, message) {
390
+ super(message ?? "Access denied", {
391
+ code: "GUARD_REJECTED"
392
+ });
393
+ this.name = "GuardRejectionError";
394
+ this.guardName = guardName;
395
+ }
396
+ };
397
+ NotAModuleError = class extends ControllerError {
398
+ static {
399
+ __name(this, "NotAModuleError");
400
+ }
401
+ constructor(className) {
402
+ super(`Class "${className}" is not a module.
403
+
404
+ To make it a module, add the @Module decorator:
405
+
406
+ import { Module } from '@nextrush/class';
407
+
408
+ @Module({
409
+ controllers: [SomeController],
410
+ providers: [SomeService],
411
+ })
412
+ class ${className} {}
413
+ `, "NOT_A_MODULE");
414
+ this.name = "NotAModuleError";
415
+ }
416
+ };
417
+ }
418
+ });
419
+
420
+ // src/discovery/discovery.ts
421
+ var discovery_exports = {};
422
+ __export(discovery_exports, {
423
+ DEFAULT_EXCLUDE: () => DEFAULT_EXCLUDE,
424
+ DEFAULT_INCLUDE: () => DEFAULT_INCLUDE,
425
+ discoverControllers: () => discoverControllers,
426
+ getControllersFromResults: () => getControllersFromResults,
427
+ getErrorsFromResults: () => getErrorsFromResults
428
+ });
429
+ function matchesPattern(filename, patterns) {
430
+ return patterns.some((pattern) => {
431
+ let regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*\//g, "(.*\\/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
432
+ const regex = new RegExp(`^${regexStr}$`);
433
+ return regex.test(filename);
434
+ });
435
+ }
436
+ function shouldInclude(relativePath, includePatterns, excludePatterns) {
437
+ if (matchesPattern(relativePath, excludePatterns)) {
438
+ return false;
439
+ }
440
+ return matchesPattern(relativePath, includePatterns);
441
+ }
442
+ async function scanDirectory(dir, rootDir, includePatterns, excludePatterns) {
443
+ const files = [];
444
+ try {
445
+ const entries = await readdir(dir, {
446
+ withFileTypes: true
447
+ });
448
+ for (const entry of entries) {
449
+ const fullPath = join(dir, entry.name);
450
+ const relativePath = fullPath.slice(rootDir.length + 1).split(sep).join("/");
451
+ if (entry.isDirectory()) {
452
+ if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
453
+ continue;
454
+ }
455
+ const subFiles = await scanDirectory(fullPath, rootDir, includePatterns, excludePatterns);
456
+ files.push(...subFiles);
457
+ } else if (entry.isFile()) {
458
+ const ext = extname(entry.name);
459
+ if ((ext === ".ts" || ext === ".js") && !entry.name.endsWith(".d.ts")) {
460
+ if (shouldInclude(relativePath, includePatterns, excludePatterns)) {
461
+ files.push(fullPath);
462
+ }
463
+ }
464
+ }
465
+ }
466
+ } catch (error) {
467
+ if (error.code !== "ENOENT") {
468
+ throw error;
469
+ }
470
+ }
471
+ return files;
472
+ }
473
+ async function importControllers(filePath, debug) {
474
+ const controllers = [];
475
+ const errors = [];
476
+ try {
477
+ const fileUrl = pathToFileURL(filePath).href;
478
+ const module = await import(fileUrl);
479
+ for (const exportName of Object.keys(module)) {
480
+ const exported = module[exportName];
481
+ if (typeof exported === "function" && isController(exported)) {
482
+ controllers.push(exported);
483
+ if (debug) {
484
+ process.stderr.write(`[Controllers] Discovered: ${exported.name} from ${filePath}
485
+ `);
486
+ }
487
+ }
488
+ }
489
+ } catch (error) {
490
+ errors.push(new DiscoveryError(filePath, error instanceof Error ? error.message : String(error), error instanceof Error ? error : void 0));
491
+ }
492
+ return {
493
+ controllers,
494
+ errors
495
+ };
496
+ }
497
+ async function discoverControllers(options) {
498
+ const rootDir = resolve(options.root);
499
+ const includePatterns = options.include ?? DEFAULT_INCLUDE;
500
+ const excludePatterns = options.exclude ?? DEFAULT_EXCLUDE;
501
+ const debug = options.debug ?? false;
502
+ if (debug) {
503
+ process.stderr.write(`[Controllers] Scanning: ${rootDir}
504
+ `);
505
+ process.stderr.write(`[Controllers] Include: ${includePatterns.join(", ")}
506
+ `);
507
+ process.stderr.write(`[Controllers] Exclude: ${excludePatterns.join(", ")}
508
+ `);
509
+ }
510
+ const files = await scanDirectory(rootDir, rootDir, includePatterns, excludePatterns);
511
+ if (debug) {
512
+ process.stderr.write(`[Controllers] Found ${files.length} files to scan
513
+ `);
514
+ }
515
+ const results = new Array(files.length);
516
+ let cursor = 0;
517
+ const worker = /* @__PURE__ */ __name(async () => {
518
+ for (let index = cursor++; index < files.length; index = cursor++) {
519
+ const filePath = files[index];
520
+ const { controllers, errors } = await importControllers(filePath, debug);
521
+ results[index] = {
522
+ filePath,
523
+ controllers,
524
+ errors
525
+ };
526
+ }
527
+ }, "worker");
528
+ const workerCount = Math.min(IMPORT_CONCURRENCY, files.length);
529
+ await Promise.all(Array.from({
530
+ length: workerCount
531
+ }, () => worker()));
532
+ return results;
533
+ }
534
+ function getControllersFromResults(results) {
535
+ const controllers = [];
536
+ for (const result of results) {
537
+ controllers.push(...result.controllers);
538
+ }
539
+ return controllers;
540
+ }
541
+ function getErrorsFromResults(results) {
542
+ const errors = [];
543
+ for (const result of results) {
544
+ errors.push(...result.errors);
545
+ }
546
+ return errors;
547
+ }
548
+ var DEFAULT_INCLUDE, DEFAULT_EXCLUDE, IMPORT_CONCURRENCY;
549
+ var init_discovery = __esm({
550
+ "src/discovery/discovery.ts"() {
551
+ init_metadata();
552
+ init_errors();
553
+ DEFAULT_INCLUDE = [
554
+ "**/*.controller.ts",
555
+ "**/*.controller.js"
556
+ ];
557
+ DEFAULT_EXCLUDE = [
558
+ "**/*.test.ts",
559
+ "**/*.spec.ts",
560
+ "**/*.test.js",
561
+ "**/*.spec.js",
562
+ "**/node_modules/**",
563
+ "**/dist/**",
564
+ "**/__tests__/**"
565
+ ];
566
+ IMPORT_CONCURRENCY = 16;
567
+ __name(matchesPattern, "matchesPattern");
568
+ __name(shouldInclude, "shouldInclude");
569
+ __name(scanDirectory, "scanDirectory");
570
+ __name(importControllers, "importControllers");
571
+ __name(discoverControllers, "discoverControllers");
572
+ __name(getControllersFromResults, "getControllersFromResults");
573
+ __name(getErrorsFromResults, "getErrorsFromResults");
574
+ }
575
+ });
576
+
577
+ // src/index.ts
578
+ init_types();
579
+ init_lifecycle_types();
580
+
581
+ // src/decorators/class.ts
582
+ init_reflection();
583
+
584
+ // src/path-utils.ts
585
+ function normalizePath(path, options = {}) {
586
+ let normalized = path.trim();
587
+ if (!normalized.startsWith("/")) {
588
+ normalized = "/" + normalized;
589
+ }
590
+ if (options.stripTrailingSlash && normalized.length > 1 && normalized.endsWith("/")) {
591
+ normalized = normalized.slice(0, -1);
592
+ }
593
+ return normalized;
594
+ }
595
+ __name(normalizePath, "normalizePath");
596
+
597
+ // src/decorators/class.ts
598
+ init_types();
599
+ function Controller(pathOrOptions) {
600
+ return /* @__PURE__ */ __name(function controllerDecorator(target) {
601
+ const options = normalizeControllerOptions(pathOrOptions, target.name);
602
+ const metadata = {
603
+ path: options.path ?? "/",
604
+ version: options.version,
605
+ middleware: options.middleware,
606
+ tags: options.tags
607
+ };
608
+ defineMetadata(DECORATOR_METADATA_KEYS.CONTROLLER, metadata, target);
609
+ markInjectable(target);
610
+ return target;
611
+ }, "controllerDecorator");
612
+ }
613
+ __name(Controller, "Controller");
614
+ function normalizeControllerOptions(input, className) {
615
+ if (typeof input === "string") {
616
+ return {
617
+ path: normalizePath(input, {
618
+ stripTrailingSlash: true
619
+ })
620
+ };
621
+ }
622
+ if (input && typeof input === "object") {
623
+ return {
624
+ ...input,
625
+ path: input.path ? normalizePath(input.path, {
626
+ stripTrailingSlash: true
627
+ }) : derivePathFromClassName(className)
628
+ };
629
+ }
630
+ return {
631
+ path: derivePathFromClassName(className)
632
+ };
633
+ }
634
+ __name(normalizeControllerOptions, "normalizeControllerOptions");
635
+ function derivePathFromClassName(className) {
636
+ const baseName = className.replace(/Controller$/i, "");
637
+ if (!baseName) {
638
+ return "/";
639
+ }
640
+ const kebabCase = baseName.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
641
+ return `/${kebabCase}`;
642
+ }
643
+ __name(derivePathFromClassName, "derivePathFromClassName");
644
+
645
+ // src/modules/module.ts
646
+ init_reflection();
647
+ init_metadata_keys();
648
+ function Module(options = {}) {
649
+ return /* @__PURE__ */ __name(function moduleDecorator(target) {
650
+ const metadata = {
651
+ imports: [
652
+ ...options.imports ?? []
653
+ ],
654
+ controllers: [
655
+ ...options.controllers ?? []
656
+ ],
657
+ providers: [
658
+ ...options.providers ?? []
659
+ ],
660
+ exports: [
661
+ ...options.exports ?? []
662
+ ]
663
+ };
664
+ defineMetadata(DECORATOR_METADATA_KEYS.MODULE, metadata, target);
665
+ markInjectable(target);
666
+ return target;
667
+ }, "moduleDecorator");
668
+ }
669
+ __name(Module, "Module");
670
+ function isModule(target) {
671
+ return hasOwnMetadata(DECORATOR_METADATA_KEYS.MODULE, target);
672
+ }
673
+ __name(isModule, "isModule");
674
+ function getModuleMetadata(target) {
675
+ const meta = getOwnMetadata(DECORATOR_METADATA_KEYS.MODULE, target);
676
+ if (!meta) {
677
+ return void 0;
678
+ }
679
+ return {
680
+ imports: [
681
+ ...meta.imports
682
+ ],
683
+ controllers: [
684
+ ...meta.controllers
685
+ ],
686
+ providers: [
687
+ ...meta.providers
688
+ ],
689
+ exports: [
690
+ ...meta.exports
691
+ ]
692
+ };
693
+ }
694
+ __name(getModuleMetadata, "getModuleMetadata");
695
+
696
+ // src/decorators/http-code.ts
697
+ init_reflection();
698
+ init_types();
699
+ function HttpCode(statusCode) {
700
+ return /* @__PURE__ */ __name(function httpCodeDecorator(target, propertyKey, descriptor) {
701
+ const methodKey = String(propertyKey);
702
+ const existing = getOwnMetadata(DECORATOR_METADATA_KEYS.HTTP_CODE, target.constructor) ?? /* @__PURE__ */ new Map();
703
+ existing.set(methodKey, statusCode);
704
+ defineMetadata(DECORATOR_METADATA_KEYS.HTTP_CODE, existing, target.constructor);
705
+ return descriptor;
706
+ }, "httpCodeDecorator");
707
+ }
708
+ __name(HttpCode, "HttpCode");
709
+
710
+ // src/decorators/response-decorators.ts
711
+ init_reflection();
712
+ init_types();
713
+ function SetHeader(name, value) {
714
+ return /* @__PURE__ */ __name(function setHeaderDecorator(target, propertyKey, descriptor) {
715
+ const methodKey = String(propertyKey);
716
+ const existing = getOwnMetadata(DECORATOR_METADATA_KEYS.RESPONSE_HEADERS, target.constructor) ?? /* @__PURE__ */ new Map();
717
+ const headers = existing.get(methodKey) ?? [];
718
+ headers.push({
719
+ name,
720
+ value
721
+ });
722
+ existing.set(methodKey, headers);
723
+ defineMetadata(DECORATOR_METADATA_KEYS.RESPONSE_HEADERS, existing, target.constructor);
724
+ return descriptor;
725
+ }, "setHeaderDecorator");
726
+ }
727
+ __name(SetHeader, "SetHeader");
728
+ function Redirect(url, statusCode = 302) {
729
+ return /* @__PURE__ */ __name(function redirectDecorator(target, propertyKey, descriptor) {
730
+ const methodKey = String(propertyKey);
731
+ const existing = getOwnMetadata(DECORATOR_METADATA_KEYS.REDIRECT, target.constructor) ?? /* @__PURE__ */ new Map();
732
+ existing.set(methodKey, {
733
+ url,
734
+ statusCode
735
+ });
736
+ defineMetadata(DECORATOR_METADATA_KEYS.REDIRECT, existing, target.constructor);
737
+ return descriptor;
738
+ }, "redirectDecorator");
739
+ }
740
+ __name(Redirect, "Redirect");
741
+
742
+ // src/decorators/routes.ts
743
+ init_reflection();
744
+ init_types();
745
+ function createRouteDecorator(method) {
746
+ return /* @__PURE__ */ __name(function routeDecoratorFactory(pathOrOptions, options) {
747
+ return /* @__PURE__ */ __name(function routeDecorator(target, propertyKey, descriptor) {
748
+ const { path, routeOptions } = normalizeRouteInput(pathOrOptions, options);
749
+ const metadata = {
750
+ method,
751
+ path,
752
+ methodName: propertyKey,
753
+ propertyKey,
754
+ middleware: routeOptions?.middleware,
755
+ statusCode: routeOptions?.statusCode,
756
+ description: routeOptions?.description,
757
+ deprecated: routeOptions?.deprecated
758
+ };
759
+ const existingRoutes = getOwnMetadata(DECORATOR_METADATA_KEYS.ROUTES, target.constructor) ?? [];
760
+ defineMetadata(DECORATOR_METADATA_KEYS.ROUTES, [
761
+ ...existingRoutes,
762
+ metadata
763
+ ], target.constructor);
764
+ return descriptor;
765
+ }, "routeDecorator");
766
+ }, "routeDecoratorFactory");
767
+ }
768
+ __name(createRouteDecorator, "createRouteDecorator");
769
+ function normalizeRouteInput(pathOrOptions, options) {
770
+ if (typeof pathOrOptions === "string") {
771
+ return {
772
+ path: normalizePath(pathOrOptions),
773
+ routeOptions: options
774
+ };
775
+ }
776
+ if (pathOrOptions && typeof pathOrOptions === "object") {
777
+ const path = pathOrOptions.path ? normalizePath(pathOrOptions.path) : "/";
778
+ return {
779
+ path,
780
+ routeOptions: pathOrOptions
781
+ };
782
+ }
783
+ return {
784
+ path: "/",
785
+ routeOptions: options
786
+ };
787
+ }
788
+ __name(normalizeRouteInput, "normalizeRouteInput");
789
+ var Get = createRouteDecorator("GET");
790
+ var Post = createRouteDecorator("POST");
791
+ var Put = createRouteDecorator("PUT");
792
+ var Delete = createRouteDecorator("DELETE");
793
+ var Patch = createRouteDecorator("PATCH");
794
+ var Head = createRouteDecorator("HEAD");
795
+ var Options = createRouteDecorator("OPTIONS");
796
+ var All = createRouteDecorator("ALL");
797
+
798
+ // src/binding/param-factory.ts
799
+ init_types();
800
+ init_reflection();
801
+ function createParamDecorator(source, defaultRequired) {
802
+ return /* @__PURE__ */ __name(function paramDecoratorFactory(nameOrOptions, options) {
803
+ return /* @__PURE__ */ __name(function paramDecorator(target, propertyKey, parameterIndex) {
804
+ if (propertyKey === void 0) {
805
+ throw new Error(`Parameter decorator @${source.charAt(0).toUpperCase() + source.slice(1)} can only be used on method parameters, not constructor parameters.`);
806
+ }
807
+ const { name, paramOptions } = normalizeParamInput(nameOrOptions, options, source);
808
+ const metadata = {
809
+ source,
810
+ index: parameterIndex,
811
+ name,
812
+ required: paramOptions?.required ?? defaultRequired,
813
+ defaultValue: paramOptions?.defaultValue,
814
+ transform: paramOptions?.transform
815
+ };
816
+ pushParamMetadata(target, propertyKey, metadata);
817
+ }, "paramDecorator");
818
+ }, "paramDecoratorFactory");
819
+ }
820
+ __name(createParamDecorator, "createParamDecorator");
821
+ function normalizeParamInput(nameOrOptions, options, source) {
822
+ if (typeof nameOrOptions === "string") {
823
+ return {
824
+ name: nameOrOptions,
825
+ paramOptions: options
826
+ };
827
+ }
828
+ if (nameOrOptions && typeof nameOrOptions === "object") {
829
+ return {
830
+ paramOptions: nameOrOptions
831
+ };
832
+ }
833
+ if (source === "body" || source === "ctx" || source === "req" || source === "res") {
834
+ return {
835
+ paramOptions: options
836
+ };
837
+ }
838
+ return {
839
+ paramOptions: options
840
+ };
841
+ }
842
+ __name(normalizeParamInput, "normalizeParamInput");
843
+ function pushParamMetadata(target, propertyKey, metadata) {
844
+ const methodKey = `${String(propertyKey)}`;
845
+ const existingParams = getOwnMetadata(DECORATOR_METADATA_KEYS.PARAMS, target.constructor) ?? /* @__PURE__ */ new Map();
846
+ const methodParams = existingParams.get(methodKey) ?? [];
847
+ methodParams.push(metadata);
848
+ existingParams.set(methodKey, methodParams);
849
+ defineMetadata(DECORATOR_METADATA_KEYS.PARAMS, existingParams, target.constructor);
850
+ }
851
+ __name(pushParamMetadata, "pushParamMetadata");
852
+
853
+ // src/binding/param-decorators.ts
854
+ var Body = createParamDecorator("body", true);
855
+ var Param = createParamDecorator("param", true);
856
+ var Query = createParamDecorator("query", false);
857
+ var Header = createParamDecorator("header", false);
858
+ function Ctx() {
859
+ return /* @__PURE__ */ __name(function ctxDecorator(target, propertyKey, parameterIndex) {
860
+ if (propertyKey === void 0) {
861
+ throw new Error("@Ctx can only be used on method parameters, not constructor parameters.");
862
+ }
863
+ const metadata = {
864
+ source: "ctx",
865
+ index: parameterIndex,
866
+ required: false
867
+ };
868
+ pushParamMetadata(target, propertyKey, metadata);
869
+ }, "ctxDecorator");
870
+ }
871
+ __name(Ctx, "Ctx");
872
+ function Req() {
873
+ return /* @__PURE__ */ __name(function reqDecorator(target, propertyKey, parameterIndex) {
874
+ if (propertyKey === void 0) {
875
+ throw new Error("@Req can only be used on method parameters, not constructor parameters.");
876
+ }
877
+ const metadata = {
878
+ source: "req",
879
+ index: parameterIndex,
880
+ required: false
881
+ };
882
+ pushParamMetadata(target, propertyKey, metadata);
883
+ }, "reqDecorator");
884
+ }
885
+ __name(Req, "Req");
886
+ function Res() {
887
+ return /* @__PURE__ */ __name(function resDecorator(target, propertyKey, parameterIndex) {
888
+ if (propertyKey === void 0) {
889
+ throw new Error("@Res can only be used on method parameters, not constructor parameters.");
890
+ }
891
+ const metadata = {
892
+ source: "res",
893
+ index: parameterIndex,
894
+ required: false
895
+ };
896
+ pushParamMetadata(target, propertyKey, metadata);
897
+ }, "resDecorator");
898
+ }
899
+ __name(Res, "Res");
900
+
901
+ // src/binding/custom-param.ts
902
+ init_types();
903
+ init_reflection();
904
+ function pushParamMetadata2(target, propertyKey, metadata) {
905
+ const methodKey = `${String(propertyKey)}`;
906
+ const existingParams = getOwnMetadata(DECORATOR_METADATA_KEYS.PARAMS, target.constructor) ?? /* @__PURE__ */ new Map();
907
+ const methodParams = existingParams.get(methodKey) ?? [];
908
+ methodParams.push(metadata);
909
+ existingParams.set(methodKey, methodParams);
910
+ defineMetadata(DECORATOR_METADATA_KEYS.PARAMS, existingParams, target.constructor);
911
+ }
912
+ __name(pushParamMetadata2, "pushParamMetadata");
913
+ function createCustomParamDecorator(extractor, options) {
914
+ return /* @__PURE__ */ __name(function customParamDecorator(target, propertyKey, parameterIndex) {
915
+ if (propertyKey === void 0) {
916
+ throw new Error("Custom parameter decorator can only be used on method parameters, not constructor parameters.");
917
+ }
918
+ const metadata = {
919
+ source: "custom",
920
+ index: parameterIndex,
921
+ required: options?.required ?? false,
922
+ transform: options?.transform,
923
+ customExtractor: extractor
924
+ };
925
+ pushParamMetadata2(target, propertyKey, metadata);
926
+ }, "customParamDecorator");
927
+ }
928
+ __name(createCustomParamDecorator, "createCustomParamDecorator");
929
+
930
+ // src/guards/guards.ts
931
+ init_reflection();
932
+ init_types();
933
+ function UseGuard(...guards) {
934
+ return /* @__PURE__ */ __name(function guardDecorator(target, propertyKey, descriptor) {
935
+ if (propertyKey !== void 0 && descriptor !== void 0) {
936
+ const existingGuards = getMetadata(DECORATOR_METADATA_KEYS.GUARDS, target.constructor, propertyKey) ?? [];
937
+ const metadata = {
938
+ guards,
939
+ target: "method",
940
+ methodName: propertyKey
941
+ };
942
+ defineMetadata(DECORATOR_METADATA_KEYS.GUARDS, [
943
+ ...existingGuards,
944
+ metadata
945
+ ], target.constructor, propertyKey);
946
+ } else {
947
+ const existingGuards = getMetadata(DECORATOR_METADATA_KEYS.GUARDS, target) ?? [];
948
+ const metadata = {
949
+ guards,
950
+ target: "class"
951
+ };
952
+ defineMetadata(DECORATOR_METADATA_KEYS.GUARDS, [
953
+ ...existingGuards,
954
+ metadata
955
+ ], target);
956
+ }
957
+ }, "guardDecorator");
958
+ }
959
+ __name(UseGuard, "UseGuard");
960
+ function getClassGuards(target) {
961
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.GUARDS, target) ?? [];
962
+ return metadata.flatMap((m) => m.guards);
963
+ }
964
+ __name(getClassGuards, "getClassGuards");
965
+ function getMethodGuards(target, methodName) {
966
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.GUARDS, target, methodName) ?? [];
967
+ return metadata.flatMap((m) => m.guards);
968
+ }
969
+ __name(getMethodGuards, "getMethodGuards");
970
+ function getAllGuards(target, methodName) {
971
+ const classGuards = getClassGuards(target);
972
+ const methodGuards = getMethodGuards(target, methodName);
973
+ return [
974
+ ...classGuards,
975
+ ...methodGuards
976
+ ];
977
+ }
978
+ __name(getAllGuards, "getAllGuards");
979
+
980
+ // src/filters/filters.ts
981
+ init_reflection();
982
+ init_types();
983
+ function Catch(...errorTypes) {
984
+ return /* @__PURE__ */ __name(function catchDecorator(target) {
985
+ defineMetadata(DECORATOR_METADATA_KEYS.CATCH, errorTypes, target);
986
+ }, "catchDecorator");
987
+ }
988
+ __name(Catch, "Catch");
989
+ function UseFilter(...filters) {
990
+ return /* @__PURE__ */ __name(function filterDecorator(target, propertyKey, descriptor) {
991
+ if (propertyKey !== void 0 && descriptor !== void 0) {
992
+ const existing = getMetadata(DECORATOR_METADATA_KEYS.FILTERS, target.constructor, propertyKey) ?? [];
993
+ const metadata = {
994
+ filters,
995
+ target: "method",
996
+ methodName: propertyKey
997
+ };
998
+ defineMetadata(DECORATOR_METADATA_KEYS.FILTERS, [
999
+ ...existing,
1000
+ metadata
1001
+ ], target.constructor, propertyKey);
1002
+ } else {
1003
+ const existing = getMetadata(DECORATOR_METADATA_KEYS.FILTERS, target) ?? [];
1004
+ const metadata = {
1005
+ filters,
1006
+ target: "class"
1007
+ };
1008
+ defineMetadata(DECORATOR_METADATA_KEYS.FILTERS, [
1009
+ ...existing,
1010
+ metadata
1011
+ ], target);
1012
+ }
1013
+ }, "filterDecorator");
1014
+ }
1015
+ __name(UseFilter, "UseFilter");
1016
+ function getCatchTypes(target) {
1017
+ return getMetadata(DECORATOR_METADATA_KEYS.CATCH, target) ?? [];
1018
+ }
1019
+ __name(getCatchTypes, "getCatchTypes");
1020
+ function getClassFilters(target) {
1021
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.FILTERS, target) ?? [];
1022
+ return metadata.flatMap((m) => m.filters);
1023
+ }
1024
+ __name(getClassFilters, "getClassFilters");
1025
+ function getMethodFilters(target, methodName) {
1026
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.FILTERS, target, methodName) ?? [];
1027
+ return metadata.flatMap((m) => m.filters);
1028
+ }
1029
+ __name(getMethodFilters, "getMethodFilters");
1030
+ function getAllFilters(target, methodName) {
1031
+ const methodFilters = getMethodFilters(target, methodName);
1032
+ const classFilters = getClassFilters(target);
1033
+ return [
1034
+ ...methodFilters,
1035
+ ...classFilters
1036
+ ];
1037
+ }
1038
+ __name(getAllFilters, "getAllFilters");
1039
+
1040
+ // src/interceptors/interceptors.ts
1041
+ init_reflection();
1042
+ init_types();
1043
+ function UseInterceptor(...interceptors) {
1044
+ return /* @__PURE__ */ __name(function interceptorDecorator(target, propertyKey, descriptor) {
1045
+ if (propertyKey !== void 0 && descriptor !== void 0) {
1046
+ const existing = getMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, target.constructor, propertyKey) ?? [];
1047
+ const metadata = {
1048
+ interceptors,
1049
+ target: "method",
1050
+ methodName: propertyKey
1051
+ };
1052
+ defineMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, [
1053
+ ...existing,
1054
+ metadata
1055
+ ], target.constructor, propertyKey);
1056
+ } else {
1057
+ const existing = getMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, target) ?? [];
1058
+ const metadata = {
1059
+ interceptors,
1060
+ target: "class"
1061
+ };
1062
+ defineMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, [
1063
+ ...existing,
1064
+ metadata
1065
+ ], target);
1066
+ }
1067
+ }, "interceptorDecorator");
1068
+ }
1069
+ __name(UseInterceptor, "UseInterceptor");
1070
+ function getClassInterceptors(target) {
1071
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, target) ?? [];
1072
+ return metadata.flatMap((m) => m.interceptors);
1073
+ }
1074
+ __name(getClassInterceptors, "getClassInterceptors");
1075
+ function getMethodInterceptors(target, methodName) {
1076
+ const metadata = getMetadata(DECORATOR_METADATA_KEYS.INTERCEPTORS, target, methodName) ?? [];
1077
+ return metadata.flatMap((m) => m.interceptors);
1078
+ }
1079
+ __name(getMethodInterceptors, "getMethodInterceptors");
1080
+ function getAllInterceptors(target, methodName) {
1081
+ const classInterceptors = getClassInterceptors(target);
1082
+ const methodInterceptors = getMethodInterceptors(target, methodName);
1083
+ return [
1084
+ ...classInterceptors,
1085
+ ...methodInterceptors
1086
+ ];
1087
+ }
1088
+ __name(getAllInterceptors, "getAllInterceptors");
1089
+
1090
+ // src/index.ts
1091
+ init_metadata();
1092
+ init_reflection();
1093
+
1094
+ // src/registrar/registrar.ts
1095
+ init_discovery();
1096
+ init_errors();
1097
+
1098
+ // src/bootstrap/stages/discover.ts
1099
+ async function discoverStage(ctx) {
1100
+ ctx.discoveredClasses = await ctx.source.discover();
1101
+ }
1102
+ __name(discoverStage, "discoverStage");
1103
+
1104
+ // src/bootstrap/stages/metadata.ts
1105
+ init_metadata();
1106
+ function metadataStage(ctx) {
1107
+ ctx.controllerDefinitions = ctx.discoveredClasses.map((cls) => {
1108
+ const def = getControllerDefinition(cls);
1109
+ if (!def) {
1110
+ throw new Error(`Controller class ${cls.name} has no metadata. Did you apply @Controller?`);
1111
+ }
1112
+ return def;
1113
+ });
1114
+ }
1115
+ __name(metadataStage, "metadataStage");
1116
+
1117
+ // src/request/isolation.ts
1118
+ init_reflection();
1119
+ var TSYRINGE_INJECTION_TOKENS = "injectionTokens";
1120
+ function tokenOf(descriptor) {
1121
+ if (descriptor !== null && typeof descriptor === "object" && "token" in descriptor) {
1122
+ return descriptor.token;
1123
+ }
1124
+ return descriptor;
1125
+ }
1126
+ __name(tokenOf, "tokenOf");
1127
+ function collectDependencyClasses(target) {
1128
+ const paramTypes = getConstructorParamTypes(target);
1129
+ const injectionTokens = getOwnMetadata(TSYRINGE_INJECTION_TOKENS, target) ?? {};
1130
+ const optional = getOptionalParams(target);
1131
+ const indices = /* @__PURE__ */ new Set();
1132
+ for (let i = 0; i < paramTypes.length; i++) {
1133
+ indices.add(i);
1134
+ }
1135
+ for (const key of Object.keys(injectionTokens)) {
1136
+ indices.add(Number(key));
1137
+ }
1138
+ const deps = [];
1139
+ for (const index of indices) {
1140
+ if (optional.has(index)) {
1141
+ continue;
1142
+ }
1143
+ const explicit = injectionTokens[index];
1144
+ const token = explicit !== void 0 ? tokenOf(explicit) : paramTypes[index];
1145
+ if (typeof token === "function") {
1146
+ deps.push(token);
1147
+ }
1148
+ }
1149
+ return deps;
1150
+ }
1151
+ __name(collectDependencyClasses, "collectDependencyClasses");
1152
+ function collectServiceGraph(controllers) {
1153
+ const visited = /* @__PURE__ */ new Set();
1154
+ const result = [];
1155
+ const queue = [];
1156
+ for (const controller of controllers) {
1157
+ for (const dep of collectDependencyClasses(controller)) {
1158
+ queue.push(dep);
1159
+ }
1160
+ }
1161
+ for (let head = 0; head < queue.length; head++) {
1162
+ const dep = queue[head];
1163
+ if (visited.has(dep)) {
1164
+ continue;
1165
+ }
1166
+ visited.add(dep);
1167
+ if (!hasServiceMetadata(dep)) {
1168
+ continue;
1169
+ }
1170
+ result.push(dep);
1171
+ for (const sub of collectDependencyClasses(dep)) {
1172
+ queue.push(sub);
1173
+ }
1174
+ }
1175
+ return result;
1176
+ }
1177
+ __name(collectServiceGraph, "collectServiceGraph");
1178
+ function registerServiceGraph(controllers, container2, effectiveScopes) {
1179
+ for (const dep of collectServiceGraph(controllers)) {
1180
+ const scope = effectiveScopes?.get(dep) ?? getServiceScope(dep) ?? "singleton";
1181
+ const token = dep;
1182
+ if (!container2.isRegistered(token)) {
1183
+ container2.register(token, {
1184
+ useClass: dep
1185
+ }, {
1186
+ scope
1187
+ });
1188
+ }
1189
+ }
1190
+ }
1191
+ __name(registerServiceGraph, "registerServiceGraph");
1192
+
1193
+ // src/request/scope.ts
1194
+ function isEffectivelyRequest(cls, cache, visiting) {
1195
+ const cached = cache.get(cls);
1196
+ if (cached !== void 0) {
1197
+ return cached;
1198
+ }
1199
+ if (visiting.has(cls)) {
1200
+ return false;
1201
+ }
1202
+ visiting.add(cls);
1203
+ let request = getServiceScope(cls) === "request";
1204
+ if (!request) {
1205
+ for (const dep of collectDependencyClasses(cls)) {
1206
+ if (hasServiceMetadata(dep) && isEffectivelyRequest(dep, cache, visiting)) {
1207
+ request = true;
1208
+ break;
1209
+ }
1210
+ }
1211
+ }
1212
+ visiting.delete(cls);
1213
+ cache.set(cls, request);
1214
+ return request;
1215
+ }
1216
+ __name(isEffectivelyRequest, "isEffectivelyRequest");
1217
+ function computeEffectiveScopes(controllers) {
1218
+ const requestCache = /* @__PURE__ */ new Map();
1219
+ const scopes = /* @__PURE__ */ new Map();
1220
+ const reachable = new Set(controllers);
1221
+ for (const service of collectServiceGraph(controllers)) {
1222
+ reachable.add(service);
1223
+ }
1224
+ for (const cls of reachable) {
1225
+ const request = isEffectivelyRequest(cls, requestCache, /* @__PURE__ */ new Set());
1226
+ scopes.set(cls, request ? "request" : getServiceScope(cls));
1227
+ }
1228
+ return scopes;
1229
+ }
1230
+ __name(computeEffectiveScopes, "computeEffectiveScopes");
1231
+ function requestScopedClasses(scopes) {
1232
+ const set = /* @__PURE__ */ new Set();
1233
+ for (const [cls, scope] of scopes) {
1234
+ if (scope === "request") {
1235
+ set.add(cls);
1236
+ }
1237
+ }
1238
+ return set;
1239
+ }
1240
+ __name(requestScopedClasses, "requestScopedClasses");
1241
+ function registerRequestScopedServices(container2, scopes) {
1242
+ for (const [cls, scope] of scopes) {
1243
+ if (scope !== "request" || !hasServiceMetadata(cls)) {
1244
+ continue;
1245
+ }
1246
+ container2.register(cls, {
1247
+ useClass: cls
1248
+ }, {
1249
+ scope: "request"
1250
+ });
1251
+ }
1252
+ }
1253
+ __name(registerRequestScopedServices, "registerRequestScopedServices");
1254
+ function bindRequestScopes(controllers, container2, isolate) {
1255
+ const scopes = computeEffectiveScopes(controllers);
1256
+ const requestScoped = requestScopedClasses(scopes);
1257
+ if (isolate) {
1258
+ registerServiceGraph(controllers, container2, scopes);
1259
+ } else if (requestScoped.size > 0) {
1260
+ registerRequestScopedServices(container2, scopes);
1261
+ }
1262
+ return requestScoped;
1263
+ }
1264
+ __name(bindRequestScopes, "bindRequestScopes");
1265
+
1266
+ // src/bootstrap/stages/provider-graph.ts
1267
+ async function providerGraphStage(ctx) {
1268
+ ctx.requestScoped = bindRequestScopes(ctx.discoveredClasses, ctx.resolvedOptions.container, ctx.resolvedOptions.isolate);
1269
+ const graph = /* @__PURE__ */ new Map();
1270
+ for (const cls of ctx.discoveredClasses) {
1271
+ graph.set(cls, collectDependencyClasses(cls));
1272
+ }
1273
+ ctx.providerGraph = graph;
1274
+ }
1275
+ __name(providerGraphStage, "providerGraphStage");
1276
+
1277
+ // src/bootstrap/stages/validation.ts
1278
+ async function validationStage(ctx) {
1279
+ await ctx.resolvedOptions.container.bootstrap();
1280
+ }
1281
+ __name(validationStage, "validationStage");
1282
+
1283
+ // src/registrar/registry.ts
1284
+ init_metadata();
1285
+
1286
+ // src/runtime/handler.ts
1287
+ init_metadata();
1288
+ init_errors();
1289
+
1290
+ // src/filters/filter-runner.ts
1291
+ function filterMatches(catchTypes, error) {
1292
+ if (catchTypes.length === 0) {
1293
+ return true;
1294
+ }
1295
+ return catchTypes.some((type) => error instanceof type);
1296
+ }
1297
+ __name(filterMatches, "filterMatches");
1298
+ async function applyFilters(filters, error, ctx, container2) {
1299
+ for (const filter of filters) {
1300
+ if (!filterMatches(getCatchTypes(filter), error)) {
1301
+ continue;
1302
+ }
1303
+ const instance = container2.resolve(filter);
1304
+ await instance.catch(error, ctx);
1305
+ return true;
1306
+ }
1307
+ return false;
1308
+ }
1309
+ __name(applyFilters, "applyFilters");
1310
+ function wrapWithFilters(execute, filters, container2) {
1311
+ return async (ctx, next) => {
1312
+ try {
1313
+ await execute(ctx, next);
1314
+ } catch (error) {
1315
+ const handled = await applyFilters(filters, error, ctx, container2);
1316
+ if (!handled) {
1317
+ throw error;
1318
+ }
1319
+ }
1320
+ };
1321
+ }
1322
+ __name(wrapWithFilters, "wrapWithFilters");
1323
+
1324
+ // src/guards/guard-runner.ts
1325
+ init_errors();
1326
+ async function executeGuards(guards, ctx, container2, _controllerName, _methodName) {
1327
+ const guardContext = {
1328
+ method: ctx.method,
1329
+ path: ctx.path,
1330
+ params: ctx.params,
1331
+ query: ctx.query,
1332
+ headers: ctx.headers,
1333
+ body: ctx.body,
1334
+ state: ctx.state,
1335
+ get: /* @__PURE__ */ __name((name) => ctx.get(name), "get")
1336
+ };
1337
+ for (let i = 0; i < guards.length; i++) {
1338
+ const guard = guards[i];
1339
+ let guardName;
1340
+ let result;
1341
+ if (isGuardClass(guard)) {
1342
+ guardName = guard.name || `ClassGuard[${i}]`;
1343
+ const guardInstance = container2.resolve(guard);
1344
+ result = await guardInstance.canActivate(guardContext);
1345
+ } else {
1346
+ guardName = guard.name || `Guard[${i}]`;
1347
+ result = await guard(guardContext);
1348
+ }
1349
+ if (!result) {
1350
+ throw new GuardRejectionError(guardName);
1351
+ }
1352
+ }
1353
+ }
1354
+ __name(executeGuards, "executeGuards");
1355
+
1356
+ // src/interceptors/interceptor-runner.ts
1357
+ async function runInterceptors(interceptors, ctx, container2, invokeMethod) {
1358
+ let next = invokeMethod;
1359
+ for (let i = interceptors.length - 1; i >= 0; i--) {
1360
+ const interceptorClass = interceptors[i];
1361
+ const downstream = next;
1362
+ next = /* @__PURE__ */ __name(() => {
1363
+ const instance = container2.resolve(interceptorClass);
1364
+ return instance.intercept(ctx, downstream);
1365
+ }, "next");
1366
+ }
1367
+ return next();
1368
+ }
1369
+ __name(runInterceptors, "runInterceptors");
1370
+
1371
+ // src/binding/param-resolver.ts
1372
+ init_errors();
1373
+ async function resolveParametersFromPlan(ctx, sortedMetadata, controllerName, methodName) {
1374
+ if (sortedMetadata.length === 0) {
1375
+ return [];
1376
+ }
1377
+ const maxIndex = sortedMetadata.length > 0 ? sortedMetadata[sortedMetadata.length - 1].index : -1;
1378
+ const args = new Array(maxIndex + 1).fill(void 0);
1379
+ for (const param of sortedMetadata) {
1380
+ try {
1381
+ const value = await extractParameterValue(ctx, param);
1382
+ if (value === void 0) {
1383
+ if (param.required && param.defaultValue === void 0) {
1384
+ throw createMissingParameterError(controllerName, methodName, param.name ?? `index ${param.index}`, param.source);
1385
+ }
1386
+ args[param.index] = param.defaultValue;
1387
+ } else {
1388
+ args[param.index] = param.transform ? await param.transform(value) : value;
1389
+ }
1390
+ } catch (error) {
1391
+ if (error instanceof MissingParameterError) {
1392
+ throw error;
1393
+ }
1394
+ throw new ParameterInjectionError(controllerName, methodName, param.index, error instanceof Error ? error.message : String(error));
1395
+ }
1396
+ }
1397
+ return args;
1398
+ }
1399
+ __name(resolveParametersFromPlan, "resolveParametersFromPlan");
1400
+ function createMissingParameterError(controllerName, methodName, paramName, source) {
1401
+ const messageOverride = source === "body" ? `Required body parameter "${paramName}" is missing. No body was parsed for this request \u2014 register a body-parser middleware before this route (e.g. app.use(json()) from '@nextrush/body-parser').` : void 0;
1402
+ return new MissingParameterError(controllerName, methodName, paramName, source, messageOverride);
1403
+ }
1404
+ __name(createMissingParameterError, "createMissingParameterError");
1405
+ function extractParameterValue(ctx, param) {
1406
+ switch (param.source) {
1407
+ case "body":
1408
+ if (param.name) {
1409
+ return ctx.body?.[param.name];
1410
+ }
1411
+ return ctx.body;
1412
+ case "param":
1413
+ if (param.name) {
1414
+ return ctx.params[param.name];
1415
+ }
1416
+ return ctx.params;
1417
+ case "query":
1418
+ if (param.name) {
1419
+ return ctx.query[param.name];
1420
+ }
1421
+ return ctx.query;
1422
+ case "header":
1423
+ if (param.name) {
1424
+ return ctx.get(param.name);
1425
+ }
1426
+ return ctx.headers;
1427
+ case "ctx":
1428
+ return ctx;
1429
+ case "req":
1430
+ return ctx.raw.req;
1431
+ case "res":
1432
+ return ctx.raw.res;
1433
+ case "custom":
1434
+ if (param.customExtractor) {
1435
+ return param.customExtractor(ctx);
1436
+ }
1437
+ return void 0;
1438
+ default:
1439
+ return void 0;
1440
+ }
1441
+ }
1442
+ __name(extractParameterValue, "extractParameterValue");
1443
+
1444
+ // src/runtime/handler.ts
1445
+ function resolveMemoizedSingleton(controllerClass, container2, instanceCache) {
1446
+ return () => {
1447
+ if (!instanceCache.has(controllerClass)) {
1448
+ try {
1449
+ instanceCache.set(controllerClass, container2.resolve(controllerClass));
1450
+ } catch (error) {
1451
+ throw new ControllerResolutionError(controllerClass.name, error instanceof Error ? error : void 0);
1452
+ }
1453
+ }
1454
+ return instanceCache.get(controllerClass);
1455
+ };
1456
+ }
1457
+ __name(resolveMemoizedSingleton, "resolveMemoizedSingleton");
1458
+ function resolveFromRequestChild(controllerClass, container2) {
1459
+ return () => {
1460
+ try {
1461
+ return container2.createChild().resolve(controllerClass);
1462
+ } catch (error) {
1463
+ throw new ControllerResolutionError(controllerClass.name, error instanceof Error ? error : void 0);
1464
+ }
1465
+ };
1466
+ }
1467
+ __name(resolveFromRequestChild, "resolveFromRequestChild");
1468
+ function createRouteHandler(controllerClass, route, container2, instanceCache, isRequestScoped = false) {
1469
+ const methodName = String(route.methodName);
1470
+ const paramMetadata = getParamMetadata(controllerClass, methodName);
1471
+ const guards = getAllGuards(controllerClass, methodName);
1472
+ const filters = getAllFilters(controllerClass, methodName);
1473
+ const interceptors = getAllInterceptors(controllerClass, methodName);
1474
+ const sortedParams = paramMetadata.length > 0 ? [
1475
+ ...paramMetadata
1476
+ ].sort((a, b) => a.index - b.index) : [];
1477
+ const statusCode = route.statusCode;
1478
+ const httpCode = getHttpCode(controllerClass, methodName);
1479
+ const effectiveStatusCode = httpCode ?? statusCode;
1480
+ const responseHeaders = getResponseHeaders(controllerClass, methodName);
1481
+ const redirectMeta = getRedirectMetadata(controllerClass, methodName);
1482
+ const resolveControllerInstance = isRequestScoped ? resolveFromRequestChild(controllerClass, container2) : resolveMemoizedSingleton(controllerClass, container2, instanceCache);
1483
+ const execute = /* @__PURE__ */ __name(async (ctx) => {
1484
+ if (guards.length > 0) {
1485
+ await executeGuards(guards, ctx, container2, controllerClass.name);
1486
+ }
1487
+ const controllerInstance = resolveControllerInstance();
1488
+ const args = await resolveParametersFromPlan(ctx, sortedParams, controllerClass.name, methodName);
1489
+ const method = controllerInstance[methodName];
1490
+ if (typeof method !== "function") {
1491
+ throw new Error(`Method "${methodName}" not found on controller "${controllerClass.name}"`);
1492
+ }
1493
+ const invokeMethod = /* @__PURE__ */ __name(() => Promise.resolve(method.apply(controllerInstance, args)), "invokeMethod");
1494
+ const result = interceptors.length > 0 ? await runInterceptors(interceptors, ctx, container2, invokeMethod) : await invokeMethod();
1495
+ for (const header of responseHeaders) {
1496
+ ctx.set(header.name, header.value);
1497
+ }
1498
+ if (effectiveStatusCode !== void 0) {
1499
+ ctx.status = effectiveStatusCode;
1500
+ }
1501
+ if (redirectMeta && !ctx.responded) {
1502
+ let redirectUrl = redirectMeta.url;
1503
+ let redirectStatus = redirectMeta.statusCode;
1504
+ if (typeof result === "string") {
1505
+ redirectUrl = result;
1506
+ } else if (result && typeof result === "object" && "url" in result) {
1507
+ const override = result;
1508
+ if (override.url) redirectUrl = override.url;
1509
+ if (override.statusCode) redirectStatus = override.statusCode;
1510
+ }
1511
+ ctx.status = redirectStatus;
1512
+ ctx.set("Location", redirectUrl);
1513
+ ctx.send("");
1514
+ return;
1515
+ }
1516
+ if (result !== void 0 && !ctx.responded) {
1517
+ if (typeof result === "object") {
1518
+ ctx.json(result);
1519
+ } else {
1520
+ ctx.send(String(result));
1521
+ }
1522
+ }
1523
+ }, "execute");
1524
+ return filters.length > 0 ? wrapWithFilters(execute, filters, container2) : execute;
1525
+ }
1526
+ __name(createRouteHandler, "createRouteHandler");
1527
+
1528
+ // src/registrar/builder.ts
1529
+ function resolveMiddlewareRefs(refs, container2) {
1530
+ return refs.map((ref) => {
1531
+ if (typeof ref === "function") {
1532
+ return ref;
1533
+ }
1534
+ const resolved = container2.resolve(ref);
1535
+ if (typeof resolved !== "function") {
1536
+ throw new Error(`Middleware token "${String(ref)}" resolved to a non-function value. Ensure the registered provider returns a middleware function.`);
1537
+ }
1538
+ return resolved;
1539
+ });
1540
+ }
1541
+ __name(resolveMiddlewareRefs, "resolveMiddlewareRefs");
1542
+ function buildRoutes(definition, container2, globalPrefix, globalMiddleware, instanceCache = /* @__PURE__ */ new Map(), isRequestScoped = false) {
1543
+ const routes = [];
1544
+ const { target, controller, routes: routeMetadata } = definition;
1545
+ for (const route of routeMetadata) {
1546
+ const handler = createRouteHandler(target, route, container2, instanceCache, isRequestScoped);
1547
+ const fullPath = buildFullRoutePath(globalPrefix, controller.path, route.path, controller.version);
1548
+ const combinedMiddleware = [
1549
+ ...globalMiddleware,
1550
+ ...resolveMiddlewareRefs(controller.middleware ?? [], container2),
1551
+ ...resolveMiddlewareRefs(route.middleware ?? [], container2)
1552
+ ];
1553
+ routes.push({
1554
+ method: route.method,
1555
+ path: fullPath,
1556
+ handler,
1557
+ middleware: combinedMiddleware,
1558
+ controller: target,
1559
+ methodName: String(route.methodName),
1560
+ metadata: toRouteMetaContribution(controller, route)
1561
+ });
1562
+ }
1563
+ return routes;
1564
+ }
1565
+ __name(buildRoutes, "buildRoutes");
1566
+ function toRouteMetaContribution(controller, route) {
1567
+ const contribution = {};
1568
+ if (route.description) {
1569
+ contribution.description = route.description;
1570
+ }
1571
+ if (route.deprecated) {
1572
+ contribution.deprecated = true;
1573
+ }
1574
+ if (controller.tags && controller.tags.length > 0) {
1575
+ contribution.tags = [
1576
+ ...controller.tags
1577
+ ];
1578
+ }
1579
+ return Object.keys(contribution).length > 0 ? contribution : void 0;
1580
+ }
1581
+ __name(toRouteMetaContribution, "toRouteMetaContribution");
1582
+ function buildFullRoutePath(globalPrefix, controllerPath, routePath, version) {
1583
+ const parts = [];
1584
+ if (globalPrefix && globalPrefix !== "/") {
1585
+ parts.push(globalPrefix.startsWith("/") ? globalPrefix : "/" + globalPrefix);
1586
+ }
1587
+ if (version) {
1588
+ parts.push("/" + version);
1589
+ }
1590
+ if (controllerPath && controllerPath !== "/") {
1591
+ parts.push(controllerPath.startsWith("/") ? controllerPath : "/" + controllerPath);
1592
+ }
1593
+ if (routePath && routePath !== "/") {
1594
+ parts.push(routePath.startsWith("/") ? routePath : "/" + routePath);
1595
+ }
1596
+ const fullPath = parts.join("") || "/";
1597
+ return fullPath.replace(/\/+/g, "/");
1598
+ }
1599
+ __name(buildFullRoutePath, "buildFullRoutePath");
1600
+
1601
+ // src/registrar/registry.ts
1602
+ init_errors();
1603
+ var ControllerRegistry = class {
1604
+ static {
1605
+ __name(this, "ControllerRegistry");
1606
+ }
1607
+ controllers = /* @__PURE__ */ new Map();
1608
+ container;
1609
+ globalPrefix;
1610
+ globalMiddleware;
1611
+ debug;
1612
+ /**
1613
+ * Classes whose effective DI scope is `'request'` (self or dependency graph
1614
+ * declares `scope: 'request'`). A request-scoped controller is registered with
1615
+ * the request lifecycle and resolved from a per-request child on every request
1616
+ * instead of being memoized. Empty by default (pure singleton/transient graph).
1617
+ */
1618
+ requestScopedClasses;
1619
+ /**
1620
+ * Shared controller-instance cache, keyed by controller class.
1621
+ *
1622
+ * Owned by the registry so a single resolved singleton is reused across the
1623
+ * boot-time eager validation (`validateControllers`) and the per-request
1624
+ * handlers built by {@link buildRoutes}. Without a shared cache, `validate: true`
1625
+ * resolves each controller twice: once at boot and again on the first request.
1626
+ *
1627
+ * A failed resolve is never stored, so resolution retries on each request until
1628
+ * it succeeds (see `createRouteHandler` in `builder.ts`).
1629
+ */
1630
+ instanceCache = /* @__PURE__ */ new Map();
1631
+ constructor(container2, globalPrefix, globalMiddleware, debug, requestScopedClasses2 = /* @__PURE__ */ new Set()) {
1632
+ this.container = container2;
1633
+ this.globalPrefix = globalPrefix;
1634
+ this.globalMiddleware = globalMiddleware;
1635
+ this.debug = debug;
1636
+ this.requestScopedClasses = requestScopedClasses2;
1637
+ }
1638
+ /**
1639
+ * Register a controller class
1640
+ */
1641
+ register(controllerClass) {
1642
+ if (this.controllers.has(controllerClass)) {
1643
+ return this.controllers.get(controllerClass);
1644
+ }
1645
+ if (!isController(controllerClass)) {
1646
+ throw new NotAControllerError(controllerClass.name);
1647
+ }
1648
+ const definition = getControllerDefinition(controllerClass);
1649
+ if (!definition) {
1650
+ throw new NotAControllerError(controllerClass.name);
1651
+ }
1652
+ if (definition.routes.length === 0) {
1653
+ throw new NoRoutesError(controllerClass.name);
1654
+ }
1655
+ this.registerInContainer(controllerClass);
1656
+ const routes = buildRoutes(definition, this.container, this.globalPrefix, this.globalMiddleware, this.instanceCache, this.requestScopedClasses.has(controllerClass));
1657
+ const registered = {
1658
+ target: controllerClass,
1659
+ definition,
1660
+ routes
1661
+ };
1662
+ this.controllers.set(controllerClass, registered);
1663
+ if (this.debug) {
1664
+ this.logRegistration(registered);
1665
+ }
1666
+ return registered;
1667
+ }
1668
+ /**
1669
+ * The shared controller-instance cache.
1670
+ *
1671
+ * Exposed so `registerControllers` can pre-seed it during eager validation
1672
+ * (`validate: true`), making the boot-time resolve and the per-request handler
1673
+ * share one singleton instead of resolving the same controller twice.
1674
+ */
1675
+ get instances() {
1676
+ return this.instanceCache;
1677
+ }
1678
+ /**
1679
+ * Register multiple controllers
1680
+ */
1681
+ registerAll(controllers) {
1682
+ return controllers.map((c) => this.register(c));
1683
+ }
1684
+ /**
1685
+ * Get all registered controllers
1686
+ */
1687
+ getAll() {
1688
+ return Array.from(this.controllers.values());
1689
+ }
1690
+ /**
1691
+ * Get all built routes from all controllers
1692
+ */
1693
+ getAllRoutes() {
1694
+ const routes = [];
1695
+ for (const controller of this.controllers.values()) {
1696
+ routes.push(...controller.routes);
1697
+ }
1698
+ return routes;
1699
+ }
1700
+ /**
1701
+ * Get total route count
1702
+ */
1703
+ get routeCount() {
1704
+ let count = 0;
1705
+ for (const controller of this.controllers.values()) {
1706
+ count += controller.routes.length;
1707
+ }
1708
+ return count;
1709
+ }
1710
+ /**
1711
+ * Check if a controller is registered
1712
+ */
1713
+ has(controllerClass) {
1714
+ return this.controllers.has(controllerClass);
1715
+ }
1716
+ /**
1717
+ * Clear all registrations
1718
+ */
1719
+ clear() {
1720
+ this.controllers.clear();
1721
+ }
1722
+ /**
1723
+ * Register the controller in the DI container with its effective scope: a
1724
+ * request-effective controller (self or dependency graph declares
1725
+ * `scope: 'request'`) uses the request (ContainerScoped) lifecycle so a fresh
1726
+ * instance is built per request; every other controller stays a singleton.
1727
+ */
1728
+ registerInContainer(controllerClass) {
1729
+ const token = controllerClass;
1730
+ if (!this.container.isRegistered(token)) {
1731
+ const scope = this.requestScopedClasses.has(controllerClass) ? "request" : "singleton";
1732
+ this.container.register(token, {
1733
+ useClass: token
1734
+ }, {
1735
+ scope
1736
+ });
1737
+ }
1738
+ }
1739
+ /**
1740
+ * Log controller registration details
1741
+ */
1742
+ logRegistration(registered) {
1743
+ const { target, routes } = registered;
1744
+ process.stderr.write(`[Controllers] Registered: ${target.name}
1745
+ `);
1746
+ for (const route of routes) {
1747
+ process.stderr.write(` ${route.method.padEnd(7)} ${route.path}
1748
+ `);
1749
+ }
1750
+ }
1751
+ };
1752
+
1753
+ // src/bootstrap/stages/registrar.ts
1754
+ function registrarStage(ctx) {
1755
+ const registry = new ControllerRegistry(ctx.resolvedOptions.container, ctx.resolvedOptions.prefix, ctx.resolvedOptions.middleware, ctx.resolvedOptions.debug, ctx.requestScoped);
1756
+ const registered = registry.registerAll(ctx.discoveredClasses);
1757
+ ctx.builtRoutes = [];
1758
+ for (const regCtrl of registered) {
1759
+ ctx.builtRoutes.push(...regCtrl.routes);
1760
+ }
1761
+ ctx.registryInstances = registry.instances;
1762
+ ctx.lifecycleData.controllerClasses = ctx.discoveredClasses;
1763
+ if (ctx.resolvedOptions.validate) {
1764
+ validateControllers(registered, ctx.resolvedOptions.container, registry.instances);
1765
+ validateGuards(registered, ctx.resolvedOptions.container);
1766
+ }
1767
+ }
1768
+ __name(registrarStage, "registrarStage");
1769
+
1770
+ // src/bootstrap/stages/router.ts
1771
+ init_errors();
1772
+ function routerStage(ctx) {
1773
+ const router = ctx.router;
1774
+ const routes = ctx.graph ? ctx.graph.routes : ctx.builtRoutes;
1775
+ for (const route of routes) {
1776
+ try {
1777
+ const method = route.method.toLowerCase();
1778
+ if (typeof router[method] !== "function") {
1779
+ throw new RouteRegistrationError("Unknown", route.method, route.path, `Router does not support HTTP method: ${route.method}`);
1780
+ }
1781
+ const entries = buildRouteEntries(route);
1782
+ router[method](route.path, ...entries);
1783
+ } catch (error) {
1784
+ throw new RouteRegistrationError("Unknown", route.method, route.path, error instanceof Error ? error.message : String(error), error instanceof Error ? error : void 0);
1785
+ }
1786
+ }
1787
+ }
1788
+ __name(routerStage, "routerStage");
1789
+ function buildRouteEntries(route) {
1790
+ const entries = [
1791
+ ...route.middleware
1792
+ ];
1793
+ if (route.metadata) {
1794
+ entries.push({
1795
+ [ROUTE_METADATA]: route.metadata
1796
+ });
1797
+ }
1798
+ entries.push(route.handler);
1799
+ return entries;
1800
+ }
1801
+ __name(buildRouteEntries, "buildRouteEntries");
1802
+
1803
+ // src/lifecycle/lifecycle.ts
1804
+ init_lifecycle_types();
1805
+ var CONTROLLERS_LIFECYCLE_EXTENSION = "nextrush:controllers-lifecycle";
1806
+ var lifecycleRegistrationCount = 0;
1807
+ function collectLifecycleInstances(controllers, container2, instanceCache) {
1808
+ const seen = /* @__PURE__ */ new Set();
1809
+ const collected = [];
1810
+ const consider = /* @__PURE__ */ __name((instance) => {
1811
+ if (typeof instance !== "object" || instance === null || seen.has(instance)) {
1812
+ return;
1813
+ }
1814
+ seen.add(instance);
1815
+ if (isOnInit(instance) || isOnShutdown(instance)) {
1816
+ collected.push(instance);
1817
+ }
1818
+ }, "consider");
1819
+ for (const service of [
1820
+ ...collectServiceGraph(controllers)
1821
+ ].reverse()) {
1822
+ try {
1823
+ consider(container2.resolve(service));
1824
+ } catch {
1825
+ continue;
1826
+ }
1827
+ }
1828
+ for (const controller of controllers) {
1829
+ consider(instanceCache.get(controller));
1830
+ }
1831
+ return collected;
1832
+ }
1833
+ __name(collectLifecycleInstances, "collectLifecycleInstances");
1834
+ function registerLifecycleExtension(app, controllers, container2, instanceCache) {
1835
+ const instances = collectLifecycleInstances(controllers, container2, instanceCache);
1836
+ if (instances.length === 0) {
1837
+ return;
1838
+ }
1839
+ if (app.isReady || app.isRunning) {
1840
+ throw new Error("registerControllers() found services with lifecycle hooks (onInit/onShutdown), but the app is already booted (ready()) or running, so its configuration is frozen and the lifecycle Extension cannot be registered. Call registerControllers() BEFORE serve()/listen()/ready().");
1841
+ }
1842
+ app.extend({
1843
+ name: `${CONTROLLERS_LIFECYCLE_EXTENSION}#${++lifecycleRegistrationCount}`,
1844
+ async setup() {
1845
+ for (const instance of instances) {
1846
+ if (isOnInit(instance)) {
1847
+ await instance.onInit();
1848
+ }
1849
+ }
1850
+ },
1851
+ async destroy() {
1852
+ const errors = [];
1853
+ for (const instance of [
1854
+ ...instances
1855
+ ].reverse()) {
1856
+ if (isOnShutdown(instance)) {
1857
+ try {
1858
+ await instance.onShutdown();
1859
+ } catch (err) {
1860
+ errors.push(err);
1861
+ }
1862
+ }
1863
+ }
1864
+ if (errors.length > 0) {
1865
+ throw new AggregateError(errors, `${String(errors.length)} onShutdown() hook(s) failed`);
1866
+ }
1867
+ }
1868
+ });
1869
+ }
1870
+ __name(registerLifecycleExtension, "registerLifecycleExtension");
1871
+
1872
+ // src/bootstrap/graph.ts
1873
+ function buildApplicationGraph(routes, providers, requestScopedTokens) {
1874
+ return deepFreeze({
1875
+ routes: [
1876
+ ...routes
1877
+ ],
1878
+ providers,
1879
+ requestScopedTokens
1880
+ });
1881
+ }
1882
+ __name(buildApplicationGraph, "buildApplicationGraph");
1883
+ function deepFreeze(obj) {
1884
+ if (obj === null || typeof obj !== "object") {
1885
+ return obj;
1886
+ }
1887
+ if (Array.isArray(obj)) {
1888
+ for (const item of obj) {
1889
+ deepFreeze(item);
1890
+ }
1891
+ return Object.freeze(obj);
1892
+ }
1893
+ if (obj instanceof Map) {
1894
+ obj.forEach((value) => {
1895
+ deepFreeze(value);
1896
+ });
1897
+ return Object.freeze(obj);
1898
+ }
1899
+ if (obj instanceof Set) {
1900
+ obj.forEach((value) => {
1901
+ deepFreeze(value);
1902
+ });
1903
+ return Object.freeze(obj);
1904
+ }
1905
+ for (const key in obj) {
1906
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1907
+ deepFreeze(obj[key]);
1908
+ }
1909
+ }
1910
+ return Object.freeze(obj);
1911
+ }
1912
+ __name(deepFreeze, "deepFreeze");
1913
+
1914
+ // src/diagnostics/collector.ts
1915
+ function collectDiagnostics(graph, timings) {
1916
+ const routes = graph.routes.map((route) => ({
1917
+ method: route.method,
1918
+ path: route.path,
1919
+ controller: route.controller
1920
+ }));
1921
+ const providers = Array.from(graph.providers.entries()).map(([token, deps]) => ({
1922
+ token,
1923
+ dependencies: [
1924
+ ...deps
1925
+ ]
1926
+ }));
1927
+ const routeMap = /* @__PURE__ */ new Map();
1928
+ for (const route of routes) {
1929
+ const key = `${route.method}:${route.path}`;
1930
+ routeMap.set(key, (routeMap.get(key) ?? 0) + 1);
1931
+ }
1932
+ const duplicateRoutes = Array.from(routeMap.entries()).filter(([_key, count]) => count > 1).map(([key, count]) => {
1933
+ const [method, ...pathParts] = key.split(":");
1934
+ return {
1935
+ method,
1936
+ path: pathParts.join(":"),
1937
+ count
1938
+ };
1939
+ });
1940
+ const circularDependencies = detectCircularDependencies(graph.providers);
1941
+ return {
1942
+ routes: Object.freeze(routes),
1943
+ providers: Object.freeze(providers),
1944
+ duplicateRoutes: Object.freeze(duplicateRoutes),
1945
+ circularDependencies: Object.freeze(circularDependencies),
1946
+ timings: Object.freeze(timings)
1947
+ };
1948
+ }
1949
+ __name(collectDiagnostics, "collectDiagnostics");
1950
+ function detectCircularDependencies(providers) {
1951
+ const cycles = [];
1952
+ const visited = /* @__PURE__ */ new Set();
1953
+ const recursionStack = /* @__PURE__ */ new Set();
1954
+ const visit = /* @__PURE__ */ __name((token, path) => {
1955
+ if (recursionStack.has(token)) {
1956
+ const cycleStart = path.indexOf(token);
1957
+ if (cycleStart !== -1) {
1958
+ const cycle = Object.freeze([
1959
+ ...path.slice(cycleStart),
1960
+ token
1961
+ ]);
1962
+ cycles.push({
1963
+ cycle
1964
+ });
1965
+ }
1966
+ return;
1967
+ }
1968
+ if (visited.has(token)) {
1969
+ return;
1970
+ }
1971
+ visited.add(token);
1972
+ recursionStack.add(token);
1973
+ const deps = providers.get(token) || [];
1974
+ for (const dep of deps) {
1975
+ visit(dep, [
1976
+ ...path,
1977
+ token
1978
+ ]);
1979
+ }
1980
+ recursionStack.delete(token);
1981
+ }, "visit");
1982
+ for (const token of providers.keys()) {
1983
+ if (!visited.has(token)) {
1984
+ visit(token, []);
1985
+ }
1986
+ }
1987
+ return cycles;
1988
+ }
1989
+ __name(detectCircularDependencies, "detectCircularDependencies");
1990
+
1991
+ // src/bootstrap/pipeline.ts
1992
+ async function bootstrapPipeline(ctx) {
1993
+ const enableTiming = ctx.resolvedOptions.diagnostics ?? false;
1994
+ const now = enableTiming ? () => performance.now() : () => 0;
1995
+ let stageStart = now();
1996
+ await discoverStage(ctx);
1997
+ if (enableTiming) {
1998
+ ctx.timings.push({
1999
+ stage: "discover",
2000
+ ms: performance.now() - stageStart
2001
+ });
2002
+ }
2003
+ if (ctx.discoveredClasses.length === 0) {
2004
+ return;
2005
+ }
2006
+ stageStart = now();
2007
+ metadataStage(ctx);
2008
+ if (enableTiming) {
2009
+ ctx.timings.push({
2010
+ stage: "metadata",
2011
+ ms: performance.now() - stageStart
2012
+ });
2013
+ }
2014
+ stageStart = now();
2015
+ await providerGraphStage(ctx);
2016
+ if (enableTiming) {
2017
+ ctx.timings.push({
2018
+ stage: "providerGraph",
2019
+ ms: performance.now() - stageStart
2020
+ });
2021
+ }
2022
+ stageStart = now();
2023
+ await validationStage(ctx);
2024
+ if (enableTiming) {
2025
+ ctx.timings.push({
2026
+ stage: "validation",
2027
+ ms: performance.now() - stageStart
2028
+ });
2029
+ }
2030
+ stageStart = now();
2031
+ registrarStage(ctx);
2032
+ if (enableTiming) {
2033
+ ctx.timings.push({
2034
+ stage: "registrar",
2035
+ ms: performance.now() - stageStart
2036
+ });
2037
+ }
2038
+ stageStart = now();
2039
+ ctx.graph = buildApplicationGraph(ctx.builtRoutes, ctx.providerGraph, ctx.requestScoped);
2040
+ if (enableTiming) {
2041
+ ctx.timings.push({
2042
+ stage: "graph",
2043
+ ms: performance.now() - stageStart
2044
+ });
2045
+ }
2046
+ stageStart = now();
2047
+ routerStage(ctx);
2048
+ if (enableTiming) {
2049
+ ctx.timings.push({
2050
+ stage: "router",
2051
+ ms: performance.now() - stageStart
2052
+ });
2053
+ }
2054
+ stageStart = now();
2055
+ registerLifecycleExtension(ctx.app, ctx.lifecycleData.controllerClasses, ctx.resolvedOptions.container, ctx.registryInstances);
2056
+ if (enableTiming) {
2057
+ ctx.timings.push({
2058
+ stage: "lifecycle",
2059
+ ms: performance.now() - stageStart
2060
+ });
2061
+ }
2062
+ if (enableTiming && ctx.graph) {
2063
+ const report = collectDiagnostics(ctx.graph, ctx.timings);
2064
+ storeClassDiagnostics(ctx.app, report);
2065
+ }
2066
+ }
2067
+ __name(bootstrapPipeline, "bootstrapPipeline");
2068
+ var diagnosticsStore = /* @__PURE__ */ new WeakMap();
2069
+ function storeClassDiagnostics(app, report) {
2070
+ diagnosticsStore.set(app, report);
2071
+ }
2072
+ __name(storeClassDiagnostics, "storeClassDiagnostics");
2073
+ function getClassDiagnosticsInternal(app) {
2074
+ return diagnosticsStore.get(app);
2075
+ }
2076
+ __name(getClassDiagnosticsInternal, "getClassDiagnosticsInternal");
2077
+
2078
+ // src/discovery/source.ts
2079
+ var FilesystemSource = class {
2080
+ static {
2081
+ __name(this, "FilesystemSource");
2082
+ }
2083
+ root;
2084
+ include;
2085
+ exclude;
2086
+ debug;
2087
+ _discoveryErrors = [];
2088
+ constructor(root, include, exclude, debug) {
2089
+ this.root = root;
2090
+ this.include = include;
2091
+ this.exclude = exclude;
2092
+ this.debug = debug;
2093
+ }
2094
+ async discover() {
2095
+ const { discoverControllers: discoverControllers2, getControllersFromResults: getControllersFromResults2, getErrorsFromResults: getErrorsFromResults2 } = await Promise.resolve().then(() => (init_discovery(), discovery_exports));
2096
+ const results = await discoverControllers2({
2097
+ root: this.root,
2098
+ include: this.include,
2099
+ exclude: this.exclude,
2100
+ debug: this.debug
2101
+ });
2102
+ const controllers = getControllersFromResults2(results);
2103
+ this._discoveryErrors = getErrorsFromResults2(results);
2104
+ return controllers;
2105
+ }
2106
+ /**
2107
+ * Access discovery errors, if any, after discover() completes.
2108
+ * @internal
2109
+ */
2110
+ getDiscoveryErrors() {
2111
+ return this._discoveryErrors;
2112
+ }
2113
+ };
2114
+ var MemorySource = class {
2115
+ static {
2116
+ __name(this, "MemorySource");
2117
+ }
2118
+ controllers;
2119
+ constructor(controllers) {
2120
+ this.controllers = controllers;
2121
+ }
2122
+ discover() {
2123
+ return this.controllers;
2124
+ }
2125
+ };
2126
+
2127
+ // src/registrar/registrar.ts
2128
+ function debugLog(debug, message) {
2129
+ if (debug) {
2130
+ process.stderr.write(`[Controllers] ${message}
2131
+ `);
2132
+ }
2133
+ }
2134
+ __name(debugLog, "debugLog");
2135
+ function warnLog(message) {
2136
+ process.stderr.write(`[Controllers] WARNING: ${message}
2137
+ `);
2138
+ }
2139
+ __name(warnLog, "warnLog");
2140
+ function resolveOptions(options, container2) {
2141
+ return {
2142
+ root: options.root ?? null,
2143
+ include: options.include ?? DEFAULT_INCLUDE,
2144
+ exclude: options.exclude ?? DEFAULT_EXCLUDE,
2145
+ controllers: options.controllers ?? [],
2146
+ container: container2,
2147
+ middleware: options.middleware ?? [],
2148
+ debug: options.debug ?? false,
2149
+ prefix: options.prefix ?? "",
2150
+ strict: options.strict ?? false,
2151
+ validate: options.validate ?? true,
2152
+ isolate: options.isolate ?? false,
2153
+ diagnostics: options.diagnostics ?? false
2154
+ };
2155
+ }
2156
+ __name(resolveOptions, "resolveOptions");
2157
+ function validateControllers(registered, container2, instanceCache) {
2158
+ for (const controller of registered) {
2159
+ const token = controller.target;
2160
+ try {
2161
+ instanceCache.set(token, container2.resolve(token));
2162
+ } catch (error) {
2163
+ if (error instanceof DIError) {
2164
+ throw error;
2165
+ }
2166
+ throw new ControllerResolutionError(controller.target.name, error instanceof Error ? error : void 0);
2167
+ }
2168
+ }
2169
+ }
2170
+ __name(validateControllers, "validateControllers");
2171
+ function validateGuards(registered, container2) {
2172
+ const resolved = /* @__PURE__ */ new Set();
2173
+ for (const controller of registered) {
2174
+ for (const route of controller.definition.routes) {
2175
+ const guards = getAllGuards(controller.target, route.methodName);
2176
+ for (const guard of guards) {
2177
+ if (!isGuardClass(guard) || resolved.has(guard)) {
2178
+ continue;
2179
+ }
2180
+ resolved.add(guard);
2181
+ try {
2182
+ container2.resolve(guard);
2183
+ } catch (error) {
2184
+ if (error instanceof DIError) {
2185
+ throw error;
2186
+ }
2187
+ const guardName = guard.name || "AnonymousGuard";
2188
+ throw new Error(`Failed to resolve guard "${guardName}" from the DI container (used by controller "${controller.target.name}").
2189
+
2190
+ A class-based guard is resolved from DI on every request to a guarded route. Surfacing the failure here means an unresolvable or circular guard dependency fails at boot instead of as a 500 on the first request.
2191
+
2192
+ Ensure "${guardName}" and all of its constructor dependencies are registered in the DI container.`, {
2193
+ cause: error instanceof Error ? error : void 0
2194
+ });
2195
+ }
2196
+ }
2197
+ }
2198
+ }
2199
+ }
2200
+ __name(validateGuards, "validateGuards");
2201
+ async function registerControllers(app, options = {}) {
2202
+ const router = app.router;
2203
+ if (!router) {
2204
+ throw new Error("registerControllers() requires an app with a router. Create the app with `createApp()` from `nextrush`, or pass `{ router }` to `createApp()`.");
2205
+ }
2206
+ const container2 = options.container ?? (options.isolate ? createContainer() : app.container ?? container);
2207
+ const resolvedOpts = resolveOptions(options, container2);
2208
+ let source;
2209
+ if (options.source) {
2210
+ source = options.source;
2211
+ } else if (resolvedOpts.root) {
2212
+ source = new FilesystemSource(resolvedOpts.root, resolvedOpts.include, resolvedOpts.exclude, resolvedOpts.debug);
2213
+ } else {
2214
+ source = new MemorySource([
2215
+ ...resolvedOpts.controllers
2216
+ ]);
2217
+ }
2218
+ const ctx = {
2219
+ app,
2220
+ router,
2221
+ resolvedOptions: resolvedOpts,
2222
+ source,
2223
+ discoveredClasses: [],
2224
+ controllerDefinitions: [],
2225
+ providerGraph: /* @__PURE__ */ new Map(),
2226
+ requestScoped: /* @__PURE__ */ new Set(),
2227
+ registryInstances: /* @__PURE__ */ new Map(),
2228
+ builtRoutes: [],
2229
+ graph: null,
2230
+ lifecycleData: {
2231
+ controllerClasses: []
2232
+ },
2233
+ timings: []
2234
+ };
2235
+ await bootstrapPipeline(ctx);
2236
+ if (ctx.discoveredClasses.length === 0) {
2237
+ warnLog("No controllers found. Check your root path or patterns.");
2238
+ return;
2239
+ }
2240
+ debugLog(resolvedOpts.debug, `Registered ${ctx.builtRoutes.length} routes`);
2241
+ }
2242
+ __name(registerControllers, "registerControllers");
2243
+
2244
+ // src/modules/module-graph.ts
2245
+ init_errors();
2246
+ function collectModuleGraph(root) {
2247
+ const ordered = [];
2248
+ const completed = /* @__PURE__ */ new Set();
2249
+ const visiting = /* @__PURE__ */ new Set();
2250
+ const visit = /* @__PURE__ */ __name((mod) => {
2251
+ if (completed.has(mod)) {
2252
+ return;
2253
+ }
2254
+ if (visiting.has(mod)) {
2255
+ return;
2256
+ }
2257
+ if (!isModule(mod)) {
2258
+ throw new NotAModuleError(mod.name || "AnonymousModule");
2259
+ }
2260
+ visiting.add(mod);
2261
+ const metadata = getModuleMetadata(mod);
2262
+ for (const imported of metadata?.imports ?? []) {
2263
+ visit(imported);
2264
+ }
2265
+ visiting.delete(mod);
2266
+ completed.add(mod);
2267
+ ordered.push(mod);
2268
+ }, "visit");
2269
+ visit(root);
2270
+ return ordered;
2271
+ }
2272
+ __name(collectModuleGraph, "collectModuleGraph");
2273
+ function collectModuleControllers(modules) {
2274
+ const controllers = [];
2275
+ const seen = /* @__PURE__ */ new Set();
2276
+ for (const mod of modules) {
2277
+ const metadata = getModuleMetadata(mod);
2278
+ for (const controller of metadata?.controllers ?? []) {
2279
+ if (!seen.has(controller)) {
2280
+ seen.add(controller);
2281
+ controllers.push(controller);
2282
+ }
2283
+ }
2284
+ }
2285
+ return controllers;
2286
+ }
2287
+ __name(collectModuleControllers, "collectModuleControllers");
2288
+
2289
+ // src/modules/module-registrar.ts
2290
+ async function registerModule(app, rootModule, options = {}) {
2291
+ const container2 = options.container ?? (options.isolate ? createContainer() : app.container ?? container);
2292
+ const modules = collectModuleGraph(rootModule);
2293
+ for (const mod of modules) {
2294
+ const metadata = getModuleMetadata(mod);
2295
+ for (const provider of metadata?.providers ?? []) {
2296
+ registerProvider(provider, container2);
2297
+ }
2298
+ }
2299
+ const controllers = collectModuleControllers(modules);
2300
+ await registerControllers(app, {
2301
+ ...options,
2302
+ container: container2,
2303
+ controllers
2304
+ });
2305
+ }
2306
+ __name(registerModule, "registerModule");
2307
+ function registerProvider(provider, container2) {
2308
+ if (typeof provider === "function") {
2309
+ registerClassProvider(provider, container2);
2310
+ return;
2311
+ }
2312
+ registerConfigProvider(provider, container2);
2313
+ }
2314
+ __name(registerProvider, "registerProvider");
2315
+ function registerClassProvider(target, container2) {
2316
+ const token = target;
2317
+ if (container2.isRegistered(token)) {
2318
+ return;
2319
+ }
2320
+ const scope = hasServiceMetadata(target) ? getServiceScope(target) : "singleton";
2321
+ container2.register(token, {
2322
+ useClass: token
2323
+ }, {
2324
+ scope: scope ?? "singleton"
2325
+ });
2326
+ }
2327
+ __name(registerClassProvider, "registerClassProvider");
2328
+ function registerConfigProvider(provider, container2) {
2329
+ const { provide, scope } = provider;
2330
+ if ("useValue" in provider) {
2331
+ container2.register(provide, {
2332
+ useValue: provider.useValue
2333
+ });
2334
+ return;
2335
+ }
2336
+ if (provider.useFactory) {
2337
+ container2.register(provide, {
2338
+ useFactory: provider.useFactory,
2339
+ inject: provider.inject
2340
+ }, {
2341
+ scope: scope ?? "singleton"
2342
+ });
2343
+ return;
2344
+ }
2345
+ if (provider.useClass) {
2346
+ container2.register(provide, {
2347
+ useClass: provider.useClass
2348
+ }, {
2349
+ scope: scope ?? "singleton"
2350
+ });
2351
+ return;
2352
+ }
2353
+ throw new Error(`Invalid module provider for token "${String(provide)}": a provider config must set exactly one of "useValue", "useFactory", or "useClass".`);
2354
+ }
2355
+ __name(registerConfigProvider, "registerConfigProvider");
2356
+
2357
+ // src/index.ts
2358
+ init_discovery();
2359
+
2360
+ // src/diagnostics/get-diagnostics.ts
2361
+ function getClassDiagnostics(app) {
2362
+ return getClassDiagnosticsInternal(app);
2363
+ }
2364
+ __name(getClassDiagnostics, "getClassDiagnostics");
2365
+
2366
+ // src/index.ts
2367
+ init_errors();
2368
+
2369
+ export { All, Body, Catch, Controller, ControllerError, ControllerRegistry, ControllerResolutionError, Ctx, DECORATOR_METADATA_KEYS, Delete, DiscoveryError, FilesystemSource, Get, GuardRejectionError, Head, Header, HttpCode, MemorySource, MissingParameterError, Module, NoRoutesError, NotAControllerError, NotAModuleError, Options, Param, ParameterInjectionError, Patch, Post, Put, Query, Redirect, Req, Res, RouteRegistrationError, SetHeader, UseFilter, UseGuard, UseInterceptor, buildRoutes, collectModuleControllers, collectModuleGraph, createCustomParamDecorator, discoverControllers, getAllFilters, getAllGuards, getAllInterceptors, getAllParamMetadata, getCatchTypes, getClassDiagnostics, getClassFilters, getClassGuards, getClassInterceptors, getConstructorParamTypes, getControllerDefinition, getControllerMetadata, getControllersFromResults, getErrorsFromResults, getHttpCode, getMethodFilters, getMethodGuards, getMethodInterceptors, getModuleMetadata, getParamMetadata, getRedirectMetadata, getResponseHeaders, getRouteMetadata, isController, isGuardClass, isModule, isOnInit, isOnShutdown, isValidHttpMethod, isValidParamSource, registerControllers, registerModule };
2370
+ //# sourceMappingURL=index.js.map
2371
+ //# sourceMappingURL=index.js.map