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,250 @@
1
+ /**
2
+ * Central registry for managing application metadata
3
+ * Stores and provides access to:
4
+ * - Route definitions and controller configurations
5
+ * - Service and module registrations
6
+ * - Parameter metadata and context indices
7
+ * - Component registrations at global, controller, and handler levels
8
+ */
9
+ export class MetadataRegistry {
10
+ /**
11
+ * Stores route definitions for each controller
12
+ * Maps controller classes to their route configurations
13
+ */
14
+ static routes = new Map();
15
+ /**
16
+ * Stores base paths for controllers
17
+ * Maps controller classes to their route prefixes
18
+ */
19
+ static controllers = new Map();
20
+ /**
21
+ * Stores configuration options for controllers
22
+ * Includes settings like versioning and prefix options
23
+ */
24
+ static controllerOptions = new Map();
25
+ /**
26
+ * Registry of service classes
27
+ * Used for dependency injection and lifecycle management
28
+ */
29
+ static services = new Set();
30
+ /**
31
+ * Stores configuration options for modules
32
+ * Includes imports, exports, providers, and controllers
33
+ */
34
+ static modules = new Map();
35
+ /**
36
+ * Stores parameter metadata for controller methods
37
+ * Used for parameter transformation and validation
38
+ */
39
+ static parameters = new Map();
40
+ /**
41
+ * Stores indices of context parameters in controller methods
42
+ * Used for optimizing context injection
43
+ */
44
+ static contextIndices = new Map();
45
+ /**
46
+ * Registry for controller-level components
47
+ * Components registered here apply to all routes in a specific controller
48
+ */
49
+ static controller = new Map([
50
+ ['middleware', new Map()],
51
+ ['guard', new Map()],
52
+ ['pipe', new Map()],
53
+ ['filter', new Map()]
54
+ ]);
55
+ /**
56
+ * Registry for handler-level components
57
+ * Components registered here apply to specific route handlers
58
+ * Keyed by controller constructor then handler name for collision-safe lookups
59
+ */
60
+ static handler = new Map([
61
+ ['middleware', new Map()],
62
+ ['guard', new Map()],
63
+ ['pipe', new Map()],
64
+ ['filter', new Map()]
65
+ ]);
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) {
72
+ return this.routes.get(controller) || [];
73
+ }
74
+ /**
75
+ * Set routes for a controller
76
+ */
77
+ static setRoutes(controller, routes) {
78
+ this.routes.set(controller, routes);
79
+ }
80
+ /**
81
+ * Add a route to a controller
82
+ */
83
+ static addRoute(controller, route) {
84
+ if (!this.routes.has(controller)) {
85
+ this.routes.set(controller, []);
86
+ }
87
+ this.routes.get(controller).push(route);
88
+ }
89
+ /**
90
+ * Get controller path
91
+ */
92
+ static getControllerPath(controller) {
93
+ return this.controllers.get(controller) || '';
94
+ }
95
+ /**
96
+ * Check if a class is registered as a controller.
97
+ */
98
+ static hasController(controller) {
99
+ return this.controllers.has(controller);
100
+ }
101
+ /**
102
+ * Set controller path
103
+ */
104
+ static setControllerPath(controller, path) {
105
+ this.controllers.set(controller, path);
106
+ }
107
+ /**
108
+ * Get controller options
109
+ */
110
+ static getControllerOptions(controller) {
111
+ return this.controllerOptions.get(controller) || {};
112
+ }
113
+ /**
114
+ * Set controller options
115
+ */
116
+ static setControllerOptions(controller, options) {
117
+ this.controllerOptions.set(controller, options);
118
+ }
119
+ /**
120
+ * Check if class is a service
121
+ */
122
+ static isService(service) {
123
+ return this.services.has(service);
124
+ }
125
+ /**
126
+ * Add a service
127
+ */
128
+ static addService(service) {
129
+ this.services.add(service);
130
+ }
131
+ /**
132
+ * Get all services
133
+ */
134
+ static getAllServices() {
135
+ return this.services;
136
+ }
137
+ /**
138
+ * Get module options
139
+ */
140
+ static getModuleOptions(module) {
141
+ return this.modules.get(module);
142
+ }
143
+ /**
144
+ * Set module options
145
+ */
146
+ static setModuleOptions(module, options) {
147
+ this.modules.set(module, options);
148
+ }
149
+ /**
150
+ * Get parameter metadata
151
+ */
152
+ static getParameters(controller) {
153
+ return this.parameters.get(controller) || new Map();
154
+ }
155
+ /**
156
+ * Set parameter metadata
157
+ */
158
+ static setParameterMap(controller, params) {
159
+ this.parameters.set(controller, params);
160
+ }
161
+ /**
162
+ * Get context indices
163
+ */
164
+ static getContextIndices(controller) {
165
+ return this.contextIndices.get(controller) || new Map();
166
+ }
167
+ /**
168
+ * Set context indices
169
+ */
170
+ static setContextIndices(controller, indices) {
171
+ this.contextIndices.set(controller, indices);
172
+ }
173
+ /**
174
+ * Register a component at the controller level
175
+ */
176
+ static registerController(type, controller, component) {
177
+ const typeMap = this.controller.get(type);
178
+ if (!typeMap.has(controller)) {
179
+ typeMap.set(controller, []);
180
+ }
181
+ typeMap.get(controller).push(component);
182
+ }
183
+ /**
184
+ * Get all controller-level components of a specific type for a controller
185
+ */
186
+ static getController(type, controller) {
187
+ const typeMap = this.controller.get(type);
188
+ return (typeMap.get(controller) || []);
189
+ }
190
+ /**
191
+ * Register a component at the handler level
192
+ */
193
+ static registerHandler(type, controller, handlerName, component) {
194
+ const typeMap = this.handler.get(type);
195
+ if (!typeMap.has(controller)) {
196
+ typeMap.set(controller, new Map());
197
+ }
198
+ const controllerMap = typeMap.get(controller);
199
+ if (!controllerMap.has(handlerName)) {
200
+ controllerMap.set(handlerName, []);
201
+ }
202
+ controllerMap.get(handlerName).push(component);
203
+ }
204
+ /**
205
+ * Get all handler-level components of a specific type for a handler
206
+ */
207
+ static getHandler(type, controller, handlerName) {
208
+ const typeMap = this.handler.get(type);
209
+ const controllerMap = typeMap.get(controller);
210
+ if (!controllerMap) {
211
+ return [];
212
+ }
213
+ return (controllerMap.get(handlerName) || []);
214
+ }
215
+ /**
216
+ * Clears handler-level component registrations created via {@link registerHandler}.
217
+ * Does not remove decorator-defined routes, controllers, or modules.
218
+ */
219
+ static clearHandlerComponents() {
220
+ for (const typeMap of this.handler.values()) {
221
+ for (const controllerMap of typeMap.values()) {
222
+ controllerMap.clear();
223
+ }
224
+ typeMap.clear();
225
+ }
226
+ }
227
+ /**
228
+ * Clears all registered decorator metadata.
229
+ * Primarily used for testing. Warning: clearing after importing decorated classes
230
+ * (e.g. shared fixtures) removes their metadata until those modules are re-evaluated.
231
+ */
232
+ static clear() {
233
+ this.routes.clear();
234
+ this.controllers.clear();
235
+ this.controllerOptions.clear();
236
+ this.services.clear();
237
+ this.modules.clear();
238
+ this.parameters.clear();
239
+ this.contextIndices.clear();
240
+ for (const map of this.controller.values()) {
241
+ map.clear();
242
+ }
243
+ for (const typeMap of this.handler.values()) {
244
+ for (const controllerMap of typeMap.values()) {
245
+ controllerMap.clear();
246
+ }
247
+ typeMap.clear();
248
+ }
249
+ }
250
+ }
@@ -0,0 +1,30 @@
1
+ import type { ControllerOptions, IMetadataRepository, MetadataComponentType, MetadataComponentTypeMap, ModuleOptions, ParameterMetadata, RouteDefinition } from '../interfaces';
2
+ import type { Constructor } from '../types';
3
+ /**
4
+ * Immutable metadata repository for a single Application instance.
5
+ * Captures a deep copy of all metadata reachable from a root module at creation time,
6
+ * isolating the application from later mutations to the static MetadataRegistry.
7
+ */
8
+ export declare class MetadataRepository implements IMetadataRepository {
9
+ private readonly controllerPaths;
10
+ private readonly controllerOptions;
11
+ private readonly routes;
12
+ private readonly parameters;
13
+ private readonly contextIndices;
14
+ private readonly modules;
15
+ private readonly controllerComponents;
16
+ private readonly handlerComponents;
17
+ static fromRootModule(rootModule: Constructor): MetadataRepository;
18
+ hasController(controller: Constructor): boolean;
19
+ getControllerPath(controller: Constructor): string;
20
+ getControllerOptions(controller: Constructor): ControllerOptions;
21
+ getRoutes(controller: Constructor): RouteDefinition[];
22
+ getParameters(controller: Constructor): Map<string | symbol, ParameterMetadata[]>;
23
+ getContextIndices(controller: Constructor): Map<string | symbol, number>;
24
+ getModuleOptions(module: Constructor): ModuleOptions | undefined;
25
+ getControllerComponents<T extends MetadataComponentType>(type: T, controller: Constructor): MetadataComponentTypeMap[T][];
26
+ getHandlerComponents<T extends MetadataComponentType>(type: T, controller: Constructor, handlerName: string | symbol): MetadataComponentTypeMap[T][];
27
+ private captureModuleGraph;
28
+ private captureController;
29
+ private cloneRouteDefinition;
30
+ }
@@ -0,0 +1,151 @@
1
+ import { MetadataRegistry } from './metadata.registry';
2
+ /**
3
+ * Immutable metadata repository for a single Application instance.
4
+ * Captures a deep copy of all metadata reachable from a root module at creation time,
5
+ * isolating the application from later mutations to the static MetadataRegistry.
6
+ */
7
+ export class MetadataRepository {
8
+ controllerPaths = new Map();
9
+ controllerOptions = new Map();
10
+ routes = new Map();
11
+ parameters = new Map();
12
+ contextIndices = new Map();
13
+ modules = new Map();
14
+ controllerComponents = new Map([
15
+ ['middleware', new Map()],
16
+ ['guard', new Map()],
17
+ ['pipe', new Map()],
18
+ ['filter', new Map()]
19
+ ]);
20
+ handlerComponents = new Map([
21
+ ['middleware', new Map()],
22
+ ['guard', new Map()],
23
+ ['pipe', new Map()],
24
+ ['filter', new Map()]
25
+ ]);
26
+ static fromRootModule(rootModule) {
27
+ const snapshot = new MetadataRepository();
28
+ snapshot.captureModuleGraph(rootModule);
29
+ return snapshot;
30
+ }
31
+ hasController(controller) {
32
+ return this.controllerPaths.has(controller);
33
+ }
34
+ getControllerPath(controller) {
35
+ return this.controllerPaths.get(controller) || '';
36
+ }
37
+ getControllerOptions(controller) {
38
+ const options = this.controllerOptions.get(controller);
39
+ return options ? { ...options } : {};
40
+ }
41
+ getRoutes(controller) {
42
+ return (this.routes.get(controller) || []).map((route) => this.cloneRouteDefinition(route));
43
+ }
44
+ getParameters(controller) {
45
+ const parameters = this.parameters.get(controller);
46
+ if (!parameters) {
47
+ return new Map();
48
+ }
49
+ const cloned = new Map();
50
+ for (const [handlerName, entries] of parameters.entries()) {
51
+ cloned.set(handlerName, entries.map((entry) => ({ ...entry })));
52
+ }
53
+ return cloned;
54
+ }
55
+ getContextIndices(controller) {
56
+ return new Map(this.contextIndices.get(controller) || new Map());
57
+ }
58
+ getModuleOptions(module) {
59
+ const options = this.modules.get(module);
60
+ if (!options) {
61
+ return undefined;
62
+ }
63
+ return {
64
+ controllers: options.controllers ? [...options.controllers] : undefined,
65
+ services: options.services ? [...options.services] : undefined,
66
+ imports: options.imports ? [...options.imports] : undefined
67
+ };
68
+ }
69
+ getControllerComponents(type, controller) {
70
+ const map = this.controllerComponents.get(type);
71
+ const components = (map.get(controller) || []);
72
+ return [...components];
73
+ }
74
+ getHandlerComponents(type, controller, handlerName) {
75
+ const typeMap = this.handlerComponents.get(type);
76
+ const controllerMap = typeMap.get(controller);
77
+ if (!controllerMap) {
78
+ return [];
79
+ }
80
+ const components = (controllerMap.get(handlerName) || []);
81
+ return [...components];
82
+ }
83
+ captureModuleGraph(rootModule) {
84
+ const visitedModules = new Set();
85
+ const controllers = new Set();
86
+ const visitModule = (moduleClass) => {
87
+ if (visitedModules.has(moduleClass)) {
88
+ return;
89
+ }
90
+ visitedModules.add(moduleClass);
91
+ const moduleOptions = MetadataRegistry.getModuleOptions(moduleClass);
92
+ if (!moduleOptions) {
93
+ return;
94
+ }
95
+ const moduleSnapshot = {
96
+ controllers: moduleOptions.controllers ? [...moduleOptions.controllers] : undefined,
97
+ services: moduleOptions.services ? [...moduleOptions.services] : undefined,
98
+ imports: moduleOptions.imports ? [...moduleOptions.imports] : undefined
99
+ };
100
+ this.modules.set(moduleClass, moduleSnapshot);
101
+ for (const controller of moduleSnapshot.controllers || []) {
102
+ controllers.add(controller);
103
+ }
104
+ for (const importedModule of moduleSnapshot.imports || []) {
105
+ visitModule(importedModule);
106
+ }
107
+ };
108
+ visitModule(rootModule);
109
+ for (const controller of controllers) {
110
+ this.captureController(controller);
111
+ }
112
+ }
113
+ captureController(controller) {
114
+ if (!MetadataRegistry.hasController(controller)) {
115
+ return;
116
+ }
117
+ this.controllerPaths.set(controller, MetadataRegistry.getControllerPath(controller) || '');
118
+ this.controllerOptions.set(controller, { ...MetadataRegistry.getControllerOptions(controller) });
119
+ const routes = (MetadataRegistry.getRoutes(controller) || []).map((route) => this.cloneRouteDefinition(route));
120
+ this.routes.set(controller, routes);
121
+ const parameters = MetadataRegistry.getParameters(controller);
122
+ const parameterSnapshot = new Map();
123
+ for (const [handlerName, entries] of parameters.entries()) {
124
+ parameterSnapshot.set(handlerName, (entries || []).map((entry) => ({ ...entry })));
125
+ }
126
+ this.parameters.set(controller, parameterSnapshot);
127
+ this.contextIndices.set(controller, new Map(MetadataRegistry.getContextIndices(controller) || new Map()));
128
+ for (const type of ['middleware', 'guard', 'pipe', 'filter']) {
129
+ const controllerMap = this.controllerComponents.get(type);
130
+ controllerMap.set(controller, [...(MetadataRegistry.getController(type, controller) || [])]);
131
+ }
132
+ for (const route of routes) {
133
+ for (const type of ['middleware', 'guard', 'pipe', 'filter']) {
134
+ const typeMap = this.handlerComponents.get(type);
135
+ if (!typeMap.has(controller)) {
136
+ typeMap.set(controller, new Map());
137
+ }
138
+ const controllerHandlers = typeMap.get(controller);
139
+ controllerHandlers.set(route.handlerName, [
140
+ ...MetadataRegistry.getHandler(type, controller, route.handlerName)
141
+ ]);
142
+ }
143
+ }
144
+ }
145
+ cloneRouteDefinition(route) {
146
+ return {
147
+ ...route,
148
+ version: Array.isArray(route.version) ? [...route.version] : route.version
149
+ };
150
+ }
151
+ }
@@ -0,0 +1,16 @@
1
+ import type { RouteInfo } from '../interfaces';
2
+ /**
3
+ * Registry for managing and querying route information in the application.
4
+ *
5
+ * Each Application instance owns its own RouteRegistry, ensuring routes
6
+ * from one app never leak into another.
7
+ */
8
+ export declare class RouteRegistry {
9
+ private readonly routes;
10
+ registerRoute(routeInfo: RouteInfo): void;
11
+ getRoutes(): ReadonlyArray<RouteInfo>;
12
+ getRoutesByController(controllerName: string | symbol): ReadonlyArray<RouteInfo>;
13
+ getRoutesByMethod(method: string): ReadonlyArray<RouteInfo>;
14
+ getRoutesByPath(pattern: RegExp): ReadonlyArray<RouteInfo>;
15
+ clear(): void;
16
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Registry for managing and querying route information in the application.
3
+ *
4
+ * Each Application instance owns its own RouteRegistry, ensuring routes
5
+ * from one app never leak into another.
6
+ */
7
+ export class RouteRegistry {
8
+ routes = [];
9
+ registerRoute(routeInfo) {
10
+ if (!routeInfo) {
11
+ throw new Error('Route info is required');
12
+ }
13
+ if (!routeInfo.controller) {
14
+ throw new Error('Route controller is required');
15
+ }
16
+ if (!routeInfo.handler) {
17
+ throw new Error('Route handler is required');
18
+ }
19
+ if (!routeInfo.method) {
20
+ throw new Error('Route method is required');
21
+ }
22
+ if (!routeInfo.fullPath) {
23
+ throw new Error('Route fullPath is required');
24
+ }
25
+ const isDuplicate = this.routes.some((route) => route.fullPath === routeInfo.fullPath && route.method.toUpperCase() === routeInfo.method.toUpperCase());
26
+ if (isDuplicate) {
27
+ throw new Error(`Duplicate route detected: ${routeInfo.method.toUpperCase()} ${routeInfo.fullPath} (${String(routeInfo.controller)}.${String(routeInfo.handler)})`);
28
+ }
29
+ this.routes.push(routeInfo);
30
+ }
31
+ getRoutes() {
32
+ return this.routes;
33
+ }
34
+ getRoutesByController(controllerName) {
35
+ return this.routes.filter((route) => route.controller === controllerName);
36
+ }
37
+ getRoutesByMethod(method) {
38
+ return this.routes.filter((route) => route.method.toUpperCase() === method.toUpperCase());
39
+ }
40
+ getRoutesByPath(pattern) {
41
+ return this.routes.filter((route) => pattern.test(route.fullPath));
42
+ }
43
+ clear() {
44
+ this.routes.length = 0;
45
+ }
46
+ }
@@ -0,0 +1,8 @@
1
+ import type { IServiceRegistry } from '../interfaces';
2
+ import type { Constructor } from '../types';
3
+ /**
4
+ * Adapter exposing service checks through the DI service registry contract.
5
+ */
6
+ export declare class StaticServiceRegistry implements IServiceRegistry {
7
+ isService(service: Constructor): boolean;
8
+ }
@@ -0,0 +1,9 @@
1
+ import { MetadataRegistry } from './metadata.registry';
2
+ /**
3
+ * Adapter exposing service checks through the DI service registry contract.
4
+ */
5
+ export class StaticServiceRegistry {
6
+ isService(service) {
7
+ return MetadataRegistry.isService(service);
8
+ }
9
+ }
@@ -0,0 +1,5 @@
1
+ import type { CreateControllerTestApplicationOptions, TestApplication } from './testing.interface';
2
+ /**
3
+ * Create a test application for a single controller with optional services/imports.
4
+ */
5
+ export declare function createControllerTestApplication(options: CreateControllerTestApplicationOptions): Promise<TestApplication>;
@@ -0,0 +1,11 @@
1
+ import { createTestApplication } from './create-test-application';
2
+ /**
3
+ * Create a test application for a single controller with optional services/imports.
4
+ */
5
+ export async function createControllerTestApplication(options) {
6
+ const { controller, ...rest } = options;
7
+ return createTestApplication({
8
+ ...rest,
9
+ controllers: [controller]
10
+ });
11
+ }
@@ -0,0 +1,5 @@
1
+ import type { CreateServiceTestContainerOptions, TestServiceContainer } from './testing.interface';
2
+ /**
3
+ * Create a lightweight DI container for service-only tests without HTTP bootstrap.
4
+ */
5
+ export declare function createServiceTestContainer(options?: CreateServiceTestContainerOptions): TestServiceContainer;
@@ -0,0 +1,31 @@
1
+ import { NoopLogger } from '../loggers';
2
+ import { Container } from '../di';
3
+ /**
4
+ * Create a lightweight DI container for service-only tests without HTTP bootstrap.
5
+ */
6
+ export function createServiceTestContainer(options = {}) {
7
+ const logger = options.logger ?? new NoopLogger();
8
+ const container = new Container(undefined, logger, Boolean(options.debugDi));
9
+ for (const override of options.overrides ?? []) {
10
+ const typedOverride = override;
11
+ container.register(typedOverride.provide, typedOverride.useValue);
12
+ }
13
+ for (const service of options.preload ?? []) {
14
+ container.resolve(service);
15
+ }
16
+ return {
17
+ container,
18
+ get(target) {
19
+ return container.resolve(target);
20
+ },
21
+ register(target, instance) {
22
+ container.register(target, instance);
23
+ },
24
+ has(target) {
25
+ return container.has(target);
26
+ },
27
+ clear() {
28
+ container.clear();
29
+ }
30
+ };
31
+ }
@@ -0,0 +1,5 @@
1
+ import type { CreateTestApplicationOptions, TestApplication } from './testing.interface';
2
+ /**
3
+ * Create a test-friendly application instance with a convenience request helper.
4
+ */
5
+ export declare function createTestApplication(options?: CreateTestApplicationOptions): Promise<TestApplication>;
@@ -0,0 +1,20 @@
1
+ import { Application } from '../application';
2
+ import { createTestingModule } from './create-testing-module';
3
+ /**
4
+ * Create a test-friendly application instance with a convenience request helper.
5
+ */
6
+ export async function createTestApplication(options = {}) {
7
+ const { module, appOptions, ...moduleOptions } = options;
8
+ const rootModule = (module ?? createTestingModule(moduleOptions));
9
+ const { app, hono } = await Application.create(rootModule, appOptions);
10
+ const request = (input, init) => {
11
+ if (typeof input === 'string') {
12
+ const normalizedInput = input.startsWith('http://') || input.startsWith('https://')
13
+ ? input
14
+ : `http://localhost${input.startsWith('/') ? input : `/${input}`}`;
15
+ return Promise.resolve(hono.request(normalizedInput, init));
16
+ }
17
+ return Promise.resolve(hono.request(input));
18
+ };
19
+ return { app, hono, request };
20
+ }
@@ -0,0 +1,6 @@
1
+ import type { Constructor } from '../types';
2
+ import type { TestModuleOptions } from './testing.interface';
3
+ /**
4
+ * Create a runtime module class for tests without boilerplate.
5
+ */
6
+ export declare function createTestingModule(options?: TestModuleOptions): Constructor;
@@ -0,0 +1,13 @@
1
+ import { Module } from '../decorators';
2
+ /**
3
+ * Create a runtime module class for tests without boilerplate.
4
+ */
5
+ export function createTestingModule(options = {}) {
6
+ const { controllers, services, imports, name = 'TestModule' } = options;
7
+ const dynamicModule = {
8
+ [name]: class {
9
+ }
10
+ }[name];
11
+ Module({ controllers, services, imports })(dynamicModule);
12
+ return dynamicModule;
13
+ }
@@ -0,0 +1,17 @@
1
+ import type { Constructor } from '../../types';
2
+ export declare function createTestController(): Constructor;
3
+ export declare function createPayloadController(): Constructor;
4
+ export declare function createRawResponseController(): Constructor;
5
+ export declare function createOnlyAController(): Constructor;
6
+ export declare function createOnlyBController(): Constructor;
7
+ export declare function createUndecoratedController(): Constructor;
8
+ export declare function createBrokenControllerModule(): Constructor;
9
+ export declare function createEmptyModule(): Constructor;
10
+ export declare function createDuplicateRouteControllers(): {
11
+ a: Constructor;
12
+ b: Constructor;
13
+ };
14
+ export declare function createUnsafeParamController(): Constructor;
15
+ export declare function createDiagnosticsAController(): Constructor;
16
+ export declare function createDiagnosticsBController(): Constructor;
17
+ export declare function createRuntimeMetadataController(): Constructor;