@bool-ts/core 2.2.4 → 2.3.1

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