@velajs/vela 1.0.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 (71) hide show
  1. package/CHANGELOG.md +80 -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 +8 -8
  7. package/dist/constants.js +8 -8
  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 +19 -25
  28. package/dist/http/types.d.ts +0 -1
  29. package/dist/index.d.ts +2 -8
  30. package/dist/index.js +6 -11
  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 -14
  41. package/dist/{http/middleware-consumer.js → module/middleware.js} +0 -12
  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/decorators.d.ts +5 -5
  50. package/dist/pipeline/decorators.js +1 -9
  51. package/dist/pipeline/reflector.d.ts +8 -24
  52. package/dist/pipeline/reflector.js +10 -55
  53. package/dist/registry/index.d.ts +1 -1
  54. package/dist/registry/metadata.registry.d.ts +18 -13
  55. package/dist/registry/metadata.registry.js +133 -159
  56. package/dist/registry/paths.d.ts +3 -0
  57. package/dist/registry/paths.js +15 -0
  58. package/dist/registry/types.d.ts +24 -14
  59. package/dist/registry/types.js +0 -1
  60. package/dist/registry/util.d.ts +3 -0
  61. package/dist/registry/util.js +8 -0
  62. package/dist/schedule/schedule.decorators.js +3 -6
  63. package/dist/schedule/schedule.registry.js +3 -2
  64. package/dist/throttler/throttler.decorators.d.ts +2 -2
  65. package/package.json +5 -9
  66. package/dist/testing/index.d.ts +0 -2
  67. package/dist/testing/index.js +0 -2
  68. package/dist/testing/testing.builder.d.ts +0 -29
  69. package/dist/testing/testing.builder.js +0 -148
  70. package/dist/testing/testing.module.d.ts +0 -9
  71. package/dist/testing/testing.module.js +0 -15
@@ -1,14 +1,4 @@
1
1
  import { MetadataRegistry } from "../registry/metadata.registry.js";
2
- export const RequestMethod = {
3
- GET: 'GET',
4
- POST: 'POST',
5
- PUT: 'PUT',
6
- DELETE: 'DELETE',
7
- PATCH: 'PATCH',
8
- OPTIONS: 'OPTIONS',
9
- HEAD: 'HEAD',
10
- ALL: 'ALL'
11
- };
12
2
  export class MiddlewareBuilder {
13
3
  definitions = [];
14
4
  apply(...middleware) {
@@ -26,7 +16,6 @@ export class MiddlewareBuilder {
26
16
  currentPriority = priority;
27
17
  return proxy;
28
18
  },
29
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
30
19
  forRoutes: (...routes)=>{
31
20
  this.definitions.push({
32
21
  middleware: currentMiddleware,
@@ -56,7 +45,6 @@ function normalizeRouteArg(route) {
56
45
  path: route
57
46
  } : route;
58
47
  }
59
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
60
48
  function resolveRouteArg(route) {
61
49
  if (typeof route === 'string') {
62
50
  return [
@@ -1,7 +1,7 @@
1
1
  import type { Container } from "../container/container";
2
2
  import type { Token, Type } from "../container/types";
3
- import type { MiddlewareRouteDefinition } from "../http/middleware-consumer";
4
3
  import type { RouteManager } from "../http/route.manager";
4
+ import type { MiddlewareRouteDefinition } from "./middleware";
5
5
  export declare class ModuleLoader {
6
6
  private container;
7
7
  private router;
@@ -1,7 +1,10 @@
1
1
  import { Scope } from "../constants.js";
2
2
  import { ForwardRef, InjectionToken } from "../container/types.js";
3
- import { MiddlewareBuilder } from "../http/middleware-consumer.js";
4
3
  import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "../pipeline/tokens.js";
4
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
5
+ import { getOrCreateArray } from "../registry/util.js";
6
+ import { getModuleMetadata, isModule } from "./decorators.js";
7
+ import { MiddlewareBuilder } from "./middleware.js";
5
8
  const APP_TOKENS = new Set([
6
9
  APP_GUARD,
7
10
  APP_PIPE,
@@ -9,11 +12,12 @@ const APP_TOKENS = new Set([
9
12
  APP_FILTER,
10
13
  APP_MIDDLEWARE
11
14
  ]);
12
- import { MetadataRegistry } from "../registry/metadata.registry.js";
13
- import { getModuleMetadata, isModule } from "./decorators.js";
14
15
  function isDynamicModule(value) {
15
16
  return typeof value === "object" && value !== null && "module" in value && typeof value.module === "function";
16
17
  }
18
+ function implementsNestModule(cls) {
19
+ return typeof cls.prototype?.configure === "function";
20
+ }
17
21
  export class ModuleLoader {
18
22
  container;
19
23
  router;
@@ -152,16 +156,16 @@ export class ModuleLoader {
152
156
  this.globalExports.add(token);
153
157
  }
154
158
  }
155
- // Call configure() if the module implements NestModule
156
- if (typeof moduleClass.prototype?.configure === "function") {
157
- try {
158
- const instance = new moduleClass();
159
- const builder = new MiddlewareBuilder();
160
- instance.configure(builder);
161
- this.consumerMiddlewareDefinitions.push(...builder.getDefinitions());
162
- } catch {
163
- // Module has constructor dependencies — configure() skipped
159
+ // Call configure() if the module implements NestModule. The module is
160
+ // resolved through the container so it can have its own DI dependencies.
161
+ if (implementsNestModule(moduleClass)) {
162
+ if (!this.container.has(moduleClass)) {
163
+ this.container.register(moduleClass);
164
164
  }
165
+ const instance = this.container.resolve(moduleClass);
166
+ const builder = new MiddlewareBuilder();
167
+ instance.configure(builder);
168
+ this.consumerMiddlewareDefinitions.push(...builder.getDefinitions());
165
169
  }
166
170
  return exports;
167
171
  } finally{
@@ -187,7 +191,7 @@ export class ModuleLoader {
187
191
  provide: syntheticToken
188
192
  });
189
193
  this.registeredProviders.push(syntheticToken);
190
- this.appProviderTokens.get(token).push(syntheticToken);
194
+ getOrCreateArray(this.appProviderTokens, token).push(syntheticToken);
191
195
  return;
192
196
  }
193
197
  if (!this.container.has(token)) {
@@ -1,29 +1 @@
1
- import type { ForwardRef, InjectionToken, ProviderOptions, Token, Type } from '../container/types';
2
- export type ModuleImport = Type | DynamicModule | ForwardRef;
3
- export interface AsyncModuleOptions<T = unknown> {
4
- imports?: ModuleImport[];
5
- useFactory: (...args: any[]) => T | Promise<T>;
6
- inject?: Token[];
7
- }
8
- export interface DynamicModule {
9
- module: Type;
10
- imports?: ModuleImport[];
11
- providers?: Array<Type | ProviderOptions>;
12
- controllers?: Type[];
13
- exports?: Array<Type | InjectionToken>;
14
- global?: boolean;
15
- }
16
- export interface ModuleOptions {
17
- providers?: Array<Type | ProviderOptions>;
18
- controllers?: Type[];
19
- imports?: ModuleImport[];
20
- exports?: Array<Type | InjectionToken>;
21
- isGlobal?: boolean;
22
- }
23
- export interface ModuleMetadata {
24
- providers: Array<Type | ProviderOptions>;
25
- controllers: Type[];
26
- imports: ModuleImport[];
27
- exports: Array<Type | InjectionToken>;
28
- isGlobal: boolean;
29
- }
1
+ export type { AsyncModuleOptions, DynamicModule, ModuleImport, ModuleMetadata, ModuleOptions, } from '../registry/types';
@@ -1,3 +1,4 @@
1
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
1
2
  export const API_DOC_METADATA = 'vela:openapi:doc';
2
3
  export const API_TAGS_METADATA = 'vela:openapi:tags';
3
4
  export const API_RESPONSES_METADATA = 'vela:openapi:responses';
@@ -12,9 +13,9 @@ export const API_RESPONSES_METADATA = 'vela:openapi:responses';
12
13
  */ export function ApiDoc(metadata) {
13
14
  return (target, propertyKey)=>{
14
15
  if (propertyKey !== undefined) {
15
- Reflect.defineMetadata(API_DOC_METADATA, metadata, target, propertyKey);
16
+ MetadataRegistry.setCustomHandlerMeta(target.constructor, propertyKey, API_DOC_METADATA, metadata);
16
17
  } else {
17
- Reflect.defineMetadata(API_DOC_METADATA, metadata, target);
18
+ MetadataRegistry.setCustomClassMeta(target, API_DOC_METADATA, metadata);
18
19
  }
19
20
  };
20
21
  }
@@ -24,17 +25,23 @@ export const API_RESPONSES_METADATA = 'vela:openapi:responses';
24
25
  */ export function ApiTags(...tags) {
25
26
  return (target, propertyKey)=>{
26
27
  if (propertyKey !== undefined) {
27
- Reflect.defineMetadata(API_TAGS_METADATA, tags, target, propertyKey);
28
+ MetadataRegistry.setCustomHandlerMeta(target.constructor, propertyKey, API_TAGS_METADATA, tags);
28
29
  } else {
29
- Reflect.defineMetadata(API_TAGS_METADATA, tags, target);
30
+ MetadataRegistry.setCustomClassMeta(target, API_TAGS_METADATA, tags);
30
31
  }
31
32
  };
32
33
  }
33
34
  export function getApiDoc(target, propertyKey) {
34
- return propertyKey !== undefined ? Reflect.getMetadata(API_DOC_METADATA, target, propertyKey) : Reflect.getMetadata(API_DOC_METADATA, target);
35
+ if (propertyKey !== undefined) {
36
+ return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_DOC_METADATA);
37
+ }
38
+ return MetadataRegistry.getCustomClassMeta(target, API_DOC_METADATA);
35
39
  }
36
40
  export function getApiTags(target, propertyKey) {
37
- return propertyKey !== undefined ? Reflect.getMetadata(API_TAGS_METADATA, target, propertyKey) : Reflect.getMetadata(API_TAGS_METADATA, target);
41
+ if (propertyKey !== undefined) {
42
+ return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_TAGS_METADATA);
43
+ }
44
+ return MetadataRegistry.getCustomClassMeta(target, API_TAGS_METADATA);
38
45
  }
39
46
  /**
40
47
  * Document a response for a given status code. Stackable: apply multiple
@@ -48,14 +55,12 @@ export function getApiTags(target, propertyKey) {
48
55
  * ```
49
56
  */ export function ApiResponse(status, options) {
50
57
  return (target, propertyKey)=>{
51
- const existing = Reflect.getMetadata(API_RESPONSES_METADATA, target, propertyKey) ?? [];
52
- existing.push({
58
+ MetadataRegistry.appendCustomHandlerMeta(target.constructor, propertyKey, API_RESPONSES_METADATA, {
53
59
  status,
54
60
  ...options
55
61
  });
56
- Reflect.defineMetadata(API_RESPONSES_METADATA, existing, target, propertyKey);
57
62
  };
58
63
  }
59
64
  export function getApiResponses(target, propertyKey) {
60
- return Reflect.getMetadata(API_RESPONSES_METADATA, target, propertyKey);
65
+ return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_RESPONSES_METADATA);
61
66
  }
@@ -1,44 +1,9 @@
1
- import { getModuleMetadata } from "../module/decorators.js";
2
- import { ForwardRef } from "../container/types.js";
1
+ import { collectControllers } from "../module/graph.js";
3
2
  import { MetadataRegistry } from "../registry/metadata.registry.js";
3
+ import { joinPaths, toOpenApiPath } from "../registry/paths.js";
4
4
  import { ParamType } from "../constants.js";
5
5
  import { getApiDoc, getApiResponses, getApiTags } from "./decorators.js";
6
6
  import { isOptional, zodToJsonSchema } from "./zod-to-json-schema.js";
7
- function isDynamicModuleLike(v) {
8
- return !!v && typeof v === 'object' && 'module' in v && typeof v.module === 'function';
9
- }
10
- function collectControllers(rootModule) {
11
- const visited = new Set();
12
- const controllers = new Set();
13
- function visit(entry) {
14
- const unwrapped = entry instanceof ForwardRef ? entry.factory() : entry;
15
- const moduleClass = isDynamicModuleLike(unwrapped) ? unwrapped.module : unwrapped;
16
- const extraControllers = isDynamicModuleLike(unwrapped) ? unwrapped.controllers ?? [] : [];
17
- const extraImports = isDynamicModuleLike(unwrapped) ? unwrapped.imports ?? [] : [];
18
- if (typeof moduleClass !== 'function' || visited.has(moduleClass)) return;
19
- visited.add(moduleClass);
20
- const metadata = getModuleMetadata(moduleClass);
21
- if (metadata) {
22
- for (const controller of metadata.controllers)controllers.add(controller);
23
- for (const imp of metadata.imports)visit(imp);
24
- }
25
- for (const controller of extraControllers)controllers.add(controller);
26
- for (const imp of extraImports)visit(imp);
27
- }
28
- visit(rootModule);
29
- return [
30
- ...controllers
31
- ];
32
- }
33
- function normalizePath(path) {
34
- return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');
35
- }
36
- function joinPath(a, b) {
37
- const left = a.endsWith('/') ? a.slice(0, -1) : a;
38
- const right = b && !b.startsWith('/') ? `/${b}` : b;
39
- const joined = `${left}${right}`;
40
- return joined || '/';
41
- }
42
7
  /**
43
8
  * Tracks DTO classes referenced during document generation and registers
44
9
  * each under `components.schemas`. Handles name collisions by suffixing.
@@ -176,9 +141,9 @@ function buildOperation(controller, route, pathString, registry) {
176
141
  });
177
142
  }
178
143
  }
179
- const docMeta = getApiDoc(controller.prototype, handlerName);
144
+ const docMeta = getApiDoc(controller, handlerName);
180
145
  const controllerTags = getApiTags(controller) ?? [];
181
- const handlerTags = getApiTags(controller.prototype, handlerName) ?? [];
146
+ const handlerTags = getApiTags(controller, handlerName) ?? [];
182
147
  const docTags = docMeta?.tags ?? [];
183
148
  const mergedTags = [
184
149
  ...new Set([
@@ -192,7 +157,7 @@ function buildOperation(controller, route, pathString, registry) {
192
157
  description: 'OK'
193
158
  }
194
159
  };
195
- const apiResponses = getApiResponses(controller.prototype, handlerName) ?? [];
160
+ const apiResponses = getApiResponses(controller, handlerName) ?? [];
196
161
  for (const entry of apiResponses){
197
162
  const key = String(entry.status);
198
163
  const resolved = {
@@ -239,8 +204,8 @@ export function createOpenApiDocument(rootModule, options = {}) {
239
204
  for (const route of routes){
240
205
  const method = route.method.toLowerCase();
241
206
  if (!VERB_WHITELIST.has(method)) continue;
242
- const rawPath = joinPath(joinPath(globalPrefix, controllerPath), route.path);
243
- const pathString = normalizePath(rawPath);
207
+ const rawPath = joinPaths(joinPaths(globalPrefix, controllerPath), route.path);
208
+ const pathString = toOpenApiPath(rawPath);
244
209
  const operation = buildOperation(controller, route, pathString, registry);
245
210
  const pathItem = paths[pathString] ?? {};
246
211
  pathItem[method] = operation;
@@ -0,0 +1,10 @@
1
+ import type { Container } from '../container/container';
2
+ import type { RouteManager } from '../http/route.manager';
3
+ import type { ModuleLoader } from '../module/module-loader';
4
+ /**
5
+ * Bind every APP_* provider registered in the container as a global
6
+ * component on the RouteManager. The single source of truth for app-level
7
+ * pipeline configuration — used by both VelaFactory.create and
8
+ * @velajs/testing's TestingModuleBuilder.compile.
9
+ */
10
+ export declare function bindAppProviders(routeManager: RouteManager, container: Container, loader: ModuleLoader): void;
@@ -0,0 +1,43 @@
1
+ import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "./tokens.js";
2
+ // NestJS-style "APP_*" provider convention: each slot maps a well-known DI
3
+ // token to the RouteManager method that installs providers under it as a
4
+ // global component. Module-level @Provide(APP_*) entries expand into
5
+ // synthetic tokens (collected by ModuleLoader); a single @Inject(APP_*)
6
+ // provider falls back to the bare token.
7
+ const SLOTS = [
8
+ {
9
+ token: APP_GUARD,
10
+ bind: (rm)=>rm.useGlobalGuardTokens.bind(rm)
11
+ },
12
+ {
13
+ token: APP_PIPE,
14
+ bind: (rm)=>rm.useGlobalPipeTokens.bind(rm)
15
+ },
16
+ {
17
+ token: APP_INTERCEPTOR,
18
+ bind: (rm)=>rm.useGlobalInterceptorTokens.bind(rm)
19
+ },
20
+ {
21
+ token: APP_FILTER,
22
+ bind: (rm)=>rm.useGlobalFilterTokens.bind(rm)
23
+ },
24
+ {
25
+ token: APP_MIDDLEWARE,
26
+ bind: (rm)=>rm.useGlobalMiddlewareTokens.bind(rm)
27
+ }
28
+ ];
29
+ /**
30
+ * Bind every APP_* provider registered in the container as a global
31
+ * component on the RouteManager. The single source of truth for app-level
32
+ * pipeline configuration — used by both VelaFactory.create and
33
+ * @velajs/testing's TestingModuleBuilder.compile.
34
+ */ export function bindAppProviders(routeManager, container, loader) {
35
+ for (const { token, bind } of SLOTS){
36
+ const collected = loader.getAppProviderTokens(token);
37
+ if (collected.length > 0) {
38
+ bind(routeManager)(...collected);
39
+ } else if (container.has(token)) {
40
+ bind(routeManager)(token);
41
+ }
42
+ }
43
+ }
@@ -1,10 +1,10 @@
1
1
  import type { Type } from '../container/types';
2
2
  import type { ComponentTypeMap } from '../registry/types';
3
- export declare function UseMiddleware(...middleware: ComponentTypeMap['middleware'][]): (target: Function | object, propertyKey?: string | symbol) => void;
4
- export declare function UseGuards(...guards: ComponentTypeMap['guard'][]): (target: Function | object, propertyKey?: string | symbol) => void;
5
- export declare function UsePipes(...pipes: ComponentTypeMap['pipe'][]): (target: Function | object, propertyKey?: string | symbol) => void;
6
- export declare function UseInterceptors(...interceptors: ComponentTypeMap['interceptor'][]): (target: Function | object, propertyKey?: string | symbol) => void;
7
- export declare function UseFilters(...filters: ComponentTypeMap['filter'][]): (target: Function | object, propertyKey?: string | symbol) => void;
3
+ export declare function UseMiddleware(...middleware: ComponentTypeMap['middleware'][]): (target: object, propertyKey?: string | symbol) => void;
4
+ export declare function UseGuards(...guards: ComponentTypeMap['guard'][]): (target: object, propertyKey?: string | symbol) => void;
5
+ export declare function UsePipes(...pipes: ComponentTypeMap['pipe'][]): (target: object, propertyKey?: string | symbol) => void;
6
+ export declare function UseInterceptors(...interceptors: ComponentTypeMap['interceptor'][]): (target: object, propertyKey?: string | symbol) => void;
7
+ export declare function UseFilters(...filters: ComponentTypeMap['filter'][]): (target: object, propertyKey?: string | symbol) => void;
8
8
  export declare function Catch(...exceptions: Type<Error>[]): ClassDecorator;
9
9
  export declare function getCatchTypes(filter: unknown): Type<Error>[];
10
10
  export declare function shouldFilterCatch(filter: unknown, exception: unknown): boolean;
@@ -1,15 +1,10 @@
1
- import { METADATA_KEYS } from "../constants.js";
2
- import { getMetadata } from "../metadata.js";
3
1
  import { ComponentManager } from "./component.manager.js";
4
2
  import { MetadataRegistry } from "../registry/metadata.registry.js";
5
3
  function UseComponent(type, ...components) {
6
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
7
4
  return (target, propertyKey)=>{
8
5
  if (propertyKey !== undefined) {
9
- // Method decorator — target is prototype, target.constructor is the class
10
6
  ComponentManager.registerHandler(type, target.constructor, propertyKey, ...components);
11
7
  } else {
12
- // Class decorator — target is the class itself
13
8
  ComponentManager.registerController(type, target, ...components);
14
9
  }
15
10
  };
@@ -36,10 +31,7 @@ export function Catch(...exceptions) {
36
31
  }
37
32
  export function getCatchTypes(filter) {
38
33
  const filterClass = typeof filter === 'function' ? filter : filter.constructor;
39
- if (MetadataRegistry.hasCatchTypes(filterClass)) {
40
- return MetadataRegistry.getCatchTypes(filterClass) ?? [];
41
- }
42
- return getMetadata(METADATA_KEYS.CATCH, filterClass) ?? [];
34
+ return MetadataRegistry.getCatchTypes(filterClass) ?? [];
43
35
  }
44
36
  export function shouldFilterCatch(filter, exception) {
45
37
  const catchTypes = getCatchTypes(filter);
@@ -1,3 +1,4 @@
1
+ import type { ExecutionContext } from './types';
1
2
  export interface CreateDecoratorOptions {
2
3
  key?: string;
3
4
  }
@@ -18,7 +19,7 @@ export interface ReflectableDecorator<TParam> {
18
19
  * class AdminController { ... }
19
20
  * ```
20
21
  */
21
- export declare function SetMetadata<V = unknown>(key: string, value: V): (target: Function | object, propertyKey?: string | symbol) => void;
22
+ export declare function SetMetadata<V = unknown>(key: string, value: V): (target: object, propertyKey?: string | symbol) => void;
22
23
  /**
23
24
  * Reads custom metadata set by @SetMetadata().
24
25
  * Use in guards/interceptors via ExecutionContext.
@@ -54,39 +55,25 @@ export declare class Reflector {
54
55
  * Get metadata value from handler first, then class.
55
56
  * Handler-level metadata takes priority over class-level.
56
57
  */
57
- get<T = unknown>(key: string | ReflectableDecorator<T>, context: {
58
- getClass(): Function;
59
- getHandler(): string | symbol;
60
- }): T | undefined;
58
+ get<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
61
59
  /**
62
60
  * Get metadata only from the handler (method) level.
63
61
  */
64
- getHandler<T = unknown>(key: string | ReflectableDecorator<T>, context: {
65
- getClass(): Function;
66
- getHandler(): string | symbol;
67
- }): T | undefined;
62
+ getHandler<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
68
63
  /**
69
64
  * Get metadata only from the class level.
70
65
  */
71
- getClass<T = unknown>(key: string | ReflectableDecorator<T>, context: {
72
- getClass(): Function;
73
- }): T | undefined;
66
+ getClass<T = unknown>(key: string | ReflectableDecorator<T>, context: Pick<ExecutionContext, "getClass">): T | undefined;
74
67
  /**
75
68
  * Get all metadata values for a key from both handler and class.
76
69
  * Returns [handlerValue, classValue] (either may be undefined).
77
70
  */
78
- getAll<T = unknown>(key: string | ReflectableDecorator<T>, context: {
79
- getClass(): Function;
80
- getHandler(): string | symbol;
81
- }): [T | undefined, T | undefined];
71
+ getAll<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): [T | undefined, T | undefined];
82
72
  /**
83
73
  * Returns handler value if defined, else class value, else undefined.
84
74
  * First defined value wins.
85
75
  */
86
- getAllAndOverride<T = unknown>(key: string | ReflectableDecorator<T>, context: {
87
- getClass(): Function;
88
- getHandler(): string | symbol;
89
- }): T | undefined;
76
+ getAllAndOverride<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
90
77
  /**
91
78
  * Collects handler + class values, filters undefineds.
92
79
  * - 0 values → []
@@ -95,8 +82,5 @@ export declare class Reflector {
95
82
  * - Objects → Object.assign({}, ...values)
96
83
  * - Otherwise → wrap in array
97
84
  */
98
- getAllAndMerge<T = unknown>(key: string | ReflectableDecorator<T>, context: {
99
- getClass(): Function;
100
- getHandler(): string | symbol;
101
- }): T | T[];
85
+ getAllAndMerge<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | T[];
102
86
  }
@@ -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;
@@ -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
  }