@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/LICENSE +21 -0
- package/README.md +456 -0
- package/dist/index.d.ts +2306 -0
- package/dist/index.js +2371 -0
- package/dist/index.js.map +1 -0
- package/package.json +68 -0
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
|