@hono-di/core 0.0.9 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/decorators-ClTJ_N9c.d.cts +304 -0
- package/dist/decorators-ClTJ_N9c.d.ts +304 -0
- package/dist/decorators.d.cts +1 -68
- package/dist/decorators.d.ts +1 -68
- package/dist/index.cjs +774 -101
- package/dist/index.d.cts +520 -7
- package/dist/index.d.ts +520 -7
- package/dist/index.js +773 -100
- package/package.json +1 -1
- package/dist/core.cjs +0 -1790
- package/dist/core.d.cts +0 -339
- package/dist/core.d.ts +0 -339
- package/dist/core.js +0 -1755
- package/dist/interfaces-4oTuNIHA.d.cts +0 -139
- package/dist/interfaces-4oTuNIHA.d.ts +0 -139
package/dist/index.cjs
CHANGED
|
@@ -407,14 +407,13 @@ var Container = class {
|
|
|
407
407
|
};
|
|
408
408
|
|
|
409
409
|
// src/injector/context-id.ts
|
|
410
|
-
var ContextId = class
|
|
410
|
+
var ContextId = class {
|
|
411
411
|
static {
|
|
412
412
|
__name(this, "ContextId");
|
|
413
413
|
}
|
|
414
|
-
static idCounter = 0;
|
|
415
414
|
id;
|
|
416
415
|
constructor() {
|
|
417
|
-
this.id =
|
|
416
|
+
this.id = crypto.randomUUID();
|
|
418
417
|
}
|
|
419
418
|
};
|
|
420
419
|
new ContextId();
|
|
@@ -459,6 +458,13 @@ var ModuleRefImpl = class extends ModuleRef {
|
|
|
459
458
|
}
|
|
460
459
|
return this.injector.loadInstance(wrapper, contextId || new ContextId());
|
|
461
460
|
}
|
|
461
|
+
async create(type, contextId) {
|
|
462
|
+
const instance = await this.injector.resolveConstructorParams({
|
|
463
|
+
metatype: type
|
|
464
|
+
}, this.moduleRef, Reflect.getMetadata("design:paramtypes", type) || [], () => {
|
|
465
|
+
}, contextId || new ContextId());
|
|
466
|
+
return new type(...instance);
|
|
467
|
+
}
|
|
462
468
|
};
|
|
463
469
|
|
|
464
470
|
// src/injector/injector.ts
|
|
@@ -471,8 +477,12 @@ var Injector = class {
|
|
|
471
477
|
this.container = container;
|
|
472
478
|
}
|
|
473
479
|
async resolveConstructorParams(wrapper, module, inject, callback, contextId = new ContextId(), inquire = [], parentInquire = []) {
|
|
474
|
-
if (inquire.some((item) => item === wrapper)
|
|
475
|
-
|
|
480
|
+
if (inquire.some((item) => item === wrapper)) {
|
|
481
|
+
const chain = [
|
|
482
|
+
...inquire,
|
|
483
|
+
wrapper
|
|
484
|
+
].map((w) => w.name || "Unknown").join(" -> ");
|
|
485
|
+
throw new Error(`Circular dependency detected: ${chain}`);
|
|
476
486
|
}
|
|
477
487
|
const args = [];
|
|
478
488
|
for (const [index, token] of inject.entries()) {
|
|
@@ -513,13 +523,13 @@ var Injector = class {
|
|
|
513
523
|
return this.loadInstance(wrapper, contextId, inquire);
|
|
514
524
|
}
|
|
515
525
|
lookupProvider(token, module) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
526
|
+
let wrapper = module.getProvider(token);
|
|
527
|
+
if (wrapper) return wrapper;
|
|
519
528
|
for (const importedModule of module.imports) {
|
|
520
529
|
if (importedModule.exports.has(token)) {
|
|
521
|
-
|
|
522
|
-
|
|
530
|
+
wrapper = importedModule.getProvider(token);
|
|
531
|
+
if (wrapper) {
|
|
532
|
+
return wrapper;
|
|
523
533
|
}
|
|
524
534
|
const nestedWrapper = this.lookupProvider(token, importedModule);
|
|
525
535
|
if (nestedWrapper) return nestedWrapper;
|
|
@@ -529,8 +539,9 @@ var Injector = class {
|
|
|
529
539
|
for (const globalModule of this.container.getGlobalModules()) {
|
|
530
540
|
if (globalModule === module) continue;
|
|
531
541
|
if (globalModule.exports.has(token)) {
|
|
532
|
-
|
|
533
|
-
|
|
542
|
+
wrapper = globalModule.getProvider(token);
|
|
543
|
+
if (wrapper) {
|
|
544
|
+
return wrapper;
|
|
534
545
|
}
|
|
535
546
|
const nestedWrapper = this.lookupProvider(token, globalModule);
|
|
536
547
|
if (nestedWrapper) return nestedWrapper;
|
|
@@ -585,7 +596,15 @@ var Injector = class {
|
|
|
585
596
|
}, contextId, inquire);
|
|
586
597
|
const instance = new metatype(...constructorArgs);
|
|
587
598
|
if (wrapper.properties) {
|
|
599
|
+
const UNSAFE_KEYS = [
|
|
600
|
+
"__proto__",
|
|
601
|
+
"constructor",
|
|
602
|
+
"prototype"
|
|
603
|
+
];
|
|
588
604
|
for (const prop of wrapper.properties) {
|
|
605
|
+
if (typeof prop.key === "string" && UNSAFE_KEYS.includes(prop.key)) {
|
|
606
|
+
throw new Error(`Unsafe property injection detected: ${prop.key}`);
|
|
607
|
+
}
|
|
589
608
|
const propInstance = await this.resolveSingleParam(prop.token, wrapper.host, contextId, inquire, prop.isOptional);
|
|
590
609
|
if (propInstance !== void 0) {
|
|
591
610
|
instance[prop.key] = propInstance;
|
|
@@ -637,6 +656,7 @@ var InstanceWrapper = class {
|
|
|
637
656
|
this.useExisting = metadata.useExisting;
|
|
638
657
|
this.isAlias = !!metadata.useExisting;
|
|
639
658
|
this.isOptional = metadata.isOptional;
|
|
659
|
+
this.properties = metadata.properties;
|
|
640
660
|
this.id = Math.random().toString(36).substring(7);
|
|
641
661
|
}
|
|
642
662
|
getInstanceByContextId(contextId) {
|
|
@@ -661,6 +681,13 @@ var InstanceWrapper = class {
|
|
|
661
681
|
}
|
|
662
682
|
this.inject[index] = token;
|
|
663
683
|
}
|
|
684
|
+
cleanup(contextId) {
|
|
685
|
+
if (contextId) {
|
|
686
|
+
this.instancesPerContext.delete(contextId);
|
|
687
|
+
} else {
|
|
688
|
+
this.instancesPerContext.clear();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
664
691
|
};
|
|
665
692
|
|
|
666
693
|
// src/services/logger.service.ts
|
|
@@ -714,7 +741,14 @@ var Logger = class _Logger {
|
|
|
714
741
|
const timestamp = date.toLocaleString();
|
|
715
742
|
const pid = process.pid;
|
|
716
743
|
const contextMessage = context ? `[${context}] ` : "";
|
|
717
|
-
|
|
744
|
+
let output;
|
|
745
|
+
if (typeof message === "symbol") {
|
|
746
|
+
output = message.toString();
|
|
747
|
+
} else if (message instanceof Object) {
|
|
748
|
+
output = JSON.stringify(message, null, 2);
|
|
749
|
+
} else {
|
|
750
|
+
output = String(message);
|
|
751
|
+
}
|
|
718
752
|
const currentTimestamp = Date.now();
|
|
719
753
|
const timeDiff = _Logger.updateAndGetTimestampDiff(currentTimestamp);
|
|
720
754
|
const C = {
|
|
@@ -761,15 +795,15 @@ var Logger = class _Logger {
|
|
|
761
795
|
};
|
|
762
796
|
|
|
763
797
|
// src/scanner.ts
|
|
764
|
-
var
|
|
798
|
+
var Scanner = class {
|
|
765
799
|
static {
|
|
766
|
-
__name(this, "
|
|
800
|
+
__name(this, "Scanner");
|
|
767
801
|
}
|
|
768
802
|
container;
|
|
769
803
|
constructor(container) {
|
|
770
804
|
this.container = container;
|
|
771
805
|
}
|
|
772
|
-
logger = new Logger("
|
|
806
|
+
logger = new Logger("Scanner");
|
|
773
807
|
async scan(module) {
|
|
774
808
|
await this.scanModule(module);
|
|
775
809
|
}
|
|
@@ -858,12 +892,13 @@ var HonoDiScanner = class {
|
|
|
858
892
|
const isPlainObject = provider && typeof provider === "object" && "provide" in provider;
|
|
859
893
|
if (!isPlainObject) {
|
|
860
894
|
const token2 = provider;
|
|
895
|
+
const providerScope = Reflect.getMetadata(METADATA_KEYS.SCOPE, provider) ?? Scope.DEFAULT;
|
|
861
896
|
const wrapper2 = new InstanceWrapper({
|
|
862
897
|
token: token2,
|
|
863
898
|
name: token2.name,
|
|
864
899
|
metatype: provider,
|
|
865
900
|
host: moduleRef,
|
|
866
|
-
scope:
|
|
901
|
+
scope: providerScope
|
|
867
902
|
});
|
|
868
903
|
this.scanDependencies(wrapper2);
|
|
869
904
|
moduleRef.addProvider(wrapper2);
|
|
@@ -892,12 +927,13 @@ var HonoDiScanner = class {
|
|
|
892
927
|
}
|
|
893
928
|
insertController(controller, moduleRef) {
|
|
894
929
|
const token = controller;
|
|
930
|
+
const controllerScope = Reflect.getMetadata(METADATA_KEYS.SCOPE, controller) ?? Scope.DEFAULT;
|
|
895
931
|
const wrapper = new InstanceWrapper({
|
|
896
932
|
token,
|
|
897
933
|
name: token.name,
|
|
898
934
|
metatype: controller,
|
|
899
935
|
host: moduleRef,
|
|
900
|
-
scope:
|
|
936
|
+
scope: controllerScope
|
|
901
937
|
});
|
|
902
938
|
this.scanDependencies(wrapper);
|
|
903
939
|
moduleRef.addController(wrapper);
|
|
@@ -1011,6 +1047,172 @@ var MiddlewareConfigProxyImpl = class MiddlewareConfigProxyImpl2 {
|
|
|
1011
1047
|
}
|
|
1012
1048
|
};
|
|
1013
1049
|
|
|
1050
|
+
// src/application/context-manager.ts
|
|
1051
|
+
var ContextManager = class {
|
|
1052
|
+
static {
|
|
1053
|
+
__name(this, "ContextManager");
|
|
1054
|
+
}
|
|
1055
|
+
activeContexts = /* @__PURE__ */ new Set();
|
|
1056
|
+
/**
|
|
1057
|
+
* Registers a new context as active
|
|
1058
|
+
*/
|
|
1059
|
+
addContext(contextId) {
|
|
1060
|
+
this.activeContexts.add(contextId);
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Removes a context and allows cleanup
|
|
1064
|
+
*/
|
|
1065
|
+
removeContext(contextId) {
|
|
1066
|
+
this.activeContexts.delete(contextId);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Gets all currently active contexts
|
|
1070
|
+
*/
|
|
1071
|
+
getActiveContexts() {
|
|
1072
|
+
return this.activeContexts;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Clears all tracked contexts
|
|
1076
|
+
*/
|
|
1077
|
+
clearAll() {
|
|
1078
|
+
this.activeContexts.clear();
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
|
|
1082
|
+
// src/application/lifecycle-manager.ts
|
|
1083
|
+
var LifecycleManager = class {
|
|
1084
|
+
static {
|
|
1085
|
+
__name(this, "LifecycleManager");
|
|
1086
|
+
}
|
|
1087
|
+
logger = new Logger("LifecycleManager");
|
|
1088
|
+
/**
|
|
1089
|
+
* Calls a lifecycle hook on all providers/controllers in all modules
|
|
1090
|
+
*
|
|
1091
|
+
* @param hookName - Name of the lifecycle method to call
|
|
1092
|
+
* @param container - DI container with all modules
|
|
1093
|
+
*/
|
|
1094
|
+
async callHook(hookName, container) {
|
|
1095
|
+
const modules = container.getModules();
|
|
1096
|
+
for (const module of modules.values()) {
|
|
1097
|
+
for (const wrapper of module.providers.values()) {
|
|
1098
|
+
const instance = wrapper.instance;
|
|
1099
|
+
if (instance && typeof instance[hookName] === "function") {
|
|
1100
|
+
await instance[hookName]();
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
for (const wrapper of module.controllers.values()) {
|
|
1104
|
+
const instance = wrapper.instance;
|
|
1105
|
+
if (instance && typeof instance[hookName] === "function") {
|
|
1106
|
+
await instance[hookName]();
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Calls OnModuleInit on all modules
|
|
1113
|
+
*/
|
|
1114
|
+
async onModuleInit(container) {
|
|
1115
|
+
this.logger.log("Calling OnModuleInit...");
|
|
1116
|
+
await this.callHook("onModuleInit", container);
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Calls OnApplicationBootstrap on all modules
|
|
1120
|
+
*/
|
|
1121
|
+
async onApplicationBootstrap(container) {
|
|
1122
|
+
this.logger.log("Calling OnApplicationBootstrap...");
|
|
1123
|
+
await this.callHook("onApplicationBootstrap", container);
|
|
1124
|
+
}
|
|
1125
|
+
/**
|
|
1126
|
+
* Calls shutdown hooks on all modules
|
|
1127
|
+
*/
|
|
1128
|
+
async onApplicationShutdown(container) {
|
|
1129
|
+
this.logger.log("Calling shutdown hooks...");
|
|
1130
|
+
await this.callHook("beforeApplicationShutdown", container);
|
|
1131
|
+
await this.callHook("onApplicationShutdown", container);
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
// src/application/argument-resolver.ts
|
|
1136
|
+
var ArgumentResolver = class {
|
|
1137
|
+
static {
|
|
1138
|
+
__name(this, "ArgumentResolver");
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Resolves arguments from request context based on metadata
|
|
1142
|
+
*
|
|
1143
|
+
* @param c - Hono context
|
|
1144
|
+
* @param argsMetadata - Pre-parsed argument metadata
|
|
1145
|
+
* @param pipes - Pipes to apply to arguments
|
|
1146
|
+
* @param executionContext - Execution context for guards/interceptors
|
|
1147
|
+
* @param moduleRef - Module reference for DI
|
|
1148
|
+
* @param contextId - Request context ID
|
|
1149
|
+
* @returns Array of resolved arguments
|
|
1150
|
+
*/
|
|
1151
|
+
async resolveArgs(c, argsMetadata, pipes, executionContext, moduleRef, contextId) {
|
|
1152
|
+
const args = [];
|
|
1153
|
+
for (const metadata of argsMetadata) {
|
|
1154
|
+
let value;
|
|
1155
|
+
switch (metadata.paramtype) {
|
|
1156
|
+
case RouteParamtypes.BODY:
|
|
1157
|
+
value = await c.req.json().catch(() => ({}));
|
|
1158
|
+
if (metadata.data) {
|
|
1159
|
+
value = value[metadata.data];
|
|
1160
|
+
}
|
|
1161
|
+
break;
|
|
1162
|
+
case RouteParamtypes.QUERY:
|
|
1163
|
+
if (metadata.data) {
|
|
1164
|
+
value = c.req.query(metadata.data);
|
|
1165
|
+
} else {
|
|
1166
|
+
value = c.req.query();
|
|
1167
|
+
}
|
|
1168
|
+
break;
|
|
1169
|
+
case RouteParamtypes.PARAM:
|
|
1170
|
+
if (metadata.data) {
|
|
1171
|
+
value = c.req.param(metadata.data);
|
|
1172
|
+
} else {
|
|
1173
|
+
value = c.req.param();
|
|
1174
|
+
}
|
|
1175
|
+
break;
|
|
1176
|
+
case RouteParamtypes.HEADERS:
|
|
1177
|
+
if (metadata.data) {
|
|
1178
|
+
value = c.req.header(metadata.data);
|
|
1179
|
+
} else {
|
|
1180
|
+
const headers = {};
|
|
1181
|
+
c.req.raw.headers.forEach((val, key) => {
|
|
1182
|
+
headers[key] = val;
|
|
1183
|
+
});
|
|
1184
|
+
value = headers;
|
|
1185
|
+
}
|
|
1186
|
+
break;
|
|
1187
|
+
case RouteParamtypes.CONTEXT:
|
|
1188
|
+
value = c;
|
|
1189
|
+
break;
|
|
1190
|
+
case RouteParamtypes.REQUEST:
|
|
1191
|
+
value = c.req;
|
|
1192
|
+
break;
|
|
1193
|
+
case RouteParamtypes.RESPONSE:
|
|
1194
|
+
value = c.res;
|
|
1195
|
+
break;
|
|
1196
|
+
default:
|
|
1197
|
+
value = void 0;
|
|
1198
|
+
}
|
|
1199
|
+
const allPipes = [
|
|
1200
|
+
...metadata.pipes,
|
|
1201
|
+
...pipes
|
|
1202
|
+
];
|
|
1203
|
+
for (const pipe of allPipes) {
|
|
1204
|
+
value = await pipe.transform(value, {
|
|
1205
|
+
type: metadata.paramtype,
|
|
1206
|
+
data: metadata.data,
|
|
1207
|
+
metatype: void 0
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
args[metadata.index] = value;
|
|
1211
|
+
}
|
|
1212
|
+
return args;
|
|
1213
|
+
}
|
|
1214
|
+
};
|
|
1215
|
+
|
|
1014
1216
|
// src/common/exceptions/http.exception.ts
|
|
1015
1217
|
var HttpException = class extends Error {
|
|
1016
1218
|
static {
|
|
@@ -1173,10 +1375,191 @@ var GatewayTimeoutException = class extends HttpException {
|
|
|
1173
1375
|
}
|
|
1174
1376
|
};
|
|
1175
1377
|
|
|
1176
|
-
// src/application.ts
|
|
1177
|
-
var
|
|
1378
|
+
// src/application/exception-handler.ts
|
|
1379
|
+
var ExceptionHandler = class {
|
|
1380
|
+
static {
|
|
1381
|
+
__name(this, "ExceptionHandler");
|
|
1382
|
+
}
|
|
1383
|
+
logger = new Logger("ExceptionHandler");
|
|
1384
|
+
/**
|
|
1385
|
+
* Handles an exception through the filter chain
|
|
1386
|
+
*
|
|
1387
|
+
* @param exception - The exception to handle
|
|
1388
|
+
* @param c - Hono context
|
|
1389
|
+
* @param controllerClass - Controller class
|
|
1390
|
+
* @param methodName - Handler method name
|
|
1391
|
+
* @param moduleRef - Module reference
|
|
1392
|
+
* @param contextId - Context ID
|
|
1393
|
+
* @param globalFilters - Global exception filters
|
|
1394
|
+
* @param resolveFilters - Function to resolve filters
|
|
1395
|
+
* @returns Response from filter or default error response
|
|
1396
|
+
*/
|
|
1397
|
+
async handle(exception, c, controllerClass, methodName, moduleRef, contextId, globalFilters, resolveFilters) {
|
|
1398
|
+
const filters = await resolveFilters([
|
|
1399
|
+
...Reflect.getMetadata(METADATA_KEYS.USE_FILTERS, moduleRef.controllers.get(controllerClass)?.instance?.[methodName]) || [],
|
|
1400
|
+
...Reflect.getMetadata(METADATA_KEYS.USE_FILTERS, controllerClass) || [],
|
|
1401
|
+
...globalFilters
|
|
1402
|
+
], moduleRef, contextId);
|
|
1403
|
+
for (const filter of filters) {
|
|
1404
|
+
const catchExceptions = Reflect.getMetadata(METADATA_KEYS.FILTER_CATCH, filter.constructor) || [];
|
|
1405
|
+
if (catchExceptions.length === 0 || catchExceptions.some((e) => exception instanceof e)) {
|
|
1406
|
+
const host = new ExecutionContextHost([
|
|
1407
|
+
c
|
|
1408
|
+
], controllerClass, moduleRef.controllers.get(controllerClass)?.instance?.[methodName]);
|
|
1409
|
+
return await filter.catch(exception, host);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
return this.handleDefault(exception, c);
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Default exception handling when no filter matches
|
|
1416
|
+
*/
|
|
1417
|
+
async handleDefault(exception, c) {
|
|
1418
|
+
this.logger.error(exception);
|
|
1419
|
+
if (exception instanceof HttpException) {
|
|
1420
|
+
const status = exception.getStatus();
|
|
1421
|
+
const response = exception.getResponse();
|
|
1422
|
+
c.status(status);
|
|
1423
|
+
return c.json(response);
|
|
1424
|
+
}
|
|
1425
|
+
if (exception instanceof Error) {
|
|
1426
|
+
c.status(500);
|
|
1427
|
+
return c.json({
|
|
1428
|
+
statusCode: 500,
|
|
1429
|
+
message: "Internal Server Error",
|
|
1430
|
+
cause: exception.message
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
c.status(500);
|
|
1434
|
+
return c.json({
|
|
1435
|
+
statusCode: 500,
|
|
1436
|
+
message: "Internal Server Error",
|
|
1437
|
+
error: String(exception)
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
};
|
|
1441
|
+
|
|
1442
|
+
// src/application/middleware-handler.ts
|
|
1443
|
+
var MiddlewareHandler = class {
|
|
1444
|
+
static {
|
|
1445
|
+
__name(this, "MiddlewareHandler");
|
|
1446
|
+
}
|
|
1447
|
+
logger = new Logger("MiddlewareHandler");
|
|
1448
|
+
/**
|
|
1449
|
+
* Registers all middleware to the Hono app
|
|
1450
|
+
*
|
|
1451
|
+
* @param app - Hono instance
|
|
1452
|
+
* @param middlewareConfigs - Middleware configurations from modules
|
|
1453
|
+
* @param container - DI container
|
|
1454
|
+
* @param injector - Injector for resolving middleware instances
|
|
1455
|
+
*/
|
|
1456
|
+
async registerMiddleware(app, middlewareConfigs, container, injector) {
|
|
1457
|
+
const resolvedConfigs = [];
|
|
1458
|
+
for (const config of middlewareConfigs) {
|
|
1459
|
+
const instances = [];
|
|
1460
|
+
for (const middleware of config.middlewares) {
|
|
1461
|
+
if (typeof middleware === "function") {
|
|
1462
|
+
const modules = container.getModules();
|
|
1463
|
+
let wrapper;
|
|
1464
|
+
for (const module of modules.values()) {
|
|
1465
|
+
wrapper = module.getProvider(middleware);
|
|
1466
|
+
if (wrapper) break;
|
|
1467
|
+
}
|
|
1468
|
+
if (!wrapper) {
|
|
1469
|
+
wrapper = new InstanceWrapper({
|
|
1470
|
+
token: middleware,
|
|
1471
|
+
name: middleware.name,
|
|
1472
|
+
metatype: middleware,
|
|
1473
|
+
host: Array.from(modules.values())[0]
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
const instance = wrapper.instance || await injector.loadInstance(wrapper, new ContextId());
|
|
1477
|
+
instances.push(instance);
|
|
1478
|
+
} else {
|
|
1479
|
+
instances.push(middleware);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
resolvedConfigs.push({
|
|
1483
|
+
...config,
|
|
1484
|
+
instances
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1487
|
+
app.use("*", async (c, next) => {
|
|
1488
|
+
const path = c.req.path;
|
|
1489
|
+
const method = c.req.method;
|
|
1490
|
+
const matchingMiddleware = [];
|
|
1491
|
+
for (const config of resolvedConfigs) {
|
|
1492
|
+
if (this.isRouteExcluded(config.excludes, path, method)) continue;
|
|
1493
|
+
if (this.isRouteMatch(config.routes, path, method)) {
|
|
1494
|
+
matchingMiddleware.push(...config.instances);
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
if (matchingMiddleware.length === 0) {
|
|
1498
|
+
return await next();
|
|
1499
|
+
}
|
|
1500
|
+
const executeChain = /* @__PURE__ */ __name(async (index, finalNext) => {
|
|
1501
|
+
if (index >= matchingMiddleware.length) {
|
|
1502
|
+
return await finalNext();
|
|
1503
|
+
}
|
|
1504
|
+
const middleware = matchingMiddleware[index];
|
|
1505
|
+
return new Promise((resolve, reject) => {
|
|
1506
|
+
let nextCalled = false;
|
|
1507
|
+
const nextFn = /* @__PURE__ */ __name(async () => {
|
|
1508
|
+
nextCalled = true;
|
|
1509
|
+
try {
|
|
1510
|
+
await executeChain(index + 1, finalNext);
|
|
1511
|
+
resolve();
|
|
1512
|
+
} catch (e) {
|
|
1513
|
+
reject(e);
|
|
1514
|
+
}
|
|
1515
|
+
}, "nextFn");
|
|
1516
|
+
try {
|
|
1517
|
+
const useFn = middleware.use ? middleware.use.bind(middleware) : middleware;
|
|
1518
|
+
const result = useFn(c, nextFn);
|
|
1519
|
+
if (result instanceof Promise) {
|
|
1520
|
+
result.then(() => {
|
|
1521
|
+
if (!nextCalled) resolve();
|
|
1522
|
+
}).catch(reject);
|
|
1523
|
+
} else {
|
|
1524
|
+
if (!nextCalled) resolve();
|
|
1525
|
+
}
|
|
1526
|
+
} catch (e) {
|
|
1527
|
+
reject(e);
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
}, "executeChain");
|
|
1531
|
+
await executeChain(0, next);
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
isRouteMatch(routes, path, method) {
|
|
1535
|
+
for (const route of routes) {
|
|
1536
|
+
if (typeof route === "string") {
|
|
1537
|
+
const normalizedRoute = route === "*" ? "*" : route.startsWith("/") ? route : `/${route}`;
|
|
1538
|
+
if (normalizedRoute === "*" || path.startsWith(normalizedRoute)) return true;
|
|
1539
|
+
if (path === normalizedRoute) return true;
|
|
1540
|
+
} else if (route instanceof RegExp) {
|
|
1541
|
+
if (route.test(path)) return true;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
return false;
|
|
1545
|
+
}
|
|
1546
|
+
isRouteExcluded(excludes, path, method) {
|
|
1547
|
+
for (const exclude of excludes) {
|
|
1548
|
+
if (typeof exclude === "string") {
|
|
1549
|
+
const normalizedExclude = exclude.startsWith("/") ? exclude : `/${exclude}`;
|
|
1550
|
+
if (path.startsWith(normalizedExclude)) return true;
|
|
1551
|
+
} else if (exclude instanceof RegExp) {
|
|
1552
|
+
if (exclude.test(path)) return true;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
return false;
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1558
|
+
|
|
1559
|
+
// src/application/index.ts
|
|
1560
|
+
var Application = class {
|
|
1178
1561
|
static {
|
|
1179
|
-
__name(this, "
|
|
1562
|
+
__name(this, "Application");
|
|
1180
1563
|
}
|
|
1181
1564
|
app;
|
|
1182
1565
|
container;
|
|
@@ -1185,28 +1568,114 @@ var HonoDiApplication = class {
|
|
|
1185
1568
|
globalPipes = [];
|
|
1186
1569
|
globalGuards = [];
|
|
1187
1570
|
globalInterceptors = [];
|
|
1571
|
+
scanner;
|
|
1572
|
+
// Delegate to managers
|
|
1573
|
+
contextManager = new ContextManager();
|
|
1574
|
+
lifecycleManager = new LifecycleManager();
|
|
1575
|
+
argumentResolver = new ArgumentResolver();
|
|
1576
|
+
exceptionHandler = new ExceptionHandler();
|
|
1577
|
+
middlewareHandler = new MiddlewareHandler();
|
|
1578
|
+
// Security: Dangerous property names that should not be injected
|
|
1579
|
+
UNSAFE_PROPERTIES = /* @__PURE__ */ new Set([
|
|
1580
|
+
"__proto__",
|
|
1581
|
+
"constructor",
|
|
1582
|
+
"prototype"
|
|
1583
|
+
]);
|
|
1584
|
+
// Performance: Route metadata cache
|
|
1585
|
+
routeCache = /* @__PURE__ */ new Map();
|
|
1188
1586
|
constructor(app, container, injector) {
|
|
1189
1587
|
this.app = app;
|
|
1190
1588
|
this.container = container;
|
|
1191
1589
|
this.injector = injector;
|
|
1192
|
-
|
|
1590
|
+
this.scanner = new Scanner(container);
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Registers global exception filters
|
|
1594
|
+
*
|
|
1595
|
+
* @param filters - Exception filter instances or classes
|
|
1596
|
+
* @returns This application instance for chaining
|
|
1597
|
+
*
|
|
1598
|
+
* @example
|
|
1599
|
+
* ```typescript
|
|
1600
|
+
* app.useGlobalFilters(new HttpExceptionFilter());
|
|
1601
|
+
* ```
|
|
1602
|
+
*
|
|
1603
|
+
* @public
|
|
1604
|
+
*/
|
|
1193
1605
|
useGlobalFilters(...filters) {
|
|
1194
1606
|
this.globalFilters.push(...filters);
|
|
1195
1607
|
return this;
|
|
1196
1608
|
}
|
|
1609
|
+
/**
|
|
1610
|
+
* Registers global pipes for request transformation/validation
|
|
1611
|
+
*
|
|
1612
|
+
* @param pipes - Pipe instances or classes
|
|
1613
|
+
* @returns This application instance for chaining
|
|
1614
|
+
*
|
|
1615
|
+
* @example
|
|
1616
|
+
* ```typescript
|
|
1617
|
+
* app.useGlobalPipes(new ValidationPipe());
|
|
1618
|
+
* ```
|
|
1619
|
+
*
|
|
1620
|
+
* @public
|
|
1621
|
+
*/
|
|
1197
1622
|
useGlobalPipes(...pipes) {
|
|
1198
1623
|
this.globalPipes.push(...pipes);
|
|
1199
1624
|
return this;
|
|
1200
1625
|
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Registers global interceptors for request/response manipulation
|
|
1628
|
+
*
|
|
1629
|
+
* @param interceptors - Interceptor instances or classes
|
|
1630
|
+
* @returns This application instance for chaining
|
|
1631
|
+
*
|
|
1632
|
+
* @example
|
|
1633
|
+
* ```typescript
|
|
1634
|
+
* app.useGlobalInterceptors(new LoggingInterceptor());
|
|
1635
|
+
* ```
|
|
1636
|
+
*
|
|
1637
|
+
* @public
|
|
1638
|
+
*/
|
|
1201
1639
|
useGlobalInterceptors(...interceptors) {
|
|
1202
1640
|
this.globalInterceptors.push(...interceptors);
|
|
1203
1641
|
return this;
|
|
1204
1642
|
}
|
|
1643
|
+
/**
|
|
1644
|
+
* Registers global guards for route protection
|
|
1645
|
+
*
|
|
1646
|
+
* @param guards - Guard instances or classes
|
|
1647
|
+
* @returns This application instance for chaining
|
|
1648
|
+
*
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```typescript
|
|
1651
|
+
* app.useGlobalGuards(new AuthGuard());
|
|
1652
|
+
* ```
|
|
1653
|
+
*
|
|
1654
|
+
* @public
|
|
1655
|
+
*/
|
|
1205
1656
|
useGlobalGuards(...guards) {
|
|
1206
1657
|
this.globalGuards.push(...guards);
|
|
1207
1658
|
return this;
|
|
1208
1659
|
}
|
|
1209
1660
|
globalPrefix = "";
|
|
1661
|
+
/**
|
|
1662
|
+
* Sets a global prefix for all routes
|
|
1663
|
+
*
|
|
1664
|
+
* @param prefix - URL prefix (e.g., 'api' or 'api/v1')
|
|
1665
|
+
* @returns This application instance for chaining
|
|
1666
|
+
*
|
|
1667
|
+
* @remarks
|
|
1668
|
+
* Must be called before init() if using autoInit: true.
|
|
1669
|
+
* Use autoInit: false in HonoDiFactory.create() to set prefix before initialization.
|
|
1670
|
+
*
|
|
1671
|
+
* @example
|
|
1672
|
+
* ```typescript
|
|
1673
|
+
* app.setGlobalPrefix('api/v1');
|
|
1674
|
+
* // All routes will be prefixed with /api/v1
|
|
1675
|
+
* ```
|
|
1676
|
+
*
|
|
1677
|
+
* @public
|
|
1678
|
+
*/
|
|
1210
1679
|
setGlobalPrefix(prefix) {
|
|
1211
1680
|
if (this.isInitialized) {
|
|
1212
1681
|
this.logger.warn("Setting global prefix after initialization will not affect existing routes. Use { autoInit: false } in HonoDiFactory.create() if you need to set a prefix.");
|
|
@@ -1217,8 +1686,22 @@ var HonoDiApplication = class {
|
|
|
1217
1686
|
getGlobalPrefix() {
|
|
1218
1687
|
return this.globalPrefix;
|
|
1219
1688
|
}
|
|
1220
|
-
logger = new Logger("
|
|
1689
|
+
logger = new Logger("Application");
|
|
1221
1690
|
isInitialized = false;
|
|
1691
|
+
/**
|
|
1692
|
+
* Initializes the application by registering all routes and middleware
|
|
1693
|
+
*
|
|
1694
|
+
* @returns Promise resolving to this application instance
|
|
1695
|
+
*
|
|
1696
|
+
* @remarks
|
|
1697
|
+
* Called automatically unless autoInit: false is specified in HonoDiFactory.create().
|
|
1698
|
+
* Handles:
|
|
1699
|
+
* - Middleware registration
|
|
1700
|
+
* - Controller and route registration
|
|
1701
|
+
* - Lifecycle hook execution (OnApplicationBootstrap)
|
|
1702
|
+
*
|
|
1703
|
+
* @public
|
|
1704
|
+
*/
|
|
1222
1705
|
async init() {
|
|
1223
1706
|
if (this.isInitialized) return this;
|
|
1224
1707
|
this.logger.log("Initializing middleware...");
|
|
@@ -1269,26 +1752,63 @@ var HonoDiApplication = class {
|
|
|
1269
1752
|
const { prefix } = Reflect.getMetadata(METADATA_KEYS.CONTROLLER, controllerClass);
|
|
1270
1753
|
const routes = Reflect.getMetadata(METADATA_KEYS.ROUTES, controllerClass);
|
|
1271
1754
|
if (!routes) return;
|
|
1755
|
+
const controllerGuards = Reflect.getMetadata(METADATA_KEYS.USE_GUARDS, controllerClass) || [];
|
|
1756
|
+
const controllerInterceptors = Reflect.getMetadata(METADATA_KEYS.USE_INTERCEPTORS, controllerClass) || [];
|
|
1757
|
+
const controllerPipes = Reflect.getMetadata(METADATA_KEYS.USE_PIPES, controllerClass) || [];
|
|
1272
1758
|
routes.forEach((route) => {
|
|
1273
1759
|
const globalPrefix = this.getGlobalPrefix();
|
|
1274
1760
|
const fullPath = this.combinePaths(globalPrefix, this.combinePaths(prefix, route.path));
|
|
1761
|
+
const cacheKey = `${controllerClass.name}.${route.methodName}`;
|
|
1762
|
+
const handler = controllerClass.prototype[route.methodName];
|
|
1763
|
+
const methodGuards = Reflect.getMetadata(METADATA_KEYS.USE_GUARDS, handler) || [];
|
|
1764
|
+
const methodInterceptors = Reflect.getMetadata(METADATA_KEYS.USE_INTERCEPTORS, handler) || [];
|
|
1765
|
+
const methodPipes = Reflect.getMetadata(METADATA_KEYS.USE_PIPES, handler) || [];
|
|
1766
|
+
const allGuards = [
|
|
1767
|
+
...this.globalGuards,
|
|
1768
|
+
...controllerGuards,
|
|
1769
|
+
...methodGuards
|
|
1770
|
+
];
|
|
1771
|
+
const allInterceptors = [
|
|
1772
|
+
...this.globalInterceptors,
|
|
1773
|
+
...controllerInterceptors,
|
|
1774
|
+
...methodInterceptors
|
|
1775
|
+
];
|
|
1776
|
+
const allPipes = [
|
|
1777
|
+
...this.globalPipes,
|
|
1778
|
+
...controllerPipes,
|
|
1779
|
+
...methodPipes
|
|
1780
|
+
];
|
|
1781
|
+
const argsMetadata = Reflect.getMetadata(METADATA_KEYS.ROUTE_ARGS_METADATA, controllerClass, route.methodName) || {};
|
|
1782
|
+
const parsedArgs = Object.values(argsMetadata).sort((a, b) => a.index - b.index).map((arg) => ({
|
|
1783
|
+
index: arg.index,
|
|
1784
|
+
data: arg.data,
|
|
1785
|
+
paramtype: arg.paramtype,
|
|
1786
|
+
pipes: arg.pipes || []
|
|
1787
|
+
}));
|
|
1788
|
+
const wrapper = moduleRef.controllers.get(controllerClass);
|
|
1789
|
+
this.routeCache.set(cacheKey, {
|
|
1790
|
+
wrapper,
|
|
1791
|
+
handler,
|
|
1792
|
+
guards: allGuards,
|
|
1793
|
+
interceptors: allInterceptors,
|
|
1794
|
+
pipes: allPipes,
|
|
1795
|
+
argsMetadata: parsedArgs,
|
|
1796
|
+
httpCode: Reflect.getMetadata(METADATA_KEYS.HTTP_CODE, handler),
|
|
1797
|
+
headers: Reflect.getMetadata(METADATA_KEYS.HEADERS, handler),
|
|
1798
|
+
redirect: Reflect.getMetadata(METADATA_KEYS.REDIRECT, handler)
|
|
1799
|
+
});
|
|
1275
1800
|
app[route.requestMethod](fullPath, async (c) => {
|
|
1276
1801
|
const contextId = new ContextId();
|
|
1802
|
+
this.contextManager.addContext(contextId);
|
|
1803
|
+
const cacheKey2 = `${controllerClass.name}.${route.methodName}`;
|
|
1804
|
+
const cache = this.routeCache.get(cacheKey2);
|
|
1277
1805
|
const executionContext = new ExecutionContextHost([
|
|
1278
1806
|
c
|
|
1279
|
-
], controllerClass,
|
|
1807
|
+
], controllerClass, cache.handler);
|
|
1280
1808
|
try {
|
|
1281
|
-
const
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1285
|
-
const controllerInstance = await this.injector.loadInstance(wrapper, contextId);
|
|
1286
|
-
const handler = controllerInstance[route.methodName].bind(controllerInstance);
|
|
1287
|
-
const guards = await this.resolveContextItems([
|
|
1288
|
-
...this.globalGuards,
|
|
1289
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_GUARDS, controllerClass) || [],
|
|
1290
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_GUARDS, controllerInstance[route.methodName]) || []
|
|
1291
|
-
], moduleRef, contextId);
|
|
1809
|
+
const controllerInstance = await this.injector.loadInstance(cache.wrapper, contextId);
|
|
1810
|
+
const handler2 = controllerInstance[route.methodName].bind(controllerInstance);
|
|
1811
|
+
const guards = await this.resolveContextItems(cache.guards, moduleRef, contextId);
|
|
1292
1812
|
if (!await this.runGuards(guards, executionContext)) {
|
|
1293
1813
|
c.status(403);
|
|
1294
1814
|
return c.json({
|
|
@@ -1296,25 +1816,16 @@ var HonoDiApplication = class {
|
|
|
1296
1816
|
message: "Forbidden resource"
|
|
1297
1817
|
});
|
|
1298
1818
|
}
|
|
1299
|
-
const interceptors = await this.resolveContextItems(
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
]
|
|
1304
|
-
const pipes = await this.resolveContextItems([
|
|
1305
|
-
...this.globalPipes,
|
|
1306
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_PIPES, controllerClass) || [],
|
|
1307
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_PIPES, controllerInstance[route.methodName]) || []
|
|
1308
|
-
], moduleRef, contextId);
|
|
1309
|
-
const args = await this.resolveArgs(contextId, executionContext, controllerClass, route.methodName, moduleRef, pipes);
|
|
1310
|
-
if (args.length === 0 && !Reflect.hasMetadata(METADATA_KEYS.ROUTE_ARGS_METADATA, controllerClass, route.methodName)) {
|
|
1311
|
-
args.push(c);
|
|
1312
|
-
}
|
|
1819
|
+
const interceptors = await this.resolveContextItems(cache.interceptors, moduleRef, contextId);
|
|
1820
|
+
const pipes = await this.resolveContextItems(cache.pipes, moduleRef, contextId);
|
|
1821
|
+
const args = cache.argsMetadata.length > 0 ? await this.resolveArgsFromCache(c, cache.argsMetadata, pipes, executionContext, moduleRef, contextId) : [
|
|
1822
|
+
c
|
|
1823
|
+
];
|
|
1313
1824
|
const interceptorChain = /* @__PURE__ */ __name(async (index) => {
|
|
1314
1825
|
if (index >= interceptors.length) {
|
|
1315
1826
|
return new rxjs.Observable((subscriber) => {
|
|
1316
1827
|
try {
|
|
1317
|
-
const result2 =
|
|
1828
|
+
const result2 = handler2(...args);
|
|
1318
1829
|
if (result2 instanceof Promise) {
|
|
1319
1830
|
result2.then((data) => {
|
|
1320
1831
|
subscriber.next(data);
|
|
@@ -1335,20 +1846,17 @@ var HonoDiApplication = class {
|
|
|
1335
1846
|
}, "interceptorChain");
|
|
1336
1847
|
const obs = await interceptorChain(0);
|
|
1337
1848
|
const result = await rxjs.lastValueFrom(obs);
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
c.status(httpCode);
|
|
1849
|
+
if (cache.httpCode) {
|
|
1850
|
+
c.status(cache.httpCode);
|
|
1341
1851
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
headers.forEach((h) => c.header(h.name, h.value));
|
|
1852
|
+
if (cache.headers) {
|
|
1853
|
+
cache.headers.forEach((h) => c.header(h.name, h.value));
|
|
1345
1854
|
}
|
|
1346
|
-
|
|
1347
|
-
if (redirect) {
|
|
1855
|
+
if (cache.redirect) {
|
|
1348
1856
|
if (result && typeof result === "object" && result.url) {
|
|
1349
|
-
return c.redirect(result.url, result.statusCode || redirect.statusCode);
|
|
1857
|
+
return c.redirect(result.url, result.statusCode || cache.redirect.statusCode);
|
|
1350
1858
|
}
|
|
1351
|
-
return c.redirect(redirect.url, redirect.statusCode);
|
|
1859
|
+
return c.redirect(cache.redirect.url, cache.redirect.statusCode);
|
|
1352
1860
|
}
|
|
1353
1861
|
if (result instanceof Response) {
|
|
1354
1862
|
return result;
|
|
@@ -1358,46 +1866,16 @@ var HonoDiApplication = class {
|
|
|
1358
1866
|
}
|
|
1359
1867
|
return c.json(result);
|
|
1360
1868
|
} catch (exception) {
|
|
1361
|
-
return await this.
|
|
1869
|
+
return await this.exceptionHandler.handle(exception, c, controllerClass, route.methodName, moduleRef, contextId, this.globalFilters, this.resolveContextItems.bind(this));
|
|
1870
|
+
} finally {
|
|
1871
|
+
this.cleanupContext(contextId);
|
|
1362
1872
|
}
|
|
1363
1873
|
});
|
|
1364
1874
|
this.logger.log(`[Route] Mapped {${fullPath}, ${route.requestMethod.toUpperCase()}}`);
|
|
1365
1875
|
});
|
|
1366
1876
|
}
|
|
1367
|
-
async handleException(exception, c, controllerClass, methodName, moduleRef, contextId) {
|
|
1368
|
-
const filters = await this.resolveContextItems([
|
|
1369
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_FILTERS, moduleRef.controllers.get(controllerClass)?.instance?.[methodName]) || [],
|
|
1370
|
-
...Reflect.getMetadata(METADATA_KEYS.USE_FILTERS, controllerClass) || [],
|
|
1371
|
-
...this.globalFilters
|
|
1372
|
-
], moduleRef, contextId);
|
|
1373
|
-
for (const filter of filters) {
|
|
1374
|
-
const catchExceptions = Reflect.getMetadata(METADATA_KEYS.FILTER_CATCH, filter.constructor) || [];
|
|
1375
|
-
if (catchExceptions.length === 0 || catchExceptions.some((e) => exception instanceof e)) {
|
|
1376
|
-
const host = new ExecutionContextHost([
|
|
1377
|
-
c
|
|
1378
|
-
], controllerClass, moduleRef.controllers.get(controllerClass)?.instance?.[methodName]);
|
|
1379
|
-
return await filter.catch(exception, host);
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
this.logger.error(exception);
|
|
1383
|
-
if (exception instanceof HttpException) {
|
|
1384
|
-
const status = exception.getStatus();
|
|
1385
|
-
const response = exception.getResponse();
|
|
1386
|
-
c.status(status);
|
|
1387
|
-
return c.json(response);
|
|
1388
|
-
}
|
|
1389
|
-
if (exception instanceof Error) {
|
|
1390
|
-
c.status(500);
|
|
1391
|
-
return c.json({
|
|
1392
|
-
statusCode: 500,
|
|
1393
|
-
message: "Internal Server Error",
|
|
1394
|
-
cause: exception.message
|
|
1395
|
-
});
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
1877
|
async resolveContextItems(items, moduleRef, contextId) {
|
|
1399
1878
|
const instances = [];
|
|
1400
|
-
const scanner = new HonoDiScanner(this.container);
|
|
1401
1879
|
for (const item of items) {
|
|
1402
1880
|
if (typeof item === "function") {
|
|
1403
1881
|
let wrapper = moduleRef.getProvider(item);
|
|
@@ -1409,7 +1887,7 @@ var HonoDiApplication = class {
|
|
|
1409
1887
|
host: moduleRef,
|
|
1410
1888
|
scope: Scope.TRANSIENT
|
|
1411
1889
|
});
|
|
1412
|
-
scanner.scanDependencies(wrapper);
|
|
1890
|
+
this.scanner.scanDependencies(wrapper);
|
|
1413
1891
|
}
|
|
1414
1892
|
const instance = await this.injector.loadInstance(wrapper, contextId);
|
|
1415
1893
|
instances.push(instance);
|
|
@@ -1497,6 +1975,66 @@ var HonoDiApplication = class {
|
|
|
1497
1975
|
}
|
|
1498
1976
|
return true;
|
|
1499
1977
|
}
|
|
1978
|
+
// Performance: Optimized args resolution using pre-parsed metadata
|
|
1979
|
+
async resolveArgsFromCache(c, parsedArgs, methodPipes, context, moduleRef, contextId) {
|
|
1980
|
+
const args = [];
|
|
1981
|
+
for (const arg of parsedArgs) {
|
|
1982
|
+
let value;
|
|
1983
|
+
switch (arg.paramtype) {
|
|
1984
|
+
case RouteParamtypes.REQUEST:
|
|
1985
|
+
value = c.req;
|
|
1986
|
+
break;
|
|
1987
|
+
case RouteParamtypes.RESPONSE:
|
|
1988
|
+
value = c;
|
|
1989
|
+
break;
|
|
1990
|
+
case RouteParamtypes.CONTEXT:
|
|
1991
|
+
value = c;
|
|
1992
|
+
break;
|
|
1993
|
+
case RouteParamtypes.BODY:
|
|
1994
|
+
try {
|
|
1995
|
+
const body = await c.req.json();
|
|
1996
|
+
value = arg.data ? body[arg.data] : body;
|
|
1997
|
+
} catch (e) {
|
|
1998
|
+
value = null;
|
|
1999
|
+
}
|
|
2000
|
+
break;
|
|
2001
|
+
case RouteParamtypes.QUERY:
|
|
2002
|
+
value = arg.data ? c.req.query(arg.data) : c.req.query();
|
|
2003
|
+
break;
|
|
2004
|
+
case RouteParamtypes.PARAM:
|
|
2005
|
+
value = arg.data ? c.req.param(arg.data) : c.req.param();
|
|
2006
|
+
break;
|
|
2007
|
+
case RouteParamtypes.HEADERS:
|
|
2008
|
+
value = arg.data ? c.req.header(arg.data) : c.req.header();
|
|
2009
|
+
break;
|
|
2010
|
+
case RouteParamtypes.IP:
|
|
2011
|
+
value = c.req.header("x-forwarded-for") || "127.0.0.1";
|
|
2012
|
+
break;
|
|
2013
|
+
case RouteParamtypes.CUSTOM:
|
|
2014
|
+
const factory = arg.pipes[0];
|
|
2015
|
+
if (typeof factory === "function") {
|
|
2016
|
+
value = factory(arg.data, context);
|
|
2017
|
+
}
|
|
2018
|
+
break;
|
|
2019
|
+
default:
|
|
2020
|
+
value = null;
|
|
2021
|
+
}
|
|
2022
|
+
const paramPipes = await this.resolveContextItems(arg.pipes, moduleRef, contextId);
|
|
2023
|
+
const allPipes = [
|
|
2024
|
+
...methodPipes,
|
|
2025
|
+
...paramPipes
|
|
2026
|
+
];
|
|
2027
|
+
for (const pipe of allPipes) {
|
|
2028
|
+
value = await pipe.transform(value, {
|
|
2029
|
+
type: "custom",
|
|
2030
|
+
metatype: null,
|
|
2031
|
+
data: arg.data
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
args[arg.index] = value;
|
|
2035
|
+
}
|
|
2036
|
+
return args;
|
|
2037
|
+
}
|
|
1500
2038
|
async initializeMiddleware() {
|
|
1501
2039
|
const configs = [];
|
|
1502
2040
|
const modules = this.container.getModules();
|
|
@@ -1524,7 +2062,7 @@ var HonoDiApplication = class {
|
|
|
1524
2062
|
metatype: m,
|
|
1525
2063
|
scope: Scope.TRANSIENT
|
|
1526
2064
|
});
|
|
1527
|
-
const scanner = new
|
|
2065
|
+
const scanner = new Scanner(this.container);
|
|
1528
2066
|
scanner.scanDependencies(wrapper);
|
|
1529
2067
|
const hostModule = config.module || modules.values().next().value;
|
|
1530
2068
|
wrapper.host = hostModule;
|
|
@@ -1627,6 +2165,41 @@ var HonoDiApplication = class {
|
|
|
1627
2165
|
throw new Error(`Provider ${String(typeOrToken)} not found`);
|
|
1628
2166
|
}
|
|
1629
2167
|
async close() {
|
|
2168
|
+
await this.callLifecycleHook("beforeApplicationShutdown");
|
|
2169
|
+
for (const contextId of this.contextManager.getActiveContexts()) {
|
|
2170
|
+
this.cleanupContext(contextId);
|
|
2171
|
+
}
|
|
2172
|
+
this.contextManager.clearAll();
|
|
2173
|
+
const modules = this.container.getModules();
|
|
2174
|
+
for (const module of modules.values()) {
|
|
2175
|
+
for (const wrapper of module.providers.values()) {
|
|
2176
|
+
if (wrapper.scope !== Scope.DEFAULT) {
|
|
2177
|
+
wrapper.cleanup();
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
for (const wrapper of module.controllers.values()) {
|
|
2181
|
+
if (wrapper.scope !== Scope.DEFAULT) {
|
|
2182
|
+
wrapper.cleanup();
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
await this.callLifecycleHook("onApplicationShutdown");
|
|
2187
|
+
}
|
|
2188
|
+
cleanupContext(contextId) {
|
|
2189
|
+
this.contextManager.removeContext(contextId);
|
|
2190
|
+
const modules = this.container.getModules();
|
|
2191
|
+
for (const module of modules.values()) {
|
|
2192
|
+
for (const wrapper of module.providers.values()) {
|
|
2193
|
+
if (wrapper.scope === Scope.REQUEST) {
|
|
2194
|
+
wrapper.cleanup(contextId);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
for (const wrapper of module.controllers.values()) {
|
|
2198
|
+
if (wrapper.scope === Scope.REQUEST) {
|
|
2199
|
+
wrapper.cleanup(contextId);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
1630
2203
|
}
|
|
1631
2204
|
// Getters for global items (internal use)
|
|
1632
2205
|
getGlobalFilters() {
|
|
@@ -1651,7 +2224,31 @@ var HonoDiFactory = class {
|
|
|
1651
2224
|
static {
|
|
1652
2225
|
__name(this, "HonoDiFactory");
|
|
1653
2226
|
}
|
|
1654
|
-
static logger = new Logger("
|
|
2227
|
+
static logger = new Logger("Factory");
|
|
2228
|
+
/**
|
|
2229
|
+
* Creates and bootstraps a Hono DI application
|
|
2230
|
+
*
|
|
2231
|
+
* @param rootModule - The root module class decorated with @Module
|
|
2232
|
+
* @param appOrOptions - Optional Hono instance or configuration options
|
|
2233
|
+
* @param appOrOptions.app - Custom Hono instance to use
|
|
2234
|
+
* @param appOrOptions.autoInit - Whether to automatically initialize the app (default: true)
|
|
2235
|
+
*
|
|
2236
|
+
* @returns Promise resolving to the initialized application instance
|
|
2237
|
+
*
|
|
2238
|
+
* @throws {Error} If module scanning fails
|
|
2239
|
+
* @throws {Error} If circular dependencies are detected
|
|
2240
|
+
* @throws {Error} If required dependencies cannot be resolved
|
|
2241
|
+
*
|
|
2242
|
+
* @remarks
|
|
2243
|
+
* The creation process includes:
|
|
2244
|
+
* 1. Module scanning and dependency graph building
|
|
2245
|
+
* 2. Scope bubbling optimization (REQUEST-scoped propagation)
|
|
2246
|
+
* 3. Singleton provider instantiation
|
|
2247
|
+
* 4. Lifecycle hook execution (OnModuleInit, OnApplicationBootstrap)
|
|
2248
|
+
* 5. Route registration and middleware setup
|
|
2249
|
+
*
|
|
2250
|
+
* @public
|
|
2251
|
+
*/
|
|
1655
2252
|
static async create(rootModule, appOrOptions) {
|
|
1656
2253
|
let app;
|
|
1657
2254
|
let autoInit = true;
|
|
@@ -1665,12 +2262,13 @@ var HonoDiFactory = class {
|
|
|
1665
2262
|
}
|
|
1666
2263
|
}
|
|
1667
2264
|
const container = new Container();
|
|
1668
|
-
const scanner = new
|
|
2265
|
+
const scanner = new Scanner(container);
|
|
1669
2266
|
const injector = new Injector(container);
|
|
1670
2267
|
const honoApp = app || new hono.Hono();
|
|
1671
|
-
const bunApp = new
|
|
2268
|
+
const bunApp = new Application(honoApp, container, injector);
|
|
1672
2269
|
this.logger.log("Scanning modules...");
|
|
1673
2270
|
await scanner.scan(rootModule);
|
|
2271
|
+
this.applyScopeBubbling(container);
|
|
1674
2272
|
this.logger.log("Instantiating providers...");
|
|
1675
2273
|
await this.instantiateProviders(container, injector);
|
|
1676
2274
|
this.logger.log("Calling OnModuleInit...");
|
|
@@ -1680,6 +2278,61 @@ var HonoDiFactory = class {
|
|
|
1680
2278
|
}
|
|
1681
2279
|
return bunApp;
|
|
1682
2280
|
}
|
|
2281
|
+
static applyScopeBubbling(container) {
|
|
2282
|
+
const modules = container.getModules();
|
|
2283
|
+
const tokenMap = /* @__PURE__ */ new Map();
|
|
2284
|
+
for (const module of modules.values()) {
|
|
2285
|
+
for (const wrapper of module.providers.values()) {
|
|
2286
|
+
tokenMap.set(wrapper.token, wrapper);
|
|
2287
|
+
}
|
|
2288
|
+
for (const wrapper of module.controllers.values()) {
|
|
2289
|
+
tokenMap.set(wrapper.token, wrapper);
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
const hasRequestScopedDeps = /* @__PURE__ */ __name((wrapper, visited = /* @__PURE__ */ new Set()) => {
|
|
2293
|
+
if (visited.has(wrapper)) return false;
|
|
2294
|
+
visited.add(wrapper);
|
|
2295
|
+
if (wrapper.inject) {
|
|
2296
|
+
for (const token of wrapper.inject) {
|
|
2297
|
+
const depWrapper = tokenMap.get(token);
|
|
2298
|
+
if (depWrapper) {
|
|
2299
|
+
if (depWrapper.scope === Scope.REQUEST) {
|
|
2300
|
+
return true;
|
|
2301
|
+
}
|
|
2302
|
+
if (hasRequestScopedDeps(depWrapper, visited)) {
|
|
2303
|
+
return true;
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
if (wrapper.properties) {
|
|
2309
|
+
for (const prop of wrapper.properties) {
|
|
2310
|
+
const depWrapper = tokenMap.get(prop.token);
|
|
2311
|
+
if (depWrapper) {
|
|
2312
|
+
if (depWrapper.scope === Scope.REQUEST) {
|
|
2313
|
+
return true;
|
|
2314
|
+
}
|
|
2315
|
+
if (hasRequestScopedDeps(depWrapper, visited)) {
|
|
2316
|
+
return true;
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
return false;
|
|
2322
|
+
}, "hasRequestScopedDeps");
|
|
2323
|
+
for (const module of modules.values()) {
|
|
2324
|
+
for (const wrapper of module.providers.values()) {
|
|
2325
|
+
if (wrapper.scope === Scope.DEFAULT && hasRequestScopedDeps(wrapper)) {
|
|
2326
|
+
wrapper.scope = Scope.REQUEST;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
for (const wrapper of module.controllers.values()) {
|
|
2330
|
+
if (wrapper.scope === Scope.DEFAULT && hasRequestScopedDeps(wrapper)) {
|
|
2331
|
+
wrapper.scope = Scope.REQUEST;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
1683
2336
|
static async instantiateProviders(container, injector) {
|
|
1684
2337
|
const modules = container.getModules();
|
|
1685
2338
|
const globalContextId = new ContextId();
|
|
@@ -1940,11 +2593,31 @@ exports.BaseExceptionFilter = class BaseExceptionFilter {
|
|
|
1940
2593
|
honoCtx.status(status);
|
|
1941
2594
|
return honoCtx.json(res);
|
|
1942
2595
|
}
|
|
2596
|
+
if (exception instanceof Error) {
|
|
2597
|
+
console.error(exception);
|
|
2598
|
+
honoCtx.status(500);
|
|
2599
|
+
return honoCtx.json({
|
|
2600
|
+
statusCode: 500,
|
|
2601
|
+
message: "Internal Server Error",
|
|
2602
|
+
cause: exception.message
|
|
2603
|
+
});
|
|
2604
|
+
}
|
|
1943
2605
|
console.error(exception);
|
|
1944
2606
|
honoCtx.status(500);
|
|
2607
|
+
let errorMessage;
|
|
2608
|
+
if (typeof exception === "symbol") {
|
|
2609
|
+
errorMessage = exception.toString();
|
|
2610
|
+
} else if (exception === null || exception === void 0) {
|
|
2611
|
+
errorMessage = String(exception);
|
|
2612
|
+
} else if (typeof exception === "object") {
|
|
2613
|
+
errorMessage = JSON.stringify(exception);
|
|
2614
|
+
} else {
|
|
2615
|
+
errorMessage = String(exception);
|
|
2616
|
+
}
|
|
1945
2617
|
return honoCtx.json({
|
|
1946
2618
|
statusCode: 500,
|
|
1947
|
-
message: "Internal Server Error"
|
|
2619
|
+
message: "Internal Server Error",
|
|
2620
|
+
error: errorMessage
|
|
1948
2621
|
});
|
|
1949
2622
|
}
|
|
1950
2623
|
};
|
|
@@ -1957,6 +2630,7 @@ var HonoDi = HonoDiFactory;
|
|
|
1957
2630
|
|
|
1958
2631
|
exports.All = All;
|
|
1959
2632
|
exports.AppError = AppError;
|
|
2633
|
+
exports.Application = Application;
|
|
1960
2634
|
exports.BadGatewayException = BadGatewayException;
|
|
1961
2635
|
exports.BadRequestError = BadRequestError;
|
|
1962
2636
|
exports.BadRequestException = BadRequestException;
|
|
@@ -1981,9 +2655,7 @@ exports.Head = Head;
|
|
|
1981
2655
|
exports.Header = Header;
|
|
1982
2656
|
exports.Headers = Headers;
|
|
1983
2657
|
exports.HonoDi = HonoDi;
|
|
1984
|
-
exports.HonoDiApplication = HonoDiApplication;
|
|
1985
2658
|
exports.HonoDiFactory = HonoDiFactory;
|
|
1986
|
-
exports.HonoDiScanner = HonoDiScanner;
|
|
1987
2659
|
exports.HostParam = HostParam;
|
|
1988
2660
|
exports.HttpCode = HttpCode;
|
|
1989
2661
|
exports.HttpException = HttpException;
|
|
@@ -2016,6 +2688,7 @@ exports.RequestMethod = RequestMethod;
|
|
|
2016
2688
|
exports.RequestTimeoutException = RequestTimeoutException;
|
|
2017
2689
|
exports.Response = Response2;
|
|
2018
2690
|
exports.RouteParamtypes = RouteParamtypes;
|
|
2691
|
+
exports.Scanner = Scanner;
|
|
2019
2692
|
exports.Scope = Scope;
|
|
2020
2693
|
exports.ServiceUnavailableException = ServiceUnavailableException;
|
|
2021
2694
|
exports.Session = Session;
|