topsyde-utils 1.3.2 → 2.0.1

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 (148) hide show
  1. package/dist/index.d.ts +2 -43
  2. package/dist/index.js +1 -38
  3. package/dist/index.js.map +1 -1
  4. package/dist/utils/Lib.d.ts +0 -12
  5. package/dist/utils/Lib.js +0 -65
  6. package/dist/utils/Lib.js.map +1 -1
  7. package/dist/utils/index.d.ts +0 -3
  8. package/dist/utils/index.js +0 -3
  9. package/dist/utils/index.js.map +1 -1
  10. package/dist/websocket.shared.types.d.ts +25 -0
  11. package/dist/websocket.shared.types.js +4 -0
  12. package/dist/websocket.shared.types.js.map +1 -0
  13. package/package.json +12 -66
  14. package/src/__tests__/singleton.test.ts +0 -143
  15. package/src/index.ts +2 -83
  16. package/src/utils/Lib.ts +0 -77
  17. package/src/utils/index.ts +0 -3
  18. package/src/websocket.shared.types.ts +27 -0
  19. package/dist/application.d.ts +0 -18
  20. package/dist/application.js +0 -60
  21. package/dist/application.js.map +0 -1
  22. package/dist/client/api/base.api.d.ts +0 -63
  23. package/dist/client/api/base.api.js +0 -61
  24. package/dist/client/api/base.api.js.map +0 -1
  25. package/dist/client/api/index.d.ts +0 -2
  26. package/dist/client/api/index.js +0 -5
  27. package/dist/client/api/index.js.map +0 -1
  28. package/dist/client/rxjs/index.d.ts +0 -1
  29. package/dist/client/rxjs/index.js +0 -4
  30. package/dist/client/rxjs/index.js.map +0 -1
  31. package/dist/client/rxjs/useRxjs.d.ts +0 -17
  32. package/dist/client/rxjs/useRxjs.js +0 -87
  33. package/dist/client/rxjs/useRxjs.js.map +0 -1
  34. package/dist/client/vite/plugins/index.d.ts +0 -2
  35. package/dist/client/vite/plugins/index.js +0 -5
  36. package/dist/client/vite/plugins/index.js.map +0 -1
  37. package/dist/client/vite/plugins/topsydeUtilsVitePlugin.d.ts +0 -9
  38. package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js +0 -74
  39. package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js.map +0 -1
  40. package/dist/external/index.d.ts +0 -1
  41. package/dist/external/index.js +0 -4
  42. package/dist/external/index.js.map +0 -1
  43. package/dist/external/re-exports.d.ts +0 -16
  44. package/dist/external/re-exports.js +0 -24
  45. package/dist/external/re-exports.js.map +0 -1
  46. package/dist/server/base/base.database.d.ts +0 -10
  47. package/dist/server/base/base.database.js +0 -23
  48. package/dist/server/base/base.database.js.map +0 -1
  49. package/dist/server/base/index.d.ts +0 -2
  50. package/dist/server/base/index.js +0 -5
  51. package/dist/server/base/index.js.map +0 -1
  52. package/dist/server/bun/index.d.ts +0 -3
  53. package/dist/server/bun/index.js +0 -6
  54. package/dist/server/bun/index.js.map +0 -1
  55. package/dist/server/bun/router/controller-discovery.d.ts +0 -13
  56. package/dist/server/bun/router/controller-discovery.js +0 -83
  57. package/dist/server/bun/router/controller-discovery.js.map +0 -1
  58. package/dist/server/bun/router/index.d.ts +0 -6
  59. package/dist/server/bun/router/index.js +0 -9
  60. package/dist/server/bun/router/index.js.map +0 -1
  61. package/dist/server/bun/router/router.d.ts +0 -12
  62. package/dist/server/bun/router/router.internal.d.ts +0 -15
  63. package/dist/server/bun/router/router.internal.js +0 -51
  64. package/dist/server/bun/router/router.internal.js.map +0 -1
  65. package/dist/server/bun/router/router.js +0 -38
  66. package/dist/server/bun/router/router.js.map +0 -1
  67. package/dist/server/bun/router/routes.d.ts +0 -5
  68. package/dist/server/bun/router/routes.js +0 -2
  69. package/dist/server/bun/router/routes.js.map +0 -1
  70. package/dist/server/bun/websocket/Channel.d.ts +0 -68
  71. package/dist/server/bun/websocket/Channel.js +0 -263
  72. package/dist/server/bun/websocket/Channel.js.map +0 -1
  73. package/dist/server/bun/websocket/Client.d.ts +0 -87
  74. package/dist/server/bun/websocket/Client.js +0 -193
  75. package/dist/server/bun/websocket/Client.js.map +0 -1
  76. package/dist/server/bun/websocket/Message.d.ts +0 -10
  77. package/dist/server/bun/websocket/Message.js +0 -103
  78. package/dist/server/bun/websocket/Message.js.map +0 -1
  79. package/dist/server/bun/websocket/Websocket.d.ts +0 -171
  80. package/dist/server/bun/websocket/Websocket.js +0 -336
  81. package/dist/server/bun/websocket/Websocket.js.map +0 -1
  82. package/dist/server/bun/websocket/index.d.ts +0 -11
  83. package/dist/server/bun/websocket/index.js +0 -14
  84. package/dist/server/bun/websocket/index.js.map +0 -1
  85. package/dist/server/bun/websocket/websocket.enums.d.ts +0 -27
  86. package/dist/server/bun/websocket/websocket.enums.js +0 -31
  87. package/dist/server/bun/websocket/websocket.enums.js.map +0 -1
  88. package/dist/server/bun/websocket/websocket.guards.d.ts +0 -3
  89. package/dist/server/bun/websocket/websocket.guards.js +0 -17
  90. package/dist/server/bun/websocket/websocket.guards.js.map +0 -1
  91. package/dist/server/bun/websocket/websocket.types.d.ts +0 -235
  92. package/dist/server/bun/websocket/websocket.types.js +0 -2
  93. package/dist/server/bun/websocket/websocket.types.js.map +0 -1
  94. package/dist/server/controller.d.ts +0 -62
  95. package/dist/server/controller.js +0 -55
  96. package/dist/server/controller.js.map +0 -1
  97. package/dist/server/index.d.ts +0 -4
  98. package/dist/server/index.js +0 -7
  99. package/dist/server/index.js.map +0 -1
  100. package/dist/server/service.d.ts +0 -5
  101. package/dist/server/service.js +0 -38
  102. package/dist/server/service.js.map +0 -1
  103. package/dist/utils/BaseDto.d.ts +0 -33
  104. package/dist/utils/BaseDto.js +0 -69
  105. package/dist/utils/BaseDto.js.map +0 -1
  106. package/dist/utils/BaseEntity.d.ts +0 -31
  107. package/dist/utils/BaseEntity.js +0 -37
  108. package/dist/utils/BaseEntity.js.map +0 -1
  109. package/dist/utils/dto_validators/IsNumberOrRangeConstraint.d.ts +0 -9
  110. package/dist/utils/dto_validators/IsNumberOrRangeConstraint.js +0 -85
  111. package/dist/utils/dto_validators/IsNumberOrRangeConstraint.js.map +0 -1
  112. package/dist/utils/dto_validators/index.d.ts +0 -1
  113. package/dist/utils/dto_validators/index.js +0 -4
  114. package/dist/utils/dto_validators/index.js.map +0 -1
  115. package/src/__tests__/app.test.ts +0 -206
  116. package/src/application.ts +0 -73
  117. package/src/client/api/base.api.ts +0 -111
  118. package/src/client/api/index.ts +0 -5
  119. package/src/client/rxjs/index.ts +0 -4
  120. package/src/client/rxjs/useRxjs.ts +0 -113
  121. package/src/client/vite/plugins/index.ts +0 -5
  122. package/src/client/vite/plugins/topsydeUtilsVitePlugin.ts +0 -80
  123. package/src/external/index.ts +0 -4
  124. package/src/external/re-exports.ts +0 -54
  125. package/src/server/base/base.database.ts +0 -31
  126. package/src/server/base/index.ts +0 -5
  127. package/src/server/bun/index.ts +0 -6
  128. package/src/server/bun/router/controller-discovery.ts +0 -94
  129. package/src/server/bun/router/index.ts +0 -9
  130. package/src/server/bun/router/router.internal.ts +0 -64
  131. package/src/server/bun/router/router.ts +0 -51
  132. package/src/server/bun/router/routes.ts +0 -7
  133. package/src/server/bun/websocket/Channel.ts +0 -310
  134. package/src/server/bun/websocket/Client.ts +0 -243
  135. package/src/server/bun/websocket/ISSUES.md +0 -1175
  136. package/src/server/bun/websocket/Message.ts +0 -120
  137. package/src/server/bun/websocket/Websocket.ts +0 -402
  138. package/src/server/bun/websocket/index.ts +0 -14
  139. package/src/server/bun/websocket/websocket.enums.ts +0 -29
  140. package/src/server/bun/websocket/websocket.guards.ts +0 -22
  141. package/src/server/bun/websocket/websocket.types.ts +0 -252
  142. package/src/server/controller.ts +0 -121
  143. package/src/server/index.ts +0 -7
  144. package/src/server/service.ts +0 -36
  145. package/src/utils/BaseDto.ts +0 -77
  146. package/src/utils/BaseEntity.ts +0 -49
  147. package/src/utils/dto_validators/IsNumberOrRangeConstraint.ts +0 -32
  148. package/src/utils/dto_validators/index.ts +0 -4
@@ -1,252 +0,0 @@
1
- import { ServerWebSocket, WebSocketHandler } from "bun";
2
- import Channel from "./Channel";
3
- import Websocket from "./Websocket";
4
- import { E_ClientState } from "./websocket.enums";
5
-
6
- export type BunWebsocketMessage = string | Buffer<ArrayBufferLike>;
7
-
8
- export type WebsocketChannel<T extends I_WebsocketChannel = Channel> = Map<string, T>;
9
- export type WebsocketClients = Map<string, I_WebsocketClient>;
10
- export type WebsocketMessageOptions = {
11
- /**
12
- * Additional data to include in the message content
13
- * If an object is provided, it will be merged with the content
14
- * If a primitive value is provided, it will be added as content.data
15
- */
16
- data?: any;
17
-
18
- /**
19
- * Client information to include in the message
20
- * Will be added as content.client
21
- */
22
- client?: Partial<WebsocketEntityData> & {
23
- [key: string]: any;
24
- };
25
-
26
- /**
27
- * Channel metadata to include in the message
28
- * If true, all metadata will be included
29
- * If an array of strings, only the specified keys will be included
30
- */
31
- includeMetadata?: boolean | string[];
32
-
33
- /**
34
- * Client IDs to exclude from receiving the broadcast
35
- * Useful for sending messages to all clients except the sender
36
- */
37
- excludeClients?: string[];
38
-
39
- /**
40
- * Channel to include in the message
41
- * Defaults to the channel of the message
42
- */
43
- channel?: string;
44
-
45
- /**
46
- * Whether to include timestamp in the message
47
- * Defaults to true
48
- */
49
- includeTimestamp?: boolean;
50
-
51
- /**
52
- * Custom fields to add to the root of the message
53
- * These will be merged with the message object
54
- */
55
- customFields?: Record<string, any>;
56
-
57
- /**
58
- * Transform function to modify the final message before sending
59
- * This is applied after all other processing
60
- */
61
- transform?: (message: any) => any;
62
-
63
- /**
64
- * Priority of the message (higher numbers = higher priority)
65
- * Can be used by clients to determine processing order
66
- */
67
- priority?: number;
68
-
69
- /**
70
- * Message expiration time in milliseconds since epoch
71
- * Can be used by clients to ignore outdated messages
72
- */
73
- expiresAt?: number;
74
-
75
- /**
76
- * Metadata to include in the message
77
- * If an array of strings, only the specified keys will be included
78
- */
79
- metadata?: boolean | string[] | Record<string, string>;
80
- };
81
-
82
- export type WebsocketMessage<T extends Record<string, any> = Record<string, any>> = {
83
- /**
84
- * Message type identifier used for client-side routing
85
- */
86
- type: string;
87
- /**
88
- * Message content - can be any data structure
89
- * If a string is provided, it will be wrapped in {message: string}
90
- */
91
- content: T;
92
- /**
93
- * Channel ID
94
- */
95
- channel?: string;
96
- /**
97
- * Timestamp of the message
98
- */
99
- timestamp?: string;
100
- /**
101
- * Any additional custom fields
102
- */
103
- [key: string]: any;
104
- };
105
-
106
- /**
107
- * Message structure sent over the wire to clients.
108
- * This is the actual WebSocket payload format - transport options are NOT included.
109
- */
110
- export type WebsocketStructuredMessage<T extends Record<string, any> = Record<string, any>> = {
111
- /** Message type identifier for client-side routing */
112
- type: string;
113
-
114
- /** Message payload */
115
- content: T;
116
-
117
- /** Channel ID where message originated */
118
- channel?: string;
119
-
120
- /** ISO timestamp when message was created */
121
- timestamp?: string;
122
-
123
- /** Client information (who sent this) */
124
- client?: WebsocketEntityData;
125
-
126
- /** Channel metadata (if included) */
127
- metadata?: Record<string, string>;
128
-
129
- /** Message priority for client-side processing */
130
- priority?: number;
131
-
132
- /** Expiration timestamp (milliseconds since epoch) */
133
- expiresAt?: number;
134
-
135
- /** Any additional custom fields */
136
- [key: string]: any;
137
- };
138
-
139
- /**
140
- * @deprecated This type incorrectly mixed transport options with wire format.
141
- * Use WebsocketStructuredMessage for wire format and WebsocketMessageOptions for options.
142
- * This will be removed in a future version.
143
- */
144
- export type deprecated_WebsocketStructuredMessage<T extends Record<string, any> = Record<string, any>> = WebsocketMessage<T> & WebsocketMessageOptions;
145
-
146
- export type WebsocketEntityId = string;
147
- export type WebsocketEntityName = string;
148
- export type WebsocketEntityData = { id: WebsocketEntityId; name: WebsocketEntityName };
149
-
150
- export interface I_WebsocketEntity extends WebsocketEntityData {
151
- ws: ServerWebSocket<WebsocketEntityData>;
152
- }
153
-
154
- export interface I_WebsocketClient extends I_WebsocketEntity {
155
- channels: WebsocketChannel<I_WebsocketChannel>;
156
- state: E_ClientState;
157
- send(message: string, options?: WebsocketMessageOptions): void;
158
- send(message: WebsocketStructuredMessage): void;
159
- subscribe(channel: string): any;
160
- trackChannel(channel: I_WebsocketChannel): void;
161
- untrackChannel(channel: I_WebsocketChannel): void;
162
- joinChannel(channel: I_WebsocketChannel, send?: boolean): { success: boolean; reason: string };
163
- leaveChannel(channel: I_WebsocketChannel, send?: boolean): void;
164
- joinChannels(channels: I_WebsocketChannel[], send?: boolean): void;
165
- leaveChannels(channels?: I_WebsocketChannel[], send?: boolean): void;
166
- unsubscribe(channel: string): any;
167
- whoami(): WebsocketEntityData;
168
- canReceiveMessages(): boolean;
169
- markConnected(): void;
170
- markDisconnecting(): void;
171
- markDisconnected(): void;
172
- getConnectionInfo(): { id: string; name: string; state: E_ClientState; connectedAt?: Date; disconnectedAt?: Date; uptime: number; channelCount: number };
173
- }
174
-
175
- export interface I_WebsocketChannelEntity<T extends Websocket = Websocket> extends WebsocketEntityData {
176
- ws: T;
177
- }
178
-
179
- // New types for the broadcast method
180
- export type BroadcastOptions = WebsocketMessageOptions & {
181
- debug?: boolean;
182
- };
183
-
184
- // Result type for addMember operations
185
- export type AddMemberResult = { success: true; client: I_WebsocketClient } | { success: false; reason: "full" | "already_member" | "error"; error?: Error };
186
-
187
- // Options for addMember operations
188
- export type AddMemberOptions = {
189
- /** Whether to notify client when channel is full (default: false) */
190
- notify_when_full?: boolean;
191
- /** Whether to send welcome notification to client on successful join (default: false) */
192
- notify?: boolean;
193
- };
194
-
195
- // Options for removeMember operations
196
- export type RemoveMemberOptions = {
197
- /** Whether to send goodbye notification to client on successful leave (default: false) */
198
- notify?: boolean;
199
- };
200
-
201
- export interface I_WebsocketChannel<T extends Websocket = Websocket> extends I_WebsocketChannelEntity<T> {
202
- limit: number;
203
- members: Map<string, I_WebsocketClient>;
204
- metadata: Record<string, string>;
205
- createdAt: Date;
206
- broadcast(message: WebsocketStructuredMessage | string, options?: BroadcastOptions): void;
207
- hasMember(client: I_WebsocketEntity | string): boolean;
208
- addMember(entity: I_WebsocketClient, options?: AddMemberOptions): AddMemberResult;
209
- removeFromMembersMap(entity: I_WebsocketClient): void;
210
- removeMember(entity: I_WebsocketEntity, options?: RemoveMemberOptions): I_WebsocketClient | false;
211
- getMember(client: I_WebsocketEntity | string): I_WebsocketClient | undefined;
212
- getMembers(clients?: string[] | I_WebsocketEntity[]): I_WebsocketClient[];
213
- getMetadata(): Record<string, string>;
214
- getCreatedAt(): Date;
215
- getId(): string;
216
- getSize(): number;
217
- getLimit(): number;
218
- getName(): string;
219
- canAddMember(): boolean;
220
- }
221
-
222
- /**
223
- * Interface for implementing custom WebSocket behavior.
224
- *
225
- * @interface I_WebsocketInterface
226
- *
227
- * @property {Function} setup - Initializes the WebSocket handler with channels and clients
228
- *
229
- * The interface supports three optional handler methods:
230
- *
231
- * - `message`: Custom message handler that replaces the default handler
232
- * - `open`: Connection handler that runs after the default open handler
233
- * - `close`: Disconnection handler that runs before the default close handler
234
- */
235
- export type WebsocketInterfaceHandlers = Partial<WebSocketHandler<WebsocketEntityData>>;
236
-
237
- /**
238
- * Interface for implementing custom WebSocket behavior.
239
- *
240
- * @interface I_WebsocketInterface
241
- *
242
- * @property {Function} setup - Initializes the WebSocket handler with channels and clients
243
- *
244
- * The interface supports three optional handler methods:
245
- *
246
- * - `message`: Custom message handler that replaces the default handler
247
- * - `open`: Connection handler that runs after the default open handler
248
- * - `close`: Disconnection handler that runs before the default close handler
249
- */
250
- export interface I_WebsocketInterface {
251
- handlers: (channels: WebsocketChannel, clients: WebsocketClients) => WebsocketInterfaceHandlers;
252
- }
@@ -1,121 +0,0 @@
1
- import { ERROR_CODE } from "../errors";
2
- import Initializable, { InitializableOptions } from "../initializable";
3
- import { I_ApplicationResponse } from "../types";
4
- import { Guards } from "../utils";
5
-
6
- export type ControllerResponse<T = unknown> = Promise<I_ApplicationResponse<T> | PromiseLike<I_ApplicationResponse<T>>>;
7
- export type ControllerAction<T> = (req: Request) => ControllerResponse<T>;
8
-
9
- export type ControllerMap<T = unknown> = Map<string, ControllerAction<T>>;
10
-
11
- export type ControllerOptions<T = unknown> = InitializableOptions & {
12
- path: string | undefined;
13
- post: Map<string, ControllerAction<T>>;
14
- get: ControllerMap<T>;
15
- controllerMap: Map<string, ControllerMap<T>>;
16
- };
17
-
18
- export default abstract class Controller extends Initializable {
19
- path: string | undefined;
20
- post: Map<string, ControllerAction<any>> = new Map();
21
- get: ControllerMap = new Map();
22
- controllerMap: Map<string, ControllerMap> = new Map();
23
-
24
- constructor() {
25
- super();
26
- this.POST();
27
- this.GET();
28
- this.setControllerMap();
29
- }
30
-
31
- public static Create<T>(this: new () => T): T {
32
- return new this();
33
- }
34
-
35
- public static async AsyncCreate<T>(this: new () => T): Promise<T> {
36
- return new this();
37
- }
38
-
39
- public async call<T>(request: Request): Promise<T> {
40
- await this.isInitialized();
41
- const action = this.resolve(request);
42
- return await action(request);
43
- }
44
-
45
- public success<T>(data: T, after_action?: () => void | Promise<void>): I_ApplicationResponse<T> {
46
- return { status: true, data, after_action };
47
- }
48
-
49
- public failure<T>(data: T, after_action?: () => void | Promise<void>): I_ApplicationResponse<T> {
50
- return { status: false, data, after_action };
51
- }
52
-
53
- private setControllerMap() {
54
- this.controllerMap.set("GET", this.get);
55
- this.controllerMap.set("POST", this.post);
56
- }
57
-
58
- private resolve(req: Request): Function {
59
- const url = new URL(req.url);
60
- const endpointArray = url.pathname.split("/");
61
- const actionName = endpointArray.slice(2).join("/");
62
-
63
- if (!Guards.IsString(actionName, true)) throw ERROR_CODE.INVALID_ACTION;
64
-
65
- const methodMap = this.controllerMap.get(req.method);
66
-
67
- if (Guards.IsNil(methodMap)) {
68
- throw ERROR_CODE.INVALID_METHOD;
69
- }
70
-
71
- return this.resolveAction(methodMap, actionName);
72
- }
73
-
74
- private resolveAction(methodMap: ControllerMap, actionName: string) {
75
- const action = methodMap.get(actionName) as Function;
76
-
77
- if (!Guards.IsFunction(action, true)) {
78
- throw ERROR_CODE.NO_ACTION_IN_MAP;
79
- }
80
-
81
- return action;
82
- }
83
-
84
- /**
85
- * Abstract method that needs to be implemented in derived classes.
86
- * This method is used to map post actions to their corresponding handler methods.
87
- *
88
- * Each action is a string that represents a specific operation, and the handler is a function that performs this operation.
89
- * The handler is bound to the current context (`this`) to ensure it has access to the class's properties and methods.
90
- *
91
- * Example implementation:
92
- *
93
- * ```typescript
94
- * setActionsMap() {
95
- * this.post.set("action", this.controllerMethod.bind(this));
96
- * }
97
- * ```
98
- *
99
- * In this example, when "https://webserver_url:port/controller/action" is called by the router, the `controllerMethod` method will be called.
100
- */
101
- abstract POST(): void;
102
-
103
- /**
104
- * Abstract method that needs to be implemented in derived classes.
105
- * This method is used to map actions to their corresponding handler methods.
106
- *
107
- * Each action is a string that represents a specific operation, and the handler is a function that performs this operation.
108
- * The handler is bound to the current context (`this`) to ensure it has access to the class's properties and methods.
109
- *
110
- * Example implementation:
111
- *
112
- * ```typescript
113
- * setActionsMap() {
114
- * this.get.set("action", this.controllerMethod.bind(this));
115
- * }
116
- * ```
117
- *
118
- * In this example, when "https://webserver_url:port/controller/action" is called by the router, the `controllerMethod` method will be called.
119
- */
120
- abstract GET(): void;
121
- }
@@ -1,7 +0,0 @@
1
- // This file is auto-generated by scripts/generate-indexes.ts
2
- // Do not edit this file directly
3
-
4
- export * from './controller';
5
- export * from './service';
6
- export { default as Controller } from './controller';
7
- export { default as Service } from './service';
@@ -1,36 +0,0 @@
1
- import { ERROR_CODE } from "../errors";
2
- import Initializable from "../initializable";
3
- import { Guards } from "../utils";
4
-
5
- export default abstract class Service extends Initializable {
6
- async validateRequestBody<T>(request: Request, keys: (keyof T)[]): Promise<T> {
7
- return await Service.validateRequestBody(request, keys);
8
- }
9
-
10
- static async validateRequestBody<T>(request: Request, keys: (keyof T)[]): Promise<T> {
11
- try {
12
- let body;
13
- try {
14
- body = await request.json();
15
- } catch (e) {
16
- body = await request.text();
17
- const cleanedBody = body
18
- .replace(/\/\/.*$/gm, "") // Remove comments
19
- .replace(/\n/g, "") // Remove newlines
20
- .replace(/\s+/g, " ") // Replace multiple spaces with single space
21
- .trim(); // Remove leading/trailing whitespace
22
-
23
- body = JSON.parse(cleanedBody);
24
- }
25
- if (Guards.IsNil(body)) throw ERROR_CODE.REQ_BODY_EMPTY;
26
- if (!Guards.IsType<T>(body, keys)) {
27
- console.error(`Expected keys: ${keys.join(", ")}`);
28
- throw ERROR_CODE.INVALID_METHOD_INPUT;
29
- }
30
- return body;
31
- } catch (e) {
32
- if (e instanceof Error || e instanceof SyntaxError) throw "Could not parse request body: " + e;
33
- throw e;
34
- }
35
- }
36
- }
@@ -1,77 +0,0 @@
1
- import type { ClassConstructor, ClassTransformOptions } from "class-transformer";
2
- import { instanceToPlain, plainToInstance } from "class-transformer";
3
- import Guards from "./Guards";
4
- import { ValidationError, validateSync } from "class-validator";
5
-
6
- /**
7
- * Base typesafe class for Data Transfer Objects (DTOs)
8
- */
9
- export abstract class Dto {
10
- /**
11
- * Default options for class transformation
12
- */
13
- protected static readonly defaultTransformOptions: ClassTransformOptions = {
14
- excludeExtraneousValues: true,
15
- enableCircularCheck: true,
16
- exposeDefaultValues: true,
17
- enableImplicitConversion: false, // Safer default, especially when using class-validator
18
- };
19
-
20
- /**
21
- * Validates the DTO instance
22
- * @throws ValidationError[] if validation fails
23
- */
24
- public validate(): ValidationError[] {
25
- const errors = validateSync(this, {
26
- validationError: { target: false },
27
- forbidUnknownValues: true,
28
- });
29
- if (errors.length > 0) {
30
- throw errors;
31
- }
32
- return errors;
33
- }
34
-
35
- /**
36
- * Converts the DTO to a plain object
37
- * @param options - Class transformer options for controlling exposure and transformation
38
- * @returns Plain object representation of the DTO
39
- */
40
- public toJSON<T = Record<string, unknown>>(include_undefined: boolean = true, options?: ClassTransformOptions): T {
41
- const value = instanceToPlain(this, {
42
- ...Dto.defaultTransformOptions,
43
- ...options,
44
- }) as T;
45
-
46
- if (!include_undefined) {
47
- return Object.fromEntries(Object.entries(value as Record<string, unknown>).filter(([_, v]) => !Guards.IsNil(v))) as T;
48
- }
49
-
50
- return value;
51
- }
52
-
53
- /**
54
- * Creates a new instance of the DTO with validation (infers class from `this`)
55
- * @param data - Data to create the DTO from
56
- * @param options - Class transformer options
57
- * @returns New instance of the DTO
58
- */
59
- public static Create<T extends Dto>(this: ClassConstructor<T>, data: Record<string, unknown>, options: ClassTransformOptions = {}): T {
60
- const instance = plainToInstance(this, data, {
61
- ...Dto.defaultTransformOptions,
62
- ...options,
63
- });
64
-
65
- return instance;
66
- }
67
-
68
- /**
69
- * Creates an array of DTOs from an array of plain objects
70
- */
71
- public static CreateMany<T extends Dto>(this: ClassConstructor<T>, dataArray: Record<string, unknown>[], options: ClassTransformOptions = {}): T[] {
72
- return plainToInstance(this, dataArray, {
73
- ...Dto.defaultTransformOptions,
74
- ...options,
75
- }) as T[];
76
- }
77
- }
@@ -1,49 +0,0 @@
1
- import { ClassConstructor, instanceToPlain, plainToInstance } from "class-transformer";
2
- import { Dto } from "./BaseDto";
3
-
4
- export default abstract class BaseEntity {
5
- /**
6
- * Converts entity to plain object
7
- */
8
- public toJSON<T = Record<string, unknown>>(): T {
9
- return instanceToPlain(this) as T;
10
- }
11
-
12
- /**
13
- * Abstract method - entities must define how to convert to DTO
14
- */
15
- public abstract toDto(): Dto;
16
-
17
- /**
18
- * Updates entity with partial data (immutable - returns new instance)
19
- * @param data - Partial data to update
20
- * @param validate - Whether to validate after update (default: true)
21
- */
22
- public update<T extends BaseEntity>(this: T, data: Partial<T>): T {
23
- const updated = Object.assign(Object.create(Object.getPrototypeOf(this)), this, data);
24
- return updated;
25
- }
26
-
27
- /**
28
- * Creates a new entity instance from DTO (infers class from `this`)
29
- *
30
- * NOTE: This is a BASE implementation that uses plainToInstance.
31
- * Derived classes should override if they need custom construction logic.
32
- *
33
- * @param dto - DTO to create entity from
34
- */
35
- public static FromDto<T extends BaseEntity>(this: ClassConstructor<T>, dto: Dto): T {
36
- const instance = plainToInstance(this, dto.toJSON());
37
- return instance;
38
- }
39
-
40
- /**
41
- * Creates multiple entities from DTOs
42
- */
43
- public static FromDtos<T extends BaseEntity>(this: ClassConstructor<T>, dtos: Dto[]): T[] {
44
- return plainToInstance(
45
- this,
46
- dtos.map((dto) => dto.toJSON()),
47
- );
48
- }
49
- }
@@ -1,32 +0,0 @@
1
- import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from "class-validator";
2
-
3
- /**
4
- * Custom validator for number | number[] type
5
- * Validates that value is either a single number or a 2-element number array [min, max]
6
- */
7
-
8
- @ValidatorConstraint({ name: "isNumberOrRange", async: false })
9
- export class IsNumberOrRangeConstraint implements ValidatorConstraintInterface {
10
- validate(value: any, args: ValidationArguments) {
11
- // Allow single number
12
- if (typeof value === "number" && !isNaN(value)) {
13
- return true;
14
- }
15
-
16
- // Allow array of exactly 2 numbers (range)
17
- if (Array.isArray(value)) {
18
- if (value.length !== 2) return false;
19
- if (typeof value[0] !== "number" || isNaN(value[0])) return false;
20
- if (typeof value[1] !== "number" || isNaN(value[1])) return false;
21
- // Optional: Validate min <= max
22
- if (value[0] > value[1]) return false;
23
- return true;
24
- }
25
-
26
- return false;
27
- }
28
-
29
- defaultMessage(args: ValidationArguments) {
30
- return "Value must be a number or a 2-element number array [min, max]";
31
- }
32
- }
@@ -1,4 +0,0 @@
1
- // This file is auto-generated by scripts/generate-indexes.ts
2
- // Do not edit this file directly
3
-
4
- export * from './IsNumberOrRangeConstraint';