@bool-ts/core 1.9.28 → 2.0.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 (44) hide show
  1. package/dist/decorators/arguments.d.ts +19 -20
  2. package/dist/decorators/container.d.ts +10 -11
  3. package/dist/decorators/http.d.ts +11 -11
  4. package/dist/decorators/index.d.ts +1 -2
  5. package/dist/entities/application.d.ts +111 -0
  6. package/dist/entities/context.d.ts +14 -0
  7. package/dist/entities/httpRoute.d.ts +18 -9
  8. package/dist/entities/httpRouter.d.ts +13 -0
  9. package/dist/entities/httpRouterGroup.d.ts +6 -0
  10. package/dist/entities/index.d.ts +2 -0
  11. package/dist/entities/injector.d.ts +29 -0
  12. package/dist/entities/validationFailed.d.ts +5 -0
  13. package/dist/entities/webSocketRoute.d.ts +1 -0
  14. package/dist/entities/webSocketRouter.d.ts +1 -0
  15. package/dist/entities/webSocketRouterGroup.d.ts +1 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +6 -6
  18. package/dist/index.js.map +22 -21
  19. package/dist/interfaces/customValidator.d.ts +4 -0
  20. package/dist/interfaces/index.d.ts +1 -0
  21. package/dist/producers/factory.d.ts +3 -1
  22. package/dist/producers/index.d.ts +1 -1
  23. package/package.json +2 -2
  24. package/src/decorators/arguments.ts +44 -39
  25. package/src/decorators/container.ts +12 -12
  26. package/src/decorators/http.ts +8 -8
  27. package/src/decorators/index.ts +1 -2
  28. package/src/entities/application.ts +2236 -0
  29. package/src/{producers → entities}/context.ts +2 -0
  30. package/src/entities/httpRoute.ts +18 -9
  31. package/src/entities/httpRouter.ts +13 -0
  32. package/src/entities/httpRouterGroup.ts +10 -4
  33. package/src/entities/index.ts +2 -0
  34. package/src/entities/validationFailed.ts +5 -0
  35. package/src/entities/webSocketRoute.ts +2 -0
  36. package/src/entities/webSocketRouter.ts +2 -0
  37. package/src/entities/webSocketRouterGroup.ts +2 -0
  38. package/src/index.ts +1 -0
  39. package/src/interfaces/customValidator.ts +10 -0
  40. package/src/interfaces/index.ts +1 -0
  41. package/src/producers/factory.ts +7 -2037
  42. package/src/producers/index.ts +1 -1
  43. package/src/decorators/zodSchema.ts +0 -21
  44. /package/src/{producers → entities}/injector.ts +0 -0
@@ -0,0 +1,2236 @@
1
+ import type { BunFile, Server } from "bun";
2
+ import type {
3
+ TArgumentsMetadataCollection,
4
+ TContainerMetadata,
5
+ TControllerMetadata,
6
+ TModuleMetadata,
7
+ TWebSocketEventHandlerMetadata,
8
+ TWebSocketMetadata
9
+ } from "../decorators";
10
+ import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
11
+ import type { IGuard, IInterceptor, IMiddleware } from "../interfaces";
12
+ import type { TConstructor } from "../ultils";
13
+
14
+ import { ETimeUnit, add as TimeAdd } from "@bool-ts/date-time";
15
+ import { parse as QsParse } from "qs";
16
+ import type { ICustomValidator } from "../interfaces/customValidator";
17
+ import {
18
+ argumentsKey,
19
+ configKey,
20
+ containerKey,
21
+ contextArgsKey,
22
+ controllerKey,
23
+ httpServerArgsKey,
24
+ moduleKey,
25
+ paramArgsKey,
26
+ paramsArgsKey,
27
+ queryArgsKey,
28
+ requestArgsKey,
29
+ requestBodyArgsKey,
30
+ requestHeaderArgsKey,
31
+ requestHeadersArgsKey,
32
+ responseBodyArgsKey,
33
+ responseHeadersArgsKey,
34
+ responseStatusArgsKey,
35
+ responseStatusTextArgsKey,
36
+ routeModelArgsKey,
37
+ webSocketCloseCodeArgsKey,
38
+ webSocketCloseReasonArgsKey,
39
+ webSocketConnectionArgsKey,
40
+ webSocketKey,
41
+ webSocketMessageArgsKey,
42
+ webSocketServerArgsKey
43
+ } from "../keys";
44
+ import { ansiText, isWebSocketUpgrade } from "../ultils";
45
+ import { Context } from "./context";
46
+ import { HttpRouter } from "./httpRouter";
47
+ import { HttpRouterGroup } from "./httpRouterGroup";
48
+ import { Injector } from "./injector";
49
+ import ValidationFailed from "./validationFailed";
50
+ import { WebSocketRoute } from "./webSocketRoute";
51
+ import { WebSocketRouter } from "./webSocketRouter";
52
+ import { WebSocketRouterGroup } from "./webSocketRouterGroup";
53
+
54
+ type TParamsType = Record<string, string>;
55
+
56
+ type TApplicationOptions = Required<{
57
+ port: number;
58
+ }> &
59
+ Partial<{
60
+ config: Record<string | symbol, any> | (() => Record<string | symbol, any>);
61
+ prefix: string;
62
+ debug: boolean;
63
+ log: Partial<{
64
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
65
+ }>;
66
+ queryParser: Parameters<typeof QsParse>[1];
67
+ static: Required<{
68
+ path: string;
69
+ }> &
70
+ Partial<{
71
+ headers: TParamsType;
72
+ cacheTimeInSeconds: number;
73
+ }>;
74
+ cors: Partial<{
75
+ credentials: boolean;
76
+ origins: string | Array<string>;
77
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
78
+ headers: Array<string>;
79
+ }>;
80
+ }>;
81
+
82
+ type TGroupElementModel<
83
+ TFuncName extends keyof TClass,
84
+ TClass extends Object = Object,
85
+ TFunc = TClass[TFuncName]
86
+ > = Readonly<{
87
+ class: TClass;
88
+ func: TFunc;
89
+ funcName: TFuncName;
90
+ argumentsMetadata: TArgumentsMetadataCollection;
91
+ }>;
92
+
93
+ type TStaticMap = Map<
94
+ string,
95
+ Readonly<{
96
+ expiredAt: Date;
97
+ file: BunFile;
98
+ }>
99
+ >;
100
+
101
+ type TResolutedOptions = Readonly<{
102
+ allowLogsMethods: ("GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS")[];
103
+ allowOrigins: string[];
104
+ allowMethods: string[];
105
+ allowHeaders: string[];
106
+ allowCredentials: boolean;
107
+ staticOption?: Required<{
108
+ path: string;
109
+ }> &
110
+ Partial<{
111
+ headers: TParamsType;
112
+ cacheTimeInSeconds: number;
113
+ }>;
114
+ }>;
115
+
116
+ type TWebSocketUpgradeData = {
117
+ pathname: string;
118
+ method: string;
119
+ query: Record<string, unknown>;
120
+ };
121
+
122
+ type TPreLaunch =
123
+ | undefined
124
+ | Readonly<{
125
+ containerMetadata: TContainerMetadata;
126
+ modulesConverted: Array<TConstructor<unknown>>;
127
+ resolutedContainer?: Awaited<ReturnType<Application["containerResolution"]>>;
128
+ resolutedModules: Array<Awaited<ReturnType<Application["moduleResolution"]>>>;
129
+ webSocketsMap: Map<string, TWebSocketEventHandlerMetadata>;
130
+ }>;
131
+
132
+ type TValidator = undefined | ICustomValidator;
133
+
134
+ export class Application<TRootClass extends Object = Object> {
135
+ #preLaunchData: TPreLaunch;
136
+ #inputedConstructorKeys: any[];
137
+ #injector = new Injector();
138
+ #staticMap: TStaticMap = new Map();
139
+ #resolutedOptions: TResolutedOptions;
140
+ #staticCacheTimeInSecond: number = 900;
141
+ #customValidator: TValidator;
142
+
143
+ constructor(
144
+ private readonly classConstructor: TConstructor<TRootClass>,
145
+ private readonly options: TApplicationOptions
146
+ ) {
147
+ this.#inputedConstructorKeys = Reflect.getOwnMetadataKeys(classConstructor);
148
+
149
+ if (
150
+ !this.#inputedConstructorKeys.includes(containerKey) &&
151
+ !this.#inputedConstructorKeys.includes(moduleKey)
152
+ ) {
153
+ throw Error(
154
+ `Can not detect! ${classConstructor.name} class is not a container or module.`
155
+ );
156
+ }
157
+
158
+ this.#staticCacheTimeInSecond =
159
+ typeof options.static?.cacheTimeInSeconds !== "number"
160
+ ? 900
161
+ : options.static.cacheTimeInSeconds;
162
+
163
+ this.#resolutedOptions = Object.freeze({
164
+ allowLogsMethods: this.options?.log?.methods || [],
165
+ staticOption: this.options.static,
166
+ allowOrigins: !this.options.cors?.origins
167
+ ? ["*"]
168
+ : typeof this.options.cors.origins !== "string"
169
+ ? this.options.cors.origins.includes("*") || this.options.cors.origins.length < 1
170
+ ? ["*"]
171
+ : this.options.cors.origins
172
+ : [this.options.cors.origins !== "*" ? this.options.cors.origins : "*"],
173
+ allowMethods: this.options.cors?.methods || [
174
+ "GET",
175
+ "POST",
176
+ "PUT",
177
+ "PATCH",
178
+ "DELETE",
179
+ "OPTIONS"
180
+ ],
181
+ allowCredentials: !this.options.cors?.credentials ? false : true,
182
+ allowHeaders:
183
+ !this.options.cors?.headers || this.options.cors.headers.includes("*")
184
+ ? ["*"]
185
+ : this.options.cors.headers
186
+ });
187
+ }
188
+
189
+ public useValidator(validator: ICustomValidator) {
190
+ this.#customValidator = validator;
191
+ }
192
+
193
+ /**
194
+ *
195
+ * @returns
196
+ */
197
+ public async preLaunch(): Promise<NonNullable<TPreLaunch>> {
198
+ if (this.#preLaunchData) {
199
+ return this.#preLaunchData;
200
+ }
201
+
202
+ const containerMetadata: TContainerMetadata = !this.#inputedConstructorKeys.includes(
203
+ containerKey
204
+ )
205
+ ? undefined
206
+ : Reflect.getOwnMetadata(containerKey, this.classConstructor);
207
+
208
+ const modulesConverted = !this.#inputedConstructorKeys.includes(containerKey)
209
+ ? [this.classConstructor]
210
+ : containerMetadata?.modules || [];
211
+
212
+ const resolutedContainer = !containerMetadata
213
+ ? undefined
214
+ : await this.containerResolution({
215
+ containerClass: this.classConstructor,
216
+ options: this.options,
217
+ extendInjector: this.#injector
218
+ });
219
+
220
+ const resolutedModules = await Promise.all(
221
+ modulesConverted.map((moduleConverted) =>
222
+ this.moduleResolution({
223
+ moduleClass: moduleConverted,
224
+ options: this.options,
225
+ extendInjector: !resolutedContainer
226
+ ? this.#injector
227
+ : resolutedContainer.injector
228
+ })
229
+ )
230
+ );
231
+
232
+ const prefixs = [
233
+ ...new Set(
234
+ resolutedModules.map(
235
+ (availableModuleResolution) => availableModuleResolution.prefix
236
+ )
237
+ )
238
+ ];
239
+
240
+ if (prefixs.length !== resolutedModules.length) {
241
+ throw Error("Module prefix should be unique.");
242
+ }
243
+
244
+ const webSocketsMap = new Map<string, TWebSocketEventHandlerMetadata>();
245
+
246
+ for (const availableModuleResolution of resolutedModules) {
247
+ const webSocketMap = availableModuleResolution.webSocketRouterGroup.execute();
248
+
249
+ for (const [key, metadata] of webSocketMap.entries()) {
250
+ webSocketsMap.set(key, metadata);
251
+ }
252
+ }
253
+
254
+ const preLaunchData = Object.freeze({
255
+ containerMetadata,
256
+ modulesConverted,
257
+ resolutedContainer,
258
+ resolutedModules,
259
+ webSocketsMap
260
+ });
261
+
262
+ this.#preLaunchData = preLaunchData;
263
+
264
+ return preLaunchData;
265
+ }
266
+
267
+ /**
268
+ * Start listen app on a port
269
+ * @param port
270
+ */
271
+ public async listen() {
272
+ const {
273
+ allowLogsMethods,
274
+ allowOrigins,
275
+ allowMethods,
276
+ allowHeaders,
277
+ allowCredentials,
278
+ staticOption
279
+ } = this.#resolutedOptions;
280
+
281
+ const { resolutedContainer, resolutedModules, webSocketsMap } = await this.preLaunch();
282
+
283
+ const server = Bun.serve<TWebSocketUpgradeData, {}>({
284
+ port: this.options.port,
285
+ fetch: async (request, server) => {
286
+ const start = performance.now(),
287
+ url = new URL(request.url),
288
+ query = QsParse(url.searchParams.toString(), this.options.queryParser),
289
+ origin = request.headers.get("origin") || "*",
290
+ method = request.method.toUpperCase(),
291
+ responseHeaders = new Headers();
292
+
293
+ let context = new Context()
294
+ .setOptions({ isStatic: true })
295
+ .set(httpServerArgsKey, server)
296
+ .set(requestArgsKey, request)
297
+ .set(requestHeaderArgsKey, request.headers)
298
+ .set(responseHeadersArgsKey, responseHeaders)
299
+ .set(queryArgsKey, query);
300
+
301
+ try {
302
+ const isUpgradable = isWebSocketUpgrade(request);
303
+
304
+ let collection:
305
+ | undefined
306
+ | Required<{
307
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
308
+ resolution: NonNullable<
309
+ Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>
310
+ >;
311
+ }>;
312
+
313
+ if (isUpgradable) {
314
+ for (const availableModuleResolution of resolutedModules) {
315
+ const routeResult =
316
+ availableModuleResolution.webSocketHttpRouterGroup.find(
317
+ url.pathname,
318
+ request.method as keyof THttpMethods
319
+ );
320
+
321
+ if (routeResult) {
322
+ collection = Object.freeze({
323
+ route: routeResult,
324
+ resolution: availableModuleResolution
325
+ });
326
+ break;
327
+ }
328
+ }
329
+
330
+ if (!collection) {
331
+ return this.finalizeResponse(
332
+ new Response(
333
+ JSON.stringify({
334
+ httpCode: 404,
335
+ message: "Route not found",
336
+ data: undefined
337
+ }),
338
+ {
339
+ status: 404,
340
+ statusText: "Not found.",
341
+ headers: responseHeaders
342
+ }
343
+ )
344
+ );
345
+ }
346
+
347
+ const upgradeResult = await this.webSocketFetcher(
348
+ {
349
+ request,
350
+ server
351
+ },
352
+ {
353
+ query: query,
354
+ responseHeaders: responseHeaders,
355
+ route: collection.route,
356
+ moduleResolution: collection.resolution
357
+ }
358
+ );
359
+
360
+ return upgradeResult instanceof Response ? upgradeResult : undefined;
361
+ }
362
+
363
+ [
364
+ ...(!allowCredentials
365
+ ? []
366
+ : [
367
+ {
368
+ key: "Access-Control-Allow-Credentials",
369
+ value: "true"
370
+ }
371
+ ]),
372
+ {
373
+ key: "Access-Control-Allow-Origin",
374
+ value: allowOrigins.includes("*")
375
+ ? "*"
376
+ : !allowOrigins.includes(origin)
377
+ ? allowOrigins[0]
378
+ : origin
379
+ },
380
+ { key: "Access-Control-Allow-Methods", value: allowMethods.join(", ") },
381
+ { key: "Access-Control-Allow-Headers", value: allowHeaders.join(", ") }
382
+ ].forEach(({ key, value }) => responseHeaders.set(key, value));
383
+
384
+ if (!allowMethods.includes(method)) {
385
+ return this.finalizeResponse(
386
+ new Response(undefined, {
387
+ status: 405,
388
+ statusText: "Method Not Allowed.",
389
+ headers: responseHeaders
390
+ })
391
+ );
392
+ }
393
+
394
+ if (request.method.toUpperCase() === "OPTIONS") {
395
+ return this.finalizeResponse(
396
+ allowOrigins.includes("*") || allowOrigins.includes(origin)
397
+ ? new Response(undefined, {
398
+ status: 204,
399
+ statusText: "No Content.",
400
+ headers: responseHeaders
401
+ })
402
+ : new Response(undefined, {
403
+ status: 417,
404
+ statusText: "Expectation Failed.",
405
+ headers: responseHeaders
406
+ })
407
+ );
408
+ }
409
+
410
+ if (staticOption) {
411
+ const { path, headers } = staticOption;
412
+ const pathname = `${path}/${url.pathname}`;
413
+ const cachedFile = this.#staticMap.get(pathname);
414
+
415
+ if (!cachedFile) {
416
+ const file = Bun.file(pathname);
417
+ const isFileExists = await file.exists();
418
+
419
+ if (isFileExists) {
420
+ if (headers) {
421
+ for (const [key, value] of Object.entries(headers)) {
422
+ responseHeaders.set(key, value);
423
+ }
424
+ }
425
+
426
+ responseHeaders.set("Content-Type", file.type);
427
+
428
+ return this.finalizeResponse(
429
+ new Response(await file.arrayBuffer(), {
430
+ status: 200,
431
+ statusText: "SUCCESS",
432
+ headers: responseHeaders
433
+ })
434
+ );
435
+ }
436
+ } else {
437
+ const isExpired = new Date() > cachedFile.expiredAt;
438
+
439
+ if (isExpired) {
440
+ this.#staticMap.delete(pathname);
441
+ }
442
+
443
+ const file = !isExpired ? cachedFile.file : Bun.file(pathname);
444
+ const isFileExists = await file.exists();
445
+
446
+ if (isFileExists) {
447
+ this.#staticMap.set(
448
+ pathname,
449
+ Object.freeze({
450
+ expiredAt: TimeAdd(
451
+ new Date(),
452
+ this.#staticCacheTimeInSecond,
453
+ ETimeUnit.seconds
454
+ ),
455
+ file: file
456
+ })
457
+ );
458
+
459
+ if (headers) {
460
+ for (const [key, value] of Object.entries(headers)) {
461
+ responseHeaders.set(key, value);
462
+ }
463
+ }
464
+
465
+ responseHeaders.set("Content-Type", file.type);
466
+
467
+ return this.finalizeResponse(
468
+ new Response(await file.arrayBuffer(), {
469
+ status: 200,
470
+ statusText: "SUCCESS",
471
+ headers: responseHeaders
472
+ })
473
+ );
474
+ }
475
+ }
476
+ }
477
+
478
+ if (resolutedContainer) {
479
+ const { context: newContext } = await this.httpFetcher({
480
+ context: context,
481
+ resolutedMap: {
482
+ injector: resolutedContainer.injector,
483
+ startMiddlewareGroup: resolutedContainer.startMiddlewareGroup,
484
+ guardGroup: resolutedContainer.guardGroup
485
+ },
486
+ options: {
487
+ isContainer: true
488
+ }
489
+ });
490
+
491
+ context = newContext;
492
+ }
493
+
494
+ for (const availableModuleResolution of resolutedModules) {
495
+ const routeResult = availableModuleResolution.controllerRouterGroup.find(
496
+ url.pathname,
497
+ method as keyof THttpMethods
498
+ );
499
+
500
+ if (routeResult) {
501
+ collection = Object.freeze({
502
+ route: routeResult,
503
+ resolution: availableModuleResolution
504
+ });
505
+ break;
506
+ }
507
+ }
508
+
509
+ if (!collection) {
510
+ context
511
+ .setOptions({ isStatic: false })
512
+ .set(responseStatusArgsKey, 404)
513
+ .set(responseStatusTextArgsKey, "Not found.")
514
+ .set(
515
+ responseBodyArgsKey,
516
+ JSON.stringify({
517
+ httpCode: 404,
518
+ message: "Route not found",
519
+ data: undefined
520
+ })
521
+ );
522
+ } else {
523
+ const { context: newContext } = await this.httpFetcher({
524
+ context: context,
525
+ route: collection.route,
526
+ resolutedMap: collection.resolution
527
+ });
528
+
529
+ context = newContext;
530
+ }
531
+
532
+ if (resolutedContainer) {
533
+ const { context: newContext } = await this.httpFetcher({
534
+ context: context,
535
+ resolutedMap: {
536
+ injector: resolutedContainer.injector,
537
+ endMiddlewareGroup: resolutedContainer.endMiddlewareGroup
538
+ },
539
+ options: {
540
+ isContainer: true
541
+ }
542
+ });
543
+
544
+ context = newContext;
545
+ }
546
+
547
+ const latestResponseHeaders =
548
+ context.get<Headers | null | undefined>(responseHeadersArgsKey, {
549
+ isStatic: true
550
+ }) || new Headers(),
551
+ latestResponseBody =
552
+ context.get<unknown>(responseBodyArgsKey, { isStatic: false }) ||
553
+ undefined,
554
+ latestResponseStatus = context.get<unknown>(responseStatusArgsKey, {
555
+ isStatic: false
556
+ }),
557
+ latestResponseStatusText = context.get<unknown>(responseStatusArgsKey, {
558
+ isStatic: false
559
+ });
560
+
561
+ return this.serializeResponse({
562
+ status:
563
+ typeof latestResponseStatus !== "number"
564
+ ? undefined
565
+ : latestResponseStatus,
566
+ statusText:
567
+ typeof latestResponseStatusText !== "string"
568
+ ? undefined
569
+ : latestResponseStatusText,
570
+ headers: latestResponseHeaders,
571
+ data: latestResponseBody
572
+ });
573
+ } catch (error) {
574
+ this.options.debug && console.error(error);
575
+
576
+ return this.finalizeResponse(jsonErrorInfer(error, responseHeaders));
577
+ } finally {
578
+ if (allowLogsMethods) {
579
+ const end = performance.now();
580
+ const pathname = ansiText(url.pathname, { color: "blue" });
581
+ const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
582
+ const convertedMethod = ansiText(request.method, {
583
+ color: "yellow",
584
+ backgroundColor: "blue"
585
+ });
586
+ const convertedReqIp = ansiText(
587
+ `${
588
+ request.headers.get("x-forwarded-for") ||
589
+ request.headers.get("x-real-ip") ||
590
+ server.requestIP(request)?.address ||
591
+ "<Unknown>"
592
+ }`,
593
+ {
594
+ color: "yellow"
595
+ }
596
+ );
597
+ const convertedTime = ansiText(
598
+ `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`,
599
+ {
600
+ color: "yellow",
601
+ backgroundColor: "blue"
602
+ }
603
+ );
604
+
605
+ allowLogsMethods.includes(
606
+ request.method.toUpperCase() as (typeof allowLogsMethods)[number]
607
+ ) &&
608
+ console.info(
609
+ `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`
610
+ );
611
+ }
612
+ }
613
+ },
614
+ websocket: {
615
+ open: (connection) => {
616
+ const pathnameKey = `${connection.data.pathname}:::open`;
617
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
618
+
619
+ if (!handlerMetadata) {
620
+ return;
621
+ }
622
+
623
+ const argumentsMetadata = handlerMetadata.arguments || {};
624
+ const args: Array<unknown> = [];
625
+
626
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
627
+ switch (argumentMetadata.type) {
628
+ case webSocketConnectionArgsKey:
629
+ args[argumentMetadata.index] = connection;
630
+ break;
631
+ case webSocketServerArgsKey:
632
+ args[argumentMetadata.index] = server;
633
+ break;
634
+ }
635
+ }
636
+
637
+ handlerMetadata.descriptor.value(...args);
638
+ },
639
+ close: (connection, code, reason) => {
640
+ const pathnameKey = `${connection.data.pathname}:::close`;
641
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
642
+
643
+ if (!handlerMetadata) {
644
+ return;
645
+ }
646
+
647
+ const argumentsMetadata = handlerMetadata.arguments || {};
648
+ const args: Array<unknown> = [];
649
+
650
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
651
+ switch (argumentMetadata.type) {
652
+ case webSocketConnectionArgsKey:
653
+ args[argumentMetadata.index] = connection;
654
+ break;
655
+ case webSocketServerArgsKey:
656
+ args[argumentMetadata.index] = server;
657
+ break;
658
+ case webSocketCloseCodeArgsKey:
659
+ args[argumentMetadata.index] = code;
660
+ break;
661
+ case webSocketCloseReasonArgsKey:
662
+ args[argumentMetadata.index] = reason;
663
+ break;
664
+ }
665
+ }
666
+
667
+ handlerMetadata.descriptor.value(...args);
668
+ },
669
+ message: (connection, message) => {
670
+ const pathnameKey = `${connection.data.pathname}:::message`;
671
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
672
+
673
+ if (!handlerMetadata) {
674
+ return;
675
+ }
676
+
677
+ const argumentsMetadata = handlerMetadata.arguments || {};
678
+ const args: Array<unknown> = [];
679
+
680
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
681
+ switch (argumentMetadata.type) {
682
+ case webSocketConnectionArgsKey:
683
+ args[argumentMetadata.index] = connection;
684
+ break;
685
+ case webSocketMessageArgsKey:
686
+ args[argumentMetadata.index] = message;
687
+ break;
688
+ case webSocketServerArgsKey:
689
+ args[argumentMetadata.index] = server;
690
+ break;
691
+ }
692
+ }
693
+
694
+ handlerMetadata.descriptor.value(...args);
695
+ },
696
+ drain: (connection) => {
697
+ const pathnameKey = `${connection.data.pathname}:::drain`;
698
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
699
+
700
+ if (!handlerMetadata) {
701
+ return;
702
+ }
703
+
704
+ const argumentsMetadata = handlerMetadata.arguments || {};
705
+ const args: Array<unknown> = [];
706
+
707
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
708
+ switch (argumentMetadata.type) {
709
+ case webSocketConnectionArgsKey:
710
+ args[argumentMetadata.index] = connection;
711
+ break;
712
+ case webSocketServerArgsKey:
713
+ args[argumentMetadata.index] = server;
714
+ break;
715
+ }
716
+ }
717
+
718
+ handlerMetadata.descriptor.value(...args);
719
+ },
720
+ ping: (connection, data) => {
721
+ const pathnameKey = `${connection.data.pathname}:::ping`;
722
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
723
+
724
+ if (!handlerMetadata) {
725
+ return;
726
+ }
727
+
728
+ const argumentsMetadata = handlerMetadata.arguments || {};
729
+ const args: Array<unknown> = [];
730
+
731
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
732
+ switch (argumentMetadata.type) {
733
+ case webSocketConnectionArgsKey:
734
+ args[argumentMetadata.index] = connection;
735
+ break;
736
+ case webSocketServerArgsKey:
737
+ args[argumentMetadata.index] = server;
738
+ break;
739
+ case webSocketMessageArgsKey:
740
+ args[argumentMetadata.index] = data;
741
+ break;
742
+ }
743
+ }
744
+
745
+ handlerMetadata.descriptor.value(...args);
746
+ },
747
+ pong: (connection, data) => {
748
+ const pathnameKey = `${connection.data.pathname}:::pong`;
749
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
750
+
751
+ if (!handlerMetadata) {
752
+ return;
753
+ }
754
+
755
+ const argumentsMetadata = handlerMetadata.arguments || {};
756
+ const args: Array<unknown> = [];
757
+
758
+ for (const [_key, argumentMetadata] of Object.entries(argumentsMetadata)) {
759
+ switch (argumentMetadata.type) {
760
+ case webSocketConnectionArgsKey:
761
+ args[argumentMetadata.index] = connection;
762
+ break;
763
+ case webSocketServerArgsKey:
764
+ args[argumentMetadata.index] = server;
765
+ break;
766
+ case webSocketMessageArgsKey:
767
+ args[argumentMetadata.index] = data;
768
+ break;
769
+ }
770
+ }
771
+
772
+ handlerMetadata.descriptor.value(...args);
773
+ }
774
+ }
775
+ });
776
+ }
777
+
778
+ /**
779
+ *
780
+ * @param param0
781
+ * @returns
782
+ */
783
+ private async containerResolution({
784
+ containerClass,
785
+ options,
786
+ extendInjector
787
+ }: {
788
+ containerClass: TConstructor<unknown>;
789
+ options: TApplicationOptions;
790
+ extendInjector: Injector;
791
+ }) {
792
+ if (!Reflect.getOwnMetadataKeys(containerClass).includes(containerKey)) {
793
+ throw Error(`${containerClass.name} is not a container.`);
794
+ }
795
+
796
+ const injector = new Injector(extendInjector);
797
+ const containerMetadata: TContainerMetadata = Reflect.getOwnMetadata(
798
+ containerKey,
799
+ containerClass
800
+ );
801
+
802
+ const {
803
+ loaders,
804
+ middlewares,
805
+ guards,
806
+ dependencies,
807
+ config: containerConfig
808
+ } = containerMetadata || {};
809
+
810
+ //#region [Configuration(s)]
811
+ const { config } = Object.freeze({
812
+ config: {
813
+ ...(typeof options.config !== "function" ? options.config : await options.config()),
814
+ ...(typeof containerConfig !== "function"
815
+ ? typeof containerConfig !== "object"
816
+ ? undefined
817
+ : "key" in containerConfig &&
818
+ "value" in containerConfig &&
819
+ typeof containerConfig.key === "symbol"
820
+ ? typeof containerConfig.value !== "function"
821
+ ? containerConfig.value
822
+ : await containerConfig.value()
823
+ : containerConfig
824
+ : await containerConfig())
825
+ }
826
+ });
827
+ //#endregion
828
+
829
+ //#region [Register config like an injection]
830
+ injector.set(
831
+ containerConfig &&
832
+ "key" in containerConfig &&
833
+ "value" in containerConfig &&
834
+ typeof containerConfig.key === "symbol"
835
+ ? containerConfig.key
836
+ : configKey,
837
+ config
838
+ );
839
+ //#endregion
840
+
841
+ //#region [Run loader(s)]
842
+ if (loaders) {
843
+ const loaderFunctions = [];
844
+
845
+ for (const [key, func] of Object.entries(loaders)) {
846
+ loaderFunctions.push(async () => {
847
+ try {
848
+ const result = await func({ config });
849
+
850
+ console.info(
851
+ `${ansiText(" INFO ", {
852
+ color: "white",
853
+ backgroundColor: "blue",
854
+ bold: true
855
+ })} Loader [${key}] initialized successfully.`
856
+ );
857
+
858
+ return result;
859
+ } catch (error) {
860
+ console.error(
861
+ `${ansiText(" WARN ", {
862
+ color: "yellow",
863
+ backgroundColor: "red",
864
+ bold: true
865
+ })} Loader [${key}] initialization failed.`
866
+ );
867
+ options.debug && console.error(error);
868
+ throw error;
869
+ }
870
+ });
871
+ }
872
+
873
+ const results = await Promise.all(loaderFunctions.map((func) => func()));
874
+
875
+ for (const [key, value] of results) {
876
+ injector.set(key, value);
877
+ }
878
+ }
879
+ //#endregion
880
+
881
+ //#region [Dependencies]
882
+ !dependencies || dependencies.map((dependency) => injector.get(dependency));
883
+ //#endregion
884
+
885
+ //#region [Middleware(s)]
886
+ const startMiddlewareGroup: Array<
887
+ TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>
888
+ > = [];
889
+ const endMiddlewareGroup: Array<
890
+ TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>
891
+ > = [];
892
+
893
+ !middlewares ||
894
+ middlewares.forEach((middleware) => {
895
+ const instance = injector.get<IMiddleware>(middleware);
896
+
897
+ if (instance.start && typeof instance.start === "function") {
898
+ const argumentsMetadata: TArgumentsMetadataCollection =
899
+ Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
900
+
901
+ startMiddlewareGroup.push(
902
+ Object.freeze({
903
+ class: middleware as IMiddleware,
904
+ funcName: "start",
905
+ func: instance.start.bind(instance),
906
+ argumentsMetadata: argumentsMetadata
907
+ })
908
+ );
909
+ }
910
+
911
+ if (instance.end && typeof instance.end === "function") {
912
+ const argumentsMetadata: TArgumentsMetadataCollection =
913
+ Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
914
+
915
+ endMiddlewareGroup.push(
916
+ Object.freeze({
917
+ class: middleware as IMiddleware,
918
+ funcName: "end",
919
+ func: instance.end.bind(instance),
920
+ argumentsMetadata: argumentsMetadata
921
+ })
922
+ );
923
+ }
924
+ });
925
+ //#endregion
926
+
927
+ //#region [Guard(s)]
928
+ const guardGroup: Array<
929
+ TGroupElementModel<"enforce", IGuard, NonNullable<IGuard["enforce"]>>
930
+ > = !guards
931
+ ? []
932
+ : guards.map((guard) => {
933
+ const guardInstance = injector.get<IGuard>(guard);
934
+ const argumentsMetadata: TArgumentsMetadataCollection =
935
+ Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
936
+
937
+ return Object.freeze({
938
+ class: guard as unknown as IGuard,
939
+ funcName: "enforce",
940
+ func: guardInstance.enforce.bind(guardInstance),
941
+ argumentsMetadata: argumentsMetadata
942
+ });
943
+ });
944
+ //#endregion
945
+
946
+ return Object.freeze({
947
+ injector,
948
+ startMiddlewareGroup,
949
+ endMiddlewareGroup,
950
+ guardGroup
951
+ });
952
+ }
953
+
954
+ /**
955
+ *
956
+ * @param param0
957
+ * @returns
958
+ */
959
+ private async moduleResolution({
960
+ moduleClass,
961
+ options,
962
+ extendInjector
963
+ }: {
964
+ moduleClass: TConstructor<unknown>;
965
+ options: TApplicationOptions;
966
+ extendInjector: Injector;
967
+ }) {
968
+ if (!Reflect.getOwnMetadataKeys(moduleClass).includes(moduleKey)) {
969
+ throw Error(`${moduleClass.name} is not a module.`);
970
+ }
971
+
972
+ const injector = new Injector(extendInjector);
973
+ const moduleMetadata: TModuleMetadata = Reflect.getOwnMetadata(moduleKey, moduleClass);
974
+
975
+ const {
976
+ loaders,
977
+ middlewares,
978
+ guards,
979
+ interceptors,
980
+ controllers,
981
+ dependencies,
982
+ webSockets,
983
+ prefix: modulePrefix,
984
+ config: moduleConfig
985
+ } = moduleMetadata || {};
986
+
987
+ const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
988
+
989
+ //#region [Configuration(s)]
990
+ const { config } = Object.freeze({
991
+ config: {
992
+ ...(typeof options.config !== "function" ? options.config : await options.config()),
993
+ ...(typeof moduleConfig !== "function"
994
+ ? typeof moduleConfig !== "object"
995
+ ? undefined
996
+ : "key" in moduleConfig &&
997
+ "value" in moduleConfig &&
998
+ typeof moduleConfig.key === "symbol"
999
+ ? typeof moduleConfig.value !== "function"
1000
+ ? moduleConfig.value
1001
+ : await moduleConfig.value()
1002
+ : moduleConfig
1003
+ : await moduleConfig())
1004
+ }
1005
+ });
1006
+ //#endregion
1007
+
1008
+ //#region [Register config like an injection]
1009
+ injector.set(
1010
+ moduleConfig &&
1011
+ "key" in moduleConfig &&
1012
+ "value" in moduleConfig &&
1013
+ typeof moduleConfig.key === "symbol"
1014
+ ? moduleConfig.key
1015
+ : configKey,
1016
+ config
1017
+ );
1018
+ //#endregion
1019
+
1020
+ //#region [Run loader(s)]
1021
+ if (loaders) {
1022
+ const loaderFunctions = [];
1023
+
1024
+ for (const [key, func] of Object.entries(loaders)) {
1025
+ loaderFunctions.push(async () => {
1026
+ try {
1027
+ const result = await func({ config });
1028
+
1029
+ console.info(
1030
+ `${ansiText(" INFO ", {
1031
+ color: "white",
1032
+ backgroundColor: "blue",
1033
+ bold: true
1034
+ })} Loader [${key}] initialized successfully.`
1035
+ );
1036
+
1037
+ return result;
1038
+ } catch (error) {
1039
+ console.error(
1040
+ `${ansiText(" WARN ", {
1041
+ color: "yellow",
1042
+ backgroundColor: "red",
1043
+ bold: true
1044
+ })} Loader [${key}] initialization failed.`
1045
+ );
1046
+ options.debug && console.error(error);
1047
+ throw error;
1048
+ }
1049
+ });
1050
+ }
1051
+
1052
+ const results = await Promise.all(loaderFunctions.map((func) => func()));
1053
+
1054
+ for (let i = 0; i < results.length; i++) {
1055
+ const [key, value] = results[i];
1056
+ injector.set(key, value);
1057
+ }
1058
+ }
1059
+ //#endregion
1060
+
1061
+ //#region [Dependencies]
1062
+ !dependencies || dependencies.map((dependency) => injector.get(dependency));
1063
+ //#endregion
1064
+
1065
+ //#region [Middleware(s)]
1066
+ const startMiddlewareGroup: Array<
1067
+ TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>
1068
+ > = [];
1069
+ const endMiddlewareGroup: Array<
1070
+ TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>
1071
+ > = [];
1072
+
1073
+ !middlewares ||
1074
+ middlewares.forEach((middleware) => {
1075
+ const instance = injector.get<IMiddleware>(middleware);
1076
+
1077
+ if (instance.start && typeof instance.start === "function") {
1078
+ const argumentsMetadata: TArgumentsMetadataCollection =
1079
+ Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
1080
+
1081
+ startMiddlewareGroup.push(
1082
+ Object.freeze({
1083
+ class: middleware as IMiddleware,
1084
+ funcName: "start",
1085
+ func: instance.start.bind(instance),
1086
+ argumentsMetadata: argumentsMetadata
1087
+ })
1088
+ );
1089
+ }
1090
+
1091
+ if (instance.end && typeof instance.end === "function") {
1092
+ const argumentsMetadata: TArgumentsMetadataCollection =
1093
+ Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
1094
+
1095
+ endMiddlewareGroup.push(
1096
+ Object.freeze({
1097
+ class: middleware as IMiddleware,
1098
+ funcName: "end",
1099
+ func: instance.end.bind(instance),
1100
+ argumentsMetadata: argumentsMetadata
1101
+ })
1102
+ );
1103
+ }
1104
+ });
1105
+ //#endregion
1106
+
1107
+ //#region [Guard(s)]
1108
+ const guardGroup: Array<
1109
+ TGroupElementModel<"enforce", IGuard, NonNullable<IGuard["enforce"]>>
1110
+ > = !guards
1111
+ ? []
1112
+ : guards.map((guard) => {
1113
+ const guardInstance = injector.get<IGuard>(guard);
1114
+ const argumentsMetadata: TArgumentsMetadataCollection =
1115
+ Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
1116
+
1117
+ return Object.freeze({
1118
+ class: guard as unknown as IGuard,
1119
+ funcName: "enforce",
1120
+ func: guardInstance.enforce.bind(guardInstance),
1121
+ argumentsMetadata: argumentsMetadata
1122
+ });
1123
+ });
1124
+ //#endregion
1125
+
1126
+ //#region [Before interceptor(s)]
1127
+ const openInterceptorGroup: Array<
1128
+ TGroupElementModel<"open", IInterceptor, NonNullable<IInterceptor["open"]>>
1129
+ > = [];
1130
+ const closeInterceptorGroup: Array<
1131
+ TGroupElementModel<"close", IInterceptor, NonNullable<IInterceptor["close"]>>
1132
+ > = [];
1133
+
1134
+ !interceptors ||
1135
+ interceptors.forEach((interceptor) => {
1136
+ const instance = injector.get<IInterceptor>(interceptor);
1137
+
1138
+ if (instance.open && typeof instance.open === "function") {
1139
+ const argumentsMetadata: TArgumentsMetadataCollection =
1140
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "open") || {};
1141
+
1142
+ openInterceptorGroup.push(
1143
+ Object.freeze({
1144
+ class: interceptor as IInterceptor,
1145
+ funcName: "open",
1146
+ func: instance.open.bind(instance),
1147
+ argumentsMetadata: argumentsMetadata
1148
+ })
1149
+ );
1150
+ }
1151
+
1152
+ if (instance.close && typeof instance.close === "function") {
1153
+ const argumentsMetadata: TArgumentsMetadataCollection =
1154
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "close") || {};
1155
+
1156
+ closeInterceptorGroup.push(
1157
+ Object.freeze({
1158
+ class: interceptor as IInterceptor,
1159
+ funcName: "close",
1160
+ func: instance.close.bind(instance),
1161
+ argumentsMetadata: argumentsMetadata
1162
+ })
1163
+ );
1164
+ }
1165
+ });
1166
+ //#endregion
1167
+
1168
+ //#region [Controller(s)]
1169
+ const controllerRouterGroup = new HttpRouterGroup();
1170
+
1171
+ !controllers ||
1172
+ controllers.forEach((controllerConstructor) =>
1173
+ this.initControllerInstance({
1174
+ controllerConstructor,
1175
+ httpRouterGroup: controllerRouterGroup,
1176
+ injector: injector,
1177
+ prefix: fullPrefix
1178
+ })
1179
+ );
1180
+ //#endregion
1181
+
1182
+ //#region [WebSocket(s)]
1183
+ const webSocketHttpRouterGroup = new HttpRouterGroup();
1184
+ const webSocketRouterGroup = new WebSocketRouterGroup();
1185
+
1186
+ webSockets &&
1187
+ webSockets.forEach((webSocket) =>
1188
+ this.initWebSocketInstance({
1189
+ webSocketConstructor: webSocket,
1190
+ httpRouterGroup: webSocketHttpRouterGroup,
1191
+ webSocketRouterGroup: webSocketRouterGroup,
1192
+ injector: injector,
1193
+ prefix: fullPrefix
1194
+ })
1195
+ );
1196
+ //#endregion
1197
+
1198
+ return Object.freeze({
1199
+ prefix: modulePrefix || "",
1200
+ injector: injector,
1201
+ startMiddlewareGroup: startMiddlewareGroup,
1202
+ endMiddlewareGroup: endMiddlewareGroup,
1203
+ guardGroup: guardGroup,
1204
+ openInterceptorGroup: openInterceptorGroup,
1205
+ closeInterceptorGroup: closeInterceptorGroup,
1206
+ controllerRouterGroup: controllerRouterGroup,
1207
+ webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1208
+ webSocketRouterGroup: webSocketRouterGroup
1209
+ });
1210
+ }
1211
+
1212
+ /**
1213
+ *
1214
+ * @param data
1215
+ * @param zodSchema
1216
+ * @param argumentIndex
1217
+ * @param funcName
1218
+ * @returns
1219
+ */
1220
+ private async argumentsResolution<TValidationSchema = unknown>(
1221
+ data: unknown,
1222
+ validationSchema: TValidationSchema,
1223
+ argumentIndex: number,
1224
+ funcName: string | symbol
1225
+ ) {
1226
+ if (!this.#customValidator) {
1227
+ return data;
1228
+ }
1229
+
1230
+ try {
1231
+ const validation = await this.#customValidator.validate(
1232
+ data,
1233
+ validationSchema,
1234
+ argumentIndex,
1235
+ funcName
1236
+ );
1237
+
1238
+ if (!(validation instanceof ValidationFailed)) {
1239
+ return validation;
1240
+ }
1241
+
1242
+ throw new HttpClientError({
1243
+ httpCode: 400,
1244
+ message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
1245
+ data: validation.error
1246
+ });
1247
+ } catch (error) {
1248
+ if (error instanceof HttpClientError) {
1249
+ throw error;
1250
+ }
1251
+
1252
+ throw new HttpServerError({
1253
+ httpCode: 500,
1254
+ message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
1255
+ data: !(error instanceof Error)
1256
+ ? error
1257
+ : [
1258
+ {
1259
+ message: error.message,
1260
+ code: error.name,
1261
+ cause: error.cause
1262
+ }
1263
+ ]
1264
+ });
1265
+ }
1266
+ }
1267
+
1268
+ /**
1269
+ *
1270
+ * @param param0
1271
+ * @returns
1272
+ */
1273
+ private initControllerInstance({
1274
+ controllerConstructor,
1275
+ httpRouterGroup,
1276
+ injector,
1277
+ prefix
1278
+ }: Readonly<{
1279
+ controllerConstructor: TConstructor<unknown>;
1280
+ httpRouterGroup: HttpRouterGroup;
1281
+ injector: Injector;
1282
+ prefix?: string;
1283
+ }>) {
1284
+ if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
1285
+ throw Error(`${controllerConstructor.name} is not a controller.`);
1286
+ }
1287
+
1288
+ const controller = injector.get(controllerConstructor);
1289
+
1290
+ if (!controller) {
1291
+ throw Error("Can not initialize controller.");
1292
+ }
1293
+
1294
+ const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(
1295
+ controllerKey,
1296
+ controllerConstructor
1297
+ ) || {
1298
+ prefix: "/",
1299
+ httpMetadata: []
1300
+ };
1301
+
1302
+ const router = new HttpRouter(`/${prefix || ""}/${controllerMetadata.prefix}`);
1303
+
1304
+ controllerMetadata.httpMetadata.forEach((routeMetadata) => {
1305
+ if (typeof routeMetadata.descriptor.value !== "function") {
1306
+ return;
1307
+ }
1308
+
1309
+ const route = router.route(routeMetadata.path);
1310
+ const handler = routeMetadata.descriptor.value.bind(controller);
1311
+ const routeArgument = Object.freeze({
1312
+ class: controllerConstructor,
1313
+ funcName: routeMetadata.methodName,
1314
+ func: handler,
1315
+ argumentsMetadata: routeMetadata.argumentsMetadata
1316
+ });
1317
+
1318
+ switch (routeMetadata.httpMethod) {
1319
+ case "GET":
1320
+ return route.get(routeArgument);
1321
+ case "POST":
1322
+ return route.post(routeArgument);
1323
+ case "PUT":
1324
+ return route.put(routeArgument);
1325
+ case "PATCH":
1326
+ return route.patch(routeArgument);
1327
+ case "DELETE":
1328
+ return route.delete(routeArgument);
1329
+ case "OPTIONS":
1330
+ return route.options(routeArgument);
1331
+ }
1332
+ });
1333
+
1334
+ return httpRouterGroup.add(router);
1335
+ }
1336
+
1337
+ /**
1338
+ *
1339
+ * @param param0
1340
+ * @returns
1341
+ */
1342
+ private initWebSocketInstance({
1343
+ injector,
1344
+ httpRouterGroup,
1345
+ prefix,
1346
+ webSocketRouterGroup,
1347
+ webSocketConstructor
1348
+ }: Readonly<{
1349
+ webSocketConstructor: TConstructor<unknown>;
1350
+ httpRouterGroup: HttpRouterGroup;
1351
+ webSocketRouterGroup: WebSocketRouterGroup;
1352
+ injector: Injector;
1353
+ prefix?: string;
1354
+ }>): Readonly<{
1355
+ httpRouterGroup: HttpRouterGroup;
1356
+ webSocketRouterGroup: WebSocketRouterGroup;
1357
+ }> {
1358
+ if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
1359
+ throw Error(`${webSocketConstructor.name} is not a websocket route.`);
1360
+ }
1361
+
1362
+ const webSocket = injector.get(webSocketConstructor);
1363
+
1364
+ if (!webSocket) {
1365
+ throw Error("Can not initialize webSocket.");
1366
+ }
1367
+
1368
+ const webSocketMetadata: TWebSocketMetadata = Reflect.getOwnMetadata(
1369
+ webSocketKey,
1370
+ webSocketConstructor
1371
+ ) || {
1372
+ prefix: "/",
1373
+ events: [],
1374
+ http: []
1375
+ };
1376
+
1377
+ const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
1378
+
1379
+ //#region [HTTP ROUTER]
1380
+ const router = new HttpRouter(fullPrefix);
1381
+
1382
+ for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
1383
+ if (typeof httpMetadata.descriptor?.value !== "function") {
1384
+ continue;
1385
+ }
1386
+
1387
+ const route = router.route(httpMetadata.path);
1388
+ const handler = httpMetadata.descriptor.value.bind(webSocket);
1389
+ const routeArgument = Object.freeze({
1390
+ class: webSocketConstructor,
1391
+ funcName: httpMetadata.methodName,
1392
+ func: handler,
1393
+ argumentsMetadata: httpMetadata.argumentsMetadata
1394
+ });
1395
+
1396
+ switch (httpMetadata.httpMethod) {
1397
+ case "GET":
1398
+ route.get(routeArgument);
1399
+ break;
1400
+ case "POST":
1401
+ route.post(routeArgument);
1402
+ break;
1403
+ }
1404
+ }
1405
+
1406
+ httpRouterGroup.add(router);
1407
+ //#endregion
1408
+
1409
+ //#region [WEBSOCKET ROUTER]
1410
+ const webSocketRouter = new WebSocketRouter(fullPrefix);
1411
+
1412
+ for (const [key, event] of Object.entries(webSocketMetadata.events)) {
1413
+ const webSocketRoute = new WebSocketRoute({
1414
+ eventName: key,
1415
+ metadata: event
1416
+ });
1417
+
1418
+ webSocketRouter.addRoutes(webSocketRoute);
1419
+ }
1420
+
1421
+ webSocketRouter.bind(webSocket);
1422
+ webSocketRouterGroup.addRouters(webSocketRouter);
1423
+ //#endregion
1424
+
1425
+ return Object.freeze({
1426
+ httpRouterGroup: httpRouterGroup,
1427
+ webSocketRouterGroup: webSocketRouterGroup
1428
+ });
1429
+ }
1430
+
1431
+ /**
1432
+ *
1433
+ * @param param0
1434
+ * @returns
1435
+ */
1436
+ private serializeResponse({
1437
+ status,
1438
+ statusText,
1439
+ headers,
1440
+ data
1441
+ }: {
1442
+ status?: number;
1443
+ statusText?: string;
1444
+ headers: Headers;
1445
+ data: unknown;
1446
+ }): Response {
1447
+ const contentType = headers.get("Content-Type") || "text/plain";
1448
+
1449
+ if (contentType.includes("application/json")) {
1450
+ return this.finalizeResponse(
1451
+ new Response(data instanceof ReadableStream ? data : JSON.stringify(data), {
1452
+ status: status,
1453
+ statusText: statusText,
1454
+ headers: headers
1455
+ })
1456
+ );
1457
+ }
1458
+
1459
+ if (contentType.includes("text/plain") || contentType.includes("text/html")) {
1460
+ return this.finalizeResponse(
1461
+ new Response(data instanceof ReadableStream ? data : String(data), {
1462
+ status: status,
1463
+ statusText: statusText,
1464
+ headers: headers
1465
+ })
1466
+ );
1467
+ }
1468
+
1469
+ if (contentType.includes("application/octet-stream")) {
1470
+ if (
1471
+ data instanceof Uint8Array ||
1472
+ data instanceof ArrayBuffer ||
1473
+ data instanceof Blob ||
1474
+ data instanceof ReadableStream
1475
+ ) {
1476
+ return this.finalizeResponse(
1477
+ new Response(data, { status: status, statusText: statusText, headers: headers })
1478
+ );
1479
+ }
1480
+
1481
+ throw new Error("Invalid data type for application/octet-stream");
1482
+ }
1483
+
1484
+ if (contentType.includes("multipart/form-data")) {
1485
+ if (data instanceof FormData) {
1486
+ return this.finalizeResponse(
1487
+ new Response(data, { status: status, statusText: statusText, headers: headers })
1488
+ );
1489
+ }
1490
+
1491
+ throw new Error("multipart/form-data requires FormData object");
1492
+ }
1493
+
1494
+ return this.finalizeResponse(
1495
+ new Response(String(data), { status: status, statusText: statusText, headers: headers })
1496
+ );
1497
+ }
1498
+
1499
+ /**
1500
+ *
1501
+ * @param response
1502
+ * @returns
1503
+ */
1504
+ private finalizeResponse(response: Response) {
1505
+ response.headers.set("X-Powered-By", "Bool Typescript");
1506
+
1507
+ return response;
1508
+ }
1509
+
1510
+ /**
1511
+ *
1512
+ * @param param0
1513
+ * @returns
1514
+ */
1515
+ private async httpFetcher({
1516
+ context: outerContext,
1517
+ route,
1518
+ options,
1519
+ resolutedMap
1520
+ }: Partial<{
1521
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
1522
+ resolutedMap:
1523
+ | Partial<
1524
+ NonNullable<Awaited<ReturnType<Application<TRootClass>["containerResolution"]>>>
1525
+ >
1526
+ | Partial<
1527
+ NonNullable<Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>>
1528
+ >;
1529
+ context: Context;
1530
+ options: Partial<{
1531
+ isContainer: boolean;
1532
+ }>;
1533
+ }>) {
1534
+ const contextOptions = { isStatic: true };
1535
+ const context = (!outerContext ? new Context() : new Context(outerContext)).setOptions(
1536
+ contextOptions
1537
+ );
1538
+
1539
+ if (route) {
1540
+ context.set(paramsArgsKey, route.parameters).set(routeModelArgsKey, route.model);
1541
+ }
1542
+
1543
+ const httpServer =
1544
+ context.get<Server | null | undefined>(httpServerArgsKey, contextOptions) ||
1545
+ undefined,
1546
+ request =
1547
+ context.get<Request | null | undefined>(requestArgsKey, contextOptions) ||
1548
+ undefined,
1549
+ requestHeaders =
1550
+ context.get<Headers | null | undefined>(requestHeaderArgsKey, contextOptions) ||
1551
+ undefined,
1552
+ responseHeaders =
1553
+ context.get<Headers | null | undefined>(responseHeadersArgsKey, contextOptions) ||
1554
+ undefined,
1555
+ parameters =
1556
+ context.get<TParamsType | null | undefined>(paramsArgsKey, contextOptions) ||
1557
+ undefined,
1558
+ routeModel =
1559
+ context.get<
1560
+ NonNullable<ReturnType<HttpRouterGroup["find"]>>["model"] | null | undefined
1561
+ >(routeModelArgsKey, contextOptions) || undefined;
1562
+
1563
+ if (resolutedMap) {
1564
+ const { startMiddlewareGroup, guardGroup } = resolutedMap;
1565
+
1566
+ // Execute start middleware(s)
1567
+ if (startMiddlewareGroup) {
1568
+ for (let i = 0; i < startMiddlewareGroup.length; i++) {
1569
+ const args = [];
1570
+ const {
1571
+ func: handler,
1572
+ funcName: functionName,
1573
+ argumentsMetadata
1574
+ } = startMiddlewareGroup[i];
1575
+
1576
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1577
+ switch (argMetadata.type) {
1578
+ case contextArgsKey:
1579
+ args[argMetadata.index] = !argMetadata.key
1580
+ ? context
1581
+ : context.get(argMetadata.key, contextOptions);
1582
+ break;
1583
+ case requestArgsKey:
1584
+ args[argMetadata.index] = !argMetadata.validationSchema
1585
+ ? request
1586
+ : await this.argumentsResolution(
1587
+ request,
1588
+ argMetadata.validationSchema,
1589
+ argMetadata.index,
1590
+ functionName
1591
+ );
1592
+ break;
1593
+ case requestBodyArgsKey:
1594
+ args[argMetadata.index] = !argMetadata.validationSchema
1595
+ ? await request?.[argMetadata.parser || "json"]()
1596
+ : await this.argumentsResolution(
1597
+ await request?.[argMetadata.parser || "json"](),
1598
+ argMetadata.validationSchema,
1599
+ argMetadata.index,
1600
+ functionName
1601
+ );
1602
+ break;
1603
+ case requestHeadersArgsKey:
1604
+ args[argMetadata.index] = !argMetadata.validationSchema
1605
+ ? requestHeaders
1606
+ : await this.argumentsResolution(
1607
+ requestHeaders?.toJSON(),
1608
+ argMetadata.validationSchema,
1609
+ argMetadata.index,
1610
+ functionName
1611
+ );
1612
+ break;
1613
+ case requestHeaderArgsKey:
1614
+ args[argMetadata.index] = !argMetadata.validationSchema
1615
+ ? requestHeaders?.get(argMetadata.key) || undefined
1616
+ : await this.argumentsResolution(
1617
+ requestHeaders?.get(argMetadata.key) || undefined,
1618
+ argMetadata.validationSchema,
1619
+ argMetadata.index,
1620
+ functionName
1621
+ );
1622
+ break;
1623
+ case paramArgsKey:
1624
+ args[argMetadata.index] = !argMetadata.validationSchema
1625
+ ? parameters?.[argMetadata.key] || undefined
1626
+ : await this.argumentsResolution(
1627
+ parameters?.[argMetadata.key] || undefined,
1628
+ argMetadata.validationSchema,
1629
+ argMetadata.index,
1630
+ functionName
1631
+ );
1632
+ break;
1633
+ case routeModelArgsKey:
1634
+ args[argMetadata.index] = routeModel;
1635
+ break;
1636
+ case responseHeadersArgsKey:
1637
+ args[argMetadata.index] = responseHeaders;
1638
+ break;
1639
+ case httpServerArgsKey:
1640
+ args[argMetadata.index] = httpServer;
1641
+ break;
1642
+ default:
1643
+ args[argMetadata.index] = !argMetadata.validationSchema
1644
+ ? !context.has(argMetadata.type, contextOptions)
1645
+ ? undefined
1646
+ : context.get(argMetadata.type, contextOptions)
1647
+ : await this.argumentsResolution(
1648
+ !(argMetadata.type in context)
1649
+ ? undefined
1650
+ : context.get(argMetadata.type, contextOptions),
1651
+ argMetadata.validationSchema,
1652
+ argMetadata.index,
1653
+ functionName
1654
+ );
1655
+ break;
1656
+ }
1657
+ }
1658
+
1659
+ await handler(...args);
1660
+ }
1661
+ }
1662
+
1663
+ // Execute guard(s)
1664
+ if (guardGroup) {
1665
+ for (let i = 0; i < guardGroup.length; i++) {
1666
+ const args = [];
1667
+ const {
1668
+ func: handler,
1669
+ funcName: functionName,
1670
+ argumentsMetadata
1671
+ } = guardGroup[i];
1672
+
1673
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1674
+ switch (argMetadata.type) {
1675
+ case requestArgsKey:
1676
+ args[argMetadata.index] = !argMetadata.validationSchema
1677
+ ? request
1678
+ : await this.argumentsResolution(
1679
+ request,
1680
+ argMetadata.validationSchema,
1681
+ argMetadata.index,
1682
+ functionName
1683
+ );
1684
+ break;
1685
+ case requestBodyArgsKey:
1686
+ args[argMetadata.index] = !argMetadata.validationSchema
1687
+ ? await request?.[argMetadata.parser || "json"]()
1688
+ : await this.argumentsResolution(
1689
+ await request?.[argMetadata.parser || "json"](),
1690
+ argMetadata.validationSchema,
1691
+ argMetadata.index,
1692
+ functionName
1693
+ );
1694
+ break;
1695
+ case contextArgsKey:
1696
+ args[argMetadata.index] = !argMetadata.key
1697
+ ? context
1698
+ : context.get(argMetadata.key);
1699
+ break;
1700
+ case requestHeadersArgsKey:
1701
+ args[argMetadata.index] = !argMetadata.validationSchema
1702
+ ? requestHeaders
1703
+ : await this.argumentsResolution(
1704
+ requestHeaders?.toJSON(),
1705
+ argMetadata.validationSchema,
1706
+ argMetadata.index,
1707
+ functionName
1708
+ );
1709
+ break;
1710
+ case responseHeadersArgsKey:
1711
+ args[argMetadata.index] = responseHeaders;
1712
+ break;
1713
+ case requestHeaderArgsKey:
1714
+ args[argMetadata.index] = !argMetadata.validationSchema
1715
+ ? requestHeaders?.get(argMetadata.key) || undefined
1716
+ : await this.argumentsResolution(
1717
+ requestHeaders?.get(argMetadata.key) || undefined,
1718
+ argMetadata.validationSchema,
1719
+ argMetadata.index,
1720
+ functionName
1721
+ );
1722
+ break;
1723
+ case paramArgsKey:
1724
+ args[argMetadata.index] = !argMetadata.validationSchema
1725
+ ? parameters?.[argMetadata.key] || undefined
1726
+ : await this.argumentsResolution(
1727
+ parameters?.[argMetadata.key],
1728
+ argMetadata.validationSchema,
1729
+ argMetadata.index,
1730
+ functionName
1731
+ );
1732
+ break;
1733
+ case routeModelArgsKey:
1734
+ args[argMetadata.index] = routeModel;
1735
+ break;
1736
+ case httpServerArgsKey:
1737
+ args[argMetadata.index] = httpServer;
1738
+ break;
1739
+ default:
1740
+ args[argMetadata.index] = !argMetadata.validationSchema
1741
+ ? !context.has(argMetadata.type)
1742
+ ? undefined
1743
+ : context.get(argMetadata.type)
1744
+ : await this.argumentsResolution(
1745
+ context.get(argMetadata.type),
1746
+ argMetadata.validationSchema,
1747
+ argMetadata.index,
1748
+ functionName
1749
+ );
1750
+ break;
1751
+ }
1752
+ }
1753
+
1754
+ const guardResult = await handler(...args);
1755
+
1756
+ if (typeof guardResult !== "boolean" || !guardResult) {
1757
+ throw new HttpClientError({
1758
+ httpCode: 401,
1759
+ message: "Unauthorization.",
1760
+ data: undefined
1761
+ });
1762
+ }
1763
+ }
1764
+ }
1765
+ }
1766
+
1767
+ if (routeModel && !options?.isContainer) {
1768
+ if (
1769
+ resolutedMap &&
1770
+ "openInterceptorGroup" in resolutedMap &&
1771
+ resolutedMap.openInterceptorGroup
1772
+ ) {
1773
+ const { openInterceptorGroup } = resolutedMap;
1774
+
1775
+ // Execute open interceptor(s)
1776
+ for (let i = 0; i < openInterceptorGroup.length; i++) {
1777
+ const args = [];
1778
+ const {
1779
+ func: handler,
1780
+ funcName: functionName,
1781
+ argumentsMetadata
1782
+ } = openInterceptorGroup[i];
1783
+
1784
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1785
+ switch (argMetadata.type) {
1786
+ case requestArgsKey:
1787
+ args[argMetadata.index] = !argMetadata.validationSchema
1788
+ ? request
1789
+ : await this.argumentsResolution(
1790
+ request,
1791
+ argMetadata.validationSchema,
1792
+ argMetadata.index,
1793
+ functionName
1794
+ );
1795
+ break;
1796
+ case requestBodyArgsKey:
1797
+ args[argMetadata.index] = !argMetadata.validationSchema
1798
+ ? await request?.[argMetadata.parser || "json"]()
1799
+ : await this.argumentsResolution(
1800
+ await request?.[argMetadata.parser || "json"](),
1801
+ argMetadata.validationSchema,
1802
+ argMetadata.index,
1803
+ functionName
1804
+ );
1805
+ break;
1806
+ case contextArgsKey:
1807
+ args[argMetadata.index] = !argMetadata.key
1808
+ ? context
1809
+ : context.get(argMetadata.key);
1810
+ break;
1811
+ case requestHeadersArgsKey:
1812
+ args[argMetadata.index] = !argMetadata.validationSchema
1813
+ ? requestHeaders
1814
+ : await this.argumentsResolution(
1815
+ requestHeaders?.toJSON(),
1816
+ argMetadata.validationSchema,
1817
+ argMetadata.index,
1818
+ functionName
1819
+ );
1820
+ break;
1821
+ case requestHeaderArgsKey:
1822
+ args[argMetadata.index] = !argMetadata.validationSchema
1823
+ ? requestHeaders?.get(argMetadata.key) || undefined
1824
+ : await this.argumentsResolution(
1825
+ requestHeaders?.get(argMetadata.key) || undefined,
1826
+ argMetadata.validationSchema,
1827
+ argMetadata.index,
1828
+ functionName
1829
+ );
1830
+ break;
1831
+ case responseHeadersArgsKey:
1832
+ args[argMetadata.index] = responseHeaders;
1833
+ break;
1834
+ case paramArgsKey:
1835
+ args[argMetadata.index] = !argMetadata.validationSchema
1836
+ ? parameters?.[argMetadata.key] || undefined
1837
+ : await this.argumentsResolution(
1838
+ parameters?.[argMetadata.key] || undefined,
1839
+ argMetadata.validationSchema,
1840
+ argMetadata.index,
1841
+ functionName
1842
+ );
1843
+ break;
1844
+ case routeModelArgsKey:
1845
+ args[argMetadata.index] = routeModel;
1846
+ break;
1847
+ case httpServerArgsKey:
1848
+ args[argMetadata.index] = httpServer;
1849
+ break;
1850
+ default:
1851
+ args[argMetadata.index] = !argMetadata.validationSchema
1852
+ ? !context.has(argMetadata.type)
1853
+ ? undefined
1854
+ : context.get(argMetadata.type)
1855
+ : await this.argumentsResolution(
1856
+ context.get(argMetadata.type),
1857
+ argMetadata.validationSchema,
1858
+ argMetadata.index,
1859
+ functionName
1860
+ );
1861
+ break;
1862
+ }
1863
+ }
1864
+
1865
+ await handler(...args);
1866
+ }
1867
+ }
1868
+
1869
+ // Execute controller action
1870
+ const controllerActionArguments: any[] = [];
1871
+ const {
1872
+ func: controllerAction,
1873
+ funcName: controllerActionName,
1874
+ argumentsMetadata: controllerActionArgumentsMetadata
1875
+ } = routeModel;
1876
+
1877
+ for (const [_key, argMetadata] of Object.entries(controllerActionArgumentsMetadata)) {
1878
+ switch (argMetadata.type) {
1879
+ case requestArgsKey:
1880
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1881
+ ? request
1882
+ : await this.argumentsResolution(
1883
+ request,
1884
+ argMetadata.validationSchema,
1885
+ argMetadata.index,
1886
+ controllerActionName
1887
+ );
1888
+ break;
1889
+ case requestBodyArgsKey:
1890
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1891
+ ? await request?.[argMetadata.parser || "json"]()
1892
+ : await this.argumentsResolution(
1893
+ await request?.[argMetadata.parser || "json"](),
1894
+ argMetadata.validationSchema,
1895
+ argMetadata.index,
1896
+ controllerActionName
1897
+ );
1898
+ break;
1899
+ case contextArgsKey:
1900
+ controllerActionArguments[argMetadata.index] = !argMetadata.key
1901
+ ? context
1902
+ : context.get(argMetadata.key);
1903
+ break;
1904
+ case requestHeadersArgsKey:
1905
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1906
+ ? requestHeaders
1907
+ : await this.argumentsResolution(
1908
+ requestHeaders?.toJSON(),
1909
+ argMetadata.validationSchema,
1910
+ argMetadata.index,
1911
+ controllerActionName
1912
+ );
1913
+ break;
1914
+ case requestHeaderArgsKey:
1915
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1916
+ ? requestHeaders?.get(argMetadata.key) || undefined
1917
+ : await this.argumentsResolution(
1918
+ requestHeaders?.get(argMetadata.key) || undefined,
1919
+ argMetadata.validationSchema,
1920
+ argMetadata.index,
1921
+ controllerActionName
1922
+ );
1923
+ break;
1924
+ case responseHeadersArgsKey:
1925
+ controllerActionArguments[argMetadata.index] = responseHeaders;
1926
+ break;
1927
+ case paramArgsKey:
1928
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1929
+ ? parameters?.[argMetadata.key] || undefined
1930
+ : await this.argumentsResolution(
1931
+ parameters?.[argMetadata.key] || undefined,
1932
+ argMetadata.validationSchema,
1933
+ argMetadata.index,
1934
+ controllerActionName
1935
+ );
1936
+ break;
1937
+ case routeModelArgsKey:
1938
+ controllerActionArguments[argMetadata.index] = routeModel;
1939
+ break;
1940
+ case httpServerArgsKey:
1941
+ controllerActionArguments[argMetadata.index] = httpServer;
1942
+ break;
1943
+ default:
1944
+ controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1945
+ ? !context.has(argMetadata.type)
1946
+ ? undefined
1947
+ : context.get(argMetadata.type)
1948
+ : await this.argumentsResolution(
1949
+ context.get(argMetadata.type),
1950
+ argMetadata.validationSchema,
1951
+ argMetadata.index,
1952
+ controllerActionName
1953
+ );
1954
+ break;
1955
+ }
1956
+ }
1957
+
1958
+ context.set(responseBodyArgsKey, await controllerAction(...controllerActionArguments), {
1959
+ isStatic: false
1960
+ });
1961
+
1962
+ if (
1963
+ resolutedMap &&
1964
+ "closeInterceptorGroup" in resolutedMap &&
1965
+ resolutedMap.closeInterceptorGroup
1966
+ ) {
1967
+ const { closeInterceptorGroup } = resolutedMap;
1968
+
1969
+ // Execute close interceptor(s)
1970
+ for (let i = 0; i < closeInterceptorGroup.length; i++) {
1971
+ const args = [];
1972
+ const {
1973
+ func: handler,
1974
+ funcName: functionName,
1975
+ argumentsMetadata
1976
+ } = closeInterceptorGroup[i];
1977
+
1978
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1979
+ switch (argMetadata.type) {
1980
+ case requestArgsKey:
1981
+ args[argMetadata.index] = !argMetadata.validationSchema
1982
+ ? request
1983
+ : await this.argumentsResolution(
1984
+ request,
1985
+ argMetadata.validationSchema,
1986
+ argMetadata.index,
1987
+ functionName
1988
+ );
1989
+ break;
1990
+ case requestBodyArgsKey:
1991
+ args[argMetadata.index] = !argMetadata.validationSchema
1992
+ ? await request?.[argMetadata.parser || "json"]()
1993
+ : await this.argumentsResolution(
1994
+ await request?.[argMetadata.parser || "json"](),
1995
+ argMetadata.validationSchema,
1996
+ argMetadata.index,
1997
+ functionName
1998
+ );
1999
+ break;
2000
+ case contextArgsKey:
2001
+ args[argMetadata.index] = !argMetadata.key
2002
+ ? context
2003
+ : context.get(argMetadata.key);
2004
+ break;
2005
+ case requestHeadersArgsKey:
2006
+ args[argMetadata.index] = !argMetadata.validationSchema
2007
+ ? requestHeaders
2008
+ : await this.argumentsResolution(
2009
+ requestHeaders?.toJSON(),
2010
+ argMetadata.validationSchema,
2011
+ argMetadata.index,
2012
+ functionName
2013
+ );
2014
+ break;
2015
+ case responseHeadersArgsKey:
2016
+ args[argMetadata.index] = context.get(argMetadata.type);
2017
+ break;
2018
+ case requestHeaderArgsKey:
2019
+ args[argMetadata.index] = !argMetadata.validationSchema
2020
+ ? requestHeaders?.get(argMetadata.key) || undefined
2021
+ : await this.argumentsResolution(
2022
+ requestHeaders?.get(argMetadata.key) || undefined,
2023
+ argMetadata.validationSchema,
2024
+ argMetadata.index,
2025
+ functionName
2026
+ );
2027
+ break;
2028
+ case paramArgsKey:
2029
+ args[argMetadata.index] = !argMetadata.validationSchema
2030
+ ? parameters?.[argMetadata.key] || undefined
2031
+ : await this.argumentsResolution(
2032
+ parameters?.[argMetadata.key] || undefined,
2033
+ argMetadata.validationSchema,
2034
+ argMetadata.index,
2035
+ functionName
2036
+ );
2037
+ break;
2038
+ case routeModelArgsKey:
2039
+ args[argMetadata.index] = routeModel;
2040
+ break;
2041
+ case httpServerArgsKey:
2042
+ args[argMetadata.index] = httpServer;
2043
+ break;
2044
+ default:
2045
+ args[argMetadata.index] = !argMetadata.validationSchema
2046
+ ? !context.has(argMetadata.type)
2047
+ ? undefined
2048
+ : context.get(argMetadata.type)
2049
+ : await this.argumentsResolution(
2050
+ context.get(argMetadata.type),
2051
+ argMetadata.validationSchema,
2052
+ argMetadata.index,
2053
+ functionName
2054
+ );
2055
+ break;
2056
+ }
2057
+ }
2058
+
2059
+ await handler(...args);
2060
+ }
2061
+ }
2062
+ }
2063
+
2064
+ if (resolutedMap) {
2065
+ const { endMiddlewareGroup } = resolutedMap;
2066
+
2067
+ // Execute end middleware(s)
2068
+ if (endMiddlewareGroup) {
2069
+ for (let i = 0; i < endMiddlewareGroup.length; i++) {
2070
+ const args = [];
2071
+ const {
2072
+ func: handler,
2073
+ funcName: functionName,
2074
+ argumentsMetadata
2075
+ } = endMiddlewareGroup[i];
2076
+
2077
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
2078
+ switch (argMetadata.type) {
2079
+ case requestArgsKey:
2080
+ args[argMetadata.index] = !argMetadata.validationSchema
2081
+ ? request
2082
+ : await this.argumentsResolution(
2083
+ request,
2084
+ argMetadata.validationSchema,
2085
+ argMetadata.index,
2086
+ functionName
2087
+ );
2088
+ break;
2089
+ case requestBodyArgsKey:
2090
+ args[argMetadata.index] = !argMetadata.validationSchema
2091
+ ? await request?.[argMetadata.parser || "json"]()
2092
+ : await this.argumentsResolution(
2093
+ await request?.[argMetadata.parser || "json"](),
2094
+ argMetadata.validationSchema,
2095
+ argMetadata.index,
2096
+ functionName
2097
+ );
2098
+ break;
2099
+ case contextArgsKey:
2100
+ args[argMetadata.index] = !argMetadata.key
2101
+ ? context
2102
+ : context.get(argMetadata.key);
2103
+ break;
2104
+ case requestHeadersArgsKey:
2105
+ args[argMetadata.index] = !argMetadata.validationSchema
2106
+ ? requestHeaders
2107
+ : await this.argumentsResolution(
2108
+ requestHeaders?.toJSON(),
2109
+ argMetadata.validationSchema,
2110
+ argMetadata.index,
2111
+ functionName
2112
+ );
2113
+ break;
2114
+ case responseHeadersArgsKey:
2115
+ args[argMetadata.index] = context.get(argMetadata.type);
2116
+ break;
2117
+ case requestHeaderArgsKey:
2118
+ args[argMetadata.index] = !argMetadata.validationSchema
2119
+ ? requestHeaders?.get(argMetadata.key) || undefined
2120
+ : await this.argumentsResolution(
2121
+ requestHeaders?.get(argMetadata.key) || undefined,
2122
+ argMetadata.validationSchema,
2123
+ argMetadata.index,
2124
+ functionName
2125
+ );
2126
+ break;
2127
+ case paramArgsKey:
2128
+ args[argMetadata.index] = !argMetadata.validationSchema
2129
+ ? parameters?.[argMetadata.key] || undefined
2130
+ : await this.argumentsResolution(
2131
+ parameters?.[argMetadata.key] || undefined,
2132
+ argMetadata.validationSchema,
2133
+ argMetadata.index,
2134
+ functionName
2135
+ );
2136
+ break;
2137
+ case routeModelArgsKey:
2138
+ args[argMetadata.index] = routeModel;
2139
+ break;
2140
+ case httpServerArgsKey:
2141
+ args[argMetadata.index] = httpServer;
2142
+ break;
2143
+ default:
2144
+ args[argMetadata.index] = !argMetadata.validationSchema
2145
+ ? !context.has(argMetadata.type)
2146
+ ? undefined
2147
+ : context.get(argMetadata.type)
2148
+ : await this.argumentsResolution(
2149
+ !(argMetadata.type in context)
2150
+ ? undefined
2151
+ : context.get(argMetadata.type),
2152
+ argMetadata.validationSchema,
2153
+ argMetadata.index,
2154
+ functionName
2155
+ );
2156
+ break;
2157
+ }
2158
+ }
2159
+
2160
+ await handler(...args);
2161
+ }
2162
+ }
2163
+ }
2164
+
2165
+ return Object.freeze({
2166
+ context: context
2167
+ });
2168
+ }
2169
+
2170
+ /**
2171
+ *
2172
+ * @param bun
2173
+ * @param bool
2174
+ * @returns
2175
+ */
2176
+ private async webSocketFetcher(
2177
+ bun: Required<{
2178
+ request: Request;
2179
+ server: Server;
2180
+ }>,
2181
+ bool: Required<{
2182
+ responseHeaders: Headers;
2183
+ query: Record<string, unknown>;
2184
+ route: NonNullable<ReturnType<HttpRouterGroup["find"]>>;
2185
+ moduleResolution: NonNullable<
2186
+ Awaited<ReturnType<Application<TRootClass>["moduleResolution"]>>
2187
+ >;
2188
+ }>
2189
+ ) {
2190
+ const { request, server } = bun;
2191
+ const {
2192
+ query,
2193
+ responseHeaders,
2194
+ route: { model }
2195
+ } = bool;
2196
+
2197
+ // Execute controller action
2198
+ const isUpgrade = await model.func(...[server, request, query]);
2199
+
2200
+ if (typeof isUpgrade !== "boolean") {
2201
+ return this.finalizeResponse(
2202
+ new Response(
2203
+ JSON.stringify({
2204
+ httpCode: 500,
2205
+ message: "Can not detect webSocket upgrade result.",
2206
+ data: undefined
2207
+ }),
2208
+ {
2209
+ status: 500,
2210
+ statusText: "Internal server error.",
2211
+ headers: responseHeaders
2212
+ }
2213
+ )
2214
+ );
2215
+ }
2216
+
2217
+ if (!isUpgrade) {
2218
+ return this.finalizeResponse(
2219
+ new Response(
2220
+ JSON.stringify({
2221
+ httpCode: 500,
2222
+ message: "Can not upgrade.",
2223
+ data: undefined
2224
+ }),
2225
+ {
2226
+ status: 500,
2227
+ statusText: "Internal server error.",
2228
+ headers: responseHeaders
2229
+ }
2230
+ )
2231
+ );
2232
+ }
2233
+
2234
+ return isUpgrade;
2235
+ }
2236
+ }