@bool-ts/core 1.7.17 → 1.8.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.
Files changed (83) hide show
  1. package/.prettierrc +1 -1
  2. package/__test/controller.ts +1 -1
  3. package/__test/index.ts +4 -1
  4. package/__test/module.ts +3 -1
  5. package/__test/tsconfig.json +109 -0
  6. package/__test/webSocket.ts +37 -0
  7. package/__test/webSocketClient.ts +23 -0
  8. package/dist/decorators/controller.d.ts +3 -3
  9. package/dist/decorators/controller.js +1 -2
  10. package/dist/decorators/dispatcher.d.ts +1 -1
  11. package/dist/decorators/http.d.ts +12 -12
  12. package/dist/decorators/http.js +1 -1
  13. package/dist/decorators/index.d.ts +5 -0
  14. package/dist/decorators/index.js +3 -0
  15. package/dist/decorators/module.d.ts +3 -1
  16. package/dist/decorators/module.js +9 -2
  17. package/dist/decorators/webSocket.d.ts +25 -0
  18. package/dist/decorators/webSocket.js +40 -0
  19. package/dist/decorators/webSocketArguments.d.ts +22 -0
  20. package/dist/decorators/webSocketArguments.js +49 -0
  21. package/dist/decorators/webSocketEvent.d.ts +15 -0
  22. package/dist/decorators/webSocketEvent.js +24 -0
  23. package/dist/decorators/zodSchema.js +3 -3
  24. package/dist/entities/{route.d.ts → httpRoute.d.ts} +12 -12
  25. package/dist/entities/{route.js → httpRoute.js} +27 -25
  26. package/dist/entities/httpRouter.d.ts +10 -0
  27. package/dist/entities/{router.js → httpRouter.js} +7 -5
  28. package/dist/entities/{routerGroup.d.ts → httpRouterGroup.d.ts} +4 -4
  29. package/dist/entities/{routerGroup.js → httpRouterGroup.js} +1 -1
  30. package/dist/entities/index.d.ts +7 -4
  31. package/dist/entities/index.js +6 -3
  32. package/dist/entities/webSocketRoute.d.ts +10 -0
  33. package/dist/entities/webSocketRoute.js +15 -0
  34. package/dist/entities/webSocketRouter.d.ts +31 -0
  35. package/dist/entities/webSocketRouter.js +54 -0
  36. package/dist/entities/webSocketRouterGroup.d.ts +25 -0
  37. package/dist/entities/webSocketRouterGroup.js +51 -0
  38. package/dist/hooks/factory.d.ts +0 -39
  39. package/dist/hooks/factory.js +398 -63
  40. package/dist/hooks/injector.js +2 -2
  41. package/dist/index.d.ts +1 -1
  42. package/dist/interfaces/index.d.ts +1 -0
  43. package/dist/interfaces/webSocket.d.ts +2 -0
  44. package/dist/interfaces/webSocket.js +1 -0
  45. package/dist/keys/index.d.ts +10 -1
  46. package/dist/keys/index.js +21 -12
  47. package/dist/ultils/colors.d.ts +30 -0
  48. package/dist/ultils/colors.js +41 -0
  49. package/dist/ultils/index.d.ts +2 -0
  50. package/dist/ultils/index.js +2 -0
  51. package/dist/ultils/socket.d.ts +1 -0
  52. package/dist/ultils/socket.js +7 -0
  53. package/package.json +7 -7
  54. package/src/decorators/controller.ts +5 -4
  55. package/src/decorators/dispatcher.ts +2 -1
  56. package/src/decorators/guard.ts +1 -0
  57. package/src/decorators/http.ts +3 -3
  58. package/src/decorators/index.ts +10 -0
  59. package/src/decorators/middleware.ts +1 -0
  60. package/src/decorators/module.ts +14 -3
  61. package/src/decorators/webSocket.ts +81 -0
  62. package/src/decorators/webSocketArguments.ts +144 -0
  63. package/src/decorators/webSocketEvent.ts +56 -0
  64. package/src/decorators/zodSchema.ts +5 -3
  65. package/src/entities/{route.ts → httpRoute.ts} +71 -57
  66. package/src/entities/{router.ts → httpRouter.ts} +8 -6
  67. package/src/entities/{routerGroup.ts → httpRouterGroup.ts} +4 -4
  68. package/src/entities/index.ts +7 -4
  69. package/src/entities/webSocketRoute.ts +27 -0
  70. package/src/entities/webSocketRouter.ts +64 -0
  71. package/src/entities/webSocketRouterGroup.ts +64 -0
  72. package/src/hooks/factory.ts +622 -95
  73. package/src/hooks/injector.ts +2 -2
  74. package/src/index.ts +1 -1
  75. package/src/interfaces/index.ts +1 -0
  76. package/src/interfaces/webSocket.ts +1 -0
  77. package/src/keys/index.ts +22 -12
  78. package/src/ultils/colors.ts +56 -0
  79. package/src/ultils/index.ts +3 -1
  80. package/src/ultils/socket.ts +9 -0
  81. package/test.ts +0 -0
  82. package/tsconfig.json +3 -2
  83. package/dist/entities/router.d.ts +0 -10
@@ -1,8 +1,15 @@
1
- import type { TArgumentsMetadata, TControllerMetadata, THttpMetadata, TModuleMetadata } from "../decorators";
1
+ import type {
2
+ TArgumentsMetadata,
3
+ TControllerMetadata,
4
+ THttpMetadata,
5
+ TModuleMetadata,
6
+ TWebSocketEventHandlerMetadata,
7
+ TWebSocketMetadata
8
+ } from "../decorators";
9
+ import type { TWebSocketUpgradeData } from "../decorators/webSocket";
2
10
  import type { IContext, IGuard, IMiddleware } from "../interfaces";
3
11
  import type { IDispatcher } from "../interfaces/dispatcher";
4
12
 
5
- import "colors";
6
13
  import "reflect-metadata";
7
14
 
8
15
  import Qs from "qs";
@@ -10,7 +17,13 @@ import * as Zod from "zod";
10
17
 
11
18
  import { ETimeUnit, add as TimeAdd } from "@bool-ts/date-time";
12
19
  import type { BunFile, Server } from "bun";
13
- import { Router, RouterGroup } from "../entities";
20
+ import {
21
+ HttpRouter,
22
+ HttpRouterGroup,
23
+ WebSocketRoute,
24
+ WebSocketRouter,
25
+ WebSocketRouterGroup
26
+ } from "../entities";
14
27
  import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
15
28
  import {
16
29
  argumentsKey,
@@ -28,8 +41,15 @@ import {
28
41
  requestHeadersArgsKey,
29
42
  responseBodyArgsKey,
30
43
  responseHeadersArgsKey,
31
- routeModelArgsKey
44
+ routeModelArgsKey,
45
+ webSocketCloseCodeArgsKey,
46
+ webSocketCloseReasonArgsKey,
47
+ webSocketConnectionArgsKey,
48
+ webSocketKey,
49
+ webSocketMessageArgsKey,
50
+ webSocketServerArgsKey
32
51
  } from "../keys";
52
+ import { ansiText, isWebSocketUpgrade } from "../ultils";
33
53
  import { Injector } from "./injector";
34
54
 
35
55
  export type TGroupElementModel<
@@ -70,18 +90,23 @@ export type TBoolFactoryOptions = Required<{
70
90
 
71
91
  const DEFAULT_STATIC_CACHE_TIME_IN_SECONDS = 900;
72
92
 
73
- export const responseConverter = (response: Response) => {
93
+ const responseConverter = (response: Response) => {
74
94
  response.headers.set("X-Powered-By", "Bool Typescript");
75
95
 
76
96
  return response;
77
97
  };
78
98
 
79
- export const controllerCreator = (
80
- controllerConstructor: new (...args: any[]) => unknown,
81
- group: RouterGroup,
82
- injector: Injector,
83
- prefix?: string
84
- ) => {
99
+ const controllerCreator = ({
100
+ controllerConstructor,
101
+ httpRouterGroup,
102
+ injector,
103
+ prefix
104
+ }: Readonly<{
105
+ controllerConstructor: new (...args: any[]) => unknown;
106
+ httpRouterGroup: HttpRouterGroup;
107
+ injector: Injector;
108
+ prefix?: string;
109
+ }>) => {
85
110
  if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
86
111
  throw Error(`${controllerConstructor.name} is not a controller.`);
87
112
  }
@@ -92,12 +117,16 @@ export const controllerCreator = (
92
117
  throw Error("Can not initialize controller.");
93
118
  }
94
119
 
95
- const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(controllerKey, controllerConstructor) || {
120
+ const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(
121
+ controllerKey,
122
+ controllerConstructor
123
+ ) || {
96
124
  prefix: "/",
97
125
  httpMetadata: []
98
126
  };
99
- const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []) as THttpMetadata;
100
- const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
127
+ const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) ||
128
+ []) as THttpMetadata;
129
+ const router = new HttpRouter(`/${prefix || ""}/${controllerMetadata.prefix}`);
101
130
 
102
131
  routesMetadata.forEach((routeMetadata) => {
103
132
  if (typeof routeMetadata.descriptor.value !== "function") {
@@ -128,10 +157,98 @@ export const controllerCreator = (
128
157
  }
129
158
  });
130
159
 
131
- return group.add(router);
160
+ return httpRouterGroup.add(router);
132
161
  };
133
162
 
134
- export const argumentsResolution = async (
163
+ const webSocketCreator = ({
164
+ injector,
165
+ httpRouterGroup,
166
+ prefix,
167
+ webSocketRouterGroup,
168
+ webSocketConstructor
169
+ }: Readonly<{
170
+ webSocketConstructor: new (...args: any[]) => unknown;
171
+ httpRouterGroup: HttpRouterGroup;
172
+ webSocketRouterGroup: WebSocketRouterGroup;
173
+ injector: Injector;
174
+ prefix?: string;
175
+ }>): Readonly<{
176
+ httpRouterGroup: HttpRouterGroup;
177
+ webSocketRouterGroup: WebSocketRouterGroup;
178
+ }> => {
179
+ if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
180
+ throw Error(`${webSocketConstructor.name} is not a controller.`);
181
+ }
182
+
183
+ const webSocket = injector.get(webSocketConstructor);
184
+
185
+ if (!webSocket) {
186
+ throw Error("Can not initialize webSocket.");
187
+ }
188
+
189
+ const webSocketMetadata: TWebSocketMetadata = Reflect.getOwnMetadata(
190
+ webSocketKey,
191
+ webSocketConstructor
192
+ ) || {
193
+ prefix: "/",
194
+ events: [],
195
+ http: []
196
+ };
197
+
198
+ const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
199
+
200
+ //#region [HTTP ROUTER]
201
+ const router = new HttpRouter(fullPrefix);
202
+
203
+ for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
204
+ if (typeof httpMetadata.descriptor?.value !== "function") {
205
+ continue;
206
+ }
207
+
208
+ const route = router.route(httpMetadata.path);
209
+ const handler = httpMetadata.descriptor.value.bind(webSocket);
210
+ const routeArgument = Object.freeze({
211
+ class: webSocketConstructor,
212
+ funcName: httpMetadata.methodName,
213
+ func: handler
214
+ });
215
+
216
+ switch (httpMetadata.httpMethod) {
217
+ case "GET":
218
+ route.get(routeArgument);
219
+ break;
220
+ case "POST":
221
+ route.post(routeArgument);
222
+ break;
223
+ }
224
+ }
225
+
226
+ httpRouterGroup.add(router);
227
+ //#endregion
228
+
229
+ //#region [WEBSOCKET ROUTER]
230
+ const webSocketRouter = new WebSocketRouter(fullPrefix);
231
+
232
+ for (const [key, event] of Object.entries(webSocketMetadata.events)) {
233
+ const webSocketRoute = new WebSocketRoute({
234
+ eventName: key,
235
+ metadata: event
236
+ });
237
+
238
+ webSocketRouter.addRoutes(webSocketRoute);
239
+ }
240
+
241
+ webSocketRouter.bind(webSocket);
242
+ webSocketRouterGroup.addRouters(webSocketRouter);
243
+ //#endregion
244
+
245
+ return Object.freeze({
246
+ httpRouterGroup: httpRouterGroup,
247
+ webSocketRouterGroup: webSocketRouterGroup
248
+ });
249
+ };
250
+
251
+ const argumentsResolution = async (
135
252
  data: unknown,
136
253
  zodSchema: Zod.Schema,
137
254
  argumentIndex: number,
@@ -170,7 +287,10 @@ export const argumentsResolution = async (
170
287
  }
171
288
  };
172
289
 
173
- export const moduleResolution = async (module: new (...args: any[]) => unknown, options: TBoolFactoryOptions) => {
290
+ const moduleResolution = async (
291
+ module: new (...args: any[]) => unknown,
292
+ options: TBoolFactoryOptions
293
+ ) => {
174
294
  if (!Reflect.getOwnMetadataKeys(module).includes(moduleKey)) {
175
295
  throw Error(`${module.name} is not a module.`);
176
296
  }
@@ -189,11 +309,14 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
189
309
  dispatchers,
190
310
  controllers,
191
311
  dependencies,
312
+ webSockets,
192
313
  prefix: modulePrefix,
193
314
  config: moduleConfig
194
315
  } = moduleMetadata;
195
316
 
196
- // Configuration(s)
317
+ const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
318
+
319
+ //#region [Configuration(s)]
197
320
  const { config } = Object.freeze({
198
321
  config: {
199
322
  ...(typeof options.config !== "function" ? options.config : await options.config()),
@@ -204,10 +327,13 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
204
327
  : await moduleConfig())
205
328
  }
206
329
  });
330
+ //#endregion
207
331
 
208
- // Register config like an injection
332
+ //#region [Register config like an injection]
209
333
  injector.set(configKey, config);
334
+ //#endregion
210
335
 
336
+ //#region [Run loader(s)]
211
337
  if (loaders) {
212
338
  const loaderFunctions = [];
213
339
 
@@ -215,10 +341,24 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
215
341
  loaderFunctions.push(async () => {
216
342
  try {
217
343
  const result = await func({ config });
218
- console.info(`INFO! Loader [${key}] initialized successfully.`);
344
+
345
+ console.info(
346
+ `${ansiText(" INFO ", {
347
+ color: "white",
348
+ backgroundColor: "blue",
349
+ bold: true
350
+ })} Loader [${key}] initialized successfully.`
351
+ );
352
+
219
353
  return result;
220
354
  } catch (error) {
221
- console.error(`WARNING! Loader [${key}] initialization failed.`);
355
+ console.error(
356
+ `${ansiText(" WARN ", {
357
+ color: "yellow",
358
+ backgroundColor: "red",
359
+ bold: true
360
+ })} Loader [${key}] initialization failed.`
361
+ );
222
362
  options.debug && console.error(error);
223
363
  throw error;
224
364
  }
@@ -232,22 +372,28 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
232
372
  injector.set(key, value);
233
373
  }
234
374
  }
375
+ //#endregion
235
376
 
236
- // Dependencies
377
+ //#region [Dependencies]
237
378
  !dependencies || dependencies.map((dependency) => injector.get(dependency));
379
+ //#endregion
238
380
 
239
- // Middleware(s)
240
- const startMiddlewareGroup: Array<TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>> = [];
241
- const endMiddlewareGroup: Array<TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>> = [];
381
+ //#region [Middleware(s)]
382
+ const startMiddlewareGroup: Array<
383
+ TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>
384
+ > = [];
385
+ const endMiddlewareGroup: Array<
386
+ TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>
387
+ > = [];
242
388
 
243
- if (middlewares) {
244
- for (let i = 0; i < middlewares.length; i++) {
245
- const instance = injector.get<IMiddleware>(middlewares[i]);
389
+ middlewares &&
390
+ middlewares.forEach((middleware) => {
391
+ const instance = injector.get<IMiddleware>(middleware);
246
392
 
247
393
  if (instance.start && typeof instance.start === "function") {
248
394
  startMiddlewareGroup.push(
249
395
  Object.freeze({
250
- class: middlewares[i] as IMiddleware,
396
+ class: middleware as IMiddleware,
251
397
  funcName: "start",
252
398
  func: instance.start.bind(instance)
253
399
  })
@@ -257,16 +403,16 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
257
403
  if (instance.end && typeof instance.end === "function") {
258
404
  endMiddlewareGroup.push(
259
405
  Object.freeze({
260
- class: middlewares[i] as IMiddleware,
406
+ class: middleware as IMiddleware,
261
407
  funcName: "end",
262
408
  func: instance.end.bind(instance)
263
409
  })
264
410
  );
265
411
  }
266
- }
267
- }
412
+ });
413
+ //#endregion
268
414
 
269
- // Guard(s)
415
+ //#region [Guard(s)]
270
416
  const guardGroup = !guards
271
417
  ? []
272
418
  : guards.map((guard) => {
@@ -278,19 +424,24 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
278
424
  func: guardInstance.enforce.bind(guardInstance)
279
425
  });
280
426
  });
427
+ //#endregion
281
428
 
282
- // Before dispatcher(s)
283
- const openDispatcherGroup: Array<TGroupElementModel<"open", IDispatcher, NonNullable<IDispatcher["open"]>>> = [];
284
- const closeDispatcherGroup: Array<TGroupElementModel<"close", IDispatcher, NonNullable<IDispatcher["close"]>>> = [];
429
+ //#region [Before dispatcher(s)]
430
+ const openDispatcherGroup: Array<
431
+ TGroupElementModel<"open", IDispatcher, NonNullable<IDispatcher["open"]>>
432
+ > = [];
433
+ const closeDispatcherGroup: Array<
434
+ TGroupElementModel<"close", IDispatcher, NonNullable<IDispatcher["close"]>>
435
+ > = [];
285
436
 
286
- if (dispatchers) {
287
- for (let i = 0; i < dispatchers.length; i++) {
288
- const instance = injector.get<IDispatcher>(dispatchers[i]);
437
+ dispatchers &&
438
+ dispatchers.forEach((dispatcher) => {
439
+ const instance = injector.get<IDispatcher>(dispatcher);
289
440
 
290
441
  if (instance.open && typeof instance.open === "function") {
291
442
  openDispatcherGroup.push(
292
443
  Object.freeze({
293
- class: dispatchers[i] as IDispatcher,
444
+ class: dispatcher as IDispatcher,
294
445
  funcName: "open",
295
446
  func: instance.open.bind(instance)
296
447
  })
@@ -300,22 +451,44 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
300
451
  if (instance.close && typeof instance.close === "function") {
301
452
  closeDispatcherGroup.push(
302
453
  Object.freeze({
303
- class: dispatchers[i] as IDispatcher,
454
+ class: dispatcher as IDispatcher,
304
455
  funcName: "close",
305
456
  func: instance.close.bind(instance)
306
457
  })
307
458
  );
308
459
  }
309
- }
310
- }
460
+ });
461
+ //#endregion
311
462
 
312
- // Controller(s)
313
- const routerGroup = new RouterGroup();
463
+ //#region [Controller(s)]
464
+ const controllerRouterGroup = new HttpRouterGroup();
314
465
 
315
466
  controllers &&
316
- controllers.map((controllerConstructor) =>
317
- controllerCreator(controllerConstructor, routerGroup, injector, `${options.prefix || ""}/${modulePrefix || ""}`)
467
+ controllers.forEach((controllerConstructor) =>
468
+ controllerCreator({
469
+ controllerConstructor,
470
+ httpRouterGroup: controllerRouterGroup,
471
+ injector: injector,
472
+ prefix: fullPrefix
473
+ })
474
+ );
475
+ //#endregion
476
+
477
+ //#region [WebSocket(s)]
478
+ const webSocketHttpRouterGroup = new HttpRouterGroup();
479
+ const webSocketRouterGroup = new WebSocketRouterGroup();
480
+
481
+ webSockets &&
482
+ webSockets.forEach((webSocket) =>
483
+ webSocketCreator({
484
+ webSocketConstructor: webSocket,
485
+ httpRouterGroup: webSocketHttpRouterGroup,
486
+ webSocketRouterGroup: webSocketRouterGroup,
487
+ injector,
488
+ prefix: fullPrefix
489
+ })
318
490
  );
491
+ //#endregion
319
492
 
320
493
  return Object.freeze({
321
494
  prefix: moduleMetadata.prefix,
@@ -325,11 +498,13 @@ export const moduleResolution = async (module: new (...args: any[]) => unknown,
325
498
  guardGroup,
326
499
  openDispatcherGroup,
327
500
  closeDispatcherGroup,
328
- routerGroup
501
+ controllerRouterGroup,
502
+ webSocketHttpRouterGroup,
503
+ webSocketRouterGroup
329
504
  });
330
505
  };
331
506
 
332
- const fetcher = async (
507
+ const webSocketFetcher = async (
333
508
  bun: Required<{
334
509
  request: Request;
335
510
  server: Server;
@@ -337,17 +512,82 @@ const fetcher = async (
337
512
  bool: Required<{
338
513
  responseHeaders: Headers;
339
514
  query: Record<string, unknown>;
340
- route: NonNullable<ReturnType<RouterGroup["find"]>>;
515
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
341
516
  moduleResolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
342
517
  }>
343
518
  ) => {
519
+ const { request, server } = bun;
344
520
  const {
345
521
  query,
346
522
  responseHeaders,
347
- route: { parameters, model },
348
- moduleResolution: { startMiddlewareGroup, endMiddlewareGroup, guardGroup, openDispatcherGroup, closeDispatcherGroup }
523
+ route: { model }
349
524
  } = bool;
525
+
526
+ // Execute controller action
527
+ const isUpgrade = await model.func(...[server, request, query]);
528
+
529
+ if (typeof isUpgrade !== "boolean") {
530
+ return responseConverter(
531
+ new Response(
532
+ JSON.stringify({
533
+ httpCode: 500,
534
+ message: "Can not detect webSocket upgrade result.",
535
+ data: undefined
536
+ }),
537
+ {
538
+ status: 500,
539
+ statusText: "Internal server error.",
540
+ headers: responseHeaders
541
+ }
542
+ )
543
+ );
544
+ }
545
+
546
+ if (!isUpgrade) {
547
+ return responseConverter(
548
+ new Response(
549
+ JSON.stringify({
550
+ httpCode: 500,
551
+ message: "Can not upgrade.",
552
+ data: undefined
553
+ }),
554
+ {
555
+ status: 500,
556
+ statusText: "Internal server error.",
557
+ headers: responseHeaders
558
+ }
559
+ )
560
+ );
561
+ }
562
+
563
+ return isUpgrade;
564
+ };
565
+
566
+ const httpFetcher = async (
567
+ bun: Required<{
568
+ request: Request;
569
+ server: Server;
570
+ }>,
571
+ bool: Required<{
572
+ responseHeaders: Headers;
573
+ query: Record<string, unknown>;
574
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
575
+ moduleResolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
576
+ }>
577
+ ) => {
350
578
  const { request, server: _server } = bun;
579
+ const {
580
+ query,
581
+ responseHeaders,
582
+ route: { parameters, model },
583
+ moduleResolution: {
584
+ startMiddlewareGroup,
585
+ endMiddlewareGroup,
586
+ guardGroup,
587
+ openDispatcherGroup,
588
+ closeDispatcherGroup
589
+ }
590
+ } = bool;
351
591
 
352
592
  const context: Record<symbol, any> = {
353
593
  [requestHeadersArgsKey]: request.headers,
@@ -401,7 +641,9 @@ const fetcher = async (
401
641
  );
402
642
  break;
403
643
  case contextArgsKey:
404
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
644
+ args[argsMetadata.index] = !argsMetadata.key
645
+ ? contextHook
646
+ : contextHook.get(argsMetadata.key);
405
647
  break;
406
648
  case requestHeadersArgsKey:
407
649
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -445,7 +687,9 @@ const fetcher = async (
445
687
  ? undefined
446
688
  : context[argsMetadata.type]
447
689
  : await argumentsResolution(
448
- !(argsMetadata.type in context) ? undefined : context[argsMetadata.type],
690
+ !(argsMetadata.type in context)
691
+ ? undefined
692
+ : context[argsMetadata.type],
449
693
  argsMetadata.zodSchema,
450
694
  argsMetadata.index,
451
695
  collection.funcName
@@ -493,7 +737,9 @@ const fetcher = async (
493
737
  );
494
738
  break;
495
739
  case contextArgsKey:
496
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
740
+ args[argsMetadata.index] = !argsMetadata.key
741
+ ? contextHook
742
+ : contextHook.get(argsMetadata.key);
497
743
  break;
498
744
  case requestHeadersArgsKey:
499
745
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -587,7 +833,9 @@ const fetcher = async (
587
833
  );
588
834
  break;
589
835
  case contextArgsKey:
590
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
836
+ args[argsMetadata.index] = !argsMetadata.key
837
+ ? contextHook
838
+ : contextHook.get(argsMetadata.key);
591
839
  break;
592
840
  case requestHeadersArgsKey:
593
841
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -646,7 +894,11 @@ const fetcher = async (
646
894
  const controllerActionArguments: any[] = [];
647
895
  const controllerActionCollection = model;
648
896
  const controllerActionMetadata: Record<string, TArgumentsMetadata> =
649
- Reflect.getOwnMetadata(argumentsKey, controllerActionCollection.class, controllerActionCollection.funcName) || {};
897
+ Reflect.getOwnMetadata(
898
+ argumentsKey,
899
+ controllerActionCollection.class,
900
+ controllerActionCollection.funcName
901
+ ) || {};
650
902
 
651
903
  if (controllerActionMetadata) {
652
904
  for (const [_key, argsMetadata] of Object.entries(controllerActionMetadata)) {
@@ -726,7 +978,9 @@ const fetcher = async (
726
978
  }
727
979
  }
728
980
 
729
- context[responseBodyArgsKey] = await controllerActionCollection.func(...controllerActionArguments);
981
+ context[responseBodyArgsKey] = await controllerActionCollection.func(
982
+ ...controllerActionArguments
983
+ );
730
984
 
731
985
  // Execute close dispatcher(s)
732
986
  for (let i = 0; i < closeDispatcherGroup.length; i++) {
@@ -759,7 +1013,9 @@ const fetcher = async (
759
1013
  );
760
1014
  break;
761
1015
  case contextArgsKey:
762
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
1016
+ args[argsMetadata.index] = !argsMetadata.key
1017
+ ? contextHook
1018
+ : contextHook.get(argsMetadata.key);
763
1019
  break;
764
1020
  case requestHeadersArgsKey:
765
1021
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -849,7 +1105,9 @@ const fetcher = async (
849
1105
  );
850
1106
  break;
851
1107
  case contextArgsKey:
852
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
1108
+ args[argsMetadata.index] = !argsMetadata.key
1109
+ ? contextHook
1110
+ : contextHook.get(argsMetadata.key);
853
1111
  break;
854
1112
  case requestHeadersArgsKey:
855
1113
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -893,7 +1151,9 @@ const fetcher = async (
893
1151
  ? undefined
894
1152
  : context[argsMetadata.type]
895
1153
  : await argumentsResolution(
896
- !(argsMetadata.type in context) ? undefined : context[argsMetadata.type],
1154
+ !(argsMetadata.type in context)
1155
+ ? undefined
1156
+ : context[argsMetadata.type],
897
1157
  argsMetadata.zodSchema,
898
1158
  argsMetadata.index,
899
1159
  collection.funcName
@@ -928,7 +1188,10 @@ const fetcher = async (
928
1188
  );
929
1189
  };
930
1190
 
931
- export const BoolFactory = async (modules: Object | Array<Object>, options: TBoolFactoryOptions) => {
1191
+ export const BoolFactory = async (
1192
+ modules: Object | Array<Object>,
1193
+ options: TBoolFactoryOptions
1194
+ ) => {
932
1195
  try {
933
1196
  const staticMap: Map<
934
1197
  string,
@@ -940,7 +1203,14 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
940
1203
 
941
1204
  const modulesConverted = !Array.isArray(modules) ? [modules] : modules;
942
1205
 
943
- const { allowLogsMethods, staticOption, allowOrigins, allowMethods, allowCredentials, allowHeaders } = Object.freeze({
1206
+ const {
1207
+ allowLogsMethods,
1208
+ staticOption,
1209
+ allowOrigins,
1210
+ allowMethods,
1211
+ allowCredentials,
1212
+ allowHeaders
1213
+ } = Object.freeze({
944
1214
  allowLogsMethods: options?.log?.methods,
945
1215
  staticOption: options.static,
946
1216
  allowOrigins: !options.cors?.origins
@@ -950,9 +1220,19 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
950
1220
  ? ["*"]
951
1221
  : options.cors.origins
952
1222
  : [options.cors.origins !== "*" ? options.cors.origins : "*"],
953
- allowMethods: options.cors?.methods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
1223
+ allowMethods: options.cors?.methods || [
1224
+ "GET",
1225
+ "POST",
1226
+ "PUT",
1227
+ "PATCH",
1228
+ "DELETE",
1229
+ "OPTIONS"
1230
+ ],
954
1231
  allowCredentials: !options.cors?.credentials ? false : true,
955
- allowHeaders: !options.cors?.headers || options.cors.headers.includes("*") ? ["*"] : options.cors.headers
1232
+ allowHeaders:
1233
+ !options.cors?.headers || options.cors.headers.includes("*")
1234
+ ? ["*"]
1235
+ : options.cors.headers
956
1236
  });
957
1237
 
958
1238
  const moduleResolutions = await Promise.all(
@@ -964,14 +1244,28 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
964
1244
  );
965
1245
 
966
1246
  const prefixs = [
967
- ...new Set(availableModuleResolutions.map((availableModuleResolution) => availableModuleResolution.prefix))
1247
+ ...new Set(
1248
+ availableModuleResolutions.map(
1249
+ (availableModuleResolution) => availableModuleResolution.prefix
1250
+ )
1251
+ )
968
1252
  ];
969
1253
 
970
1254
  if (prefixs.length !== availableModuleResolutions.length) {
971
- throw Error(`Module prefix should be unique.`);
1255
+ throw Error("Module prefix should be unique.");
1256
+ }
1257
+
1258
+ const webSocketsMap = new Map<string, TWebSocketEventHandlerMetadata>();
1259
+
1260
+ for (const availableModuleResolution of availableModuleResolutions) {
1261
+ const webSocketMap = availableModuleResolution.webSocketRouterGroup.execute();
1262
+
1263
+ for (const [key, metadata] of webSocketMap.entries()) {
1264
+ webSocketsMap.set(key, metadata);
1265
+ }
972
1266
  }
973
1267
 
974
- Bun.serve({
1268
+ const server = Bun.serve<TWebSocketUpgradeData>({
975
1269
  port: options.port,
976
1270
  fetch: async (request, server) => {
977
1271
  const start = performance.now();
@@ -982,13 +1276,77 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
982
1276
  const responseHeaders = new Headers();
983
1277
 
984
1278
  try {
985
- allowCredentials && responseHeaders.set("Access-Control-Allow-Credentials", "true");
1279
+ const isUpgradable = isWebSocketUpgrade(request);
1280
+
1281
+ let collection:
1282
+ | undefined
1283
+ | Required<{
1284
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
1285
+ resolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
1286
+ }>;
1287
+
1288
+ if (isUpgradable) {
1289
+ for (const availableModuleResolution of availableModuleResolutions) {
1290
+ const routeResult =
1291
+ availableModuleResolution.webSocketHttpRouterGroup.find(
1292
+ url.pathname,
1293
+ request.method as keyof THttpMethods
1294
+ );
1295
+
1296
+ if (routeResult) {
1297
+ collection = Object.freeze({
1298
+ route: routeResult,
1299
+ resolution: availableModuleResolution
1300
+ });
1301
+ break;
1302
+ }
1303
+ }
1304
+
1305
+ if (!collection) {
1306
+ return responseConverter(
1307
+ new Response(
1308
+ JSON.stringify({
1309
+ httpCode: 404,
1310
+ message: "Route not found",
1311
+ data: undefined
1312
+ }),
1313
+ {
1314
+ status: 404,
1315
+ statusText: "Not found.",
1316
+ headers: responseHeaders
1317
+ }
1318
+ )
1319
+ );
1320
+ }
1321
+
1322
+ const upgradeResult = await webSocketFetcher(
1323
+ {
1324
+ request,
1325
+ server
1326
+ },
1327
+ {
1328
+ query: query,
1329
+ responseHeaders: responseHeaders,
1330
+ route: collection.route,
1331
+ moduleResolution: collection.resolution
1332
+ }
1333
+ );
1334
+
1335
+ return upgradeResult instanceof Response ? upgradeResult : undefined;
1336
+ }
1337
+
1338
+ allowCredentials &&
1339
+ responseHeaders.set("Access-Control-Allow-Credentials", "true");
986
1340
 
987
1341
  responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(", "));
988
1342
  responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
989
1343
  responseHeaders.set(
990
1344
  "Access-Control-Allow-Origin",
991
- allowOrigins.includes("*") ? "*" : !allowOrigins.includes(origin) ? allowOrigins[0] : origin
1345
+ allowOrigins.includes("*")
1346
+ ? "*"
1347
+ : !allowOrigins.includes(origin)
1348
+ ? allowOrigins[0]
1349
+ : origin
992
1350
  );
993
1351
 
994
1352
  if (!allowMethods.includes(method)) {
@@ -1087,15 +1445,8 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
1087
1445
  }
1088
1446
  }
1089
1447
 
1090
- let collection:
1091
- | undefined
1092
- | Required<{
1093
- route: NonNullable<ReturnType<RouterGroup["find"]>>;
1094
- resolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
1095
- }>;
1096
-
1097
- for (let i = 0; i < availableModuleResolutions.length; i++) {
1098
- const routeResult = availableModuleResolutions[i].routerGroup.find(
1448
+ for (const availableModuleResolution of availableModuleResolutions) {
1449
+ const routeResult = availableModuleResolution.controllerRouterGroup.find(
1099
1450
  url.pathname,
1100
1451
  request.method as keyof THttpMethods
1101
1452
  );
@@ -1103,7 +1454,7 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
1103
1454
  if (routeResult) {
1104
1455
  collection = Object.freeze({
1105
1456
  route: routeResult,
1106
- resolution: availableModuleResolutions[i]
1457
+ resolution: availableModuleResolution
1107
1458
  });
1108
1459
  break;
1109
1460
  }
@@ -1126,7 +1477,7 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
1126
1477
  );
1127
1478
  }
1128
1479
 
1129
- return await fetcher(
1480
+ return await httpFetcher(
1130
1481
  {
1131
1482
  request,
1132
1483
  server
@@ -1145,24 +1496,200 @@ export const BoolFactory = async (modules: Object | Array<Object>, options: TBoo
1145
1496
  } finally {
1146
1497
  if (allowLogsMethods) {
1147
1498
  const end = performance.now();
1148
- const convertedPID = `${process.pid}`.yellow;
1149
- const convertedMethod = `${request.method.yellow}`.bgBlue;
1150
- const convertedReqIp = `${
1151
- request.headers.get("x-forwarded-for") ||
1152
- request.headers.get("x-real-ip") ||
1153
- server.requestIP(request)?.address ||
1154
- "<Unknown>"
1155
- }`.yellow;
1156
- const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
1157
-
1158
- allowLogsMethods.includes(request.method.toUpperCase() as (typeof allowLogsMethods)[number]) &&
1499
+ const pathname = ansiText(url.pathname, { color: "blue" });
1500
+ const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
1501
+ const convertedMethod = ansiText(request.method, {
1502
+ color: "yellow",
1503
+ backgroundColor: "blue"
1504
+ });
1505
+ const convertedReqIp = ansiText(
1506
+ `${
1507
+ request.headers.get("x-forwarded-for") ||
1508
+ request.headers.get("x-real-ip") ||
1509
+ server.requestIP(request)?.address ||
1510
+ "<Unknown>"
1511
+ }`,
1512
+ {
1513
+ color: "yellow"
1514
+ }
1515
+ );
1516
+ const convertedTime = ansiText(
1517
+ `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`,
1518
+ {
1519
+ color: "yellow",
1520
+ backgroundColor: "blue"
1521
+ }
1522
+ );
1523
+
1524
+ allowLogsMethods.includes(
1525
+ request.method.toUpperCase() as (typeof allowLogsMethods)[number]
1526
+ ) &&
1159
1527
  console.info(
1160
- `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${
1161
- new URL(request.url).pathname.blue
1162
- } - Time: ${convertedTime}`
1528
+ `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`
1163
1529
  );
1164
1530
  }
1165
1531
  }
1532
+ },
1533
+ websocket: {
1534
+ open: (connection) => {
1535
+ const pathnameKey = `${connection.data.pathname}:::open`;
1536
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1537
+
1538
+ if (!handlerMetadata) {
1539
+ return;
1540
+ }
1541
+
1542
+ const argumentsMetadata = handlerMetadata.arguments || {};
1543
+ const args: Array<unknown> = [];
1544
+
1545
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1546
+ switch (argumentMetadata.type) {
1547
+ case webSocketConnectionArgsKey:
1548
+ args[argumentMetadata.index] = connection;
1549
+ break;
1550
+ case webSocketServerArgsKey:
1551
+ args[argumentMetadata.index] = server;
1552
+ break;
1553
+ }
1554
+ }
1555
+
1556
+ handlerMetadata.descriptor.value(...args);
1557
+ },
1558
+ close: (connection, code: number, reason: string) => {
1559
+ const pathnameKey = `${connection.data.pathname}:::close`;
1560
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1561
+
1562
+ if (!handlerMetadata) {
1563
+ return;
1564
+ }
1565
+
1566
+ const argumentsMetadata = handlerMetadata.arguments || {};
1567
+ const args: Array<unknown> = [];
1568
+
1569
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1570
+ switch (argumentMetadata.type) {
1571
+ case webSocketConnectionArgsKey:
1572
+ args[argumentMetadata.index] = connection;
1573
+ break;
1574
+ case webSocketServerArgsKey:
1575
+ args[argumentMetadata.index] = server;
1576
+ break;
1577
+ case webSocketCloseCodeArgsKey:
1578
+ args[argumentMetadata.index] = code;
1579
+ break;
1580
+ case webSocketCloseReasonArgsKey:
1581
+ args[argumentMetadata.index] = reason;
1582
+ break;
1583
+ }
1584
+ }
1585
+
1586
+ handlerMetadata.descriptor.value(...args);
1587
+ },
1588
+ message: (connection, message) => {
1589
+ const pathnameKey = `${connection.data.pathname}:::message`;
1590
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1591
+
1592
+ if (!handlerMetadata) {
1593
+ return;
1594
+ }
1595
+
1596
+ const argumentsMetadata = handlerMetadata.arguments || {};
1597
+ const args: Array<unknown> = [];
1598
+
1599
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1600
+ switch (argumentMetadata.type) {
1601
+ case webSocketConnectionArgsKey:
1602
+ args[argumentMetadata.index] = connection;
1603
+ break;
1604
+ case webSocketMessageArgsKey:
1605
+ args[argumentMetadata.index] = message;
1606
+ break;
1607
+ case webSocketServerArgsKey:
1608
+ args[argumentMetadata.index] = server;
1609
+ break;
1610
+ }
1611
+ }
1612
+
1613
+ handlerMetadata.descriptor.value(...args);
1614
+ },
1615
+ drain: (connection) => {
1616
+ const pathnameKey = `${connection.data.pathname}:::drain`;
1617
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1618
+
1619
+ if (!handlerMetadata) {
1620
+ return;
1621
+ }
1622
+
1623
+ const argumentsMetadata = handlerMetadata.arguments || {};
1624
+ const args: Array<unknown> = [];
1625
+
1626
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1627
+ switch (argumentMetadata.type) {
1628
+ case webSocketConnectionArgsKey:
1629
+ args[argumentMetadata.index] = connection;
1630
+ break;
1631
+ case webSocketServerArgsKey:
1632
+ args[argumentMetadata.index] = server;
1633
+ break;
1634
+ }
1635
+ }
1636
+
1637
+ handlerMetadata.descriptor.value(...args);
1638
+ },
1639
+ ping: (connection, data) => {
1640
+ const pathnameKey = `${connection.data.pathname}:::ping`;
1641
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1642
+
1643
+ if (!handlerMetadata) {
1644
+ return;
1645
+ }
1646
+
1647
+ const argumentsMetadata = handlerMetadata.arguments || {};
1648
+ const args: Array<unknown> = [];
1649
+
1650
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1651
+ switch (argumentMetadata.type) {
1652
+ case webSocketConnectionArgsKey:
1653
+ args[argumentMetadata.index] = connection;
1654
+ break;
1655
+ case webSocketServerArgsKey:
1656
+ args[argumentMetadata.index] = server;
1657
+ break;
1658
+ case webSocketMessageArgsKey:
1659
+ args[argumentMetadata.index] = data;
1660
+ break;
1661
+ }
1662
+ }
1663
+
1664
+ handlerMetadata.descriptor.value(...args);
1665
+ },
1666
+ pong: (connection, data) => {
1667
+ const pathnameKey = `${connection.data.pathname}:::pong`;
1668
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1669
+
1670
+ if (!handlerMetadata) {
1671
+ return;
1672
+ }
1673
+
1674
+ const argumentsMetadata = handlerMetadata.arguments || {};
1675
+ const args: Array<unknown> = [];
1676
+
1677
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1678
+ switch (argumentMetadata.type) {
1679
+ case webSocketConnectionArgsKey:
1680
+ args[argumentMetadata.index] = connection;
1681
+ break;
1682
+ case webSocketServerArgsKey:
1683
+ args[argumentMetadata.index] = server;
1684
+ break;
1685
+ case webSocketMessageArgsKey:
1686
+ args[argumentMetadata.index] = data;
1687
+ break;
1688
+ }
1689
+ }
1690
+
1691
+ handlerMetadata.descriptor.value(...args);
1692
+ }
1166
1693
  }
1167
1694
  });
1168
1695
  } catch (error) {