@velajs/vela 0.4.3 → 0.5.1

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 (68) hide show
  1. package/dist/cache/cache.module.d.ts +4 -1
  2. package/dist/cache/cache.module.js +51 -9
  3. package/dist/config/config.module.d.ts +4 -1
  4. package/dist/config/config.module.js +40 -3
  5. package/dist/config/config.types.d.ts +1 -0
  6. package/dist/constants.d.ts +8 -2
  7. package/dist/constants.js +6 -0
  8. package/dist/container/container.d.ts +2 -0
  9. package/dist/container/container.js +51 -16
  10. package/dist/container/decorators.d.ts +3 -1
  11. package/dist/container/decorators.js +27 -6
  12. package/dist/container/index.d.ts +4 -2
  13. package/dist/container/index.js +4 -2
  14. package/dist/container/mixin.d.ts +24 -0
  15. package/dist/container/mixin.js +26 -0
  16. package/dist/container/module-ref.d.ts +21 -0
  17. package/dist/container/module-ref.js +48 -0
  18. package/dist/container/types.d.ts +9 -3
  19. package/dist/container/types.js +9 -0
  20. package/dist/cors/cors.module.js +2 -2
  21. package/dist/errors/http-exception.d.ts +20 -20
  22. package/dist/errors/http-exception.js +47 -41
  23. package/dist/factory.js +11 -2
  24. package/dist/fetch/fetch.module.d.ts +6 -0
  25. package/dist/fetch/fetch.module.js +77 -0
  26. package/dist/fetch/fetch.service.d.ts +24 -0
  27. package/dist/fetch/fetch.service.js +145 -0
  28. package/dist/fetch/fetch.types.d.ts +24 -0
  29. package/dist/fetch/fetch.types.js +1 -0
  30. package/dist/fetch/index.d.ts +3 -0
  31. package/dist/fetch/index.js +2 -0
  32. package/dist/health/health.service.js +2 -2
  33. package/dist/http/decorators.d.ts +65 -0
  34. package/dist/http/decorators.js +91 -2
  35. package/dist/http/index.d.ts +3 -1
  36. package/dist/http/index.js +2 -1
  37. package/dist/http/middleware-consumer.d.ts +36 -0
  38. package/dist/http/middleware-consumer.js +71 -0
  39. package/dist/http/route.manager.d.ts +4 -0
  40. package/dist/http/route.manager.js +91 -6
  41. package/dist/http/types.d.ts +1 -0
  42. package/dist/index.d.ts +11 -6
  43. package/dist/index.js +7 -4
  44. package/dist/metadata.d.ts +1 -1
  45. package/dist/module/decorators.d.ts +1 -0
  46. package/dist/module/decorators.js +24 -8
  47. package/dist/module/index.d.ts +2 -2
  48. package/dist/module/index.js +1 -1
  49. package/dist/module/module-loader.d.ts +5 -1
  50. package/dist/module/module-loader.js +54 -10
  51. package/dist/module/types.d.ts +13 -3
  52. package/dist/pipeline/index.d.ts +3 -2
  53. package/dist/pipeline/index.js +1 -1
  54. package/dist/pipeline/pipes.d.ts +22 -0
  55. package/dist/pipeline/pipes.js +50 -0
  56. package/dist/pipeline/tokens.d.ts +1 -1
  57. package/dist/pipeline/tokens.js +1 -1
  58. package/dist/pipeline/types.d.ts +16 -0
  59. package/dist/registry/metadata.registry.d.ts +7 -6
  60. package/dist/registry/metadata.registry.js +13 -0
  61. package/dist/registry/types.d.ts +1 -0
  62. package/dist/schedule/schedule.module.d.ts +4 -1
  63. package/dist/schedule/schedule.module.js +39 -7
  64. package/dist/testing/testing.builder.js +5 -5
  65. package/dist/throttler/throttler.module.d.ts +2 -1
  66. package/dist/throttler/throttler.module.js +47 -9
  67. package/dist/validation/validation.pipe.js +1 -1
  68. package/package.json +1 -1
@@ -1,32 +1,48 @@
1
1
  import { METADATA_KEYS } from "../constants.js";
2
- import { getMetadata } from "../metadata.js";
2
+ import { defineMetadata, getMetadata } from "../metadata.js";
3
3
  import { MetadataRegistry } from "../registry/metadata.registry.js";
4
- export function Module(options = {}) {
4
+ export function Global() {
5
5
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
6
6
  return (target)=>{
7
+ const existing = MetadataRegistry.getModuleOptions(target);
7
8
  MetadataRegistry.setModuleOptions(target, {
9
+ ...existing,
10
+ isGlobal: true
11
+ });
12
+ };
13
+ }
14
+ export function Module(options = {}) {
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
16
+ return (target)=>{
17
+ const normalized = {
8
18
  imports: options.imports,
9
19
  providers: options.providers,
10
20
  controllers: options.controllers,
11
- exports: options.exports
12
- });
21
+ exports: options.exports,
22
+ isGlobal: options.isGlobal
23
+ };
24
+ // WeakMap store survives MetadataRegistry.clear() — needed for framework modules
25
+ // decorated at import time (e.g. HttpModule, CorsModule).
26
+ defineMetadata(METADATA_KEYS.MODULE_OPTIONS, normalized, target);
27
+ MetadataRegistry.setModuleOptions(target, normalized);
13
28
  };
14
29
  }
15
30
  export function isModule(target) {
16
- return MetadataRegistry.getModuleOptions(target) !== undefined || getMetadata(METADATA_KEYS.MODULE, target) === true;
31
+ return MetadataRegistry.getModuleOptions(target) !== undefined || getMetadata(METADATA_KEYS.MODULE, target) === true || getMetadata(METADATA_KEYS.MODULE_OPTIONS, target) !== undefined;
17
32
  }
18
33
  export function getModuleMetadata(target) {
19
34
  if (!isModule(target)) {
20
35
  return undefined;
21
36
  }
22
- const options = MetadataRegistry.getModuleOptions(target);
23
- if (!options) {
37
+ const options = MetadataRegistry.getModuleOptions(target) ?? getMetadata(METADATA_KEYS.MODULE_OPTIONS, target);
38
+ if (!options || typeof options !== 'object') {
24
39
  return undefined;
25
40
  }
26
41
  return {
27
42
  providers: options.providers ?? [],
28
43
  controllers: options.controllers ?? [],
29
44
  imports: options.imports ?? [],
30
- exports: options.exports ?? []
45
+ exports: options.exports ?? [],
46
+ isGlobal: options.isGlobal === true
31
47
  };
32
48
  }
@@ -1,3 +1,3 @@
1
- export { Module, isModule, getModuleMetadata } from './decorators';
1
+ export { Global, Module, isModule, getModuleMetadata } from './decorators';
2
2
  export { ModuleLoader } from './module-loader';
3
- export type { ModuleOptions, ModuleMetadata, DynamicModule } from './types';
3
+ export type { ModuleOptions, ModuleMetadata, DynamicModule, AsyncModuleOptions, ModuleImport } from './types';
@@ -1,2 +1,2 @@
1
- export { Module, isModule, getModuleMetadata } from "./decorators.js";
1
+ export { Global, Module, isModule, getModuleMetadata } from "./decorators.js";
2
2
  export { ModuleLoader } from "./module-loader.js";
@@ -1,5 +1,6 @@
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';
3
4
  import type { RouteManager } from '../http/route.manager';
4
5
  export declare class ModuleLoader {
5
6
  private container;
@@ -9,6 +10,8 @@ export declare class ModuleLoader {
9
10
  private collectedControllers;
10
11
  private registeredProviders;
11
12
  private moduleExportsCache;
13
+ private globalExports;
14
+ private consumerMiddlewareDefinitions;
12
15
  private appProviderCounter;
13
16
  private appProviderTokens;
14
17
  constructor(container: Container, router: RouteManager);
@@ -20,5 +23,6 @@ export declare class ModuleLoader {
20
23
  getControllers(): Type[];
21
24
  getRegisteredProviders(): Token[];
22
25
  getAppProviderTokens(token: Token): Token[];
23
- resolveAllInstances(): unknown[];
26
+ getConsumerMiddlewareDefinitions(): MiddlewareRouteDefinition[];
27
+ resolveAllInstances(): Promise<unknown[]>;
24
28
  }
@@ -1,6 +1,8 @@
1
1
  import { Scope } from "../constants.js";
2
- import { InjectionToken } from "../container/types.js";
2
+ import { ForwardRef, InjectionToken } from "../container/types.js";
3
+ import { MiddlewareBuilder } from "../http/middleware-consumer.js";
3
4
  import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "../pipeline/tokens.js";
5
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
4
6
  import { getModuleMetadata, isModule } from "./decorators.js";
5
7
  function isDynamicModule(value) {
6
8
  return typeof value === 'object' && value !== null && 'module' in value && typeof value.module === 'function';
@@ -13,6 +15,8 @@ export class ModuleLoader {
13
15
  collectedControllers = [];
14
16
  registeredProviders = [];
15
17
  moduleExportsCache = new Map();
18
+ globalExports = new Set();
19
+ consumerMiddlewareDefinitions = [];
16
20
  appProviderCounter = 0;
17
21
  appProviderTokens = new Map([
18
22
  [
@@ -47,12 +51,14 @@ export class ModuleLoader {
47
51
  }
48
52
  }
49
53
  processModule(moduleClassOrDynamic) {
50
- // Handle dynamic modules ({ module, controllers, providers })
54
+ // Handle dynamic modules ({ module, imports, controllers, providers })
51
55
  let moduleClass;
56
+ let extraImports = [];
52
57
  let extraControllers = [];
53
58
  let extraProviders = [];
54
59
  if (isDynamicModule(moduleClassOrDynamic)) {
55
60
  moduleClass = moduleClassOrDynamic.module;
61
+ extraImports = moduleClassOrDynamic.imports ?? [];
56
62
  extraControllers = moduleClassOrDynamic.controllers ?? [];
57
63
  extraProviders = moduleClassOrDynamic.providers ?? [];
58
64
  } else {
@@ -83,8 +89,20 @@ export class ModuleLoader {
83
89
  }
84
90
  this.processingStack.add(moduleClass);
85
91
  try {
86
- const importedProviders = new Set();
87
- for (const importedModule of metadata.imports){
92
+ const importedProviders = new Set(this.globalExports);
93
+ for (const entry of [
94
+ ...metadata.imports,
95
+ ...extraImports
96
+ ]){
97
+ // Unwrap forwardRef(() => Module) — resolves lazy circular references
98
+ const isForwardRef = entry instanceof ForwardRef;
99
+ const importedModule = isForwardRef ? entry.factory() : entry;
100
+ const importedModuleClass = isDynamicModule(importedModule) ? importedModule.module : importedModule;
101
+ // If this forwardRef-wrapped import is currently being processed, skip it to
102
+ // break the circular chain. Non-forwardRef circular imports still throw.
103
+ if (isForwardRef && this.processingStack.has(importedModuleClass)) {
104
+ continue;
105
+ }
88
106
  const exportedTokens = this.processModule(importedModule);
89
107
  for (const token of exportedTokens){
90
108
  importedProviders.add(token);
@@ -108,9 +126,30 @@ export class ModuleLoader {
108
126
  this.collectedControllers.push(controller);
109
127
  }
110
128
  }
129
+ // Propagate module-level Use* decorators (@UseGuards, @UseInterceptors, etc.) to each controller
130
+ for (const controller of allControllers){
131
+ MetadataRegistry.propagateControllerComponents(moduleClass, controller);
132
+ }
111
133
  this.processedModules.add(moduleClass);
112
134
  const exports = this.buildExportSet(metadata.exports, allProviders, importedProviders);
113
135
  this.moduleExportsCache.set(moduleClass, exports);
136
+ const isGlobal = metadata.isGlobal || isDynamicModule(moduleClassOrDynamic) && moduleClassOrDynamic.global === true;
137
+ if (isGlobal) {
138
+ for (const token of exports){
139
+ this.globalExports.add(token);
140
+ }
141
+ }
142
+ // Call configure() if the module implements NestModule
143
+ if (typeof moduleClass.prototype?.configure === 'function') {
144
+ try {
145
+ const instance = new moduleClass();
146
+ const builder = new MiddlewareBuilder();
147
+ instance.configure(builder);
148
+ this.consumerMiddlewareDefinitions.push(...builder.getDefinitions());
149
+ } catch {
150
+ // Module has constructor dependencies — configure() skipped
151
+ }
152
+ }
114
153
  return exports;
115
154
  } finally{
116
155
  this.processingStack.delete(moduleClass);
@@ -123,7 +162,7 @@ export class ModuleLoader {
123
162
  this.registeredProviders.push(provider);
124
163
  }
125
164
  } else {
126
- const token = provider.token;
165
+ const token = provider.provide;
127
166
  if (!token) {
128
167
  this.container.register(provider);
129
168
  return;
@@ -132,7 +171,7 @@ export class ModuleLoader {
132
171
  const syntheticToken = new InjectionToken(`${token.toString()}:${this.appProviderCounter++}`);
133
172
  this.container.register({
134
173
  ...provider,
135
- token: syntheticToken
174
+ provide: syntheticToken
136
175
  });
137
176
  this.registeredProviders.push(syntheticToken);
138
177
  this.appProviderTokens.get(token).push(syntheticToken);
@@ -154,7 +193,7 @@ export class ModuleLoader {
154
193
  if (typeof p === 'function') {
155
194
  return p === exported;
156
195
  }
157
- return p.token === exported;
196
+ return p.provide === exported;
158
197
  });
159
198
  const isImportedProvider = importedProviders.has(exported);
160
199
  if (!isLocalProvider && !isImportedProvider) {
@@ -180,14 +219,19 @@ export class ModuleLoader {
180
219
  ...this.appProviderTokens.get(token) ?? []
181
220
  ];
182
221
  }
183
- resolveAllInstances() {
222
+ getConsumerMiddlewareDefinitions() {
223
+ return [
224
+ ...this.consumerMiddlewareDefinitions
225
+ ];
226
+ }
227
+ async resolveAllInstances() {
184
228
  const instances = [];
185
229
  for (const token of this.registeredProviders){
186
230
  try {
187
231
  if (this.container.getProviderScope(token) === Scope.REQUEST) {
188
232
  continue;
189
233
  }
190
- const instance = this.container.resolve(token);
234
+ const instance = await this.container.resolveAsync(token);
191
235
  instances.push(instance);
192
236
  } catch {
193
237
  // Skip unresolvable tokens
@@ -198,7 +242,7 @@ export class ModuleLoader {
198
242
  if (this.container.getProviderScope(controller) === Scope.REQUEST) {
199
243
  continue;
200
244
  }
201
- const instance = this.container.resolve(controller);
245
+ const instance = await this.container.resolveAsync(controller);
202
246
  if (!instances.includes(instance)) {
203
247
  instances.push(instance);
204
248
  }
@@ -1,19 +1,29 @@
1
- import type { InjectionToken, ProviderOptions, Type } from '../container/types';
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
+ }
2
8
  export interface DynamicModule {
3
9
  module: Type;
10
+ imports?: ModuleImport[];
4
11
  providers?: Array<Type | ProviderOptions>;
5
12
  controllers?: Type[];
6
13
  exports?: Array<Type | InjectionToken>;
14
+ global?: boolean;
7
15
  }
8
16
  export interface ModuleOptions {
9
17
  providers?: Array<Type | ProviderOptions>;
10
18
  controllers?: Type[];
11
- imports?: Array<Type | DynamicModule>;
19
+ imports?: ModuleImport[];
12
20
  exports?: Array<Type | InjectionToken>;
21
+ isGlobal?: boolean;
13
22
  }
14
23
  export interface ModuleMetadata {
15
24
  providers: Array<Type | ProviderOptions>;
16
25
  controllers: Type[];
17
- imports: Array<Type | DynamicModule>;
26
+ imports: ModuleImport[];
18
27
  exports: Array<Type | InjectionToken>;
28
+ isGlobal: boolean;
19
29
  }
@@ -3,5 +3,6 @@ export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch,
3
3
  export { SetMetadata, Reflector } from './reflector';
4
4
  export type { ReflectableDecorator, CreateDecoratorOptions } from './reflector';
5
5
  export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from './tokens';
6
- export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe, } from './pipes';
7
- export type { ExecutionContext, CanActivate, CallHandler, NestInterceptor, NestMiddleware, PipeTransform, ExceptionFilter, ArgumentMetadata, } from './types';
6
+ export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe, ParseEnumPipe, ParseArrayPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe, } from './pipes';
7
+ export type { ParseUUIDPipeOptions, ParseArrayPipeOptions } from './pipes';
8
+ export type { HttpArgumentsHost, ExecutionContext, CanActivate, CallHandler, NestInterceptor, NestMiddleware, PipeTransform, ExceptionFilter, ArgumentMetadata, } from './types';
@@ -2,4 +2,4 @@ export { ComponentManager } from "./component.manager.js";
2
2
  export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, getCatchTypes, shouldFilterCatch } from "./decorators.js";
3
3
  export { SetMetadata, Reflector } from "./reflector.js";
4
4
  export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./tokens.js";
5
- export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe } from "./pipes.js";
5
+ export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe, ParseEnumPipe, ParseArrayPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe } from "./pipes.js";
@@ -23,3 +23,25 @@ export declare class ZodValidationPipe implements PipeTransform {
23
23
  });
24
24
  transform(value: unknown, _metadata: ArgumentMetadata): unknown;
25
25
  }
26
+ export interface ParseUUIDPipeOptions {
27
+ version?: '3' | '4' | '5';
28
+ }
29
+ export declare class ParseUUIDPipe implements PipeTransform<string, string> {
30
+ private readonly options?;
31
+ constructor(options?: ParseUUIDPipeOptions | undefined);
32
+ transform(value: string, metadata: ArgumentMetadata): string;
33
+ }
34
+ export declare class ParseEnumPipe<T extends Record<string, string | number>> implements PipeTransform<string, T[keyof T]> {
35
+ private readonly enumType;
36
+ constructor(enumType: T);
37
+ transform(value: string, metadata: ArgumentMetadata): T[keyof T];
38
+ }
39
+ export interface ParseArrayPipeOptions {
40
+ separator?: string;
41
+ optional?: boolean;
42
+ }
43
+ export declare class ParseArrayPipe implements PipeTransform {
44
+ private readonly options?;
45
+ constructor(options?: ParseArrayPipeOptions | undefined);
46
+ transform(value: unknown, metadata: ArgumentMetadata): unknown[];
47
+ }
@@ -50,3 +50,53 @@ export class ZodValidationPipe {
50
50
  return this.schema.parse(value);
51
51
  }
52
52
  }
53
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
54
+ const UUID_VERSION_REGEX = {
55
+ '3': /^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
56
+ '4': /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
57
+ '5': /^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
58
+ };
59
+ export class ParseUUIDPipe {
60
+ options;
61
+ constructor(options){
62
+ this.options = options;
63
+ }
64
+ transform(value, metadata) {
65
+ const regex = this.options?.version ? UUID_VERSION_REGEX[this.options.version] : UUID_REGEX;
66
+ if (!regex.test(value)) {
67
+ throw new BadRequestException(`Validation failed (uuid${this.options?.version ? ` v${this.options.version}` : ''} expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
68
+ }
69
+ return value;
70
+ }
71
+ }
72
+ export class ParseEnumPipe {
73
+ enumType;
74
+ constructor(enumType){
75
+ this.enumType = enumType;
76
+ }
77
+ transform(value, metadata) {
78
+ const enumValues = Object.values(this.enumType);
79
+ if (!enumValues.includes(value)) {
80
+ throw new BadRequestException(`Validation failed (${enumValues.join(', ')} expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
81
+ }
82
+ return value;
83
+ }
84
+ }
85
+ export class ParseArrayPipe {
86
+ options;
87
+ constructor(options){
88
+ this.options = options;
89
+ }
90
+ transform(value, metadata) {
91
+ if (value === undefined || value === null || value === '') {
92
+ if (this.options?.optional) return [];
93
+ throw new BadRequestException(`Validation failed (array expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
94
+ }
95
+ if (Array.isArray(value)) return value;
96
+ if (typeof value === 'string') {
97
+ const sep = this.options?.separator ?? ',';
98
+ return value.split(sep).map((v)=>v.trim()).filter((v)=>v.length > 0);
99
+ }
100
+ throw new BadRequestException(`Validation failed (array expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
101
+ }
102
+ }
@@ -8,7 +8,7 @@ import type { CanActivate, ExceptionFilter, NestInterceptor, NestMiddleware, Pip
8
8
  * @Module({
9
9
  * providers: [
10
10
  * AuthGuard,
11
- * { token: APP_GUARD, useClass: AuthGuard },
11
+ * { provide: APP_GUARD, useClass: AuthGuard },
12
12
  * ],
13
13
  * })
14
14
  * class AppModule {}
@@ -7,7 +7,7 @@ import { InjectionToken } from "../container/types.js";
7
7
  * @Module({
8
8
  * providers: [
9
9
  * AuthGuard,
10
- * { token: APP_GUARD, useClass: AuthGuard },
10
+ * { provide: APP_GUARD, useClass: AuthGuard },
11
11
  * ],
12
12
  * })
13
13
  * class AppModule {}
@@ -1,10 +1,26 @@
1
1
  import type { Context } from 'hono';
2
2
  import type { Type } from '../container/types';
3
+ /**
4
+ * HTTP-specific arguments host returned by `ExecutionContext.switchToHttp()`.
5
+ * In Vela/Hono, the `Context` object holds both request and response state.
6
+ *
7
+ * - `getRequest()` — the Web `Request` object
8
+ * - `getResponse()` — the Hono `Context` (use `c.header()`, `c.setCookie()`, etc.)
9
+ */
10
+ export interface HttpArgumentsHost {
11
+ getRequest<T = Request>(): T;
12
+ getResponse<T = Context>(): T;
13
+ }
3
14
  export interface ExecutionContext {
15
+ getType<T extends string = 'http'>(): T;
4
16
  getClass(): Type;
5
17
  getHandler(): string | symbol;
18
+ /** Returns the Hono `Context` directly. */
6
19
  getContext<T = Context>(): T;
20
+ /** Shorthand for `switchToHttp().getRequest()` — returns the Web `Request`. */
7
21
  getRequest(): Request;
22
+ /** Switch to the HTTP arguments host for NestJS-style `getRequest()` / `getResponse()` access. */
23
+ switchToHttp(): HttpArgumentsHost;
8
24
  }
9
25
  export interface CanActivate {
10
26
  canActivate(context: ExecutionContext): boolean | Promise<boolean>;
@@ -38,12 +38,12 @@ export declare class MetadataRegistry {
38
38
  static getController<T extends ComponentType>(type: T, controller: Constructor): ComponentTypeMap[T][];
39
39
  static registerHandler<T extends ComponentType>(type: T, handlerKey: string, component: ComponentTypeMap[T]): void;
40
40
  static getHandler<T extends ComponentType>(type: T, handlerKey: string): ComponentTypeMap[T][];
41
- static markInjectable(target: Constructor): void;
42
- static hasInjectable(target: Constructor): boolean;
43
- static setScope(target: Constructor, scope: Scope): void;
44
- static getScope(target: Constructor): Scope | undefined;
45
- static setInjectTokens(target: Constructor, tokens: InjectMetadata[]): void;
46
- static getInjectTokens(target: Constructor): InjectMetadata[] | undefined;
41
+ static markInjectable(target: object): void;
42
+ static hasInjectable(target: object): boolean;
43
+ static setScope(target: object, scope: Scope): void;
44
+ static getScope(target: object): Scope | undefined;
45
+ static setInjectTokens(target: object, tokens: InjectMetadata[]): void;
46
+ static getInjectTokens(target: object): InjectMetadata[] | undefined;
47
47
  static setHandlerHttpMeta(controller: Constructor, method: string | symbol, meta: HttpHandlerMeta): void;
48
48
  static getHandlerHttpMeta(controller: Constructor, method: string | symbol): HttpHandlerMeta | undefined;
49
49
  static setCatchTypes(filter: Constructor, types: Type<Error>[]): void;
@@ -57,5 +57,6 @@ export declare class MetadataRegistry {
57
57
  static setCustomHandlerMeta(target: Constructor, handler: string | symbol, key: string, value: unknown): void;
58
58
  static getCustomHandlerMeta(target: Constructor, handler: string | symbol, key: string): unknown;
59
59
  static getCustomHandlerMetaAll(target: Constructor, handler: string | symbol): Map<string, unknown> | undefined;
60
+ static propagateControllerComponents(from: Constructor, to: Constructor): void;
60
61
  static clear(): void;
61
62
  }
@@ -248,6 +248,19 @@ export class MetadataRegistry {
248
248
  static getCustomHandlerMetaAll(target, handler) {
249
249
  return this.customHandlerMeta.get(target)?.get(handler);
250
250
  }
251
+ // Propagate all controller-level components from one class to another.
252
+ // Used by ModuleLoader to apply module-level decorators to every controller.
253
+ static propagateControllerComponents(from, to) {
254
+ for (const [, typeMap] of this.controller.entries()){
255
+ const components = typeMap.get(from);
256
+ if (components && components.length > 0) {
257
+ if (!typeMap.has(to)) {
258
+ typeMap.set(to, []);
259
+ }
260
+ typeMap.get(to).push(...components);
261
+ }
262
+ }
263
+ }
251
264
  // Clear all (for testing)
252
265
  static clear() {
253
266
  this.routes.clear();
@@ -41,6 +41,7 @@ export interface ModuleOptions {
41
41
  providers?: Array<Type | ProviderOptions>;
42
42
  controllers?: Type[];
43
43
  exports?: Array<Type | InjectionTokenLike>;
44
+ isGlobal?: boolean;
44
45
  }
45
46
  export interface InjectionTokenLike {
46
47
  toString(): string;
@@ -1,5 +1,8 @@
1
- import type { DynamicModule } from '../module/types';
1
+ import type { AsyncModuleOptions, DynamicModule } from '../module/types';
2
2
  import type { ScheduleModuleOptions } from './schedule.types';
3
3
  export declare class ScheduleModule {
4
4
  static forRoot(options?: ScheduleModuleOptions): DynamicModule;
5
+ static forRootAsync(options: AsyncModuleOptions<ScheduleModuleOptions> & {
6
+ enableTimers?: boolean;
7
+ }): DynamicModule;
5
8
  }
@@ -4,18 +4,22 @@ import { MetadataRegistry } from "../registry/metadata.registry.js";
4
4
  import { ScheduleExecutor } from "./schedule.executor.js";
5
5
  import { ScheduleRegistry } from "./schedule.registry.js";
6
6
  import { SCHEDULE_MODULE_OPTIONS } from "./schedule.tokens.js";
7
+ function makeScheduleModuleClass() {
8
+ const moduleClass = class ScheduleDynamicModule {
9
+ };
10
+ Object.defineProperty(moduleClass, 'name', {
11
+ value: 'ScheduleModule'
12
+ });
13
+ defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
14
+ return moduleClass;
15
+ }
7
16
  export class ScheduleModule {
8
17
  static forRoot(options = {}) {
9
18
  const { enableTimers = false } = options;
10
- const moduleClass = class ScheduleDynamicModule {
11
- };
12
- Object.defineProperty(moduleClass, 'name', {
13
- value: 'ScheduleModule'
14
- });
15
- defineMetadata(METADATA_KEYS.MODULE, true, moduleClass);
19
+ const moduleClass = makeScheduleModuleClass();
16
20
  const providers = [
17
21
  {
18
- token: SCHEDULE_MODULE_OPTIONS,
22
+ provide: SCHEDULE_MODULE_OPTIONS,
19
23
  useValue: options
20
24
  },
21
25
  ScheduleRegistry
@@ -36,4 +40,32 @@ export class ScheduleModule {
36
40
  providers
37
41
  };
38
42
  }
43
+ static forRootAsync(options) {
44
+ const { enableTimers = false } = options;
45
+ const moduleClass = makeScheduleModuleClass();
46
+ const providers = [
47
+ {
48
+ provide: SCHEDULE_MODULE_OPTIONS,
49
+ useFactory: options.useFactory,
50
+ inject: options.inject ?? []
51
+ },
52
+ ScheduleRegistry
53
+ ];
54
+ const exports = [
55
+ SCHEDULE_MODULE_OPTIONS,
56
+ ScheduleRegistry
57
+ ];
58
+ if (enableTimers) {
59
+ providers.push(ScheduleExecutor);
60
+ exports.push(ScheduleExecutor);
61
+ }
62
+ MetadataRegistry.setModuleOptions(moduleClass, {
63
+ imports: options.imports ?? [],
64
+ exports
65
+ });
66
+ return {
67
+ module: moduleClass,
68
+ providers
69
+ };
70
+ }
39
71
  }
@@ -19,7 +19,7 @@ export class OverrideBy {
19
19
  this.builder['addOverride']({
20
20
  token: this.token,
21
21
  provider: {
22
- token: this.token,
22
+ provide: this.token,
23
23
  useValue: value
24
24
  }
25
25
  });
@@ -29,7 +29,7 @@ export class OverrideBy {
29
29
  this.builder['addOverride']({
30
30
  token: this.token,
31
31
  provider: {
32
- token: this.token,
32
+ provide: this.token,
33
33
  useClass: cls
34
34
  }
35
35
  });
@@ -39,7 +39,7 @@ export class OverrideBy {
39
39
  this.builder['addOverride']({
40
40
  token: this.token,
41
41
  provider: {
42
- token: this.token,
42
+ provide: this.token,
43
43
  useFactory: options.factory,
44
44
  inject: options.inject
45
45
  }
@@ -91,7 +91,7 @@ export class TestingModuleBuilder {
91
91
  // Bootstrap (mirrors VelaFactory.create)
92
92
  const container = new Container();
93
93
  container.register({
94
- token: Container,
94
+ provide: Container,
95
95
  useValue: container
96
96
  });
97
97
  // Register overrides BEFORE module loading so ModuleLoader skips originals
@@ -133,7 +133,7 @@ export class TestingModuleBuilder {
133
133
  routeManager.useGlobalMiddlewareTokens(APP_MIDDLEWARE);
134
134
  }
135
135
  const app = new VelaApplication(container, routeManager);
136
- const instances = loader.resolveAllInstances();
136
+ const instances = await loader.resolveAllInstances();
137
137
  app.setInstances(instances);
138
138
  await app.callOnModuleInit();
139
139
  await app.callOnApplicationBootstrap();
@@ -1,5 +1,6 @@
1
- import type { DynamicModule } from '../module/types';
1
+ import type { AsyncModuleOptions, DynamicModule } from '../module/types';
2
2
  import type { ThrottlerModuleOptions } from './throttler.types';
3
3
  export declare class ThrottlerModule {
4
4
  static forRoot(options: ThrottlerModuleOptions): DynamicModule;
5
+ static forRootAsync(options: AsyncModuleOptions<ThrottlerModuleOptions>): DynamicModule;
5
6
  }