honest-node 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +277 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +36 -0
- package/dist/application/plugin-entries.d.ts +13 -0
- package/dist/application/plugin-entries.js +38 -0
- package/dist/application/startup-guide.d.ts +4 -0
- package/dist/application/startup-guide.js +53 -0
- package/dist/application-context.d.ts +13 -0
- package/dist/application-context.js +22 -0
- package/dist/application.d.ts +34 -0
- package/dist/application.js +224 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +1 -0
- package/dist/components/layout.component.d.ts +31 -0
- package/dist/components/layout.component.js +94 -0
- package/dist/constants/index.d.ts +2 -0
- package/dist/constants/index.js +2 -0
- package/dist/constants/pipeline.constants.d.ts +6 -0
- package/dist/constants/pipeline.constants.js +6 -0
- package/dist/constants/version.constants.d.ts +5 -0
- package/dist/constants/version.constants.js +5 -0
- package/dist/decorators/controller.decorator.d.ts +9 -0
- package/dist/decorators/controller.decorator.js +16 -0
- package/dist/decorators/http-method.decorator.d.ts +43 -0
- package/dist/decorators/http-method.decorator.js +44 -0
- package/dist/decorators/index.d.ts +11 -0
- package/dist/decorators/index.js +11 -0
- package/dist/decorators/module.decorator.d.ts +8 -0
- package/dist/decorators/module.decorator.js +12 -0
- package/dist/decorators/mvc.decorator.d.ts +26 -0
- package/dist/decorators/mvc.decorator.js +36 -0
- package/dist/decorators/parameter.decorator.d.ts +41 -0
- package/dist/decorators/parameter.decorator.js +59 -0
- package/dist/decorators/service.decorator.d.ts +6 -0
- package/dist/decorators/service.decorator.js +11 -0
- package/dist/decorators/use-component.decorator.d.ts +10 -0
- package/dist/decorators/use-component.decorator.js +19 -0
- package/dist/decorators/use-filters.decorator.d.ts +8 -0
- package/dist/decorators/use-filters.decorator.js +17 -0
- package/dist/decorators/use-guards.decorator.d.ts +9 -0
- package/dist/decorators/use-guards.decorator.js +18 -0
- package/dist/decorators/use-middleware.decorator.d.ts +9 -0
- package/dist/decorators/use-middleware.decorator.js +18 -0
- package/dist/decorators/use-pipes.decorator.d.ts +9 -0
- package/dist/decorators/use-pipes.decorator.js +18 -0
- package/dist/di/container.d.ts +34 -0
- package/dist/di/container.js +114 -0
- package/dist/di/index.d.ts +1 -0
- package/dist/di/index.js +1 -0
- package/dist/errors/framework.error.d.ts +19 -0
- package/dist/errors/framework.error.js +23 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/handlers/error.handler.d.ts +28 -0
- package/dist/handlers/error.handler.js +17 -0
- package/dist/handlers/index.d.ts +2 -0
- package/dist/handlers/index.js +2 -0
- package/dist/handlers/not-found.handler.d.ts +14 -0
- package/dist/handlers/not-found.handler.js +17 -0
- package/dist/helpers/create-error-response.helper.d.ts +25 -0
- package/dist/helpers/create-error-response.helper.js +90 -0
- package/dist/helpers/create-http-method-decorator.helper.d.ts +16 -0
- package/dist/helpers/create-http-method-decorator.helper.js +30 -0
- package/dist/helpers/create-param-decorator.helper.d.ts +16 -0
- package/dist/helpers/create-param-decorator.helper.js +60 -0
- package/dist/helpers/index.d.ts +3 -0
- package/dist/helpers/index.js +3 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +16 -0
- package/dist/interfaces/application-context.interface.d.ts +35 -0
- package/dist/interfaces/application-context.interface.js +1 -0
- package/dist/interfaces/controller-options.interface.d.ts +17 -0
- package/dist/interfaces/controller-options.interface.js +1 -0
- package/dist/interfaces/di-container.interface.d.ts +35 -0
- package/dist/interfaces/di-container.interface.js +1 -0
- package/dist/interfaces/error-response.interface.d.ts +13 -0
- package/dist/interfaces/error-response.interface.js +1 -0
- package/dist/interfaces/filter.interface.d.ts +20 -0
- package/dist/interfaces/filter.interface.js +1 -0
- package/dist/interfaces/guard.interface.d.ts +21 -0
- package/dist/interfaces/guard.interface.js +1 -0
- package/dist/interfaces/handler-invocation.interface.d.ts +10 -0
- package/dist/interfaces/handler-invocation.interface.js +1 -0
- package/dist/interfaces/honest-options.interface.d.ts +121 -0
- package/dist/interfaces/honest-options.interface.js +1 -0
- package/dist/interfaces/http-method-options.interface.d.ts +38 -0
- package/dist/interfaces/http-method-options.interface.js +1 -0
- package/dist/interfaces/index.d.ts +21 -0
- package/dist/interfaces/index.js +21 -0
- package/dist/interfaces/logger.interface.d.ts +23 -0
- package/dist/interfaces/logger.interface.js +1 -0
- package/dist/interfaces/metadata-repository.interface.d.ts +30 -0
- package/dist/interfaces/metadata-repository.interface.js +1 -0
- package/dist/interfaces/middleware.interface.d.ts +22 -0
- package/dist/interfaces/middleware.interface.js +1 -0
- package/dist/interfaces/module-options.interface.d.ts +18 -0
- package/dist/interfaces/module-options.interface.js +1 -0
- package/dist/interfaces/parameter-metadata.interface.d.ts +27 -0
- package/dist/interfaces/parameter-metadata.interface.js +1 -0
- package/dist/interfaces/parameter-resolution.interface.d.ts +14 -0
- package/dist/interfaces/parameter-resolution.interface.js +1 -0
- package/dist/interfaces/pipe.interface.d.ts +37 -0
- package/dist/interfaces/pipe.interface.js +1 -0
- package/dist/interfaces/pipeline-context.interface.d.ts +9 -0
- package/dist/interfaces/pipeline-context.interface.js +1 -0
- package/dist/interfaces/plugin.interface.d.ts +74 -0
- package/dist/interfaces/plugin.interface.js +1 -0
- package/dist/interfaces/route-definition.interface.d.ts +51 -0
- package/dist/interfaces/route-definition.interface.js +1 -0
- package/dist/interfaces/route-info.interface.d.ts +42 -0
- package/dist/interfaces/route-info.interface.js +1 -0
- package/dist/interfaces/service-registry.interface.d.ts +7 -0
- package/dist/interfaces/service-registry.interface.js +1 -0
- package/dist/loggers/console.logger.d.ts +7 -0
- package/dist/loggers/console.logger.js +21 -0
- package/dist/loggers/index.d.ts +2 -0
- package/dist/loggers/index.js +2 -0
- package/dist/loggers/noop.logger.d.ts +7 -0
- package/dist/loggers/noop.logger.js +8 -0
- package/dist/managers/component.manager.d.ts +48 -0
- package/dist/managers/component.manager.js +210 -0
- package/dist/managers/handler.invoker.d.ts +7 -0
- package/dist/managers/handler.invoker.js +37 -0
- package/dist/managers/index.d.ts +5 -0
- package/dist/managers/index.js +5 -0
- package/dist/managers/parameter.resolver.d.ts +13 -0
- package/dist/managers/parameter.resolver.js +58 -0
- package/dist/managers/pipeline.executor.d.ts +28 -0
- package/dist/managers/pipeline.executor.js +71 -0
- package/dist/managers/route.manager.d.ts +36 -0
- package/dist/managers/route.manager.js +149 -0
- package/dist/registries/index.d.ts +4 -0
- package/dist/registries/index.js +4 -0
- package/dist/registries/metadata.registry.d.ts +163 -0
- package/dist/registries/metadata.registry.js +250 -0
- package/dist/registries/metadata.repository.d.ts +30 -0
- package/dist/registries/metadata.repository.js +151 -0
- package/dist/registries/route.registry.d.ts +16 -0
- package/dist/registries/route.registry.js +46 -0
- package/dist/registries/service.registry.d.ts +8 -0
- package/dist/registries/service.registry.js +9 -0
- package/dist/testing/create-controller-test-application.d.ts +5 -0
- package/dist/testing/create-controller-test-application.js +11 -0
- package/dist/testing/create-service-test-container.d.ts +5 -0
- package/dist/testing/create-service-test-container.js +31 -0
- package/dist/testing/create-test-application.d.ts +5 -0
- package/dist/testing/create-test-application.js +20 -0
- package/dist/testing/create-testing-module.d.ts +6 -0
- package/dist/testing/create-testing-module.js +13 -0
- package/dist/testing/fixtures/application-test-fixtures.d.ts +17 -0
- package/dist/testing/fixtures/application-test-fixtures.js +230 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/testing.interface.d.ts +96 -0
- package/dist/testing/testing.interface.js +1 -0
- package/dist/types/constructor.type.d.ts +12 -0
- package/dist/types/constructor.type.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/common.util.d.ts +117 -0
- package/dist/utils/common.util.js +140 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ParameterResolutionInput } from '../interfaces';
|
|
2
|
+
import type { ILogger } from '../interfaces';
|
|
3
|
+
import { ComponentManager } from './component.manager';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves route handler arguments from parameter decorator metadata.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ParameterResolver {
|
|
8
|
+
private readonly componentManager;
|
|
9
|
+
private readonly logger;
|
|
10
|
+
private readonly debugPipeline;
|
|
11
|
+
constructor(componentManager: ComponentManager, logger?: ILogger, debugPipeline?: boolean);
|
|
12
|
+
resolveArguments(input: ParameterResolutionInput): Promise<unknown[]>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { NoopLogger } from '../loggers';
|
|
2
|
+
import { ComponentManager } from './component.manager';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves route handler arguments from parameter decorator metadata.
|
|
5
|
+
*/
|
|
6
|
+
export class ParameterResolver {
|
|
7
|
+
componentManager;
|
|
8
|
+
logger;
|
|
9
|
+
debugPipeline;
|
|
10
|
+
constructor(componentManager, logger = new NoopLogger(), debugPipeline = false) {
|
|
11
|
+
this.componentManager = componentManager;
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.debugPipeline = debugPipeline;
|
|
14
|
+
}
|
|
15
|
+
async resolveArguments(input) {
|
|
16
|
+
const { controllerName, handlerName, handlerArity, handlerParams, handlerPipes, context } = input;
|
|
17
|
+
const maxDecoratorIndex = handlerParams.length > 0 ? Math.max(...handlerParams.map((parameter) => parameter.index)) : -1;
|
|
18
|
+
const args = new Array(Math.max(handlerArity, maxDecoratorIndex + 1));
|
|
19
|
+
const decoratedIndices = new Set(handlerParams.map((parameter) => parameter.index));
|
|
20
|
+
if (this.debugPipeline && maxDecoratorIndex >= 0) {
|
|
21
|
+
const sparseIndices = [];
|
|
22
|
+
for (let i = 0; i <= maxDecoratorIndex; i++) {
|
|
23
|
+
if (!decoratedIndices.has(i)) {
|
|
24
|
+
sparseIndices.push(i);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const hasOutOfRangeDecoratorIndex = maxDecoratorIndex >= handlerArity;
|
|
28
|
+
if (sparseIndices.length > 0 || hasOutOfRangeDecoratorIndex) {
|
|
29
|
+
this.logger.emit({
|
|
30
|
+
level: 'warn',
|
|
31
|
+
category: 'pipeline',
|
|
32
|
+
message: `Potential parameter binding mismatch at ${controllerName}.${String(handlerName)}`,
|
|
33
|
+
details: {
|
|
34
|
+
handlerArity,
|
|
35
|
+
maxDecoratorIndex,
|
|
36
|
+
sparseIndices,
|
|
37
|
+
handlerParamCount: handlerParams.length
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const parameter of handlerParams) {
|
|
43
|
+
if (typeof parameter.factory !== 'function') {
|
|
44
|
+
throw new Error(`Invalid parameter decorator metadata for ${controllerName}.${String(handlerName)}`);
|
|
45
|
+
}
|
|
46
|
+
const rawValue = await parameter.factory(parameter.data, context);
|
|
47
|
+
const transformedValue = await this.componentManager.executePipes(rawValue, {
|
|
48
|
+
type: parameter.name,
|
|
49
|
+
metatype: parameter.metatype,
|
|
50
|
+
data: typeof parameter.data === 'string' || typeof parameter.data === 'undefined'
|
|
51
|
+
? parameter.data
|
|
52
|
+
: String(parameter.data)
|
|
53
|
+
}, handlerPipes);
|
|
54
|
+
args[parameter.index] = transformedValue;
|
|
55
|
+
}
|
|
56
|
+
return args;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { ILogger, ParameterMetadata } from '../interfaces';
|
|
3
|
+
import type { IPipe } from '../interfaces';
|
|
4
|
+
import { ComponentManager } from './component.manager';
|
|
5
|
+
import { HandlerInvoker } from './handler.invoker';
|
|
6
|
+
import { ParameterResolver } from './parameter.resolver';
|
|
7
|
+
import type { Constructor } from '../types';
|
|
8
|
+
export interface PipelineExecutionInput {
|
|
9
|
+
controllerClass: Constructor;
|
|
10
|
+
handlerName: string | symbol;
|
|
11
|
+
handler: (...args: unknown[]) => Promise<unknown> | unknown;
|
|
12
|
+
handlerParams: ReadonlyArray<ParameterMetadata>;
|
|
13
|
+
handlerPipes: ReadonlyArray<IPipe>;
|
|
14
|
+
contextIndex?: number;
|
|
15
|
+
context: Context;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Executes guard, parameter-resolution, and handler invocation stages.
|
|
19
|
+
*/
|
|
20
|
+
export declare class PipelineExecutor {
|
|
21
|
+
private readonly componentManager;
|
|
22
|
+
private readonly parameterResolver;
|
|
23
|
+
private readonly handlerInvoker;
|
|
24
|
+
private readonly logger;
|
|
25
|
+
private readonly debugPipeline;
|
|
26
|
+
constructor(componentManager: ComponentManager, parameterResolver: ParameterResolver, handlerInvoker: HandlerInvoker, logger?: ILogger, debugPipeline?: boolean);
|
|
27
|
+
execute(input: PipelineExecutionInput): Promise<unknown>;
|
|
28
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { HTTPException } from 'hono/http-exception';
|
|
2
|
+
import { HONEST_PIPELINE_CONTROLLER_KEY, HONEST_PIPELINE_HANDLER_KEY } from '../constants';
|
|
3
|
+
import { NoopLogger } from '../loggers';
|
|
4
|
+
import { ComponentManager } from './component.manager';
|
|
5
|
+
import { HandlerInvoker } from './handler.invoker';
|
|
6
|
+
import { ParameterResolver } from './parameter.resolver';
|
|
7
|
+
/**
|
|
8
|
+
* Executes guard, parameter-resolution, and handler invocation stages.
|
|
9
|
+
*/
|
|
10
|
+
export class PipelineExecutor {
|
|
11
|
+
componentManager;
|
|
12
|
+
parameterResolver;
|
|
13
|
+
handlerInvoker;
|
|
14
|
+
logger;
|
|
15
|
+
debugPipeline;
|
|
16
|
+
constructor(componentManager, parameterResolver, handlerInvoker, logger = new NoopLogger(), debugPipeline = false) {
|
|
17
|
+
this.componentManager = componentManager;
|
|
18
|
+
this.parameterResolver = parameterResolver;
|
|
19
|
+
this.handlerInvoker = handlerInvoker;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
this.debugPipeline = debugPipeline;
|
|
22
|
+
}
|
|
23
|
+
async execute(input) {
|
|
24
|
+
const { controllerClass, handlerName, handler, handlerParams, handlerPipes, contextIndex, context } = input;
|
|
25
|
+
context.set(HONEST_PIPELINE_CONTROLLER_KEY, controllerClass);
|
|
26
|
+
context.set(HONEST_PIPELINE_HANDLER_KEY, String(handlerName));
|
|
27
|
+
const guards = this.componentManager.getHandlerGuards(controllerClass, handlerName);
|
|
28
|
+
for (const guard of guards) {
|
|
29
|
+
const canActivate = await guard.canActivate(context);
|
|
30
|
+
if (!canActivate) {
|
|
31
|
+
if (this.debugPipeline) {
|
|
32
|
+
this.logger.emit({
|
|
33
|
+
level: 'warn',
|
|
34
|
+
category: 'pipeline',
|
|
35
|
+
message: `Guard rejected request at ${controllerClass.name}.${String(handlerName)}`,
|
|
36
|
+
details: { guard: guard.constructor?.name || 'UnknownGuard' }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
throw new HTTPException(403, {
|
|
40
|
+
message: `Forbidden by ${guard.constructor?.name || 'UnknownGuard'} at ${controllerClass.name}.${String(handlerName)}`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const args = await this.parameterResolver.resolveArguments({
|
|
45
|
+
controllerName: controllerClass.name,
|
|
46
|
+
handlerName,
|
|
47
|
+
handlerArity: handler.length,
|
|
48
|
+
handlerParams,
|
|
49
|
+
handlerPipes,
|
|
50
|
+
context
|
|
51
|
+
});
|
|
52
|
+
if (this.debugPipeline) {
|
|
53
|
+
this.logger.emit({
|
|
54
|
+
level: 'debug',
|
|
55
|
+
category: 'pipeline',
|
|
56
|
+
message: `Resolved handler arguments for ${controllerClass.name}.${String(handlerName)}`,
|
|
57
|
+
details: {
|
|
58
|
+
guardCount: guards.length,
|
|
59
|
+
parameterCount: handlerParams.length,
|
|
60
|
+
pipeCount: handlerPipes.length
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return this.handlerInvoker.invoke({
|
|
65
|
+
handler,
|
|
66
|
+
args,
|
|
67
|
+
context,
|
|
68
|
+
contextIndex
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import { VERSION_NEUTRAL } from '../constants';
|
|
3
|
+
import type { DiContainer, ILogger, IMetadataRepository } from '../interfaces';
|
|
4
|
+
import { ComponentManager } from './component.manager';
|
|
5
|
+
import { RouteRegistry } from '../registries/route.registry';
|
|
6
|
+
import type { Constructor } from '../types';
|
|
7
|
+
/**
|
|
8
|
+
* Manager class for handling route registration in the Honest framework.
|
|
9
|
+
*
|
|
10
|
+
* Receives all per-app dependencies (Hono, Container, RouteRegistry,
|
|
11
|
+
* ComponentManager) via constructor — no static state.
|
|
12
|
+
*/
|
|
13
|
+
export declare class RouteManager {
|
|
14
|
+
private hono;
|
|
15
|
+
private container;
|
|
16
|
+
private routeRegistry;
|
|
17
|
+
private componentManager;
|
|
18
|
+
private parameterResolver;
|
|
19
|
+
private pipelineExecutor;
|
|
20
|
+
private metadataRepository;
|
|
21
|
+
private logger;
|
|
22
|
+
private globalPrefix?;
|
|
23
|
+
private globalVersion?;
|
|
24
|
+
constructor(hono: Hono, container: DiContainer, routeRegistry: RouteRegistry, componentManager: ComponentManager, metadataRepository: IMetadataRepository, logger?: ILogger, options?: {
|
|
25
|
+
prefix?: string;
|
|
26
|
+
version?: number | typeof VERSION_NEUTRAL | number[];
|
|
27
|
+
debugPipeline?: boolean;
|
|
28
|
+
});
|
|
29
|
+
private applyGlobalMiddleware;
|
|
30
|
+
private normalizePath;
|
|
31
|
+
private registerRouteHandler;
|
|
32
|
+
private buildRoutePath;
|
|
33
|
+
private formatVersionSegment;
|
|
34
|
+
registerController(controllerClass: Constructor): Promise<void>;
|
|
35
|
+
private registerRoute;
|
|
36
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { VERSION_NEUTRAL } from '../constants';
|
|
2
|
+
import { NoopLogger } from '../loggers';
|
|
3
|
+
import { ComponentManager } from './component.manager';
|
|
4
|
+
import { HandlerInvoker } from './handler.invoker';
|
|
5
|
+
import { ParameterResolver } from './parameter.resolver';
|
|
6
|
+
import { PipelineExecutor } from './pipeline.executor';
|
|
7
|
+
import { RouteRegistry } from '../registries/route.registry';
|
|
8
|
+
import { isNil, isString, normalizePath } from '../utils';
|
|
9
|
+
/**
|
|
10
|
+
* Manager class for handling route registration in the Honest framework.
|
|
11
|
+
*
|
|
12
|
+
* Receives all per-app dependencies (Hono, Container, RouteRegistry,
|
|
13
|
+
* ComponentManager) via constructor — no static state.
|
|
14
|
+
*/
|
|
15
|
+
export class RouteManager {
|
|
16
|
+
hono;
|
|
17
|
+
container;
|
|
18
|
+
routeRegistry;
|
|
19
|
+
componentManager;
|
|
20
|
+
parameterResolver;
|
|
21
|
+
pipelineExecutor;
|
|
22
|
+
metadataRepository;
|
|
23
|
+
logger;
|
|
24
|
+
globalPrefix;
|
|
25
|
+
globalVersion;
|
|
26
|
+
constructor(hono, container, routeRegistry, componentManager, metadataRepository, logger = new NoopLogger(), options = {}) {
|
|
27
|
+
this.hono = hono;
|
|
28
|
+
this.container = container;
|
|
29
|
+
this.routeRegistry = routeRegistry;
|
|
30
|
+
this.componentManager = componentManager;
|
|
31
|
+
this.logger = logger;
|
|
32
|
+
this.parameterResolver = new ParameterResolver(this.componentManager, this.logger, Boolean(options.debugPipeline));
|
|
33
|
+
this.pipelineExecutor = new PipelineExecutor(this.componentManager, this.parameterResolver, new HandlerInvoker(), this.logger, Boolean(options.debugPipeline));
|
|
34
|
+
this.metadataRepository = metadataRepository;
|
|
35
|
+
this.globalPrefix = options.prefix !== undefined ? this.normalizePath(options.prefix) : undefined;
|
|
36
|
+
this.globalVersion = options.version;
|
|
37
|
+
this.applyGlobalMiddleware();
|
|
38
|
+
}
|
|
39
|
+
applyGlobalMiddleware() {
|
|
40
|
+
const globalMiddleware = this.componentManager.getGlobalMiddleware();
|
|
41
|
+
for (const middleware of globalMiddleware) {
|
|
42
|
+
this.hono.use('*', middleware);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
normalizePath(path) {
|
|
46
|
+
if (!isString(path)) {
|
|
47
|
+
throw new Error(`Invalid path: expected a string but received ${typeof path}. Check your @Controller() and route decorator arguments.`);
|
|
48
|
+
}
|
|
49
|
+
return normalizePath(path);
|
|
50
|
+
}
|
|
51
|
+
registerRouteHandler(method, path, handlerMiddleware, wrapperHandler) {
|
|
52
|
+
if (handlerMiddleware.length > 0) {
|
|
53
|
+
this.hono.on(method.toUpperCase(), [path], ...handlerMiddleware, wrapperHandler);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
this.hono.on(method.toUpperCase(), [path], wrapperHandler);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
buildRoutePath(prefix, version, controllerPath, methodPath) {
|
|
60
|
+
return normalizePath(`${prefix}${version}${controllerPath}${methodPath}`);
|
|
61
|
+
}
|
|
62
|
+
formatVersionSegment(version) {
|
|
63
|
+
if (isNil(version)) {
|
|
64
|
+
return '';
|
|
65
|
+
}
|
|
66
|
+
return version === VERSION_NEUTRAL ? '' : `/v${String(version)}`;
|
|
67
|
+
}
|
|
68
|
+
async registerController(controllerClass) {
|
|
69
|
+
if (!this.metadataRepository.hasController(controllerClass)) {
|
|
70
|
+
throw new Error(`Controller ${controllerClass.name} is not decorated with @Controller()`);
|
|
71
|
+
}
|
|
72
|
+
const controllerPath = this.metadataRepository.getControllerPath(controllerClass) || '';
|
|
73
|
+
const controllerOptions = this.metadataRepository.getControllerOptions(controllerClass) || {};
|
|
74
|
+
const routes = this.metadataRepository.getRoutes(controllerClass) || [];
|
|
75
|
+
const parameterMetadata = this.metadataRepository.getParameters(controllerClass) || new Map();
|
|
76
|
+
const contextIndices = this.metadataRepository.getContextIndices(controllerClass) || new Map();
|
|
77
|
+
const controllerSegment = this.normalizePath(controllerPath);
|
|
78
|
+
const controllerInstance = this.container.resolve(controllerClass);
|
|
79
|
+
const effectiveControllerPrefix = controllerOptions.prefix !== undefined ? controllerOptions.prefix : this.globalPrefix;
|
|
80
|
+
const effectiveControllerVersion = controllerOptions.version !== undefined ? controllerOptions.version : this.globalVersion;
|
|
81
|
+
if (routes.length === 0) {
|
|
82
|
+
throw new Error(`Controller ${controllerClass.name} has no route handlers. Add HTTP method decorators like @Get()`);
|
|
83
|
+
}
|
|
84
|
+
for (const route of routes) {
|
|
85
|
+
const { path, method, version: routeVersion, prefix: routePrefix } = route;
|
|
86
|
+
const effectivePrefix = routePrefix !== undefined ? routePrefix : effectiveControllerPrefix;
|
|
87
|
+
const prefixSegment = !isNil(effectivePrefix) ? this.normalizePath(effectivePrefix) : '';
|
|
88
|
+
const effectiveVersion = routeVersion !== undefined ? routeVersion : effectiveControllerVersion;
|
|
89
|
+
const methodSegment = this.normalizePath(path);
|
|
90
|
+
if (isNil(effectiveVersion)) {
|
|
91
|
+
this.registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, '', controllerSegment, methodSegment, method);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (effectiveVersion === VERSION_NEUTRAL) {
|
|
95
|
+
this.registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, '', controllerSegment, methodSegment, method);
|
|
96
|
+
this.registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, '/:version{v[0-9]+}', controllerSegment, methodSegment, method);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(effectiveVersion)) {
|
|
100
|
+
for (const version of effectiveVersion) {
|
|
101
|
+
const versionSegment = this.formatVersionSegment(version);
|
|
102
|
+
this.registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, versionSegment, controllerSegment, methodSegment, method);
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const versionSegment = this.formatVersionSegment(effectiveVersion);
|
|
107
|
+
this.registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, versionSegment, controllerSegment, methodSegment, method);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
registerRoute(controllerInstance, route, parameterMetadata, contextIndices, controllerClass, prefixSegment, versionSegment, controllerSegment, methodSegment, method) {
|
|
111
|
+
const { handlerName } = route;
|
|
112
|
+
const fullPath = this.buildRoutePath(prefixSegment, versionSegment, controllerSegment, methodSegment);
|
|
113
|
+
const handler = controllerInstance[handlerName].bind(controllerInstance);
|
|
114
|
+
const handlerParams = parameterMetadata.get(handlerName) || [];
|
|
115
|
+
const contextIndex = contextIndices.get(handlerName);
|
|
116
|
+
const handlerMiddleware = this.componentManager.getHandlerMiddleware(controllerClass, handlerName);
|
|
117
|
+
const handlerPipes = this.componentManager.getHandlerPipes(controllerClass, handlerName);
|
|
118
|
+
this.routeRegistry.registerRoute({
|
|
119
|
+
controller: controllerClass.name,
|
|
120
|
+
handler: handlerName,
|
|
121
|
+
method,
|
|
122
|
+
prefix: prefixSegment,
|
|
123
|
+
version: versionSegment,
|
|
124
|
+
route: controllerSegment,
|
|
125
|
+
path: methodSegment,
|
|
126
|
+
fullPath,
|
|
127
|
+
parameters: handlerParams
|
|
128
|
+
});
|
|
129
|
+
const componentManager = this.componentManager;
|
|
130
|
+
const pipelineExecutor = this.pipelineExecutor;
|
|
131
|
+
const wrapperHandler = async (c) => {
|
|
132
|
+
try {
|
|
133
|
+
return await pipelineExecutor.execute({
|
|
134
|
+
controllerClass,
|
|
135
|
+
handlerName,
|
|
136
|
+
handler,
|
|
137
|
+
handlerParams,
|
|
138
|
+
handlerPipes,
|
|
139
|
+
contextIndex,
|
|
140
|
+
context: c
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
return componentManager.handleException(error, c);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
this.registerRouteHandler(method, fullPath, handlerMiddleware, wrapperHandler);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { ControllerOptions, FilterType, GuardType, MiddlewareType, ModuleOptions, ParameterMetadata, PipeType, RouteDefinition } from '../interfaces';
|
|
2
|
+
import type { Constructor } from '../types';
|
|
3
|
+
export type ComponentType = 'middleware' | 'guard' | 'pipe' | 'filter';
|
|
4
|
+
export type ComponentInstance = MiddlewareType | GuardType | PipeType | FilterType;
|
|
5
|
+
export interface ComponentTypeMap {
|
|
6
|
+
middleware: MiddlewareType;
|
|
7
|
+
guard: GuardType;
|
|
8
|
+
pipe: PipeType;
|
|
9
|
+
filter: FilterType;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Central registry for managing application metadata
|
|
13
|
+
* Stores and provides access to:
|
|
14
|
+
* - Route definitions and controller configurations
|
|
15
|
+
* - Service and module registrations
|
|
16
|
+
* - Parameter metadata and context indices
|
|
17
|
+
* - Component registrations at global, controller, and handler levels
|
|
18
|
+
*/
|
|
19
|
+
export declare class MetadataRegistry {
|
|
20
|
+
/**
|
|
21
|
+
* Stores route definitions for each controller
|
|
22
|
+
* Maps controller classes to their route configurations
|
|
23
|
+
*/
|
|
24
|
+
private static readonly routes;
|
|
25
|
+
/**
|
|
26
|
+
* Stores base paths for controllers
|
|
27
|
+
* Maps controller classes to their route prefixes
|
|
28
|
+
*/
|
|
29
|
+
private static readonly controllers;
|
|
30
|
+
/**
|
|
31
|
+
* Stores configuration options for controllers
|
|
32
|
+
* Includes settings like versioning and prefix options
|
|
33
|
+
*/
|
|
34
|
+
private static readonly controllerOptions;
|
|
35
|
+
/**
|
|
36
|
+
* Registry of service classes
|
|
37
|
+
* Used for dependency injection and lifecycle management
|
|
38
|
+
*/
|
|
39
|
+
private static readonly services;
|
|
40
|
+
/**
|
|
41
|
+
* Stores configuration options for modules
|
|
42
|
+
* Includes imports, exports, providers, and controllers
|
|
43
|
+
*/
|
|
44
|
+
private static readonly modules;
|
|
45
|
+
/**
|
|
46
|
+
* Stores parameter metadata for controller methods
|
|
47
|
+
* Used for parameter transformation and validation
|
|
48
|
+
*/
|
|
49
|
+
private static readonly parameters;
|
|
50
|
+
/**
|
|
51
|
+
* Stores indices of context parameters in controller methods
|
|
52
|
+
* Used for optimizing context injection
|
|
53
|
+
*/
|
|
54
|
+
private static readonly contextIndices;
|
|
55
|
+
/**
|
|
56
|
+
* Registry for controller-level components
|
|
57
|
+
* Components registered here apply to all routes in a specific controller
|
|
58
|
+
*/
|
|
59
|
+
private static readonly controller;
|
|
60
|
+
/**
|
|
61
|
+
* Registry for handler-level components
|
|
62
|
+
* Components registered here apply to specific route handlers
|
|
63
|
+
* Keyed by controller constructor then handler name for collision-safe lookups
|
|
64
|
+
*/
|
|
65
|
+
private static readonly handler;
|
|
66
|
+
/**
|
|
67
|
+
* Gets all route definitions for a controller
|
|
68
|
+
* @param controller - The controller class to get routes for
|
|
69
|
+
* @returns Array of route definitions for the controller
|
|
70
|
+
*/
|
|
71
|
+
static getRoutes(controller: Constructor): RouteDefinition[];
|
|
72
|
+
/**
|
|
73
|
+
* Set routes for a controller
|
|
74
|
+
*/
|
|
75
|
+
static setRoutes(controller: Constructor, routes: RouteDefinition[]): void;
|
|
76
|
+
/**
|
|
77
|
+
* Add a route to a controller
|
|
78
|
+
*/
|
|
79
|
+
static addRoute(controller: Constructor, route: RouteDefinition): void;
|
|
80
|
+
/**
|
|
81
|
+
* Get controller path
|
|
82
|
+
*/
|
|
83
|
+
static getControllerPath(controller: Constructor): string;
|
|
84
|
+
/**
|
|
85
|
+
* Check if a class is registered as a controller.
|
|
86
|
+
*/
|
|
87
|
+
static hasController(controller: Constructor): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Set controller path
|
|
90
|
+
*/
|
|
91
|
+
static setControllerPath(controller: Constructor, path: string): void;
|
|
92
|
+
/**
|
|
93
|
+
* Get controller options
|
|
94
|
+
*/
|
|
95
|
+
static getControllerOptions(controller: Constructor): ControllerOptions;
|
|
96
|
+
/**
|
|
97
|
+
* Set controller options
|
|
98
|
+
*/
|
|
99
|
+
static setControllerOptions(controller: Constructor, options: ControllerOptions): void;
|
|
100
|
+
/**
|
|
101
|
+
* Check if class is a service
|
|
102
|
+
*/
|
|
103
|
+
static isService(service: Constructor): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Add a service
|
|
106
|
+
*/
|
|
107
|
+
static addService(service: Constructor): void;
|
|
108
|
+
/**
|
|
109
|
+
* Get all services
|
|
110
|
+
*/
|
|
111
|
+
static getAllServices(): Set<Constructor>;
|
|
112
|
+
/**
|
|
113
|
+
* Get module options
|
|
114
|
+
*/
|
|
115
|
+
static getModuleOptions(module: Constructor): ModuleOptions | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* Set module options
|
|
118
|
+
*/
|
|
119
|
+
static setModuleOptions(module: Constructor, options: ModuleOptions): void;
|
|
120
|
+
/**
|
|
121
|
+
* Get parameter metadata
|
|
122
|
+
*/
|
|
123
|
+
static getParameters(controller: Constructor): Map<string | symbol, ParameterMetadata[]>;
|
|
124
|
+
/**
|
|
125
|
+
* Set parameter metadata
|
|
126
|
+
*/
|
|
127
|
+
static setParameterMap(controller: Constructor, params: Map<string | symbol, ParameterMetadata[]>): void;
|
|
128
|
+
/**
|
|
129
|
+
* Get context indices
|
|
130
|
+
*/
|
|
131
|
+
static getContextIndices(controller: Constructor): Map<string | symbol, number>;
|
|
132
|
+
/**
|
|
133
|
+
* Set context indices
|
|
134
|
+
*/
|
|
135
|
+
static setContextIndices(controller: Constructor, indices: Map<string | symbol, number>): void;
|
|
136
|
+
/**
|
|
137
|
+
* Register a component at the controller level
|
|
138
|
+
*/
|
|
139
|
+
static registerController<T extends ComponentType>(type: T, controller: Constructor, component: ComponentTypeMap[T]): void;
|
|
140
|
+
/**
|
|
141
|
+
* Get all controller-level components of a specific type for a controller
|
|
142
|
+
*/
|
|
143
|
+
static getController<T extends ComponentType>(type: T, controller: Constructor): ComponentTypeMap[T][];
|
|
144
|
+
/**
|
|
145
|
+
* Register a component at the handler level
|
|
146
|
+
*/
|
|
147
|
+
static registerHandler<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol, component: ComponentTypeMap[T]): void;
|
|
148
|
+
/**
|
|
149
|
+
* Get all handler-level components of a specific type for a handler
|
|
150
|
+
*/
|
|
151
|
+
static getHandler<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol): ComponentTypeMap[T][];
|
|
152
|
+
/**
|
|
153
|
+
* Clears handler-level component registrations created via {@link registerHandler}.
|
|
154
|
+
* Does not remove decorator-defined routes, controllers, or modules.
|
|
155
|
+
*/
|
|
156
|
+
static clearHandlerComponents(): void;
|
|
157
|
+
/**
|
|
158
|
+
* Clears all registered decorator metadata.
|
|
159
|
+
* Primarily used for testing. Warning: clearing after importing decorated classes
|
|
160
|
+
* (e.g. shared fixtures) removes their metadata until those modules are re-evaluated.
|
|
161
|
+
*/
|
|
162
|
+
static clear(): void;
|
|
163
|
+
}
|