@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.
- package/CHANGELOG.md +19 -0
- package/LICENSE +21 -0
- package/README.md +84 -0
- package/dist/application.d.ts +44 -0
- package/dist/application.js +112 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.js +43 -0
- package/dist/container/container.d.ts +25 -0
- package/dist/container/container.js +195 -0
- package/dist/container/decorators.d.ts +8 -0
- package/dist/container/decorators.js +36 -0
- package/dist/container/index.d.ts +4 -0
- package/dist/container/index.js +3 -0
- package/dist/container/types.d.ts +37 -0
- package/dist/container/types.js +11 -0
- package/dist/errors/http-exception.d.ts +61 -0
- package/dist/errors/http-exception.js +128 -0
- package/dist/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/factory.d.ts +5 -0
- package/dist/factory.js +39 -0
- package/dist/http/decorators.d.ts +122 -0
- package/dist/http/decorators.js +276 -0
- package/dist/http/index.d.ts +3 -0
- package/dist/http/index.js +2 -0
- package/dist/http/route.manager.d.ts +34 -0
- package/dist/http/route.manager.js +373 -0
- package/dist/http/types.d.ts +29 -0
- package/dist/http/types.js +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +26 -0
- package/dist/lifecycle/index.d.ts +20 -0
- package/dist/lifecycle/index.js +15 -0
- package/dist/metadata.d.ts +10 -0
- package/dist/metadata.js +29 -0
- package/dist/module/decorators.d.ts +5 -0
- package/dist/module/decorators.js +32 -0
- package/dist/module/index.d.ts +3 -0
- package/dist/module/index.js +2 -0
- package/dist/module/module-loader.d.ts +20 -0
- package/dist/module/module-loader.js +159 -0
- package/dist/module/types.d.ts +19 -0
- package/dist/module/types.js +1 -0
- package/dist/pipeline/component.manager.d.ts +18 -0
- package/dist/pipeline/component.manager.js +105 -0
- package/dist/pipeline/decorators.d.ts +10 -0
- package/dist/pipeline/decorators.js +50 -0
- package/dist/pipeline/index.d.ts +7 -0
- package/dist/pipeline/index.js +5 -0
- package/dist/pipeline/pipes.d.ts +25 -0
- package/dist/pipeline/pipes.js +52 -0
- package/dist/pipeline/reflector.d.ts +102 -0
- package/dist/pipeline/reflector.js +166 -0
- package/dist/pipeline/tokens.d.ts +33 -0
- package/dist/pipeline/tokens.js +27 -0
- package/dist/pipeline/types.d.ts +31 -0
- package/dist/pipeline/types.js +1 -0
- package/dist/registry/index.d.ts +2 -0
- package/dist/registry/index.js +1 -0
- package/dist/registry/metadata.registry.d.ts +61 -0
- package/dist/registry/metadata.registry.js +276 -0
- package/dist/registry/types.d.ts +55 -0
- package/dist/registry/types.js +2 -0
- package/package.json +72 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { getModuleMetadata, isModule } from "./decorators.js";
|
|
2
|
+
function isDynamicModule(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null && 'module' in value && typeof value.module === 'function';
|
|
4
|
+
}
|
|
5
|
+
export class ModuleLoader {
|
|
6
|
+
container;
|
|
7
|
+
router;
|
|
8
|
+
processedModules = new Set();
|
|
9
|
+
processingStack = new Set();
|
|
10
|
+
collectedControllers = [];
|
|
11
|
+
registeredProviders = [];
|
|
12
|
+
moduleExportsCache = new Map();
|
|
13
|
+
constructor(container, router){
|
|
14
|
+
this.container = container;
|
|
15
|
+
this.router = router;
|
|
16
|
+
}
|
|
17
|
+
load(rootModule) {
|
|
18
|
+
this.processModule(rootModule);
|
|
19
|
+
for (const controller of this.collectedControllers){
|
|
20
|
+
this.router.registerController(controller);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
processModule(moduleClassOrDynamic) {
|
|
24
|
+
// Handle dynamic modules ({ module, controllers, providers })
|
|
25
|
+
let moduleClass;
|
|
26
|
+
let extraControllers = [];
|
|
27
|
+
let extraProviders = [];
|
|
28
|
+
if (isDynamicModule(moduleClassOrDynamic)) {
|
|
29
|
+
moduleClass = moduleClassOrDynamic.module;
|
|
30
|
+
extraControllers = moduleClassOrDynamic.controllers ?? [];
|
|
31
|
+
extraProviders = moduleClassOrDynamic.providers ?? [];
|
|
32
|
+
} else {
|
|
33
|
+
moduleClass = moduleClassOrDynamic;
|
|
34
|
+
}
|
|
35
|
+
if (this.processedModules.has(moduleClass)) {
|
|
36
|
+
// Even if already processed, still collect extra controllers from dynamic module
|
|
37
|
+
for (const controller of extraControllers){
|
|
38
|
+
if (!this.collectedControllers.includes(controller)) {
|
|
39
|
+
this.collectedControllers.push(controller);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return this.moduleExportsCache.get(moduleClass) ?? new Set();
|
|
43
|
+
}
|
|
44
|
+
if (this.processingStack.has(moduleClass)) {
|
|
45
|
+
const chain = [
|
|
46
|
+
...this.processingStack,
|
|
47
|
+
moduleClass
|
|
48
|
+
].map((m)=>m.name).join(' -> ');
|
|
49
|
+
throw new Error(`Circular module dependency detected: ${chain}`);
|
|
50
|
+
}
|
|
51
|
+
if (!isModule(moduleClass)) {
|
|
52
|
+
throw new Error(`${moduleClass.name} is not a module. Add @Module() decorator to the class.`);
|
|
53
|
+
}
|
|
54
|
+
const metadata = getModuleMetadata(moduleClass);
|
|
55
|
+
if (!metadata) {
|
|
56
|
+
throw new Error(`Failed to get module metadata for ${moduleClass.name}`);
|
|
57
|
+
}
|
|
58
|
+
this.processingStack.add(moduleClass);
|
|
59
|
+
try {
|
|
60
|
+
const importedProviders = new Set();
|
|
61
|
+
for (const importedModule of metadata.imports){
|
|
62
|
+
const exportedTokens = this.processModule(importedModule);
|
|
63
|
+
for (const token of exportedTokens){
|
|
64
|
+
importedProviders.add(token);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Register metadata providers + dynamic module extra providers
|
|
68
|
+
const allProviders = [
|
|
69
|
+
...metadata.providers,
|
|
70
|
+
...extraProviders
|
|
71
|
+
];
|
|
72
|
+
for (const provider of allProviders){
|
|
73
|
+
this.registerProvider(provider);
|
|
74
|
+
}
|
|
75
|
+
// Collect metadata controllers + dynamic module extra controllers
|
|
76
|
+
const allControllers = [
|
|
77
|
+
...metadata.controllers,
|
|
78
|
+
...extraControllers
|
|
79
|
+
];
|
|
80
|
+
for (const controller of allControllers){
|
|
81
|
+
if (!this.collectedControllers.includes(controller)) {
|
|
82
|
+
this.collectedControllers.push(controller);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
this.processedModules.add(moduleClass);
|
|
86
|
+
const exports = this.buildExportSet(metadata.exports, allProviders, importedProviders);
|
|
87
|
+
this.moduleExportsCache.set(moduleClass, exports);
|
|
88
|
+
return exports;
|
|
89
|
+
} finally{
|
|
90
|
+
this.processingStack.delete(moduleClass);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
registerProvider(provider) {
|
|
94
|
+
if (typeof provider === 'function') {
|
|
95
|
+
if (!this.container.has(provider)) {
|
|
96
|
+
this.container.register(provider);
|
|
97
|
+
this.registeredProviders.push(provider);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
const token = provider.token;
|
|
101
|
+
if (token && !this.container.has(token)) {
|
|
102
|
+
this.container.register(provider);
|
|
103
|
+
this.registeredProviders.push(token);
|
|
104
|
+
} else if (!token) {
|
|
105
|
+
this.container.register(provider);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
buildExportSet(exports, providers, importedProviders) {
|
|
110
|
+
const exportSet = new Set();
|
|
111
|
+
for (const exported of exports){
|
|
112
|
+
const isLocalProvider = providers.some((p)=>{
|
|
113
|
+
if (typeof p === 'function') {
|
|
114
|
+
return p === exported;
|
|
115
|
+
}
|
|
116
|
+
return p.token === exported;
|
|
117
|
+
});
|
|
118
|
+
const isImportedProvider = importedProviders.has(exported);
|
|
119
|
+
if (!isLocalProvider && !isImportedProvider) {
|
|
120
|
+
const name = exported.name ?? String(exported);
|
|
121
|
+
console.warn(`Warning: Exporting '${name}' which is neither a local provider nor imported from another module.`);
|
|
122
|
+
}
|
|
123
|
+
exportSet.add(exported);
|
|
124
|
+
}
|
|
125
|
+
return exportSet;
|
|
126
|
+
}
|
|
127
|
+
getControllers() {
|
|
128
|
+
return [
|
|
129
|
+
...this.collectedControllers
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
getRegisteredProviders() {
|
|
133
|
+
return [
|
|
134
|
+
...this.registeredProviders
|
|
135
|
+
];
|
|
136
|
+
}
|
|
137
|
+
resolveAllInstances() {
|
|
138
|
+
const instances = [];
|
|
139
|
+
for (const token of this.registeredProviders){
|
|
140
|
+
try {
|
|
141
|
+
const instance = this.container.resolve(token);
|
|
142
|
+
instances.push(instance);
|
|
143
|
+
} catch {
|
|
144
|
+
// Skip unresolvable tokens
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const controller of this.collectedControllers){
|
|
148
|
+
try {
|
|
149
|
+
const instance = this.container.resolve(controller);
|
|
150
|
+
if (!instances.includes(instance)) {
|
|
151
|
+
instances.push(instance);
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// Skip unresolvable controllers
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return instances;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { InjectionToken, ProviderOptions, Type } from '../container/types';
|
|
2
|
+
export interface DynamicModule {
|
|
3
|
+
module: Type;
|
|
4
|
+
providers?: Array<Type | ProviderOptions>;
|
|
5
|
+
controllers?: Type[];
|
|
6
|
+
exports?: Array<Type | InjectionToken>;
|
|
7
|
+
}
|
|
8
|
+
export interface ModuleOptions {
|
|
9
|
+
providers?: Array<Type | ProviderOptions>;
|
|
10
|
+
controllers?: Type[];
|
|
11
|
+
imports?: Array<Type | DynamicModule>;
|
|
12
|
+
exports?: Array<Type | InjectionToken>;
|
|
13
|
+
}
|
|
14
|
+
export interface ModuleMetadata {
|
|
15
|
+
providers: Array<Type | ProviderOptions>;
|
|
16
|
+
controllers: Type[];
|
|
17
|
+
imports: Array<Type | DynamicModule>;
|
|
18
|
+
exports: Array<Type | InjectionToken>;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Container } from '../container/container';
|
|
2
|
+
import type { ComponentType, ComponentTypeMap, Constructor, FilterType, GuardType, InterceptorType, MiddlewareType, PipeType } from '../registry/types';
|
|
3
|
+
import type { ArgumentMetadata, CanActivate, ExceptionFilter, ExecutionContext, NestInterceptor, NestMiddleware, PipeTransform } from './types';
|
|
4
|
+
export declare class ComponentManager {
|
|
5
|
+
private static container;
|
|
6
|
+
static init(container: Container): void;
|
|
7
|
+
static registerGlobal<T extends ComponentType>(type: T, ...components: ComponentTypeMap[T][]): void;
|
|
8
|
+
static registerController<T extends ComponentType>(type: T, controller: Constructor, ...components: ComponentTypeMap[T][]): void;
|
|
9
|
+
static registerHandler<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol, ...components: ComponentTypeMap[T][]): void;
|
|
10
|
+
static getComponents<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol): ComponentTypeMap[T][];
|
|
11
|
+
static resolveMiddleware(items: MiddlewareType[]): NestMiddleware[];
|
|
12
|
+
static resolveGuards(items: GuardType[]): CanActivate[];
|
|
13
|
+
static resolvePipes(items: PipeType[]): PipeTransform[];
|
|
14
|
+
static resolveInterceptors(items: InterceptorType[]): NestInterceptor[];
|
|
15
|
+
static resolveFilters(items: FilterType[]): ExceptionFilter[];
|
|
16
|
+
static executePipes(value: unknown, metadata: ArgumentMetadata, pipes: PipeTransform[]): Promise<unknown>;
|
|
17
|
+
static runInterceptorChain(interceptors: NestInterceptor[], context: ExecutionContext, coreHandler: () => Promise<unknown>): Promise<unknown>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return typeof value === 'object' && value !== null;
|
|
4
|
+
}
|
|
5
|
+
export class ComponentManager {
|
|
6
|
+
static container;
|
|
7
|
+
static init(container) {
|
|
8
|
+
this.container = container;
|
|
9
|
+
}
|
|
10
|
+
// 3-level registration
|
|
11
|
+
static registerGlobal(type, ...components) {
|
|
12
|
+
for (const component of components){
|
|
13
|
+
MetadataRegistry.registerGlobal(type, component);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
static registerController(type, controller, ...components) {
|
|
17
|
+
for (const component of components){
|
|
18
|
+
MetadataRegistry.registerController(type, controller, component);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
static registerHandler(type, controller, handlerName, ...components) {
|
|
22
|
+
const handlerKey = `${controller.name}:${String(handlerName)}`;
|
|
23
|
+
for (const component of components){
|
|
24
|
+
MetadataRegistry.registerHandler(type, handlerKey, component);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 3-level resolution: global → controller → handler
|
|
28
|
+
static getComponents(type, controller, handlerName) {
|
|
29
|
+
const handlerKey = `${controller.name}:${String(handlerName)}`;
|
|
30
|
+
const globalComponents = Array.from(MetadataRegistry.getGlobal(type));
|
|
31
|
+
const controllerComponents = MetadataRegistry.getController(type, controller);
|
|
32
|
+
const handlerComponents = MetadataRegistry.getHandler(type, handlerKey);
|
|
33
|
+
return [
|
|
34
|
+
...globalComponents,
|
|
35
|
+
...controllerComponents,
|
|
36
|
+
...handlerComponents
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
// Type-specific resolvers
|
|
40
|
+
static resolveMiddleware(items) {
|
|
41
|
+
return items.map((item)=>{
|
|
42
|
+
if (isObject(item) && 'use' in item) {
|
|
43
|
+
return item;
|
|
44
|
+
}
|
|
45
|
+
return this.container.resolve(item);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
static resolveGuards(items) {
|
|
49
|
+
return items.map((item)=>{
|
|
50
|
+
if (isObject(item) && 'canActivate' in item) {
|
|
51
|
+
return item;
|
|
52
|
+
}
|
|
53
|
+
return this.container.resolve(item);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
static resolvePipes(items) {
|
|
57
|
+
return items.map((item)=>{
|
|
58
|
+
if (isObject(item) && 'transform' in item) {
|
|
59
|
+
return item;
|
|
60
|
+
}
|
|
61
|
+
return this.container.resolve(item);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
static resolveInterceptors(items) {
|
|
65
|
+
return items.map((item)=>{
|
|
66
|
+
if (isObject(item) && 'intercept' in item) {
|
|
67
|
+
return item;
|
|
68
|
+
}
|
|
69
|
+
return this.container.resolve(item);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
static resolveFilters(items) {
|
|
73
|
+
return items.map((item)=>{
|
|
74
|
+
if (isObject(item) && 'catch' in item) {
|
|
75
|
+
return item;
|
|
76
|
+
}
|
|
77
|
+
return this.container.resolve(item);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Pipe execution
|
|
81
|
+
static async executePipes(value, metadata, pipes) {
|
|
82
|
+
let transformed = value;
|
|
83
|
+
for (const pipe of pipes){
|
|
84
|
+
transformed = await pipe.transform(transformed, metadata);
|
|
85
|
+
}
|
|
86
|
+
return transformed;
|
|
87
|
+
}
|
|
88
|
+
// Interceptor chain (onion pattern)
|
|
89
|
+
static async runInterceptorChain(interceptors, context, coreHandler) {
|
|
90
|
+
if (interceptors.length === 0) {
|
|
91
|
+
return coreHandler();
|
|
92
|
+
}
|
|
93
|
+
let next = {
|
|
94
|
+
handle: coreHandler
|
|
95
|
+
};
|
|
96
|
+
for(let i = interceptors.length - 1; i >= 0; i--){
|
|
97
|
+
const interceptor = interceptors[i];
|
|
98
|
+
const currentNext = next;
|
|
99
|
+
next = {
|
|
100
|
+
handle: ()=>interceptor.intercept(context, currentNext)
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return next.handle();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Type } from '../container/types';
|
|
2
|
+
import type { ComponentTypeMap } from '../registry/types';
|
|
3
|
+
export declare function UseMiddleware(...middleware: ComponentTypeMap['middleware'][]): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
4
|
+
export declare function UseGuards(...guards: ComponentTypeMap['guard'][]): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
5
|
+
export declare function UsePipes(...pipes: ComponentTypeMap['pipe'][]): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
6
|
+
export declare function UseInterceptors(...interceptors: ComponentTypeMap['interceptor'][]): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
7
|
+
export declare function UseFilters(...filters: ComponentTypeMap['filter'][]): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
8
|
+
export declare function Catch(...exceptions: Type<Error>[]): ClassDecorator;
|
|
9
|
+
export declare function getCatchTypes(filter: unknown): Type<Error>[];
|
|
10
|
+
export declare function shouldFilterCatch(filter: unknown, exception: unknown): boolean;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { METADATA_KEYS } from "../constants.js";
|
|
2
|
+
import { getMetadata } from "../metadata.js";
|
|
3
|
+
import { ComponentManager } from "./component.manager.js";
|
|
4
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
5
|
+
function UseComponent(type, ...components) {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
7
|
+
return (target, propertyKey)=>{
|
|
8
|
+
if (propertyKey !== undefined) {
|
|
9
|
+
// Method decorator — target is prototype, target.constructor is the class
|
|
10
|
+
ComponentManager.registerHandler(type, target.constructor, propertyKey, ...components);
|
|
11
|
+
} else {
|
|
12
|
+
// Class decorator — target is the class itself
|
|
13
|
+
ComponentManager.registerController(type, target, ...components);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function UseMiddleware(...middleware) {
|
|
18
|
+
return UseComponent('middleware', ...middleware);
|
|
19
|
+
}
|
|
20
|
+
export function UseGuards(...guards) {
|
|
21
|
+
return UseComponent('guard', ...guards);
|
|
22
|
+
}
|
|
23
|
+
export function UsePipes(...pipes) {
|
|
24
|
+
return UseComponent('pipe', ...pipes);
|
|
25
|
+
}
|
|
26
|
+
export function UseInterceptors(...interceptors) {
|
|
27
|
+
return UseComponent('interceptor', ...interceptors);
|
|
28
|
+
}
|
|
29
|
+
export function UseFilters(...filters) {
|
|
30
|
+
return UseComponent('filter', ...filters);
|
|
31
|
+
}
|
|
32
|
+
export function Catch(...exceptions) {
|
|
33
|
+
return (target)=>{
|
|
34
|
+
MetadataRegistry.setCatchTypes(target, exceptions);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function getCatchTypes(filter) {
|
|
38
|
+
const filterClass = typeof filter === 'function' ? filter : filter.constructor;
|
|
39
|
+
if (MetadataRegistry.hasCatchTypes(filterClass)) {
|
|
40
|
+
return MetadataRegistry.getCatchTypes(filterClass) ?? [];
|
|
41
|
+
}
|
|
42
|
+
return getMetadata(METADATA_KEYS.CATCH, filterClass) ?? [];
|
|
43
|
+
}
|
|
44
|
+
export function shouldFilterCatch(filter, exception) {
|
|
45
|
+
const catchTypes = getCatchTypes(filter);
|
|
46
|
+
if (catchTypes.length === 0) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return catchTypes.some((type)=>exception instanceof type);
|
|
50
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ComponentManager } from './component.manager';
|
|
2
|
+
export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, getCatchTypes, shouldFilterCatch, } from './decorators';
|
|
3
|
+
export { SetMetadata, Reflector } from './reflector';
|
|
4
|
+
export type { ReflectableDecorator, CreateDecoratorOptions } from './reflector';
|
|
5
|
+
export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from './tokens';
|
|
6
|
+
export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe, } from './pipes';
|
|
7
|
+
export type { ExecutionContext, CanActivate, CallHandler, NestInterceptor, NestMiddleware, PipeTransform, ExceptionFilter, ArgumentMetadata, } from './types';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ComponentManager } from "./component.manager.js";
|
|
2
|
+
export { UseMiddleware, UseGuards, UsePipes, UseInterceptors, UseFilters, Catch, getCatchTypes, shouldFilterCatch } from "./decorators.js";
|
|
3
|
+
export { SetMetadata, Reflector } from "./reflector.js";
|
|
4
|
+
export { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER, APP_MIDDLEWARE } from "./tokens.js";
|
|
5
|
+
export { ParseIntPipe, ParseFloatPipe, ParseBoolPipe, DefaultValuePipe, RequiredPipe, ZodValidationPipe } from "./pipes.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ArgumentMetadata, PipeTransform } from './types';
|
|
2
|
+
export declare class ParseIntPipe implements PipeTransform<string, number> {
|
|
3
|
+
transform(value: string, metadata: ArgumentMetadata): number;
|
|
4
|
+
}
|
|
5
|
+
export declare class ParseFloatPipe implements PipeTransform<string, number> {
|
|
6
|
+
transform(value: string, metadata: ArgumentMetadata): number;
|
|
7
|
+
}
|
|
8
|
+
export declare class ParseBoolPipe implements PipeTransform<string, boolean> {
|
|
9
|
+
transform(value: string, metadata: ArgumentMetadata): boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class DefaultValuePipe<T = unknown> implements PipeTransform<T | undefined, T> {
|
|
12
|
+
private readonly defaultValue;
|
|
13
|
+
constructor(defaultValue: T);
|
|
14
|
+
transform(value: T | undefined, _metadata: ArgumentMetadata): T;
|
|
15
|
+
}
|
|
16
|
+
export declare class RequiredPipe implements PipeTransform {
|
|
17
|
+
transform(value: unknown, metadata: ArgumentMetadata): unknown;
|
|
18
|
+
}
|
|
19
|
+
export declare class ZodValidationPipe implements PipeTransform {
|
|
20
|
+
private readonly schema;
|
|
21
|
+
constructor(schema: {
|
|
22
|
+
parse(data: unknown): unknown;
|
|
23
|
+
});
|
|
24
|
+
transform(value: unknown, _metadata: ArgumentMetadata): unknown;
|
|
25
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BadRequestException } from "../errors/http-exception.js";
|
|
2
|
+
export class ParseIntPipe {
|
|
3
|
+
transform(value, metadata) {
|
|
4
|
+
const parsed = parseInt(value, 10);
|
|
5
|
+
if (isNaN(parsed)) {
|
|
6
|
+
throw new BadRequestException(`Validation failed (numeric string expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
|
|
7
|
+
}
|
|
8
|
+
return parsed;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class ParseFloatPipe {
|
|
12
|
+
transform(value, metadata) {
|
|
13
|
+
const parsed = parseFloat(value);
|
|
14
|
+
if (isNaN(parsed)) {
|
|
15
|
+
throw new BadRequestException(`Validation failed (float string expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
|
|
16
|
+
}
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class ParseBoolPipe {
|
|
21
|
+
transform(value, metadata) {
|
|
22
|
+
if (value === 'true') return true;
|
|
23
|
+
if (value === 'false') return false;
|
|
24
|
+
throw new BadRequestException(`Validation failed (boolean string expected)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class DefaultValuePipe {
|
|
28
|
+
defaultValue;
|
|
29
|
+
constructor(defaultValue){
|
|
30
|
+
this.defaultValue = defaultValue;
|
|
31
|
+
}
|
|
32
|
+
transform(value, _metadata) {
|
|
33
|
+
return value !== undefined && value !== null ? value : this.defaultValue;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export class RequiredPipe {
|
|
37
|
+
transform(value, metadata) {
|
|
38
|
+
if (value === undefined || value === null || value === '') {
|
|
39
|
+
throw new BadRequestException(`Validation failed (value required)${metadata.data ? ` for parameter '${metadata.data}'` : ''}`);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class ZodValidationPipe {
|
|
45
|
+
schema;
|
|
46
|
+
constructor(schema){
|
|
47
|
+
this.schema = schema;
|
|
48
|
+
}
|
|
49
|
+
transform(value, _metadata) {
|
|
50
|
+
return this.schema.parse(value);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export interface CreateDecoratorOptions {
|
|
2
|
+
key?: string;
|
|
3
|
+
}
|
|
4
|
+
export interface ReflectableDecorator<TParam> {
|
|
5
|
+
(value: TParam): ClassDecorator & MethodDecorator;
|
|
6
|
+
KEY: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Decorator that attaches custom metadata to a class or method.
|
|
10
|
+
* Read via Reflector in guards/interceptors.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const Roles = (...roles: string[]) => SetMetadata('roles', roles);
|
|
15
|
+
*
|
|
16
|
+
* @Controller('/admin')
|
|
17
|
+
* @Roles('admin')
|
|
18
|
+
* class AdminController { ... }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function SetMetadata<V = unknown>(key: string, value: V): (target: Function | object, propertyKey?: string | symbol) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Reads custom metadata set by @SetMetadata().
|
|
24
|
+
* Use in guards/interceptors via ExecutionContext.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* class RolesGuard implements CanActivate {
|
|
29
|
+
* private reflector = new Reflector();
|
|
30
|
+
*
|
|
31
|
+
* canActivate(context: ExecutionContext): boolean {
|
|
32
|
+
* const roles = this.reflector.get<string[]>('roles', context);
|
|
33
|
+
* if (!roles) return true;
|
|
34
|
+
* const user = ...; // extract from request
|
|
35
|
+
* return roles.some(r => user.roles.includes(r));
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class Reflector {
|
|
41
|
+
/**
|
|
42
|
+
* Create a type-safe decorator that sets metadata with a typed key.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* const Roles = Reflector.createDecorator<string[]>();
|
|
47
|
+
* // Roles(['admin']) — class or method decorator
|
|
48
|
+
* // reflector.get(Roles, context) — typed as string[] | undefined
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
static createDecorator<TParam = unknown>(options?: CreateDecoratorOptions): ReflectableDecorator<TParam>;
|
|
52
|
+
private resolveKey;
|
|
53
|
+
/**
|
|
54
|
+
* Get metadata value from handler first, then class.
|
|
55
|
+
* Handler-level metadata takes priority over class-level.
|
|
56
|
+
*/
|
|
57
|
+
get<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
58
|
+
getClass(): Function;
|
|
59
|
+
getHandler(): string | symbol;
|
|
60
|
+
}): T | undefined;
|
|
61
|
+
/**
|
|
62
|
+
* Get metadata only from the handler (method) level.
|
|
63
|
+
*/
|
|
64
|
+
getHandler<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
65
|
+
getClass(): Function;
|
|
66
|
+
getHandler(): string | symbol;
|
|
67
|
+
}): T | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Get metadata only from the class level.
|
|
70
|
+
*/
|
|
71
|
+
getClass<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
72
|
+
getClass(): Function;
|
|
73
|
+
}): T | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Get all metadata values for a key from both handler and class.
|
|
76
|
+
* Returns [handlerValue, classValue] (either may be undefined).
|
|
77
|
+
*/
|
|
78
|
+
getAll<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
79
|
+
getClass(): Function;
|
|
80
|
+
getHandler(): string | symbol;
|
|
81
|
+
}): [T | undefined, T | undefined];
|
|
82
|
+
/**
|
|
83
|
+
* Returns handler value if defined, else class value, else undefined.
|
|
84
|
+
* First defined value wins.
|
|
85
|
+
*/
|
|
86
|
+
getAllAndOverride<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
87
|
+
getClass(): Function;
|
|
88
|
+
getHandler(): string | symbol;
|
|
89
|
+
}): T | undefined;
|
|
90
|
+
/**
|
|
91
|
+
* Collects handler + class values, filters undefineds.
|
|
92
|
+
* - 0 values → []
|
|
93
|
+
* - 1 value → return it
|
|
94
|
+
* - Arrays → concatenate
|
|
95
|
+
* - Objects → Object.assign({}, ...values)
|
|
96
|
+
* - Otherwise → wrap in array
|
|
97
|
+
*/
|
|
98
|
+
getAllAndMerge<T = unknown>(key: string | ReflectableDecorator<T>, context: {
|
|
99
|
+
getClass(): Function;
|
|
100
|
+
getHandler(): string | symbol;
|
|
101
|
+
}): T | T[];
|
|
102
|
+
}
|