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,27 @@
|
|
|
1
|
+
import type { Constructor } from '../types';
|
|
2
|
+
import type { Context } from 'hono';
|
|
3
|
+
/**
|
|
4
|
+
* Metadata for route parameters
|
|
5
|
+
*/
|
|
6
|
+
export interface ParameterMetadata {
|
|
7
|
+
/**
|
|
8
|
+
* Parameter index
|
|
9
|
+
*/
|
|
10
|
+
index: number;
|
|
11
|
+
/**
|
|
12
|
+
* Parameter name (body, param, query, etc.)
|
|
13
|
+
*/
|
|
14
|
+
name: string;
|
|
15
|
+
/**
|
|
16
|
+
* Additional parameter data (e.g., param name)
|
|
17
|
+
*/
|
|
18
|
+
data?: unknown;
|
|
19
|
+
/**
|
|
20
|
+
* Optional factory function to transform the data
|
|
21
|
+
*/
|
|
22
|
+
factory: (data: unknown, ctx: Context) => unknown | Promise<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* The class type of the parameter
|
|
25
|
+
*/
|
|
26
|
+
metatype?: Constructor<unknown>;
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Context } from 'hono';
|
|
2
|
+
import type { IPipe } from './pipe.interface';
|
|
3
|
+
import type { ParameterMetadata } from './parameter-metadata.interface';
|
|
4
|
+
/**
|
|
5
|
+
* Input contract for route-parameter resolution.
|
|
6
|
+
*/
|
|
7
|
+
export interface ParameterResolutionInput {
|
|
8
|
+
controllerName: string;
|
|
9
|
+
handlerName: string | symbol;
|
|
10
|
+
handlerArity: number;
|
|
11
|
+
handlerParams: ReadonlyArray<ParameterMetadata>;
|
|
12
|
+
handlerPipes: ReadonlyArray<IPipe>;
|
|
13
|
+
context: Context;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Constructor } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Metadata about an argument being processed by a pipe
|
|
4
|
+
*/
|
|
5
|
+
export interface ArgumentMetadata {
|
|
6
|
+
/**
|
|
7
|
+
* The type of argument (body, query, param, header, request, response, context, variable, or custom)
|
|
8
|
+
*/
|
|
9
|
+
type: 'body' | 'query' | 'param' | 'header' | 'request' | 'response' | 'context' | 'variable' | string;
|
|
10
|
+
/**
|
|
11
|
+
* The class type of the argument
|
|
12
|
+
*/
|
|
13
|
+
metatype?: Constructor<unknown>;
|
|
14
|
+
/**
|
|
15
|
+
* Additional data about the argument
|
|
16
|
+
*/
|
|
17
|
+
data?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Interface for transformation pipes
|
|
21
|
+
* Pipes transform input data before it reaches the route handler
|
|
22
|
+
*/
|
|
23
|
+
export interface IPipe {
|
|
24
|
+
/**
|
|
25
|
+
* Transforms the input value according to the pipe's logic
|
|
26
|
+
* @param value - The value to transform
|
|
27
|
+
* @param metadata - Metadata about the argument being transformed
|
|
28
|
+
* @returns The transformed value, which can be synchronous or asynchronous
|
|
29
|
+
* @throws {Error} If the transformation fails or validation fails
|
|
30
|
+
*/
|
|
31
|
+
transform(value: unknown, metadata: ArgumentMetadata): Promise<unknown> | unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Type for pipe implementations
|
|
35
|
+
* Can be either a class implementing IPipe or an instance of IPipe
|
|
36
|
+
*/
|
|
37
|
+
export type PipeType = Constructor<IPipe> | IPipe;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { Application } from '../application';
|
|
3
|
+
import type { IApplicationContext } from './application-context.interface';
|
|
4
|
+
import type { ILogger } from './logger.interface';
|
|
5
|
+
import type { Constructor } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* Processor callback for plugin pre/post hooks.
|
|
8
|
+
* Receives app, hono, and the application context (registry) for sharing pipeline data.
|
|
9
|
+
*/
|
|
10
|
+
export type PluginProcessor = (app: Application, hono: Hono, ctx: IApplicationContext) => void | Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Optional metadata for plugin diagnostics.
|
|
13
|
+
*/
|
|
14
|
+
export interface PluginMeta {
|
|
15
|
+
/**
|
|
16
|
+
* Stable plugin name used for diagnostics.
|
|
17
|
+
*/
|
|
18
|
+
name?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Object form of a plugin entry with optional pre/post processors.
|
|
22
|
+
* Processors run before (pre) or after (post) the plugin's lifecycle hooks.
|
|
23
|
+
*/
|
|
24
|
+
export interface PluginEntryObject {
|
|
25
|
+
plugin: IPlugin | Constructor<IPlugin>;
|
|
26
|
+
/**
|
|
27
|
+
* Optional stable plugin name for diagnostics.
|
|
28
|
+
* Takes precedence over plugin.meta.name.
|
|
29
|
+
*/
|
|
30
|
+
name?: string;
|
|
31
|
+
preProcessors?: PluginProcessor[];
|
|
32
|
+
postProcessors?: PluginProcessor[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Interface for Honest framework plugins
|
|
36
|
+
* Plugins can extend the framework's functionality by hooking into
|
|
37
|
+
* different stages of the application lifecycle
|
|
38
|
+
*/
|
|
39
|
+
export interface IPlugin {
|
|
40
|
+
/**
|
|
41
|
+
* Optional metadata for plugin diagnostics.
|
|
42
|
+
*/
|
|
43
|
+
meta?: PluginMeta;
|
|
44
|
+
/**
|
|
45
|
+
* Application logger, injected by the framework before lifecycle hooks run.
|
|
46
|
+
* Use this to emit structured log events from within plugin code.
|
|
47
|
+
*/
|
|
48
|
+
logger?: ILogger;
|
|
49
|
+
/**
|
|
50
|
+
* Hook that runs before module registration begins.
|
|
51
|
+
* Use this to set up plugin functionality that modules might depend on.
|
|
52
|
+
* @param app - The Honest application instance
|
|
53
|
+
* @param hono - The underlying Hono application instance
|
|
54
|
+
*/
|
|
55
|
+
beforeModulesRegistered?: (app: Application, hono: Hono) => void | Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Hook that runs after all modules have been registered.
|
|
58
|
+
* Use this to perform cleanup or setup that requires all modules to be ready.
|
|
59
|
+
* @param app - The Honest application instance
|
|
60
|
+
* @param hono - The underlying Hono application instance
|
|
61
|
+
*/
|
|
62
|
+
afterModulesRegistered?: (app: Application, hono: Hono) => void | Promise<void>;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Type for plugin implementations
|
|
66
|
+
* Can be either a class implementing IPlugin or an instance of IPlugin
|
|
67
|
+
*/
|
|
68
|
+
export type PluginType = Constructor<IPlugin> | IPlugin;
|
|
69
|
+
/**
|
|
70
|
+
* Plugin entry: either a plain plugin or an object wrapping a plugin with optional processors.
|
|
71
|
+
* Use the object form to attach preProcessors (run before lifecycle hooks) and postProcessors
|
|
72
|
+
* (run after). Processors receive (app, hono, ctx) where ctx is the application context.
|
|
73
|
+
*/
|
|
74
|
+
export type PluginEntry = PluginType | PluginEntryObject;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { VERSION_NEUTRAL } from '../constants';
|
|
2
|
+
/**
|
|
3
|
+
* Internal metadata for defining a route. This interface is used by the framework
|
|
4
|
+
* to store route information collected from decorators.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* // Internal representation of:
|
|
9
|
+
* @Controller('users')
|
|
10
|
+
* class UsersController {
|
|
11
|
+
* @Get(':id')
|
|
12
|
+
* getUser(@Param('id') id: string) {}
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export interface RouteDefinition {
|
|
17
|
+
/**
|
|
18
|
+
* Route path relative to the controller's base path.
|
|
19
|
+
* Supports path parameters using colon syntax.
|
|
20
|
+
*
|
|
21
|
+
* @example ':id' | 'users/:userId/posts/:postId' | ''
|
|
22
|
+
*/
|
|
23
|
+
path: string;
|
|
24
|
+
/**
|
|
25
|
+
* HTTP method for the route (GET, POST, PUT, DELETE, etc.)
|
|
26
|
+
*/
|
|
27
|
+
method: string;
|
|
28
|
+
/**
|
|
29
|
+
* Name of the method in the controller class that handles this route
|
|
30
|
+
*/
|
|
31
|
+
handlerName: string | symbol;
|
|
32
|
+
/**
|
|
33
|
+
* Route-specific API version, overrides controller and global version.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* version: 1 // -> /v1/...
|
|
38
|
+
* version: null // -> /... (no version)
|
|
39
|
+
* version: VERSION_NEUTRAL // -> Both /... and /v1/...
|
|
40
|
+
* version: [1, 2] // -> Both /v1/... and /v2/...
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
version?: number | null | typeof VERSION_NEUTRAL | number[];
|
|
44
|
+
/**
|
|
45
|
+
* Route-specific prefix that overrides controller and global prefix.
|
|
46
|
+
* Set to null to explicitly remove any prefix.
|
|
47
|
+
*
|
|
48
|
+
* @example 'api' | 'v2/api' | null
|
|
49
|
+
*/
|
|
50
|
+
prefix?: string | null;
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ParameterMetadata } from './parameter-metadata.interface';
|
|
2
|
+
/**
|
|
3
|
+
* Route information for registered routes
|
|
4
|
+
*/
|
|
5
|
+
export interface RouteInfo {
|
|
6
|
+
/**
|
|
7
|
+
* Controller name
|
|
8
|
+
*/
|
|
9
|
+
controller: string | symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Handler method name
|
|
12
|
+
*/
|
|
13
|
+
handler: string | symbol;
|
|
14
|
+
/**
|
|
15
|
+
* HTTP method
|
|
16
|
+
*/
|
|
17
|
+
method: string;
|
|
18
|
+
/**
|
|
19
|
+
* Effective prefix
|
|
20
|
+
*/
|
|
21
|
+
prefix: string;
|
|
22
|
+
/**
|
|
23
|
+
* Effective version
|
|
24
|
+
*/
|
|
25
|
+
version?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Controller route path
|
|
28
|
+
*/
|
|
29
|
+
route: string;
|
|
30
|
+
/**
|
|
31
|
+
* Method path
|
|
32
|
+
*/
|
|
33
|
+
path: string;
|
|
34
|
+
/**
|
|
35
|
+
* Complete path (prefix + version + route + path)
|
|
36
|
+
*/
|
|
37
|
+
fullPath: string;
|
|
38
|
+
/**
|
|
39
|
+
* Parameter metadata for the handler
|
|
40
|
+
*/
|
|
41
|
+
parameters: ParameterMetadata[];
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default logger that writes structured events to console.
|
|
3
|
+
*/
|
|
4
|
+
export class ConsoleLogger {
|
|
5
|
+
emit(event) {
|
|
6
|
+
const prefix = `[HonestJS:${event.category}]`;
|
|
7
|
+
const payload = event.details ? [prefix, event.message, event.details] : [prefix, event.message];
|
|
8
|
+
switch (event.level) {
|
|
9
|
+
case 'debug':
|
|
10
|
+
case 'info':
|
|
11
|
+
console.info(...payload);
|
|
12
|
+
break;
|
|
13
|
+
case 'warn':
|
|
14
|
+
console.warn(...payload);
|
|
15
|
+
break;
|
|
16
|
+
case 'error':
|
|
17
|
+
console.error(...payload);
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Context, Next } from 'hono';
|
|
2
|
+
import type { ArgumentMetadata, DiContainer, FilterType, GuardType, ILogger, IMetadataRepository, IGuard, IPipe, MiddlewareType, PipeType } from '../interfaces';
|
|
3
|
+
import { type ComponentType, type ComponentTypeMap } from '../registries';
|
|
4
|
+
import type { Constructor } from '../types';
|
|
5
|
+
/**
|
|
6
|
+
* Manager class for handling all component types in the Honest framework.
|
|
7
|
+
*
|
|
8
|
+
* Each Application instance owns a ComponentManager, which holds per-app
|
|
9
|
+
* global components and a reference to the DI container. Controller-level
|
|
10
|
+
* and handler-level components remain in MetadataRegistry (static, set at
|
|
11
|
+
* class-definition time by decorators).
|
|
12
|
+
*/
|
|
13
|
+
export declare class ComponentManager {
|
|
14
|
+
private readonly container;
|
|
15
|
+
private readonly metadataRepository;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
private readonly globalComponents;
|
|
18
|
+
constructor(container: DiContainer, metadataRepository: IMetadataRepository, logger?: ILogger);
|
|
19
|
+
/**
|
|
20
|
+
* Configures global components from application options.
|
|
21
|
+
*/
|
|
22
|
+
setupGlobalComponents(options: {
|
|
23
|
+
components?: {
|
|
24
|
+
middleware?: MiddlewareType[];
|
|
25
|
+
guards?: GuardType[];
|
|
26
|
+
pipes?: PipeType[];
|
|
27
|
+
filters?: FilterType[];
|
|
28
|
+
};
|
|
29
|
+
}): void;
|
|
30
|
+
registerGlobal<T extends ComponentType>(type: T, ...components: ComponentTypeMap[T][]): void;
|
|
31
|
+
getGlobal<T extends ComponentType>(type: T): Set<ComponentTypeMap[T]>;
|
|
32
|
+
/**
|
|
33
|
+
* Gets all components of a specific type for a handler.
|
|
34
|
+
* Merges: instance global → static controller → static handler.
|
|
35
|
+
*/
|
|
36
|
+
getComponents<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol): ComponentTypeMap[T][];
|
|
37
|
+
resolveMiddleware(middlewareItems: MiddlewareType[]): ((c: Context, next: Next) => Promise<Response | void>)[];
|
|
38
|
+
getHandlerMiddleware(controller: Constructor, handlerName: string | symbol): ((c: Context, next: Next) => Promise<Response | void>)[];
|
|
39
|
+
getGlobalMiddleware(): ((c: Context, next: Next) => Promise<Response | void>)[];
|
|
40
|
+
resolveGuards(guardItems: GuardType[]): IGuard[];
|
|
41
|
+
getHandlerGuards(controller: Constructor, handlerName: string | symbol): IGuard[];
|
|
42
|
+
resolvePipes(pipeItems: PipeType[]): IPipe[];
|
|
43
|
+
getHandlerPipes(controller: Constructor, handlerName: string | symbol): IPipe[];
|
|
44
|
+
executePipes(value: unknown, metadata: ArgumentMetadata, pipes: ReadonlyArray<IPipe>): Promise<unknown>;
|
|
45
|
+
handleException(exception: unknown, context: Context): Promise<Response | undefined>;
|
|
46
|
+
private executeFilters;
|
|
47
|
+
registerModule(moduleClass: Constructor, registered?: Set<Constructor>): Promise<Constructor[]>;
|
|
48
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { HONEST_PIPELINE_CONTROLLER_KEY, HONEST_PIPELINE_HANDLER_KEY } from '../constants';
|
|
2
|
+
import { createErrorResponse } from '../helpers';
|
|
3
|
+
import { NoopLogger } from '../loggers';
|
|
4
|
+
import {} from '../registries';
|
|
5
|
+
import { isObject } from '../utils';
|
|
6
|
+
/**
|
|
7
|
+
* Manager class for handling all component types in the Honest framework.
|
|
8
|
+
*
|
|
9
|
+
* Each Application instance owns a ComponentManager, which holds per-app
|
|
10
|
+
* global components and a reference to the DI container. Controller-level
|
|
11
|
+
* and handler-level components remain in MetadataRegistry (static, set at
|
|
12
|
+
* class-definition time by decorators).
|
|
13
|
+
*/
|
|
14
|
+
export class ComponentManager {
|
|
15
|
+
container;
|
|
16
|
+
metadataRepository;
|
|
17
|
+
logger;
|
|
18
|
+
globalComponents = new Map([
|
|
19
|
+
['middleware', new Set()],
|
|
20
|
+
['guard', new Set()],
|
|
21
|
+
['pipe', new Set()],
|
|
22
|
+
['filter', new Set()]
|
|
23
|
+
]);
|
|
24
|
+
constructor(container, metadataRepository, logger = new NoopLogger()) {
|
|
25
|
+
this.container = container;
|
|
26
|
+
this.metadataRepository = metadataRepository;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Configures global components from application options.
|
|
31
|
+
*/
|
|
32
|
+
setupGlobalComponents(options) {
|
|
33
|
+
const components = options.components || {};
|
|
34
|
+
if (components.middleware) {
|
|
35
|
+
this.registerGlobal('middleware', ...components.middleware);
|
|
36
|
+
}
|
|
37
|
+
if (components.guards) {
|
|
38
|
+
this.registerGlobal('guard', ...components.guards);
|
|
39
|
+
}
|
|
40
|
+
if (components.pipes) {
|
|
41
|
+
this.registerGlobal('pipe', ...components.pipes);
|
|
42
|
+
}
|
|
43
|
+
if (components.filters) {
|
|
44
|
+
this.registerGlobal('filter', ...components.filters);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
registerGlobal(type, ...components) {
|
|
48
|
+
components.forEach((component) => {
|
|
49
|
+
this.globalComponents.get(type).add(component);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
getGlobal(type) {
|
|
53
|
+
return this.globalComponents.get(type);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Gets all components of a specific type for a handler.
|
|
57
|
+
* Merges: instance global → static controller → static handler.
|
|
58
|
+
*/
|
|
59
|
+
getComponents(type, controller, handlerName) {
|
|
60
|
+
const handlerComponents = this.metadataRepository.getHandlerComponents(type, controller, handlerName);
|
|
61
|
+
const controllerComponents = this.metadataRepository.getControllerComponents(type, controller);
|
|
62
|
+
const globalComponents = Array.from(this.globalComponents.get(type) || []);
|
|
63
|
+
return [...globalComponents, ...controllerComponents, ...handlerComponents];
|
|
64
|
+
}
|
|
65
|
+
// -- Middleware --
|
|
66
|
+
resolveMiddleware(middlewareItems) {
|
|
67
|
+
return middlewareItems.map((middlewareItem) => {
|
|
68
|
+
if (isObject(middlewareItem) && 'use' in middlewareItem) {
|
|
69
|
+
return middlewareItem.use.bind(middlewareItem);
|
|
70
|
+
}
|
|
71
|
+
const middleware = this.container.resolve(middlewareItem);
|
|
72
|
+
return middleware.use.bind(middleware);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
getHandlerMiddleware(controller, handlerName) {
|
|
76
|
+
const controllerMiddleware = this.metadataRepository.getControllerComponents('middleware', controller);
|
|
77
|
+
const handlerMiddleware = this.metadataRepository.getHandlerComponents('middleware', controller, handlerName);
|
|
78
|
+
return this.resolveMiddleware([...controllerMiddleware, ...handlerMiddleware]);
|
|
79
|
+
}
|
|
80
|
+
getGlobalMiddleware() {
|
|
81
|
+
const globalMiddleware = Array.from(this.globalComponents.get('middleware') || []);
|
|
82
|
+
return this.resolveMiddleware(globalMiddleware);
|
|
83
|
+
}
|
|
84
|
+
// -- Guards --
|
|
85
|
+
resolveGuards(guardItems) {
|
|
86
|
+
return guardItems.map((guardItem) => {
|
|
87
|
+
if (isObject(guardItem) && 'canActivate' in guardItem) {
|
|
88
|
+
return guardItem;
|
|
89
|
+
}
|
|
90
|
+
return this.container.resolve(guardItem);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
getHandlerGuards(controller, handlerName) {
|
|
94
|
+
const guardItems = this.getComponents('guard', controller, handlerName);
|
|
95
|
+
return this.resolveGuards(guardItems);
|
|
96
|
+
}
|
|
97
|
+
// -- Pipes --
|
|
98
|
+
resolvePipes(pipeItems) {
|
|
99
|
+
return pipeItems.map((pipeItem) => {
|
|
100
|
+
if (isObject(pipeItem) && 'transform' in pipeItem) {
|
|
101
|
+
return pipeItem;
|
|
102
|
+
}
|
|
103
|
+
return this.container.resolve(pipeItem);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
getHandlerPipes(controller, handlerName) {
|
|
107
|
+
const pipeItems = this.getComponents('pipe', controller, handlerName);
|
|
108
|
+
return this.resolvePipes(pipeItems);
|
|
109
|
+
}
|
|
110
|
+
async executePipes(value, metadata, pipes) {
|
|
111
|
+
let transformedValue = value;
|
|
112
|
+
for (const pipe of pipes) {
|
|
113
|
+
transformedValue = await pipe.transform(transformedValue, metadata);
|
|
114
|
+
}
|
|
115
|
+
return transformedValue;
|
|
116
|
+
}
|
|
117
|
+
// -- Filters --
|
|
118
|
+
async handleException(exception, context) {
|
|
119
|
+
const normalizedException = exception instanceof Error ? exception : new Error(String(exception));
|
|
120
|
+
const controller = context.get(HONEST_PIPELINE_CONTROLLER_KEY);
|
|
121
|
+
const handlerName = context.get(HONEST_PIPELINE_HANDLER_KEY);
|
|
122
|
+
if (controller && handlerName) {
|
|
123
|
+
const handlerFilters = this.metadataRepository.getHandlerComponents('filter', controller, handlerName);
|
|
124
|
+
if (handlerFilters.length > 0) {
|
|
125
|
+
const response = await this.executeFilters(handlerFilters, normalizedException, context);
|
|
126
|
+
if (response)
|
|
127
|
+
return response;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (controller) {
|
|
131
|
+
const controllerFilters = this.metadataRepository.getControllerComponents('filter', controller);
|
|
132
|
+
if (controllerFilters.length > 0) {
|
|
133
|
+
const response = await this.executeFilters(controllerFilters, normalizedException, context);
|
|
134
|
+
if (response)
|
|
135
|
+
return response;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const globalFilters = Array.from(this.globalComponents.get('filter') || []);
|
|
139
|
+
if (globalFilters.length > 0) {
|
|
140
|
+
const response = await this.executeFilters(globalFilters, normalizedException, context);
|
|
141
|
+
if (response)
|
|
142
|
+
return response;
|
|
143
|
+
}
|
|
144
|
+
const { response, status } = createErrorResponse(normalizedException, context);
|
|
145
|
+
return context.json(response, status);
|
|
146
|
+
}
|
|
147
|
+
async executeFilters(filterItems, exception, context) {
|
|
148
|
+
for (const filterItem of filterItems) {
|
|
149
|
+
let filter;
|
|
150
|
+
if (isObject(filterItem) && 'catch' in filterItem) {
|
|
151
|
+
filter = filterItem;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
filter = this.container.resolve(filterItem);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
const result = await filter.catch(exception, context);
|
|
158
|
+
if (result !== undefined) {
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch (filterError) {
|
|
163
|
+
const filterName = filter.constructor?.name || 'UnknownFilter';
|
|
164
|
+
this.logger.emit({
|
|
165
|
+
level: 'error',
|
|
166
|
+
category: 'errors',
|
|
167
|
+
message: `Error in exception filter ${filterName}`,
|
|
168
|
+
details: {
|
|
169
|
+
error: filterError instanceof Error ? filterError.message : String(filterError)
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
const { response, status } = createErrorResponse(filterError, context);
|
|
173
|
+
return context.json(response, status);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
// -- Module registration --
|
|
179
|
+
async registerModule(moduleClass, registered = new Set()) {
|
|
180
|
+
if (registered.has(moduleClass)) {
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
registered.add(moduleClass);
|
|
184
|
+
const moduleOptions = this.metadataRepository.getModuleOptions(moduleClass);
|
|
185
|
+
if (!moduleOptions) {
|
|
186
|
+
this.logger.emit({
|
|
187
|
+
level: 'error',
|
|
188
|
+
category: 'startup',
|
|
189
|
+
message: `Module ${moduleClass.name} is not properly decorated with @Module()`
|
|
190
|
+
});
|
|
191
|
+
throw new Error(`Module ${moduleClass.name} is not properly decorated with @Module()`);
|
|
192
|
+
}
|
|
193
|
+
const controllers = [];
|
|
194
|
+
if (moduleOptions.imports && moduleOptions.imports.length > 0) {
|
|
195
|
+
for (const importedModule of moduleOptions.imports) {
|
|
196
|
+
const importedControllers = await this.registerModule(importedModule, registered);
|
|
197
|
+
controllers.push(...importedControllers);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (moduleOptions.services && moduleOptions.services.length > 0) {
|
|
201
|
+
for (const serviceClass of moduleOptions.services) {
|
|
202
|
+
this.container.resolve(serviceClass);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (moduleOptions.controllers && moduleOptions.controllers.length > 0) {
|
|
206
|
+
controllers.push(...moduleOptions.controllers);
|
|
207
|
+
}
|
|
208
|
+
return controllers;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HandlerInvocationInput } from '../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Invokes route handlers and maps non-Response results to Hono responses.
|
|
4
|
+
*/
|
|
5
|
+
export declare class HandlerInvoker {
|
|
6
|
+
invoke({ handler, args, context, contextIndex }: HandlerInvocationInput): Promise<unknown>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { FrameworkError } from '../errors';
|
|
2
|
+
import { isNil, isString } from '../utils';
|
|
3
|
+
/**
|
|
4
|
+
* Invokes route handlers and maps non-Response results to Hono responses.
|
|
5
|
+
*/
|
|
6
|
+
export class HandlerInvoker {
|
|
7
|
+
async invoke({ handler, args, context, contextIndex }) {
|
|
8
|
+
const result = await handler(...args);
|
|
9
|
+
if (contextIndex !== undefined) {
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
if (result instanceof Response) {
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
if (isNil(result)) {
|
|
16
|
+
return context.json(null);
|
|
17
|
+
}
|
|
18
|
+
if (isString(result)) {
|
|
19
|
+
return context.text(result);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
return context.json(result);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw new FrameworkError('Handler return value could not be serialized as JSON.', {
|
|
26
|
+
status: 500,
|
|
27
|
+
code: 'RESPONSE_SERIALIZATION_FAILED',
|
|
28
|
+
category: 'pipeline',
|
|
29
|
+
remediation: 'Return JSON-serializable values from handlers or map custom values in a filter before returning.',
|
|
30
|
+
details: {
|
|
31
|
+
handlerResultType: typeof result
|
|
32
|
+
},
|
|
33
|
+
cause: error
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|