@avleon/core 0.0.27 → 0.0.29

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.
Files changed (77) hide show
  1. package/README.md +601 -561
  2. package/dist/application.js +1 -1
  3. package/dist/cache.d.ts +1 -1
  4. package/dist/cache.js +2 -2
  5. package/dist/collection.d.ts +25 -32
  6. package/dist/collection.js +50 -6
  7. package/dist/collection.test.d.ts +1 -0
  8. package/dist/collection.test.js +59 -0
  9. package/dist/config.d.ts +2 -0
  10. package/dist/config.js +30 -5
  11. package/dist/config.test.d.ts +1 -0
  12. package/dist/config.test.js +40 -0
  13. package/dist/controller.js +2 -2
  14. package/dist/environment-variables.js +42 -5
  15. package/dist/event-dispatcher.d.ts +23 -0
  16. package/dist/event-dispatcher.js +102 -0
  17. package/dist/event-subscriber.d.ts +15 -0
  18. package/dist/event-subscriber.js +96 -0
  19. package/dist/exceptions/http-exceptions.js +1 -1
  20. package/dist/exceptions/index.d.ts +1 -1
  21. package/dist/exceptions/system-exception.js +3 -1
  22. package/dist/file-storage.js +1 -1
  23. package/dist/helpers.js +1 -1
  24. package/dist/icore.d.ts +5 -0
  25. package/dist/icore.js +53 -18
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.js +6 -1
  28. package/dist/utils/index.d.ts +2 -2
  29. package/dist/utils/optional-require.js +2 -2
  30. package/dist/validation.d.ts +1 -1
  31. package/dist/websocket.d.ts +7 -0
  32. package/dist/websocket.js +20 -0
  33. package/dist/websocket.test.d.ts +0 -0
  34. package/dist/websocket.test.js +1 -0
  35. package/package.json +37 -6
  36. package/src/application.ts +104 -125
  37. package/src/authentication.ts +16 -16
  38. package/src/cache.ts +91 -91
  39. package/src/collection.test.ts +71 -0
  40. package/src/collection.ts +344 -254
  41. package/src/config.test.ts +35 -0
  42. package/src/config.ts +85 -42
  43. package/src/constants.ts +1 -1
  44. package/src/container.ts +54 -54
  45. package/src/controller.ts +125 -127
  46. package/src/decorators.ts +27 -27
  47. package/src/environment-variables.ts +53 -46
  48. package/src/event-dispatcher.ts +100 -0
  49. package/src/event-subscriber.ts +79 -0
  50. package/src/exceptions/http-exceptions.ts +86 -86
  51. package/src/exceptions/index.ts +1 -1
  52. package/src/exceptions/system-exception.ts +35 -34
  53. package/src/file-storage.ts +206 -206
  54. package/src/helpers.ts +324 -328
  55. package/src/icore.ts +1106 -1084
  56. package/src/index.ts +32 -30
  57. package/src/interfaces/avleon-application.ts +32 -40
  58. package/src/logger.ts +72 -72
  59. package/src/map-types.ts +159 -159
  60. package/src/middleware.ts +121 -98
  61. package/src/multipart.ts +116 -116
  62. package/src/openapi.ts +372 -372
  63. package/src/params.ts +111 -111
  64. package/src/queue.ts +126 -126
  65. package/src/response.ts +74 -74
  66. package/src/results.ts +30 -30
  67. package/src/route-methods.ts +186 -186
  68. package/src/swagger-schema.ts +213 -213
  69. package/src/testing.ts +220 -220
  70. package/src/types/app-builder.interface.ts +18 -19
  71. package/src/types/application.interface.ts +7 -9
  72. package/src/utils/hash.ts +8 -5
  73. package/src/utils/index.ts +2 -2
  74. package/src/utils/optional-require.ts +50 -50
  75. package/src/validation.ts +160 -156
  76. package/src/validator-extend.ts +25 -25
  77. package/src/websocket.ts +47 -0
@@ -0,0 +1,100 @@
1
+ import { Service, Container } from "typedi";
2
+ import { Server as SocketIOServer, Socket, ServerOptions } from "socket.io";
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+
5
+
6
+ @Service()
7
+ export class SocketContextService {
8
+ private readonly storage = new AsyncLocalStorage<{ socket: Socket }>();
9
+
10
+ run(socket: Socket, fn: () => void | Promise<void>) {
11
+ this.storage.run({ socket }, fn);
12
+ }
13
+
14
+ getSocket(): Socket | undefined {
15
+ return this.storage.getStore()?.socket;
16
+ }
17
+ }
18
+
19
+ export type DispatchOptions = {
20
+ room?: string;
21
+ broadcast?: boolean;
22
+ transports?: ("socket" | "kafka" | "rabbitmq")[];
23
+ retry?: number;
24
+ retryDelay?: number;
25
+ };
26
+
27
+ export function sleep(ms: number) {
28
+ return new Promise((resolve) => setTimeout(resolve, ms));
29
+ }
30
+
31
+ @Service()
32
+ export class EventDispatcher {
33
+ constructor(private readonly _context: SocketContextService) {}
34
+
35
+ async dispatch<T = any>(
36
+ event: string,
37
+ data: T,
38
+ options: DispatchOptions = {}
39
+ ) {
40
+ const retryCount = options.retry ?? 0;
41
+ const delay = options.retryDelay ?? 300;
42
+
43
+ for (let attempt = 0; attempt <= retryCount; attempt++) {
44
+ try {
45
+ await this.dispatchToTransports(event, data, options);
46
+ break;
47
+ } catch (err) {
48
+ if (attempt === retryCount) throw err;
49
+ await sleep(delay * (attempt + 1));
50
+ }
51
+ }
52
+ }
53
+
54
+ private async dispatchToTransports(event: string, data: any, options: DispatchOptions) {
55
+ const transports = options.transports ?? ["socket"];
56
+
57
+ for (const transport of transports) {
58
+ if (transport === "socket") {
59
+ const io = Container.get(SocketIOServer);
60
+
61
+ //console.log('SOckert', Container.get(SocketContextService));
62
+
63
+ const context = Container.get(SocketContextService);
64
+ const socket = context.getSocket();
65
+
66
+
67
+
68
+ if (options.broadcast && socket) {
69
+ if (options.room) {
70
+ socket.broadcast.to(options.room).emit(event, data);
71
+ } else {
72
+ socket.broadcast.emit(event, data);
73
+ }
74
+ } else {
75
+ if (options.room) {
76
+ io.to(options.room).emit(event, data);
77
+ } else {
78
+ io.emit(event, data);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+
87
+ export function Dispatch(event: string, options?: Omit<DispatchOptions, "transports"> & { transports?: DispatchOptions["transports"] }) {
88
+ return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
89
+ const original = descriptor.value;
90
+
91
+ descriptor.value = async function (...args: any[]) {
92
+ const result = await original.apply(this, args);
93
+
94
+ const dispatcher = Container.get(EventDispatcher);
95
+ await dispatcher.dispatch(event, result, options);
96
+
97
+ return result;
98
+ };
99
+ };
100
+ }
@@ -0,0 +1,79 @@
1
+ import { Container, Service } from "typedi";
2
+ import { Socket, Server } from "socket.io";
3
+ import { SocketContextService } from "./event-dispatcher";
4
+ import "reflect-metadata";
5
+
6
+ const PRIVATE_META_KEY = "avleon:private";
7
+
8
+ export function Private(channelResolver?: (socket: any) => string) {
9
+ return function (target: any, propertyKey: string) {
10
+ Reflect.defineMetadata(PRIVATE_META_KEY, true, target, propertyKey);
11
+ Reflect.defineMetadata(`private:channel:${propertyKey}`, channelResolver, target);
12
+ };
13
+ }
14
+
15
+ export function isPrivate(target: any, propertyKey: string): boolean {
16
+ return Reflect.getMetadata(PRIVATE_META_KEY, target, propertyKey) || false;
17
+ }
18
+
19
+ export function getPrivateChannelResolver(target: any, propertyKey: string): ((socket: any) => string) | undefined {
20
+ return Reflect.getMetadata(`private:channel:${propertyKey}`, target);
21
+ }
22
+
23
+ const socketSubscriberClasses = new Set<Function>();
24
+
25
+ export function registerSocketSubscriber(target: Function) {
26
+ socketSubscriberClasses.add(target);
27
+ }
28
+
29
+ export function getSocketSubscribers(): Function[] {
30
+ return Array.from(socketSubscriberClasses);
31
+ }
32
+
33
+
34
+ export function Subscribe(event: string): MethodDecorator {
35
+ return (target, propertyKey) => {
36
+ Reflect.defineMetadata("socket:event", event, target, propertyKey);
37
+ registerSocketSubscriber(target.constructor);
38
+ };
39
+
40
+ }
41
+
42
+
43
+ @Service()
44
+ export class EventSubscriberRegistry {
45
+ constructor(private readonly socketContext: SocketContextService) { }
46
+
47
+ register(socket: Socket) {
48
+ const subscriberClasses = getSocketSubscribers();
49
+
50
+ for (const SubscriberClass of subscriberClasses) {
51
+ const instance: any = Container.get(SubscriberClass);
52
+ const prototype = Object.getPrototypeOf(instance);
53
+
54
+ const methodNames = Object.getOwnPropertyNames(prototype)
55
+ .filter(name => typeof prototype[name] === "function");
56
+
57
+ for (const methodName of methodNames) {
58
+ const event = Reflect.getMetadata("socket:event", prototype, methodName);
59
+ const isPrivateListener = isPrivate(instance, methodName);
60
+ const channelResolver = getPrivateChannelResolver(instance, methodName);
61
+
62
+ if (event) {
63
+ const channel = isPrivateListener && channelResolver ? channelResolver(socket) : event;
64
+ console.log("Channel", channel);
65
+ socket.on(channel, (payload: any) => {
66
+ this.socketContext.run(socket, async () => {
67
+ if (isPrivateListener) {
68
+ const user = socket.data.user;
69
+ if (!user) return; // unauthorized
70
+ // optionally add more validation here
71
+ }
72
+ await instance[methodName](payload, socket.data);
73
+ });
74
+ });
75
+ }
76
+ }
77
+ }
78
+ }
79
+ }
@@ -1,86 +1,86 @@
1
- /**
2
- * @copyright 2024
3
- * @author Tareq Hossain
4
- * @email xtrinsic96@gmail.com
5
- * @url https://github.com/xtareq
6
- */
7
- export abstract class BaseHttpException extends Error {
8
- code: number = 500;
9
- name: string = "HttpException";
10
- constructor(message: any) {
11
- super(JSON.stringify(message));
12
- }
13
- isCustomException() {
14
- return true;
15
- }
16
- }
17
-
18
- export class BadRequestException extends BaseHttpException {
19
- name: string = "BadRequest";
20
- code: number = 400;
21
-
22
- constructor(message: any) {
23
- super(message);
24
- }
25
- }
26
-
27
- export class ValidationErrorException extends BadRequestException {
28
- name: string = "ValidationError";
29
- }
30
-
31
- export class InternalErrorException extends BaseHttpException {
32
- name: string = "InternalError";
33
- code: number = 500;
34
-
35
- constructor(message: any = "Something going wrong") {
36
- super(message);
37
- }
38
- }
39
-
40
- export class NotFoundException extends BaseHttpException {
41
- name: string = "NotFound";
42
- code: number = 404;
43
- constructor(message: any) {
44
- super(message);
45
- }
46
- }
47
-
48
- export class UnauthorizedException extends BaseHttpException {
49
- name: string = "Unauthorized";
50
- code: number = 401;
51
- constructor(message: any) {
52
- super(message);
53
- }
54
- }
55
-
56
- export class ForbiddenException extends BaseHttpException {
57
- name: string = "Forbidden";
58
- code: number = 403;
59
- constructor(message: any) {
60
- super(message);
61
- }
62
- }
63
-
64
- export type HttpExceptionTypes =
65
- | NotFoundException
66
- | BadRequestException
67
- | UnauthorizedException
68
- | InternalErrorException
69
- | ForbiddenException;
70
-
71
- // export type HttpExceptions = {
72
- // NotFound: (message: any) => NotFoundException,
73
- // ValidationError: (message: any) =>ValidationErrorException,
74
- // BadRequest: (message: any) => BadRequestException,
75
- // Unauthorized: (message: any) => UnauthorizedException,
76
- // Forbidden: (message: any) => ForbiddenException,
77
- // InternalError: (message: any) => InternalErrorException
78
- // }
79
- export const HttpExceptions = {
80
- notFound:(message:any="")=>new NotFoundException(message),
81
- validationError:(message:any="")=>new ValidationErrorException(message),
82
- badRequest:(message:any="")=>new BadRequestException(message),
83
- unauthorized:(message:any="")=>new UnauthorizedException(message),
84
- forbidden:(message:any="")=>new ForbiddenException(message),
85
- internalError: (message:any="")=> new InternalErrorException(message)
86
- }
1
+ /**
2
+ * @copyright 2024
3
+ * @author Tareq Hossain
4
+ * @email xtrinsic96@gmail.com
5
+ * @url https://github.com/xtareq
6
+ */
7
+ export abstract class BaseHttpException extends Error {
8
+ code: number = 500;
9
+ name: string = "HttpException";
10
+ constructor(message: any) {
11
+ super(JSON.stringify(message));
12
+ }
13
+ isCustomException() {
14
+ return true;
15
+ }
16
+ }
17
+
18
+ export class BadRequestException extends BaseHttpException {
19
+ name: string = "BadRequest";
20
+ code: number = 400;
21
+
22
+ constructor(message: any) {
23
+ super(message);
24
+ }
25
+ }
26
+
27
+ export class ValidationErrorException extends BadRequestException {
28
+ name: string = "ValidationError";
29
+ }
30
+
31
+ export class InternalErrorException extends BaseHttpException {
32
+ name: string = "InternalError";
33
+ code: number = 500;
34
+
35
+ constructor(message: any = "Something going wrong") {
36
+ super(message);
37
+ }
38
+ }
39
+
40
+ export class NotFoundException extends BaseHttpException {
41
+ name: string = "NotFound";
42
+ code: number = 404;
43
+ constructor(message: any) {
44
+ super(message);
45
+ }
46
+ }
47
+
48
+ export class UnauthorizedException extends BaseHttpException {
49
+ name: string = "Unauthorized";
50
+ code: number = 401;
51
+ constructor(message: any) {
52
+ super(message);
53
+ }
54
+ }
55
+
56
+ export class ForbiddenException extends BaseHttpException {
57
+ name: string = "Forbidden";
58
+ code: number = 403;
59
+ constructor(message: any) {
60
+ super(message);
61
+ }
62
+ }
63
+
64
+ export type HttpExceptionTypes =
65
+ | NotFoundException
66
+ | BadRequestException
67
+ | UnauthorizedException
68
+ | InternalErrorException
69
+ | ForbiddenException;
70
+
71
+ // export type HttpExceptions = {
72
+ // NotFound: (message: any) => NotFoundException,
73
+ // ValidationError: (message: any) =>ValidationErrorException,
74
+ // BadRequest: (message: any) => BadRequestException,
75
+ // Unauthorized: (message: any) => UnauthorizedException,
76
+ // Forbidden: (message: any) => ForbiddenException,
77
+ // InternalError: (message: any) => InternalErrorException
78
+ // }
79
+ export const HttpExceptions = {
80
+ notFound: (message: any = "") => new NotFoundException(message),
81
+ validationError: (message: any = "") => new ValidationErrorException(message),
82
+ badRequest: (message: any = "") => new BadRequestException(message),
83
+ unauthorized: (message: any = "") => new UnauthorizedException(message),
84
+ forbidden: (message: any = "") => new ForbiddenException(message),
85
+ internalError: (message: any = "") => new InternalErrorException(message),
86
+ };
@@ -1 +1 @@
1
- export * from './http-exceptions';
1
+ export * from "./http-exceptions";
@@ -1,34 +1,35 @@
1
- /**
2
- * @copyright 2024
3
- * @author Tareq Hossain
4
- * @email xtrinsic96@gmail.com
5
- * @url https://github.com/xtareq
6
- */
7
- export interface IRouteDuplicateErr {
8
- path: string,
9
- mpath: string,
10
- method: string,
11
- controller: string,
12
- inverseController?: string
13
- }
14
-
15
- export class SystemUseError extends Error {
16
- constructor(message: string) {
17
- super(message);
18
- }
19
- }
20
- export class DuplicateRouteException extends Error {
21
- constructor(params: IRouteDuplicateErr) {
22
- let sameController = params.controller == params.inverseController;
23
- let message = `Duplicate route found for method ${params.method.toUpperCase()}:${params.path == "" ? "'/'" : params.path} `
24
- message += sameController ? `in ${params.controller}` : `both in ${params.controller} and ${params.inverseController}`;
25
- super(message);
26
- }
27
- }
28
-
29
-
30
- export class EnvironmentVariableNotFound extends Error{
31
- constructor(key: string) {
32
- super(`${key} not found in environment variables.`)
33
- }
34
- }
1
+ /**
2
+ * @copyright 2024
3
+ * @author Tareq Hossain
4
+ * @email xtrinsic96@gmail.com
5
+ * @url https://github.com/xtareq
6
+ */
7
+ export interface IRouteDuplicateErr {
8
+ path: string;
9
+ mpath: string;
10
+ method: string;
11
+ controller: string;
12
+ inverseController?: string;
13
+ }
14
+
15
+ export class SystemUseError extends Error {
16
+ constructor(message: string) {
17
+ super(message);
18
+ }
19
+ }
20
+ export class DuplicateRouteException extends Error {
21
+ constructor(params: IRouteDuplicateErr) {
22
+ let sameController = params.controller == params.inverseController;
23
+ let message = `Duplicate route found for method ${params.method.toUpperCase()}:${params.path == "" ? "'/'" : params.path} `;
24
+ message += sameController
25
+ ? `in ${params.controller}`
26
+ : `both in ${params.controller} and ${params.inverseController}`;
27
+ super(message);
28
+ }
29
+ }
30
+
31
+ export class EnvironmentVariableNotFound extends Error {
32
+ constructor(key: string) {
33
+ super(`${key} not found in environment variables.`);
34
+ }
35
+ }