@bool-ts/core 2.2.4 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) 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 +30 -28
  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 +1554 -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/constructor.ts +1 -0
  63. package/src/utils/functions.ts +13 -0
  64. package/src/{ultils → utils}/index.ts +1 -0
  65. /package/{src/ultils/constructor.ts → dist/utils/constructor.d.ts} +0 -0
  66. /package/src/{keys/index.ts → constants/keys.ts} +0 -0
  67. /package/src/{ultils → utils}/asyncFunction.ts +0 -0
  68. /package/src/{ultils → utils}/colors.ts +0 -0
  69. /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,641 @@ 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();
283
270
 
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);
271
+ try {
272
+ const context = new Context()
273
+ .setOptions({ isStatic: true })
274
+ .set(httpServerArgsKey, server)
275
+ .set(requestArgsKey, request)
276
+ .set(requestHeaderArgsKey, request.headers)
277
+ .set(responseHeadersArgsKey, responseHeaders)
278
+ .set(queryArgsKey, query);
279
+
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 pathname = ansiText(url.pathname, { color: "blue" });
373
+ const convertedPID = `${Bun.color("yellow", "ansi")}${process.pid}`;
374
+ const convertedMethod = ansiText(request.method, {
375
+ color: "yellow",
376
+ backgroundColor: "blue"
377
+ });
378
+ const convertedReqIp = ansiText(
379
+ `${
380
+ request.headers.get("x-forwarded-for") ||
381
+ request.headers.get("x-real-ip") ||
382
+ server.requestIP(request)?.address ||
383
+ "<Unknown>"
384
+ }`,
385
+ {
386
+ color: "yellow"
496
387
  }
388
+ );
389
+ const convertedTime = ansiText(
390
+ `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`,
391
+ {
392
+ color: "yellow",
393
+ backgroundColor: "blue"
394
+ }
395
+ );
497
396
 
498
- for (const availableModuleResolution of resolutedModules) {
499
- const routeResult = availableModuleResolution.controllerRouterGroup.find({
500
- pathname: url.pathname,
501
- method: method
502
- });
397
+ allowLogsMethods.includes(
398
+ request.method.toUpperCase() as (typeof allowLogsMethods)[number]
399
+ ) &&
400
+ console.info(
401
+ `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${pathname} - Time: ${convertedTime}`
402
+ );
403
+ }
404
+ }
405
+ }
406
+
407
+ /**
408
+ *
409
+ * @returns
410
+ */
411
+ async #rootWebSocket<T extends TWebSocketUpgradeData = TWebSocketUpgradeData>(): Promise<
412
+ WebSocketHandler<T>
413
+ > {
414
+ const { webSocketsMap } = await this.preLaunch();
415
+
416
+ return {
417
+ open: (connection: ServerWebSocket<T>) => {
418
+ const pathnameKey = `${connection.data.pathname}:::open`;
419
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
420
+
421
+ if (!handlerMetadata) {
422
+ return;
423
+ }
503
424
 
504
- if (routeResult) {
505
- collection = Object.freeze({
506
- route: routeResult,
507
- resolution: availableModuleResolution
508
- });
425
+ const argumentsMetadata = handlerMetadata.arguments || {};
426
+ const args: Array<unknown> = [];
427
+
428
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
429
+ switch (argumentMetadata.type) {
430
+ case webSocketConnectionArgsKey:
431
+ args[argumentMetadata.index] = connection;
432
+ break;
433
+ case webSocketServerArgsKey:
434
+ args[argumentMetadata.index] =
435
+ this.#globalContext.get(webSocketServerArgsKey);
509
436
  break;
510
- }
511
437
  }
438
+ }
512
439
 
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
- });
440
+ handlerMetadata.descriptor.value(...args);
441
+ },
442
+ close: (connection, code, reason) => {
443
+ const pathnameKey = `${connection.data.pathname}:::close`;
444
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
526
445
 
527
- context = newContext;
528
- }
446
+ if (!handlerMetadata) {
447
+ return;
448
+ }
529
449
 
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
- });
450
+ const argumentsMetadata = handlerMetadata.arguments || {};
451
+ const args: Array<unknown> = [];
549
452
 
550
- context = newContext;
453
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
454
+ switch (argumentMetadata.type) {
455
+ case webSocketConnectionArgsKey:
456
+ args[argumentMetadata.index] = connection;
457
+ break;
458
+ case webSocketCloseCodeArgsKey:
459
+ args[argumentMetadata.index] = code;
460
+ break;
461
+ case webSocketCloseReasonArgsKey:
462
+ args[argumentMetadata.index] = reason;
463
+ break;
464
+ case webSocketServerArgsKey:
465
+ args[argumentMetadata.index] =
466
+ this.#globalContext.get(webSocketServerArgsKey);
467
+ break;
551
468
  }
469
+ }
552
470
 
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
- });
471
+ handlerMetadata.descriptor.value(...args);
472
+ },
473
+ message: (connection: ServerWebSocket<T>, message) => {
474
+ const pathnameKey = `${connection.data.pathname}:::message`;
475
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
476
+
477
+ if (!handlerMetadata) {
478
+ return;
479
+ }
480
+
481
+ const argumentsMetadata = handlerMetadata.arguments || {};
482
+ const args: Array<unknown> = [];
564
483
 
565
- context = newContext;
484
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
485
+ switch (argumentMetadata.type) {
486
+ case webSocketConnectionArgsKey:
487
+ args[argumentMetadata.index] = connection;
488
+ break;
489
+ case webSocketMessageArgsKey:
490
+ args[argumentMetadata.index] = message;
491
+ break;
492
+ case webSocketServerArgsKey:
493
+ args[argumentMetadata.index] =
494
+ this.#globalContext.get(webSocketServerArgsKey);
495
+ break;
566
496
  }
497
+ }
567
498
 
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
- });
499
+ handlerMetadata.descriptor.value(...args);
500
+ },
501
+ drain: (connection: ServerWebSocket<T>) => {
502
+ const pathnameKey = `${connection.data.pathname}:::drain`;
503
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
581
504
 
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
- );
505
+ if (!handlerMetadata) {
506
+ return;
507
+ }
508
+
509
+ const argumentsMetadata = handlerMetadata.arguments || {};
510
+ const args: Array<unknown> = [];
627
511
 
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
- );
512
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
513
+ switch (argumentMetadata.type) {
514
+ case webSocketConnectionArgsKey:
515
+ args[argumentMetadata.index] = connection;
516
+ break;
517
+ case webSocketServerArgsKey:
518
+ args[argumentMetadata.index] =
519
+ this.#globalContext.get(webSocketServerArgsKey);
520
+ break;
634
521
  }
635
522
  }
523
+
524
+ handlerMetadata.descriptor.value(...args);
636
525
  },
637
- websocket: {
638
- open: (connection) => {
639
- const pathnameKey = `${connection.data.pathname}:::open`;
640
- const handlerMetadata = webSocketsMap.get(pathnameKey);
526
+ ping: (connection: ServerWebSocket<T>, data) => {
527
+ const pathnameKey = `${connection.data.pathname}:::ping`;
528
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
641
529
 
642
- if (!handlerMetadata) {
643
- return;
644
- }
530
+ if (!handlerMetadata) {
531
+ return;
532
+ }
533
+
534
+ const argumentsMetadata = handlerMetadata.arguments || {};
535
+ const args: Array<unknown> = [];
645
536
 
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
- }
537
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
538
+ switch (argumentMetadata.type) {
539
+ case webSocketConnectionArgsKey:
540
+ args[argumentMetadata.index] = connection;
541
+ break;
542
+ case webSocketMessageArgsKey:
543
+ args[argumentMetadata.index] = data;
544
+ break;
545
+ case webSocketServerArgsKey:
546
+ args[argumentMetadata.index] =
547
+ this.#globalContext.get(webSocketServerArgsKey);
548
+ break;
658
549
  }
550
+ }
659
551
 
660
- handlerMetadata.descriptor.value(...args);
661
- },
662
- close: (connection, code, reason) => {
663
- const pathnameKey = `${connection.data.pathname}:::close`;
664
- const handlerMetadata = webSocketsMap.get(pathnameKey);
552
+ handlerMetadata.descriptor.value(...args);
553
+ },
554
+ pong: (connection: ServerWebSocket<T>, data) => {
555
+ const pathnameKey = `${connection.data.pathname}:::pong`;
556
+ const handlerMetadata = webSocketsMap.get(pathnameKey);
665
557
 
666
- if (!handlerMetadata) {
667
- return;
668
- }
558
+ if (!handlerMetadata) {
559
+ return;
560
+ }
561
+
562
+ const argumentsMetadata = handlerMetadata.arguments || {};
563
+ const args: Array<unknown> = [];
669
564
 
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
- }
565
+ for (const [, argumentMetadata] of Object.entries(argumentsMetadata)) {
566
+ switch (argumentMetadata.type) {
567
+ case webSocketConnectionArgsKey:
568
+ args[argumentMetadata.index] = connection;
569
+ break;
570
+ case webSocketMessageArgsKey:
571
+ args[argumentMetadata.index] = data;
572
+ break;
573
+ case webSocketServerArgsKey:
574
+ args[argumentMetadata.index] =
575
+ this.#globalContext.get(webSocketServerArgsKey);
576
+ break;
688
577
  }
578
+ }
689
579
 
690
- handlerMetadata.descriptor.value(...args);
691
- },
692
- message: (connection, message) => {
693
- const pathnameKey = `${connection.data.pathname}:::message`;
694
- const handlerMetadata = webSocketsMap.get(pathnameKey);
580
+ handlerMetadata.descriptor.value(...args);
581
+ }
582
+ } satisfies WebSocketHandler<T>;
583
+ }
695
584
 
696
- if (!handlerMetadata) {
697
- return;
698
- }
585
+ /**
586
+ *
587
+ * @param param0
588
+ * @returns
589
+ */
590
+ async #webSocketFetcher({
591
+ request,
592
+ server,
593
+ context,
594
+ url,
595
+ query,
596
+ method,
597
+ responseHeaders,
598
+ httpRouterGroup,
599
+ startMiddlewareHandlers,
600
+ endMiddlewareHandlers
601
+ }: Required<{
602
+ server: Server<TWebSocketUpgradeData>;
603
+ request: Request;
604
+ context: Context;
605
+ url: URL;
606
+ query: ReturnType<typeof QsParse>;
607
+ method: THttpMethods;
608
+ responseHeaders: Headers;
609
+ httpRouterGroup: HttpRouterGroup;
610
+ startMiddlewareHandlers: TStartMiddlewareHandlers;
611
+ endMiddlewareHandlers: TEndMiddlewareHandlers;
612
+ }>) {
613
+ try {
614
+ await this.#pipesEnforcer({
615
+ type: "START_MIDDLEWARES",
616
+ handlers: startMiddlewareHandlers,
617
+ context: context
618
+ });
699
619
 
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
- }
620
+ const routeResult = httpRouterGroup.find({
621
+ pathname: url.pathname,
622
+ method: method
623
+ });
716
624
 
717
- handlerMetadata.descriptor.value(...args);
718
- },
719
- drain: (connection) => {
720
- const pathnameKey = `${connection.data.pathname}:::drain`;
721
- const handlerMetadata = webSocketsMap.get(pathnameKey);
625
+ let upgradeResult: Response | undefined = this.finalizeResponse(
626
+ new Response(undefined, {
627
+ ...Objects.serverErrorStatuses.INTERNAL_SERVER_ERROR,
628
+ headers: responseHeaders
629
+ })
630
+ );
722
631
 
723
- if (!handlerMetadata) {
724
- return;
725
- }
632
+ if (routeResult) {
633
+ const authResult = await this.#pipesEnforcer({
634
+ type: "GUARDS",
635
+ handlers: routeResult.guardHandlers,
636
+ context: context
637
+ });
726
638
 
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
- }
639
+ if (authResult !== true) {
640
+ upgradeResult = this.finalizeResponse(
641
+ new Response(undefined, {
642
+ ...(!authResult || authResult === "UNAUTHORIZATION"
643
+ ? Objects.clientErrorStatuses.UNAUTHORIZED
644
+ : Objects.clientErrorStatuses.FORBIDDEN),
645
+ headers: responseHeaders
646
+ })
647
+ );
648
+ } else {
649
+ context.set(routeModelArgsKey, routeResult.model);
740
650
 
741
- handlerMetadata.descriptor.value(...args);
742
- },
743
- ping: (connection, data) => {
744
- const pathnameKey = `${connection.data.pathname}:::ping`;
745
- const handlerMetadata = webSocketsMap.get(pathnameKey);
651
+ await this.#pipesEnforcer({
652
+ type: "OPEN_INTERCEPTORS",
653
+ handlers: routeResult.openInterceptorHandlers,
654
+ context: context
655
+ });
746
656
 
747
- if (!handlerMetadata) {
748
- return;
749
- }
657
+ const upgradeExecution = await routeResult.model.func(
658
+ ...[server, request, query]
659
+ );
660
+
661
+ await this.#pipesEnforcer({
662
+ type: "CLOSE_INTERCEPTORS",
663
+ handlers: routeResult.closeInterceptorHandlers,
664
+ context: context
665
+ });
750
666
 
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
- }
667
+ if (typeof upgradeExecution !== "boolean" || !upgradeExecution) {
668
+ upgradeResult = this.finalizeResponse(
669
+ new Response(undefined, {
670
+ ...Objects.serverErrorStatuses.INTERNAL_SERVER_ERROR,
671
+ headers: responseHeaders
672
+ })
673
+ );
674
+ } else {
675
+ upgradeResult = undefined;
766
676
  }
677
+ }
678
+ } else {
679
+ }
767
680
 
768
- handlerMetadata.descriptor.value(...args);
769
- },
770
- pong: (connection, data) => {
771
- const pathnameKey = `${connection.data.pathname}:::pong`;
772
- const handlerMetadata = webSocketsMap.get(pathnameKey);
681
+ await this.#pipesEnforcer({
682
+ type: "END_MIDDLEWARES",
683
+ handlers: endMiddlewareHandlers,
684
+ context: context
685
+ });
773
686
 
774
- if (!handlerMetadata) {
775
- return;
776
- }
687
+ return upgradeResult;
688
+ } catch (error) {
689
+ console.error(error);
690
+ throw error;
691
+ }
692
+ }
693
+
694
+ /**
695
+ *
696
+ * @param param0
697
+ * @returns
698
+ */
699
+ async #staticFetcher({
700
+ url,
701
+ path,
702
+ headers,
703
+ cacheTimeInSeconds,
704
+ responseHeaders
705
+ }: {
706
+ url: URL;
707
+ path: string;
708
+ responseHeaders: Headers;
709
+ headers?: TParamsType;
710
+ cacheTimeInSeconds?: number;
711
+ }) {
712
+ const pathname = `${path}/${url.pathname}`;
713
+ const cachedFile = this.#staticMap.get(pathname);
777
714
 
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
- }
715
+ if (!cachedFile) {
716
+ const file = Bun.file(pathname);
717
+ const isFileExists = await file.exists();
718
+
719
+ if (isFileExists) {
720
+ if (headers) {
721
+ for (const [key, value] of Object.entries(headers)) {
722
+ responseHeaders.set(key, value);
793
723
  }
724
+ }
794
725
 
795
- handlerMetadata.descriptor.value(...args);
726
+ responseHeaders.set("Content-Type", file.type);
727
+
728
+ return this.finalizeResponse(
729
+ new Response(await file.arrayBuffer(), {
730
+ ...Objects.successfulStatuses.OK,
731
+ headers: responseHeaders
732
+ })
733
+ );
734
+ }
735
+ } else {
736
+ const isExpired = new Date() > cachedFile.expiredAt;
737
+
738
+ if (isExpired) {
739
+ this.#staticMap.delete(pathname);
740
+ }
741
+
742
+ const file = !isExpired ? cachedFile.file : Bun.file(pathname);
743
+ const isFileExists = await file.exists();
744
+
745
+ if (isFileExists) {
746
+ this.#staticMap.set(
747
+ pathname,
748
+ Object.freeze({
749
+ expiredAt: TimeAdd(
750
+ new Date(),
751
+ this.#staticCacheTimeInSecond,
752
+ ETimeUnit.seconds
753
+ ),
754
+ file: file
755
+ })
756
+ );
757
+
758
+ if (headers) {
759
+ for (const [key, value] of Object.entries(headers)) {
760
+ responseHeaders.set(key, value);
761
+ }
796
762
  }
763
+
764
+ responseHeaders.set("Content-Type", file.type);
765
+
766
+ return this.finalizeResponse(
767
+ new Response(await file.arrayBuffer(), {
768
+ ...Objects.successfulStatuses.OK,
769
+ headers: responseHeaders
770
+ })
771
+ );
797
772
  }
773
+ }
774
+ }
775
+
776
+ /**
777
+ *
778
+ * @param param0
779
+ * @returns
780
+ */
781
+ async #httpFetcher({
782
+ context,
783
+ url,
784
+ method,
785
+ httpRouterGroup,
786
+ startMiddlewareHandlers = [],
787
+ endMiddlewareHandlers = []
788
+ }: {
789
+ context: Context;
790
+ url: URL;
791
+ method: THttpMethods;
792
+ httpRouterGroup: HttpRouterGroup;
793
+ startMiddlewareHandlers?: TStartMiddlewareHandlers;
794
+ endMiddlewareHandlers?: TEndMiddlewareHandlers;
795
+ }) {
796
+ const contextOptions = { isStatic: true };
797
+
798
+ context.setOptions(contextOptions);
799
+
800
+ await this.#pipesEnforcer({
801
+ type: "START_MIDDLEWARES",
802
+ handlers: startMiddlewareHandlers,
803
+ context: context
804
+ });
805
+
806
+ const routeResult = httpRouterGroup.find({
807
+ pathname: url.pathname,
808
+ method: method
809
+ });
810
+
811
+ if (!routeResult) {
812
+ context
813
+ .setOptions({ isStatic: false })
814
+ .set(responseStatusArgsKey, Objects.clientErrorStatuses.NOT_FOUND.status)
815
+ .set(responseStatusTextArgsKey, Objects.clientErrorStatuses.NOT_FOUND.statusText)
816
+ .set(responseBodyArgsKey, undefined);
817
+ } else {
818
+ context.set(routeModelArgsKey, routeResult.model);
819
+
820
+ const authResult = await this.#pipesEnforcer({
821
+ type: "GUARDS",
822
+ handlers: routeResult.guardHandlers,
823
+ context: context
824
+ });
825
+
826
+ if (authResult !== true) {
827
+ context
828
+ .setOptions({ isStatic: false })
829
+ .set(
830
+ responseStatusArgsKey,
831
+ !authResult || authResult === "UNAUTHORIZATION"
832
+ ? Objects.clientErrorStatuses.UNAUTHORIZED.status
833
+ : Objects.clientErrorStatuses.FORBIDDEN.status
834
+ )
835
+ .set(
836
+ responseStatusTextArgsKey,
837
+ !authResult || authResult === "UNAUTHORIZATION"
838
+ ? Objects.clientErrorStatuses.UNAUTHORIZED.statusText
839
+ : Objects.clientErrorStatuses.FORBIDDEN.statusText
840
+ )
841
+ .set(responseBodyArgsKey, undefined);
842
+ } else {
843
+ await this.#pipesEnforcer({
844
+ type: "OPEN_INTERCEPTORS",
845
+ handlers: routeResult.openInterceptorHandlers,
846
+ context: context
847
+ });
848
+
849
+ await this.#pipesEnforcer({
850
+ type: "CONTROLLER",
851
+ context: context
852
+ });
853
+
854
+ await this.#pipesEnforcer({
855
+ type: "CLOSE_INTERCEPTORS",
856
+ handlers: routeResult.closeInterceptorHandlers,
857
+ context: context
858
+ });
859
+ }
860
+ }
861
+
862
+ await this.#pipesEnforcer({
863
+ type: "END_MIDDLEWARES",
864
+ handlers: endMiddlewareHandlers,
865
+ context: context
866
+ });
867
+
868
+ const latestResponseHeaders =
869
+ context.get<Headers | null | undefined>(responseHeadersArgsKey, {
870
+ isStatic: true
871
+ }) || new Headers(),
872
+ latestResponseBody =
873
+ context.get<unknown>(responseBodyArgsKey, { isStatic: false }) || undefined,
874
+ latestResponseStatus = context.get<unknown>(responseStatusArgsKey, {
875
+ isStatic: false
876
+ }),
877
+ latestResponseStatusText = context.get<unknown>(responseStatusTextArgsKey, {
878
+ isStatic: false
879
+ });
880
+
881
+ return this.serializeResponse({
882
+ status:
883
+ typeof latestResponseStatus !== "number"
884
+ ? method === "POST"
885
+ ? 201
886
+ : undefined
887
+ : latestResponseStatus,
888
+ statusText:
889
+ typeof latestResponseStatusText !== "string" ? undefined : latestResponseStatusText,
890
+ headers: latestResponseHeaders,
891
+ data: latestResponseBody
798
892
  });
799
893
  }
800
894
 
@@ -803,20 +897,38 @@ export class Application<TRootClass extends Object = Object> {
803
897
  * @param param0
804
898
  * @returns
805
899
  */
806
- private async containerResolution({
900
+ async #containerResolver({
901
+ prefix = "",
902
+ startMiddlewareHandlers = [],
903
+ endMiddlewareHandlers = [],
904
+ guardHandlers = [],
905
+ openInterceptorHandlers = [],
906
+ closeInterceptorHandlers = [],
907
+ controllerRouterGroup = new HttpRouterGroup(),
908
+ webSocketHttpRouterGroup = new HttpRouterGroup(),
909
+ webSocketRouterGroup = new WebSocketRouterGroup(),
807
910
  containerClass,
808
911
  options,
809
912
  extendInjector
810
913
  }: {
811
914
  containerClass: TConstructor<unknown>;
812
- options: TApplicationOptions;
813
- extendInjector: Injector;
915
+ prefix?: string;
916
+ options: Omit<TApplicationOptions, "prefix">;
917
+ extendInjector?: Injector;
918
+ startMiddlewareHandlers?: TStartMiddlewareHandlers;
919
+ endMiddlewareHandlers?: TEndMiddlewareHandlers;
920
+ guardHandlers?: TGuardHandlers;
921
+ openInterceptorHandlers?: TOpenInterceptorHandlers;
922
+ closeInterceptorHandlers?: TCloseInterceptorHandlers;
923
+ controllerRouterGroup?: HttpRouterGroup;
924
+ webSocketHttpRouterGroup?: HttpRouterGroup;
925
+ webSocketRouterGroup?: WebSocketRouterGroup;
814
926
  }) {
815
927
  if (!Reflect.getOwnMetadataKeys(containerClass).includes(containerKey)) {
816
- throw Error(`${containerClass.name} is not a container.`);
928
+ throw Error(`[${containerClass.name}] is not a container.`);
817
929
  }
818
930
 
819
- const injector = new Injector(extendInjector);
931
+ const injector = !extendInjector ? new Injector() : new Injector(extendInjector);
820
932
  const containerMetadata: TContainerMetadata = Reflect.getOwnMetadata(
821
933
  containerKey,
822
934
  containerClass
@@ -825,8 +937,11 @@ export class Application<TRootClass extends Object = Object> {
825
937
  const {
826
938
  loaders,
827
939
  middlewares,
828
- guards,
829
940
  dependencies,
941
+ modules,
942
+ guards,
943
+ interceptors,
944
+ prefix: containerPrefix,
830
945
  config: containerConfig
831
946
  } = containerMetadata || {};
832
947
 
@@ -902,28 +1017,38 @@ export class Application<TRootClass extends Object = Object> {
902
1017
  //#endregion
903
1018
 
904
1019
  //#region [Dependencies]
905
- !dependencies || dependencies.map((dependency) => injector.get(dependency));
1020
+ if (dependencies) {
1021
+ for (const dependency of dependencies) {
1022
+ const registerResult = injector.get(dependency);
1023
+
1024
+ if (!registerResult) {
1025
+ throw new Error(`Can not collect dependency [${dependency.name}].`);
1026
+ }
1027
+ }
1028
+ }
906
1029
  //#endregion
907
1030
 
908
1031
  //#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) => {
1032
+ if (middlewares) {
1033
+ const middlwareChecker = (
1034
+ instance: unknown,
1035
+ ofClass: unknown
1036
+ ): ofClass is IMiddleware => !!instance;
1037
+
1038
+ for (const middleware of middlewares) {
918
1039
  const instance = injector.get<IMiddleware>(middleware);
919
1040
 
1041
+ if (!instance || !middlwareChecker(instance, middleware)) {
1042
+ throw new Error(`Can not collect middleware [${middleware.name}].`);
1043
+ }
1044
+
920
1045
  if (instance.start && typeof instance.start === "function") {
921
1046
  const argumentsMetadata: TArgumentsMetadataCollection =
922
1047
  Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
923
1048
 
924
- startMiddlewareGroup.push(
1049
+ startMiddlewareHandlers.push(
925
1050
  Object.freeze({
926
- class: middleware as IMiddleware,
1051
+ class: middleware,
927
1052
  funcName: "start",
928
1053
  func: instance.start.bind(instance),
929
1054
  argumentsMetadata: argumentsMetadata
@@ -935,42 +1060,131 @@ export class Application<TRootClass extends Object = Object> {
935
1060
  const argumentsMetadata: TArgumentsMetadataCollection =
936
1061
  Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
937
1062
 
938
- endMiddlewareGroup.push(
1063
+ endMiddlewareHandlers.push(
939
1064
  Object.freeze({
940
- class: middleware as IMiddleware,
1065
+ class: middleware,
941
1066
  funcName: "end",
942
1067
  func: instance.end.bind(instance),
943
1068
  argumentsMetadata: argumentsMetadata
944
1069
  })
945
1070
  );
946
1071
  }
947
- });
1072
+ }
1073
+ }
948
1074
  //#endregion
949
1075
 
950
1076
  //#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
- });
1077
+ if (guards) {
1078
+ const guardChecker = (instance: unknown, ofClass: unknown): ofClass is IGuard =>
1079
+ !!instance;
1080
+
1081
+ for (const guard of guards) {
1082
+ const instance = injector.get<IGuard>(guard);
1083
+
1084
+ if (!instance || !guardChecker(instance, guard)) {
1085
+ throw new Error(`Can not collect guard [${guard.name}].`);
1086
+ }
1087
+
1088
+ const argumentsMetadata: TArgumentsMetadataCollection =
1089
+ Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
1090
+
1091
+ guardHandlers.push(
1092
+ Object.freeze({
1093
+ class: guard,
1094
+ funcName: "enforce",
1095
+ func: instance.enforce.bind(instance),
1096
+ argumentsMetadata: argumentsMetadata
1097
+ })
1098
+ );
1099
+ }
1100
+ }
1101
+ //#endregion
1102
+
1103
+ //#region [Interceptor(s)]
1104
+ if (interceptors) {
1105
+ const interceptorChecker = (
1106
+ instance: unknown,
1107
+ ofClass: unknown
1108
+ ): ofClass is IInterceptor => !!instance;
1109
+
1110
+ for (const interceptor of interceptors) {
1111
+ const instance = injector.get<IInterceptor>(interceptor);
1112
+
1113
+ if (!instance || !interceptorChecker(instance, interceptor)) {
1114
+ throw new Error(`Can not collect interceptor [${interceptor.name}].`);
1115
+ }
1116
+
1117
+ if (instance) {
1118
+ if (instance.open && typeof instance.open === "function") {
1119
+ const argumentsMetadata: TArgumentsMetadataCollection =
1120
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "open") || {};
1121
+
1122
+ openInterceptorHandlers.push(
1123
+ Object.freeze({
1124
+ class: interceptor,
1125
+ funcName: "open",
1126
+ func: instance.open.bind(instance),
1127
+ argumentsMetadata: argumentsMetadata
1128
+ })
1129
+ );
1130
+ }
1131
+
1132
+ if (instance.close && typeof instance.close === "function") {
1133
+ const argumentsMetadata: TArgumentsMetadataCollection =
1134
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "close") || {};
1135
+
1136
+ closeInterceptorHandlers.push(
1137
+ Object.freeze({
1138
+ class: interceptor,
1139
+ funcName: "close",
1140
+ func: instance.close.bind(instance),
1141
+ argumentsMetadata: argumentsMetadata
1142
+ })
1143
+ );
1144
+ }
1145
+ }
1146
+ }
1147
+ }
1148
+ //#endregion
1149
+
1150
+ //#region [Module(s)]
1151
+ if (modules) {
1152
+ const fullPrefix = [prefix.trim(), containerPrefix?.trim() || ""]
1153
+ .filter((prefix) => prefix.length > 0)
1154
+ .join("/");
1155
+
1156
+ for (const module of modules) {
1157
+ try {
1158
+ await this.#moduleResolver({
1159
+ prefix: fullPrefix,
1160
+ moduleClass: module,
1161
+ extendInjector: injector,
1162
+ options: options,
1163
+ startMiddlewareHandlers: startMiddlewareHandlers,
1164
+ endMiddlewareHandlers: endMiddlewareHandlers,
1165
+ guardHandlers: [...guardHandlers],
1166
+ openInterceptorHandlers: [...openInterceptorHandlers],
1167
+ closeInterceptorHandlers: [...closeInterceptorHandlers],
1168
+ controllerRouterGroup: controllerRouterGroup,
1169
+ webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1170
+ webSocketRouterGroup: webSocketRouterGroup
1171
+ });
1172
+ } catch (error: unknown) {
1173
+ console.group(`Can not resolve module: [${module.name}].`);
1174
+ console.error(error);
1175
+ console.groupEnd();
1176
+ throw error;
1177
+ }
1178
+ }
1179
+ }
967
1180
  //#endregion
968
1181
 
969
1182
  return Object.freeze({
970
- injector,
971
- startMiddlewareGroup,
972
- endMiddlewareGroup,
973
- guardGroup
1183
+ startMiddlewareHandlers: startMiddlewareHandlers,
1184
+ endMiddlewareHandlers: endMiddlewareHandlers,
1185
+ controllerRouterGroup: controllerRouterGroup,
1186
+ webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1187
+ webSocketRouterGroup: webSocketRouterGroup
974
1188
  });
975
1189
  }
976
1190
 
@@ -979,20 +1193,38 @@ export class Application<TRootClass extends Object = Object> {
979
1193
  * @param param0
980
1194
  * @returns
981
1195
  */
982
- private async moduleResolution({
1196
+ async #moduleResolver({
1197
+ prefix = "",
1198
+ startMiddlewareHandlers = [],
1199
+ endMiddlewareHandlers = [],
1200
+ guardHandlers = [],
1201
+ openInterceptorHandlers = [],
1202
+ closeInterceptorHandlers = [],
1203
+ controllerRouterGroup = new HttpRouterGroup(),
1204
+ webSocketHttpRouterGroup = new HttpRouterGroup(),
1205
+ webSocketRouterGroup = new WebSocketRouterGroup(),
983
1206
  moduleClass,
984
1207
  options,
985
1208
  extendInjector
986
1209
  }: {
1210
+ prefix?: string;
987
1211
  moduleClass: TConstructor<unknown>;
988
- options: TApplicationOptions;
989
- extendInjector: Injector;
1212
+ options: Omit<TApplicationOptions, "prefix">;
1213
+ extendInjector?: Injector;
1214
+ startMiddlewareHandlers?: TStartMiddlewareHandlers;
1215
+ endMiddlewareHandlers?: TEndMiddlewareHandlers;
1216
+ guardHandlers?: TGuardHandlers;
1217
+ openInterceptorHandlers?: TOpenInterceptorHandlers;
1218
+ closeInterceptorHandlers?: TCloseInterceptorHandlers;
1219
+ controllerRouterGroup?: HttpRouterGroup;
1220
+ webSocketHttpRouterGroup?: HttpRouterGroup;
1221
+ webSocketRouterGroup?: WebSocketRouterGroup;
990
1222
  }) {
991
1223
  if (!Reflect.getOwnMetadataKeys(moduleClass).includes(moduleKey)) {
992
- throw Error(`${moduleClass.name} is not a module.`);
1224
+ throw Error(`[${moduleClass.name}] is not a module.`);
993
1225
  }
994
1226
 
995
- const injector = new Injector(extendInjector);
1227
+ const injector = !extendInjector ? new Injector() : new Injector(extendInjector);
996
1228
  const moduleMetadata: TModuleMetadata = Reflect.getOwnMetadata(moduleKey, moduleClass);
997
1229
 
998
1230
  const {
@@ -1007,7 +1239,9 @@ export class Application<TRootClass extends Object = Object> {
1007
1239
  config: moduleConfig
1008
1240
  } = moduleMetadata || {};
1009
1241
 
1010
- const fullPrefix = `${options.prefix || ""}/${modulePrefix || ""}`;
1242
+ const fullPrefix = [prefix.trim(), modulePrefix?.trim() || ""]
1243
+ .filter((prefix) => prefix.length > 0)
1244
+ .join("/");
1011
1245
 
1012
1246
  //#region [Configuration(s)]
1013
1247
  const { config } = Object.freeze({
@@ -1087,28 +1321,38 @@ export class Application<TRootClass extends Object = Object> {
1087
1321
  //#endregion
1088
1322
 
1089
1323
  //#region [Dependencies]
1090
- !dependencies || dependencies.map((dependency) => injector.get(dependency));
1324
+ if (dependencies) {
1325
+ for (const dependency of dependencies) {
1326
+ const registerResult = injector.get(dependency);
1327
+
1328
+ if (!registerResult) {
1329
+ throw new Error(`Can not collect dependency [${dependency.name}].`);
1330
+ }
1331
+ }
1332
+ }
1091
1333
  //#endregion
1092
1334
 
1093
1335
  //#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) => {
1336
+ if (middlewares) {
1337
+ const middlwareChecker = (
1338
+ instance: unknown,
1339
+ ofClass: unknown
1340
+ ): ofClass is IMiddleware => !!instance;
1341
+
1342
+ for (const middleware of middlewares) {
1103
1343
  const instance = injector.get<IMiddleware>(middleware);
1104
1344
 
1345
+ if (!instance || !middlwareChecker(instance, middleware)) {
1346
+ throw new Error(`Can not collect middleware [${middleware.name}].`);
1347
+ }
1348
+
1105
1349
  if (instance.start && typeof instance.start === "function") {
1106
1350
  const argumentsMetadata: TArgumentsMetadataCollection =
1107
1351
  Reflect.getOwnMetadata(argumentsKey, middleware, "start") || {};
1108
1352
 
1109
- startMiddlewareGroup.push(
1353
+ startMiddlewareHandlers.push(
1110
1354
  Object.freeze({
1111
- class: middleware as IMiddleware,
1355
+ class: middleware,
1112
1356
  funcName: "start",
1113
1357
  func: instance.start.bind(instance),
1114
1358
  argumentsMetadata: argumentsMetadata
@@ -1120,200 +1364,159 @@ export class Application<TRootClass extends Object = Object> {
1120
1364
  const argumentsMetadata: TArgumentsMetadataCollection =
1121
1365
  Reflect.getOwnMetadata(argumentsKey, middleware, "end") || {};
1122
1366
 
1123
- endMiddlewareGroup.push(
1367
+ endMiddlewareHandlers.push(
1124
1368
  Object.freeze({
1125
- class: middleware as IMiddleware,
1369
+ class: middleware,
1126
1370
  funcName: "end",
1127
1371
  func: instance.end.bind(instance),
1128
1372
  argumentsMetadata: argumentsMetadata
1129
1373
  })
1130
1374
  );
1131
1375
  }
1132
- });
1376
+ }
1377
+ }
1133
1378
  //#endregion
1134
1379
 
1135
1380
  //#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
- });
1381
+ if (guards) {
1382
+ const guardChecker = (instance: unknown, ofClass: unknown): ofClass is IGuard =>
1383
+ !!instance;
1384
+
1385
+ for (const guard of guards) {
1386
+ const instance = injector.get<IGuard>(guard);
1387
+
1388
+ if (!instance || !guardChecker(instance, guard)) {
1389
+ throw new Error(`Can not collect guard [${guard.name}].`);
1390
+ }
1391
+
1392
+ const argumentsMetadata: TArgumentsMetadataCollection =
1393
+ Reflect.getOwnMetadata(argumentsKey, guard, "enforce") || {};
1394
+
1395
+ guardHandlers.push(
1396
+ Object.freeze({
1397
+ class: guard,
1398
+ funcName: "enforce",
1399
+ func: instance.enforce.bind(instance),
1400
+ argumentsMetadata: argumentsMetadata
1401
+ })
1402
+ );
1403
+ }
1404
+ }
1152
1405
  //#endregion
1153
1406
 
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
- > = [];
1407
+ //#region [Interceptor(s)]
1408
+ if (interceptors) {
1409
+ const interceptorChecker = (
1410
+ instance: unknown,
1411
+ ofClass: unknown
1412
+ ): ofClass is IInterceptor => !!instance;
1161
1413
 
1162
- !interceptors ||
1163
- interceptors.forEach((interceptor) => {
1414
+ for (const interceptor of interceptors) {
1164
1415
  const instance = injector.get<IInterceptor>(interceptor);
1165
1416
 
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
- );
1417
+ if (!instance || !interceptorChecker(instance, interceptor)) {
1418
+ throw new Error(`Can not collect interceptor [${interceptor.name}].`);
1178
1419
  }
1179
1420
 
1180
- if (instance.close && typeof instance.close === "function") {
1181
- const argumentsMetadata: TArgumentsMetadataCollection =
1182
- Reflect.getOwnMetadata(argumentsKey, interceptor, "close") || {};
1421
+ if (instance) {
1422
+ if (instance.open && typeof instance.open === "function") {
1423
+ const argumentsMetadata: TArgumentsMetadataCollection =
1424
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "open") || {};
1425
+
1426
+ openInterceptorHandlers.push(
1427
+ Object.freeze({
1428
+ class: interceptor,
1429
+ funcName: "open",
1430
+ func: instance.open.bind(instance),
1431
+ argumentsMetadata: argumentsMetadata
1432
+ })
1433
+ );
1434
+ }
1183
1435
 
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
- );
1436
+ if (instance.close && typeof instance.close === "function") {
1437
+ const argumentsMetadata: TArgumentsMetadataCollection =
1438
+ Reflect.getOwnMetadata(argumentsKey, interceptor, "close") || {};
1439
+
1440
+ closeInterceptorHandlers.push(
1441
+ Object.freeze({
1442
+ class: interceptor,
1443
+ funcName: "close",
1444
+ func: instance.close.bind(instance),
1445
+ argumentsMetadata: argumentsMetadata
1446
+ })
1447
+ );
1448
+ }
1192
1449
  }
1193
- });
1450
+ }
1451
+ }
1194
1452
  //#endregion
1195
1453
 
1196
1454
  //#region [Controller(s)]
1197
- const controllerRouterGroup = new HttpRouterGroup();
1198
-
1199
- !controllers ||
1200
- controllers.forEach((controllerConstructor) =>
1201
- this.initControllerInstance({
1202
- controllerConstructor,
1203
- httpRouterGroup: controllerRouterGroup,
1455
+ if (controllers) {
1456
+ for (const controller of controllers) {
1457
+ this.#controllerResolver({
1458
+ controllerConstructor: controller,
1459
+ prefix: fullPrefix,
1204
1460
  injector: injector,
1205
- prefix: fullPrefix
1206
- })
1207
- );
1461
+ httpRouterGroup: controllerRouterGroup,
1462
+ guardHandlers: [...guardHandlers],
1463
+ openInterceptorHandlers: [...openInterceptorHandlers],
1464
+ closeInterceptorHandlers: [...closeInterceptorHandlers]
1465
+ });
1466
+ }
1467
+ }
1208
1468
  //#endregion
1209
1469
 
1210
1470
  //#region [WebSocket(s)]
1211
- const webSocketHttpRouterGroup = new HttpRouterGroup();
1212
- const webSocketRouterGroup = new WebSocketRouterGroup();
1213
-
1214
- webSockets &&
1215
- webSockets.forEach((webSocket) =>
1216
- this.initWebSocketInstance({
1471
+ if (webSockets) {
1472
+ for (const webSocket of webSockets) {
1473
+ this.#webSocketResolver({
1217
1474
  webSocketConstructor: webSocket,
1218
- httpRouterGroup: webSocketHttpRouterGroup,
1219
- webSocketRouterGroup: webSocketRouterGroup,
1220
1475
  injector: injector,
1221
- prefix: fullPrefix
1222
- })
1223
- );
1476
+ prefix: fullPrefix,
1477
+ webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1478
+ webSocketRouterGroup: webSocketRouterGroup
1479
+ });
1480
+ }
1481
+ }
1224
1482
  //#endregion
1225
1483
 
1226
1484
  return Object.freeze({
1227
- prefix: modulePrefix || "",
1228
- injector: injector,
1229
- startMiddlewareGroup: startMiddlewareGroup,
1230
- endMiddlewareGroup: endMiddlewareGroup,
1231
- guardGroup: guardGroup,
1232
- openInterceptorGroup: openInterceptorGroup,
1233
- closeInterceptorGroup: closeInterceptorGroup,
1485
+ startMiddlewareHandlers: startMiddlewareHandlers,
1486
+ endMiddlewareHandlers: endMiddlewareHandlers,
1234
1487
  controllerRouterGroup: controllerRouterGroup,
1235
1488
  webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1236
1489
  webSocketRouterGroup: webSocketRouterGroup
1237
1490
  });
1238
1491
  }
1239
1492
 
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
1493
  /**
1297
1494
  *
1298
1495
  * @param param0
1299
1496
  * @returns
1300
1497
  */
1301
- private initControllerInstance({
1498
+ #controllerResolver({
1302
1499
  controllerConstructor,
1303
- httpRouterGroup,
1304
- injector,
1305
- prefix
1500
+ prefix = "",
1501
+ injector = new Injector(),
1502
+ httpRouterGroup = new HttpRouterGroup(),
1503
+ guardHandlers = [],
1504
+ openInterceptorHandlers = [],
1505
+ closeInterceptorHandlers = []
1306
1506
  }: Readonly<{
1307
1507
  controllerConstructor: TConstructor<unknown>;
1308
- httpRouterGroup: HttpRouterGroup;
1309
- injector: Injector;
1508
+ injector?: Injector;
1310
1509
  prefix?: string;
1510
+ httpRouterGroup?: HttpRouterGroup;
1511
+ guardHandlers?: TGuardHandlers;
1512
+ openInterceptorHandlers?: TOpenInterceptorHandlers;
1513
+ closeInterceptorHandlers?: TCloseInterceptorHandlers;
1311
1514
  }>) {
1312
1515
  if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
1313
- throw Error(`${controllerConstructor.name} is not a controller.`);
1516
+ throw Error(`[${controllerConstructor.name}] is not a controller.`);
1314
1517
  }
1315
1518
 
1316
- const controller = injector.get(controllerConstructor);
1519
+ const controller = injector.get<IController>(controllerConstructor);
1317
1520
 
1318
1521
  if (!controller) {
1319
1522
  throw Error("Can not initialize controller.");
@@ -1327,16 +1530,25 @@ export class Application<TRootClass extends Object = Object> {
1327
1530
  httpMetadata: []
1328
1531
  };
1329
1532
 
1533
+ const fullPrefix = [prefix.trim(), controllerMetadata.prefix.trim()]
1534
+ .filter((prefix) => prefix.length > 0)
1535
+ .join("/");
1536
+
1330
1537
  const router = new HttpRouter({
1331
- alias: `/${prefix || ""}/${controllerMetadata.prefix}`
1538
+ alias: fullPrefix,
1539
+ guardHandlers: guardHandlers,
1540
+ openInterceptorHandlers: openInterceptorHandlers,
1541
+ closeInterceptorHandlers: closeInterceptorHandlers
1332
1542
  });
1333
1543
 
1334
- controllerMetadata.httpMetadata.forEach((routeMetadata) => {
1544
+ for (const routeMetadata of controllerMetadata.httpMetadata) {
1335
1545
  if (typeof routeMetadata.descriptor.value !== "function") {
1336
- return;
1546
+ continue;
1337
1547
  }
1338
1548
 
1339
- const route = router.route(routeMetadata.path);
1549
+ const route = router.route({
1550
+ alias: routeMetadata.path
1551
+ });
1340
1552
  const handler = routeMetadata.descriptor.value.bind(controller);
1341
1553
  const httpRouteModel = Object.freeze({
1342
1554
  class: controllerConstructor,
@@ -1347,21 +1559,85 @@ export class Application<TRootClass extends Object = Object> {
1347
1559
 
1348
1560
  switch (routeMetadata.httpMethod) {
1349
1561
  case "GET":
1350
- return route.get({ model: httpRouteModel });
1562
+ route.get({ model: httpRouteModel });
1563
+ break;
1351
1564
  case "POST":
1352
- return route.post({ model: httpRouteModel });
1565
+ route.post({ model: httpRouteModel });
1566
+ break;
1353
1567
  case "PUT":
1354
- return route.put({ model: httpRouteModel });
1568
+ route.put({ model: httpRouteModel });
1569
+ break;
1355
1570
  case "PATCH":
1356
- return route.patch({ model: httpRouteModel });
1571
+ route.patch({ model: httpRouteModel });
1572
+ break;
1357
1573
  case "DELETE":
1358
- return route.delete({ model: httpRouteModel });
1574
+ route.delete({ model: httpRouteModel });
1575
+ break;
1359
1576
  case "OPTIONS":
1360
- return route.options({ model: httpRouteModel });
1577
+ route.options({ model: httpRouteModel });
1578
+ break;
1579
+ }
1580
+ }
1581
+
1582
+ return httpRouterGroup.add(router);
1583
+ }
1584
+
1585
+ /**
1586
+ *
1587
+ * @param param0
1588
+ * @returns
1589
+ */
1590
+ async #argumentsResolver<TValidationSchema = unknown>({
1591
+ data,
1592
+ validationSchema,
1593
+ argumentIndex,
1594
+ funcName
1595
+ }: {
1596
+ data: unknown;
1597
+ validationSchema: TValidationSchema;
1598
+ argumentIndex: number;
1599
+ funcName: string | symbol;
1600
+ }) {
1601
+ if (!this.#customValidator) {
1602
+ return data;
1603
+ }
1604
+
1605
+ try {
1606
+ const validation = await this.#customValidator.validate(
1607
+ data,
1608
+ validationSchema,
1609
+ argumentIndex,
1610
+ funcName
1611
+ );
1612
+
1613
+ if (!(validation instanceof ValidationFailed)) {
1614
+ return validation;
1615
+ }
1616
+
1617
+ throw new HttpClientError({
1618
+ httpCode: 400,
1619
+ message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
1620
+ data: validation.error
1621
+ });
1622
+ } catch (error) {
1623
+ if (error instanceof HttpClientError) {
1624
+ throw error;
1361
1625
  }
1362
- });
1363
1626
 
1364
- return httpRouterGroup.add(router);
1627
+ throw new HttpServerError({
1628
+ httpCode: 500,
1629
+ message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
1630
+ data: !(error instanceof Error)
1631
+ ? error
1632
+ : [
1633
+ {
1634
+ message: error.message,
1635
+ code: error.name,
1636
+ cause: error.cause
1637
+ }
1638
+ ]
1639
+ });
1640
+ }
1365
1641
  }
1366
1642
 
1367
1643
  /**
@@ -1369,24 +1645,30 @@ export class Application<TRootClass extends Object = Object> {
1369
1645
  * @param param0
1370
1646
  * @returns
1371
1647
  */
1372
- private initWebSocketInstance({
1373
- injector,
1374
- httpRouterGroup,
1375
- prefix,
1376
- webSocketRouterGroup,
1377
- webSocketConstructor
1648
+ #webSocketResolver({
1649
+ webSocketConstructor,
1650
+ prefix = "",
1651
+ injector = new Injector(),
1652
+ webSocketHttpRouterGroup = new HttpRouterGroup(),
1653
+ webSocketRouterGroup = new WebSocketRouterGroup(),
1654
+ guardHandlers = [],
1655
+ openInterceptorHandlers = [],
1656
+ closeInterceptorHandlers = []
1378
1657
  }: Readonly<{
1379
1658
  webSocketConstructor: TConstructor<unknown>;
1380
- httpRouterGroup: HttpRouterGroup;
1381
- webSocketRouterGroup: WebSocketRouterGroup;
1382
- injector: Injector;
1659
+ webSocketHttpRouterGroup?: HttpRouterGroup;
1660
+ webSocketRouterGroup?: WebSocketRouterGroup;
1661
+ injector?: Injector;
1383
1662
  prefix?: string;
1663
+ guardHandlers?: TGuardHandlers;
1664
+ openInterceptorHandlers?: TOpenInterceptorHandlers;
1665
+ closeInterceptorHandlers?: TCloseInterceptorHandlers;
1384
1666
  }>): Readonly<{
1385
- httpRouterGroup: HttpRouterGroup;
1667
+ webSocketHttpRouterGroup: HttpRouterGroup;
1386
1668
  webSocketRouterGroup: WebSocketRouterGroup;
1387
1669
  }> {
1388
1670
  if (!Reflect.getOwnMetadataKeys(webSocketConstructor).includes(webSocketKey)) {
1389
- throw Error(`${webSocketConstructor.name} is not a websocket route.`);
1671
+ throw Error(`[${webSocketConstructor.name}] is not a websocket route.`);
1390
1672
  }
1391
1673
 
1392
1674
  const webSocket = injector.get(webSocketConstructor);
@@ -1404,11 +1686,14 @@ export class Application<TRootClass extends Object = Object> {
1404
1686
  http: []
1405
1687
  };
1406
1688
 
1407
- const fullPrefix = `/${prefix || ""}/${webSocketMetadata.prefix}`;
1689
+ const fullPrefix = `/${prefix}/${webSocketMetadata.prefix}`;
1408
1690
 
1409
1691
  //#region [HTTP ROUTER]
1410
1692
  const router = new HttpRouter({
1411
- alias: fullPrefix
1693
+ alias: fullPrefix,
1694
+ guardHandlers: guardHandlers,
1695
+ openInterceptorHandlers: openInterceptorHandlers,
1696
+ closeInterceptorHandlers: closeInterceptorHandlers
1412
1697
  });
1413
1698
 
1414
1699
  for (const [_key, httpMetadata] of Object.entries(webSocketMetadata.http)) {
@@ -1416,7 +1701,9 @@ export class Application<TRootClass extends Object = Object> {
1416
1701
  continue;
1417
1702
  }
1418
1703
 
1419
- const route = router.route(httpMetadata.path);
1704
+ const route = router.route({
1705
+ alias: httpMetadata.path
1706
+ });
1420
1707
  const handler = httpMetadata.descriptor.value.bind(webSocket);
1421
1708
  const httpRouteModel = Object.freeze({
1422
1709
  class: webSocketConstructor,
@@ -1435,7 +1722,7 @@ export class Application<TRootClass extends Object = Object> {
1435
1722
  }
1436
1723
  }
1437
1724
 
1438
- httpRouterGroup.add(router);
1725
+ webSocketHttpRouterGroup.add(router);
1439
1726
  //#endregion
1440
1727
 
1441
1728
  //#region [WEBSOCKET ROUTER]
@@ -1455,142 +1742,55 @@ export class Application<TRootClass extends Object = Object> {
1455
1742
  //#endregion
1456
1743
 
1457
1744
  return Object.freeze({
1458
- httpRouterGroup: httpRouterGroup,
1745
+ webSocketHttpRouterGroup: webSocketHttpRouterGroup,
1459
1746
  webSocketRouterGroup: webSocketRouterGroup
1460
1747
  });
1461
1748
  }
1462
1749
 
1463
1750
  /**
1464
1751
  *
1465
- * @param param0
1466
- * @returns
1752
+ * @param data
1467
1753
  */
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
- );
1754
+ async #pipesEnforcer(
1755
+ data: TStartMiddlewaresPipe & {
1756
+ context: Context;
1496
1757
  }
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
- );
1758
+ ): Promise<undefined>;
1759
+ async #pipesEnforcer(
1760
+ data: TEndMiddlewaresPipe & {
1761
+ context: Context;
1509
1762
  }
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");
1763
+ ): Promise<undefined>;
1764
+ async #pipesEnforcer(
1765
+ data: TOpenInterceptorsPipe & {
1766
+ context: Context;
1528
1767
  }
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");
1768
+ ): Promise<undefined>;
1769
+ async #pipesEnforcer(
1770
+ data: TCloseInterceptorsPipe & {
1771
+ context: Context;
1538
1772
  }
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
- >;
1773
+ ): Promise<undefined>;
1774
+ async #pipesEnforcer(
1775
+ data: TGuardsPipe & {
1776
+ context: Context;
1777
+ }
1778
+ ): Promise<TGuardReturn>;
1779
+ async #pipesEnforcer(
1780
+ data: TControllerPipe & {
1781
+ context: Context;
1782
+ }
1783
+ ): Promise<undefined>;
1784
+ async #pipesEnforcer({
1785
+ type,
1786
+ handlers,
1787
+ context
1788
+ }: TPipesEnforcerUnion & {
1579
1789
  context: Context;
1580
- options: Partial<{
1581
- isContainer: boolean;
1582
- }>;
1583
- }>) {
1790
+ }): Promise<unknown> {
1584
1791
  const contextOptions = { isStatic: true };
1585
- const context = new Context(...(!outerContext ? [] : [outerContext])).setOptions(
1586
- contextOptions
1587
- );
1588
1792
 
1589
- if (route) {
1590
- context
1591
- .set(paramsArgsKey, route.parameters, { isPassthrough: true })
1592
- .set(routeModelArgsKey, route.model, { isPassthrough: true });
1593
- }
1793
+ context.setOptions(contextOptions);
1594
1794
 
1595
1795
  const httpServer =
1596
1796
  context.get<Server<TWebSocketUpgradeData> | null | undefined>(
@@ -1614,313 +1814,285 @@ export class Application<TRootClass extends Object = Object> {
1614
1814
  NonNullable<ReturnType<HttpRouterGroup["find"]>>["model"] | null | undefined
1615
1815
  >(routeModelArgsKey, contextOptions) || undefined;
1616
1816
 
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
- }
1817
+ if (type === "START_MIDDLEWARES" || type === "END_MIDDLEWARES") {
1818
+ const strategy =
1819
+ type === "START_MIDDLEWARES"
1820
+ ? this.#resolutedOptions.pipelineStrategy.startMiddlewares
1821
+ : this.#resolutedOptions.pipelineStrategy.endMiddlewares;
1712
1822
 
1713
- await handler(...args);
1823
+ for (
1824
+ let i = strategy === "FIFO" ? 0 : handlers.length - 1;
1825
+ strategy === "FIFO" ? i < handlers.length : i > -1;
1826
+ strategy === "FIFO" ? i++ : i--
1827
+ ) {
1828
+ const args = [];
1829
+ const { func: handler, funcName: functionName, argumentsMetadata } = handlers[i];
1830
+
1831
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1832
+ switch (argMetadata.type) {
1833
+ case contextArgsKey:
1834
+ args[argMetadata.index] = !argMetadata.key
1835
+ ? context
1836
+ : context.get(argMetadata.key, contextOptions);
1837
+ break;
1838
+ case requestArgsKey:
1839
+ args[argMetadata.index] = request;
1840
+ break;
1841
+ case requestBodyArgsKey:
1842
+ args[argMetadata.index] = !argMetadata.validationSchema
1843
+ ? await request?.[argMetadata.parser || "json"]()
1844
+ : await this.#argumentsResolver({
1845
+ data: await request?.[argMetadata.parser || "json"](),
1846
+ validationSchema: argMetadata.validationSchema,
1847
+ argumentIndex: argMetadata.index,
1848
+ funcName: functionName
1849
+ });
1850
+ break;
1851
+ case requestHeadersArgsKey:
1852
+ args[argMetadata.index] = !argMetadata.validationSchema
1853
+ ? requestHeaders
1854
+ : await this.#argumentsResolver({
1855
+ data: requestHeaders?.toJSON(),
1856
+ validationSchema: argMetadata.validationSchema,
1857
+ argumentIndex: argMetadata.index,
1858
+ funcName: functionName
1859
+ });
1860
+ break;
1861
+ case requestHeaderArgsKey:
1862
+ args[argMetadata.index] = !argMetadata.validationSchema
1863
+ ? requestHeaders?.get(argMetadata.key) || undefined
1864
+ : await this.#argumentsResolver({
1865
+ data: requestHeaders?.get(argMetadata.key) || undefined,
1866
+ validationSchema: argMetadata.validationSchema,
1867
+ argumentIndex: argMetadata.index,
1868
+ funcName: functionName
1869
+ });
1870
+ break;
1871
+ case paramArgsKey:
1872
+ args[argMetadata.index] = !argMetadata.validationSchema
1873
+ ? parameters?.[argMetadata.key] || undefined
1874
+ : await this.#argumentsResolver({
1875
+ data: parameters?.[argMetadata.key] || undefined,
1876
+ validationSchema: argMetadata.validationSchema,
1877
+ argumentIndex: argMetadata.index,
1878
+ funcName: functionName
1879
+ });
1880
+ break;
1881
+ case routeModelArgsKey:
1882
+ args[argMetadata.index] = undefined;
1883
+ break;
1884
+ case responseHeadersArgsKey:
1885
+ args[argMetadata.index] = responseHeaders;
1886
+ break;
1887
+ case httpServerArgsKey:
1888
+ args[argMetadata.index] = httpServer;
1889
+ break;
1890
+ default:
1891
+ args[argMetadata.index] = !argMetadata.validationSchema
1892
+ ? context.get(argMetadata.type, contextOptions)
1893
+ : await this.#argumentsResolver({
1894
+ data: context.get(argMetadata.type, contextOptions),
1895
+ validationSchema: argMetadata.validationSchema,
1896
+ argumentIndex: argMetadata.index,
1897
+ funcName: functionName
1898
+ });
1899
+ break;
1900
+ }
1714
1901
  }
1902
+
1903
+ await handler(...args);
1715
1904
  }
1905
+ } else if (type === "GUARDS") {
1906
+ if (!routeModel || handlers.length === 0) {
1907
+ return true;
1908
+ }
1909
+
1910
+ for (let i = 0; i < handlers.length; i++) {
1911
+ const args = [];
1912
+ const { func: handler, funcName: functionName, argumentsMetadata } = handlers[i];
1716
1913
 
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
- }
1914
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
1915
+ switch (argMetadata.type) {
1916
+ case requestArgsKey:
1917
+ args[argMetadata.index] = request;
1918
+ break;
1919
+ case requestBodyArgsKey:
1920
+ args[argMetadata.index] = !argMetadata.validationSchema
1921
+ ? await request?.[argMetadata.parser || "json"]()
1922
+ : await this.#argumentsResolver({
1923
+ data: await request?.[argMetadata.parser || "json"](),
1924
+ validationSchema: argMetadata.validationSchema,
1925
+ argumentIndex: argMetadata.index,
1926
+ funcName: functionName
1927
+ });
1928
+ break;
1929
+ case contextArgsKey:
1930
+ args[argMetadata.index] = !argMetadata.key
1931
+ ? context
1932
+ : context.get(argMetadata.key);
1933
+ break;
1934
+ case requestHeadersArgsKey:
1935
+ args[argMetadata.index] = !argMetadata.validationSchema
1936
+ ? requestHeaders
1937
+ : await this.#argumentsResolver({
1938
+ data: requestHeaders?.toJSON(),
1939
+ validationSchema: argMetadata.validationSchema,
1940
+ argumentIndex: argMetadata.index,
1941
+ funcName: functionName
1942
+ });
1943
+ break;
1944
+ case responseHeadersArgsKey:
1945
+ args[argMetadata.index] = responseHeaders;
1946
+ break;
1947
+ case requestHeaderArgsKey:
1948
+ args[argMetadata.index] = !argMetadata.validationSchema
1949
+ ? requestHeaders?.get(argMetadata.key) || undefined
1950
+ : await this.#argumentsResolver({
1951
+ data: requestHeaders?.get(argMetadata.key) || undefined,
1952
+ validationSchema: argMetadata.validationSchema,
1953
+ argumentIndex: argMetadata.index,
1954
+ funcName: functionName
1955
+ });
1956
+ break;
1957
+ case paramArgsKey:
1958
+ args[argMetadata.index] = !argMetadata.validationSchema
1959
+ ? parameters?.[argMetadata.key] || undefined
1960
+ : await this.#argumentsResolver({
1961
+ data: parameters?.[argMetadata.key],
1962
+ validationSchema: argMetadata.validationSchema,
1963
+ argumentIndex: argMetadata.index,
1964
+ funcName: functionName
1965
+ });
1966
+ break;
1967
+ case routeModelArgsKey:
1968
+ args[argMetadata.index] = routeModel;
1969
+ break;
1970
+ case httpServerArgsKey:
1971
+ args[argMetadata.index] = httpServer;
1972
+ break;
1973
+ default:
1974
+ args[argMetadata.index] = !argMetadata.validationSchema
1975
+ ? context.get(argMetadata.type)
1976
+ : await this.#argumentsResolver({
1977
+ data: context.get(argMetadata.type),
1978
+ validationSchema: argMetadata.validationSchema,
1979
+ argumentIndex: argMetadata.index,
1980
+ funcName: functionName
1981
+ });
1982
+ break;
1806
1983
  }
1984
+ }
1807
1985
 
1808
- const guardResult = await handler(...args);
1986
+ const guardResult = await handler(...args);
1809
1987
 
1810
- if (typeof guardResult !== "boolean" || !guardResult) {
1811
- throw new HttpClientError({
1812
- httpCode: 401,
1813
- message: "Unauthorization.",
1814
- data: undefined
1815
- });
1816
- }
1988
+ if (guardResult !== true) {
1989
+ return guardResult;
1817
1990
  }
1818
1991
  }
1819
- }
1820
1992
 
1821
- if (routeModel && !options?.isContainer) {
1822
- if (
1823
- resolutedMap &&
1824
- "openInterceptorGroup" in resolutedMap &&
1825
- resolutedMap.openInterceptorGroup
1993
+ return true;
1994
+ } else if (type === "OPEN_INTERCEPTORS" || type === "CLOSE_INTERCEPTORS") {
1995
+ if (!routeModel) {
1996
+ return;
1997
+ }
1998
+
1999
+ const strategy =
2000
+ type === "OPEN_INTERCEPTORS"
2001
+ ? this.#resolutedOptions.pipelineStrategy.openInterceptors
2002
+ : this.#resolutedOptions.pipelineStrategy.closeInterceptors;
2003
+
2004
+ for (
2005
+ let i = strategy === "FIFO" ? 0 : handlers.length - 1;
2006
+ strategy === "FIFO" ? i < handlers.length : i > -1;
2007
+ strategy === "FIFO" ? i++ : i--
1826
2008
  ) {
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
- }
2009
+ const args = [];
2010
+ const { func: handler, funcName: functionName, argumentsMetadata } = handlers[i];
1918
2011
 
1919
- await handler(...args);
2012
+ for (const [_key, argMetadata] of Object.entries(argumentsMetadata)) {
2013
+ switch (argMetadata.type) {
2014
+ case requestArgsKey:
2015
+ args[argMetadata.index] = request;
2016
+ break;
2017
+ case requestBodyArgsKey:
2018
+ args[argMetadata.index] = !argMetadata.validationSchema
2019
+ ? await request?.[argMetadata.parser || "json"]()
2020
+ : await this.#argumentsResolver({
2021
+ data: await request?.[argMetadata.parser || "json"](),
2022
+ validationSchema: argMetadata.validationSchema,
2023
+ argumentIndex: argMetadata.index,
2024
+ funcName: functionName
2025
+ });
2026
+ break;
2027
+ case contextArgsKey:
2028
+ args[argMetadata.index] = !argMetadata.key
2029
+ ? context
2030
+ : context.get(argMetadata.key);
2031
+ break;
2032
+ case requestHeadersArgsKey:
2033
+ args[argMetadata.index] = !argMetadata.validationSchema
2034
+ ? requestHeaders
2035
+ : await this.#argumentsResolver({
2036
+ data: requestHeaders?.toJSON(),
2037
+ validationSchema: argMetadata.validationSchema,
2038
+ argumentIndex: argMetadata.index,
2039
+ funcName: functionName
2040
+ });
2041
+ break;
2042
+ case requestHeaderArgsKey:
2043
+ args[argMetadata.index] = !argMetadata.validationSchema
2044
+ ? requestHeaders?.get(argMetadata.key) || undefined
2045
+ : await this.#argumentsResolver({
2046
+ data: requestHeaders?.get(argMetadata.key) || undefined,
2047
+ validationSchema: argMetadata.validationSchema,
2048
+ argumentIndex: argMetadata.index,
2049
+ funcName: functionName
2050
+ });
2051
+ break;
2052
+ case responseHeadersArgsKey:
2053
+ args[argMetadata.index] = responseHeaders;
2054
+ break;
2055
+ case paramArgsKey:
2056
+ args[argMetadata.index] = !argMetadata.validationSchema
2057
+ ? parameters?.[argMetadata.key] || undefined
2058
+ : await this.#argumentsResolver({
2059
+ data: parameters?.[argMetadata.key] || undefined,
2060
+ validationSchema: argMetadata.validationSchema,
2061
+ argumentIndex: argMetadata.index,
2062
+ funcName: functionName
2063
+ });
2064
+ break;
2065
+ case routeModelArgsKey:
2066
+ args[argMetadata.index] = routeModel;
2067
+ break;
2068
+ case httpServerArgsKey:
2069
+ args[argMetadata.index] = httpServer;
2070
+ break;
2071
+ default:
2072
+ args[argMetadata.index] = !argMetadata.validationSchema
2073
+ ? context.get(argMetadata.type)
2074
+ : await this.#argumentsResolver({
2075
+ data: context.get(argMetadata.type),
2076
+ validationSchema: argMetadata.validationSchema,
2077
+ argumentIndex: argMetadata.index,
2078
+ funcName: functionName
2079
+ });
2080
+ break;
2081
+ }
1920
2082
  }
2083
+
2084
+ await handler(...args);
2085
+ }
2086
+ } else if (type === "CONTROLLER") {
2087
+ if (!routeModel) {
2088
+ context
2089
+ .setOptions({ isStatic: false })
2090
+ .set(responseStatusArgsKey, 404)
2091
+ .set(responseStatusTextArgsKey, "Not found.");
2092
+
2093
+ return;
1921
2094
  }
1922
2095
 
1923
- // Execute controller action
1924
2096
  const controllerActionArguments: any[] = [];
1925
2097
  const {
1926
2098
  func: controllerAction,
@@ -1931,24 +2103,17 @@ export class Application<TRootClass extends Object = Object> {
1931
2103
  for (const [_key, argMetadata] of Object.entries(controllerActionArgumentsMetadata)) {
1932
2104
  switch (argMetadata.type) {
1933
2105
  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
- );
2106
+ controllerActionArguments[argMetadata.index] = request;
1942
2107
  break;
1943
2108
  case requestBodyArgsKey:
1944
2109
  controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1945
2110
  ? await request?.[argMetadata.parser || "json"]()
1946
- : await this.argumentsResolution(
1947
- await request?.[argMetadata.parser || "json"](),
1948
- argMetadata.validationSchema,
1949
- argMetadata.index,
1950
- controllerActionName
1951
- );
2111
+ : await this.#argumentsResolver({
2112
+ data: await request?.[argMetadata.parser || "json"](),
2113
+ validationSchema: argMetadata.validationSchema,
2114
+ argumentIndex: argMetadata.index,
2115
+ funcName: controllerActionName
2116
+ });
1952
2117
  break;
1953
2118
  case contextArgsKey:
1954
2119
  controllerActionArguments[argMetadata.index] = !argMetadata.key
@@ -1958,22 +2123,22 @@ export class Application<TRootClass extends Object = Object> {
1958
2123
  case requestHeadersArgsKey:
1959
2124
  controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1960
2125
  ? requestHeaders
1961
- : await this.argumentsResolution(
1962
- requestHeaders?.toJSON(),
1963
- argMetadata.validationSchema,
1964
- argMetadata.index,
1965
- controllerActionName
1966
- );
2126
+ : await this.#argumentsResolver({
2127
+ data: requestHeaders?.toJSON(),
2128
+ validationSchema: argMetadata.validationSchema,
2129
+ argumentIndex: argMetadata.index,
2130
+ funcName: controllerActionName
2131
+ });
1967
2132
  break;
1968
2133
  case requestHeaderArgsKey:
1969
2134
  controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1970
2135
  ? requestHeaders?.get(argMetadata.key) || undefined
1971
- : await this.argumentsResolution(
1972
- requestHeaders?.get(argMetadata.key) || undefined,
1973
- argMetadata.validationSchema,
1974
- argMetadata.index,
1975
- controllerActionName
1976
- );
2136
+ : await this.#argumentsResolver({
2137
+ data: requestHeaders?.get(argMetadata.key) || undefined,
2138
+ validationSchema: argMetadata.validationSchema,
2139
+ argumentIndex: argMetadata.index,
2140
+ funcName: controllerActionName
2141
+ });
1977
2142
  break;
1978
2143
  case responseHeadersArgsKey:
1979
2144
  controllerActionArguments[argMetadata.index] = responseHeaders;
@@ -1981,12 +2146,12 @@ export class Application<TRootClass extends Object = Object> {
1981
2146
  case paramArgsKey:
1982
2147
  controllerActionArguments[argMetadata.index] = !argMetadata.validationSchema
1983
2148
  ? parameters?.[argMetadata.key] || undefined
1984
- : await this.argumentsResolution(
1985
- parameters?.[argMetadata.key] || undefined,
1986
- argMetadata.validationSchema,
1987
- argMetadata.index,
1988
- controllerActionName
1989
- );
2149
+ : await this.#argumentsResolver({
2150
+ data: parameters?.[argMetadata.key] || undefined,
2151
+ validationSchema: argMetadata.validationSchema,
2152
+ argumentIndex: argMetadata.index,
2153
+ funcName: controllerActionName
2154
+ });
1990
2155
  break;
1991
2156
  case routeModelArgsKey:
1992
2157
  controllerActionArguments[argMetadata.index] = routeModel;
@@ -1996,15 +2161,13 @@ export class Application<TRootClass extends Object = Object> {
1996
2161
  break;
1997
2162
  default:
1998
2163
  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
- );
2164
+ ? context.get(argMetadata.type)
2165
+ : await this.#argumentsResolver({
2166
+ data: context.get(argMetadata.type),
2167
+ validationSchema: argMetadata.validationSchema,
2168
+ argumentIndex: argMetadata.index,
2169
+ funcName: controllerActionName
2170
+ });
2008
2171
  break;
2009
2172
  }
2010
2173
  }
@@ -2012,279 +2175,111 @@ export class Application<TRootClass extends Object = Object> {
2012
2175
  context.set(responseBodyArgsKey, await controllerAction(...controllerActionArguments), {
2013
2176
  isStatic: false
2014
2177
  });
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
2178
  }
2218
2179
 
2219
- return Object.freeze({
2220
- context: context
2221
- });
2180
+ return;
2222
2181
  }
2223
2182
 
2224
2183
  /**
2225
2184
  *
2226
- * @param bun
2227
- * @param bool
2185
+ * @param param0
2228
2186
  * @returns
2229
2187
  */
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]);
2188
+ private serializeResponse({
2189
+ status,
2190
+ statusText,
2191
+ headers,
2192
+ data
2193
+ }: {
2194
+ status?: number;
2195
+ statusText?: string;
2196
+ headers: Headers;
2197
+ data: unknown;
2198
+ }): Response {
2199
+ const contentType = headers.get("Content-Type") || "text/plain";
2200
+ const inferedStatus = !status ? (!data ? 204 : 200) : status;
2201
+ const inferedStatusText = inferStatusText(inferedStatus);
2253
2202
 
2254
- if (typeof isUpgrade !== "boolean") {
2203
+ if (contentType.includes("application/json")) {
2255
2204
  return this.finalizeResponse(
2256
2205
  new Response(
2257
- JSON.stringify({
2258
- httpCode: 500,
2259
- message: "Can not detect webSocket upgrade result.",
2260
- data: undefined
2261
- }),
2206
+ !data
2207
+ ? undefined
2208
+ : data instanceof ReadableStream
2209
+ ? data
2210
+ : JSON.stringify(data),
2262
2211
  {
2263
- status: 500,
2264
- statusText: "Internal server error.",
2265
- headers: responseHeaders
2212
+ status: inferedStatus,
2213
+ statusText: inferedStatusText,
2214
+ headers: headers
2266
2215
  }
2267
2216
  )
2268
2217
  );
2269
2218
  }
2270
2219
 
2271
- if (!isUpgrade) {
2220
+ if (contentType.includes("text/plain") || contentType.includes("text/html")) {
2272
2221
  return this.finalizeResponse(
2273
2222
  new Response(
2274
- JSON.stringify({
2275
- httpCode: 500,
2276
- message: "Can not upgrade.",
2277
- data: undefined
2278
- }),
2223
+ !data ? undefined : data instanceof ReadableStream ? data : String(data),
2279
2224
  {
2280
- status: 500,
2281
- statusText: "Internal server error.",
2282
- headers: responseHeaders
2225
+ status: inferedStatus,
2226
+ statusText: inferedStatusText,
2227
+ headers: headers
2283
2228
  }
2284
2229
  )
2285
2230
  );
2286
2231
  }
2287
2232
 
2288
- return isUpgrade;
2233
+ if (contentType.includes("application/octet-stream")) {
2234
+ if (
2235
+ data instanceof Uint8Array ||
2236
+ data instanceof ArrayBuffer ||
2237
+ data instanceof Blob ||
2238
+ data instanceof ReadableStream
2239
+ ) {
2240
+ return this.finalizeResponse(
2241
+ new Response(data as BodyInit, {
2242
+ status: inferedStatus,
2243
+ statusText: inferedStatusText,
2244
+ headers: headers
2245
+ })
2246
+ );
2247
+ }
2248
+
2249
+ throw new Error("Invalid data type for application/octet-stream");
2250
+ }
2251
+
2252
+ if (contentType.includes("multipart/form-data")) {
2253
+ if (data instanceof FormData) {
2254
+ return this.finalizeResponse(
2255
+ new Response(data, {
2256
+ status: inferedStatus,
2257
+ statusText: inferedStatusText,
2258
+ headers: headers
2259
+ })
2260
+ );
2261
+ }
2262
+
2263
+ throw new Error("multipart/form-data requires FormData object");
2264
+ }
2265
+
2266
+ return this.finalizeResponse(
2267
+ new Response(!data ? undefined : String(data), {
2268
+ status: inferedStatus,
2269
+ statusText: inferedStatusText,
2270
+ headers: headers
2271
+ })
2272
+ );
2273
+ }
2274
+
2275
+ /**
2276
+ *
2277
+ * @param response
2278
+ * @returns
2279
+ */
2280
+ private finalizeResponse(response: Response) {
2281
+ response.headers.set("X-Powered-By", "Bool Typescript");
2282
+
2283
+ return response;
2289
2284
  }
2290
2285
  }