@ajke/core 0.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/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/chunk-AT2R2CGV.js +234 -0
- package/dist/chunk-AT2R2CGV.js.map +1 -0
- package/dist/chunk-EUXUH3YW.js +15 -0
- package/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/chunk-YUBEJL4T.cjs +234 -0
- package/dist/chunk-YUBEJL4T.cjs.map +1 -0
- package/dist/chunk-ZBDE64SD.cjs +15 -0
- package/dist/chunk-ZBDE64SD.cjs.map +1 -0
- package/dist/config.cjs +10 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +13 -0
- package/dist/config.d.ts +13 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/index.cjs +974 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +255 -0
- package/dist/index.d.ts +255 -0
- package/dist/index.js +974 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/index.cjs +10 -0
- package/dist/middleware/index.cjs.map +1 -0
- package/dist/middleware/index.d.cts +18 -0
- package/dist/middleware/index.d.ts +18 -0
- package/dist/middleware/index.js +10 -0
- package/dist/middleware/index.js.map +1 -0
- package/package.json +56 -0
- package/src/README.md +285 -0
- package/src/config.ts +14 -0
- package/src/context/execution-context.ts +36 -0
- package/src/context/index.ts +1 -0
- package/src/decorators/core/exception-filters.decorator.ts +24 -0
- package/src/decorators/core/index.ts +6 -0
- package/src/decorators/core/injectable.decorator.ts +41 -0
- package/src/decorators/core/optional.decorator.ts +9 -0
- package/src/decorators/core/set-metadata.decorator.ts +20 -0
- package/src/decorators/core/use-guards.decorator.ts +14 -0
- package/src/decorators/core/use-interceptors.decorator.ts +16 -0
- package/src/decorators/http/controller.decorator.ts +230 -0
- package/src/decorators/http/header.decorator.ts +11 -0
- package/src/decorators/http/http-code.decorator.ts +8 -0
- package/src/decorators/http/index.ts +6 -0
- package/src/decorators/http/redirect.decorator.ts +13 -0
- package/src/decorators/http/route-mapping.decorator.ts +22 -0
- package/src/decorators/http/route-params.decorator.ts +60 -0
- package/src/decorators/index.ts +3 -0
- package/src/decorators/modules/global.decorator.ts +8 -0
- package/src/decorators/modules/index.ts +2 -0
- package/src/decorators/modules/module.decorator.ts +16 -0
- package/src/exceptions/http-exception.ts +17 -0
- package/src/exceptions/http-exceptions.ts +85 -0
- package/src/exceptions/index.ts +2 -0
- package/src/index.ts +11 -0
- package/src/injector/index.ts +1 -0
- package/src/injector/injector.ts +103 -0
- package/src/injector/module-compiler.ts +48 -0
- package/src/injector/module.factory.ts +74 -0
- package/src/interfaces/core/filter.interface.ts +5 -0
- package/src/interfaces/core/guard.interface.ts +5 -0
- package/src/interfaces/core/index.ts +5 -0
- package/src/interfaces/core/interceptor.interface.ts +9 -0
- package/src/interfaces/core/lifecycle.interface.ts +19 -0
- package/src/interfaces/core/pipe.interface.ts +9 -0
- package/src/interfaces/http/index.ts +1 -0
- package/src/interfaces/http/response.interface.ts +27 -0
- package/src/interfaces/index.ts +3 -0
- package/src/interfaces/modules/index.ts +1 -0
- package/src/interfaces/modules/module.interface.ts +17 -0
- package/src/middleware/error-handler.middleware.ts +63 -0
- package/src/middleware/index.ts +2 -0
- package/src/middleware/request-logger.middleware.ts +17 -0
- package/src/pipes/index.ts +3 -0
- package/src/pipes/validate.pipe.ts +79 -0
- package/src/pipes/zod-query.pipe.ts +42 -0
- package/src/pipes/zod-validate.pipe.ts +49 -0
- package/src/services/index.ts +1 -0
- package/src/services/reflector.service.ts +24 -0
- package/src/utils/apply-decorators.util.ts +17 -0
- package/src/utils/forward-ref.util.ts +14 -0
- package/src/utils/index.ts +22 -0
- package/src/utils/logger.util.ts +189 -0
- package/src/utils/response.util.ts +72 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { injectable, inject as tsyringeInject } from "tsyringe";
|
|
2
|
+
import { isForwardRef } from "../../utils/forward-ref.util";
|
|
3
|
+
|
|
4
|
+
export const INJECT_CUSTOM_TOKENS_KEY = "wilt:inject:custom:tokens";
|
|
5
|
+
|
|
6
|
+
export function Injectable() {
|
|
7
|
+
return (target: any) => {
|
|
8
|
+
injectable()(target);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Inject(token?: any) {
|
|
13
|
+
return (
|
|
14
|
+
target: any,
|
|
15
|
+
propertyKey: string | symbol | undefined,
|
|
16
|
+
parameterIndex: number
|
|
17
|
+
) => {
|
|
18
|
+
if (isForwardRef(token)) {
|
|
19
|
+
const existing: Record<number, any> =
|
|
20
|
+
Reflect.getMetadata(INJECT_CUSTOM_TOKENS_KEY, target) || {};
|
|
21
|
+
existing[parameterIndex] = token;
|
|
22
|
+
Reflect.defineMetadata(INJECT_CUSTOM_TOKENS_KEY, existing, target);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!token) {
|
|
27
|
+
const paramTypes = Reflect.getMetadata("design:paramtypes", target) || [];
|
|
28
|
+
const paramType = paramTypes[parameterIndex];
|
|
29
|
+
if (paramType) {
|
|
30
|
+
return tsyringeInject(paramType)(target, propertyKey, parameterIndex);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const existing: Record<number, any> =
|
|
36
|
+
Reflect.getMetadata(INJECT_CUSTOM_TOKENS_KEY, target) || {};
|
|
37
|
+
existing[parameterIndex] = token;
|
|
38
|
+
Reflect.defineMetadata(INJECT_CUSTOM_TOKENS_KEY, existing, target);
|
|
39
|
+
return tsyringeInject(token)(target, propertyKey, parameterIndex);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const OPTIONAL_METADATA = "wilt:optional";
|
|
2
|
+
|
|
3
|
+
export function Optional(): ParameterDecorator {
|
|
4
|
+
return (target, _key, parameterIndex) => {
|
|
5
|
+
const existing: number[] = Reflect.getMetadata(OPTIONAL_METADATA, target) || [];
|
|
6
|
+
existing.push(parameterIndex);
|
|
7
|
+
Reflect.defineMetadata(OPTIONAL_METADATA, existing, target);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface CustomDecorator<TKey = string> {
|
|
2
|
+
(target: any, key?: any, descriptor?: any): any;
|
|
3
|
+
KEY: TKey;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function SetMetadata<K = string, V = any>(
|
|
7
|
+
metadataKey: K,
|
|
8
|
+
metadataValue: V
|
|
9
|
+
): CustomDecorator<K> {
|
|
10
|
+
const decoratorFactory = (target: any, key?: any, descriptor?: PropertyDescriptor): any => {
|
|
11
|
+
if (descriptor) {
|
|
12
|
+
Reflect.defineMetadata(metadataKey as string, metadataValue, descriptor.value);
|
|
13
|
+
return descriptor;
|
|
14
|
+
}
|
|
15
|
+
Reflect.defineMetadata(metadataKey as string, metadataValue, target);
|
|
16
|
+
return target;
|
|
17
|
+
};
|
|
18
|
+
(decoratorFactory as any).KEY = metadataKey;
|
|
19
|
+
return decoratorFactory as CustomDecorator<K>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const GUARDS_METADATA = "wilt:guards";
|
|
2
|
+
|
|
3
|
+
export function UseGuards(...guards: (new (...args: any[]) => any)[]): MethodDecorator & ClassDecorator {
|
|
4
|
+
return (target: any, key?: any, descriptor?: PropertyDescriptor): any => {
|
|
5
|
+
if (descriptor) {
|
|
6
|
+
const existing: any[] = Reflect.getMetadata(GUARDS_METADATA, descriptor.value) || [];
|
|
7
|
+
Reflect.defineMetadata(GUARDS_METADATA, [...existing, ...guards], descriptor.value);
|
|
8
|
+
return descriptor;
|
|
9
|
+
}
|
|
10
|
+
const existing: any[] = Reflect.getMetadata(GUARDS_METADATA, target) || [];
|
|
11
|
+
Reflect.defineMetadata(GUARDS_METADATA, [...existing, ...guards], target);
|
|
12
|
+
return target;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const INTERCEPTORS_METADATA = "wilt:interceptors";
|
|
2
|
+
|
|
3
|
+
export function UseInterceptors(
|
|
4
|
+
...interceptors: (new (...args: any[]) => any)[]
|
|
5
|
+
): MethodDecorator & ClassDecorator {
|
|
6
|
+
return (target: any, key?: any, descriptor?: PropertyDescriptor): any => {
|
|
7
|
+
if (descriptor) {
|
|
8
|
+
const existing: any[] = Reflect.getMetadata(INTERCEPTORS_METADATA, descriptor.value) || [];
|
|
9
|
+
Reflect.defineMetadata(INTERCEPTORS_METADATA, [...existing, ...interceptors], descriptor.value);
|
|
10
|
+
return descriptor;
|
|
11
|
+
}
|
|
12
|
+
const existing: any[] = Reflect.getMetadata(INTERCEPTORS_METADATA, target) || [];
|
|
13
|
+
Reflect.defineMetadata(INTERCEPTORS_METADATA, [...existing, ...interceptors], target);
|
|
14
|
+
return target;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { Hono } from "hono";
|
|
3
|
+
import { injectable } from "tsyringe";
|
|
4
|
+
import { createArgumentsHost, createExecutionContext } from "../../context/execution-context";
|
|
5
|
+
import { CATCH_METADATA, FILTERS_METADATA } from "../core/exception-filters.decorator";
|
|
6
|
+
import { GUARDS_METADATA } from "../core/use-guards.decorator";
|
|
7
|
+
import { INTERCEPTORS_METADATA } from "../core/use-interceptors.decorator";
|
|
8
|
+
import { HEADER_METADATA } from "./header.decorator";
|
|
9
|
+
import { HTTP_CODE_METADATA } from "./http-code.decorator";
|
|
10
|
+
import { REDIRECT_METADATA, type RedirectMetadata } from "./redirect.decorator";
|
|
11
|
+
import { ROUTE_PARAMS_METADATA, TOTAL_PARAMS_METADATA, type RouteParamMetadata } from "./route-params.decorator";
|
|
12
|
+
import { ForbiddenException } from "../../exceptions/http-exceptions";
|
|
13
|
+
import type { RouteMetadata } from "../../interfaces/modules/module.interface";
|
|
14
|
+
|
|
15
|
+
export function Controller(prefix: string = "") {
|
|
16
|
+
return (target: any) => {
|
|
17
|
+
injectable()(target);
|
|
18
|
+
target.prototype.prefix = prefix;
|
|
19
|
+
target.prototype.constructorParams = target.prototype.constructorParams || [];
|
|
20
|
+
target.prototype.constructorClass = target;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveFromRegistry(
|
|
25
|
+
ClassOrInstance: any,
|
|
26
|
+
instanceRegistry?: Map<any, any>
|
|
27
|
+
): any {
|
|
28
|
+
if (typeof ClassOrInstance !== "function") return ClassOrInstance;
|
|
29
|
+
if (instanceRegistry?.has(ClassOrInstance)) return instanceRegistry.get(ClassOrInstance);
|
|
30
|
+
return new ClassOrInstance();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function resolveParamArgs(
|
|
34
|
+
paramMeta: RouteParamMetadata[],
|
|
35
|
+
c: Context,
|
|
36
|
+
totalParams: number
|
|
37
|
+
): Promise<any[]> {
|
|
38
|
+
const maxIndex = Math.max(totalParams - 1, ...paramMeta.map((p) => p.index));
|
|
39
|
+
const args = new Array(maxIndex + 1).fill(undefined);
|
|
40
|
+
const filledIndices = new Set(paramMeta.map((p) => p.index));
|
|
41
|
+
|
|
42
|
+
let body: any;
|
|
43
|
+
const needsBody = paramMeta.some((p) => p.type === "body");
|
|
44
|
+
if (needsBody) {
|
|
45
|
+
const ct = c.req.header("content-type") || "";
|
|
46
|
+
if (ct.includes("multipart/form-data")) {
|
|
47
|
+
const fd = await c.req.formData();
|
|
48
|
+
body = {};
|
|
49
|
+
fd.forEach((v, k) => { body[k] = v; });
|
|
50
|
+
} else {
|
|
51
|
+
body = await c.req.json().catch(() => ({}));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const p of paramMeta) {
|
|
56
|
+
switch (p.type) {
|
|
57
|
+
case "body":
|
|
58
|
+
args[p.index] = p.data ? body?.[p.data] : body;
|
|
59
|
+
break;
|
|
60
|
+
case "param":
|
|
61
|
+
args[p.index] = p.data ? c.req.param(p.data) : c.req.param();
|
|
62
|
+
break;
|
|
63
|
+
case "query":
|
|
64
|
+
args[p.index] = p.data ? c.req.query(p.data) : c.req.query();
|
|
65
|
+
break;
|
|
66
|
+
case "headers":
|
|
67
|
+
args[p.index] = p.data
|
|
68
|
+
? c.req.header(p.data)
|
|
69
|
+
: Object.fromEntries((c.req.raw.headers as any).entries());
|
|
70
|
+
break;
|
|
71
|
+
case "ip":
|
|
72
|
+
args[p.index] =
|
|
73
|
+
c.req.header("cf-connecting-ip") ||
|
|
74
|
+
c.req.header("x-forwarded-for") ||
|
|
75
|
+
"";
|
|
76
|
+
break;
|
|
77
|
+
case "req":
|
|
78
|
+
args[p.index] = c;
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Any slot not covered by a decorator receives the raw Hono context
|
|
84
|
+
for (let i = 0; i <= maxIndex; i++) {
|
|
85
|
+
if (!filledIndices.has(i)) args[i] = c;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return args;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function registerControllerRoutes(
|
|
92
|
+
router: Hono<{ Bindings: any }>,
|
|
93
|
+
controller: any,
|
|
94
|
+
prefix: string = "",
|
|
95
|
+
instanceRegistry?: Map<any, any>
|
|
96
|
+
) {
|
|
97
|
+
const routes: RouteMetadata[] = (controller.constructor as any).prototype.routes || [];
|
|
98
|
+
const controllerClass = controller.constructor;
|
|
99
|
+
|
|
100
|
+
for (const route of routes) {
|
|
101
|
+
const handlerFn = controller[route.handler as keyof typeof controller] as Function;
|
|
102
|
+
const fullPath = prefix + route.path;
|
|
103
|
+
const proto = controllerClass.prototype;
|
|
104
|
+
|
|
105
|
+
const classGuards: any[] = Reflect.getMetadata(GUARDS_METADATA, controllerClass) || [];
|
|
106
|
+
const methodGuards: any[] = Reflect.getMetadata(GUARDS_METADATA, handlerFn) || [];
|
|
107
|
+
const guards = [...classGuards, ...methodGuards];
|
|
108
|
+
|
|
109
|
+
const classInterceptors: any[] = Reflect.getMetadata(INTERCEPTORS_METADATA, controllerClass) || [];
|
|
110
|
+
const methodInterceptors: any[] = Reflect.getMetadata(INTERCEPTORS_METADATA, handlerFn) || [];
|
|
111
|
+
const interceptors = [...classInterceptors, ...methodInterceptors];
|
|
112
|
+
|
|
113
|
+
const classFilters: any[] = Reflect.getMetadata(FILTERS_METADATA, controllerClass) || [];
|
|
114
|
+
const methodFilters: any[] = Reflect.getMetadata(FILTERS_METADATA, handlerFn) || [];
|
|
115
|
+
const filters = [...classFilters, ...methodFilters];
|
|
116
|
+
|
|
117
|
+
const httpCode: number | undefined = Reflect.getMetadata(HTTP_CODE_METADATA, handlerFn);
|
|
118
|
+
const headersToSet: { name: string; value: string }[] =
|
|
119
|
+
Reflect.getMetadata(HEADER_METADATA, handlerFn) || [];
|
|
120
|
+
const redirectMeta: RedirectMetadata | undefined = Reflect.getMetadata(REDIRECT_METADATA, handlerFn);
|
|
121
|
+
const paramMeta: RouteParamMetadata[] =
|
|
122
|
+
Reflect.getMetadata(ROUTE_PARAMS_METADATA, proto, route.handler) || [];
|
|
123
|
+
const totalParams: number =
|
|
124
|
+
Reflect.getMetadata(TOTAL_PARAMS_METADATA, proto, route.handler) ?? 0;
|
|
125
|
+
const methodMiddlewares: any[] = (handlerFn as any).middlewares || [];
|
|
126
|
+
|
|
127
|
+
const finalHandler = async (c: Context): Promise<Response> => {
|
|
128
|
+
const execCtx = createExecutionContext(c, controllerClass, handlerFn);
|
|
129
|
+
|
|
130
|
+
const runCore = async (): Promise<Response> => {
|
|
131
|
+
// Guards
|
|
132
|
+
for (const G of guards) {
|
|
133
|
+
const guard = resolveFromRegistry(G, instanceRegistry);
|
|
134
|
+
const ok = await guard.canActivate(execCtx);
|
|
135
|
+
if (!ok) throw new ForbiddenException();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Build the actual call
|
|
139
|
+
const callHandler = async (): Promise<Response> => {
|
|
140
|
+
let result: any;
|
|
141
|
+
if (paramMeta.length > 0) {
|
|
142
|
+
const args = await resolveParamArgs(paramMeta, c, totalParams);
|
|
143
|
+
result = await handlerFn.call(controller, ...args);
|
|
144
|
+
} else {
|
|
145
|
+
result = await handlerFn.call(controller, c);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Set response headers
|
|
149
|
+
for (const { name, value } of headersToSet) {
|
|
150
|
+
c.header(name, value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Redirect takes precedence
|
|
154
|
+
if (redirectMeta) {
|
|
155
|
+
const url =
|
|
156
|
+
result && typeof result === "object" && "url" in result
|
|
157
|
+
? (result as any).url
|
|
158
|
+
: redirectMeta.url;
|
|
159
|
+
const code =
|
|
160
|
+
result && typeof result === "object" && "statusCode" in result
|
|
161
|
+
? (result as any).statusCode
|
|
162
|
+
: redirectMeta.statusCode;
|
|
163
|
+
return c.redirect(url, code as any);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Auto-serialize non-Response returns
|
|
167
|
+
if (result instanceof Response) return result;
|
|
168
|
+
if (result !== undefined && result !== null) {
|
|
169
|
+
return c.json(result, (httpCode ?? 200) as any);
|
|
170
|
+
}
|
|
171
|
+
return new Response(null, { status: httpCode ?? 204 });
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Method-level legacy middlewares
|
|
175
|
+
if (methodMiddlewares.length > 0) {
|
|
176
|
+
let idx = 0;
|
|
177
|
+
const next = async (): Promise<Response | void> => {
|
|
178
|
+
if (idx < methodMiddlewares.length) {
|
|
179
|
+
return await methodMiddlewares[idx++](c, next);
|
|
180
|
+
}
|
|
181
|
+
return callHandler();
|
|
182
|
+
};
|
|
183
|
+
return (await next()) as Response;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return callHandler();
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Interceptors wrap the core call
|
|
190
|
+
const runWithInterceptors = async (): Promise<Response> => {
|
|
191
|
+
if (interceptors.length === 0) return runCore();
|
|
192
|
+
|
|
193
|
+
let chain = runCore;
|
|
194
|
+
for (let i = interceptors.length - 1; i >= 0; i--) {
|
|
195
|
+
const interceptor = resolveFromRegistry(interceptors[i], instanceRegistry);
|
|
196
|
+
const inner = chain;
|
|
197
|
+
chain = () => interceptor.intercept(execCtx, { handle: inner });
|
|
198
|
+
}
|
|
199
|
+
return chain();
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
// Exception filters wrap everything
|
|
203
|
+
if (filters.length === 0) return runWithInterceptors();
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
return await runWithInterceptors();
|
|
207
|
+
} catch (err) {
|
|
208
|
+
for (const F of filters) {
|
|
209
|
+
const filter = resolveFromRegistry(F, instanceRegistry);
|
|
210
|
+
const catchTypes: any[] =
|
|
211
|
+
Reflect.getMetadata(CATCH_METADATA, filter.constructor ?? F) || [];
|
|
212
|
+
if (catchTypes.length === 0 || catchTypes.some((T) => err instanceof T)) {
|
|
213
|
+
const host = createArgumentsHost(c);
|
|
214
|
+
const res = await filter.catch(err, host);
|
|
215
|
+
if (res instanceof Response) return res;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
throw err;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
switch (route.method) {
|
|
223
|
+
case "GET": router.get(fullPath, finalHandler); break;
|
|
224
|
+
case "POST": router.post(fullPath, finalHandler); break;
|
|
225
|
+
case "PUT": router.put(fullPath, finalHandler); break;
|
|
226
|
+
case "DELETE": router.delete(fullPath, finalHandler); break;
|
|
227
|
+
case "PATCH": router.patch(fullPath, finalHandler); break;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const HEADER_METADATA = "wilt:response-headers";
|
|
2
|
+
|
|
3
|
+
export function Header(name: string, value: string): MethodDecorator {
|
|
4
|
+
return (_target, _key, descriptor: PropertyDescriptor) => {
|
|
5
|
+
const existing: { name: string; value: string }[] =
|
|
6
|
+
Reflect.getMetadata(HEADER_METADATA, descriptor.value) || [];
|
|
7
|
+
existing.push({ name, value });
|
|
8
|
+
Reflect.defineMetadata(HEADER_METADATA, existing, descriptor.value);
|
|
9
|
+
return descriptor;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const HTTP_CODE_METADATA = "wilt:http-code";
|
|
2
|
+
|
|
3
|
+
export function HttpCode(statusCode: number): MethodDecorator {
|
|
4
|
+
return (_target, _key, descriptor: PropertyDescriptor) => {
|
|
5
|
+
Reflect.defineMetadata(HTTP_CODE_METADATA, statusCode, descriptor.value);
|
|
6
|
+
return descriptor;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const REDIRECT_METADATA = "wilt:redirect";
|
|
2
|
+
|
|
3
|
+
export interface RedirectMetadata {
|
|
4
|
+
url: string;
|
|
5
|
+
statusCode: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function Redirect(url: string, statusCode: number = 302): MethodDecorator {
|
|
9
|
+
return (_target, _key, descriptor: PropertyDescriptor) => {
|
|
10
|
+
Reflect.defineMetadata(REDIRECT_METADATA, { url, statusCode }, descriptor.value);
|
|
11
|
+
return descriptor;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function createMethodDecorator(
|
|
2
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH"
|
|
3
|
+
) {
|
|
4
|
+
return (path: string = "") =>
|
|
5
|
+
(target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
|
|
6
|
+
if (!target.constructor.prototype.routes) {
|
|
7
|
+
target.constructor.prototype.routes = [];
|
|
8
|
+
}
|
|
9
|
+
target.constructor.prototype.routes.push({
|
|
10
|
+
method,
|
|
11
|
+
path,
|
|
12
|
+
handler: propertyKey,
|
|
13
|
+
});
|
|
14
|
+
return descriptor;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const Get = createMethodDecorator("GET");
|
|
19
|
+
export const Post = createMethodDecorator("POST");
|
|
20
|
+
export const Put = createMethodDecorator("PUT");
|
|
21
|
+
export const Delete = createMethodDecorator("DELETE");
|
|
22
|
+
export const Patch = createMethodDecorator("PATCH");
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const ROUTE_PARAMS_METADATA = "wilt:route-params";
|
|
2
|
+
export const TOTAL_PARAMS_METADATA = "wilt:total-params";
|
|
3
|
+
|
|
4
|
+
export type RouteParamType = "body" | "param" | "query" | "headers" | "ip" | "req";
|
|
5
|
+
|
|
6
|
+
export interface RouteParamMetadata {
|
|
7
|
+
index: number;
|
|
8
|
+
type: RouteParamType;
|
|
9
|
+
data?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function recordTotalParams(target: any, key: string) {
|
|
13
|
+
if (!Reflect.hasMetadata(TOTAL_PARAMS_METADATA, target, key)) {
|
|
14
|
+
const fn = target[key];
|
|
15
|
+
if (typeof fn === "function") {
|
|
16
|
+
Reflect.defineMetadata(TOTAL_PARAMS_METADATA, fn.length, target, key);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function createParamDecorator(type: RouteParamType) {
|
|
22
|
+
return (data?: string): ParameterDecorator =>
|
|
23
|
+
(target, propertyKey, parameterIndex) => {
|
|
24
|
+
const key = propertyKey as string;
|
|
25
|
+
const existing: RouteParamMetadata[] =
|
|
26
|
+
Reflect.getMetadata(ROUTE_PARAMS_METADATA, target, key) || [];
|
|
27
|
+
existing.push({ index: parameterIndex, type, data });
|
|
28
|
+
Reflect.defineMetadata(ROUTE_PARAMS_METADATA, existing, target, key);
|
|
29
|
+
recordTotalParams(target, key);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Body = createParamDecorator("body");
|
|
34
|
+
export const Param = createParamDecorator("param");
|
|
35
|
+
export const Query = createParamDecorator("query");
|
|
36
|
+
export const Headers = createParamDecorator("headers");
|
|
37
|
+
|
|
38
|
+
export function Ip(): ParameterDecorator {
|
|
39
|
+
return (target, propertyKey, parameterIndex) => {
|
|
40
|
+
const key = propertyKey as string;
|
|
41
|
+
const existing: RouteParamMetadata[] =
|
|
42
|
+
Reflect.getMetadata(ROUTE_PARAMS_METADATA, target, key) || [];
|
|
43
|
+
existing.push({ index: parameterIndex, type: "ip" });
|
|
44
|
+
Reflect.defineMetadata(ROUTE_PARAMS_METADATA, existing, target, key);
|
|
45
|
+
recordTotalParams(target, key);
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function Req(): ParameterDecorator {
|
|
50
|
+
return (target, propertyKey, parameterIndex) => {
|
|
51
|
+
const key = propertyKey as string;
|
|
52
|
+
const existing: RouteParamMetadata[] =
|
|
53
|
+
Reflect.getMetadata(ROUTE_PARAMS_METADATA, target, key) || [];
|
|
54
|
+
existing.push({ index: parameterIndex, type: "req" });
|
|
55
|
+
Reflect.defineMetadata(ROUTE_PARAMS_METADATA, existing, target, key);
|
|
56
|
+
recordTotalParams(target, key);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { Req as Request };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { injectable } from "tsyringe";
|
|
2
|
+
import type { ModuleMetadata } from "../../interfaces/modules/module.interface";
|
|
3
|
+
|
|
4
|
+
export function Module(config: ModuleMetadata) {
|
|
5
|
+
return (target: any) => {
|
|
6
|
+
target.prototype.moduleConfig = config;
|
|
7
|
+
|
|
8
|
+
if (config.providers) {
|
|
9
|
+
config.providers.forEach((ProviderClass: any) => {
|
|
10
|
+
if (ProviderClass && typeof ProviderClass === "function") {
|
|
11
|
+
injectable()(ProviderClass);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class HttpException extends Error {
|
|
2
|
+
constructor(
|
|
3
|
+
private readonly response: string | Record<string, any>,
|
|
4
|
+
private readonly statusCode: number
|
|
5
|
+
) {
|
|
6
|
+
super(typeof response === "string" ? response : (response as any).message ?? "HttpException");
|
|
7
|
+
this.name = "HttpException";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getStatus(): number {
|
|
11
|
+
return this.statusCode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getResponse(): string | Record<string, any> {
|
|
15
|
+
return this.response;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { HttpException } from "./http-exception";
|
|
2
|
+
|
|
3
|
+
export class BadRequestException extends HttpException {
|
|
4
|
+
constructor(message: string | Record<string, any> = "Bad Request") {
|
|
5
|
+
super(message, 400);
|
|
6
|
+
this.name = "BadRequestException";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class UnauthorizedException extends HttpException {
|
|
11
|
+
constructor(message: string | Record<string, any> = "Unauthorized") {
|
|
12
|
+
super(message, 401);
|
|
13
|
+
this.name = "UnauthorizedException";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ForbiddenException extends HttpException {
|
|
18
|
+
constructor(message: string | Record<string, any> = "Forbidden") {
|
|
19
|
+
super(message, 403);
|
|
20
|
+
this.name = "ForbiddenException";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class NotFoundException extends HttpException {
|
|
25
|
+
constructor(message: string | Record<string, any> = "Not Found") {
|
|
26
|
+
super(message, 404);
|
|
27
|
+
this.name = "NotFoundException";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class MethodNotAllowedException extends HttpException {
|
|
32
|
+
constructor(message: string | Record<string, any> = "Method Not Allowed") {
|
|
33
|
+
super(message, 405);
|
|
34
|
+
this.name = "MethodNotAllowedException";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class ConflictException extends HttpException {
|
|
39
|
+
constructor(message: string | Record<string, any> = "Conflict") {
|
|
40
|
+
super(message, 409);
|
|
41
|
+
this.name = "ConflictException";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class GoneException extends HttpException {
|
|
46
|
+
constructor(message: string | Record<string, any> = "Gone") {
|
|
47
|
+
super(message, 410);
|
|
48
|
+
this.name = "GoneException";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class UnprocessableEntityException extends HttpException {
|
|
53
|
+
constructor(message: string | Record<string, any> = "Unprocessable Entity") {
|
|
54
|
+
super(message, 422);
|
|
55
|
+
this.name = "UnprocessableEntityException";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class TooManyRequestsException extends HttpException {
|
|
60
|
+
constructor(message: string | Record<string, any> = "Too Many Requests") {
|
|
61
|
+
super(message, 429);
|
|
62
|
+
this.name = "TooManyRequestsException";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export class InternalServerErrorException extends HttpException {
|
|
67
|
+
constructor(message: string | Record<string, any> = "Internal Server Error") {
|
|
68
|
+
super(message, 500);
|
|
69
|
+
this.name = "InternalServerErrorException";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class NotImplementedException extends HttpException {
|
|
74
|
+
constructor(message: string | Record<string, any> = "Not Implemented") {
|
|
75
|
+
super(message, 501);
|
|
76
|
+
this.name = "NotImplementedException";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export class ServiceUnavailableException extends HttpException {
|
|
81
|
+
constructor(message: string | Record<string, any> = "Service Unavailable") {
|
|
82
|
+
super(message, 503);
|
|
83
|
+
this.name = "ServiceUnavailableException";
|
|
84
|
+
}
|
|
85
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./decorators";
|
|
2
|
+
export * from "./injector";
|
|
3
|
+
export * from "./interfaces";
|
|
4
|
+
export * from "./middleware";
|
|
5
|
+
export * from "./pipes";
|
|
6
|
+
export * from "./exceptions";
|
|
7
|
+
export * from "./context";
|
|
8
|
+
export * from "./services";
|
|
9
|
+
export * from "./utils/forward-ref.util";
|
|
10
|
+
export * from "./utils/response.util";
|
|
11
|
+
export * from "./utils/apply-decorators.util";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./module.factory";
|