@bool-ts/core 1.7.16 → 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 (84) 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 +1 -39
  39. package/dist/hooks/factory.js +450 -76
  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 +8 -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 +704 -112
  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/bun.lockb +0 -0
  84. package/dist/entities/router.d.ts +0 -10
@@ -1,16 +1,18 @@
1
- import "colors";
2
1
  import "reflect-metadata";
3
2
  import Qs from "qs";
4
3
  import * as Zod from "zod";
5
- import { Router, RouterGroup } from "../entities";
4
+ import { ETimeUnit, add as TimeAdd } from "@bool-ts/date-time";
5
+ import { HttpRouter, HttpRouterGroup, WebSocketRoute, WebSocketRouter, WebSocketRouterGroup } from "../entities";
6
6
  import { HttpClientError, HttpServerError, jsonErrorInfer } from "../http";
7
- import { argumentsKey, configKey, contextArgsKey, controllerHttpKey, controllerKey, moduleKey, paramArgsKey, paramsArgsKey, queryArgsKey, requestArgsKey, requestBodyArgsKey, requestHeaderArgsKey, requestHeadersArgsKey, responseBodyArgsKey, responseHeadersArgsKey, routeModelArgsKey } from "../keys";
7
+ import { argumentsKey, configKey, contextArgsKey, controllerHttpKey, controllerKey, moduleKey, paramArgsKey, paramsArgsKey, queryArgsKey, requestArgsKey, requestBodyArgsKey, requestHeaderArgsKey, requestHeadersArgsKey, responseBodyArgsKey, responseHeadersArgsKey, routeModelArgsKey, webSocketCloseCodeArgsKey, webSocketCloseReasonArgsKey, webSocketConnectionArgsKey, webSocketKey, webSocketMessageArgsKey, webSocketServerArgsKey } from "../keys";
8
+ import { ansiText, isWebSocketUpgrade } from "../ultils";
8
9
  import { Injector } from "./injector";
9
- export const responseConverter = (response) => {
10
+ const DEFAULT_STATIC_CACHE_TIME_IN_SECONDS = 900;
11
+ const responseConverter = (response) => {
10
12
  response.headers.set("X-Powered-By", "Bool Typescript");
11
13
  return response;
12
14
  };
13
- export const controllerCreator = (controllerConstructor, group, injector, prefix) => {
15
+ const controllerCreator = ({ controllerConstructor, httpRouterGroup, injector, prefix }) => {
14
16
  if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
15
17
  throw Error(`${controllerConstructor.name} is not a controller.`);
16
18
  }
@@ -22,8 +24,9 @@ export const controllerCreator = (controllerConstructor, group, injector, prefix
22
24
  prefix: "/",
23
25
  httpMetadata: []
24
26
  };
25
- const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []);
26
- const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
27
+ const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) ||
28
+ []);
29
+ const router = new HttpRouter(`/${prefix || ""}/${controllerMetadata.prefix}`);
27
30
  routesMetadata.forEach((routeMetadata) => {
28
31
  if (typeof routeMetadata.descriptor.value !== "function") {
29
32
  return;
@@ -50,9 +53,64 @@ export const controllerCreator = (controllerConstructor, group, injector, prefix
50
53
  return route.options(routeArgument);
51
54
  }
52
55
  });
53
- return group.add(router);
56
+ return httpRouterGroup.add(router);
54
57
  };
55
- export const argumentsResolution = async (data, zodSchema, argumentIndex, funcName) => {
58
+ const webSocketCreator = ({ injector, httpRouterGroup, prefix, webSocketRouterGroup, webSocketConstructor }) => {
59
+ if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
60
+ throw Error(`${webSocketConstructor.name} is not a controller.`);
61
+ }
62
+ const webSocket = injector.get(webSocketConstructor);
63
+ if (!webSocket) {
64
+ throw Error("Can not initialize webSocket.");
65
+ }
66
+ const webSocketMetadata = Reflect.getOwnMetadata(webSocketKey, webSocketConstructor) || {
67
+ prefix: "/",
68
+ events: [],
69
+ http: []
70
+ };
71
+ const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
72
+ //#region [HTTP ROUTER]
73
+ const router = new HttpRouter(fullPrefix);
74
+ for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
75
+ if (typeof httpMetadata.descriptor?.value !== "function") {
76
+ continue;
77
+ }
78
+ const route = router.route(httpMetadata.path);
79
+ const handler = httpMetadata.descriptor.value.bind(webSocket);
80
+ const routeArgument = Object.freeze({
81
+ class: webSocketConstructor,
82
+ funcName: httpMetadata.methodName,
83
+ func: handler
84
+ });
85
+ switch (httpMetadata.httpMethod) {
86
+ case "GET":
87
+ route.get(routeArgument);
88
+ break;
89
+ case "POST":
90
+ route.post(routeArgument);
91
+ break;
92
+ }
93
+ }
94
+ httpRouterGroup.add(router);
95
+ //#endregion
96
+ //#region [WEBSOCKET ROUTER]
97
+ const webSocketRouter = new WebSocketRouter(fullPrefix);
98
+ for (const [key, event] of Object.entries(webSocketMetadata.events)) {
99
+ const webSocketRoute = new WebSocketRoute({
100
+ eventName: key,
101
+ metadata: event
102
+ });
103
+ webSocketRouter.addRoutes(webSocketRoute);
104
+ }
105
+ webSocketRouter.bind(webSocket);
106
+ webSocketRouterGroup.addRouters(webSocketRouter);
107
+ //#endregion
108
+ return Object.freeze({
109
+ httpRouterGroup: httpRouterGroup,
110
+ webSocketRouterGroup: webSocketRouterGroup
111
+ });
112
+ };
113
+ const argumentsResolution = async (data, zodSchema, argumentIndex, funcName) => {
56
114
  try {
57
115
  const validation = await zodSchema.safeParseAsync(data);
58
116
  if (!validation.success) {
@@ -83,7 +141,7 @@ export const argumentsResolution = async (data, zodSchema, argumentIndex, funcNa
83
141
  });
84
142
  }
85
143
  };
86
- export const moduleResolution = async (module, options) => {
144
+ const moduleResolution = async (module, options) => {
87
145
  if (!Reflect.getOwnMetadataKeys(module).includes(moduleKey)) {
88
146
  throw Error(`${module.name} is not a module.`);
89
147
  }
@@ -92,8 +150,9 @@ export const moduleResolution = async (module, options) => {
92
150
  if (!moduleMetadata) {
93
151
  return;
94
152
  }
95
- const { loaders, middlewares, guards, dispatchers, controllers, dependencies, prefix: modulePrefix, config: moduleConfig } = moduleMetadata;
96
- // Configuration(s)
153
+ const { loaders, middlewares, guards, dispatchers, controllers, dependencies, webSockets, prefix: modulePrefix, config: moduleConfig } = moduleMetadata;
154
+ const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
155
+ //#region [Configuration(s)]
97
156
  const { config } = Object.freeze({
98
157
  config: {
99
158
  ...(typeof options.config !== "function" ? options.config : await options.config()),
@@ -104,19 +163,30 @@ export const moduleResolution = async (module, options) => {
104
163
  : await moduleConfig())
105
164
  }
106
165
  });
107
- // Register config like an injection
166
+ //#endregion
167
+ //#region [Register config like an injection]
108
168
  injector.set(configKey, config);
169
+ //#endregion
170
+ //#region [Run loader(s)]
109
171
  if (loaders) {
110
172
  const loaderFunctions = [];
111
173
  for (const [key, func] of Object.entries(loaders)) {
112
174
  loaderFunctions.push(async () => {
113
175
  try {
114
176
  const result = await func({ config });
115
- console.info(`INFO! Loader [${key}] initialized successfully.`);
177
+ console.info(`${ansiText(" INFO ", {
178
+ color: "white",
179
+ backgroundColor: "blue",
180
+ bold: true
181
+ })} Loader [${key}] initialized successfully.`);
116
182
  return result;
117
183
  }
118
184
  catch (error) {
119
- console.error(`WARNING! Loader [${key}] initialization failed.`);
185
+ console.error(`${ansiText(" WARN ", {
186
+ color: "yellow",
187
+ backgroundColor: "red",
188
+ bold: true
189
+ })} Loader [${key}] initialization failed.`);
120
190
  options.debug && console.error(error);
121
191
  throw error;
122
192
  }
@@ -128,31 +198,33 @@ export const moduleResolution = async (module, options) => {
128
198
  injector.set(key, value);
129
199
  }
130
200
  }
131
- // Dependencies
201
+ //#endregion
202
+ //#region [Dependencies]
132
203
  !dependencies || dependencies.map((dependency) => injector.get(dependency));
133
- // Middleware(s)
204
+ //#endregion
205
+ //#region [Middleware(s)]
134
206
  const startMiddlewareGroup = [];
135
207
  const endMiddlewareGroup = [];
136
- if (middlewares) {
137
- for (let i = 0; i < middlewares.length; i++) {
138
- const instance = injector.get(middlewares[i]);
208
+ middlewares &&
209
+ middlewares.forEach((middleware) => {
210
+ const instance = injector.get(middleware);
139
211
  if (instance.start && typeof instance.start === "function") {
140
212
  startMiddlewareGroup.push(Object.freeze({
141
- class: middlewares[i],
213
+ class: middleware,
142
214
  funcName: "start",
143
215
  func: instance.start.bind(instance)
144
216
  }));
145
217
  }
146
218
  if (instance.end && typeof instance.end === "function") {
147
219
  endMiddlewareGroup.push(Object.freeze({
148
- class: middlewares[i],
220
+ class: middleware,
149
221
  funcName: "end",
150
222
  func: instance.end.bind(instance)
151
223
  }));
152
224
  }
153
- }
154
- }
155
- // Guard(s)
225
+ });
226
+ //#endregion
227
+ //#region [Guard(s)]
156
228
  const guardGroup = !guards
157
229
  ? []
158
230
  : guards.map((guard) => {
@@ -163,32 +235,51 @@ export const moduleResolution = async (module, options) => {
163
235
  func: guardInstance.enforce.bind(guardInstance)
164
236
  });
165
237
  });
166
- // Before dispatcher(s)
238
+ //#endregion
239
+ //#region [Before dispatcher(s)]
167
240
  const openDispatcherGroup = [];
168
241
  const closeDispatcherGroup = [];
169
- if (dispatchers) {
170
- for (let i = 0; i < dispatchers.length; i++) {
171
- const instance = injector.get(dispatchers[i]);
242
+ dispatchers &&
243
+ dispatchers.forEach((dispatcher) => {
244
+ const instance = injector.get(dispatcher);
172
245
  if (instance.open && typeof instance.open === "function") {
173
246
  openDispatcherGroup.push(Object.freeze({
174
- class: dispatchers[i],
247
+ class: dispatcher,
175
248
  funcName: "open",
176
249
  func: instance.open.bind(instance)
177
250
  }));
178
251
  }
179
252
  if (instance.close && typeof instance.close === "function") {
180
253
  closeDispatcherGroup.push(Object.freeze({
181
- class: dispatchers[i],
254
+ class: dispatcher,
182
255
  funcName: "close",
183
256
  func: instance.close.bind(instance)
184
257
  }));
185
258
  }
186
- }
187
- }
188
- // Controller(s)
189
- const routerGroup = new RouterGroup();
259
+ });
260
+ //#endregion
261
+ //#region [Controller(s)]
262
+ const controllerRouterGroup = new HttpRouterGroup();
190
263
  controllers &&
191
- controllers.map((controllerConstructor) => controllerCreator(controllerConstructor, routerGroup, injector, `${options.prefix || ""}/${modulePrefix || ""}`));
264
+ controllers.forEach((controllerConstructor) => controllerCreator({
265
+ controllerConstructor,
266
+ httpRouterGroup: controllerRouterGroup,
267
+ injector: injector,
268
+ prefix: fullPrefix
269
+ }));
270
+ //#endregion
271
+ //#region [WebSocket(s)]
272
+ const webSocketHttpRouterGroup = new HttpRouterGroup();
273
+ const webSocketRouterGroup = new WebSocketRouterGroup();
274
+ webSockets &&
275
+ webSockets.forEach((webSocket) => webSocketCreator({
276
+ webSocketConstructor: webSocket,
277
+ httpRouterGroup: webSocketHttpRouterGroup,
278
+ webSocketRouterGroup: webSocketRouterGroup,
279
+ injector,
280
+ prefix: fullPrefix
281
+ }));
282
+ //#endregion
192
283
  return Object.freeze({
193
284
  prefix: moduleMetadata.prefix,
194
285
  injector: injector,
@@ -197,12 +288,43 @@ export const moduleResolution = async (module, options) => {
197
288
  guardGroup,
198
289
  openDispatcherGroup,
199
290
  closeDispatcherGroup,
200
- routerGroup
291
+ controllerRouterGroup,
292
+ webSocketHttpRouterGroup,
293
+ webSocketRouterGroup
201
294
  });
202
295
  };
203
- const fetcher = async (bun, bool) => {
204
- const { query, responseHeaders, route: { parameters, model }, moduleResolution: { startMiddlewareGroup, endMiddlewareGroup, guardGroup, openDispatcherGroup, closeDispatcherGroup } } = bool;
296
+ const webSocketFetcher = async (bun, bool) => {
297
+ const { request, server } = bun;
298
+ const { query, responseHeaders, route: { model } } = bool;
299
+ // Execute controller action
300
+ const isUpgrade = await model.func(...[server, request, query]);
301
+ if (typeof isUpgrade !== "boolean") {
302
+ return responseConverter(new Response(JSON.stringify({
303
+ httpCode: 500,
304
+ message: "Can not detect webSocket upgrade result.",
305
+ data: undefined
306
+ }), {
307
+ status: 500,
308
+ statusText: "Internal server error.",
309
+ headers: responseHeaders
310
+ }));
311
+ }
312
+ if (!isUpgrade) {
313
+ return responseConverter(new Response(JSON.stringify({
314
+ httpCode: 500,
315
+ message: "Can not upgrade.",
316
+ data: undefined
317
+ }), {
318
+ status: 500,
319
+ statusText: "Internal server error.",
320
+ headers: responseHeaders
321
+ }));
322
+ }
323
+ return isUpgrade;
324
+ };
325
+ const httpFetcher = async (bun, bool) => {
205
326
  const { request, server: _server } = bun;
327
+ const { query, responseHeaders, route: { parameters, model }, moduleResolution: { startMiddlewareGroup, endMiddlewareGroup, guardGroup, openDispatcherGroup, closeDispatcherGroup } } = bool;
206
328
  const context = {
207
329
  [requestHeadersArgsKey]: request.headers,
208
330
  [responseHeadersArgsKey]: responseHeaders,
@@ -240,7 +362,9 @@ const fetcher = async (bun, bool) => {
240
362
  : await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
241
363
  break;
242
364
  case contextArgsKey:
243
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
365
+ args[argsMetadata.index] = !argsMetadata.key
366
+ ? contextHook
367
+ : contextHook.get(argsMetadata.key);
244
368
  break;
245
369
  case requestHeadersArgsKey:
246
370
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -268,7 +392,9 @@ const fetcher = async (bun, bool) => {
268
392
  ? !(argsMetadata.type in context)
269
393
  ? undefined
270
394
  : context[argsMetadata.type]
271
- : await argumentsResolution(!(argsMetadata.type in context) ? undefined : context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
395
+ : await argumentsResolution(!(argsMetadata.type in context)
396
+ ? undefined
397
+ : context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
272
398
  break;
273
399
  }
274
400
  }
@@ -297,7 +423,9 @@ const fetcher = async (bun, bool) => {
297
423
  : await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
298
424
  break;
299
425
  case contextArgsKey:
300
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
426
+ args[argsMetadata.index] = !argsMetadata.key
427
+ ? contextHook
428
+ : contextHook.get(argsMetadata.key);
301
429
  break;
302
430
  case requestHeadersArgsKey:
303
431
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -356,7 +484,9 @@ const fetcher = async (bun, bool) => {
356
484
  : await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
357
485
  break;
358
486
  case contextArgsKey:
359
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
487
+ args[argsMetadata.index] = !argsMetadata.key
488
+ ? contextHook
489
+ : contextHook.get(argsMetadata.key);
360
490
  break;
361
491
  case requestHeadersArgsKey:
362
492
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -460,7 +590,9 @@ const fetcher = async (bun, bool) => {
460
590
  : await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
461
591
  break;
462
592
  case contextArgsKey:
463
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
593
+ args[argsMetadata.index] = !argsMetadata.key
594
+ ? contextHook
595
+ : contextHook.get(argsMetadata.key);
464
596
  break;
465
597
  case requestHeadersArgsKey:
466
598
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -515,7 +647,9 @@ const fetcher = async (bun, bool) => {
515
647
  : await argumentsResolution(await request[argsMetadata.parser || "json"](), argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
516
648
  break;
517
649
  case contextArgsKey:
518
- args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
650
+ args[argsMetadata.index] = !argsMetadata.key
651
+ ? contextHook
652
+ : contextHook.get(argsMetadata.key);
519
653
  break;
520
654
  case requestHeadersArgsKey:
521
655
  args[argsMetadata.index] = !argsMetadata.zodSchema
@@ -543,7 +677,9 @@ const fetcher = async (bun, bool) => {
543
677
  ? !(argsMetadata.type in context)
544
678
  ? undefined
545
679
  : context[argsMetadata.type]
546
- : await argumentsResolution(!(argsMetadata.type in context) ? undefined : context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
680
+ : await argumentsResolution(!(argsMetadata.type in context)
681
+ ? undefined
682
+ : context[argsMetadata.type], argsMetadata.zodSchema, argsMetadata.index, collection.funcName);
547
683
  break;
548
684
  }
549
685
  }
@@ -567,6 +703,7 @@ const fetcher = async (bun, bool) => {
567
703
  };
568
704
  export const BoolFactory = async (modules, options) => {
569
705
  try {
706
+ const staticMap = new Map();
570
707
  const modulesConverted = !Array.isArray(modules) ? [modules] : modules;
571
708
  const { allowLogsMethods, staticOption, allowOrigins, allowMethods, allowCredentials, allowHeaders } = Object.freeze({
572
709
  allowLogsMethods: options?.log?.methods,
@@ -578,9 +715,18 @@ export const BoolFactory = async (modules, options) => {
578
715
  ? ["*"]
579
716
  : options.cors.origins
580
717
  : [options.cors.origins !== "*" ? options.cors.origins : "*"],
581
- allowMethods: options.cors?.methods || ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
718
+ allowMethods: options.cors?.methods || [
719
+ "GET",
720
+ "POST",
721
+ "PUT",
722
+ "PATCH",
723
+ "DELETE",
724
+ "OPTIONS"
725
+ ],
582
726
  allowCredentials: !options.cors?.credentials ? false : true,
583
- allowHeaders: !options.cors?.headers || options.cors.headers.includes("*") ? ["*"] : options.cors.headers
727
+ allowHeaders: !options.cors?.headers || options.cors.headers.includes("*")
728
+ ? ["*"]
729
+ : options.cors.headers
584
730
  });
585
731
  const moduleResolutions = await Promise.all(modulesConverted.map((moduleConverted) => moduleResolution(moduleConverted, options)));
586
732
  const availableModuleResolutions = moduleResolutions.filter((moduleResolution) => typeof moduleResolution !== "undefined");
@@ -588,9 +734,16 @@ export const BoolFactory = async (modules, options) => {
588
734
  ...new Set(availableModuleResolutions.map((availableModuleResolution) => availableModuleResolution.prefix))
589
735
  ];
590
736
  if (prefixs.length !== availableModuleResolutions.length) {
591
- throw Error(`Module prefix should be unique.`);
737
+ throw Error("Module prefix should be unique.");
592
738
  }
593
- Bun.serve({
739
+ const webSocketsMap = new Map();
740
+ for (const availableModuleResolution of availableModuleResolutions) {
741
+ const webSocketMap = availableModuleResolution.webSocketRouterGroup.execute();
742
+ for (const [key, metadata] of webSocketMap.entries()) {
743
+ webSocketsMap.set(key, metadata);
744
+ }
745
+ }
746
+ const server = Bun.serve({
594
747
  port: options.port,
595
748
  fetch: async (request, server) => {
596
749
  const start = performance.now();
@@ -600,10 +753,50 @@ export const BoolFactory = async (modules, options) => {
600
753
  const method = request.method.toUpperCase();
601
754
  const responseHeaders = new Headers();
602
755
  try {
603
- allowCredentials && responseHeaders.set("Access-Control-Allow-Credentials", "true");
756
+ const isUpgradable = isWebSocketUpgrade(request);
757
+ let collection;
758
+ if (isUpgradable) {
759
+ for (const availableModuleResolution of availableModuleResolutions) {
760
+ const routeResult = availableModuleResolution.webSocketHttpRouterGroup.find(url.pathname, request.method);
761
+ if (routeResult) {
762
+ collection = Object.freeze({
763
+ route: routeResult,
764
+ resolution: availableModuleResolution
765
+ });
766
+ break;
767
+ }
768
+ }
769
+ if (!collection) {
770
+ return responseConverter(new Response(JSON.stringify({
771
+ httpCode: 404,
772
+ message: "Route not found",
773
+ data: undefined
774
+ }), {
775
+ status: 404,
776
+ statusText: "Not found.",
777
+ headers: responseHeaders
778
+ }));
779
+ }
780
+ const upgradeResult = await webSocketFetcher({
781
+ request,
782
+ server
783
+ }, {
784
+ query: query,
785
+ responseHeaders: responseHeaders,
786
+ route: collection.route,
787
+ moduleResolution: collection.resolution
788
+ });
789
+ return upgradeResult instanceof Response ? upgradeResult : undefined;
790
+ }
791
+ allowCredentials &&
792
+ responseHeaders.set("Access-Control-Allow-Credentials", "true");
604
793
  responseHeaders.set("Access-Control-Allow-Methods", allowMethods.join(", "));
605
794
  responseHeaders.set("Access-Control-Allow-Headers", allowHeaders.join(", "));
606
- responseHeaders.set("Access-Control-Allow-Origin", allowOrigins.includes("*") ? "*" : !allowOrigins.includes(origin) ? allowOrigins[0] : origin);
795
+ responseHeaders.set("Access-Control-Allow-Origin", allowOrigins.includes("*")
796
+ ? "*"
797
+ : !allowOrigins.includes(origin)
798
+ ? allowOrigins[0]
799
+ : origin);
607
800
  if (!allowMethods.includes(method)) {
608
801
  return responseConverter(new Response(undefined, {
609
802
  status: 405,
@@ -625,29 +818,60 @@ export const BoolFactory = async (modules, options) => {
625
818
  }));
626
819
  }
627
820
  if (staticOption) {
628
- const file = Bun.file(`${staticOption.path}/${url.pathname}`);
629
- const isFileExists = await file.exists();
630
- if (isFileExists) {
631
- if (staticOption.headers) {
632
- for (const [key, value] of Object.entries(staticOption.headers)) {
633
- responseHeaders.set(key, value);
821
+ const { path, headers, cacheTimeInSeconds } = staticOption;
822
+ const pathname = `${path}/${url.pathname}`;
823
+ const cachedFile = staticMap.get(pathname);
824
+ if (!cachedFile) {
825
+ const file = Bun.file(pathname);
826
+ const isFileExists = await file.exists();
827
+ if (isFileExists) {
828
+ if (headers) {
829
+ for (const [key, value] of Object.entries(headers)) {
830
+ responseHeaders.set(key, value);
831
+ }
634
832
  }
833
+ responseHeaders.set("Content-Type", file.type);
834
+ return responseConverter(new Response(await file.arrayBuffer(), {
835
+ status: 200,
836
+ statusText: "SUCCESS",
837
+ headers: responseHeaders
838
+ }));
839
+ }
840
+ }
841
+ else {
842
+ const isExpired = new Date() > cachedFile.expiredAt;
843
+ if (isExpired) {
844
+ staticMap.delete(pathname);
845
+ }
846
+ const file = !isExpired ? cachedFile.file : Bun.file(pathname);
847
+ const isFileExists = await file.exists();
848
+ if (isFileExists) {
849
+ staticMap.set(pathname, Object.freeze({
850
+ expiredAt: TimeAdd(new Date(), typeof cacheTimeInSeconds !== "number"
851
+ ? DEFAULT_STATIC_CACHE_TIME_IN_SECONDS
852
+ : cacheTimeInSeconds, ETimeUnit.seconds),
853
+ file: file
854
+ }));
855
+ if (headers) {
856
+ for (const [key, value] of Object.entries(headers)) {
857
+ responseHeaders.set(key, value);
858
+ }
859
+ }
860
+ responseHeaders.set("Content-Type", file.type);
861
+ return responseConverter(new Response(await file.arrayBuffer(), {
862
+ status: 200,
863
+ statusText: "SUCCESS",
864
+ headers: responseHeaders
865
+ }));
635
866
  }
636
- responseHeaders.set("Content-Type", file.type);
637
- return responseConverter(new Response(await file.arrayBuffer(), {
638
- status: 200,
639
- statusText: "SUCCESS",
640
- headers: responseHeaders
641
- }));
642
867
  }
643
868
  }
644
- let collection;
645
- for (let i = 0; i < availableModuleResolutions.length; i++) {
646
- const routeResult = availableModuleResolutions[i].routerGroup.find(url.pathname, request.method);
869
+ for (const availableModuleResolution of availableModuleResolutions) {
870
+ const routeResult = availableModuleResolution.controllerRouterGroup.find(url.pathname, request.method);
647
871
  if (routeResult) {
648
872
  collection = Object.freeze({
649
873
  route: routeResult,
650
- resolution: availableModuleResolutions[i]
874
+ resolution: availableModuleResolution
651
875
  });
652
876
  break;
653
877
  }
@@ -657,9 +881,13 @@ export const BoolFactory = async (modules, options) => {
657
881
  httpCode: 404,
658
882
  message: "Route not found",
659
883
  data: undefined
660
- })));
884
+ }), {
885
+ status: 404,
886
+ statusText: "Not found.",
887
+ headers: responseHeaders
888
+ }));
661
889
  }
662
- return await fetcher({
890
+ return await httpFetcher({
663
891
  request,
664
892
  server
665
893
  }, {
@@ -676,16 +904,162 @@ export const BoolFactory = async (modules, options) => {
676
904
  finally {
677
905
  if (allowLogsMethods) {
678
906
  const end = performance.now();
679
- const convertedPID = `${process.pid}`.yellow;
680
- const convertedMethod = `${request.method.yellow}`.bgBlue;
681
- const convertedReqIp = `${request.headers.get("x-forwarded-for") ||
907
+ const pathname = ansiText(url.pathname, { color: "blue" });
908
+ const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
909
+ const convertedMethod = ansiText(request.method, {
910
+ color: "yellow",
911
+ backgroundColor: "blue"
912
+ });
913
+ const convertedReqIp = ansiText(`${request.headers.get("x-forwarded-for") ||
682
914
  request.headers.get("x-real-ip") ||
683
915
  server.requestIP(request)?.address ||
684
- "<Unknown>"}`.yellow;
685
- const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
916
+ "<Unknown>"}`, {
917
+ color: "yellow"
918
+ });
919
+ const convertedTime = ansiText(`${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`, {
920
+ color: "yellow",
921
+ backgroundColor: "blue"
922
+ });
686
923
  allowLogsMethods.includes(request.method.toUpperCase()) &&
687
- console.info(`PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${new URL(request.url).pathname.blue} - Time: ${convertedTime}`);
924
+ console.info(`PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`);
925
+ }
926
+ }
927
+ },
928
+ websocket: {
929
+ open: (connection) => {
930
+ const pathnameKey = `${connection.data.pathname}:::open`;
931
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
932
+ if (!handlerMetadata) {
933
+ return;
934
+ }
935
+ const argumentsMetadata = handlerMetadata.arguments || {};
936
+ const args = [];
937
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
938
+ switch (argumentMetadata.type) {
939
+ case webSocketConnectionArgsKey:
940
+ args[argumentMetadata.index] = connection;
941
+ break;
942
+ case webSocketServerArgsKey:
943
+ args[argumentMetadata.index] = server;
944
+ break;
945
+ }
946
+ }
947
+ handlerMetadata.descriptor.value(...args);
948
+ },
949
+ close: (connection, code, reason) => {
950
+ const pathnameKey = `${connection.data.pathname}:::close`;
951
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
952
+ if (!handlerMetadata) {
953
+ return;
954
+ }
955
+ const argumentsMetadata = handlerMetadata.arguments || {};
956
+ const args = [];
957
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
958
+ switch (argumentMetadata.type) {
959
+ case webSocketConnectionArgsKey:
960
+ args[argumentMetadata.index] = connection;
961
+ break;
962
+ case webSocketServerArgsKey:
963
+ args[argumentMetadata.index] = server;
964
+ break;
965
+ case webSocketCloseCodeArgsKey:
966
+ args[argumentMetadata.index] = code;
967
+ break;
968
+ case webSocketCloseReasonArgsKey:
969
+ args[argumentMetadata.index] = reason;
970
+ break;
971
+ }
972
+ }
973
+ handlerMetadata.descriptor.value(...args);
974
+ },
975
+ message: (connection, message) => {
976
+ const pathnameKey = `${connection.data.pathname}:::message`;
977
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
978
+ if (!handlerMetadata) {
979
+ return;
980
+ }
981
+ const argumentsMetadata = handlerMetadata.arguments || {};
982
+ const args = [];
983
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
984
+ switch (argumentMetadata.type) {
985
+ case webSocketConnectionArgsKey:
986
+ args[argumentMetadata.index] = connection;
987
+ break;
988
+ case webSocketMessageArgsKey:
989
+ args[argumentMetadata.index] = message;
990
+ break;
991
+ case webSocketServerArgsKey:
992
+ args[argumentMetadata.index] = server;
993
+ break;
994
+ }
995
+ }
996
+ handlerMetadata.descriptor.value(...args);
997
+ },
998
+ drain: (connection) => {
999
+ const pathnameKey = `${connection.data.pathname}:::drain`;
1000
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1001
+ if (!handlerMetadata) {
1002
+ return;
1003
+ }
1004
+ const argumentsMetadata = handlerMetadata.arguments || {};
1005
+ const args = [];
1006
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1007
+ switch (argumentMetadata.type) {
1008
+ case webSocketConnectionArgsKey:
1009
+ args[argumentMetadata.index] = connection;
1010
+ break;
1011
+ case webSocketServerArgsKey:
1012
+ args[argumentMetadata.index] = server;
1013
+ break;
1014
+ }
1015
+ }
1016
+ handlerMetadata.descriptor.value(...args);
1017
+ },
1018
+ ping: (connection, data) => {
1019
+ const pathnameKey = `${connection.data.pathname}:::ping`;
1020
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1021
+ if (!handlerMetadata) {
1022
+ return;
1023
+ }
1024
+ const argumentsMetadata = handlerMetadata.arguments || {};
1025
+ const args = [];
1026
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1027
+ switch (argumentMetadata.type) {
1028
+ case webSocketConnectionArgsKey:
1029
+ args[argumentMetadata.index] = connection;
1030
+ break;
1031
+ case webSocketServerArgsKey:
1032
+ args[argumentMetadata.index] = server;
1033
+ break;
1034
+ case webSocketMessageArgsKey:
1035
+ args[argumentMetadata.index] = data;
1036
+ break;
1037
+ }
1038
+ }
1039
+ handlerMetadata.descriptor.value(...args);
1040
+ },
1041
+ pong: (connection, data) => {
1042
+ const pathnameKey = `${connection.data.pathname}:::pong`;
1043
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
1044
+ if (!handlerMetadata) {
1045
+ return;
1046
+ }
1047
+ const argumentsMetadata = handlerMetadata.arguments || {};
1048
+ const args = [];
1049
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
1050
+ switch (argumentMetadata.type) {
1051
+ case webSocketConnectionArgsKey:
1052
+ args[argumentMetadata.index] = connection;
1053
+ break;
1054
+ case webSocketServerArgsKey:
1055
+ args[argumentMetadata.index] = server;
1056
+ break;
1057
+ case webSocketMessageArgsKey:
1058
+ args[argumentMetadata.index] = data;
1059
+ break;
1060
+ }
688
1061
  }
1062
+ handlerMetadata.descriptor.value(...args);
689
1063
  }
690
1064
  }
691
1065
  });