@avleon/core 0.0.19 → 0.0.24
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/README.md +559 -2
- package/dist/application.d.ts +26 -0
- package/dist/application.js +50 -0
- package/dist/collection.js +3 -2
- package/dist/container.d.ts +1 -0
- package/dist/container.js +2 -1
- package/dist/environment-variables.js +1 -1
- package/dist/file-storage.js +0 -128
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +41 -4
- package/dist/icore.d.ts +68 -26
- package/dist/icore.js +235 -212
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/middleware.js +0 -8
- package/dist/multipart.js +4 -1
- package/dist/openapi.d.ts +37 -37
- package/dist/params.js +2 -2
- package/dist/response.js +6 -2
- package/dist/route-methods.js +1 -1
- package/dist/swagger-schema.js +4 -1
- package/dist/testing.js +5 -2
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/optional-require.d.ts +8 -0
- package/dist/utils/optional-require.js +70 -0
- package/dist/validation.d.ts +4 -1
- package/dist/validation.js +10 -5
- package/package.json +19 -11
- package/src/application.ts +96 -0
- package/src/authentication.ts +16 -0
- package/src/collection.ts +254 -0
- package/src/config.ts +39 -0
- package/src/constants.ts +1 -0
- package/src/container.ts +54 -0
- package/src/controller.ts +128 -0
- package/src/decorators.ts +27 -0
- package/src/environment-variables.ts +46 -0
- package/src/exceptions/http-exceptions.ts +86 -0
- package/src/exceptions/index.ts +1 -0
- package/src/exceptions/system-exception.ts +34 -0
- package/src/file-storage.ts +206 -0
- package/src/helpers.ts +328 -0
- package/src/icore.ts +1064 -0
- package/src/index.ts +30 -0
- package/src/logger.ts +72 -0
- package/src/map-types.ts +159 -0
- package/src/middleware.ts +98 -0
- package/src/multipart.ts +116 -0
- package/src/openapi.ts +372 -0
- package/src/params.ts +111 -0
- package/src/queue.ts +126 -0
- package/src/response.ts +117 -0
- package/src/results.ts +30 -0
- package/src/route-methods.ts +186 -0
- package/src/swagger-schema.ts +213 -0
- package/src/testing.ts +220 -0
- package/src/types/app-builder.interface.ts +19 -0
- package/src/types/application.interface.ts +9 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/optional-require.ts +50 -0
- package/src/validation.ts +156 -0
- package/src/validator-extend.ts +25 -0
- package/dist/classToOpenapi.d.ts +0 -0
- package/dist/classToOpenapi.js +0 -1
- package/dist/render.d.ts +0 -1
- package/dist/render.js +0 -8
- package/jest.config.ts +0 -9
- package/tsconfig.json +0 -25
- /package/dist/{security.d.ts → utils/hash.d.ts} +0 -0
- /package/dist/{security.js → utils/hash.js} +0 -0
package/src/openapi.ts
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2024
|
|
3
|
+
* @author Tareq Hossain
|
|
4
|
+
* @email xtrinsic96@gmail.com
|
|
5
|
+
* @url https://github.com/xtareq
|
|
6
|
+
*/
|
|
7
|
+
export interface InfoObject {
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
termsOfService?: string;
|
|
11
|
+
contact?: ContactObject;
|
|
12
|
+
license?: LicenseObject;
|
|
13
|
+
version: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ContactObject {
|
|
16
|
+
name?: string;
|
|
17
|
+
url?: string;
|
|
18
|
+
email?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface LicenseObject {
|
|
21
|
+
name: string;
|
|
22
|
+
url?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ServerObject {
|
|
25
|
+
url: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
variables?: {
|
|
28
|
+
[variable: string]: ServerVariableObject;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export interface ServerVariableObject {
|
|
32
|
+
enum?: string[];
|
|
33
|
+
default: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type OpenApiUiOptions = {
|
|
38
|
+
logo?: any;
|
|
39
|
+
theme?: any;
|
|
40
|
+
ui?: "default" | "scalar";
|
|
41
|
+
openapi?: string;
|
|
42
|
+
configuration?: any;
|
|
43
|
+
routePrefix?: string;
|
|
44
|
+
info?: InfoObject;
|
|
45
|
+
servers?: ServerObject[];
|
|
46
|
+
paths?: PathsObject<any>;
|
|
47
|
+
components?: ComponentsObject;
|
|
48
|
+
security?: SecurityRequirementObject[];
|
|
49
|
+
tags?: TagObject[];
|
|
50
|
+
externalDocs?: any;
|
|
51
|
+
"x-express-openapi-additional-middleware"?: (
|
|
52
|
+
| ((request: any, response: any, next: any) => Promise<void>)
|
|
53
|
+
| ((request: any, response: any, next: any) => void)
|
|
54
|
+
)[];
|
|
55
|
+
"x-express-openapi-validation-strict"?: boolean;
|
|
56
|
+
};
|
|
57
|
+
export interface PathsObject<T extends {} = {}, P extends {} = {}> {
|
|
58
|
+
[pattern: string]: (PathItemObject<T> & P) | undefined;
|
|
59
|
+
}
|
|
60
|
+
enum HttpMethods {
|
|
61
|
+
GET = "get",
|
|
62
|
+
PUT = "put",
|
|
63
|
+
POST = "post",
|
|
64
|
+
DELETE = "delete",
|
|
65
|
+
OPTIONS = "options",
|
|
66
|
+
HEAD = "head",
|
|
67
|
+
PATCH = "patch",
|
|
68
|
+
TRACE = "trace",
|
|
69
|
+
}
|
|
70
|
+
export type PathItemObject<T extends {} = {}> = {
|
|
71
|
+
$ref?: string;
|
|
72
|
+
summary?: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
servers?: ServerObject[];
|
|
75
|
+
parameters?: (ReferenceObject | ParameterObject)[];
|
|
76
|
+
} & {
|
|
77
|
+
[method in HttpMethods]?: OperationObject<T>;
|
|
78
|
+
};
|
|
79
|
+
export type OperationObject<T extends {} = {}> = {
|
|
80
|
+
tags?: string[];
|
|
81
|
+
summary?: string;
|
|
82
|
+
description?: string;
|
|
83
|
+
externalDocs?: ExternalDocumentationObject;
|
|
84
|
+
operationId?: string;
|
|
85
|
+
parameters?: (ReferenceObject | ParameterObject)[];
|
|
86
|
+
requestBody?: ReferenceObject | RequestBodyObject;
|
|
87
|
+
responses: ResponsesObject;
|
|
88
|
+
callbacks?: {
|
|
89
|
+
[callback: string]: ReferenceObject | CallbackObject;
|
|
90
|
+
};
|
|
91
|
+
deprecated?: boolean;
|
|
92
|
+
security?: SecurityRequirementObject[];
|
|
93
|
+
servers?: ServerObject[];
|
|
94
|
+
} & T;
|
|
95
|
+
export interface ExternalDocumentationObject {
|
|
96
|
+
description?: string;
|
|
97
|
+
url: string;
|
|
98
|
+
}
|
|
99
|
+
export interface ParameterObject extends ParameterBaseObject {
|
|
100
|
+
name: string;
|
|
101
|
+
in: string;
|
|
102
|
+
}
|
|
103
|
+
export interface HeaderObject extends ParameterBaseObject {}
|
|
104
|
+
export interface ParameterBaseObject {
|
|
105
|
+
description?: string;
|
|
106
|
+
required?: boolean;
|
|
107
|
+
deprecated?: boolean;
|
|
108
|
+
allowEmptyValue?: boolean;
|
|
109
|
+
style?: string;
|
|
110
|
+
explode?: boolean;
|
|
111
|
+
allowReserved?: boolean;
|
|
112
|
+
schema?: ReferenceObject | SchemaObject;
|
|
113
|
+
example?: any;
|
|
114
|
+
examples?: {
|
|
115
|
+
[media: string]: ReferenceObject | ExampleObject;
|
|
116
|
+
};
|
|
117
|
+
content?: {
|
|
118
|
+
[media: string]: MediaTypeObject;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export type NonArraySchemaObjectType =
|
|
122
|
+
| "boolean"
|
|
123
|
+
| "object"
|
|
124
|
+
| "number"
|
|
125
|
+
| "string"
|
|
126
|
+
| "integer";
|
|
127
|
+
export type ArraySchemaObjectType = "array";
|
|
128
|
+
export type SchemaObject = ArraySchemaObject | NonArraySchemaObject;
|
|
129
|
+
export interface ArraySchemaObject extends BaseSchemaObject {
|
|
130
|
+
type: ArraySchemaObjectType;
|
|
131
|
+
items: ReferenceObject | SchemaObject;
|
|
132
|
+
}
|
|
133
|
+
export interface NonArraySchemaObject extends BaseSchemaObject {
|
|
134
|
+
type?: NonArraySchemaObjectType;
|
|
135
|
+
}
|
|
136
|
+
export interface BaseSchemaObject {
|
|
137
|
+
title?: string;
|
|
138
|
+
description?: string;
|
|
139
|
+
format?: string;
|
|
140
|
+
default?: any;
|
|
141
|
+
multipleOf?: number;
|
|
142
|
+
maximum?: number;
|
|
143
|
+
exclusiveMaximum?: boolean;
|
|
144
|
+
minimum?: number;
|
|
145
|
+
exclusiveMinimum?: boolean;
|
|
146
|
+
maxLength?: number;
|
|
147
|
+
minLength?: number;
|
|
148
|
+
pattern?: string;
|
|
149
|
+
additionalProperties?: boolean | ReferenceObject | SchemaObject;
|
|
150
|
+
maxItems?: number;
|
|
151
|
+
minItems?: number;
|
|
152
|
+
uniqueItems?: boolean;
|
|
153
|
+
maxProperties?: number;
|
|
154
|
+
minProperties?: number;
|
|
155
|
+
required?: string[];
|
|
156
|
+
enum?: any[];
|
|
157
|
+
properties?: {
|
|
158
|
+
[name: string]: ReferenceObject | SchemaObject;
|
|
159
|
+
};
|
|
160
|
+
allOf?: (ReferenceObject | SchemaObject)[];
|
|
161
|
+
oneOf?: (ReferenceObject | SchemaObject)[];
|
|
162
|
+
anyOf?: (ReferenceObject | SchemaObject)[];
|
|
163
|
+
not?: ReferenceObject | SchemaObject;
|
|
164
|
+
nullable?: boolean;
|
|
165
|
+
discriminator?: DiscriminatorObject;
|
|
166
|
+
readOnly?: boolean;
|
|
167
|
+
writeOnly?: boolean;
|
|
168
|
+
xml?: XMLObject;
|
|
169
|
+
externalDocs?: ExternalDocumentationObject;
|
|
170
|
+
example?: any;
|
|
171
|
+
deprecated?: boolean;
|
|
172
|
+
}
|
|
173
|
+
export interface DiscriminatorObject {
|
|
174
|
+
propertyName: string;
|
|
175
|
+
mapping?: {
|
|
176
|
+
[value: string]: string;
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
export interface XMLObject {
|
|
180
|
+
name?: string;
|
|
181
|
+
namespace?: string;
|
|
182
|
+
prefix?: string;
|
|
183
|
+
attribute?: boolean;
|
|
184
|
+
wrapped?: boolean;
|
|
185
|
+
}
|
|
186
|
+
export interface ReferenceObject {
|
|
187
|
+
$ref: string;
|
|
188
|
+
}
|
|
189
|
+
export interface ExampleObject {
|
|
190
|
+
summary?: string;
|
|
191
|
+
description?: string;
|
|
192
|
+
value?: any;
|
|
193
|
+
externalValue?: string;
|
|
194
|
+
}
|
|
195
|
+
export interface MediaTypeObject {
|
|
196
|
+
schema?: ReferenceObject | SchemaObject;
|
|
197
|
+
example?: any;
|
|
198
|
+
examples?: {
|
|
199
|
+
[media: string]: ReferenceObject | ExampleObject;
|
|
200
|
+
};
|
|
201
|
+
encoding?: {
|
|
202
|
+
[media: string]: EncodingObject;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export interface EncodingObject {
|
|
206
|
+
contentType?: string;
|
|
207
|
+
headers?: {
|
|
208
|
+
[header: string]: ReferenceObject | HeaderObject;
|
|
209
|
+
};
|
|
210
|
+
style?: string;
|
|
211
|
+
explode?: boolean;
|
|
212
|
+
allowReserved?: boolean;
|
|
213
|
+
}
|
|
214
|
+
export interface RequestBodyObject {
|
|
215
|
+
description?: string;
|
|
216
|
+
content: {
|
|
217
|
+
[media: string]: MediaTypeObject;
|
|
218
|
+
};
|
|
219
|
+
required?: boolean;
|
|
220
|
+
}
|
|
221
|
+
export interface ResponsesObject {
|
|
222
|
+
[code: string]: ReferenceObject | ResponseObject;
|
|
223
|
+
}
|
|
224
|
+
export interface ResponseObject {
|
|
225
|
+
description: string;
|
|
226
|
+
headers?: {
|
|
227
|
+
[header: string]: ReferenceObject | HeaderObject;
|
|
228
|
+
};
|
|
229
|
+
content?: {
|
|
230
|
+
[media: string]: MediaTypeObject;
|
|
231
|
+
};
|
|
232
|
+
links?: {
|
|
233
|
+
[link: string]: ReferenceObject | LinkObject;
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
export interface LinkObject {
|
|
237
|
+
operationRef?: string;
|
|
238
|
+
operationId?: string;
|
|
239
|
+
parameters?: {
|
|
240
|
+
[parameter: string]: any;
|
|
241
|
+
};
|
|
242
|
+
requestBody?: any;
|
|
243
|
+
description?: string;
|
|
244
|
+
server?: ServerObject;
|
|
245
|
+
}
|
|
246
|
+
export interface CallbackObject {
|
|
247
|
+
[url: string]: PathItemObject;
|
|
248
|
+
}
|
|
249
|
+
export interface SecurityRequirementObject {
|
|
250
|
+
[name: string]: string[];
|
|
251
|
+
}
|
|
252
|
+
export interface ComponentsObject {
|
|
253
|
+
schemas?: {
|
|
254
|
+
[key: string]: ReferenceObject | SchemaObject;
|
|
255
|
+
};
|
|
256
|
+
responses?: {
|
|
257
|
+
[key: string]: ReferenceObject | ResponseObject;
|
|
258
|
+
};
|
|
259
|
+
parameters?: {
|
|
260
|
+
[key: string]: ReferenceObject | ParameterObject;
|
|
261
|
+
};
|
|
262
|
+
examples?: {
|
|
263
|
+
[key: string]: ReferenceObject | ExampleObject;
|
|
264
|
+
};
|
|
265
|
+
requestBodies?: {
|
|
266
|
+
[key: string]: ReferenceObject | RequestBodyObject;
|
|
267
|
+
};
|
|
268
|
+
headers?: {
|
|
269
|
+
[key: string]: ReferenceObject | HeaderObject;
|
|
270
|
+
};
|
|
271
|
+
securitySchemes?: {
|
|
272
|
+
[key: string]: ReferenceObject | SecuritySchemeObject;
|
|
273
|
+
};
|
|
274
|
+
links?: {
|
|
275
|
+
[key: string]: ReferenceObject | LinkObject;
|
|
276
|
+
};
|
|
277
|
+
callbacks?: {
|
|
278
|
+
[key: string]: ReferenceObject | CallbackObject;
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
export type SecuritySchemeObject =
|
|
282
|
+
| HttpSecurityScheme
|
|
283
|
+
| ApiKeySecurityScheme
|
|
284
|
+
| OAuth2SecurityScheme
|
|
285
|
+
| OpenIdSecurityScheme;
|
|
286
|
+
export interface HttpSecurityScheme {
|
|
287
|
+
type: "http";
|
|
288
|
+
description?: string;
|
|
289
|
+
scheme: string;
|
|
290
|
+
bearerFormat?: string;
|
|
291
|
+
}
|
|
292
|
+
export interface ApiKeySecurityScheme {
|
|
293
|
+
type: "apiKey";
|
|
294
|
+
description?: string;
|
|
295
|
+
name: string;
|
|
296
|
+
in: string;
|
|
297
|
+
}
|
|
298
|
+
export interface OAuth2SecurityScheme {
|
|
299
|
+
type: "oauth2";
|
|
300
|
+
description?: string;
|
|
301
|
+
flows: {
|
|
302
|
+
implicit?: {
|
|
303
|
+
authorizationUrl: string;
|
|
304
|
+
refreshUrl?: string;
|
|
305
|
+
scopes: {
|
|
306
|
+
[scope: string]: string;
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
password?: {
|
|
310
|
+
tokenUrl: string;
|
|
311
|
+
refreshUrl?: string;
|
|
312
|
+
scopes: {
|
|
313
|
+
[scope: string]: string;
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
clientCredentials?: {
|
|
317
|
+
tokenUrl: string;
|
|
318
|
+
refreshUrl?: string;
|
|
319
|
+
scopes: {
|
|
320
|
+
[scope: string]: string;
|
|
321
|
+
};
|
|
322
|
+
};
|
|
323
|
+
authorizationCode?: {
|
|
324
|
+
authorizationUrl: string;
|
|
325
|
+
tokenUrl: string;
|
|
326
|
+
refreshUrl?: string;
|
|
327
|
+
scopes: {
|
|
328
|
+
[scope: string]: string;
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
export interface OpenIdSecurityScheme {
|
|
334
|
+
type: "openIdConnect";
|
|
335
|
+
description?: string;
|
|
336
|
+
openIdConnectUrl: string;
|
|
337
|
+
}
|
|
338
|
+
export interface TagObject {
|
|
339
|
+
name: string;
|
|
340
|
+
description?: string;
|
|
341
|
+
externalDocs?: ExternalDocumentationObject;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export type OpenApiOptions = {
|
|
345
|
+
exclude?: boolean;
|
|
346
|
+
deprecated?: boolean;
|
|
347
|
+
tags?: readonly string[];
|
|
348
|
+
description?: string;
|
|
349
|
+
summary?: string;
|
|
350
|
+
components?: ComponentsObject;
|
|
351
|
+
response?: any;
|
|
352
|
+
responseBody?: any;
|
|
353
|
+
requestBody?: any;
|
|
354
|
+
} & any;
|
|
355
|
+
|
|
356
|
+
export function OpenApi(
|
|
357
|
+
options: OpenApiOptions
|
|
358
|
+
): MethodDecorator & ClassDecorator & PropertyDecorator {
|
|
359
|
+
return function (
|
|
360
|
+
target: Object | Function,
|
|
361
|
+
propertyKey?: string | symbol,
|
|
362
|
+
descriptor?: PropertyDescriptor
|
|
363
|
+
) {
|
|
364
|
+
if (typeof target === "function" && !propertyKey) {
|
|
365
|
+
Reflect.defineMetadata("controller:openapi", options, target);
|
|
366
|
+
} else if (descriptor) {
|
|
367
|
+
Reflect.defineMetadata("route:openapi", options, target, propertyKey!);
|
|
368
|
+
} else if (propertyKey) {
|
|
369
|
+
Reflect.defineMetadata("property:openapi", options, target, propertyKey);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
}
|
package/src/params.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2024
|
|
3
|
+
* @author Tareq Hossain
|
|
4
|
+
* @email xtrinsic96@gmail.com
|
|
5
|
+
* @url https://github.com/xtareq
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
PARAM_META_KEY,
|
|
10
|
+
QUERY_META_KEY,
|
|
11
|
+
REQUEST_BODY_META_KEY,
|
|
12
|
+
REQUEST_HEADER_META_KEY,
|
|
13
|
+
REQUEST_USER_META_KEY,
|
|
14
|
+
ROUTE_META_KEY,
|
|
15
|
+
} from "./container";
|
|
16
|
+
import { getDataType, isClassValidatorClass, isValidType } from "./helpers";
|
|
17
|
+
import { generateSwaggerSchema } from "./swagger-schema";
|
|
18
|
+
|
|
19
|
+
type ParameterOptions = {
|
|
20
|
+
required?: boolean;
|
|
21
|
+
validate?: boolean;
|
|
22
|
+
type?: any;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function createParamDecorator(
|
|
26
|
+
type: string | symbol
|
|
27
|
+
): (
|
|
28
|
+
key?: string | ParameterOptions,
|
|
29
|
+
options?: ParameterOptions
|
|
30
|
+
) => ParameterDecorator {
|
|
31
|
+
return function (
|
|
32
|
+
key?: string | ParameterOptions,
|
|
33
|
+
options: { required?: boolean; validate?: boolean } = {}
|
|
34
|
+
): ParameterDecorator {
|
|
35
|
+
return function (target: any, propertyKey: any, parameterIndex: number) {
|
|
36
|
+
const existingParams =
|
|
37
|
+
Reflect.getMetadata(type, target, propertyKey) || [];
|
|
38
|
+
const parameterTypes =
|
|
39
|
+
Reflect.getMetadata("design:paramtypes", target, propertyKey) || [];
|
|
40
|
+
const functionSource: any = target[propertyKey].toString();
|
|
41
|
+
const paramNames = functionSource
|
|
42
|
+
.match(/\(([^)]*)\)/)?.[1]
|
|
43
|
+
.split(",")
|
|
44
|
+
.map((name: any) => name.trim());
|
|
45
|
+
const paramDataType = parameterTypes[parameterIndex];
|
|
46
|
+
existingParams.push({
|
|
47
|
+
index: parameterIndex,
|
|
48
|
+
key: key ? key : "all",
|
|
49
|
+
name: paramNames[parameterIndex],
|
|
50
|
+
required: options.required == undefined ? true : options.required ,
|
|
51
|
+
validate: options.validate == undefined ? true: options.validate,
|
|
52
|
+
dataType: getDataType(paramDataType),
|
|
53
|
+
validatorClass: isClassValidatorClass(paramDataType),
|
|
54
|
+
schema: isClassValidatorClass(paramDataType)
|
|
55
|
+
? generateSwaggerSchema(paramDataType)
|
|
56
|
+
: null,
|
|
57
|
+
type,
|
|
58
|
+
});
|
|
59
|
+
switch (type) {
|
|
60
|
+
case "route:param":
|
|
61
|
+
Reflect.defineMetadata(
|
|
62
|
+
PARAM_META_KEY,
|
|
63
|
+
existingParams,
|
|
64
|
+
target,
|
|
65
|
+
propertyKey
|
|
66
|
+
);
|
|
67
|
+
break;
|
|
68
|
+
case "route:query":
|
|
69
|
+
Reflect.defineMetadata(
|
|
70
|
+
QUERY_META_KEY,
|
|
71
|
+
existingParams,
|
|
72
|
+
target,
|
|
73
|
+
propertyKey
|
|
74
|
+
);
|
|
75
|
+
break;
|
|
76
|
+
case "route:body":
|
|
77
|
+
Reflect.defineMetadata(
|
|
78
|
+
REQUEST_BODY_META_KEY,
|
|
79
|
+
existingParams,
|
|
80
|
+
target,
|
|
81
|
+
propertyKey
|
|
82
|
+
);
|
|
83
|
+
break;
|
|
84
|
+
case "route:user":
|
|
85
|
+
Reflect.defineMetadata(
|
|
86
|
+
REQUEST_USER_META_KEY,
|
|
87
|
+
existingParams,
|
|
88
|
+
target,
|
|
89
|
+
propertyKey
|
|
90
|
+
);
|
|
91
|
+
break;
|
|
92
|
+
case "route:header":
|
|
93
|
+
Reflect.defineMetadata(
|
|
94
|
+
REQUEST_HEADER_META_KEY,
|
|
95
|
+
existingParams,
|
|
96
|
+
target,
|
|
97
|
+
propertyKey
|
|
98
|
+
);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export const Param = createParamDecorator("route:param");
|
|
108
|
+
export const Query = createParamDecorator("route:query");
|
|
109
|
+
export const Body = createParamDecorator("route:body");
|
|
110
|
+
export const Header = createParamDecorator("route:header");
|
|
111
|
+
export const AuthUser = createParamDecorator("route:user");
|
package/src/queue.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2024
|
|
3
|
+
* @author Tareq Hossain
|
|
4
|
+
* @email xtrinsic96@gmail.com
|
|
5
|
+
* @url https://github.com/xtareq
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fs } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { uuid } from "./helpers";
|
|
11
|
+
|
|
12
|
+
interface Job {
|
|
13
|
+
id: string;
|
|
14
|
+
data: any;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface QueueAdapter {
|
|
18
|
+
loadJobs(): Promise<Job[]>;
|
|
19
|
+
saveJobs(jobs: Job[]): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class FileQueueAdapter implements QueueAdapter {
|
|
23
|
+
private queueFile: string;
|
|
24
|
+
|
|
25
|
+
constructor(queueName: string) {
|
|
26
|
+
this.queueFile = join(__dirname, `${queueName}.json`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async loadJobs(): Promise<Job[]> {
|
|
30
|
+
try {
|
|
31
|
+
const data = await fs.readFile(this.queueFile, "utf-8");
|
|
32
|
+
return JSON.parse(data);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async saveJobs(jobs: Job[]) {
|
|
39
|
+
await fs.writeFile(this.queueFile, JSON.stringify(jobs, null, 2), "utf-8");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// class RedisQueueAdapter implements QueueAdapter {
|
|
44
|
+
// private client = createClient();
|
|
45
|
+
// private queueKey: string;
|
|
46
|
+
//
|
|
47
|
+
// constructor(queueName: string) {
|
|
48
|
+
// this.queueKey = `queue:${queueName}`;
|
|
49
|
+
// this.client.connect();
|
|
50
|
+
// }
|
|
51
|
+
//
|
|
52
|
+
// async loadJobs(): Promise<Job[]> {
|
|
53
|
+
// const jobs = await this.client.lRange(this.queueKey, 0, -1);
|
|
54
|
+
// return jobs.map((job) => JSON.parse(job));
|
|
55
|
+
// }
|
|
56
|
+
//
|
|
57
|
+
// async saveJobs(jobs: Job[]) {
|
|
58
|
+
// await this.client.del(this.queueKey);
|
|
59
|
+
// if (jobs.length > 0) {
|
|
60
|
+
// await this.client.rPush(this.queueKey, ...jobs.map(job => JSON.stringify(job)));
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
// }
|
|
64
|
+
|
|
65
|
+
class SimpleQueue {
|
|
66
|
+
private processing: boolean = false;
|
|
67
|
+
private jobHandler: (job: Job) => Promise<void>;
|
|
68
|
+
private adapter: QueueAdapter;
|
|
69
|
+
|
|
70
|
+
constructor(adapter: QueueAdapter, jobHandler: (job: Job) => Promise<void>) {
|
|
71
|
+
this.adapter = adapter;
|
|
72
|
+
this.jobHandler = jobHandler;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async addJob(data: any) {
|
|
76
|
+
const job: Job = { id: uuid, data };
|
|
77
|
+
const jobs = await this.adapter.loadJobs();
|
|
78
|
+
jobs.push(job);
|
|
79
|
+
await this.adapter.saveJobs(jobs);
|
|
80
|
+
this.processNext();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private async processNext() {
|
|
84
|
+
if (this.processing) return;
|
|
85
|
+
this.processing = true;
|
|
86
|
+
|
|
87
|
+
const jobs = await this.adapter.loadJobs();
|
|
88
|
+
if (jobs.length === 0) {
|
|
89
|
+
this.processing = false;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const job = jobs.shift();
|
|
94
|
+
if (job) {
|
|
95
|
+
try {
|
|
96
|
+
await this.jobHandler(job);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(`Error processing job ${job.id}:`, error);
|
|
99
|
+
jobs.unshift(job);
|
|
100
|
+
}
|
|
101
|
+
await this.adapter.saveJobs(jobs);
|
|
102
|
+
this.processing = false;
|
|
103
|
+
this.processNext();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class QueueManager {
|
|
109
|
+
private static instance: QueueManager;
|
|
110
|
+
private adapter: QueueAdapter;
|
|
111
|
+
|
|
112
|
+
private constructor(adapter: QueueAdapter) {
|
|
113
|
+
this.adapter = adapter;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static getInstance(adapter: QueueAdapter): QueueManager {
|
|
117
|
+
if (!QueueManager.instance) {
|
|
118
|
+
QueueManager.instance = new QueueManager(adapter);
|
|
119
|
+
}
|
|
120
|
+
return QueueManager.instance;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
createQueue(jobHandler: (job: Job) => Promise<void>): SimpleQueue {
|
|
124
|
+
return new SimpleQueue(this.adapter, jobHandler);
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/response.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @copyright 2024
|
|
3
|
+
* @author Tareq Hossain
|
|
4
|
+
* @email xtrinsic96@gmail.com
|
|
5
|
+
* @url https://github.com/xtareq
|
|
6
|
+
*/
|
|
7
|
+
import {
|
|
8
|
+
instanceToPlain,
|
|
9
|
+
plainToInstance,
|
|
10
|
+
ClassConstructor,
|
|
11
|
+
ClassTransformer,
|
|
12
|
+
} from "class-transformer";
|
|
13
|
+
|
|
14
|
+
export interface IHttpResponse<T extends any> {
|
|
15
|
+
message: string;
|
|
16
|
+
data: T | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isClassTransformerClass(target: any): boolean {
|
|
20
|
+
const prototype = target.prototype;
|
|
21
|
+
const keys = Reflect.getMetadataKeys(prototype);
|
|
22
|
+
|
|
23
|
+
// Check for class-transformer metadata
|
|
24
|
+
return keys.some((key) => key.startsWith("class_transformer:"));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isClassTransformerType<T>(target: new () => T): boolean {
|
|
28
|
+
return isClassTransformerClass(target);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class HttpResponse {
|
|
32
|
+
static Ok<T>(obj: any, s?: ClassConstructor<T>): IHttpResponse<T> {
|
|
33
|
+
if (s) {
|
|
34
|
+
const isPaginated = obj?.hasOwnProperty("total");
|
|
35
|
+
const dataToTransform = isPaginated ? obj.data : obj;
|
|
36
|
+
|
|
37
|
+
const transformedData = plainToInstance(s, dataToTransform, {
|
|
38
|
+
enableImplicitConversion: true,
|
|
39
|
+
excludeExtraneousValues: true,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const transformedResult = isPaginated
|
|
43
|
+
? { ...obj, data: instanceToPlain(transformedData) }
|
|
44
|
+
: { data: instanceToPlain(transformedData) };
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
message: "success",
|
|
48
|
+
...transformedResult,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { message: "success", data: obj };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static Created<T>(obj: any, s?: ClassConstructor<T>): IHttpResponse<T> {
|
|
56
|
+
if (s) {
|
|
57
|
+
const transformedData = plainToInstance(s, obj, {
|
|
58
|
+
enableImplicitConversion: true,
|
|
59
|
+
excludeExtraneousValues: true,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
message: "created",
|
|
64
|
+
data: instanceToPlain(transformedData) as T,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { message: "created", data: obj };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static NoContent(): IHttpResponse<null> {
|
|
72
|
+
return { message: "no content", data: null };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export class HttpExceptions {
|
|
77
|
+
static BadRequest(message: string, data: any = null): IHttpResponse<any> {
|
|
78
|
+
return { message: message, data: data };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static Unauthorized(message: string, data: any = null): IHttpResponse<any> {
|
|
82
|
+
return { message: message, data: data };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static Forbidden(message: string, data: any = null): IHttpResponse<any> {
|
|
86
|
+
return { message: message, data: data };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static NotFound(message: string, data: any = null): IHttpResponse<any> {
|
|
90
|
+
return { message: message, data: data };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
static InternalServerError(
|
|
94
|
+
message: string = "Internal server error",
|
|
95
|
+
data: any = null
|
|
96
|
+
): IHttpResponse<any> {
|
|
97
|
+
return { message: message, data: data };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static Conflict(message: string, data: any = null): IHttpResponse<any> {
|
|
101
|
+
return { message: message, data: data };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static UnprocessableEntity(
|
|
105
|
+
message: string,
|
|
106
|
+
data: any = null
|
|
107
|
+
): IHttpResponse<any> {
|
|
108
|
+
return { message: message, data: data };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static TooManyRequests(
|
|
112
|
+
message: string,
|
|
113
|
+
data: any = null
|
|
114
|
+
): IHttpResponse<any> {
|
|
115
|
+
return { message: message, data: data };
|
|
116
|
+
}
|
|
117
|
+
}
|