@smounters/imperium 1.0.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 +13 -0
- package/LICENSE +21 -0
- package/README.md +227 -0
- package/dist/core/app-tokens.d.ts +5 -0
- package/dist/core/app-tokens.js +4 -0
- package/dist/core/application.d.ts +34 -0
- package/dist/core/application.js +252 -0
- package/dist/core/config.d.ts +1 -0
- package/dist/core/config.js +1 -0
- package/dist/core/container.d.ts +76 -0
- package/dist/core/container.js +687 -0
- package/dist/core/errors.d.ts +31 -0
- package/dist/core/errors.js +169 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +4 -0
- package/dist/core/logger.d.ts +7 -0
- package/dist/core/logger.js +12 -0
- package/dist/core/reflector.d.ts +9 -0
- package/dist/core/reflector.js +39 -0
- package/dist/core/server.d.ts +10 -0
- package/dist/core/server.js +296 -0
- package/dist/core/types.d.ts +25 -0
- package/dist/core/types.js +1 -0
- package/dist/decorators/di.decorators.d.ts +10 -0
- package/dist/decorators/di.decorators.js +15 -0
- package/dist/decorators/filters.decorators.d.ts +6 -0
- package/dist/decorators/filters.decorators.js +16 -0
- package/dist/decorators/guards.decorators.d.ts +4 -0
- package/dist/decorators/guards.decorators.js +8 -0
- package/dist/decorators/http.decorators.d.ts +16 -0
- package/dist/decorators/http.decorators.js +60 -0
- package/dist/decorators/index.d.ts +8 -0
- package/dist/decorators/index.js +8 -0
- package/dist/decorators/interceptors.decorators.d.ts +4 -0
- package/dist/decorators/interceptors.decorators.js +8 -0
- package/dist/decorators/metadata.decorators.d.ts +4 -0
- package/dist/decorators/metadata.decorators.js +22 -0
- package/dist/decorators/pipes.decorators.d.ts +4 -0
- package/dist/decorators/pipes.decorators.js +8 -0
- package/dist/decorators/rpc.decorators.d.ts +11 -0
- package/dist/decorators/rpc.decorators.js +50 -0
- package/dist/http/adapter.d.ts +4 -0
- package/dist/http/adapter.js +199 -0
- package/dist/http/index.d.ts +3 -0
- package/dist/http/index.js +3 -0
- package/dist/http/router-builder.d.ts +4 -0
- package/dist/http/router-builder.js +30 -0
- package/dist/http/utils.d.ts +6 -0
- package/dist/http/utils.js +46 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/pipes/index.d.ts +1 -0
- package/dist/pipes/index.js +1 -0
- package/dist/pipes/zod.pipe.d.ts +7 -0
- package/dist/pipes/zod.pipe.js +8 -0
- package/dist/rpc/adapter.d.ts +7 -0
- package/dist/rpc/adapter.js +186 -0
- package/dist/rpc/index.d.ts +3 -0
- package/dist/rpc/index.js +3 -0
- package/dist/rpc/router-builder.d.ts +4 -0
- package/dist/rpc/router-builder.js +28 -0
- package/dist/rpc/utils.d.ts +6 -0
- package/dist/rpc/utils.js +46 -0
- package/dist/services/config.service.d.ts +10 -0
- package/dist/services/config.service.js +41 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +2 -0
- package/dist/services/logger.service.d.ts +30 -0
- package/dist/services/logger.service.js +52 -0
- package/dist/types.d.ts +167 -0
- package/dist/types.js +1 -0
- package/dist/validation/app-config.d.ts +30 -0
- package/dist/validation/app-config.js +25 -0
- package/dist/validation/common.d.ts +8 -0
- package/dist/validation/common.js +57 -0
- package/dist/validation/index.d.ts +2 -0
- package/dist/validation/index.js +2 -0
- package/package.json +98 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { appendArrayMetadata } from "./metadata.decorators";
|
|
3
|
+
export const GUARDS_KEY = Symbol("guards");
|
|
4
|
+
export function UseGuards(...guards) {
|
|
5
|
+
return (target, propertyKey) => {
|
|
6
|
+
appendArrayMetadata(GUARDS_KEY, guards, target, propertyKey);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
export declare const HTTP_ROUTES_KEY: unique symbol;
|
|
3
|
+
export declare const HTTP_CONTROLLER_KEY: unique symbol;
|
|
4
|
+
export declare const PARAMS_KEY: unique symbol;
|
|
5
|
+
export declare function HttpController(prefix?: string): ClassDecorator;
|
|
6
|
+
export declare const Get: (path: string) => MethodDecorator;
|
|
7
|
+
export declare const Post: (path: string) => MethodDecorator;
|
|
8
|
+
export declare const Put: (path: string) => MethodDecorator;
|
|
9
|
+
export declare const Patch: (path: string) => MethodDecorator;
|
|
10
|
+
export declare const Delete: (path: string) => MethodDecorator;
|
|
11
|
+
export declare const Body: (key?: string) => ParameterDecorator;
|
|
12
|
+
export declare const Query: (key?: string) => ParameterDecorator;
|
|
13
|
+
export declare const Param: (key?: string) => ParameterDecorator;
|
|
14
|
+
export declare const Header: (key: string) => ParameterDecorator;
|
|
15
|
+
export declare const Req: () => ParameterDecorator;
|
|
16
|
+
export declare const Res: () => ParameterDecorator;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { Injectable } from "./di.decorators";
|
|
3
|
+
export const HTTP_ROUTES_KEY = Symbol("http:routes");
|
|
4
|
+
export const HTTP_CONTROLLER_KEY = Symbol("http:controller");
|
|
5
|
+
export const PARAMS_KEY = Symbol("http:params");
|
|
6
|
+
export function HttpController(prefix = "") {
|
|
7
|
+
return (target) => {
|
|
8
|
+
Injectable()(target);
|
|
9
|
+
Reflect.defineMetadata(HTTP_CONTROLLER_KEY, { prefix }, target);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function methodDecorator(method) {
|
|
13
|
+
return (path) => {
|
|
14
|
+
return (target, propertyKey) => {
|
|
15
|
+
const existing = Reflect.getMetadata(HTTP_ROUTES_KEY, target.constructor) ?? [];
|
|
16
|
+
existing.push({
|
|
17
|
+
method,
|
|
18
|
+
path,
|
|
19
|
+
handlerName: propertyKey,
|
|
20
|
+
});
|
|
21
|
+
Reflect.defineMetadata(HTTP_ROUTES_KEY, existing, target.constructor);
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export const Get = methodDecorator("GET");
|
|
26
|
+
export const Post = methodDecorator("POST");
|
|
27
|
+
export const Put = methodDecorator("PUT");
|
|
28
|
+
export const Patch = methodDecorator("PATCH");
|
|
29
|
+
export const Delete = methodDecorator("DELETE");
|
|
30
|
+
function getMetaTarget(target, propertyKey) {
|
|
31
|
+
if (propertyKey === undefined) {
|
|
32
|
+
return target;
|
|
33
|
+
}
|
|
34
|
+
const value = target[propertyKey];
|
|
35
|
+
if (typeof value !== "object" && typeof value !== "function") {
|
|
36
|
+
throw new Error("Decorator target is not an object");
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
function setParamMeta(target, propertyKey, meta) {
|
|
41
|
+
const metaTarget = getMetaTarget(target, propertyKey);
|
|
42
|
+
const existing = Reflect.getMetadata(PARAMS_KEY, metaTarget);
|
|
43
|
+
const next = existing ? [...existing, meta] : [meta];
|
|
44
|
+
Reflect.defineMetadata(PARAMS_KEY, next, metaTarget);
|
|
45
|
+
}
|
|
46
|
+
function paramDecorator(source, key) {
|
|
47
|
+
return (target, propertyKey, index) => {
|
|
48
|
+
setParamMeta(target, propertyKey ?? undefined, {
|
|
49
|
+
index,
|
|
50
|
+
source,
|
|
51
|
+
key,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export const Body = (key) => paramDecorator("body", key);
|
|
56
|
+
export const Query = (key) => paramDecorator("query", key);
|
|
57
|
+
export const Param = (key) => paramDecorator("param", key);
|
|
58
|
+
export const Header = (key) => paramDecorator("header", key);
|
|
59
|
+
export const Req = () => paramDecorator("req");
|
|
60
|
+
export const Res = () => paramDecorator("res");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Inject, InjectAll, Injectable, Module, Optional, Scope } from "./di.decorators";
|
|
2
|
+
export { Catch, UseFilters } from "./filters.decorators";
|
|
3
|
+
export { UseGuards } from "./guards.decorators";
|
|
4
|
+
export { Body, Delete, Get, Header, HttpController, Param, Patch, Post, Put, Query, Req, Res } from "./http.decorators";
|
|
5
|
+
export { UseInterceptors } from "./interceptors.decorators";
|
|
6
|
+
export { SetMetadata } from "./metadata.decorators";
|
|
7
|
+
export { UsePipes } from "./pipes.decorators";
|
|
8
|
+
export { RpcContext, RpcData, RpcHeader, RpcHeaders, RpcMethod, RpcService } from "./rpc.decorators";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { Inject, InjectAll, Injectable, Module, Optional, Scope } from "./di.decorators";
|
|
2
|
+
export { Catch, UseFilters } from "./filters.decorators";
|
|
3
|
+
export { UseGuards } from "./guards.decorators";
|
|
4
|
+
export { Body, Delete, Get, Header, HttpController, Param, Patch, Post, Put, Query, Req, Res } from "./http.decorators";
|
|
5
|
+
export { UseInterceptors } from "./interceptors.decorators";
|
|
6
|
+
export { SetMetadata } from "./metadata.decorators";
|
|
7
|
+
export { UsePipes } from "./pipes.decorators";
|
|
8
|
+
export { RpcContext, RpcData, RpcHeader, RpcHeaders, RpcMethod, RpcService } from "./rpc.decorators";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { appendArrayMetadata } from "./metadata.decorators";
|
|
3
|
+
export const INTERCEPTORS_KEY = Symbol("interceptors");
|
|
4
|
+
export function UseInterceptors(...inters) {
|
|
5
|
+
return (target, propertyKey) => {
|
|
6
|
+
appendArrayMetadata(INTERCEPTORS_KEY, inters, target, propertyKey);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import type { MetadataKey } from "../types";
|
|
3
|
+
export declare function SetMetadata<T = unknown>(metadataKey: MetadataKey, metadataValue: T): ClassDecorator & MethodDecorator;
|
|
4
|
+
export declare function appendArrayMetadata<T>(metadataKey: MetadataKey, values: readonly T[], target: object, propertyKey?: string | symbol): void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
function getMetaTarget(target, propertyKey) {
|
|
3
|
+
if (propertyKey === undefined) {
|
|
4
|
+
return target;
|
|
5
|
+
}
|
|
6
|
+
const value = target[propertyKey];
|
|
7
|
+
if (typeof value !== "object" && typeof value !== "function") {
|
|
8
|
+
throw new Error("Decorator target is not an object");
|
|
9
|
+
}
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
export function SetMetadata(metadataKey, metadataValue) {
|
|
13
|
+
return (target, propertyKey) => {
|
|
14
|
+
const metaTarget = getMetaTarget(target, propertyKey);
|
|
15
|
+
Reflect.defineMetadata(metadataKey, metadataValue, metaTarget);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function appendArrayMetadata(metadataKey, values, target, propertyKey) {
|
|
19
|
+
const metaTarget = getMetaTarget(target, propertyKey);
|
|
20
|
+
const prev = Reflect.getMetadata(metadataKey, metaTarget) ?? [];
|
|
21
|
+
Reflect.defineMetadata(metadataKey, [...prev, ...values], metaTarget);
|
|
22
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { appendArrayMetadata } from "./metadata.decorators";
|
|
3
|
+
export const PIPES_KEY = Symbol("pipes");
|
|
4
|
+
export function UsePipes(...pipes) {
|
|
5
|
+
return (target, propertyKey) => {
|
|
6
|
+
appendArrayMetadata(PIPES_KEY, pipes, target, propertyKey);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import type { RpcMethodDescriptor, RpcServiceDescriptor } from "../core/types";
|
|
3
|
+
export declare const RPC_SERVICE_KEY: unique symbol;
|
|
4
|
+
export declare const RPC_METHODS_KEY: unique symbol;
|
|
5
|
+
export declare const RPC_PARAMS_KEY: unique symbol;
|
|
6
|
+
export declare function RpcService(service: RpcServiceDescriptor): ClassDecorator;
|
|
7
|
+
export declare function RpcMethod(method: RpcMethodDescriptor): MethodDecorator;
|
|
8
|
+
export declare const RpcData: (key?: string) => ParameterDecorator;
|
|
9
|
+
export declare const RpcContext: () => ParameterDecorator;
|
|
10
|
+
export declare const RpcHeaders: () => ParameterDecorator;
|
|
11
|
+
export declare const RpcHeader: (key: string) => ParameterDecorator;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { Injectable } from "./di.decorators";
|
|
3
|
+
export const RPC_SERVICE_KEY = Symbol("rpc:service");
|
|
4
|
+
export const RPC_METHODS_KEY = Symbol("rpc:methods");
|
|
5
|
+
export const RPC_PARAMS_KEY = Symbol("rpc:params");
|
|
6
|
+
export function RpcService(service) {
|
|
7
|
+
return (target) => {
|
|
8
|
+
Injectable()(target);
|
|
9
|
+
Reflect.defineMetadata(RPC_SERVICE_KEY, service, target);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export function RpcMethod(method) {
|
|
13
|
+
return (target, propertyKey) => {
|
|
14
|
+
const existing = Reflect.getMetadata(RPC_METHODS_KEY, target.constructor) ?? [];
|
|
15
|
+
existing.push({
|
|
16
|
+
method,
|
|
17
|
+
handlerName: propertyKey,
|
|
18
|
+
});
|
|
19
|
+
Reflect.defineMetadata(RPC_METHODS_KEY, existing, target.constructor);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function getMetaTarget(target, propertyKey) {
|
|
23
|
+
if (propertyKey === undefined) {
|
|
24
|
+
return target;
|
|
25
|
+
}
|
|
26
|
+
const value = target[propertyKey];
|
|
27
|
+
if (typeof value !== "object" && typeof value !== "function") {
|
|
28
|
+
throw new Error("Decorator target is not an object");
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function setParamMeta(target, propertyKey, meta) {
|
|
33
|
+
const metaTarget = getMetaTarget(target, propertyKey);
|
|
34
|
+
const existing = Reflect.getMetadata(RPC_PARAMS_KEY, metaTarget);
|
|
35
|
+
const next = existing ? [...existing, meta] : [meta];
|
|
36
|
+
Reflect.defineMetadata(RPC_PARAMS_KEY, next, metaTarget);
|
|
37
|
+
}
|
|
38
|
+
function rpcParamDecorator(source, key) {
|
|
39
|
+
return (target, propertyKey, index) => {
|
|
40
|
+
setParamMeta(target, propertyKey ?? undefined, {
|
|
41
|
+
index,
|
|
42
|
+
source,
|
|
43
|
+
key,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export const RpcData = (key) => rpcParamDecorator("data", key);
|
|
48
|
+
export const RpcContext = () => rpcParamDecorator("context");
|
|
49
|
+
export const RpcHeaders = () => rpcParamDecorator("headers");
|
|
50
|
+
export const RpcHeader = (key) => rpcParamDecorator("header", key);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
+
import type { AppContainer } from "../core/container";
|
|
3
|
+
import type { Constructor } from "../types";
|
|
4
|
+
export declare function createHttpHandler<TController extends Record<string, unknown>>(app: AppContainer, controller: Constructor<TController>, methodName: keyof TController & string): (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { LoggerService } from "../services";
|
|
2
|
+
import { ForbiddenException, toHttpError } from "../core/errors";
|
|
3
|
+
import { CATCH_EXCEPTIONS_KEY } from "../decorators/filters.decorators";
|
|
4
|
+
import { PARAMS_KEY } from "../decorators/http.decorators";
|
|
5
|
+
import { collectFiltersForHttp, collectGuardsForHttp, collectInterceptorsForHttp, collectPipesForHttp } from "./utils";
|
|
6
|
+
function logHttpError(app, scope, details, error) {
|
|
7
|
+
try {
|
|
8
|
+
scope.resolve(LoggerService).error(details, error);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// fallback below
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
app.getLogger().error(details, error);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// final fallback
|
|
20
|
+
}
|
|
21
|
+
console.error("[imperium] http_error", details, error);
|
|
22
|
+
}
|
|
23
|
+
function resolveEnhancer(scope, enhancer) {
|
|
24
|
+
if (typeof enhancer === "function") {
|
|
25
|
+
return scope.resolve(enhancer);
|
|
26
|
+
}
|
|
27
|
+
return enhancer;
|
|
28
|
+
}
|
|
29
|
+
function getRequestScope(req, app, controller) {
|
|
30
|
+
const request = req;
|
|
31
|
+
if (request.diScope?.isRegistered(controller, true)) {
|
|
32
|
+
return request.diScope;
|
|
33
|
+
}
|
|
34
|
+
const scope = app.createRequestScope(controller);
|
|
35
|
+
request.diScope = scope;
|
|
36
|
+
return scope;
|
|
37
|
+
}
|
|
38
|
+
function getPayloadValue(payload, key) {
|
|
39
|
+
if (!key) {
|
|
40
|
+
return payload;
|
|
41
|
+
}
|
|
42
|
+
if (!payload || typeof payload !== "object") {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return payload[key];
|
|
46
|
+
}
|
|
47
|
+
function buildArgs(req, reply, handler) {
|
|
48
|
+
const metas = Reflect.getMetadata(PARAMS_KEY, handler) ?? [];
|
|
49
|
+
if (!metas.length) {
|
|
50
|
+
return {
|
|
51
|
+
args: [req, reply],
|
|
52
|
+
metasByIndex: new Map(),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const args = [];
|
|
56
|
+
const metasByIndex = new Map();
|
|
57
|
+
for (const meta of metas) {
|
|
58
|
+
let value;
|
|
59
|
+
switch (meta.source) {
|
|
60
|
+
case "body":
|
|
61
|
+
value = getPayloadValue(req.body, meta.key);
|
|
62
|
+
break;
|
|
63
|
+
case "query":
|
|
64
|
+
value = getPayloadValue(req.query, meta.key);
|
|
65
|
+
break;
|
|
66
|
+
case "param":
|
|
67
|
+
value = getPayloadValue(req.params, meta.key);
|
|
68
|
+
break;
|
|
69
|
+
case "header":
|
|
70
|
+
value = meta.key ? req.headers[meta.key.toLowerCase()] : undefined;
|
|
71
|
+
break;
|
|
72
|
+
case "req":
|
|
73
|
+
value = req;
|
|
74
|
+
break;
|
|
75
|
+
case "res":
|
|
76
|
+
value = reply;
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
args[meta.index] = value;
|
|
80
|
+
metasByIndex.set(meta.index, meta);
|
|
81
|
+
}
|
|
82
|
+
return { args, metasByIndex };
|
|
83
|
+
}
|
|
84
|
+
function canHandleException(error, filter) {
|
|
85
|
+
const exceptions = Reflect.getMetadata(CATCH_EXCEPTIONS_KEY, filter.constructor);
|
|
86
|
+
if (!exceptions || exceptions.length === 0) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (!(error instanceof Error)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return exceptions.some((ExceptionType) => error instanceof ExceptionType);
|
|
93
|
+
}
|
|
94
|
+
async function runHttpFilters(error, filters, ctx, reply) {
|
|
95
|
+
let currentError = error;
|
|
96
|
+
for (const filter of filters) {
|
|
97
|
+
if (!canHandleException(currentError, filter)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
try {
|
|
101
|
+
const result = await filter.catch(currentError, ctx);
|
|
102
|
+
if (reply.sent) {
|
|
103
|
+
return { handled: true, error: currentError };
|
|
104
|
+
}
|
|
105
|
+
if (result !== undefined) {
|
|
106
|
+
reply.send(result);
|
|
107
|
+
return { handled: true, error: currentError };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch (nextError) {
|
|
111
|
+
currentError = nextError;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { handled: false, error: currentError };
|
|
115
|
+
}
|
|
116
|
+
export function createHttpHandler(app, controller, methodName) {
|
|
117
|
+
return async (req, reply) => {
|
|
118
|
+
const requestScope = getRequestScope(req, app, controller);
|
|
119
|
+
const handler = controller.prototype[methodName];
|
|
120
|
+
return app.runInRequestScope(requestScope, async () => {
|
|
121
|
+
const ctx = {
|
|
122
|
+
type: "http",
|
|
123
|
+
method: methodName,
|
|
124
|
+
fastify: { req, reply },
|
|
125
|
+
controller,
|
|
126
|
+
handler,
|
|
127
|
+
getType: () => "http",
|
|
128
|
+
getClass: () => controller,
|
|
129
|
+
getHandler: () => handler,
|
|
130
|
+
switchToHttp: () => ({
|
|
131
|
+
getRequest: () => req,
|
|
132
|
+
getResponse: () => reply,
|
|
133
|
+
}),
|
|
134
|
+
switchToRpc: () => ({
|
|
135
|
+
getData: () => undefined,
|
|
136
|
+
getContext: () => undefined,
|
|
137
|
+
}),
|
|
138
|
+
};
|
|
139
|
+
try {
|
|
140
|
+
const instance = requestScope.resolve(controller);
|
|
141
|
+
const guards = collectGuardsForHttp(controller, methodName, app.getGlobalGuards()).map((guardLike) => resolveEnhancer(requestScope, guardLike));
|
|
142
|
+
for (const guard of guards) {
|
|
143
|
+
const ok = await guard.canActivate(ctx);
|
|
144
|
+
if (!ok) {
|
|
145
|
+
throw new ForbiddenException();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
const { args, metasByIndex } = buildArgs(req, reply, handler);
|
|
149
|
+
const pipes = collectPipesForHttp(controller, methodName, app.getGlobalPipes()).map((pipeLike) => resolveEnhancer(requestScope, pipeLike));
|
|
150
|
+
const transformedArgs = [...args];
|
|
151
|
+
for (const [index, meta] of metasByIndex) {
|
|
152
|
+
if (meta.source === "req" || meta.source === "res") {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
let value = args[index];
|
|
156
|
+
for (const pipe of pipes) {
|
|
157
|
+
value = await pipe.transform(value, ctx);
|
|
158
|
+
}
|
|
159
|
+
transformedArgs[index] = value;
|
|
160
|
+
}
|
|
161
|
+
const interceptors = collectInterceptorsForHttp(controller, methodName, app.getGlobalInterceptors()).map((interceptorLike) => resolveEnhancer(requestScope, interceptorLike));
|
|
162
|
+
const controllerHandler = instance[methodName];
|
|
163
|
+
let idx = -1;
|
|
164
|
+
const next = async () => {
|
|
165
|
+
idx++;
|
|
166
|
+
if (idx < interceptors.length) {
|
|
167
|
+
return interceptors[idx].intercept(ctx, next);
|
|
168
|
+
}
|
|
169
|
+
return controllerHandler(...transformedArgs);
|
|
170
|
+
};
|
|
171
|
+
const result = await next();
|
|
172
|
+
if (!reply.sent && result !== undefined) {
|
|
173
|
+
reply.send(result);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
logHttpError(app, requestScope, {
|
|
178
|
+
type: "http_error",
|
|
179
|
+
method: req.method,
|
|
180
|
+
url: req.url,
|
|
181
|
+
controller: controller.name,
|
|
182
|
+
handler: methodName,
|
|
183
|
+
}, error);
|
|
184
|
+
if (reply.sent) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const filters = collectFiltersForHttp(controller, methodName, app.getGlobalFilters()).map((filterLike) => resolveEnhancer(requestScope, filterLike));
|
|
188
|
+
const filtered = await runHttpFilters(error, filters, ctx, reply);
|
|
189
|
+
if (filtered.handled || reply.sent) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const mapped = toHttpError(filtered.error, {
|
|
193
|
+
exposeInternalErrors: app.shouldExposeInternalErrors(),
|
|
194
|
+
});
|
|
195
|
+
reply.status(mapped.status).send(mapped.body);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { HTTP_CONTROLLER_KEY, HTTP_ROUTES_KEY } from "../decorators/http.decorators";
|
|
3
|
+
import { createHttpHandler } from "./adapter";
|
|
4
|
+
function joinUrlPath(...segments) {
|
|
5
|
+
const parts = segments
|
|
6
|
+
.map((segment) => segment.trim())
|
|
7
|
+
.filter((segment) => segment.length > 0 && segment !== "/")
|
|
8
|
+
.map((segment) => segment.replace(/^\/+|\/+$/g, ""));
|
|
9
|
+
if (parts.length === 0) {
|
|
10
|
+
return "/";
|
|
11
|
+
}
|
|
12
|
+
return `/${parts.join("/")}`;
|
|
13
|
+
}
|
|
14
|
+
export function registerHttpRoutes(server, di, globalPrefix = "") {
|
|
15
|
+
for (const ctrl of di.getHttpControllers()) {
|
|
16
|
+
// сузим тип конструктора контроллера
|
|
17
|
+
const controller = ctrl;
|
|
18
|
+
const meta = Reflect.getMetadata(HTTP_CONTROLLER_KEY, controller) ?? {
|
|
19
|
+
prefix: "",
|
|
20
|
+
};
|
|
21
|
+
const routes = Reflect.getMetadata(HTTP_ROUTES_KEY, controller) ?? [];
|
|
22
|
+
for (const r of routes) {
|
|
23
|
+
server.route({
|
|
24
|
+
method: r.method,
|
|
25
|
+
url: joinUrlPath(globalPrefix, meta.prefix, r.path),
|
|
26
|
+
handler: createHttpHandler(di, controller, r.handlerName),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import type { Constructor, ExceptionFilterLike, GuardLike, InterceptorLike, PipeLike } from "../types";
|
|
3
|
+
export declare function collectGuardsForHttp(controller: Constructor, method: string, global: GuardLike[]): GuardLike[];
|
|
4
|
+
export declare function collectInterceptorsForHttp(controller: Constructor, method: string, global: InterceptorLike[]): InterceptorLike[];
|
|
5
|
+
export declare function collectPipesForHttp(controller: Constructor, method: string, global: PipeLike[]): PipeLike[];
|
|
6
|
+
export declare function collectFiltersForHttp(controller: Constructor, method: string, global: ExceptionFilterLike[]): ExceptionFilterLike[];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
import { Reflector } from "../core/reflector";
|
|
3
|
+
import { FILTERS_KEY } from "../decorators/filters.decorators";
|
|
4
|
+
import { GUARDS_KEY } from "../decorators/guards.decorators";
|
|
5
|
+
import { INTERCEPTORS_KEY } from "../decorators/interceptors.decorators";
|
|
6
|
+
import { PIPES_KEY } from "../decorators/pipes.decorators";
|
|
7
|
+
const reflector = new Reflector();
|
|
8
|
+
function enhancerKey(value) {
|
|
9
|
+
if (typeof value === "function") {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
if (value && typeof value === "object") {
|
|
13
|
+
const ctor = value.constructor;
|
|
14
|
+
if (ctor && ctor !== Object) {
|
|
15
|
+
return ctor;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function uniqueByEnhancer(values) {
|
|
21
|
+
return values.filter((value, index, arr) => {
|
|
22
|
+
const key = enhancerKey(value);
|
|
23
|
+
return arr.findIndex((other) => enhancerKey(other) === key) === index;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function readClassAndMethodMeta(metadataKey, controller, method) {
|
|
27
|
+
const classValues = (reflector.get(metadataKey, controller) ?? []);
|
|
28
|
+
const methodValues = (reflector.get(metadataKey, controller.prototype[method]) ?? []);
|
|
29
|
+
return [...classValues, ...methodValues];
|
|
30
|
+
}
|
|
31
|
+
export function collectGuardsForHttp(controller, method, global) {
|
|
32
|
+
const local = readClassAndMethodMeta(GUARDS_KEY, controller, method);
|
|
33
|
+
return [...global, ...local];
|
|
34
|
+
}
|
|
35
|
+
export function collectInterceptorsForHttp(controller, method, global) {
|
|
36
|
+
const local = readClassAndMethodMeta(INTERCEPTORS_KEY, controller, method);
|
|
37
|
+
return uniqueByEnhancer([...global, ...local]);
|
|
38
|
+
}
|
|
39
|
+
export function collectPipesForHttp(controller, method, global) {
|
|
40
|
+
const local = readClassAndMethodMeta(PIPES_KEY, controller, method);
|
|
41
|
+
return [...global, ...local];
|
|
42
|
+
}
|
|
43
|
+
export function collectFiltersForHttp(controller, method, global) {
|
|
44
|
+
const local = readClassAndMethodMeta(FILTERS_KEY, controller, method);
|
|
45
|
+
return uniqueByEnhancer([...global, ...local]);
|
|
46
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./zod.pipe";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./zod.pipe";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { PipeTransform } from "../types";
|
|
3
|
+
export declare class ZodPipe<TSchema extends z.ZodType> implements PipeTransform<unknown, z.infer<TSchema>> {
|
|
4
|
+
private schema;
|
|
5
|
+
constructor(schema: TSchema);
|
|
6
|
+
transform(value: unknown): z.infer<TSchema>;
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HandlerContext } from "@connectrpc/connect";
|
|
2
|
+
import "reflect-metadata";
|
|
3
|
+
import type { AppContainer } from "../core/container";
|
|
4
|
+
import type { Constructor } from "../types";
|
|
5
|
+
type RpcResponse = Record<string, unknown>;
|
|
6
|
+
export declare function createRpcHandler<TController extends Record<string, unknown>>(app: AppContainer, controller: Constructor<TController>, methodName: keyof TController & string): (req: unknown, context: HandlerContext) => Promise<RpcResponse>;
|
|
7
|
+
export {};
|