@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
@@ -3,16 +3,16 @@ import type { ControllerOptions } from './types';
3
3
  import type { ExecutionContext } from '../pipeline/types';
4
4
  /**
5
5
  * Marks a class as a controller.
6
- * Accepts a prefix string or an options object with prefix and version.
6
+ * Accepts a path string or an options object with `path` and `version`.
7
7
  *
8
8
  * @example
9
9
  * ```ts
10
10
  * @Controller('/users')
11
- * @Controller({ prefix: '/users', version: 1 })
12
- * @Controller({ prefix: '/users', version: [1, 2] })
11
+ * @Controller({ path: '/users', version: 1 })
12
+ * @Controller({ path: '/users', version: [1, 2] })
13
13
  * ```
14
14
  */
15
- export declare function Controller(prefixOrOptions?: string | ControllerOptions): ClassDecorator;
15
+ export declare function Controller(pathOrOptions?: string | ControllerOptions): ClassDecorator;
16
16
  /**
17
17
  * Override the version for a specific route method.
18
18
  *
@@ -184,5 +184,6 @@ export declare function getRedirect(target: Constructor, method: string | symbol
184
184
  * handle() { ... }
185
185
  * ```
186
186
  */
187
- export declare function applyDecorators(...decorators: Array<ClassDecorator | MethodDecorator | PropertyDecorator | ParameterDecorator>): ClassDecorator & MethodDecorator & PropertyDecorator;
187
+ export type ComposedDecorator = <T>(target: T, propertyKey?: string | symbol, descriptor?: PropertyDescriptor | number) => void;
188
+ export declare function applyDecorators(...decorators: Array<ClassDecorator | MethodDecorator | PropertyDecorator | ParameterDecorator>): ComposedDecorator;
188
189
  export declare function isController(target: Constructor): boolean;
@@ -1,43 +1,30 @@
1
- import { HttpMethod, METADATA_KEYS, ParamType, Scope } from "../constants.js";
2
- import { defineMetadata, getMetadata } from "../metadata.js";
1
+ import { HttpMethod, ParamType, Scope } from "../constants.js";
3
2
  import { MetadataRegistry } from "../registry/metadata.registry.js";
4
- function normalizePath(path) {
5
- return path && !path.startsWith('/') ? `/${path}` : path;
6
- }
3
+ import { normalizePath } from "../registry/paths.js";
4
+ import { buildExecutionContext } from "./execution-context.js";
7
5
  /**
8
6
  * Marks a class as a controller.
9
- * Accepts a prefix string or an options object with prefix and version.
7
+ * Accepts a path string or an options object with `path` and `version`.
10
8
  *
11
9
  * @example
12
10
  * ```ts
13
11
  * @Controller('/users')
14
- * @Controller({ prefix: '/users', version: 1 })
15
- * @Controller({ prefix: '/users', version: [1, 2] })
12
+ * @Controller({ path: '/users', version: 1 })
13
+ * @Controller({ path: '/users', version: [1, 2] })
16
14
  * ```
17
- */ export function Controller(prefixOrOptions) {
18
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
15
+ */ export function Controller(pathOrOptions) {
19
16
  return (target)=>{
20
- let prefix;
21
- let version;
22
- if (typeof prefixOrOptions === 'string') {
23
- prefix = prefixOrOptions;
24
- } else if (prefixOrOptions) {
25
- prefix = prefixOrOptions.path ?? prefixOrOptions.prefix ?? '';
26
- version = prefixOrOptions.version;
27
- } else {
28
- prefix = '';
29
- }
30
- MetadataRegistry.setControllerPath(target, normalizePath(prefix));
17
+ const ctor = target;
18
+ const path = typeof pathOrOptions === 'string' ? pathOrOptions : pathOrOptions?.path ?? '';
19
+ const version = typeof pathOrOptions === 'object' ? pathOrOptions?.version : undefined;
20
+ MetadataRegistry.setControllerPath(ctor, normalizePath(path));
31
21
  if (version !== undefined) {
32
- MetadataRegistry.setControllerOptions(target, {
22
+ MetadataRegistry.setControllerOptions(ctor, {
33
23
  version
34
24
  });
35
25
  }
36
- MetadataRegistry.markInjectable(target);
37
- MetadataRegistry.setScope(target, Scope.SINGLETON);
38
- // Keep WeakMap write for external package compat
39
- defineMetadata(METADATA_KEYS.INJECTABLE, true, target);
40
- defineMetadata(METADATA_KEYS.SCOPE, Scope.SINGLETON, target);
26
+ MetadataRegistry.markInjectable(ctor);
27
+ MetadataRegistry.setScope(ctor, Scope.SINGLETON);
41
28
  };
42
29
  }
43
30
  /**
@@ -57,28 +44,25 @@ function normalizePath(path) {
57
44
  * ```
58
45
  */ export function Version(version) {
59
46
  return (target, propertyKey, _descriptor)=>{
60
- MetadataRegistry.setRouteVersion(target.constructor, propertyKey, version);
61
- // Keep WeakMap write for compat
62
- const versionKey = `vela:route-version:${String(propertyKey)}`;
63
- defineMetadata(versionKey, version, target.constructor);
64
- // If route was already registered (decorator ran after @Get), patch it
65
- const routes = MetadataRegistry.getRoutes(target.constructor);
66
- const route = routes.find((r)=>r.handlerName === propertyKey);
47
+ const ctor = target.constructor;
48
+ MetadataRegistry.setRouteVersion(ctor, propertyKey, version);
49
+ // If route was already registered (decorator ran after @Get), patch it.
50
+ const route = MetadataRegistry.getRoutes(ctor).find((r)=>r.handlerName === propertyKey);
67
51
  if (route) {
68
52
  route.version = version;
69
53
  }
70
54
  };
71
55
  }
72
56
  export function getRouteVersion(target, propertyKey) {
73
- return MetadataRegistry.getRouteVersion(target, propertyKey) ?? getMetadata(`vela:route-version:${String(propertyKey)}`, target);
57
+ return MetadataRegistry.getRouteVersion(target, propertyKey);
74
58
  }
75
59
  function createMethodDecorator(method) {
76
60
  return (path = '')=>{
77
61
  return (target, propertyKey, _descriptor)=>{
62
+ const ctor = target.constructor;
78
63
  const normalizedPath = normalizePath(path);
79
- // Check for @Version metadata on this method
80
- const version = MetadataRegistry.getRouteVersion(target.constructor, propertyKey) ?? getMetadata(`vela:route-version:${String(propertyKey)}`, target.constructor);
81
- MetadataRegistry.addRoute(target.constructor, {
64
+ const version = MetadataRegistry.getRouteVersion(ctor, propertyKey);
65
+ MetadataRegistry.addRoute(ctor, {
82
66
  method: method,
83
67
  path: normalizedPath,
84
68
  handlerName: propertyKey,
@@ -229,17 +213,7 @@ export const Cookie = createBuiltinParamDecorator(ParamType.COOKIE);
229
213
  name: undefined,
230
214
  factory: (_unused, ctx)=>{
231
215
  const honoCtx = ctx;
232
- const execCtx = {
233
- getType: ()=>'http',
234
- getClass: ()=>target.constructor,
235
- getHandler: ()=>propertyKey,
236
- getContext: ()=>honoCtx,
237
- getRequest: ()=>honoCtx.req.raw,
238
- switchToHttp: ()=>({
239
- getRequest: ()=>honoCtx.req.raw,
240
- getResponse: ()=>honoCtx
241
- })
242
- };
216
+ const execCtx = buildExecutionContext(honoCtx, target.constructor, propertyKey);
243
217
  return factory(data, execCtx);
244
218
  },
245
219
  ...pipes.length > 0 ? {
@@ -317,37 +291,16 @@ export const Cookie = createBuiltinParamDecorator(ParamType.COOKIE);
317
291
  }
318
292
  // Metadata readers (used by RouteManager)
319
293
  export function getHttpCode(target, method) {
320
- const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
321
- if (meta?.httpCode !== undefined) return meta.httpCode;
322
- return getMetadata(METADATA_KEYS.HTTP_CODE, target, method);
294
+ return MetadataRegistry.getHandlerHttpMeta(target, method)?.httpCode;
323
295
  }
324
296
  export function getResponseHeaders(target, method) {
325
- const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
326
- if (meta?.responseHeaders) return meta.responseHeaders;
327
- return getMetadata(METADATA_KEYS.RESPONSE_HEADERS, target, method) ?? [];
297
+ return MetadataRegistry.getHandlerHttpMeta(target, method)?.responseHeaders ?? [];
328
298
  }
329
299
  export function getRedirect(target, method) {
330
- const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
331
- if (meta?.redirect) return meta.redirect;
332
- return getMetadata(METADATA_KEYS.REDIRECT, target, method);
300
+ return MetadataRegistry.getHandlerHttpMeta(target, method)?.redirect;
333
301
  }
334
- /**
335
- * Composes multiple decorators into one. Applies them in order (top to bottom),
336
- * matching how stacked decorators behave when written separately.
337
- *
338
- * @example
339
- * ```ts
340
- * const Auth = (...roles: string[]) => applyDecorators(
341
- * UseGuards(JwtGuard, RolesGuard),
342
- * SetMetadata('roles', roles),
343
- * );
344
- *
345
- * @Auth('admin')
346
- * @Get('/admin')
347
- * handle() { ... }
348
- * ```
349
- */ export function applyDecorators(...decorators) {
350
- const composed = (target, propertyKey, descriptor)=>{
302
+ export function applyDecorators(...decorators) {
303
+ return (target, propertyKey, descriptor)=>{
351
304
  for (const decorator of decorators){
352
305
  if (propertyKey === undefined && typeof descriptor !== 'number') {
353
306
  decorator(target);
@@ -358,7 +311,6 @@ export function getRedirect(target, method) {
358
311
  }
359
312
  }
360
313
  };
361
- return composed;
362
314
  }
363
315
  // Helpers
364
316
  export function isController(target) {
@@ -0,0 +1,4 @@
1
+ import type { Context } from 'hono';
2
+ import type { Type } from '../container/types';
3
+ import type { ExecutionContext } from '../pipeline/types';
4
+ export declare function buildExecutionContext(c: Context, controller: Type, handlerName: string | symbol): ExecutionContext;
@@ -0,0 +1,15 @@
1
+ // Single source of ExecutionContext shape — used by the route pipeline AND
2
+ // by createParamDecorator's deferred factory call.
3
+ export function buildExecutionContext(c, controller, handlerName) {
4
+ return {
5
+ getType: ()=>'http',
6
+ getClass: ()=>controller,
7
+ getHandler: ()=>handlerName,
8
+ getContext: ()=>c,
9
+ getRequest: ()=>c.req.raw,
10
+ switchToHttp: ()=>({
11
+ getRequest: ()=>c.req.raw,
12
+ getResponse: ()=>c
13
+ })
14
+ };
15
+ }
@@ -1,6 +1,6 @@
1
1
  export { RouteManager } from './route.manager';
2
2
  export type { RouteManagerOptions } from './route.manager';
3
- export { MiddlewareBuilder, RequestMethod } from './middleware-consumer';
4
- export type { MiddlewareConsumer, MiddlewareConfigProxy, RouteInfo, NestModule, MiddlewareRouteDefinition } from './middleware-consumer';
3
+ export { MiddlewareBuilder } from '../module/middleware';
4
+ export type { MiddlewareConsumer, MiddlewareConfigProxy, RouteInfo, NestModule, MiddlewareRouteDefinition } from '../module/middleware';
5
5
  export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, All, Sse, Param, Query, Body, Headers, Req, Res, Ip, Cookie, Cookies, RawBody, HttpCode, Header, Redirect, createParamDecorator, applyDecorators, isController, } from './decorators';
6
6
  export type { RouteMetadata, ControllerMetadata, ControllerOptions, ParamMetadata, ControllerRegistration, } from './types';
@@ -1,3 +1,3 @@
1
1
  export { RouteManager } from "./route.manager.js";
2
- export { MiddlewareBuilder, RequestMethod } from "./middleware-consumer.js";
2
+ export { MiddlewareBuilder } from "../module/middleware.js";
3
3
  export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, All, Sse, Param, Query, Body, Headers, Req, Res, Ip, Cookie, Cookies, RawBody, HttpCode, Header, Redirect, createParamDecorator, applyDecorators, isController } from "./decorators.js";
@@ -1,7 +1,7 @@
1
1
  import { type Context, type MiddlewareHandler, Hono } from 'hono';
2
2
  import type { Container } from '../container/container';
3
3
  import type { Token, Type } from '../container/types';
4
- import type { MiddlewareRouteDefinition } from './middleware-consumer';
4
+ import type { MiddlewareRouteDefinition } from '../module/middleware';
5
5
  import type { CanActivate, ExceptionFilter, NestInterceptor, NestMiddleware, PipeTransform } from '../pipeline/types';
6
6
  import type { FilterType, GuardType, InterceptorType, MiddlewareType, PipeType } from '../registry/types';
7
7
  import type { ControllerRegistration } from './types';
@@ -49,7 +49,6 @@ export declare class RouteManager {
49
49
  private createResponse;
50
50
  private registerRoute;
51
51
  private buildVersionedPaths;
52
- private joinPaths;
53
52
  private compilePathMatcher;
54
53
  private compileRouteMatcher;
55
54
  getControllers(): ControllerRegistration[];
@@ -2,7 +2,8 @@ import { Hono } from "hono";
2
2
  import { getCookie } from "hono/cookie";
3
3
  import { HttpMethod, ParamType } from "../constants.js";
4
4
  import { getMetadata } from "../metadata.js";
5
- import { RequestMethod } from "./middleware-consumer.js";
5
+ import { joinPaths } from "../registry/paths.js";
6
+ import { buildExecutionContext } from "./execution-context.js";
6
7
  import { ForbiddenException, HttpException } from "../errors/http-exception.js";
7
8
  import { ComponentManager } from "../pipeline/component.manager.js";
8
9
  import { shouldFilterCatch } from "../pipeline/decorators.js";
@@ -50,7 +51,7 @@ export class RouteManager {
50
51
  ],
51
52
  [
52
53
  HttpMethod.HEAD,
53
- (app, p, h)=>app.get(p, h)
54
+ (app, p, h)=>app.get(p, (c, next)=>c.req.method === 'HEAD' ? h(c) : next())
54
55
  ],
55
56
  [
56
57
  HttpMethod.ALL,
@@ -175,11 +176,6 @@ export class RouteManager {
175
176
  instantiateMany(items, container) {
176
177
  return items.map((item)=>this.instantiate(item, container));
177
178
  }
178
- // Resolve middleware priority at build time. Supports three sources:
179
- // 1. static priority on the class constructor
180
- // 2. priority property on the instance
181
- // 3. for container tokens, resolve and inspect the instance (+ its ctor)
182
- // Falls back to 0 on any error or when no priority is set.
183
179
  getMiddlewarePriority(entry) {
184
180
  if (entry == null) return 0;
185
181
  if (typeof entry === 'function') {
@@ -306,31 +302,25 @@ export class RouteManager {
306
302
  await buildCrudRoutes(app, controller, metadata.prefix, crudConfig, {
307
303
  globalPrefix: this.globalPrefix,
308
304
  globalGuards: this.instantiateMany(this.globalGuards, this.container),
309
- joinPaths: this.joinPaths.bind(this)
305
+ joinPaths
306
+ });
307
+ } catch (err) {
308
+ throw new Error(`@Crud() requires '@velajs/crud'. Install it: pnpm add @velajs/crud`, {
309
+ cause: err
310
310
  });
311
- } catch {
312
- throw new Error(`@Crud() requires '@velajs/crud'. Install it: bun add @velajs/crud`);
313
311
  }
314
312
  }
315
313
  }
316
314
  return app;
317
315
  }
318
316
  createExecutionContext(c, controller, route) {
319
- return {
320
- getType: ()=>'http',
321
- getClass: ()=>controller,
322
- getHandler: ()=>route.handlerName,
323
- getContext: ()=>c,
324
- getRequest: ()=>c.req.raw,
325
- switchToHttp: ()=>({
326
- getRequest: ()=>c.req.raw,
327
- getResponse: ()=>c
328
- })
329
- };
317
+ return buildExecutionContext(c, controller, route.handlerName);
330
318
  }
331
319
  createHandler(route, controller, allParamMetadata) {
332
320
  const paramMetadata = (allParamMetadata.get(route.handlerName) || []).sort((a, b)=>a.index - b.index);
333
- // Read param types once at build time for metatype population
321
+ // Read param types once at build time for metatype population.
322
+ // Routed via Reflect so the polyfill funnels both src-level and dist-level
323
+ // consumers to the same registry (matters in tests that import from dist).
334
324
  const paramTypes = Reflect.getMetadata('design:paramtypes', controller.prototype, route.handlerName);
335
325
  // Collect controller + method level components at build time
336
326
  const methodGuards = ComponentManager.getComponents('guard', controller, route.handlerName);
@@ -475,12 +465,16 @@ export class RouteManager {
475
465
  registerRoute(app, method, path, handler) {
476
466
  const normalizedPath = path || '/';
477
467
  const registrar = RouteManager.METHOD_REGISTRAR.get(method);
478
- registrar ? registrar(app, normalizedPath, handler) : app.on(method.toUpperCase(), normalizedPath, handler);
468
+ if (registrar) {
469
+ registrar(app, normalizedPath, handler);
470
+ } else {
471
+ app.on(method, normalizedPath, handler);
472
+ }
479
473
  }
480
474
  buildVersionedPaths(globalPrefix, controllerPrefix, routePath, version) {
481
475
  if (version === undefined) {
482
476
  return [
483
- this.joinPaths(globalPrefix, this.joinPaths(controllerPrefix, routePath))
477
+ joinPaths(globalPrefix, joinPaths(controllerPrefix, routePath))
484
478
  ];
485
479
  }
486
480
  const versions = Array.isArray(version) ? version : [
@@ -488,14 +482,9 @@ export class RouteManager {
488
482
  ];
489
483
  return versions.map((v)=>{
490
484
  const versionSegment = `/v${v}`;
491
- return this.joinPaths(globalPrefix, this.joinPaths(versionSegment, this.joinPaths(controllerPrefix, routePath)));
485
+ return joinPaths(globalPrefix, joinPaths(versionSegment, joinPaths(controllerPrefix, routePath)));
492
486
  });
493
487
  }
494
- joinPaths(prefix, path) {
495
- const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
496
- const cleanPath = path && !path.startsWith('/') ? `/${path}` : path;
497
- return `${cleanPrefix}${cleanPath}` || '/';
498
- }
499
488
  compilePathMatcher(routePath) {
500
489
  if (routePath === '*') return ()=>true;
501
490
  const norm = routePath.startsWith('/') ? routePath : `/${routePath}`;
@@ -510,7 +499,7 @@ export class RouteManager {
510
499
  const matchPath = this.compilePathMatcher(route.path);
511
500
  return (path, method)=>{
512
501
  if (!matchPath(path)) return false;
513
- if (route.method && route.method !== RequestMethod.ALL) return method === route.method;
502
+ if (route.method && route.method !== HttpMethod.ALL) return method === route.method;
514
503
  return true;
515
504
  };
516
505
  });
@@ -8,7 +8,6 @@ export interface RouteMetadata {
8
8
  version?: number | number[];
9
9
  }
10
10
  export interface ControllerOptions {
11
- prefix?: string;
12
11
  path?: string;
13
12
  version?: number | number[];
14
13
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import './metadata';
2
2
  export { defineMetadata, getMetadata } from './metadata';
3
- export { VelaFactory, createApplication } from './factory';
3
+ export { VelaFactory } from './factory';
4
4
  export { VelaApplication } from './application';
5
5
  export { createOpenApiDocument, ApiDoc, ApiTags, ApiResponse, zodToJsonSchema, } from './openapi/index';
6
6
  export type { OpenApiDocument, OpenApiInfo, OpenApiOperation, OpenApiParameter, OpenApiPathItem, OpenApiRequestBody, OpenApiResponse, ApiDocMetadata, ApiResponseEntry, ApiResponseOptions, CreateOpenApiDocumentOptions, HttpVerb, JsonSchema, } from './openapi/index';
@@ -20,15 +20,14 @@ export { CacheModule, CacheService, CacheInterceptor, MemoryCacheStore, CacheKey
20
20
  export type { CacheModuleOptions, CacheStore, CacheEntry } from './cache/index';
21
21
  export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_EVENT_METADATA, } from './event-emitter/index';
22
22
  export type { EventHandler, OnEventMetadata } from './event-emitter/index';
23
- export { ScheduleModule, ScheduleRegistry, ScheduleExecutor, Cron, Interval, SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA, } from './schedule/index';
24
- export type { RegisteredCronJob, RegisteredIntervalJob, CronMetadata, IntervalMetadata, ScheduleModuleOptions, } from './schedule/index';
23
+ export { ScheduleModule, ScheduleRegistry, Cron, Interval, parseCron, CRON_METADATA, INTERVAL_METADATA, } from './schedule/index';
24
+ export type { RegisteredCronJob, RegisteredIntervalJob, CronMetadata, IntervalMetadata, CronMatcher, } from './schedule/index';
25
25
  export { HealthModule, HealthCheckService, HealthIndicatorService, HttpHealthIndicator, } from './health/index';
26
26
  export type { HealthCheckResult, HealthCheckStatus, HealthIndicatorResult, HealthIndicatorFunction, ResponseCheckCallback, HttpPingOptions, } from './health/index';
27
27
  export { ThrottlerModule, ThrottlerGuard, ThrottlerStorage, Throttle, SkipThrottle, THROTTLER_OPTIONS, THROTTLER_STORAGE, THROTTLE_METADATA, SKIP_THROTTLE_METADATA, } from './throttler/index';
28
28
  export type { ThrottlerModuleOptions, ThrottleConfig, ThrottlerStore, ThrottlerStorageRecord, RateLimitInfo, } from './throttler/index';
29
29
  export { Global, Module, createModuleRef } from './module/index';
30
30
  export type { ModuleOptions, DynamicModule, AsyncModuleOptions, ModuleImport } from './module/index';
31
- export { RequestMethod } from './http/index';
32
31
  export type { MiddlewareConsumer, NestModule, RouteInfo } from './http/index';
33
32
  export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, SetMetadata, Reflector, APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE, } from './pipeline/index';
34
33
  export type { HttpArgumentsHost, ExecutionContext, CanActivate, CallHandler, NestInterceptor, NestMiddleware, PipeTransform, ExceptionFilter, ArgumentMetadata, ReflectableDecorator, CreateDecoratorOptions, } from './pipeline/index';
@@ -37,13 +36,8 @@ export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe, ParseEnumPi
37
36
  export type { ParseUUIDPipeOptions, ParseArrayPipeOptions } from './pipeline/index';
38
37
  export { HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, GoneException, PayloadTooLargeException, UnsupportedMediaTypeException, UnprocessableEntityException, TooManyRequestsException, InternalServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, GatewayTimeoutException, } from './errors/index';
39
38
  export type { OnModuleInit, OnApplicationBootstrap, OnModuleDestroy, OnApplicationShutdown, BeforeApplicationShutdown, } from './lifecycle/index';
40
- export { MetadataRegistry } from './registry/index';
41
- export { RouteManager } from './http/index';
42
- export type { RouteManagerOptions } from './http/index';
43
- export { ModuleLoader } from './module/index';
44
- export { ComponentManager } from './pipeline/index';
39
+ export { MetadataRegistry } from './registry/metadata.registry';
45
40
  export { createZodDto, ValidationPipe } from './validation/index';
46
41
  export type { CreateZodDtoOptions } from './validation/index';
47
42
  export { Serialize, SerializerInterceptor, SERIALIZE_METADATA } from './serialization/index';
48
- export { Test, TestingModule, TestingModuleBuilder } from './testing/index';
49
43
  export { getRuntimeKey, env } from 'hono/adapter';
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./metadata.js";
2
2
  export { defineMetadata, getMetadata } from "./metadata.js";
3
3
  // Factory & Application
4
- export { VelaFactory, createApplication } from "./factory.js";
4
+ export { VelaFactory } from "./factory.js";
5
5
  export { VelaApplication } from "./application.js";
6
6
  // OpenAPI
7
7
  export { createOpenApiDocument, ApiDoc, ApiTags, ApiResponse, zodToJsonSchema } from "./openapi/index.js";
@@ -24,32 +24,27 @@ export { CacheModule, CacheService, CacheInterceptor, MemoryCacheStore, CacheKey
24
24
  // Event Emitter
25
25
  export { EventEmitterModule, EventEmitter, EventEmitterSubscriber, OnEvent, ON_EVENT_METADATA } from "./event-emitter/index.js";
26
26
  // Schedule
27
- export { ScheduleModule, ScheduleRegistry, ScheduleExecutor, Cron, Interval, SCHEDULE_MODULE_OPTIONS, CRON_METADATA, INTERVAL_METADATA } from "./schedule/index.js";
27
+ export { ScheduleModule, ScheduleRegistry, Cron, Interval, parseCron, CRON_METADATA, INTERVAL_METADATA } from "./schedule/index.js";
28
28
  // Health
29
29
  export { HealthModule, HealthCheckService, HealthIndicatorService, HttpHealthIndicator } from "./health/index.js";
30
30
  // Throttler
31
31
  export { ThrottlerModule, ThrottlerGuard, ThrottlerStorage, Throttle, SkipThrottle, THROTTLER_OPTIONS, THROTTLER_STORAGE, THROTTLE_METADATA, SKIP_THROTTLE_METADATA } from "./throttler/index.js";
32
32
  // Module
33
33
  export { Global, Module, createModuleRef } from "./module/index.js";
34
- export { RequestMethod } from "./http/index.js";
35
34
  // Pipeline Decorators
36
35
  export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, SetMetadata, Reflector, APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./pipeline/index.js";
37
36
  // Built-in Pipes
38
37
  export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, ParseUUIDPipe, ParseEnumPipe, ParseArrayPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe } from "./pipeline/index.js";
39
38
  // Errors
40
39
  export { HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, GoneException, PayloadTooLargeException, UnsupportedMediaTypeException, UnprocessableEntityException, TooManyRequestsException, InternalServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, GatewayTimeoutException } from "./errors/index.js";
41
- // Registry (for advanced usage)
42
- export { MetadataRegistry } from "./registry/index.js";
43
- // Module internals (for @velajs/testing and advanced usage)
44
- export { RouteManager } from "./http/index.js";
45
- export { ModuleLoader } from "./module/index.js";
46
- // Component Manager (for advanced usage)
47
- export { ComponentManager } from "./pipeline/index.js";
40
+ // MetadataRegistry the central decoration store. Test setup typically
41
+ // uses `MetadataRegistry.clear()` between cases. Internal primitives like
42
+ // RouteManager/ModuleLoader/ComponentManager live only at @velajs/vela/internal.
43
+ export { MetadataRegistry } from "./registry/metadata.registry.js";
48
44
  // Validation
49
45
  export { createZodDto, ValidationPipe } from "./validation/index.js";
50
46
  // Serialization
51
47
  export { Serialize, SerializerInterceptor, SERIALIZE_METADATA } from "./serialization/index.js";
52
- // Testing
53
- export { Test, TestingModule, TestingModuleBuilder } from "./testing/index.js";
48
+ // Testing utilities live in @velajs/testing — see https://github.com/velajs/testing
54
49
  // Hono Adapter Utilities
55
50
  export { getRuntimeKey, env } from "hono/adapter";
@@ -0,0 +1,11 @@
1
+ export { Container } from './container/container';
2
+ export { ModuleRef } from './container/module-ref';
3
+ export { bindAppProviders } from './pipeline/app-providers';
4
+ export { RouteManager } from './http/route.manager';
5
+ export type { RouteManagerOptions } from './http/route.manager';
6
+ export { ModuleLoader } from './module/module-loader';
7
+ export { ComponentManager } from './pipeline/component.manager';
8
+ export { VelaApplication } from './application';
9
+ export { MetadataRegistry } from './registry/metadata.registry';
10
+ export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE, } from './pipeline/tokens';
11
+ export { getModuleMetadata, isModule } from './module/decorators';
@@ -0,0 +1,13 @@
1
+ // Internal framework primitives — exposed for first-party consumers like
2
+ // @velajs/testing and advanced integration. Not covered by semver guarantees
3
+ // to the same degree as the public surface (./index).
4
+ export { Container } from "./container/container.js";
5
+ export { ModuleRef } from "./container/module-ref.js";
6
+ export { bindAppProviders } from "./pipeline/app-providers.js";
7
+ export { RouteManager } from "./http/route.manager.js";
8
+ export { ModuleLoader } from "./module/module-loader.js";
9
+ export { ComponentManager } from "./pipeline/component.manager.js";
10
+ export { VelaApplication } from "./application.js";
11
+ export { MetadataRegistry } from "./registry/metadata.registry.js";
12
+ export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./pipeline/tokens.js";
13
+ export { getModuleMetadata, isModule } from "./module/decorators.js";
package/dist/metadata.js CHANGED
@@ -1,22 +1,13 @@
1
- // Lightweight metadata store replaces the `reflect-metadata` npm package.
2
- // All custom vela:* keys go through defineMetadata/getMetadata.
3
- // The Reflect.metadata polyfill keeps SWC's auto-emitted design:paramtypes working.
4
- const store = new WeakMap();
5
- function compositeKey(key, prop) {
6
- return prop !== undefined ? `${key}\0${String(prop)}` : key;
7
- }
1
+ // Single funnel: every Reflect.* metadata write/read routes into MetadataRegistry.
2
+ // SWC emits Reflect.metadata('design:paramtypes', ...) calls in compiled code;
3
+ // the polyfill catches those and parks them in the registry alongside vela's typed slots.
4
+ import { MetadataRegistry } from "./registry/metadata.registry.js";
8
5
  export function defineMetadata(key, value, target, propertyKey) {
9
- let map = store.get(target);
10
- if (!map) {
11
- map = new Map();
12
- store.set(target, map);
13
- }
14
- map.set(compositeKey(key, propertyKey), value);
6
+ MetadataRegistry.setReflectMetadata(target, key, value, propertyKey);
15
7
  }
16
8
  export function getMetadata(key, target, propertyKey) {
17
- return store.get(target)?.get(compositeKey(key, propertyKey));
9
+ return MetadataRegistry.getReflectMetadata(target, key, propertyKey);
18
10
  }
19
- // Minimal Reflect polyfill so the compiler's `Reflect.metadata(...)` calls work.
20
11
  if (typeof Reflect.defineMetadata !== 'function') {
21
12
  Reflect.defineMetadata = defineMetadata;
22
13
  Reflect.getMetadata = getMetadata;
@@ -1,6 +1,4 @@
1
- import type { Constructor } from '../registry/types';
2
- import type { Type } from '../container/types';
3
- import type { ModuleMetadata, ModuleOptions } from './types';
1
+ import type { Constructor, ModuleMetadata, ModuleOptions, Type } from '../registry/types';
4
2
  export declare function Global(): ClassDecorator;
5
3
  export declare function Module(options?: ModuleOptions): ClassDecorator;
6
4
  export declare function isModule(target: Constructor): boolean;
@@ -1,34 +1,27 @@
1
- import { METADATA_KEYS } from "../constants.js";
2
- import { defineMetadata, getMetadata } from "../metadata.js";
3
1
  import { MetadataRegistry } from "../registry/metadata.registry.js";
4
2
  export function Global() {
5
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
6
3
  return (target)=>{
7
- const existing = MetadataRegistry.getModuleOptions(target);
8
- MetadataRegistry.setModuleOptions(target, {
4
+ const ctor = target;
5
+ const existing = MetadataRegistry.getModuleOptions(ctor);
6
+ MetadataRegistry.setModuleOptions(ctor, {
9
7
  ...existing,
10
8
  isGlobal: true
11
9
  });
12
10
  };
13
11
  }
14
12
  export function Module(options = {}) {
15
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
16
13
  return (target)=>{
17
- const normalized = {
14
+ MetadataRegistry.setModuleOptions(target, {
18
15
  imports: options.imports,
19
16
  providers: options.providers,
20
17
  controllers: options.controllers,
21
18
  exports: options.exports,
22
19
  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);
20
+ });
28
21
  };
29
22
  }
30
23
  export function isModule(target) {
31
- return MetadataRegistry.getModuleOptions(target) !== undefined || getMetadata(METADATA_KEYS.MODULE, target) === true || getMetadata(METADATA_KEYS.MODULE_OPTIONS, target) !== undefined;
24
+ return MetadataRegistry.getModuleOptions(target) !== undefined;
32
25
  }
33
26
  export function createModuleRef(name) {
34
27
  const moduleClass = class {
@@ -39,13 +32,8 @@ export function createModuleRef(name) {
39
32
  return moduleClass;
40
33
  }
41
34
  export function getModuleMetadata(target) {
42
- if (!isModule(target)) {
43
- return undefined;
44
- }
45
- const options = MetadataRegistry.getModuleOptions(target) ?? getMetadata(METADATA_KEYS.MODULE_OPTIONS, target);
46
- if (!options || typeof options !== 'object') {
47
- return undefined;
48
- }
35
+ const options = MetadataRegistry.getModuleOptions(target);
36
+ if (!options) return undefined;
49
37
  return {
50
38
  providers: options.providers ?? [],
51
39
  controllers: options.controllers ?? [],
@@ -0,0 +1,10 @@
1
+ import type { Type } from '../container/types';
2
+ /**
3
+ * Walks the module dependency graph from a root, unwrapping ForwardRef and
4
+ * dynamic-module imports along the way, and returns every controller
5
+ * reachable from it. Pure metadata read — no Container, no side effects.
6
+ *
7
+ * Used by createOpenApiDocument and any other consumer that needs a
8
+ * controller list without bootstrapping the application.
9
+ */
10
+ export declare function collectControllers(rootModule: Type): Type[];