@onebun/core 0.1.1 → 0.1.3

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 (81) hide show
  1. package/README.md +233 -0
  2. package/package.json +1 -1
  3. package/src/{application.test.ts → application/application.test.ts} +125 -5
  4. package/src/{application.ts → application/application.ts} +239 -13
  5. package/src/application/index.ts +9 -0
  6. package/src/{multi-service-application.test.ts → application/multi-service-application.test.ts} +2 -1
  7. package/src/{multi-service-application.ts → application/multi-service-application.ts} +2 -1
  8. package/src/{multi-service.types.ts → application/multi-service.types.ts} +1 -1
  9. package/src/{decorators.test.ts → decorators/decorators.test.ts} +2 -1
  10. package/src/{decorators.ts → decorators/decorators.ts} +3 -2
  11. package/src/decorators/index.ts +15 -0
  12. package/src/docs-examples.test.ts +753 -0
  13. package/src/index.ts +50 -41
  14. package/src/module/index.ts +12 -0
  15. package/src/{module.test.ts → module/module.test.ts} +3 -2
  16. package/src/{module.ts → module/module.ts} +15 -8
  17. package/src/queue/adapters/index.ts +8 -0
  18. package/src/queue/adapters/memory.adapter.test.ts +405 -0
  19. package/src/queue/adapters/memory.adapter.ts +509 -0
  20. package/src/queue/adapters/redis.adapter.ts +673 -0
  21. package/src/queue/cron-expression.test.ts +145 -0
  22. package/src/queue/cron-expression.ts +115 -0
  23. package/src/queue/cron-parser.test.ts +185 -0
  24. package/src/queue/cron-parser.ts +287 -0
  25. package/src/queue/decorators.test.ts +292 -0
  26. package/src/queue/decorators.ts +493 -0
  27. package/src/queue/docs-examples.test.ts +449 -0
  28. package/src/queue/guards.test.ts +309 -0
  29. package/src/queue/guards.ts +307 -0
  30. package/src/queue/index.ts +118 -0
  31. package/src/queue/pattern-matcher.test.ts +191 -0
  32. package/src/queue/pattern-matcher.ts +252 -0
  33. package/src/queue/queue.service.ts +421 -0
  34. package/src/queue/scheduler.test.ts +235 -0
  35. package/src/queue/scheduler.ts +379 -0
  36. package/src/queue/types.ts +502 -0
  37. package/src/redis/index.ts +8 -0
  38. package/src/redis/redis-client.ts +502 -0
  39. package/src/redis/shared-redis.ts +231 -0
  40. package/src/{env-resolver.ts → service-client/env-resolver.ts} +1 -1
  41. package/src/service-client/index.ts +10 -0
  42. package/src/{service-client.test.ts → service-client/service-client.test.ts} +3 -2
  43. package/src/{service-client.ts → service-client/service-client.ts} +1 -1
  44. package/src/{service-definition.test.ts → service-client/service-definition.test.ts} +3 -2
  45. package/src/{service-definition.ts → service-client/service-definition.ts} +2 -2
  46. package/src/testing/index.ts +7 -0
  47. package/src/types.ts +84 -5
  48. package/src/websocket/index.ts +50 -0
  49. package/src/websocket/ws-base-gateway.test.ts +479 -0
  50. package/src/websocket/ws-base-gateway.ts +514 -0
  51. package/src/websocket/ws-client.test.ts +511 -0
  52. package/src/websocket/ws-client.ts +628 -0
  53. package/src/websocket/ws-client.types.ts +129 -0
  54. package/src/websocket/ws-decorators.test.ts +331 -0
  55. package/src/websocket/ws-decorators.ts +418 -0
  56. package/src/websocket/ws-guards.test.ts +334 -0
  57. package/src/websocket/ws-guards.ts +298 -0
  58. package/src/websocket/ws-handler.ts +658 -0
  59. package/src/websocket/ws-integration.test.ts +518 -0
  60. package/src/websocket/ws-pattern-matcher.test.ts +152 -0
  61. package/src/websocket/ws-pattern-matcher.ts +240 -0
  62. package/src/websocket/ws-service-definition.ts +224 -0
  63. package/src/websocket/ws-socketio-protocol.test.ts +344 -0
  64. package/src/websocket/ws-socketio-protocol.ts +567 -0
  65. package/src/websocket/ws-storage-memory.test.ts +246 -0
  66. package/src/websocket/ws-storage-memory.ts +222 -0
  67. package/src/websocket/ws-storage-redis.ts +302 -0
  68. package/src/websocket/ws-storage.ts +210 -0
  69. package/src/websocket/ws.types.ts +342 -0
  70. /package/src/{metadata.test.ts → decorators/metadata.test.ts} +0 -0
  71. /package/src/{metadata.ts → decorators/metadata.ts} +0 -0
  72. /package/src/{config.service.test.ts → module/config.service.test.ts} +0 -0
  73. /package/src/{config.service.ts → module/config.service.ts} +0 -0
  74. /package/src/{controller.test.ts → module/controller.test.ts} +0 -0
  75. /package/src/{controller.ts → module/controller.ts} +0 -0
  76. /package/src/{service.test.ts → module/service.test.ts} +0 -0
  77. /package/src/{service.ts → module/service.ts} +0 -0
  78. /package/src/{env-resolver.test.ts → service-client/env-resolver.test.ts} +0 -0
  79. /package/src/{service-client.types.ts → service-client/service-client.types.ts} +0 -0
  80. /package/src/{test-utils.test.ts → testing/test-utils.test.ts} +0 -0
  81. /package/src/{test-utils.ts → testing/test-utils.ts} +0 -0
@@ -0,0 +1,418 @@
1
+ /**
2
+ * WebSocket Decorators
3
+ *
4
+ * Decorators for creating WebSocket gateways with event handlers.
5
+ */
6
+
7
+ /* eslint-disable @typescript-eslint/naming-convention */
8
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
9
+
10
+ import type {
11
+ WebSocketGatewayOptions,
12
+ GatewayMetadata,
13
+ WsHandlerMetadata,
14
+ WsParamMetadata,
15
+ WsGuard,
16
+ } from './ws.types';
17
+
18
+ import { Reflect } from '../decorators/metadata';
19
+
20
+ import { WsHandlerType, WsParamType } from './ws.types';
21
+
22
+ // ============================================================================
23
+ // Metadata Keys
24
+ // ============================================================================
25
+
26
+ const WS_GATEWAY_METADATA = 'onebun:ws:gateway';
27
+ const WS_PARAMS_METADATA = 'onebun:ws:params';
28
+ const WS_GUARDS_METADATA = 'onebun:ws:guards';
29
+
30
+ // ============================================================================
31
+ // Metadata Storage
32
+ // ============================================================================
33
+
34
+ const META_GATEWAYS = new Map<Function, GatewayMetadata>();
35
+
36
+ // ============================================================================
37
+ // Class Decorators
38
+ // ============================================================================
39
+
40
+ /**
41
+ * Decorator to mark a class as a WebSocket Gateway
42
+ *
43
+ * @param options - Gateway options (path, namespace)
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * @WebSocketGateway({ path: '/ws', namespace: 'chat' })
48
+ * export class ChatGateway extends BaseWebSocketGateway {
49
+ * // ...
50
+ * }
51
+ * ```
52
+ */
53
+ export function WebSocketGateway(options?: WebSocketGatewayOptions) {
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ return <T extends new (...args: any[]) => any>(target: T): T => {
56
+ const metadata: GatewayMetadata = {
57
+ path: options?.path || '/',
58
+ namespace: options?.namespace,
59
+ handlers: [],
60
+ };
61
+
62
+ // Get existing handlers metadata
63
+ const existingMetadata = META_GATEWAYS.get(target);
64
+ if (existingMetadata) {
65
+ metadata.handlers = existingMetadata.handlers;
66
+ }
67
+
68
+ META_GATEWAYS.set(target, metadata);
69
+ Reflect.defineMetadata(WS_GATEWAY_METADATA, metadata, target);
70
+
71
+ return target;
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Get gateway metadata for a class
77
+ */
78
+ export function getGatewayMetadata(target: Function): GatewayMetadata | undefined {
79
+ return META_GATEWAYS.get(target) || Reflect.getMetadata(WS_GATEWAY_METADATA, target);
80
+ }
81
+
82
+ /**
83
+ * Check if a class is a WebSocket Gateway
84
+ */
85
+ export function isWebSocketGateway(target: Function): boolean {
86
+ return META_GATEWAYS.has(target) || Reflect.getMetadata(WS_GATEWAY_METADATA, target) !== undefined;
87
+ }
88
+
89
+ // ============================================================================
90
+ // Method Decorators (Event Handlers)
91
+ // ============================================================================
92
+
93
+ /**
94
+ * Create a WebSocket handler decorator
95
+ */
96
+ function createWsHandlerDecorator(type: WsHandlerType, pattern?: string) {
97
+ return (target: object, propertyKey: string, descriptor: PropertyDescriptor) => {
98
+ const gatewayClass = target.constructor as Function;
99
+
100
+ // Get existing metadata or create new
101
+ let metadata = META_GATEWAYS.get(gatewayClass);
102
+ if (!metadata) {
103
+ metadata = { path: '/', handlers: [] };
104
+ }
105
+
106
+ // Get parameter metadata
107
+ const params: WsParamMetadata[] =
108
+ Reflect.getMetadata(WS_PARAMS_METADATA, target, propertyKey) || [];
109
+
110
+ // Get guards metadata
111
+ const guards: Function[] =
112
+ Reflect.getMetadata(WS_GUARDS_METADATA, target, propertyKey) || [];
113
+
114
+ // Create handler metadata
115
+ const handlerMetadata: WsHandlerMetadata = {
116
+ type,
117
+ pattern,
118
+ handler: propertyKey,
119
+ params,
120
+ guards,
121
+ };
122
+
123
+ metadata.handlers.push(handlerMetadata);
124
+ META_GATEWAYS.set(gatewayClass, metadata);
125
+
126
+ return descriptor;
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Decorator for handling client connection events
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * @OnConnect()
136
+ * handleConnect(@Client() client: WsClientData) {
137
+ * console.log('Client connected:', client.id);
138
+ * return { event: 'welcome', data: { message: 'Welcome!' } };
139
+ * }
140
+ * ```
141
+ */
142
+ export function OnConnect() {
143
+ return createWsHandlerDecorator(WsHandlerType.CONNECT);
144
+ }
145
+
146
+ /**
147
+ * Decorator for handling client disconnection events
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * @OnDisconnect()
152
+ * handleDisconnect(@Client() client: WsClientData) {
153
+ * console.log('Client disconnected:', client.id);
154
+ * }
155
+ * ```
156
+ */
157
+ export function OnDisconnect() {
158
+ return createWsHandlerDecorator(WsHandlerType.DISCONNECT);
159
+ }
160
+
161
+ /**
162
+ * Decorator for handling room join events
163
+ *
164
+ * @param pattern - Optional pattern for room name matching
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * @OnJoinRoom('room:{roomId}')
169
+ * handleJoinRoom(@Client() client: WsClientData, @RoomName() room: string) {
170
+ * console.log(`Client ${client.id} joined room ${room}`);
171
+ * }
172
+ * ```
173
+ */
174
+ export function OnJoinRoom(pattern?: string) {
175
+ return createWsHandlerDecorator(WsHandlerType.JOIN_ROOM, pattern);
176
+ }
177
+
178
+ /**
179
+ * Decorator for handling room leave events
180
+ *
181
+ * @param pattern - Optional pattern for room name matching
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * @OnLeaveRoom('room:*')
186
+ * handleLeaveRoom(@Client() client: WsClientData, @RoomName() room: string) {
187
+ * console.log(`Client ${client.id} left room ${room}`);
188
+ * }
189
+ * ```
190
+ */
191
+ export function OnLeaveRoom(pattern?: string) {
192
+ return createWsHandlerDecorator(WsHandlerType.LEAVE_ROOM, pattern);
193
+ }
194
+
195
+ /**
196
+ * Decorator for handling incoming messages
197
+ *
198
+ * @param pattern - Event pattern to match (supports wildcards and parameters)
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * @OnMessage('chat:message')
203
+ * handleMessage(@Client() client: WsClientData, @MessageData() data: any) {
204
+ * this.broadcast('chat:message', { userId: client.id, ...data });
205
+ * }
206
+ *
207
+ * @OnMessage('chat:{roomId}:message')
208
+ * handleRoomMessage(
209
+ * @Client() client: WsClientData,
210
+ * @MessageData() data: any,
211
+ * @PatternParams() params: { roomId: string }
212
+ * ) {
213
+ * this.emitToRoom(`room:${params.roomId}`, 'chat:message', data);
214
+ * }
215
+ * ```
216
+ */
217
+ export function OnMessage(pattern: string) {
218
+ return createWsHandlerDecorator(WsHandlerType.MESSAGE, pattern);
219
+ }
220
+
221
+ // ============================================================================
222
+ // Parameter Decorators
223
+ // ============================================================================
224
+
225
+ /**
226
+ * Create a WebSocket parameter decorator
227
+ */
228
+ function createWsParamDecorator(type: WsParamType, property?: string) {
229
+ return (target: object, propertyKey: string, parameterIndex: number) => {
230
+ const params: WsParamMetadata[] =
231
+ Reflect.getMetadata(WS_PARAMS_METADATA, target, propertyKey) || [];
232
+
233
+ params.push({
234
+ type,
235
+ property,
236
+ index: parameterIndex,
237
+ });
238
+
239
+ Reflect.defineMetadata(WS_PARAMS_METADATA, params, target, propertyKey);
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Decorator to inject client data into handler
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * @OnMessage('chat:message')
249
+ * handleMessage(@Client() client: WsClientData) {
250
+ * console.log('Message from:', client.id);
251
+ * }
252
+ * ```
253
+ */
254
+ export function Client() {
255
+ return createWsParamDecorator(WsParamType.CLIENT);
256
+ }
257
+
258
+ /**
259
+ * Decorator to inject raw WebSocket into handler
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * @OnMessage('ping')
264
+ * handlePing(@Socket() socket: ServerWebSocket) {
265
+ * socket.send(JSON.stringify({ event: 'pong', data: {} }));
266
+ * }
267
+ * ```
268
+ */
269
+ export function Socket() {
270
+ return createWsParamDecorator(WsParamType.SOCKET);
271
+ }
272
+
273
+ /**
274
+ * Decorator to inject message data into handler
275
+ *
276
+ * @param property - Optional property path to extract from message data
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * @OnMessage('chat:message')
281
+ * handleMessage(@MessageData() data: { text: string }) {
282
+ * console.log('Message:', data.text);
283
+ * }
284
+ *
285
+ * @OnMessage('chat:message')
286
+ * handleMessage(@MessageData('text') text: string) {
287
+ * console.log('Message text:', text);
288
+ * }
289
+ * ```
290
+ */
291
+ export function MessageData(property?: string) {
292
+ return createWsParamDecorator(WsParamType.MESSAGE_DATA, property);
293
+ }
294
+
295
+ /**
296
+ * Decorator to inject room name into handler
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * @OnJoinRoom('room:{roomId}')
301
+ * handleJoinRoom(@RoomName() room: string) {
302
+ * console.log('Joining room:', room);
303
+ * }
304
+ * ```
305
+ */
306
+ export function RoomName() {
307
+ return createWsParamDecorator(WsParamType.ROOM_NAME);
308
+ }
309
+
310
+ /**
311
+ * Decorator to inject pattern parameters into handler
312
+ *
313
+ * @example
314
+ * ```typescript
315
+ * @OnMessage('chat:{roomId}:message')
316
+ * handleMessage(@PatternParams() params: { roomId: string }) {
317
+ * console.log('Room ID:', params.roomId);
318
+ * }
319
+ * ```
320
+ */
321
+ export function PatternParams() {
322
+ return createWsParamDecorator(WsParamType.PATTERN_PARAMS);
323
+ }
324
+
325
+ /**
326
+ * Decorator to inject WebSocket server into handler or property
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * @WebSocketGateway()
331
+ * export class ChatGateway extends BaseWebSocketGateway {
332
+ * @WsServer()
333
+ * server: WsServer;
334
+ *
335
+ * // Or as parameter
336
+ * @OnMessage('broadcast')
337
+ * handleBroadcast(@WsServer() server: WsServer) {
338
+ * server.publish('all', 'Hello everyone!');
339
+ * }
340
+ * }
341
+ * ```
342
+ */
343
+ export function WsServer() {
344
+ return createWsParamDecorator(WsParamType.SERVER);
345
+ }
346
+
347
+ // ============================================================================
348
+ // Guards Decorator
349
+ // ============================================================================
350
+
351
+ /**
352
+ * Decorator to apply guards to a WebSocket handler
353
+ *
354
+ * @param guards - Guard classes or instances to apply
355
+ *
356
+ * @example
357
+ * ```typescript
358
+ * @UseWsGuards(WsAuthGuard)
359
+ * @OnMessage('admin:*')
360
+ * handleAdminMessage(@Client() client: WsClientData) {
361
+ * // Only authenticated clients can reach here
362
+ * }
363
+ * ```
364
+ */
365
+ export function UseWsGuards(...guards: (Function | WsGuard)[]): MethodDecorator {
366
+ return (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) => {
367
+ const existingGuards: (Function | WsGuard)[] =
368
+ Reflect.getMetadata(WS_GUARDS_METADATA, target, propertyKey as string) || [];
369
+
370
+ Reflect.defineMetadata(
371
+ WS_GUARDS_METADATA,
372
+ [...existingGuards, ...guards],
373
+ target,
374
+ propertyKey as string,
375
+ );
376
+
377
+ return descriptor;
378
+ };
379
+ }
380
+
381
+ // ============================================================================
382
+ // Helper Functions
383
+ // ============================================================================
384
+
385
+ /**
386
+ * Get handler metadata for a gateway method
387
+ */
388
+ export function getWsHandlerMetadata(
389
+ target: Function,
390
+ methodName: string,
391
+ ): WsHandlerMetadata | undefined {
392
+ const metadata = getGatewayMetadata(target);
393
+
394
+ return metadata?.handlers.find((h) => h.handler === methodName);
395
+ }
396
+
397
+ /**
398
+ * Get all handlers from a gateway
399
+ */
400
+ export function getWsHandlers(target: Function): WsHandlerMetadata[] {
401
+ const metadata = getGatewayMetadata(target);
402
+
403
+ return metadata?.handlers || [];
404
+ }
405
+
406
+ /**
407
+ * Get parameter metadata for a handler
408
+ */
409
+ export function getWsParamMetadata(target: object, methodName: string): WsParamMetadata[] {
410
+ return Reflect.getMetadata(WS_PARAMS_METADATA, target, methodName) || [];
411
+ }
412
+
413
+ /**
414
+ * Get guards for a handler
415
+ */
416
+ export function getWsGuards(target: object, methodName: string): Function[] {
417
+ return Reflect.getMetadata(WS_GUARDS_METADATA, target, methodName) || [];
418
+ }