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,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,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;
|