@velajs/vela 0.10.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +113 -0
- package/README.md +36 -2
- package/dist/cache/cache.decorators.d.ts +2 -2
- package/dist/cache/cache.module.d.ts +1 -1
- package/dist/cache/cache.module.js +1 -1
- package/dist/constants.d.ts +29 -26
- package/dist/constants.js +26 -29
- package/dist/container/container.js +2 -2
- package/dist/container/decorators.js +8 -11
- package/dist/container/types.d.ts +1 -0
- package/dist/errors/http-exception.d.ts +21 -20
- package/dist/errors/http-exception.js +6 -6
- package/dist/event-emitter/event-emitter.decorators.js +2 -3
- package/dist/event-emitter/event-emitter.subscriber.js +2 -1
- package/dist/factory.d.ts +0 -1
- package/dist/factory.js +2 -32
- package/dist/fetch/fetch.module.d.ts +2 -2
- package/dist/fetch/fetch.module.js +2 -2
- package/dist/health/health.service.js +6 -2
- package/dist/http/decorators.d.ts +6 -5
- package/dist/http/decorators.js +28 -76
- package/dist/http/execution-context.d.ts +4 -0
- package/dist/http/execution-context.js +15 -0
- package/dist/http/index.d.ts +2 -2
- package/dist/http/index.js +1 -1
- package/dist/http/route.manager.d.ts +1 -2
- package/dist/http/route.manager.js +20 -31
- package/dist/http/types.d.ts +0 -1
- package/dist/index.d.ts +4 -10
- package/dist/index.js +7 -12
- package/dist/internal.d.ts +11 -0
- package/dist/internal.js +13 -0
- package/dist/metadata.js +6 -15
- package/dist/module/decorators.d.ts +1 -3
- package/dist/module/decorators.js +8 -20
- package/dist/module/graph.d.ts +10 -0
- package/dist/module/graph.js +35 -0
- package/dist/module/index.d.ts +2 -0
- package/dist/module/index.js +1 -0
- package/dist/{http/middleware-consumer.d.ts → module/middleware.d.ts} +4 -13
- package/dist/{http/middleware-consumer.js → module/middleware.js} +0 -13
- package/dist/module/module-loader.d.ts +1 -1
- package/dist/module/module-loader.js +17 -13
- package/dist/module/types.d.ts +1 -29
- package/dist/openapi/decorators.js +15 -10
- package/dist/openapi/document.js +7 -42
- package/dist/pipeline/app-providers.d.ts +10 -0
- package/dist/pipeline/app-providers.js +43 -0
- package/dist/pipeline/component.manager.d.ts +1 -0
- package/dist/pipeline/component.manager.js +10 -34
- package/dist/pipeline/decorators.d.ts +5 -5
- package/dist/pipeline/decorators.js +1 -9
- package/dist/pipeline/reflector.d.ts +8 -24
- package/dist/pipeline/reflector.js +10 -55
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/metadata.registry.d.ts +20 -15
- package/dist/registry/metadata.registry.js +135 -154
- package/dist/registry/paths.d.ts +3 -0
- package/dist/registry/paths.js +15 -0
- package/dist/registry/types.d.ts +24 -14
- package/dist/registry/types.js +0 -1
- package/dist/registry/util.d.ts +3 -0
- package/dist/registry/util.js +8 -0
- package/dist/schedule/cron-matcher.d.ts +2 -0
- package/dist/schedule/cron-matcher.js +62 -0
- package/dist/schedule/index.d.ts +4 -3
- package/dist/schedule/index.js +2 -2
- package/dist/schedule/schedule.decorators.js +3 -6
- package/dist/schedule/schedule.module.d.ts +2 -6
- package/dist/schedule/schedule.module.js +4 -42
- package/dist/schedule/schedule.registry.js +3 -2
- package/dist/schedule/schedule.tokens.d.ts +0 -3
- package/dist/schedule/schedule.tokens.js +0 -2
- package/dist/schedule/schedule.types.d.ts +0 -3
- package/dist/schedule-node/index.d.ts +2 -0
- package/dist/schedule-node/index.js +2 -0
- package/dist/schedule-node/schedule-node.module.d.ts +4 -0
- package/dist/schedule-node/schedule-node.module.js +18 -0
- package/dist/{schedule → schedule-node}/schedule.executor.d.ts +2 -5
- package/dist/schedule-node/schedule.executor.js +91 -0
- package/dist/services/logger.d.ts +9 -8
- package/dist/services/logger.js +14 -15
- package/dist/throttler/throttler.decorators.d.ts +2 -2
- package/package.json +9 -9
- package/dist/schedule/schedule.executor.js +0 -169
- package/dist/testing/index.d.ts +0 -2
- package/dist/testing/index.js +0 -2
- package/dist/testing/testing.builder.d.ts +0 -29
- package/dist/testing/testing.builder.js +0 -148
- package/dist/testing/testing.module.d.ts +0 -9
- package/dist/testing/testing.module.js +0 -15
|
@@ -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
|
+
}
|
package/dist/module/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/module/index.js
CHANGED
|
@@ -1,18 +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 enum RequestMethod {
|
|
4
|
-
GET = "GET",
|
|
5
|
-
POST = "POST",
|
|
6
|
-
PUT = "PUT",
|
|
7
|
-
DELETE = "DELETE",
|
|
8
|
-
PATCH = "PATCH",
|
|
9
|
-
OPTIONS = "OPTIONS",
|
|
10
|
-
HEAD = "HEAD",
|
|
11
|
-
ALL = "ALL"
|
|
12
|
-
}
|
|
3
|
+
import type { Constructor, Type } from '../registry/types';
|
|
13
4
|
export interface RouteInfo {
|
|
14
5
|
path: string;
|
|
15
|
-
method?:
|
|
6
|
+
method?: HttpMethod;
|
|
16
7
|
}
|
|
17
8
|
export interface MiddlewareRouteDefinition {
|
|
18
9
|
middleware: Array<Type<NestMiddleware> | NestMiddleware>;
|
|
@@ -24,7 +15,7 @@ export interface MiddlewareRouteDefinition {
|
|
|
24
15
|
export interface MiddlewareConfigProxy {
|
|
25
16
|
exclude(...routes: Array<string | RouteInfo>): MiddlewareConfigProxy;
|
|
26
17
|
withPriority(priority: number): MiddlewareConfigProxy;
|
|
27
|
-
forRoutes(...routes: Array<string |
|
|
18
|
+
forRoutes(...routes: Array<string | Constructor | RouteInfo>): MiddlewareConsumer;
|
|
28
19
|
}
|
|
29
20
|
export interface MiddlewareConsumer {
|
|
30
21
|
apply(...middleware: Array<Type<NestMiddleware> | NestMiddleware>): MiddlewareConfigProxy;
|
|
@@ -1,15 +1,4 @@
|
|
|
1
1
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
2
|
-
export var RequestMethod = /*#__PURE__*/ function(RequestMethod) {
|
|
3
|
-
RequestMethod["GET"] = "GET";
|
|
4
|
-
RequestMethod["POST"] = "POST";
|
|
5
|
-
RequestMethod["PUT"] = "PUT";
|
|
6
|
-
RequestMethod["DELETE"] = "DELETE";
|
|
7
|
-
RequestMethod["PATCH"] = "PATCH";
|
|
8
|
-
RequestMethod["OPTIONS"] = "OPTIONS";
|
|
9
|
-
RequestMethod["HEAD"] = "HEAD";
|
|
10
|
-
RequestMethod["ALL"] = "ALL";
|
|
11
|
-
return RequestMethod;
|
|
12
|
-
}({});
|
|
13
2
|
export class MiddlewareBuilder {
|
|
14
3
|
definitions = [];
|
|
15
4
|
apply(...middleware) {
|
|
@@ -27,7 +16,6 @@ export class MiddlewareBuilder {
|
|
|
27
16
|
currentPriority = priority;
|
|
28
17
|
return proxy;
|
|
29
18
|
},
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
31
19
|
forRoutes: (...routes)=>{
|
|
32
20
|
this.definitions.push({
|
|
33
21
|
middleware: currentMiddleware,
|
|
@@ -57,7 +45,6 @@ function normalizeRouteArg(route) {
|
|
|
57
45
|
path: route
|
|
58
46
|
} : route;
|
|
59
47
|
}
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
61
48
|
function resolveRouteArg(route) {
|
|
62
49
|
if (typeof route === 'string') {
|
|
63
50
|
return [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Container } from "../container/container";
|
|
2
2
|
import type { Token, Type } from "../container/types";
|
|
3
|
-
import type { MiddlewareRouteDefinition } from "../http/middleware-consumer";
|
|
4
3
|
import type { RouteManager } from "../http/route.manager";
|
|
4
|
+
import type { MiddlewareRouteDefinition } from "./middleware";
|
|
5
5
|
export declare class ModuleLoader {
|
|
6
6
|
private container;
|
|
7
7
|
private router;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { Scope } from "../constants.js";
|
|
2
2
|
import { ForwardRef, InjectionToken } from "../container/types.js";
|
|
3
|
-
import { MiddlewareBuilder } from "../http/middleware-consumer.js";
|
|
4
3
|
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "../pipeline/tokens.js";
|
|
4
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
5
|
+
import { getOrCreateArray } from "../registry/util.js";
|
|
6
|
+
import { getModuleMetadata, isModule } from "./decorators.js";
|
|
7
|
+
import { MiddlewareBuilder } from "./middleware.js";
|
|
5
8
|
const APP_TOKENS = new Set([
|
|
6
9
|
APP_GUARD,
|
|
7
10
|
APP_PIPE,
|
|
@@ -9,11 +12,12 @@ const APP_TOKENS = new Set([
|
|
|
9
12
|
APP_FILTER,
|
|
10
13
|
APP_MIDDLEWARE
|
|
11
14
|
]);
|
|
12
|
-
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
13
|
-
import { getModuleMetadata, isModule } from "./decorators.js";
|
|
14
15
|
function isDynamicModule(value) {
|
|
15
16
|
return typeof value === "object" && value !== null && "module" in value && typeof value.module === "function";
|
|
16
17
|
}
|
|
18
|
+
function implementsNestModule(cls) {
|
|
19
|
+
return typeof cls.prototype?.configure === "function";
|
|
20
|
+
}
|
|
17
21
|
export class ModuleLoader {
|
|
18
22
|
container;
|
|
19
23
|
router;
|
|
@@ -152,16 +156,16 @@ export class ModuleLoader {
|
|
|
152
156
|
this.globalExports.add(token);
|
|
153
157
|
}
|
|
154
158
|
}
|
|
155
|
-
// Call configure() if the module implements NestModule
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
instance.configure(builder);
|
|
161
|
-
this.consumerMiddlewareDefinitions.push(...builder.getDefinitions());
|
|
162
|
-
} catch {
|
|
163
|
-
// Module has constructor dependencies — configure() skipped
|
|
159
|
+
// Call configure() if the module implements NestModule. The module is
|
|
160
|
+
// resolved through the container so it can have its own DI dependencies.
|
|
161
|
+
if (implementsNestModule(moduleClass)) {
|
|
162
|
+
if (!this.container.has(moduleClass)) {
|
|
163
|
+
this.container.register(moduleClass);
|
|
164
164
|
}
|
|
165
|
+
const instance = this.container.resolve(moduleClass);
|
|
166
|
+
const builder = new MiddlewareBuilder();
|
|
167
|
+
instance.configure(builder);
|
|
168
|
+
this.consumerMiddlewareDefinitions.push(...builder.getDefinitions());
|
|
165
169
|
}
|
|
166
170
|
return exports;
|
|
167
171
|
} finally{
|
|
@@ -187,7 +191,7 @@ export class ModuleLoader {
|
|
|
187
191
|
provide: syntheticToken
|
|
188
192
|
});
|
|
189
193
|
this.registeredProviders.push(syntheticToken);
|
|
190
|
-
this.appProviderTokens
|
|
194
|
+
getOrCreateArray(this.appProviderTokens, token).push(syntheticToken);
|
|
191
195
|
return;
|
|
192
196
|
}
|
|
193
197
|
if (!this.container.has(token)) {
|
package/dist/module/types.d.ts
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export type ModuleImport = Type | DynamicModule | ForwardRef;
|
|
3
|
-
export interface AsyncModuleOptions<T = unknown> {
|
|
4
|
-
imports?: ModuleImport[];
|
|
5
|
-
useFactory: (...args: any[]) => T | Promise<T>;
|
|
6
|
-
inject?: Token[];
|
|
7
|
-
}
|
|
8
|
-
export interface DynamicModule {
|
|
9
|
-
module: Type;
|
|
10
|
-
imports?: ModuleImport[];
|
|
11
|
-
providers?: Array<Type | ProviderOptions>;
|
|
12
|
-
controllers?: Type[];
|
|
13
|
-
exports?: Array<Type | InjectionToken>;
|
|
14
|
-
global?: boolean;
|
|
15
|
-
}
|
|
16
|
-
export interface ModuleOptions {
|
|
17
|
-
providers?: Array<Type | ProviderOptions>;
|
|
18
|
-
controllers?: Type[];
|
|
19
|
-
imports?: ModuleImport[];
|
|
20
|
-
exports?: Array<Type | InjectionToken>;
|
|
21
|
-
isGlobal?: boolean;
|
|
22
|
-
}
|
|
23
|
-
export interface ModuleMetadata {
|
|
24
|
-
providers: Array<Type | ProviderOptions>;
|
|
25
|
-
controllers: Type[];
|
|
26
|
-
imports: ModuleImport[];
|
|
27
|
-
exports: Array<Type | InjectionToken>;
|
|
28
|
-
isGlobal: boolean;
|
|
29
|
-
}
|
|
1
|
+
export type { AsyncModuleOptions, DynamicModule, ModuleImport, ModuleMetadata, ModuleOptions, } from '../registry/types';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
1
2
|
export const API_DOC_METADATA = 'vela:openapi:doc';
|
|
2
3
|
export const API_TAGS_METADATA = 'vela:openapi:tags';
|
|
3
4
|
export const API_RESPONSES_METADATA = 'vela:openapi:responses';
|
|
@@ -12,9 +13,9 @@ export const API_RESPONSES_METADATA = 'vela:openapi:responses';
|
|
|
12
13
|
*/ export function ApiDoc(metadata) {
|
|
13
14
|
return (target, propertyKey)=>{
|
|
14
15
|
if (propertyKey !== undefined) {
|
|
15
|
-
|
|
16
|
+
MetadataRegistry.setCustomHandlerMeta(target.constructor, propertyKey, API_DOC_METADATA, metadata);
|
|
16
17
|
} else {
|
|
17
|
-
|
|
18
|
+
MetadataRegistry.setCustomClassMeta(target, API_DOC_METADATA, metadata);
|
|
18
19
|
}
|
|
19
20
|
};
|
|
20
21
|
}
|
|
@@ -24,17 +25,23 @@ export const API_RESPONSES_METADATA = 'vela:openapi:responses';
|
|
|
24
25
|
*/ export function ApiTags(...tags) {
|
|
25
26
|
return (target, propertyKey)=>{
|
|
26
27
|
if (propertyKey !== undefined) {
|
|
27
|
-
|
|
28
|
+
MetadataRegistry.setCustomHandlerMeta(target.constructor, propertyKey, API_TAGS_METADATA, tags);
|
|
28
29
|
} else {
|
|
29
|
-
|
|
30
|
+
MetadataRegistry.setCustomClassMeta(target, API_TAGS_METADATA, tags);
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
export function getApiDoc(target, propertyKey) {
|
|
34
|
-
|
|
35
|
+
if (propertyKey !== undefined) {
|
|
36
|
+
return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_DOC_METADATA);
|
|
37
|
+
}
|
|
38
|
+
return MetadataRegistry.getCustomClassMeta(target, API_DOC_METADATA);
|
|
35
39
|
}
|
|
36
40
|
export function getApiTags(target, propertyKey) {
|
|
37
|
-
|
|
41
|
+
if (propertyKey !== undefined) {
|
|
42
|
+
return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_TAGS_METADATA);
|
|
43
|
+
}
|
|
44
|
+
return MetadataRegistry.getCustomClassMeta(target, API_TAGS_METADATA);
|
|
38
45
|
}
|
|
39
46
|
/**
|
|
40
47
|
* Document a response for a given status code. Stackable: apply multiple
|
|
@@ -48,14 +55,12 @@ export function getApiTags(target, propertyKey) {
|
|
|
48
55
|
* ```
|
|
49
56
|
*/ export function ApiResponse(status, options) {
|
|
50
57
|
return (target, propertyKey)=>{
|
|
51
|
-
|
|
52
|
-
existing.push({
|
|
58
|
+
MetadataRegistry.appendCustomHandlerMeta(target.constructor, propertyKey, API_RESPONSES_METADATA, {
|
|
53
59
|
status,
|
|
54
60
|
...options
|
|
55
61
|
});
|
|
56
|
-
Reflect.defineMetadata(API_RESPONSES_METADATA, existing, target, propertyKey);
|
|
57
62
|
};
|
|
58
63
|
}
|
|
59
64
|
export function getApiResponses(target, propertyKey) {
|
|
60
|
-
return
|
|
65
|
+
return MetadataRegistry.getCustomHandlerMeta(target, propertyKey, API_RESPONSES_METADATA);
|
|
61
66
|
}
|
package/dist/openapi/document.js
CHANGED
|
@@ -1,44 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ForwardRef } from "../container/types.js";
|
|
1
|
+
import { collectControllers } from "../module/graph.js";
|
|
3
2
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
3
|
+
import { joinPaths, toOpenApiPath } from "../registry/paths.js";
|
|
4
4
|
import { ParamType } from "../constants.js";
|
|
5
5
|
import { getApiDoc, getApiResponses, getApiTags } from "./decorators.js";
|
|
6
6
|
import { isOptional, zodToJsonSchema } from "./zod-to-json-schema.js";
|
|
7
|
-
function isDynamicModuleLike(v) {
|
|
8
|
-
return !!v && typeof v === 'object' && 'module' in v && typeof v.module === 'function';
|
|
9
|
-
}
|
|
10
|
-
function collectControllers(rootModule) {
|
|
11
|
-
const visited = new Set();
|
|
12
|
-
const controllers = new Set();
|
|
13
|
-
function visit(entry) {
|
|
14
|
-
const unwrapped = entry instanceof ForwardRef ? entry.factory() : entry;
|
|
15
|
-
const moduleClass = isDynamicModuleLike(unwrapped) ? unwrapped.module : unwrapped;
|
|
16
|
-
const extraControllers = isDynamicModuleLike(unwrapped) ? unwrapped.controllers ?? [] : [];
|
|
17
|
-
const extraImports = isDynamicModuleLike(unwrapped) ? unwrapped.imports ?? [] : [];
|
|
18
|
-
if (typeof moduleClass !== 'function' || visited.has(moduleClass)) return;
|
|
19
|
-
visited.add(moduleClass);
|
|
20
|
-
const metadata = getModuleMetadata(moduleClass);
|
|
21
|
-
if (metadata) {
|
|
22
|
-
for (const controller of metadata.controllers)controllers.add(controller);
|
|
23
|
-
for (const imp of metadata.imports)visit(imp);
|
|
24
|
-
}
|
|
25
|
-
for (const controller of extraControllers)controllers.add(controller);
|
|
26
|
-
for (const imp of extraImports)visit(imp);
|
|
27
|
-
}
|
|
28
|
-
visit(rootModule);
|
|
29
|
-
return [
|
|
30
|
-
...controllers
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
function normalizePath(path) {
|
|
34
|
-
return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, '{$1}');
|
|
35
|
-
}
|
|
36
|
-
function joinPath(a, b) {
|
|
37
|
-
const left = a.endsWith('/') ? a.slice(0, -1) : a;
|
|
38
|
-
const right = b && !b.startsWith('/') ? `/${b}` : b;
|
|
39
|
-
const joined = `${left}${right}`;
|
|
40
|
-
return joined || '/';
|
|
41
|
-
}
|
|
42
7
|
/**
|
|
43
8
|
* Tracks DTO classes referenced during document generation and registers
|
|
44
9
|
* each under `components.schemas`. Handles name collisions by suffixing.
|
|
@@ -176,9 +141,9 @@ function buildOperation(controller, route, pathString, registry) {
|
|
|
176
141
|
});
|
|
177
142
|
}
|
|
178
143
|
}
|
|
179
|
-
const docMeta = getApiDoc(controller
|
|
144
|
+
const docMeta = getApiDoc(controller, handlerName);
|
|
180
145
|
const controllerTags = getApiTags(controller) ?? [];
|
|
181
|
-
const handlerTags = getApiTags(controller
|
|
146
|
+
const handlerTags = getApiTags(controller, handlerName) ?? [];
|
|
182
147
|
const docTags = docMeta?.tags ?? [];
|
|
183
148
|
const mergedTags = [
|
|
184
149
|
...new Set([
|
|
@@ -192,7 +157,7 @@ function buildOperation(controller, route, pathString, registry) {
|
|
|
192
157
|
description: 'OK'
|
|
193
158
|
}
|
|
194
159
|
};
|
|
195
|
-
const apiResponses = getApiResponses(controller
|
|
160
|
+
const apiResponses = getApiResponses(controller, handlerName) ?? [];
|
|
196
161
|
for (const entry of apiResponses){
|
|
197
162
|
const key = String(entry.status);
|
|
198
163
|
const resolved = {
|
|
@@ -239,8 +204,8 @@ export function createOpenApiDocument(rootModule, options = {}) {
|
|
|
239
204
|
for (const route of routes){
|
|
240
205
|
const method = route.method.toLowerCase();
|
|
241
206
|
if (!VERB_WHITELIST.has(method)) continue;
|
|
242
|
-
const rawPath =
|
|
243
|
-
const pathString =
|
|
207
|
+
const rawPath = joinPaths(joinPaths(globalPrefix, controllerPath), route.path);
|
|
208
|
+
const pathString = toOpenApiPath(rawPath);
|
|
244
209
|
const operation = buildOperation(controller, route, pathString, registry);
|
|
245
210
|
const pathItem = paths[pathString] ?? {};
|
|
246
211
|
pathItem[method] = operation;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Container } from '../container/container';
|
|
2
|
+
import type { RouteManager } from '../http/route.manager';
|
|
3
|
+
import type { ModuleLoader } from '../module/module-loader';
|
|
4
|
+
/**
|
|
5
|
+
* Bind every APP_* provider registered in the container as a global
|
|
6
|
+
* component on the RouteManager. The single source of truth for app-level
|
|
7
|
+
* pipeline configuration — used by both VelaFactory.create and
|
|
8
|
+
* @velajs/testing's TestingModuleBuilder.compile.
|
|
9
|
+
*/
|
|
10
|
+
export declare function bindAppProviders(routeManager: RouteManager, container: Container, loader: ModuleLoader): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_MIDDLEWARE, APP_PIPE } from "./tokens.js";
|
|
2
|
+
// NestJS-style "APP_*" provider convention: each slot maps a well-known DI
|
|
3
|
+
// token to the RouteManager method that installs providers under it as a
|
|
4
|
+
// global component. Module-level @Provide(APP_*) entries expand into
|
|
5
|
+
// synthetic tokens (collected by ModuleLoader); a single @Inject(APP_*)
|
|
6
|
+
// provider falls back to the bare token.
|
|
7
|
+
const SLOTS = [
|
|
8
|
+
{
|
|
9
|
+
token: APP_GUARD,
|
|
10
|
+
bind: (rm)=>rm.useGlobalGuardTokens.bind(rm)
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
token: APP_PIPE,
|
|
14
|
+
bind: (rm)=>rm.useGlobalPipeTokens.bind(rm)
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
token: APP_INTERCEPTOR,
|
|
18
|
+
bind: (rm)=>rm.useGlobalInterceptorTokens.bind(rm)
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
token: APP_FILTER,
|
|
22
|
+
bind: (rm)=>rm.useGlobalFilterTokens.bind(rm)
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
token: APP_MIDDLEWARE,
|
|
26
|
+
bind: (rm)=>rm.useGlobalMiddlewareTokens.bind(rm)
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Bind every APP_* provider registered in the container as a global
|
|
31
|
+
* component on the RouteManager. The single source of truth for app-level
|
|
32
|
+
* pipeline configuration — used by both VelaFactory.create and
|
|
33
|
+
* @velajs/testing's TestingModuleBuilder.compile.
|
|
34
|
+
*/ export function bindAppProviders(routeManager, container, loader) {
|
|
35
|
+
for (const { token, bind } of SLOTS){
|
|
36
|
+
const collected = loader.getAppProviderTokens(token);
|
|
37
|
+
if (collected.length > 0) {
|
|
38
|
+
bind(routeManager)(...collected);
|
|
39
|
+
} else if (container.has(token)) {
|
|
40
|
+
bind(routeManager)(token);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -8,6 +8,7 @@ export declare class ComponentManager {
|
|
|
8
8
|
static registerController<T extends ComponentType>(type: T, controller: Constructor, ...components: ComponentTypeMap[T][]): void;
|
|
9
9
|
static registerHandler<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol, ...components: ComponentTypeMap[T][]): void;
|
|
10
10
|
static getComponents<T extends ComponentType>(type: T, controller: Constructor, handlerName: string | symbol): ComponentTypeMap[T][];
|
|
11
|
+
private static resolveAll;
|
|
11
12
|
static resolveMiddleware(items: MiddlewareType[]): NestMiddleware[];
|
|
12
13
|
static resolveGuards(items: GuardType[]): CanActivate[];
|
|
13
14
|
static resolvePipes(items: PipeType[]): PipeTransform[];
|
|
@@ -19,16 +19,14 @@ export class ComponentManager {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
static registerHandler(type, controller, handlerName, ...components) {
|
|
22
|
-
const handlerKey = `${controller.name}:${String(handlerName)}`;
|
|
23
22
|
for (const component of components){
|
|
24
|
-
MetadataRegistry.registerHandler(type,
|
|
23
|
+
MetadataRegistry.registerHandler(type, controller, handlerName, component);
|
|
25
24
|
}
|
|
26
25
|
}
|
|
27
26
|
// 3-level resolution: global → controller → handler
|
|
28
27
|
static getComponents(type, controller, handlerName) {
|
|
29
|
-
const handlerKey = `${controller.name}:${String(handlerName)}`;
|
|
30
28
|
const controllerComponents = MetadataRegistry.getController(type, controller);
|
|
31
|
-
const handlerComponents = MetadataRegistry.getHandler(type,
|
|
29
|
+
const handlerComponents = MetadataRegistry.getHandler(type, controller, handlerName);
|
|
32
30
|
return [
|
|
33
31
|
...MetadataRegistry.getGlobal(type),
|
|
34
32
|
...controllerComponents,
|
|
@@ -36,45 +34,23 @@ export class ComponentManager {
|
|
|
36
34
|
];
|
|
37
35
|
}
|
|
38
36
|
// Type-specific resolvers
|
|
37
|
+
static resolveAll(items, methodKey) {
|
|
38
|
+
return items.map((item)=>isObject(item) && methodKey in item ? item : this.container.resolve(item));
|
|
39
|
+
}
|
|
39
40
|
static resolveMiddleware(items) {
|
|
40
|
-
return
|
|
41
|
-
if (isObject(item) && 'use' in item) {
|
|
42
|
-
return item;
|
|
43
|
-
}
|
|
44
|
-
return this.container.resolve(item);
|
|
45
|
-
});
|
|
41
|
+
return this.resolveAll(items, 'use');
|
|
46
42
|
}
|
|
47
43
|
static resolveGuards(items) {
|
|
48
|
-
return
|
|
49
|
-
if (isObject(item) && 'canActivate' in item) {
|
|
50
|
-
return item;
|
|
51
|
-
}
|
|
52
|
-
return this.container.resolve(item);
|
|
53
|
-
});
|
|
44
|
+
return this.resolveAll(items, 'canActivate');
|
|
54
45
|
}
|
|
55
46
|
static resolvePipes(items) {
|
|
56
|
-
return
|
|
57
|
-
if (isObject(item) && 'transform' in item) {
|
|
58
|
-
return item;
|
|
59
|
-
}
|
|
60
|
-
return this.container.resolve(item);
|
|
61
|
-
});
|
|
47
|
+
return this.resolveAll(items, 'transform');
|
|
62
48
|
}
|
|
63
49
|
static resolveInterceptors(items) {
|
|
64
|
-
return
|
|
65
|
-
if (isObject(item) && 'intercept' in item) {
|
|
66
|
-
return item;
|
|
67
|
-
}
|
|
68
|
-
return this.container.resolve(item);
|
|
69
|
-
});
|
|
50
|
+
return this.resolveAll(items, 'intercept');
|
|
70
51
|
}
|
|
71
52
|
static resolveFilters(items) {
|
|
72
|
-
return
|
|
73
|
-
if (isObject(item) && 'catch' in item) {
|
|
74
|
-
return item;
|
|
75
|
-
}
|
|
76
|
-
return this.container.resolve(item);
|
|
77
|
-
});
|
|
53
|
+
return this.resolveAll(items, 'catch');
|
|
78
54
|
}
|
|
79
55
|
// Pipe execution
|
|
80
56
|
static async executePipes(value, metadata, pipes) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Type } from '../container/types';
|
|
2
2
|
import type { ComponentTypeMap } from '../registry/types';
|
|
3
|
-
export declare function UseMiddleware(...middleware: ComponentTypeMap['middleware'][]): (target:
|
|
4
|
-
export declare function UseGuards(...guards: ComponentTypeMap['guard'][]): (target:
|
|
5
|
-
export declare function UsePipes(...pipes: ComponentTypeMap['pipe'][]): (target:
|
|
6
|
-
export declare function UseInterceptors(...interceptors: ComponentTypeMap['interceptor'][]): (target:
|
|
7
|
-
export declare function UseFilters(...filters: ComponentTypeMap['filter'][]): (target:
|
|
3
|
+
export declare function UseMiddleware(...middleware: ComponentTypeMap['middleware'][]): (target: object, propertyKey?: string | symbol) => void;
|
|
4
|
+
export declare function UseGuards(...guards: ComponentTypeMap['guard'][]): (target: object, propertyKey?: string | symbol) => void;
|
|
5
|
+
export declare function UsePipes(...pipes: ComponentTypeMap['pipe'][]): (target: object, propertyKey?: string | symbol) => void;
|
|
6
|
+
export declare function UseInterceptors(...interceptors: ComponentTypeMap['interceptor'][]): (target: object, propertyKey?: string | symbol) => void;
|
|
7
|
+
export declare function UseFilters(...filters: ComponentTypeMap['filter'][]): (target: object, propertyKey?: string | symbol) => void;
|
|
8
8
|
export declare function Catch(...exceptions: Type<Error>[]): ClassDecorator;
|
|
9
9
|
export declare function getCatchTypes(filter: unknown): Type<Error>[];
|
|
10
10
|
export declare function shouldFilterCatch(filter: unknown, exception: unknown): boolean;
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
import { METADATA_KEYS } from "../constants.js";
|
|
2
|
-
import { getMetadata } from "../metadata.js";
|
|
3
1
|
import { ComponentManager } from "./component.manager.js";
|
|
4
2
|
import { MetadataRegistry } from "../registry/metadata.registry.js";
|
|
5
3
|
function UseComponent(type, ...components) {
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
7
4
|
return (target, propertyKey)=>{
|
|
8
5
|
if (propertyKey !== undefined) {
|
|
9
|
-
// Method decorator — target is prototype, target.constructor is the class
|
|
10
6
|
ComponentManager.registerHandler(type, target.constructor, propertyKey, ...components);
|
|
11
7
|
} else {
|
|
12
|
-
// Class decorator — target is the class itself
|
|
13
8
|
ComponentManager.registerController(type, target, ...components);
|
|
14
9
|
}
|
|
15
10
|
};
|
|
@@ -36,10 +31,7 @@ export function Catch(...exceptions) {
|
|
|
36
31
|
}
|
|
37
32
|
export function getCatchTypes(filter) {
|
|
38
33
|
const filterClass = typeof filter === 'function' ? filter : filter.constructor;
|
|
39
|
-
|
|
40
|
-
return MetadataRegistry.getCatchTypes(filterClass) ?? [];
|
|
41
|
-
}
|
|
42
|
-
return getMetadata(METADATA_KEYS.CATCH, filterClass) ?? [];
|
|
34
|
+
return MetadataRegistry.getCatchTypes(filterClass) ?? [];
|
|
43
35
|
}
|
|
44
36
|
export function shouldFilterCatch(filter, exception) {
|
|
45
37
|
const catchTypes = getCatchTypes(filter);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ExecutionContext } from './types';
|
|
1
2
|
export interface CreateDecoratorOptions {
|
|
2
3
|
key?: string;
|
|
3
4
|
}
|
|
@@ -18,7 +19,7 @@ export interface ReflectableDecorator<TParam> {
|
|
|
18
19
|
* class AdminController { ... }
|
|
19
20
|
* ```
|
|
20
21
|
*/
|
|
21
|
-
export declare function SetMetadata<V = unknown>(key: string, value: V): (target:
|
|
22
|
+
export declare function SetMetadata<V = unknown>(key: string, value: V): (target: object, propertyKey?: string | symbol) => void;
|
|
22
23
|
/**
|
|
23
24
|
* Reads custom metadata set by @SetMetadata().
|
|
24
25
|
* Use in guards/interceptors via ExecutionContext.
|
|
@@ -54,39 +55,25 @@ export declare class Reflector {
|
|
|
54
55
|
* Get metadata value from handler first, then class.
|
|
55
56
|
* Handler-level metadata takes priority over class-level.
|
|
56
57
|
*/
|
|
57
|
-
get<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
58
|
-
getClass(): Function;
|
|
59
|
-
getHandler(): string | symbol;
|
|
60
|
-
}): T | undefined;
|
|
58
|
+
get<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
|
|
61
59
|
/**
|
|
62
60
|
* Get metadata only from the handler (method) level.
|
|
63
61
|
*/
|
|
64
|
-
getHandler<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
65
|
-
getClass(): Function;
|
|
66
|
-
getHandler(): string | symbol;
|
|
67
|
-
}): T | undefined;
|
|
62
|
+
getHandler<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
|
|
68
63
|
/**
|
|
69
64
|
* Get metadata only from the class level.
|
|
70
65
|
*/
|
|
71
|
-
getClass<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
72
|
-
getClass(): Function;
|
|
73
|
-
}): T | undefined;
|
|
66
|
+
getClass<T = unknown>(key: string | ReflectableDecorator<T>, context: Pick<ExecutionContext, "getClass">): T | undefined;
|
|
74
67
|
/**
|
|
75
68
|
* Get all metadata values for a key from both handler and class.
|
|
76
69
|
* Returns [handlerValue, classValue] (either may be undefined).
|
|
77
70
|
*/
|
|
78
|
-
getAll<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
79
|
-
getClass(): Function;
|
|
80
|
-
getHandler(): string | symbol;
|
|
81
|
-
}): [T | undefined, T | undefined];
|
|
71
|
+
getAll<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): [T | undefined, T | undefined];
|
|
82
72
|
/**
|
|
83
73
|
* Returns handler value if defined, else class value, else undefined.
|
|
84
74
|
* First defined value wins.
|
|
85
75
|
*/
|
|
86
|
-
getAllAndOverride<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
87
|
-
getClass(): Function;
|
|
88
|
-
getHandler(): string | symbol;
|
|
89
|
-
}): T | undefined;
|
|
76
|
+
getAllAndOverride<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | undefined;
|
|
90
77
|
/**
|
|
91
78
|
* Collects handler + class values, filters undefineds.
|
|
92
79
|
* - 0 values → []
|
|
@@ -95,8 +82,5 @@ export declare class Reflector {
|
|
|
95
82
|
* - Objects → Object.assign({}, ...values)
|
|
96
83
|
* - Otherwise → wrap in array
|
|
97
84
|
*/
|
|
98
|
-
getAllAndMerge<T = unknown>(key: string | ReflectableDecorator<T>, context:
|
|
99
|
-
getClass(): Function;
|
|
100
|
-
getHandler(): string | symbol;
|
|
101
|
-
}): T | T[];
|
|
85
|
+
getAllAndMerge<T = unknown>(key: string | ReflectableDecorator<T>, context: ExecutionContext): T | T[];
|
|
102
86
|
}
|