@velajs/vela 0.10.0 → 1.1.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 (91) hide show
  1. package/CHANGELOG.md +113 -0
  2. package/README.md +36 -2
  3. package/dist/cache/cache.decorators.d.ts +2 -2
  4. package/dist/cache/cache.module.d.ts +1 -1
  5. package/dist/cache/cache.module.js +1 -1
  6. package/dist/constants.d.ts +29 -26
  7. package/dist/constants.js +26 -29
  8. package/dist/container/container.js +2 -2
  9. package/dist/container/decorators.js +8 -11
  10. package/dist/container/types.d.ts +1 -0
  11. package/dist/errors/http-exception.d.ts +21 -20
  12. package/dist/errors/http-exception.js +6 -6
  13. package/dist/event-emitter/event-emitter.decorators.js +2 -3
  14. package/dist/event-emitter/event-emitter.subscriber.js +2 -1
  15. package/dist/factory.d.ts +0 -1
  16. package/dist/factory.js +2 -32
  17. package/dist/fetch/fetch.module.d.ts +2 -2
  18. package/dist/fetch/fetch.module.js +2 -2
  19. package/dist/health/health.service.js +6 -2
  20. package/dist/http/decorators.d.ts +6 -5
  21. package/dist/http/decorators.js +28 -76
  22. package/dist/http/execution-context.d.ts +4 -0
  23. package/dist/http/execution-context.js +15 -0
  24. package/dist/http/index.d.ts +2 -2
  25. package/dist/http/index.js +1 -1
  26. package/dist/http/route.manager.d.ts +1 -2
  27. package/dist/http/route.manager.js +20 -31
  28. package/dist/http/types.d.ts +0 -1
  29. package/dist/index.d.ts +4 -10
  30. package/dist/index.js +7 -12
  31. package/dist/internal.d.ts +11 -0
  32. package/dist/internal.js +13 -0
  33. package/dist/metadata.js +6 -15
  34. package/dist/module/decorators.d.ts +1 -3
  35. package/dist/module/decorators.js +8 -20
  36. package/dist/module/graph.d.ts +10 -0
  37. package/dist/module/graph.js +35 -0
  38. package/dist/module/index.d.ts +2 -0
  39. package/dist/module/index.js +1 -0
  40. package/dist/{http/middleware-consumer.d.ts → module/middleware.d.ts} +4 -13
  41. package/dist/{http/middleware-consumer.js → module/middleware.js} +0 -13
  42. package/dist/module/module-loader.d.ts +1 -1
  43. package/dist/module/module-loader.js +17 -13
  44. package/dist/module/types.d.ts +1 -29
  45. package/dist/openapi/decorators.js +15 -10
  46. package/dist/openapi/document.js +7 -42
  47. package/dist/pipeline/app-providers.d.ts +10 -0
  48. package/dist/pipeline/app-providers.js +43 -0
  49. package/dist/pipeline/component.manager.d.ts +1 -0
  50. package/dist/pipeline/component.manager.js +10 -34
  51. package/dist/pipeline/decorators.d.ts +5 -5
  52. package/dist/pipeline/decorators.js +1 -9
  53. package/dist/pipeline/reflector.d.ts +8 -24
  54. package/dist/pipeline/reflector.js +10 -55
  55. package/dist/registry/index.d.ts +1 -1
  56. package/dist/registry/metadata.registry.d.ts +20 -15
  57. package/dist/registry/metadata.registry.js +135 -154
  58. package/dist/registry/paths.d.ts +3 -0
  59. package/dist/registry/paths.js +15 -0
  60. package/dist/registry/types.d.ts +24 -14
  61. package/dist/registry/types.js +0 -1
  62. package/dist/registry/util.d.ts +3 -0
  63. package/dist/registry/util.js +8 -0
  64. package/dist/schedule/cron-matcher.d.ts +2 -0
  65. package/dist/schedule/cron-matcher.js +62 -0
  66. package/dist/schedule/index.d.ts +4 -3
  67. package/dist/schedule/index.js +2 -2
  68. package/dist/schedule/schedule.decorators.js +3 -6
  69. package/dist/schedule/schedule.module.d.ts +2 -6
  70. package/dist/schedule/schedule.module.js +4 -42
  71. package/dist/schedule/schedule.registry.js +3 -2
  72. package/dist/schedule/schedule.tokens.d.ts +0 -3
  73. package/dist/schedule/schedule.tokens.js +0 -2
  74. package/dist/schedule/schedule.types.d.ts +0 -3
  75. package/dist/schedule-node/index.d.ts +2 -0
  76. package/dist/schedule-node/index.js +2 -0
  77. package/dist/schedule-node/schedule-node.module.d.ts +4 -0
  78. package/dist/schedule-node/schedule-node.module.js +18 -0
  79. package/dist/{schedule → schedule-node}/schedule.executor.d.ts +2 -5
  80. package/dist/schedule-node/schedule.executor.js +91 -0
  81. package/dist/services/logger.d.ts +9 -8
  82. package/dist/services/logger.js +14 -15
  83. package/dist/throttler/throttler.decorators.d.ts +2 -2
  84. package/package.json +9 -9
  85. package/dist/schedule/schedule.executor.js +0 -169
  86. package/dist/testing/index.d.ts +0 -2
  87. package/dist/testing/index.js +0 -2
  88. package/dist/testing/testing.builder.d.ts +0 -29
  89. package/dist/testing/testing.builder.js +0 -148
  90. package/dist/testing/testing.module.d.ts +0 -9
  91. package/dist/testing/testing.module.js +0 -15
@@ -1,7 +1,4 @@
1
- import { getMetadata as internalGetMetadata } from "../metadata.js";
2
1
  import { MetadataRegistry } from "../registry/metadata.registry.js";
3
- const SET_METADATA_KEY = 'vela:metadata';
4
- let _decoratorKeyCounter = 0;
5
2
  /**
6
3
  * Decorator that attaches custom metadata to a class or method.
7
4
  * Read via Reflector in guards/interceptors.
@@ -15,13 +12,10 @@ let _decoratorKeyCounter = 0;
15
12
  * class AdminController { ... }
16
13
  * ```
17
14
  */ export function SetMetadata(key, value) {
18
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
19
15
  return (target, propertyKey)=>{
20
16
  if (propertyKey !== undefined) {
21
- // Method decorator
22
17
  MetadataRegistry.setCustomHandlerMeta(target.constructor, propertyKey, key, value);
23
18
  } else {
24
- // Class decorator
25
19
  MetadataRegistry.setCustomClassMeta(target, key, value);
26
20
  }
27
21
  };
@@ -54,7 +48,7 @@ let _decoratorKeyCounter = 0;
54
48
  * // reflector.get(Roles, context) — typed as string[] | undefined
55
49
  * ```
56
50
  */ static createDecorator(options) {
57
- const key = options?.key ?? `vela:custom:${_decoratorKeyCounter++}`;
51
+ const key = options?.key ?? `vela:custom:${crypto.randomUUID()}`;
58
52
  const decorator = (value)=>SetMetadata(key, value);
59
53
  decorator.KEY = key;
60
54
  return decorator;
@@ -67,52 +61,22 @@ let _decoratorKeyCounter = 0;
67
61
  * Handler-level metadata takes priority over class-level.
68
62
  */ get(key, context) {
69
63
  const resolvedKey = this.resolveKey(key);
70
- // Check handler-level first (MetadataRegistry)
71
- const handlerValue = MetadataRegistry.getCustomHandlerMeta(context.getClass(), context.getHandler(), resolvedKey);
72
- if (handlerValue !== undefined) {
73
- return handlerValue;
74
- }
75
- // Check class-level (MetadataRegistry)
76
- const classValue = MetadataRegistry.getCustomClassMeta(context.getClass(), resolvedKey);
77
- if (classValue !== undefined) {
78
- return classValue;
79
- }
80
- // Fallback to WeakMap for external package compat
81
- const handlerMeta = internalGetMetadata(SET_METADATA_KEY, context.getClass(), context.getHandler());
82
- if (handlerMeta?.has(resolvedKey)) {
83
- return handlerMeta.get(resolvedKey);
84
- }
85
- const classMeta = internalGetMetadata(SET_METADATA_KEY, context.getClass());
86
- if (classMeta?.has(resolvedKey)) {
87
- return classMeta.get(resolvedKey);
88
- }
89
- return undefined;
64
+ const ctor = context.getClass();
65
+ const handlerValue = MetadataRegistry.getCustomHandlerMeta(ctor, context.getHandler(), resolvedKey);
66
+ if (handlerValue !== undefined) return handlerValue;
67
+ return MetadataRegistry.getCustomClassMeta(ctor, resolvedKey);
90
68
  }
91
69
  /**
92
70
  * Get metadata only from the handler (method) level.
93
71
  */ getHandler(key, context) {
94
72
  const resolvedKey = this.resolveKey(key);
95
- // MetadataRegistry first
96
- const value = MetadataRegistry.getCustomHandlerMeta(context.getClass(), context.getHandler(), resolvedKey);
97
- if (value !== undefined) {
98
- return value;
99
- }
100
- // Fallback to WeakMap
101
- const meta = internalGetMetadata(SET_METADATA_KEY, context.getClass(), context.getHandler());
102
- return meta?.get(resolvedKey);
73
+ return MetadataRegistry.getCustomHandlerMeta(context.getClass(), context.getHandler(), resolvedKey);
103
74
  }
104
75
  /**
105
76
  * Get metadata only from the class level.
106
77
  */ getClass(key, context) {
107
78
  const resolvedKey = this.resolveKey(key);
108
- // MetadataRegistry first
109
- const value = MetadataRegistry.getCustomClassMeta(context.getClass(), resolvedKey);
110
- if (value !== undefined) {
111
- return value;
112
- }
113
- // Fallback to WeakMap
114
- const meta = internalGetMetadata(SET_METADATA_KEY, context.getClass());
115
- return meta?.get(resolvedKey);
79
+ return MetadataRegistry.getCustomClassMeta(context.getClass(), resolvedKey);
116
80
  }
117
81
  /**
118
82
  * Get all metadata values for a key from both handler and class.
@@ -127,11 +91,7 @@ let _decoratorKeyCounter = 0;
127
91
  * Returns handler value if defined, else class value, else undefined.
128
92
  * First defined value wins.
129
93
  */ getAllAndOverride(key, context) {
130
- const handlerValue = this.getHandler(key, context);
131
- if (handlerValue !== undefined) {
132
- return handlerValue;
133
- }
134
- return this.getClass(key, context);
94
+ return this.getHandler(key, context) ?? this.getClass(key, context);
135
95
  }
136
96
  /**
137
97
  * Collects handler + class values, filters undefineds.
@@ -145,13 +105,8 @@ let _decoratorKeyCounter = 0;
145
105
  const values = [];
146
106
  if (handlerValue !== undefined) values.push(handlerValue);
147
107
  if (classValue !== undefined) values.push(classValue);
148
- if (values.length === 0) {
149
- return [];
150
- }
151
- if (values.length === 1) {
152
- return values[0];
153
- }
154
- // Both defined — merge strategy depends on type
108
+ if (values.length === 0) return [];
109
+ if (values.length === 1) return values[0];
155
110
  if (Array.isArray(values[0]) && Array.isArray(values[1])) {
156
111
  return [
157
112
  ...values[0],
@@ -1,2 +1,2 @@
1
1
  export { MetadataRegistry } from './metadata.registry';
2
- export type { Type, Constructor, ComponentType, ComponentTypeMap, ComponentInstance, MiddlewareType, GuardType, PipeType, InterceptorType, FilterType, RouteDefinition, ParameterMetadata, ModuleOptions, ProviderOptions, InjectionTokenLike, } from './types';
2
+ export type { Type, Constructor, ComponentType, ComponentTypeMap, ComponentInstance, MiddlewareType, GuardType, PipeType, InterceptorType, FilterType, RouteDefinition, ParameterMetadata, ModuleOptions, ProviderOptions, InjectionToken, } from './types';
@@ -1,7 +1,6 @@
1
1
  import type { Scope } from '../constants';
2
2
  import type { InjectMetadata } from '../container/types';
3
- import type { Type } from '../container/types';
4
- import type { ComponentType, ComponentTypeMap, Constructor, HttpHandlerMeta, ModuleOptions, ParameterMetadata, RouteDefinition } from './types';
3
+ import type { ComponentType, ComponentTypeMap, Constructor, HttpHandlerMeta, ModuleOptions, ParameterMetadata, RouteDefinition, Type } from './types';
5
4
  export interface ControllerOptions {
6
5
  version?: number | number[];
7
6
  }
@@ -11,17 +10,17 @@ export declare class MetadataRegistry {
11
10
  private static readonly controllerOptions;
12
11
  private static readonly modules;
13
12
  private static readonly parameters;
14
- private static readonly global;
15
- private static readonly controller;
16
- private static readonly handler;
17
13
  private static readonly injectables;
18
14
  private static readonly scopes;
19
15
  private static readonly injectTokens;
20
16
  private static readonly handlerHttpMeta;
21
17
  private static readonly catchTypes;
22
18
  private static readonly routeVersions;
23
- private static readonly customClassMeta;
24
- private static readonly customHandlerMeta;
19
+ private static readonly classMeta;
20
+ private static readonly handlerMeta;
21
+ private static readonly controllerComponents;
22
+ private static readonly handlerComponents;
23
+ private static globalComponents;
25
24
  static getRoutes(controller: Constructor): RouteDefinition[];
26
25
  static addRoute(controller: Constructor, route: RouteDefinition): void;
27
26
  static getControllerPath(controller: Constructor): string;
@@ -36,8 +35,8 @@ export declare class MetadataRegistry {
36
35
  static getGlobal<T extends ComponentType>(type: T): Set<ComponentTypeMap[T]>;
37
36
  static registerController<T extends ComponentType>(type: T, controller: Constructor, component: ComponentTypeMap[T]): void;
38
37
  static getController<T extends ComponentType>(type: T, controller: Constructor): ComponentTypeMap[T][];
39
- static registerHandler<T extends ComponentType>(type: T, handlerKey: string, component: ComponentTypeMap[T]): void;
40
- static getHandler<T extends ComponentType>(type: T, handlerKey: string): ComponentTypeMap[T][];
38
+ static registerHandler<T extends ComponentType>(type: T, controller: Constructor, methodName: string | symbol, component: ComponentTypeMap[T]): void;
39
+ static getHandler<T extends ComponentType>(type: T, controller: Constructor, methodName: string | symbol): ComponentTypeMap[T][];
41
40
  static markInjectable(target: object): void;
42
41
  static hasInjectable(target: object): boolean;
43
42
  static setScope(target: object, scope: Scope): void;
@@ -51,12 +50,18 @@ export declare class MetadataRegistry {
51
50
  static hasCatchTypes(filter: Constructor): boolean;
52
51
  static setRouteVersion(controller: Constructor, method: string | symbol, version: number | number[]): void;
53
52
  static getRouteVersion(controller: Constructor, method: string | symbol): number | number[] | undefined;
54
- static setCustomClassMeta(target: Constructor, key: string, value: unknown): void;
55
- static getCustomClassMeta(target: Constructor, key: string): unknown;
56
- static getCustomClassMetaAll(target: Constructor): Map<string, unknown> | undefined;
57
- static setCustomHandlerMeta(target: Constructor, handler: string | symbol, key: string, value: unknown): void;
58
- static getCustomHandlerMeta(target: Constructor, handler: string | symbol, key: string): unknown;
59
- static getCustomHandlerMetaAll(target: Constructor, handler: string | symbol): Map<string, unknown> | undefined;
53
+ static setCustomClassMeta(target: object, key: string, value: unknown): void;
54
+ static getCustomClassMeta(target: object, key: string): unknown;
55
+ static getCustomClassMetaAll(target: object): Map<string, unknown> | undefined;
56
+ static setCustomHandlerMeta(target: object, handler: string | symbol, key: string, value: unknown): void;
57
+ static getCustomHandlerMeta(target: object, handler: string | symbol, key: string): unknown;
58
+ static getCustomHandlerMetaAll(target: object, handler: string | symbol): Map<string, unknown> | undefined;
59
+ static appendCustomClassMeta<T>(target: object, key: string, item: T): void;
60
+ static appendCustomHandlerMeta<T>(target: object, handler: string | symbol, key: string, item: T): void;
61
+ static setReflectMetadata(target: object, key: string, value: unknown, propertyKey?: string | symbol): void;
62
+ static getReflectMetadata<T = unknown>(target: object, key: string, propertyKey?: string | symbol): T | undefined;
63
+ static getParamTypes(target: object, propertyKey?: string | symbol): unknown[] | undefined;
60
64
  static propagateControllerComponents(from: Constructor, to: Constructor): void;
61
65
  static clear(): void;
66
+ static reset(): void;
62
67
  }
@@ -1,108 +1,69 @@
1
+ import { getOrCreate, getOrCreateArray, getOrCreateMap } from "./util.js";
2
+ function emptyComponentStore() {
3
+ return {
4
+ middleware: new Set(),
5
+ guard: new Set(),
6
+ pipe: new Set(),
7
+ interceptor: new Set(),
8
+ filter: new Set()
9
+ };
10
+ }
11
+ function emptyComponentByOwner() {
12
+ return {
13
+ middleware: new Map(),
14
+ guard: new Map(),
15
+ pipe: new Map(),
16
+ interceptor: new Map(),
17
+ filter: new Map()
18
+ };
19
+ }
1
20
  export class MetadataRegistry {
21
+ // Decoration metadata (set at import time, persists across clear()).
2
22
  static routes = new Map();
3
23
  static controllers = new Map();
4
24
  static controllerOptions = new Map();
5
25
  static modules = new Map();
6
26
  static parameters = new Map();
7
- // 3-level component hierarchy
8
- static global = new Map([
9
- [
10
- 'middleware',
11
- new Set()
12
- ],
13
- [
14
- 'guard',
15
- new Set()
16
- ],
17
- [
18
- 'pipe',
19
- new Set()
20
- ],
21
- [
22
- 'interceptor',
23
- new Set()
24
- ],
25
- [
26
- 'filter',
27
- new Set()
28
- ]
29
- ]);
30
- static controller = new Map([
31
- [
32
- 'middleware',
33
- new Map()
34
- ],
35
- [
36
- 'guard',
37
- new Map()
38
- ],
39
- [
40
- 'pipe',
41
- new Map()
42
- ],
43
- [
44
- 'interceptor',
45
- new Map()
46
- ],
47
- [
48
- 'filter',
49
- new Map()
50
- ]
51
- ]);
52
- static handler = new Map([
53
- [
54
- 'middleware',
55
- new Map()
56
- ],
57
- [
58
- 'guard',
59
- new Map()
60
- ],
61
- [
62
- 'pipe',
63
- new Map()
64
- ],
65
- [
66
- 'interceptor',
67
- new Map()
68
- ],
69
- [
70
- 'filter',
71
- new Map()
72
- ]
73
- ]);
74
- // DI metadata
75
27
  static injectables = new Set();
76
28
  static scopes = new Map();
77
29
  static injectTokens = new Map();
78
- // HTTP handler metadata
79
30
  static handlerHttpMeta = new Map();
80
- // Exception filter types
81
31
  static catchTypes = new Map();
82
- // Route versions
83
32
  static routeVersions = new Map();
84
- // Custom metadata (SetMetadata)
85
- static customClassMeta = new Map();
86
- static customHandlerMeta = new Map();
33
+ // Free-form key→value class & handler meta. Backs:
34
+ // - @SetMetadata (custom user keys)
35
+ // - feature decorators (@Cron, @OnEvent, @ApiDoc, …)
36
+ // - the SWC shim (Reflect.metadata's design:* keys)
37
+ // - external Reflect.defineMetadata / Reflect.getMetadata calls.
38
+ static classMeta = new Map();
39
+ static handlerMeta = new Map();
40
+ // Component decoration (set by @UseGuards/@UsePipes/etc. at decoration time).
41
+ static controllerComponents = emptyComponentByOwner();
42
+ static handlerComponents = {
43
+ middleware: new Map(),
44
+ guard: new Map(),
45
+ pipe: new Map(),
46
+ interceptor: new Map(),
47
+ filter: new Map()
48
+ };
49
+ // Global components — app-time state (cleared by clear()).
50
+ static globalComponents = emptyComponentStore();
87
51
  // Routes
88
52
  static getRoutes(controller) {
89
- return this.routes.get(controller) || [];
53
+ return this.routes.get(controller) ?? [];
90
54
  }
91
55
  static addRoute(controller, route) {
92
- if (!this.routes.has(controller)) {
93
- this.routes.set(controller, []);
94
- }
95
- this.routes.get(controller).push(route);
56
+ getOrCreateArray(this.routes, controller).push(route);
96
57
  }
97
58
  // Controllers
98
59
  static getControllerPath(controller) {
99
- return this.controllers.get(controller) || '';
60
+ return this.controllers.get(controller) ?? '';
100
61
  }
101
62
  static setControllerPath(controller, path) {
102
63
  this.controllers.set(controller, path);
103
64
  }
104
65
  static getControllerOptions(controller) {
105
- return this.controllerOptions.get(controller) || {};
66
+ return this.controllerOptions.get(controller) ?? {};
106
67
  }
107
68
  static setControllerOptions(controller, options) {
108
69
  this.controllerOptions.set(controller, options);
@@ -116,46 +77,37 @@ export class MetadataRegistry {
116
77
  }
117
78
  // Parameters
118
79
  static getParameters(controller) {
119
- return this.parameters.get(controller) || new Map();
80
+ return this.parameters.get(controller) ?? new Map();
120
81
  }
121
82
  static addParameter(controller, methodName, param) {
122
- if (!this.parameters.has(controller)) {
123
- this.parameters.set(controller, new Map());
124
- }
125
- const methodParams = this.parameters.get(controller);
126
- if (!methodParams.has(methodName)) {
127
- methodParams.set(methodName, []);
128
- }
129
- methodParams.get(methodName).push(param);
83
+ const methodMap = getOrCreateMap(this.parameters, controller);
84
+ getOrCreateArray(methodMap, methodName).push(param);
130
85
  }
131
- // Component registration — 3 levels
86
+ // Component registration — global
132
87
  static registerGlobal(type, component) {
133
- this.global.get(type).add(component);
88
+ this.globalComponents[type].add(component);
134
89
  }
135
90
  static getGlobal(type) {
136
- return this.global.get(type);
91
+ return this.globalComponents[type];
137
92
  }
93
+ // Component registration — controller-level
138
94
  static registerController(type, controller, component) {
139
- const typeMap = this.controller.get(type);
140
- if (!typeMap.has(controller)) {
141
- typeMap.set(controller, []);
142
- }
143
- typeMap.get(controller).push(component);
95
+ const map = this.controllerComponents[type];
96
+ getOrCreateArray(map, controller).push(component);
144
97
  }
145
98
  static getController(type, controller) {
146
- const typeMap = this.controller.get(type);
147
- return typeMap.get(controller) || [];
99
+ const map = this.controllerComponents[type];
100
+ return map.get(controller) ?? [];
148
101
  }
149
- static registerHandler(type, handlerKey, component) {
150
- const typeMap = this.handler.get(type);
151
- if (!typeMap.has(handlerKey)) {
152
- typeMap.set(handlerKey, []);
153
- }
154
- typeMap.get(handlerKey).push(component);
102
+ // Component registration handler-level
103
+ static registerHandler(type, controller, methodName, component) {
104
+ const map = this.handlerComponents[type];
105
+ const methodMap = getOrCreateMap(map, controller);
106
+ getOrCreateArray(methodMap, methodName).push(component);
155
107
  }
156
- static getHandler(type, handlerKey) {
157
- const typeMap = this.handler.get(type);
158
- return typeMap.get(handlerKey) || [];
108
+ static getHandler(type, controller, methodName) {
109
+ const map = this.handlerComponents[type];
110
+ return map.get(controller)?.get(methodName) ?? [];
159
111
  }
160
112
  // DI metadata
161
113
  static markInjectable(target) {
@@ -178,12 +130,8 @@ export class MetadataRegistry {
178
130
  }
179
131
  // HTTP handler metadata
180
132
  static setHandlerHttpMeta(controller, method, meta) {
181
- if (!this.handlerHttpMeta.has(controller)) {
182
- this.handlerHttpMeta.set(controller, new Map());
183
- }
184
- const methodMap = this.handlerHttpMeta.get(controller);
133
+ const methodMap = getOrCreateMap(this.handlerHttpMeta, controller);
185
134
  const existing = methodMap.get(method) ?? {};
186
- // Append-merge responseHeaders
187
135
  const merged = {
188
136
  ...existing,
189
137
  ...meta
@@ -211,79 +159,112 @@ export class MetadataRegistry {
211
159
  }
212
160
  // Route versions
213
161
  static setRouteVersion(controller, method, version) {
214
- if (!this.routeVersions.has(controller)) {
215
- this.routeVersions.set(controller, new Map());
216
- }
217
- this.routeVersions.get(controller).set(method, version);
162
+ getOrCreateMap(this.routeVersions, controller).set(method, version);
218
163
  }
219
164
  static getRouteVersion(controller, method) {
220
165
  return this.routeVersions.get(controller)?.get(method);
221
166
  }
222
- // Custom metadata (SetMetadata)
167
+ // Custom class/handler metadata. One slot per (target, key) for class-level,
168
+ // (target, handler, key) for handler-level. The same slots back @SetMetadata,
169
+ // every feature decorator (@Cron, @OnEvent, @ApiDoc, …), the SWC shim, and
170
+ // external Reflect.defineMetadata calls — one model, one source of truth.
223
171
  static setCustomClassMeta(target, key, value) {
224
- if (!this.customClassMeta.has(target)) {
225
- this.customClassMeta.set(target, new Map());
226
- }
227
- this.customClassMeta.get(target).set(key, value);
172
+ getOrCreate(this.classMeta, target, ()=>new Map()).set(key, value);
228
173
  }
229
174
  static getCustomClassMeta(target, key) {
230
- return this.customClassMeta.get(target)?.get(key);
175
+ return this.classMeta.get(target)?.get(key);
231
176
  }
232
177
  static getCustomClassMetaAll(target) {
233
- return this.customClassMeta.get(target);
178
+ return this.classMeta.get(target);
234
179
  }
235
180
  static setCustomHandlerMeta(target, handler, key, value) {
236
- if (!this.customHandlerMeta.has(target)) {
237
- this.customHandlerMeta.set(target, new Map());
238
- }
239
- const handlerMap = this.customHandlerMeta.get(target);
240
- if (!handlerMap.has(handler)) {
241
- handlerMap.set(handler, new Map());
242
- }
243
- handlerMap.get(handler).set(key, value);
181
+ const byHandler = getOrCreate(this.handlerMeta, target, ()=>new Map());
182
+ getOrCreate(byHandler, handler, ()=>new Map()).set(key, value);
244
183
  }
245
184
  static getCustomHandlerMeta(target, handler, key) {
246
- return this.customHandlerMeta.get(target)?.get(handler)?.get(key);
185
+ return this.handlerMeta.get(target)?.get(handler)?.get(key);
247
186
  }
248
187
  static getCustomHandlerMetaAll(target, handler) {
249
- return this.customHandlerMeta.get(target)?.get(handler);
188
+ return this.handlerMeta.get(target)?.get(handler);
189
+ }
190
+ // Append helpers — for stackable metadata like @Cron / @Interval / @OnEvent / @ApiResponse.
191
+ static appendCustomClassMeta(target, key, item) {
192
+ const list = this.getCustomClassMeta(target, key) ?? [];
193
+ list.push(item);
194
+ this.setCustomClassMeta(target, key, list);
195
+ }
196
+ static appendCustomHandlerMeta(target, handler, key, item) {
197
+ const list = this.getCustomHandlerMeta(target, handler, key) ?? [];
198
+ list.push(item);
199
+ this.setCustomHandlerMeta(target, handler, key, list);
200
+ }
201
+ // Reflect-style API — same storage as the typed setters above. Lets external
202
+ // code (and the SWC shim's Reflect.metadata polyfill) write/read uniformly.
203
+ static setReflectMetadata(target, key, value, propertyKey) {
204
+ if (propertyKey !== undefined) {
205
+ this.setCustomHandlerMeta(target, propertyKey, key, value);
206
+ } else {
207
+ this.setCustomClassMeta(target, key, value);
208
+ }
209
+ }
210
+ static getReflectMetadata(target, key, propertyKey) {
211
+ if (propertyKey !== undefined) {
212
+ return this.getCustomHandlerMeta(target, propertyKey, key);
213
+ }
214
+ return this.getCustomClassMeta(target, key);
215
+ }
216
+ // SWC-emitted design:paramtypes — class-level for constructors, handler-level for methods.
217
+ static getParamTypes(target, propertyKey) {
218
+ return this.getReflectMetadata(target, 'design:paramtypes', propertyKey);
250
219
  }
251
220
  // Propagate all controller-level components from one class to another.
252
- // Used by ModuleLoader to apply module-level decorators to every controller.
253
221
  static propagateControllerComponents(from, to) {
254
- for (const [, typeMap] of this.controller.entries()){
255
- const components = typeMap.get(from);
222
+ for (const type of [
223
+ 'middleware',
224
+ 'guard',
225
+ 'pipe',
226
+ 'interceptor',
227
+ 'filter'
228
+ ]){
229
+ const map = this.controllerComponents[type];
230
+ const components = map.get(from);
256
231
  if (components && components.length > 0) {
257
- if (!typeMap.has(to)) {
258
- typeMap.set(to, []);
259
- }
260
- typeMap.get(to).push(...components);
232
+ const target = getOrCreateArray(map, to);
233
+ target.push(...components);
261
234
  }
262
235
  }
263
236
  }
264
- // Clear all (for testing)
237
+ // Clear app-time state. Decoration metadata persists — once a class is
238
+ // decorated, that fact is permanent for the lifetime of the process.
265
239
  static clear() {
240
+ this.globalComponents = emptyComponentStore();
241
+ }
242
+ // Full reset, including decoration metadata. Used in framework-internal scenarios.
243
+ static reset() {
266
244
  this.routes.clear();
267
245
  this.controllers.clear();
268
246
  this.controllerOptions.clear();
269
247
  this.modules.clear();
270
248
  this.parameters.clear();
271
- for (const set of this.global.values()){
272
- set.clear();
273
- }
274
- for (const map of this.controller.values()){
275
- map.clear();
276
- }
277
- for (const map of this.handler.values()){
278
- map.clear();
279
- }
280
249
  this.injectables.clear();
281
250
  this.scopes.clear();
282
251
  this.injectTokens.clear();
283
252
  this.handlerHttpMeta.clear();
284
253
  this.catchTypes.clear();
285
254
  this.routeVersions.clear();
286
- this.customClassMeta.clear();
287
- this.customHandlerMeta.clear();
255
+ this.classMeta.clear();
256
+ this.handlerMeta.clear();
257
+ for (const type of [
258
+ 'middleware',
259
+ 'guard',
260
+ 'pipe',
261
+ 'interceptor',
262
+ 'filter'
263
+ ]){
264
+ this.controllerComponents[type].clear();
265
+ this.handlerComponents[type].clear();
266
+ }
267
+ this.globalComponents = emptyComponentStore();
268
+ // reflectMeta is a WeakMap — entries die with their targets.
288
269
  }
289
270
  }
@@ -0,0 +1,3 @@
1
+ export declare function normalizePath(path: string): string;
2
+ export declare function joinPaths(prefix: string, path: string): string;
3
+ export declare function toOpenApiPath(path: string): string;
@@ -0,0 +1,15 @@
1
+ // Single home for path manipulation. Used by route building, openapi docs,
2
+ // and decorator metadata.
3
+ export function normalizePath(path) {
4
+ if (!path) return path;
5
+ return path.startsWith('/') ? path : `/${path}`;
6
+ }
7
+ export function joinPaths(prefix, path) {
8
+ const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
9
+ const cleanPath = path && !path.startsWith('/') ? `/${path}` : path;
10
+ return `${cleanPrefix}${cleanPath}` || '/';
11
+ }
12
+ // Convert ':id' Hono syntax to '{id}' OpenAPI syntax.
13
+ export function toOpenApiPath(path) {
14
+ return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');
15
+ }
@@ -1,7 +1,7 @@
1
1
  import type { Context } from 'hono';
2
2
  import type { CanActivate, ExceptionFilter, NestInterceptor, NestMiddleware, PipeTransform } from '../pipeline/types';
3
- export type Type<T = any> = new (...args: any[]) => T;
4
- export type Constructor = Function;
3
+ import type { Constructor, ForwardRef, InjectionToken, ProviderOptions, Token, Type } from '../container/types';
4
+ export type { Constructor, ForwardRef, InjectionToken, ProviderOptions, Token, Type };
5
5
  export type ComponentType = 'middleware' | 'guard' | 'pipe' | 'interceptor' | 'filter';
6
6
  export type MiddlewareType = Type<NestMiddleware> | NestMiddleware;
7
7
  export type GuardType = Type<CanActivate> | CanActivate;
@@ -37,21 +37,31 @@ export interface HttpHandlerMeta {
37
37
  statusCode: number;
38
38
  };
39
39
  }
40
+ export type ModuleImport = Type | DynamicModule | ForwardRef;
41
+ export interface AsyncModuleOptions<T = unknown> {
42
+ imports?: ModuleImport[];
43
+ useFactory: (...args: any[]) => T | Promise<T>;
44
+ inject?: Token[];
45
+ }
46
+ export interface DynamicModule {
47
+ module: Type;
48
+ imports?: ModuleImport[];
49
+ providers?: Array<Type | ProviderOptions>;
50
+ controllers?: Type[];
51
+ exports?: Array<Type | InjectionToken>;
52
+ global?: boolean;
53
+ }
40
54
  export interface ModuleOptions {
41
- imports?: unknown[];
42
55
  providers?: Array<Type | ProviderOptions>;
43
56
  controllers?: Type[];
44
- exports?: Array<Type | InjectionTokenLike>;
57
+ imports?: ModuleImport[];
58
+ exports?: Array<Type | InjectionToken>;
45
59
  isGlobal?: boolean;
46
60
  }
47
- export interface InjectionTokenLike {
48
- toString(): string;
49
- }
50
- export interface ProviderOptions<T = unknown> {
51
- token?: unknown;
52
- scope?: string;
53
- useValue?: T;
54
- useFactory?: (...args: unknown[]) => T | Promise<T>;
55
- inject?: unknown[];
56
- useExisting?: unknown;
61
+ export interface ModuleMetadata {
62
+ providers: Array<Type | ProviderOptions>;
63
+ controllers: Type[];
64
+ imports: ModuleImport[];
65
+ exports: Array<Type | InjectionToken>;
66
+ isGlobal: boolean;
57
67
  }
@@ -1,2 +1 @@
1
- // Forward-compatible with ProviderOptions
2
1
  export { };
@@ -0,0 +1,3 @@
1
+ export declare function getOrCreate<K, V>(map: Map<K, V>, key: K, factory: () => V): V;
2
+ export declare const getOrCreateMap: <K, NK, NV>(map: Map<K, Map<NK, NV>>, key: K) => Map<NK, NV>;
3
+ export declare const getOrCreateArray: <K, V>(map: Map<K, V[]>, key: K) => V[];
@@ -0,0 +1,8 @@
1
+ // Single map.get + optional set. Replaces `map.has(key) || map.set(key, …); map.get(key)!` patterns.
2
+ export function getOrCreate(map, key, factory) {
3
+ let v = map.get(key);
4
+ if (v === undefined) map.set(key, v = factory());
5
+ return v;
6
+ }
7
+ export const getOrCreateMap = (map, key)=>getOrCreate(map, key, ()=>new Map());
8
+ export const getOrCreateArray = (map, key)=>getOrCreate(map, key, ()=>[]);