@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,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";
@@ -301,31 +302,25 @@ export class RouteManager {
301
302
  await buildCrudRoutes(app, controller, metadata.prefix, crudConfig, {
302
303
  globalPrefix: this.globalPrefix,
303
304
  globalGuards: this.instantiateMany(this.globalGuards, this.container),
304
- 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
305
310
  });
306
- } catch {
307
- throw new Error(`@Crud() requires '@velajs/crud'. Install it: bun add @velajs/crud`);
308
311
  }
309
312
  }
310
313
  }
311
314
  return app;
312
315
  }
313
316
  createExecutionContext(c, controller, route) {
314
- return {
315
- getType: ()=>'http',
316
- getClass: ()=>controller,
317
- getHandler: ()=>route.handlerName,
318
- getContext: ()=>c,
319
- getRequest: ()=>c.req.raw,
320
- switchToHttp: ()=>({
321
- getRequest: ()=>c.req.raw,
322
- getResponse: ()=>c
323
- })
324
- };
317
+ return buildExecutionContext(c, controller, route.handlerName);
325
318
  }
326
319
  createHandler(route, controller, allParamMetadata) {
327
320
  const paramMetadata = (allParamMetadata.get(route.handlerName) || []).sort((a, b)=>a.index - b.index);
328
- // 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).
329
324
  const paramTypes = Reflect.getMetadata('design:paramtypes', controller.prototype, route.handlerName);
330
325
  // Collect controller + method level components at build time
331
326
  const methodGuards = ComponentManager.getComponents('guard', controller, route.handlerName);
@@ -470,12 +465,16 @@ export class RouteManager {
470
465
  registerRoute(app, method, path, handler) {
471
466
  const normalizedPath = path || '/';
472
467
  const registrar = RouteManager.METHOD_REGISTRAR.get(method);
473
- 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
+ }
474
473
  }
475
474
  buildVersionedPaths(globalPrefix, controllerPrefix, routePath, version) {
476
475
  if (version === undefined) {
477
476
  return [
478
- this.joinPaths(globalPrefix, this.joinPaths(controllerPrefix, routePath))
477
+ joinPaths(globalPrefix, joinPaths(controllerPrefix, routePath))
479
478
  ];
480
479
  }
481
480
  const versions = Array.isArray(version) ? version : [
@@ -483,14 +482,9 @@ export class RouteManager {
483
482
  ];
484
483
  return versions.map((v)=>{
485
484
  const versionSegment = `/v${v}`;
486
- return this.joinPaths(globalPrefix, this.joinPaths(versionSegment, this.joinPaths(controllerPrefix, routePath)));
485
+ return joinPaths(globalPrefix, joinPaths(versionSegment, joinPaths(controllerPrefix, routePath)));
487
486
  });
488
487
  }
489
- joinPaths(prefix, path) {
490
- const cleanPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
491
- const cleanPath = path && !path.startsWith('/') ? `/${path}` : path;
492
- return `${cleanPrefix}${cleanPath}` || '/';
493
- }
494
488
  compilePathMatcher(routePath) {
495
489
  if (routePath === '*') return ()=>true;
496
490
  const norm = routePath.startsWith('/') ? routePath : `/${routePath}`;
@@ -505,7 +499,7 @@ export class RouteManager {
505
499
  const matchPath = this.compilePathMatcher(route.path);
506
500
  return (path, method)=>{
507
501
  if (!matchPath(path)) return false;
508
- if (route.method && route.method !== RequestMethod.ALL) return method === route.method;
502
+ if (route.method && route.method !== HttpMethod.ALL) return method === route.method;
509
503
  return true;
510
504
  };
511
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';
@@ -28,7 +28,6 @@ export { ThrottlerModule, ThrottlerGuard, ThrottlerStorage, Throttle, SkipThrott
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";
@@ -31,25 +31,20 @@ export { HealthModule, HealthCheckService, HealthIndicatorService, HttpHealthInd
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[];
@@ -0,0 +1,35 @@
1
+ import { ForwardRef } from "../container/types.js";
2
+ import { getModuleMetadata } from "./decorators.js";
3
+ function isDynamicModuleLike(value) {
4
+ return !!value && typeof value === 'object' && 'module' in value && typeof value.module === 'function';
5
+ }
6
+ /**
7
+ * Walks the module dependency graph from a root, unwrapping ForwardRef and
8
+ * dynamic-module imports along the way, and returns every controller
9
+ * reachable from it. Pure metadata read — no Container, no side effects.
10
+ *
11
+ * Used by createOpenApiDocument and any other consumer that needs a
12
+ * controller list without bootstrapping the application.
13
+ */ export function collectControllers(rootModule) {
14
+ const visited = new Set();
15
+ const controllers = new Set();
16
+ const visit = (entry)=>{
17
+ const unwrapped = entry instanceof ForwardRef ? entry.factory() : entry;
18
+ const moduleClass = isDynamicModuleLike(unwrapped) ? unwrapped.module : unwrapped;
19
+ const extraControllers = isDynamicModuleLike(unwrapped) ? unwrapped.controllers ?? [] : [];
20
+ const extraImports = isDynamicModuleLike(unwrapped) ? unwrapped.imports ?? [] : [];
21
+ if (typeof moduleClass !== 'function' || visited.has(moduleClass)) return;
22
+ visited.add(moduleClass);
23
+ const metadata = getModuleMetadata(moduleClass);
24
+ if (metadata) {
25
+ for (const controller of metadata.controllers)controllers.add(controller);
26
+ for (const imp of metadata.imports)visit(imp);
27
+ }
28
+ for (const controller of extraControllers)controllers.add(controller);
29
+ for (const imp of extraImports)visit(imp);
30
+ };
31
+ visit(rootModule);
32
+ return [
33
+ ...controllers
34
+ ];
35
+ }
@@ -1,3 +1,5 @@
1
1
  export { Global, Module, isModule, getModuleMetadata, createModuleRef } from './decorators';
2
2
  export { ModuleLoader } from './module-loader';
3
+ export { MiddlewareBuilder } from './middleware';
4
+ export type { MiddlewareConsumer, MiddlewareConfigProxy, MiddlewareRouteDefinition, NestModule, RouteInfo, } from './middleware';
3
5
  export type { ModuleOptions, ModuleMetadata, DynamicModule, AsyncModuleOptions, ModuleImport } from './types';
@@ -1,2 +1,3 @@
1
1
  export { Global, Module, isModule, getModuleMetadata, createModuleRef } from "./decorators.js";
2
2
  export { ModuleLoader } from "./module-loader.js";
3
+ export { MiddlewareBuilder } from "./middleware.js";
@@ -1,19 +1,9 @@
1
+ import { HttpMethod } from '../constants';
1
2
  import type { NestMiddleware } from '../pipeline/types';
2
- import type { Type } from '../registry/types';
3
- export declare const RequestMethod: {
4
- readonly GET: "GET";
5
- readonly POST: "POST";
6
- readonly PUT: "PUT";
7
- readonly DELETE: "DELETE";
8
- readonly PATCH: "PATCH";
9
- readonly OPTIONS: "OPTIONS";
10
- readonly HEAD: "HEAD";
11
- readonly ALL: "ALL";
12
- };
13
- export type RequestMethod = (typeof RequestMethod)[keyof typeof RequestMethod];
3
+ import type { Constructor, Type } from '../registry/types';
14
4
  export interface RouteInfo {
15
5
  path: string;
16
- method?: RequestMethod;
6
+ method?: HttpMethod;
17
7
  }
18
8
  export interface MiddlewareRouteDefinition {
19
9
  middleware: Array<Type<NestMiddleware> | NestMiddleware>;
@@ -25,7 +15,7 @@ export interface MiddlewareRouteDefinition {
25
15
  export interface MiddlewareConfigProxy {
26
16
  exclude(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
27
17
  withPriority(priority: number): MiddlewareConfigProxy;
28
- forRoutes(...routes: Array<string | Function | RouteInfo>): MiddlewareConsumer;
18
+ forRoutes(...routes: Array<string | Constructor | RouteInfo>): MiddlewareConsumer;
29
19
  }
30
20
  export interface MiddlewareConsumer {
31
21
  apply(...middleware: Array<Type<NestMiddleware> | NestMiddleware>): MiddlewareConfigProxy;