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.
Files changed (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +277 -0
  3. package/dist/app.d.ts +1 -0
  4. package/dist/app.js +36 -0
  5. package/dist/application/plugin-entries.d.ts +13 -0
  6. package/dist/application/plugin-entries.js +38 -0
  7. package/dist/application/startup-guide.d.ts +4 -0
  8. package/dist/application/startup-guide.js +53 -0
  9. package/dist/application-context.d.ts +13 -0
  10. package/dist/application-context.js +22 -0
  11. package/dist/application.d.ts +34 -0
  12. package/dist/application.js +224 -0
  13. package/dist/components/index.d.ts +1 -0
  14. package/dist/components/index.js +1 -0
  15. package/dist/components/layout.component.d.ts +31 -0
  16. package/dist/components/layout.component.js +94 -0
  17. package/dist/constants/index.d.ts +2 -0
  18. package/dist/constants/index.js +2 -0
  19. package/dist/constants/pipeline.constants.d.ts +6 -0
  20. package/dist/constants/pipeline.constants.js +6 -0
  21. package/dist/constants/version.constants.d.ts +5 -0
  22. package/dist/constants/version.constants.js +5 -0
  23. package/dist/decorators/controller.decorator.d.ts +9 -0
  24. package/dist/decorators/controller.decorator.js +16 -0
  25. package/dist/decorators/http-method.decorator.d.ts +43 -0
  26. package/dist/decorators/http-method.decorator.js +44 -0
  27. package/dist/decorators/index.d.ts +11 -0
  28. package/dist/decorators/index.js +11 -0
  29. package/dist/decorators/module.decorator.d.ts +8 -0
  30. package/dist/decorators/module.decorator.js +12 -0
  31. package/dist/decorators/mvc.decorator.d.ts +26 -0
  32. package/dist/decorators/mvc.decorator.js +36 -0
  33. package/dist/decorators/parameter.decorator.d.ts +41 -0
  34. package/dist/decorators/parameter.decorator.js +59 -0
  35. package/dist/decorators/service.decorator.d.ts +6 -0
  36. package/dist/decorators/service.decorator.js +11 -0
  37. package/dist/decorators/use-component.decorator.d.ts +10 -0
  38. package/dist/decorators/use-component.decorator.js +19 -0
  39. package/dist/decorators/use-filters.decorator.d.ts +8 -0
  40. package/dist/decorators/use-filters.decorator.js +17 -0
  41. package/dist/decorators/use-guards.decorator.d.ts +9 -0
  42. package/dist/decorators/use-guards.decorator.js +18 -0
  43. package/dist/decorators/use-middleware.decorator.d.ts +9 -0
  44. package/dist/decorators/use-middleware.decorator.js +18 -0
  45. package/dist/decorators/use-pipes.decorator.d.ts +9 -0
  46. package/dist/decorators/use-pipes.decorator.js +18 -0
  47. package/dist/di/container.d.ts +34 -0
  48. package/dist/di/container.js +114 -0
  49. package/dist/di/index.d.ts +1 -0
  50. package/dist/di/index.js +1 -0
  51. package/dist/errors/framework.error.d.ts +19 -0
  52. package/dist/errors/framework.error.js +23 -0
  53. package/dist/errors/index.d.ts +1 -0
  54. package/dist/errors/index.js +1 -0
  55. package/dist/handlers/error.handler.d.ts +28 -0
  56. package/dist/handlers/error.handler.js +17 -0
  57. package/dist/handlers/index.d.ts +2 -0
  58. package/dist/handlers/index.js +2 -0
  59. package/dist/handlers/not-found.handler.d.ts +14 -0
  60. package/dist/handlers/not-found.handler.js +17 -0
  61. package/dist/helpers/create-error-response.helper.d.ts +25 -0
  62. package/dist/helpers/create-error-response.helper.js +90 -0
  63. package/dist/helpers/create-http-method-decorator.helper.d.ts +16 -0
  64. package/dist/helpers/create-http-method-decorator.helper.js +30 -0
  65. package/dist/helpers/create-param-decorator.helper.d.ts +16 -0
  66. package/dist/helpers/create-param-decorator.helper.js +60 -0
  67. package/dist/helpers/index.d.ts +3 -0
  68. package/dist/helpers/index.js +3 -0
  69. package/dist/index.d.ts +16 -0
  70. package/dist/index.js +16 -0
  71. package/dist/interfaces/application-context.interface.d.ts +35 -0
  72. package/dist/interfaces/application-context.interface.js +1 -0
  73. package/dist/interfaces/controller-options.interface.d.ts +17 -0
  74. package/dist/interfaces/controller-options.interface.js +1 -0
  75. package/dist/interfaces/di-container.interface.d.ts +35 -0
  76. package/dist/interfaces/di-container.interface.js +1 -0
  77. package/dist/interfaces/error-response.interface.d.ts +13 -0
  78. package/dist/interfaces/error-response.interface.js +1 -0
  79. package/dist/interfaces/filter.interface.d.ts +20 -0
  80. package/dist/interfaces/filter.interface.js +1 -0
  81. package/dist/interfaces/guard.interface.d.ts +21 -0
  82. package/dist/interfaces/guard.interface.js +1 -0
  83. package/dist/interfaces/handler-invocation.interface.d.ts +10 -0
  84. package/dist/interfaces/handler-invocation.interface.js +1 -0
  85. package/dist/interfaces/honest-options.interface.d.ts +121 -0
  86. package/dist/interfaces/honest-options.interface.js +1 -0
  87. package/dist/interfaces/http-method-options.interface.d.ts +38 -0
  88. package/dist/interfaces/http-method-options.interface.js +1 -0
  89. package/dist/interfaces/index.d.ts +21 -0
  90. package/dist/interfaces/index.js +21 -0
  91. package/dist/interfaces/logger.interface.d.ts +23 -0
  92. package/dist/interfaces/logger.interface.js +1 -0
  93. package/dist/interfaces/metadata-repository.interface.d.ts +30 -0
  94. package/dist/interfaces/metadata-repository.interface.js +1 -0
  95. package/dist/interfaces/middleware.interface.d.ts +22 -0
  96. package/dist/interfaces/middleware.interface.js +1 -0
  97. package/dist/interfaces/module-options.interface.d.ts +18 -0
  98. package/dist/interfaces/module-options.interface.js +1 -0
  99. package/dist/interfaces/parameter-metadata.interface.d.ts +27 -0
  100. package/dist/interfaces/parameter-metadata.interface.js +1 -0
  101. package/dist/interfaces/parameter-resolution.interface.d.ts +14 -0
  102. package/dist/interfaces/parameter-resolution.interface.js +1 -0
  103. package/dist/interfaces/pipe.interface.d.ts +37 -0
  104. package/dist/interfaces/pipe.interface.js +1 -0
  105. package/dist/interfaces/pipeline-context.interface.d.ts +9 -0
  106. package/dist/interfaces/pipeline-context.interface.js +1 -0
  107. package/dist/interfaces/plugin.interface.d.ts +74 -0
  108. package/dist/interfaces/plugin.interface.js +1 -0
  109. package/dist/interfaces/route-definition.interface.d.ts +51 -0
  110. package/dist/interfaces/route-definition.interface.js +1 -0
  111. package/dist/interfaces/route-info.interface.d.ts +42 -0
  112. package/dist/interfaces/route-info.interface.js +1 -0
  113. package/dist/interfaces/service-registry.interface.d.ts +7 -0
  114. package/dist/interfaces/service-registry.interface.js +1 -0
  115. package/dist/loggers/console.logger.d.ts +7 -0
  116. package/dist/loggers/console.logger.js +21 -0
  117. package/dist/loggers/index.d.ts +2 -0
  118. package/dist/loggers/index.js +2 -0
  119. package/dist/loggers/noop.logger.d.ts +7 -0
  120. package/dist/loggers/noop.logger.js +8 -0
  121. package/dist/managers/component.manager.d.ts +48 -0
  122. package/dist/managers/component.manager.js +210 -0
  123. package/dist/managers/handler.invoker.d.ts +7 -0
  124. package/dist/managers/handler.invoker.js +37 -0
  125. package/dist/managers/index.d.ts +5 -0
  126. package/dist/managers/index.js +5 -0
  127. package/dist/managers/parameter.resolver.d.ts +13 -0
  128. package/dist/managers/parameter.resolver.js +58 -0
  129. package/dist/managers/pipeline.executor.d.ts +28 -0
  130. package/dist/managers/pipeline.executor.js +71 -0
  131. package/dist/managers/route.manager.d.ts +36 -0
  132. package/dist/managers/route.manager.js +149 -0
  133. package/dist/registries/index.d.ts +4 -0
  134. package/dist/registries/index.js +4 -0
  135. package/dist/registries/metadata.registry.d.ts +163 -0
  136. package/dist/registries/metadata.registry.js +250 -0
  137. package/dist/registries/metadata.repository.d.ts +30 -0
  138. package/dist/registries/metadata.repository.js +151 -0
  139. package/dist/registries/route.registry.d.ts +16 -0
  140. package/dist/registries/route.registry.js +46 -0
  141. package/dist/registries/service.registry.d.ts +8 -0
  142. package/dist/registries/service.registry.js +9 -0
  143. package/dist/testing/create-controller-test-application.d.ts +5 -0
  144. package/dist/testing/create-controller-test-application.js +11 -0
  145. package/dist/testing/create-service-test-container.d.ts +5 -0
  146. package/dist/testing/create-service-test-container.js +31 -0
  147. package/dist/testing/create-test-application.d.ts +5 -0
  148. package/dist/testing/create-test-application.js +20 -0
  149. package/dist/testing/create-testing-module.d.ts +6 -0
  150. package/dist/testing/create-testing-module.js +13 -0
  151. package/dist/testing/fixtures/application-test-fixtures.d.ts +17 -0
  152. package/dist/testing/fixtures/application-test-fixtures.js +230 -0
  153. package/dist/testing/index.d.ts +5 -0
  154. package/dist/testing/index.js +5 -0
  155. package/dist/testing/testing.interface.d.ts +96 -0
  156. package/dist/testing/testing.interface.js +1 -0
  157. package/dist/types/constructor.type.d.ts +12 -0
  158. package/dist/types/constructor.type.js +1 -0
  159. package/dist/types/index.d.ts +1 -0
  160. package/dist/types/index.js +1 -0
  161. package/dist/utils/common.util.d.ts +117 -0
  162. package/dist/utils/common.util.js +140 -0
  163. package/dist/utils/index.d.ts +1 -0
  164. package/dist/utils/index.js +1 -0
  165. 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,9 @@
1
+ import type { Constructor } from '../types';
2
+ /**
3
+ * Request-scoped keys used by the framework pipeline.
4
+ */
5
+ export interface PipelineContextValues {
6
+ controllerClass?: Constructor;
7
+ handlerName?: string;
8
+ bodyCache?: unknown;
9
+ }
@@ -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,7 @@
1
+ import type { Constructor } from '../types';
2
+ /**
3
+ * Contract for checking whether classes are registered as injectable services.
4
+ */
5
+ export interface IServiceRegistry {
6
+ isService(service: Constructor): boolean;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { LogEvent, ILogger } from '../interfaces';
2
+ /**
3
+ * Default logger that writes structured events to console.
4
+ */
5
+ export declare class ConsoleLogger implements ILogger {
6
+ emit(event: LogEvent): void;
7
+ }
@@ -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,2 @@
1
+ export * from './console.logger';
2
+ export * from './noop.logger';
@@ -0,0 +1,2 @@
1
+ export * from './console.logger';
2
+ export * from './noop.logger';
@@ -0,0 +1,7 @@
1
+ import type { LogEvent, ILogger } from '../interfaces';
2
+ /**
3
+ * Logger implementation that intentionally does nothing.
4
+ */
5
+ export declare class NoopLogger implements ILogger {
6
+ emit(_event: LogEvent): void;
7
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Logger implementation that intentionally does nothing.
3
+ */
4
+ export class NoopLogger {
5
+ emit(_event) {
6
+ // no-op
7
+ }
8
+ }
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ export * from './component.manager';
2
+ export * from './handler.invoker';
3
+ export * from './parameter.resolver';
4
+ export * from './pipeline.executor';
5
+ export * from './route.manager';
@@ -0,0 +1,5 @@
1
+ export * from './component.manager';
2
+ export * from './handler.invoker';
3
+ export * from './parameter.resolver';
4
+ export * from './pipeline.executor';
5
+ export * from './route.manager';