@velajs/vela 0.2.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 (64) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +84 -0
  4. package/dist/application.d.ts +44 -0
  5. package/dist/application.js +112 -0
  6. package/dist/constants.d.ts +35 -0
  7. package/dist/constants.js +43 -0
  8. package/dist/container/container.d.ts +25 -0
  9. package/dist/container/container.js +195 -0
  10. package/dist/container/decorators.d.ts +8 -0
  11. package/dist/container/decorators.js +36 -0
  12. package/dist/container/index.d.ts +4 -0
  13. package/dist/container/index.js +3 -0
  14. package/dist/container/types.d.ts +37 -0
  15. package/dist/container/types.js +11 -0
  16. package/dist/errors/http-exception.d.ts +61 -0
  17. package/dist/errors/http-exception.js +128 -0
  18. package/dist/errors/index.d.ts +1 -0
  19. package/dist/errors/index.js +1 -0
  20. package/dist/factory.d.ts +5 -0
  21. package/dist/factory.js +39 -0
  22. package/dist/http/decorators.d.ts +122 -0
  23. package/dist/http/decorators.js +276 -0
  24. package/dist/http/index.d.ts +3 -0
  25. package/dist/http/index.js +2 -0
  26. package/dist/http/route.manager.d.ts +34 -0
  27. package/dist/http/route.manager.js +373 -0
  28. package/dist/http/types.d.ts +29 -0
  29. package/dist/http/types.js +1 -0
  30. package/dist/index.d.ts +20 -0
  31. package/dist/index.js +26 -0
  32. package/dist/lifecycle/index.d.ts +20 -0
  33. package/dist/lifecycle/index.js +15 -0
  34. package/dist/metadata.d.ts +10 -0
  35. package/dist/metadata.js +29 -0
  36. package/dist/module/decorators.d.ts +5 -0
  37. package/dist/module/decorators.js +32 -0
  38. package/dist/module/index.d.ts +3 -0
  39. package/dist/module/index.js +2 -0
  40. package/dist/module/module-loader.d.ts +20 -0
  41. package/dist/module/module-loader.js +159 -0
  42. package/dist/module/types.d.ts +19 -0
  43. package/dist/module/types.js +1 -0
  44. package/dist/pipeline/component.manager.d.ts +18 -0
  45. package/dist/pipeline/component.manager.js +105 -0
  46. package/dist/pipeline/decorators.d.ts +10 -0
  47. package/dist/pipeline/decorators.js +50 -0
  48. package/dist/pipeline/index.d.ts +7 -0
  49. package/dist/pipeline/index.js +5 -0
  50. package/dist/pipeline/pipes.d.ts +25 -0
  51. package/dist/pipeline/pipes.js +52 -0
  52. package/dist/pipeline/reflector.d.ts +102 -0
  53. package/dist/pipeline/reflector.js +166 -0
  54. package/dist/pipeline/tokens.d.ts +33 -0
  55. package/dist/pipeline/tokens.js +27 -0
  56. package/dist/pipeline/types.d.ts +31 -0
  57. package/dist/pipeline/types.js +1 -0
  58. package/dist/registry/index.d.ts +2 -0
  59. package/dist/registry/index.js +1 -0
  60. package/dist/registry/metadata.registry.d.ts +61 -0
  61. package/dist/registry/metadata.registry.js +276 -0
  62. package/dist/registry/types.d.ts +55 -0
  63. package/dist/registry/types.js +2 -0
  64. package/package.json +72 -0
@@ -0,0 +1,11 @@
1
+ export class InjectionToken {
2
+ description;
3
+ options;
4
+ constructor(description, options){
5
+ this.description = description;
6
+ this.options = options;
7
+ }
8
+ toString() {
9
+ return `InjectionToken(${this.description})`;
10
+ }
11
+ }
@@ -0,0 +1,61 @@
1
+ export declare class HttpException extends Error {
2
+ readonly statusCode: number;
3
+ readonly response?: unknown | undefined;
4
+ constructor(message: string, statusCode: number, response?: unknown | undefined);
5
+ getStatus(): number;
6
+ getResponse(): unknown;
7
+ }
8
+ export declare class BadRequestException extends HttpException {
9
+ constructor(message?: string, response?: unknown);
10
+ }
11
+ export declare class UnauthorizedException extends HttpException {
12
+ constructor(message?: string, response?: unknown);
13
+ }
14
+ export declare class ForbiddenException extends HttpException {
15
+ constructor(message?: string, response?: unknown);
16
+ }
17
+ export declare class NotFoundException extends HttpException {
18
+ constructor(message?: string, response?: unknown);
19
+ }
20
+ export declare class MethodNotAllowedException extends HttpException {
21
+ constructor(message?: string, response?: unknown);
22
+ }
23
+ export declare class NotAcceptableException extends HttpException {
24
+ constructor(message?: string, response?: unknown);
25
+ }
26
+ export declare class RequestTimeoutException extends HttpException {
27
+ constructor(message?: string, response?: unknown);
28
+ }
29
+ export declare class ConflictException extends HttpException {
30
+ constructor(message?: string, response?: unknown);
31
+ }
32
+ export declare class GoneException extends HttpException {
33
+ constructor(message?: string, response?: unknown);
34
+ }
35
+ export declare class PayloadTooLargeException extends HttpException {
36
+ constructor(message?: string, response?: unknown);
37
+ }
38
+ export declare class UnsupportedMediaTypeException extends HttpException {
39
+ constructor(message?: string, response?: unknown);
40
+ }
41
+ export declare class UnprocessableEntityException extends HttpException {
42
+ constructor(message?: string, response?: unknown);
43
+ }
44
+ export declare class TooManyRequestsException extends HttpException {
45
+ constructor(message?: string, response?: unknown);
46
+ }
47
+ export declare class InternalServerErrorException extends HttpException {
48
+ constructor(message?: string, response?: unknown);
49
+ }
50
+ export declare class NotImplementedException extends HttpException {
51
+ constructor(message?: string, response?: unknown);
52
+ }
53
+ export declare class BadGatewayException extends HttpException {
54
+ constructor(message?: string, response?: unknown);
55
+ }
56
+ export declare class ServiceUnavailableException extends HttpException {
57
+ constructor(message?: string, response?: unknown);
58
+ }
59
+ export declare class GatewayTimeoutException extends HttpException {
60
+ constructor(message?: string, response?: unknown);
61
+ }
@@ -0,0 +1,128 @@
1
+ export class HttpException extends Error {
2
+ statusCode;
3
+ response;
4
+ constructor(message, statusCode, response){
5
+ super(message), this.statusCode = statusCode, this.response = response;
6
+ this.name = 'HttpException';
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ }
9
+ getStatus() {
10
+ return this.statusCode;
11
+ }
12
+ getResponse() {
13
+ return this.response ?? {
14
+ statusCode: this.statusCode,
15
+ message: this.message
16
+ };
17
+ }
18
+ }
19
+ // 4xx
20
+ export class BadRequestException extends HttpException {
21
+ constructor(message = 'Bad Request', response){
22
+ super(message, 400, response);
23
+ this.name = 'BadRequestException';
24
+ }
25
+ }
26
+ export class UnauthorizedException extends HttpException {
27
+ constructor(message = 'Unauthorized', response){
28
+ super(message, 401, response);
29
+ this.name = 'UnauthorizedException';
30
+ }
31
+ }
32
+ export class ForbiddenException extends HttpException {
33
+ constructor(message = 'Forbidden', response){
34
+ super(message, 403, response);
35
+ this.name = 'ForbiddenException';
36
+ }
37
+ }
38
+ export class NotFoundException extends HttpException {
39
+ constructor(message = 'Not Found', response){
40
+ super(message, 404, response);
41
+ this.name = 'NotFoundException';
42
+ }
43
+ }
44
+ export class MethodNotAllowedException extends HttpException {
45
+ constructor(message = 'Method Not Allowed', response){
46
+ super(message, 405, response);
47
+ this.name = 'MethodNotAllowedException';
48
+ }
49
+ }
50
+ export class NotAcceptableException extends HttpException {
51
+ constructor(message = 'Not Acceptable', response){
52
+ super(message, 406, response);
53
+ this.name = 'NotAcceptableException';
54
+ }
55
+ }
56
+ export class RequestTimeoutException extends HttpException {
57
+ constructor(message = 'Request Timeout', response){
58
+ super(message, 408, response);
59
+ this.name = 'RequestTimeoutException';
60
+ }
61
+ }
62
+ export class ConflictException extends HttpException {
63
+ constructor(message = 'Conflict', response){
64
+ super(message, 409, response);
65
+ this.name = 'ConflictException';
66
+ }
67
+ }
68
+ export class GoneException extends HttpException {
69
+ constructor(message = 'Gone', response){
70
+ super(message, 410, response);
71
+ this.name = 'GoneException';
72
+ }
73
+ }
74
+ export class PayloadTooLargeException extends HttpException {
75
+ constructor(message = 'Payload Too Large', response){
76
+ super(message, 413, response);
77
+ this.name = 'PayloadTooLargeException';
78
+ }
79
+ }
80
+ export class UnsupportedMediaTypeException extends HttpException {
81
+ constructor(message = 'Unsupported Media Type', response){
82
+ super(message, 415, response);
83
+ this.name = 'UnsupportedMediaTypeException';
84
+ }
85
+ }
86
+ export class UnprocessableEntityException extends HttpException {
87
+ constructor(message = 'Unprocessable Entity', response){
88
+ super(message, 422, response);
89
+ this.name = 'UnprocessableEntityException';
90
+ }
91
+ }
92
+ export class TooManyRequestsException extends HttpException {
93
+ constructor(message = 'Too Many Requests', response){
94
+ super(message, 429, response);
95
+ this.name = 'TooManyRequestsException';
96
+ }
97
+ }
98
+ // 5xx
99
+ export class InternalServerErrorException extends HttpException {
100
+ constructor(message = 'Internal Server Error', response){
101
+ super(message, 500, response);
102
+ this.name = 'InternalServerErrorException';
103
+ }
104
+ }
105
+ export class NotImplementedException extends HttpException {
106
+ constructor(message = 'Not Implemented', response){
107
+ super(message, 501, response);
108
+ this.name = 'NotImplementedException';
109
+ }
110
+ }
111
+ export class BadGatewayException extends HttpException {
112
+ constructor(message = 'Bad Gateway', response){
113
+ super(message, 502, response);
114
+ this.name = 'BadGatewayException';
115
+ }
116
+ }
117
+ export class ServiceUnavailableException extends HttpException {
118
+ constructor(message = 'Service Unavailable', response){
119
+ super(message, 503, response);
120
+ this.name = 'ServiceUnavailableException';
121
+ }
122
+ }
123
+ export class GatewayTimeoutException extends HttpException {
124
+ constructor(message = 'Gateway Timeout', response){
125
+ super(message, 504, response);
126
+ this.name = 'GatewayTimeoutException';
127
+ }
128
+ }
@@ -0,0 +1 @@
1
+ export { HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, GoneException, PayloadTooLargeException, UnsupportedMediaTypeException, UnprocessableEntityException, TooManyRequestsException, InternalServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, GatewayTimeoutException, } from './http-exception';
@@ -0,0 +1 @@
1
+ export { HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, MethodNotAllowedException, NotAcceptableException, RequestTimeoutException, ConflictException, GoneException, PayloadTooLargeException, UnsupportedMediaTypeException, UnprocessableEntityException, TooManyRequestsException, InternalServerErrorException, NotImplementedException, BadGatewayException, ServiceUnavailableException, GatewayTimeoutException } from "./http-exception.js";
@@ -0,0 +1,5 @@
1
+ import { VelaApplication } from './application';
2
+ import type { Type } from './container/types';
3
+ export declare const VelaFactory: {
4
+ create(rootModule: Type): Promise<VelaApplication>;
5
+ };
@@ -0,0 +1,39 @@
1
+ import { VelaApplication } from "./application.js";
2
+ import { Container } from "./container/container.js";
3
+ import { RouteManager } from "./http/route.manager.js";
4
+ import { ModuleLoader } from "./module/module-loader.js";
5
+ import { ComponentManager } from "./pipeline/component.manager.js";
6
+ import { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./pipeline/tokens.js";
7
+ export const VelaFactory = {
8
+ async create (rootModule) {
9
+ const container = new Container();
10
+ const routeManager = new RouteManager(container);
11
+ ComponentManager.init(container);
12
+ const loader = new ModuleLoader(container, routeManager);
13
+ loader.load(rootModule);
14
+ // Resolve APP_* tokens registered via module providers
15
+ if (container.has(APP_GUARD)) {
16
+ routeManager.useGlobalGuards(container.resolve(APP_GUARD));
17
+ }
18
+ if (container.has(APP_PIPE)) {
19
+ routeManager.useGlobalPipes(container.resolve(APP_PIPE));
20
+ }
21
+ if (container.has(APP_INTERCEPTOR)) {
22
+ routeManager.useGlobalInterceptors(container.resolve(APP_INTERCEPTOR));
23
+ }
24
+ if (container.has(APP_FILTER)) {
25
+ routeManager.useGlobalFilters(container.resolve(APP_FILTER));
26
+ }
27
+ if (container.has(APP_MIDDLEWARE)) {
28
+ routeManager.useGlobalMiddleware(container.resolve(APP_MIDDLEWARE));
29
+ }
30
+ const app = new VelaApplication(container, routeManager);
31
+ const instances = loader.resolveAllInstances();
32
+ app.setInstances(instances);
33
+ await app.callOnModuleInit();
34
+ await app.callOnApplicationBootstrap();
35
+ // Build routes (async — supports CRUD integration with dynamic imports)
36
+ await app.initRoutes();
37
+ return app;
38
+ }
39
+ };
@@ -0,0 +1,122 @@
1
+ import type { Constructor, PipeType } from '../registry/types';
2
+ import type { ControllerOptions } from './types';
3
+ import type { ExecutionContext } from '../pipeline/types';
4
+ /**
5
+ * Marks a class as a controller.
6
+ * Accepts a prefix string or an options object with prefix and version.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * @Controller('/users')
11
+ * @Controller({ prefix: '/users', version: 1 })
12
+ * @Controller({ prefix: '/users', version: [1, 2] })
13
+ * ```
14
+ */
15
+ export declare function Controller(prefixOrOptions?: string | ControllerOptions): ClassDecorator;
16
+ /**
17
+ * Override the version for a specific route method.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * @Controller({ prefix: '/users', version: 1 })
22
+ * class UserController {
23
+ * @Get()
24
+ * listV1() { ... } // GET /v1/users
25
+ *
26
+ * @Version(2)
27
+ * @Get()
28
+ * listV2() { ... } // GET /v2/users
29
+ * }
30
+ * ```
31
+ */
32
+ export declare function Version(version: number | number[]): MethodDecorator;
33
+ export declare function getRouteVersion(target: Constructor, propertyKey: string | symbol): number | number[] | undefined;
34
+ export declare const Get: (path?: string) => MethodDecorator;
35
+ export declare const Post: (path?: string) => MethodDecorator;
36
+ export declare const Put: (path?: string) => MethodDecorator;
37
+ export declare const Patch: (path?: string) => MethodDecorator;
38
+ export declare const Delete: (path?: string) => MethodDecorator;
39
+ export declare const Options: (path?: string) => MethodDecorator;
40
+ export declare const Head: (path?: string) => MethodDecorator;
41
+ export declare const Param: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
42
+ export declare const Query: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
43
+ export declare const Body: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
44
+ export declare const Headers: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
45
+ export declare const Req: (nameOrPipe?: string | PipeType, ...pipes: PipeType[]) => ParameterDecorator;
46
+ /**
47
+ * Factory for creating custom parameter decorators.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * const CurrentUser = createParamDecorator(
52
+ * (data: unknown, ctx: ExecutionContext) => {
53
+ * const req = ctx.getRequest();
54
+ * return req.headers.get('x-user-id');
55
+ * }
56
+ * );
57
+ *
58
+ * // Usage:
59
+ * @Get()
60
+ * handle(@CurrentUser() userId: string) { ... }
61
+ *
62
+ * // With data argument:
63
+ * const Header = createParamDecorator(
64
+ * (data: string, ctx: ExecutionContext) => {
65
+ * return ctx.getRequest().headers.get(data);
66
+ * }
67
+ * );
68
+ *
69
+ * @Get()
70
+ * handle(@Header('x-request-id') requestId: string) { ... }
71
+ * ```
72
+ */
73
+ export declare function createParamDecorator<TData = unknown>(factory: (data: TData, ctx: ExecutionContext) => unknown): (data?: TData, ...pipes: PipeType[]) => ParameterDecorator;
74
+ /**
75
+ * Override the HTTP status code for a handler's response.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * @Post()
80
+ * @HttpCode(201)
81
+ * create(@Body() data: CreateDto) { return data; }
82
+ * ```
83
+ */
84
+ export declare function HttpCode(statusCode: number): MethodDecorator;
85
+ /**
86
+ * Set a response header declaratively.
87
+ * Can be applied multiple times to set multiple headers.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * @Get()
92
+ * @Header('Cache-Control', 'no-cache')
93
+ * @Header('X-Custom', 'value')
94
+ * handle() { return { ok: true }; }
95
+ * ```
96
+ */
97
+ export declare function Header(name: string, value: string): MethodDecorator;
98
+ /**
99
+ * Redirect to another URL. The handler's return value can override the URL.
100
+ *
101
+ * @example
102
+ * ```ts
103
+ * @Get('/old')
104
+ * @Redirect('/new', 301)
105
+ * handleOld() {}
106
+ *
107
+ * // Dynamic redirect — return { url, statusCode? } to override
108
+ * @Get('/go')
109
+ * @Redirect('/default')
110
+ * handleGo(@Query('to') to?: string) {
111
+ * if (to) return { url: to };
112
+ * }
113
+ * ```
114
+ */
115
+ export declare function Redirect(url: string, statusCode?: number): MethodDecorator;
116
+ export declare function getHttpCode(target: Constructor, method: string | symbol): number | undefined;
117
+ export declare function getResponseHeaders(target: Constructor, method: string | symbol): Array<[string, string]>;
118
+ export declare function getRedirect(target: Constructor, method: string | symbol): {
119
+ url: string;
120
+ statusCode: number;
121
+ } | undefined;
122
+ export declare function isController(target: Constructor): boolean;
@@ -0,0 +1,276 @@
1
+ import { HttpMethod, METADATA_KEYS, ParamType, Scope } from "../constants.js";
2
+ import { defineMetadata, getMetadata } from "../metadata.js";
3
+ import { MetadataRegistry } from "../registry/metadata.registry.js";
4
+ function normalizePath(path) {
5
+ return path && !path.startsWith('/') ? `/${path}` : path;
6
+ }
7
+ /**
8
+ * Marks a class as a controller.
9
+ * Accepts a prefix string or an options object with prefix and version.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * @Controller('/users')
14
+ * @Controller({ prefix: '/users', version: 1 })
15
+ * @Controller({ prefix: '/users', version: [1, 2] })
16
+ * ```
17
+ */ export function Controller(prefixOrOptions) {
18
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
19
+ return (target)=>{
20
+ let prefix;
21
+ let version;
22
+ if (typeof prefixOrOptions === 'string') {
23
+ prefix = prefixOrOptions;
24
+ } else if (prefixOrOptions) {
25
+ prefix = prefixOrOptions.prefix ?? '';
26
+ version = prefixOrOptions.version;
27
+ } else {
28
+ prefix = '';
29
+ }
30
+ MetadataRegistry.setControllerPath(target, normalizePath(prefix));
31
+ if (version !== undefined) {
32
+ MetadataRegistry.setControllerOptions(target, {
33
+ version
34
+ });
35
+ }
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);
41
+ };
42
+ }
43
+ /**
44
+ * Override the version for a specific route method.
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * @Controller({ prefix: '/users', version: 1 })
49
+ * class UserController {
50
+ * @Get()
51
+ * listV1() { ... } // GET /v1/users
52
+ *
53
+ * @Version(2)
54
+ * @Get()
55
+ * listV2() { ... } // GET /v2/users
56
+ * }
57
+ * ```
58
+ */ export function Version(version) {
59
+ 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);
67
+ if (route) {
68
+ route.version = version;
69
+ }
70
+ };
71
+ }
72
+ export function getRouteVersion(target, propertyKey) {
73
+ return MetadataRegistry.getRouteVersion(target, propertyKey) ?? getMetadata(`vela:route-version:${String(propertyKey)}`, target);
74
+ }
75
+ function createMethodDecorator(method) {
76
+ return (path = '')=>{
77
+ return (target, propertyKey, _descriptor)=>{
78
+ 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, {
82
+ method: method,
83
+ path: normalizedPath,
84
+ handlerName: propertyKey,
85
+ ...version !== undefined ? {
86
+ version
87
+ } : {}
88
+ });
89
+ };
90
+ };
91
+ }
92
+ export const Get = createMethodDecorator(HttpMethod.GET);
93
+ export const Post = createMethodDecorator(HttpMethod.POST);
94
+ export const Put = createMethodDecorator(HttpMethod.PUT);
95
+ export const Patch = createMethodDecorator(HttpMethod.PATCH);
96
+ export const Delete = createMethodDecorator(HttpMethod.DELETE);
97
+ export const Options = createMethodDecorator(HttpMethod.OPTIONS);
98
+ export const Head = createMethodDecorator(HttpMethod.HEAD);
99
+ // Parameter decorators
100
+ const CUSTOM_PARAM_TYPE = 'custom';
101
+ function createBuiltinParamDecorator(type) {
102
+ return (nameOrPipe, ...pipes)=>{
103
+ return (target, propertyKey, parameterIndex)=>{
104
+ if (propertyKey === undefined) {
105
+ throw new Error('Parameter decorators can only be used on method parameters');
106
+ }
107
+ let name;
108
+ let allPipes;
109
+ if (typeof nameOrPipe === 'string') {
110
+ name = nameOrPipe;
111
+ allPipes = pipes;
112
+ } else if (nameOrPipe !== undefined) {
113
+ name = undefined;
114
+ allPipes = [
115
+ nameOrPipe,
116
+ ...pipes
117
+ ];
118
+ } else {
119
+ name = undefined;
120
+ allPipes = pipes;
121
+ }
122
+ MetadataRegistry.addParameter(target.constructor, propertyKey, {
123
+ index: parameterIndex,
124
+ type,
125
+ name,
126
+ ...allPipes.length > 0 ? {
127
+ pipes: allPipes
128
+ } : {}
129
+ });
130
+ };
131
+ };
132
+ }
133
+ export const Param = createBuiltinParamDecorator(ParamType.PARAM);
134
+ export const Query = createBuiltinParamDecorator(ParamType.QUERY);
135
+ export const Body = createBuiltinParamDecorator(ParamType.BODY);
136
+ export const Headers = createBuiltinParamDecorator(ParamType.HEADERS);
137
+ export const Req = createBuiltinParamDecorator(ParamType.REQUEST);
138
+ /**
139
+ * Factory for creating custom parameter decorators.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const CurrentUser = createParamDecorator(
144
+ * (data: unknown, ctx: ExecutionContext) => {
145
+ * const req = ctx.getRequest();
146
+ * return req.headers.get('x-user-id');
147
+ * }
148
+ * );
149
+ *
150
+ * // Usage:
151
+ * @Get()
152
+ * handle(@CurrentUser() userId: string) { ... }
153
+ *
154
+ * // With data argument:
155
+ * const Header = createParamDecorator(
156
+ * (data: string, ctx: ExecutionContext) => {
157
+ * return ctx.getRequest().headers.get(data);
158
+ * }
159
+ * );
160
+ *
161
+ * @Get()
162
+ * handle(@Header('x-request-id') requestId: string) { ... }
163
+ * ```
164
+ */ export function createParamDecorator(factory) {
165
+ return (data, ...pipes)=>{
166
+ return (target, propertyKey, parameterIndex)=>{
167
+ if (propertyKey === undefined) {
168
+ throw new Error('Parameter decorators can only be used on method parameters');
169
+ }
170
+ MetadataRegistry.addParameter(target.constructor, propertyKey, {
171
+ index: parameterIndex,
172
+ type: CUSTOM_PARAM_TYPE,
173
+ name: undefined,
174
+ factory: (_unused, ctx)=>{
175
+ const honoCtx = ctx;
176
+ const execCtx = {
177
+ getClass: ()=>target.constructor,
178
+ getHandler: ()=>propertyKey,
179
+ getContext: ()=>honoCtx,
180
+ getRequest: ()=>honoCtx.req.raw
181
+ };
182
+ return factory(data, execCtx);
183
+ },
184
+ ...pipes.length > 0 ? {
185
+ pipes
186
+ } : {}
187
+ });
188
+ };
189
+ };
190
+ }
191
+ // Response decorators
192
+ /**
193
+ * Override the HTTP status code for a handler's response.
194
+ *
195
+ * @example
196
+ * ```ts
197
+ * @Post()
198
+ * @HttpCode(201)
199
+ * create(@Body() data: CreateDto) { return data; }
200
+ * ```
201
+ */ export function HttpCode(statusCode) {
202
+ return (target, propertyKey, _descriptor)=>{
203
+ MetadataRegistry.setHandlerHttpMeta(target.constructor, propertyKey, {
204
+ httpCode: statusCode
205
+ });
206
+ };
207
+ }
208
+ /**
209
+ * Set a response header declaratively.
210
+ * Can be applied multiple times to set multiple headers.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * @Get()
215
+ * @Header('Cache-Control', 'no-cache')
216
+ * @Header('X-Custom', 'value')
217
+ * handle() { return { ok: true }; }
218
+ * ```
219
+ */ export function Header(name, value) {
220
+ return (target, propertyKey, _descriptor)=>{
221
+ MetadataRegistry.setHandlerHttpMeta(target.constructor, propertyKey, {
222
+ responseHeaders: [
223
+ [
224
+ name,
225
+ value
226
+ ]
227
+ ]
228
+ });
229
+ };
230
+ }
231
+ /**
232
+ * Redirect to another URL. The handler's return value can override the URL.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * @Get('/old')
237
+ * @Redirect('/new', 301)
238
+ * handleOld() {}
239
+ *
240
+ * // Dynamic redirect — return { url, statusCode? } to override
241
+ * @Get('/go')
242
+ * @Redirect('/default')
243
+ * handleGo(@Query('to') to?: string) {
244
+ * if (to) return { url: to };
245
+ * }
246
+ * ```
247
+ */ export function Redirect(url, statusCode = 302) {
248
+ return (target, propertyKey, _descriptor)=>{
249
+ MetadataRegistry.setHandlerHttpMeta(target.constructor, propertyKey, {
250
+ redirect: {
251
+ url,
252
+ statusCode
253
+ }
254
+ });
255
+ };
256
+ }
257
+ // Metadata readers (used by RouteManager)
258
+ export function getHttpCode(target, method) {
259
+ const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
260
+ if (meta?.httpCode !== undefined) return meta.httpCode;
261
+ return getMetadata(METADATA_KEYS.HTTP_CODE, target, method);
262
+ }
263
+ export function getResponseHeaders(target, method) {
264
+ const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
265
+ if (meta?.responseHeaders) return meta.responseHeaders;
266
+ return getMetadata(METADATA_KEYS.RESPONSE_HEADERS, target, method) ?? [];
267
+ }
268
+ export function getRedirect(target, method) {
269
+ const meta = MetadataRegistry.getHandlerHttpMeta(target, method);
270
+ if (meta?.redirect) return meta.redirect;
271
+ return getMetadata(METADATA_KEYS.REDIRECT, target, method);
272
+ }
273
+ // Helpers
274
+ export function isController(target) {
275
+ return MetadataRegistry.getControllerPath(target) !== '' || MetadataRegistry.getRoutes(target).length > 0;
276
+ }
@@ -0,0 +1,3 @@
1
+ export { RouteManager } from './route.manager';
2
+ export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, Param, Query, Body, Headers, Req, HttpCode, Header, Redirect, createParamDecorator, isController, } from './decorators';
3
+ export type { RouteMetadata, ControllerMetadata, ControllerOptions, ParamMetadata, ControllerRegistration, } from './types';
@@ -0,0 +1,2 @@
1
+ export { RouteManager } from "./route.manager.js";
2
+ export { Controller, Version, Get, Post, Put, Patch, Delete, Options, Head, Param, Query, Body, Headers, Req, HttpCode, Header, Redirect, createParamDecorator, isController } from "./decorators.js";