@bool-ts/core 1.5.7 → 1.5.8

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 (48) hide show
  1. package/.prettierrc +11 -11
  2. package/LICENSE +21 -21
  3. package/__test/afterDispatcher.ts +9 -9
  4. package/__test/beforeDispatcher.ts +9 -9
  5. package/__test/controller.ts +68 -68
  6. package/__test/firstGuard.ts +10 -10
  7. package/__test/firstMiddleware.ts +9 -9
  8. package/__test/index.ts +8 -8
  9. package/__test/interfaces.ts +7 -7
  10. package/__test/module.ts +22 -22
  11. package/__test/repository.ts +16 -16
  12. package/__test/secondGuard.ts +10 -10
  13. package/__test/secondMiddleware.ts +9 -9
  14. package/__test/service.ts +20 -20
  15. package/bun.lockb +0 -0
  16. package/dist/entities/route.js +0 -1
  17. package/jsconfig.json +27 -27
  18. package/package.json +31 -31
  19. package/src/decorators/arguments.ts +214 -214
  20. package/src/decorators/controller.ts +22 -22
  21. package/src/decorators/dispatcher.ts +19 -19
  22. package/src/decorators/guard.ts +19 -19
  23. package/src/decorators/http.ts +81 -81
  24. package/src/decorators/index.ts +28 -28
  25. package/src/decorators/inject.ts +13 -13
  26. package/src/decorators/injectable.ts +8 -8
  27. package/src/decorators/middleware.ts +19 -19
  28. package/src/decorators/module.ts +92 -92
  29. package/src/decorators/zodSchema.ts +20 -20
  30. package/src/entities/index.ts +3 -3
  31. package/src/entities/route.ts +327 -328
  32. package/src/entities/router.ts +35 -35
  33. package/src/entities/routerGroup.ts +34 -34
  34. package/src/hooks/factory.ts +616 -616
  35. package/src/hooks/index.ts +2 -2
  36. package/src/hooks/injector.ts +43 -43
  37. package/src/http/clientError.ts +45 -45
  38. package/src/http/index.ts +61 -61
  39. package/src/http/serverError.ts +38 -38
  40. package/src/index.ts +6 -6
  41. package/src/interfaces/controller.ts +1 -1
  42. package/src/interfaces/dispatcher.ts +3 -3
  43. package/src/interfaces/guard.ts +3 -3
  44. package/src/interfaces/index.ts +5 -5
  45. package/src/interfaces/middleware.ts +3 -3
  46. package/src/interfaces/module.ts +1 -1
  47. package/test.http +31 -31
  48. package/tsconfig.json +108 -108
@@ -1,616 +1,616 @@
1
- import "colors";
2
- import "reflect-metadata";
3
-
4
- import Qs from "qs";
5
- import * as Zod from "zod";
6
-
7
- import type { TModuleMetadata, TArgumentsMetadata, THttpMetadata, TControllerMetadata } from "../decorators";
8
-
9
- import { Router, RouterGroup } from "../entities";
10
- import { controllerHttpKey, controllerKey, argumentsKey, moduleKey, EArgumentTypes } from "../decorators";
11
- import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
12
- import { Injector } from "./injector";
13
- import type { IGuard, IMiddleware } from "../interfaces";
14
- import type { IDispatcher } from "../interfaces/dispatcher";
15
-
16
- export type TBoolFactoryOptions = Required<{
17
- port: number;
18
- }> &
19
- Partial<{
20
- prefix: string;
21
- debug: boolean;
22
- log: Partial<{
23
- methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
24
- }>;
25
- queryParser: Parameters<typeof Qs.parse>[1];
26
- }>;
27
-
28
- export const controllerCreator = (
29
- controllerConstructor: new (...args: any[]) => unknown,
30
- group: RouterGroup,
31
- prefix?: string
32
- ) => {
33
- if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
34
- throw Error(`${controllerConstructor.name} is not a controller.`);
35
- }
36
-
37
- const controller = Injector.get(controllerConstructor);
38
-
39
- if (!controller) {
40
- throw Error("Can not initialize controller.");
41
- }
42
-
43
- const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(controllerKey, controllerConstructor) || {
44
- prefix: "/",
45
- httpMetadata: []
46
- };
47
- const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []) as THttpMetadata;
48
- const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
49
-
50
- routesMetadata.forEach((routeMetadata) => {
51
- if (typeof routeMetadata.descriptor.value !== "function") {
52
- return;
53
- }
54
-
55
- const route = router.route(routeMetadata.path);
56
- const handler = routeMetadata.descriptor.value.bind(controller);
57
- const routeArgument = {
58
- class: controllerConstructor,
59
- funcName: routeMetadata.methodName,
60
- func: handler
61
- };
62
-
63
- switch (routeMetadata.httpMethod) {
64
- case "GET":
65
- return route.get(routeArgument);
66
- case "POST":
67
- return route.post(routeArgument);
68
- case "PUT":
69
- return route.put(routeArgument);
70
- case "PATCH":
71
- return route.patch(routeArgument);
72
- case "DELETE":
73
- return route.delete(routeArgument);
74
- case "OPTIONS":
75
- return route.options(routeArgument);
76
- }
77
- });
78
-
79
- return group.add(router);
80
- };
81
-
82
- export const argumentsResolution = async (
83
- data: unknown,
84
- zodSchema: Zod.Schema,
85
- argumentIndex: number,
86
- funcName: string | symbol
87
- ) => {
88
- try {
89
- const validation = await zodSchema.safeParseAsync(data);
90
-
91
- if (!validation.success) {
92
- throw new HttpClientError({
93
- httpCode: 400,
94
- message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
95
- data: validation.error.issues
96
- });
97
- }
98
-
99
- return validation.data;
100
- } catch (error) {
101
- if (error instanceof HttpClientError) {
102
- throw error;
103
- }
104
-
105
- throw new HttpServerError({
106
- httpCode: 500,
107
- message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
108
- data: !(error instanceof Error)
109
- ? error
110
- : [
111
- {
112
- message: error.message,
113
- code: error.name,
114
- cause: error.cause
115
- }
116
- ]
117
- });
118
- }
119
- };
120
-
121
- export const BoolFactory = (target: new (...args: any[]) => unknown, options: TBoolFactoryOptions) => {
122
- if (!Reflect.getOwnMetadataKeys(target).includes(moduleKey)) {
123
- throw Error(`${target.name} is not a module.`);
124
- }
125
-
126
- const moduleMetadata = Reflect.getOwnMetadata(moduleKey, target) as TModuleMetadata;
127
-
128
- if (!moduleMetadata) {
129
- return Bun.serve({
130
- port: options.port,
131
- fetch: () => new Response()
132
- });
133
- }
134
-
135
- const { middlewares, guards, beforeDispatchers, controllers, afterDispatchers, prefix: modulePrefix } = moduleMetadata;
136
-
137
- // Middleware(s)
138
- const middlewareGroup = !middlewares
139
- ? []
140
- : middlewares.map((middleware) => {
141
- const middlewareInstance = Injector.get<IMiddleware>(middleware);
142
-
143
- return Object.freeze({
144
- class: middleware,
145
- funcName: "enforce",
146
- func: middlewareInstance.enforce.bind(middlewareInstance)
147
- });
148
- });
149
-
150
- // Guard(s)
151
- const guardGroup = !guards
152
- ? []
153
- : guards.map((guard) => {
154
- const guardInstance = Injector.get<IGuard>(guard);
155
-
156
- return Object.freeze({
157
- class: guard,
158
- funcName: "enforce",
159
- func: guardInstance.enforce.bind(guardInstance)
160
- });
161
- });
162
-
163
- // Before dispatcher(s)
164
- const beforeDispatcherGroup = !beforeDispatchers
165
- ? []
166
- : beforeDispatchers.map((beforeDispatcher) => {
167
- const beforeDispatcherInstance = Injector.get<IDispatcher>(beforeDispatcher);
168
-
169
- return Object.freeze({
170
- class: beforeDispatcher,
171
- funcName: "execute",
172
- func: beforeDispatcherInstance.execute.bind(beforeDispatcherInstance)
173
- });
174
- });
175
-
176
- // Controller(s)
177
- const routerGroup = new RouterGroup();
178
-
179
- controllers &&
180
- controllers.map((controllerConstructor) =>
181
- controllerCreator(controllerConstructor, routerGroup, `${options.prefix || ""}/${modulePrefix || ""}`)
182
- );
183
-
184
- const { allowLogsMethods } = Object.freeze({
185
- allowLogsMethods: options?.log?.methods
186
- });
187
-
188
- // After dispatcher(s)
189
- const afterDispatcherGroup = !afterDispatchers
190
- ? []
191
- : afterDispatchers.map((afterDispatcher) => {
192
- const afterDispatcherInstance = Injector.get<IDispatcher>(afterDispatcher);
193
-
194
- return Object.freeze({
195
- class: afterDispatcher,
196
- funcName: "execute",
197
- func: afterDispatcherInstance.execute.bind(afterDispatcherInstance)
198
- });
199
- });
200
-
201
- Bun.serve({
202
- port: options.port,
203
- fetch: async (request) => {
204
- const start = performance.now();
205
- const url = new URL(request.url);
206
- const reqHeaders = request.headers;
207
- const resHeaders = new Headers();
208
- const query = Qs.parse(url.searchParams.toString(), options.queryParser);
209
-
210
- try {
211
- // Execute middleware(s)
212
- for (let i = 0; i < middlewareGroup.length; i++) {
213
- const middlewareArguments = [];
214
- const middlewareCollection = middlewareGroup[i];
215
- const middlewareMetadata: Record<string, TArgumentsMetadata> =
216
- Reflect.getOwnMetadata(argumentsKey, middlewareCollection.class, middlewareCollection.funcName) || {};
217
-
218
- if (middlewareMetadata) {
219
- for (const [_key, argsMetadata] of Object.entries(middlewareMetadata)) {
220
- switch (argsMetadata.type) {
221
- case EArgumentTypes.requestHeaders:
222
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
223
- ? reqHeaders
224
- : await argumentsResolution(
225
- reqHeaders,
226
- argsMetadata.zodSchema,
227
- argsMetadata.index,
228
- middlewareCollection.funcName
229
- );
230
- break;
231
- case EArgumentTypes.body:
232
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
233
- ? await request[argsMetadata.parser || "json"]()
234
- : await argumentsResolution(
235
- await request[argsMetadata.parser || "json"](),
236
- argsMetadata.zodSchema,
237
- argsMetadata.index,
238
- middlewareCollection.funcName
239
- );
240
- break;
241
- case EArgumentTypes.query:
242
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
243
- ? query
244
- : await argumentsResolution(
245
- query,
246
- argsMetadata.zodSchema,
247
- argsMetadata.index,
248
- middlewareCollection.funcName
249
- );
250
- break;
251
- case EArgumentTypes.request:
252
- middlewareArguments[argsMetadata.index] = request;
253
- break;
254
- case EArgumentTypes.responseHeaders:
255
- middlewareArguments[argsMetadata.index] = resHeaders;
256
- break;
257
- }
258
- }
259
- }
260
-
261
- const middlewareResult = await middlewareCollection.func(...middlewareArguments);
262
-
263
- if (!(middlewareResult instanceof Response)) {
264
- continue;
265
- }
266
-
267
- return middlewareResult;
268
- }
269
-
270
- // Execute guard(s)
271
- for (let i = 0; i < guardGroup.length; i++) {
272
- const guardArguments = [];
273
- const guardCollection = guardGroup[i];
274
- const guardMetadata: Record<string, TArgumentsMetadata> =
275
- Reflect.getOwnMetadata(argumentsKey, guardCollection.class, guardCollection.funcName) || {};
276
-
277
- if (guardMetadata) {
278
- for (const [_key, argsMetadata] of Object.entries(guardMetadata)) {
279
- switch (argsMetadata.type) {
280
- case EArgumentTypes.requestHeaders:
281
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
282
- ? reqHeaders
283
- : await argumentsResolution(
284
- reqHeaders,
285
- argsMetadata.zodSchema,
286
- argsMetadata.index,
287
- guardCollection.funcName
288
- );
289
- break;
290
- case EArgumentTypes.body:
291
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
292
- ? await request[argsMetadata.parser || "json"]()
293
- : await argumentsResolution(
294
- await request[argsMetadata.parser || "json"](),
295
- argsMetadata.zodSchema,
296
- argsMetadata.index,
297
- guardCollection.funcName
298
- );
299
- break;
300
- case EArgumentTypes.query:
301
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
302
- ? query
303
- : await argumentsResolution(
304
- query,
305
- argsMetadata.zodSchema,
306
- argsMetadata.index,
307
- guardCollection.funcName
308
- );
309
- break;
310
- case EArgumentTypes.request:
311
- guardArguments[argsMetadata.index] = request;
312
- break;
313
- case EArgumentTypes.responseHeaders:
314
- guardArguments[argsMetadata.index] = resHeaders;
315
- break;
316
- }
317
- }
318
- }
319
-
320
- const guardResult = await guardCollection.func(...guardArguments);
321
-
322
- if (typeof guardResult !== "boolean" || !guardResult) {
323
- throw new HttpClientError({
324
- httpCode: 401,
325
- message: "Unauthorization.",
326
- data: undefined
327
- });
328
- }
329
- }
330
-
331
- const result = routerGroup.find(url.pathname, request.method as keyof THttpMethods);
332
-
333
- if (!result) {
334
- throw new HttpClientError({
335
- httpCode: 404,
336
- message: "Route not found.",
337
- data: undefined
338
- });
339
- }
340
-
341
- const params = result.params;
342
-
343
- let responseBody = undefined;
344
-
345
- // Execute before dispatcher(s)
346
- for (let i = 0; i < beforeDispatcherGroup.length; i++) {
347
- const beforeDispatcherArguments = [];
348
- const beforeDispatcherCollection = beforeDispatcherGroup[i];
349
- const beforeDispatcherMetadata: Record<string, TArgumentsMetadata> =
350
- Reflect.getOwnMetadata(
351
- argumentsKey,
352
- beforeDispatcherCollection.class,
353
- beforeDispatcherCollection.funcName
354
- ) || {};
355
-
356
- if (beforeDispatcherMetadata) {
357
- for (const [_key, argsMetadata] of Object.entries(beforeDispatcherMetadata)) {
358
- switch (argsMetadata.type) {
359
- case EArgumentTypes.requestHeaders:
360
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
361
- ? reqHeaders
362
- : await argumentsResolution(
363
- reqHeaders,
364
- argsMetadata.zodSchema,
365
- argsMetadata.index,
366
- beforeDispatcherCollection.funcName
367
- );
368
- break;
369
- case EArgumentTypes.body:
370
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
371
- ? await request[argsMetadata.parser || "json"]()
372
- : await argumentsResolution(
373
- await request[argsMetadata.parser || "json"](),
374
- argsMetadata.zodSchema,
375
- argsMetadata.index,
376
- beforeDispatcherCollection.funcName
377
- );
378
- break;
379
- case EArgumentTypes.params:
380
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
381
- ? params
382
- : await argumentsResolution(
383
- params,
384
- argsMetadata.zodSchema,
385
- argsMetadata.index,
386
- beforeDispatcherCollection.funcName
387
- );
388
- break;
389
- case EArgumentTypes.query:
390
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
391
- ? query
392
- : await argumentsResolution(
393
- query,
394
- argsMetadata.zodSchema,
395
- argsMetadata.index,
396
- beforeDispatcherCollection.funcName
397
- );
398
- break;
399
- case EArgumentTypes.param:
400
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
401
- ? !(argsMetadata.key in params)
402
- ? undefined
403
- : params[argsMetadata.key]
404
- : await argumentsResolution(
405
- query,
406
- argsMetadata.zodSchema,
407
- argsMetadata.index,
408
- beforeDispatcherCollection.funcName
409
- );
410
- break;
411
- case EArgumentTypes.request:
412
- beforeDispatcherArguments[argsMetadata.index] = request;
413
- break;
414
- case EArgumentTypes.responseHeaders:
415
- beforeDispatcherArguments[argsMetadata.index] = resHeaders;
416
- break;
417
- }
418
- }
419
- }
420
-
421
- await beforeDispatcherCollection.func(...beforeDispatcherArguments);
422
- }
423
-
424
- // Execute controller action
425
- for (let i = 0; i < result.handlers.length; i++) {
426
- const controllerActionArguments = [];
427
- const handler = result.handlers[i];
428
- const handlerMetadata: Record<string, TArgumentsMetadata> =
429
- Reflect.getOwnMetadata(argumentsKey, handler.class, handler.funcName) || {};
430
-
431
- if (handlerMetadata) {
432
- for (const [_key, argsMetadata] of Object.entries(handlerMetadata)) {
433
- switch (argsMetadata.type) {
434
- case EArgumentTypes.requestHeaders:
435
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
436
- ? reqHeaders
437
- : await argumentsResolution(
438
- reqHeaders,
439
- argsMetadata.zodSchema,
440
- argsMetadata.index,
441
- handler.funcName
442
- );
443
- break;
444
- case EArgumentTypes.body:
445
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
446
- ? await request[argsMetadata.parser || "json"]()
447
- : await argumentsResolution(
448
- await request[argsMetadata.parser || "json"](),
449
- argsMetadata.zodSchema,
450
- argsMetadata.index,
451
- handler.funcName
452
- );
453
- break;
454
- case EArgumentTypes.params:
455
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
456
- ? params
457
- : await argumentsResolution(
458
- params,
459
- argsMetadata.zodSchema,
460
- argsMetadata.index,
461
- handler.funcName
462
- );
463
- break;
464
- case EArgumentTypes.query:
465
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
466
- ? query
467
- : await argumentsResolution(
468
- query,
469
- argsMetadata.zodSchema,
470
- argsMetadata.index,
471
- handler.funcName
472
- );
473
- break;
474
- case EArgumentTypes.param:
475
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
476
- ? !(argsMetadata.key in params)
477
- ? undefined
478
- : params[argsMetadata.key]
479
- : await argumentsResolution(
480
- query,
481
- argsMetadata.zodSchema,
482
- argsMetadata.index,
483
- handler.funcName
484
- );
485
- break;
486
- case EArgumentTypes.request:
487
- controllerActionArguments[argsMetadata.index] = request;
488
- break;
489
- case EArgumentTypes.responseHeaders:
490
- controllerActionArguments[argsMetadata.index] = resHeaders;
491
- break;
492
- }
493
- }
494
- }
495
-
496
- responseBody = await handler.func(...controllerActionArguments);
497
- }
498
-
499
- // Execute after dispatcher(s)
500
- for (let i = 0; i < afterDispatcherGroup.length; i++) {
501
- const afterDispatcherArguments = [];
502
- const afterDispatcherCollection = afterDispatcherGroup[i];
503
- const afterDispatcherMetadata: Record<string, TArgumentsMetadata> =
504
- Reflect.getOwnMetadata(
505
- argumentsKey,
506
- afterDispatcherCollection.class,
507
- afterDispatcherCollection.funcName
508
- ) || {};
509
-
510
- if (afterDispatcherMetadata) {
511
- for (const [_key, argsMetadata] of Object.entries(afterDispatcherMetadata)) {
512
- switch (argsMetadata.type) {
513
- case EArgumentTypes.requestHeaders:
514
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
515
- ? reqHeaders
516
- : await argumentsResolution(
517
- reqHeaders,
518
- argsMetadata.zodSchema,
519
- argsMetadata.index,
520
- afterDispatcherCollection.funcName
521
- );
522
- break;
523
- case EArgumentTypes.body:
524
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
525
- ? await request[argsMetadata.parser || "json"]()
526
- : await argumentsResolution(
527
- await request[argsMetadata.parser || "json"](),
528
- argsMetadata.zodSchema,
529
- argsMetadata.index,
530
- afterDispatcherCollection.funcName
531
- );
532
- break;
533
- case EArgumentTypes.params:
534
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
535
- ? params
536
- : await argumentsResolution(
537
- params,
538
- argsMetadata.zodSchema,
539
- argsMetadata.index,
540
- afterDispatcherCollection.funcName
541
- );
542
- break;
543
- case EArgumentTypes.query:
544
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
545
- ? query
546
- : await argumentsResolution(
547
- query,
548
- argsMetadata.zodSchema,
549
- argsMetadata.index,
550
- afterDispatcherCollection.funcName
551
- );
552
- break;
553
- case EArgumentTypes.param:
554
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
555
- ? !(argsMetadata.key in params)
556
- ? undefined
557
- : params[argsMetadata.key]
558
- : await argumentsResolution(
559
- query,
560
- argsMetadata.zodSchema,
561
- argsMetadata.index,
562
- afterDispatcherCollection.funcName
563
- );
564
- break;
565
- case EArgumentTypes.request:
566
- afterDispatcherArguments[argsMetadata.index] = request;
567
- break;
568
- case EArgumentTypes.responseHeaders:
569
- afterDispatcherArguments[argsMetadata.index] = resHeaders;
570
- break;
571
- }
572
- }
573
- }
574
-
575
- await afterDispatcherCollection.func(...afterDispatcherArguments);
576
- }
577
-
578
- return responseBody instanceof Response
579
- ? responseBody
580
- : new Response(
581
- !responseBody
582
- ? undefined
583
- : JSON.stringify({
584
- httpCode: 200,
585
- message: "SUCCESS",
586
- data: responseBody
587
- }),
588
- {
589
- status: !responseBody ? 204 : 200,
590
- statusText: "SUCCESS",
591
- headers: resHeaders
592
- }
593
- );
594
- } catch (error) {
595
- return jsonErrorInfer(error, resHeaders);
596
- } finally {
597
- if (allowLogsMethods) {
598
- const end = performance.now();
599
- const convertedPID = `${process.pid}`.yellow;
600
- const convertedMethod = `${request.method.yellow}`.bgBlue;
601
- const convertedReqIp = `${
602
- request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "<Unknown>"
603
- }`.yellow;
604
- const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
605
-
606
- allowLogsMethods.includes(request.method.toUpperCase() as (typeof allowLogsMethods)[number]) &&
607
- console.info(
608
- `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${url.pathname.blue} - Time: ${convertedTime}`
609
- );
610
- }
611
- }
612
- }
613
- });
614
- };
615
-
616
- export default BoolFactory;
1
+ import "colors";
2
+ import "reflect-metadata";
3
+
4
+ import Qs from "qs";
5
+ import * as Zod from "zod";
6
+
7
+ import type { TModuleMetadata, TArgumentsMetadata, THttpMetadata, TControllerMetadata } from "../decorators";
8
+
9
+ import { Router, RouterGroup } from "../entities";
10
+ import { controllerHttpKey, controllerKey, argumentsKey, moduleKey, EArgumentTypes } from "../decorators";
11
+ import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
12
+ import { Injector } from "./injector";
13
+ import type { IGuard, IMiddleware } from "../interfaces";
14
+ import type { IDispatcher } from "../interfaces/dispatcher";
15
+
16
+ export type TBoolFactoryOptions = Required<{
17
+ port: number;
18
+ }> &
19
+ Partial<{
20
+ prefix: string;
21
+ debug: boolean;
22
+ log: Partial<{
23
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
24
+ }>;
25
+ queryParser: Parameters<typeof Qs.parse>[1];
26
+ }>;
27
+
28
+ export const controllerCreator = (
29
+ controllerConstructor: new (...args: any[]) => unknown,
30
+ group: RouterGroup,
31
+ prefix?: string
32
+ ) => {
33
+ if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
34
+ throw Error(`${controllerConstructor.name} is not a controller.`);
35
+ }
36
+
37
+ const controller = Injector.get(controllerConstructor);
38
+
39
+ if (!controller) {
40
+ throw Error("Can not initialize controller.");
41
+ }
42
+
43
+ const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(controllerKey, controllerConstructor) || {
44
+ prefix: "/",
45
+ httpMetadata: []
46
+ };
47
+ const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []) as THttpMetadata;
48
+ const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
49
+
50
+ routesMetadata.forEach((routeMetadata) => {
51
+ if (typeof routeMetadata.descriptor.value !== "function") {
52
+ return;
53
+ }
54
+
55
+ const route = router.route(routeMetadata.path);
56
+ const handler = routeMetadata.descriptor.value.bind(controller);
57
+ const routeArgument = {
58
+ class: controllerConstructor,
59
+ funcName: routeMetadata.methodName,
60
+ func: handler
61
+ };
62
+
63
+ switch (routeMetadata.httpMethod) {
64
+ case "GET":
65
+ return route.get(routeArgument);
66
+ case "POST":
67
+ return route.post(routeArgument);
68
+ case "PUT":
69
+ return route.put(routeArgument);
70
+ case "PATCH":
71
+ return route.patch(routeArgument);
72
+ case "DELETE":
73
+ return route.delete(routeArgument);
74
+ case "OPTIONS":
75
+ return route.options(routeArgument);
76
+ }
77
+ });
78
+
79
+ return group.add(router);
80
+ };
81
+
82
+ export const argumentsResolution = async (
83
+ data: unknown,
84
+ zodSchema: Zod.Schema,
85
+ argumentIndex: number,
86
+ funcName: string | symbol
87
+ ) => {
88
+ try {
89
+ const validation = await zodSchema.safeParseAsync(data);
90
+
91
+ if (!validation.success) {
92
+ throw new HttpClientError({
93
+ httpCode: 400,
94
+ message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
95
+ data: validation.error.issues
96
+ });
97
+ }
98
+
99
+ return validation.data;
100
+ } catch (error) {
101
+ if (error instanceof HttpClientError) {
102
+ throw error;
103
+ }
104
+
105
+ throw new HttpServerError({
106
+ httpCode: 500,
107
+ message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
108
+ data: !(error instanceof Error)
109
+ ? error
110
+ : [
111
+ {
112
+ message: error.message,
113
+ code: error.name,
114
+ cause: error.cause
115
+ }
116
+ ]
117
+ });
118
+ }
119
+ };
120
+
121
+ export const BoolFactory = (target: new (...args: any[]) => unknown, options: TBoolFactoryOptions) => {
122
+ if (!Reflect.getOwnMetadataKeys(target).includes(moduleKey)) {
123
+ throw Error(`${target.name} is not a module.`);
124
+ }
125
+
126
+ const moduleMetadata = Reflect.getOwnMetadata(moduleKey, target) as TModuleMetadata;
127
+
128
+ if (!moduleMetadata) {
129
+ return Bun.serve({
130
+ port: options.port,
131
+ fetch: () => new Response()
132
+ });
133
+ }
134
+
135
+ const { middlewares, guards, beforeDispatchers, controllers, afterDispatchers, prefix: modulePrefix } = moduleMetadata;
136
+
137
+ // Middleware(s)
138
+ const middlewareGroup = !middlewares
139
+ ? []
140
+ : middlewares.map((middleware) => {
141
+ const middlewareInstance = Injector.get<IMiddleware>(middleware);
142
+
143
+ return Object.freeze({
144
+ class: middleware,
145
+ funcName: "enforce",
146
+ func: middlewareInstance.enforce.bind(middlewareInstance)
147
+ });
148
+ });
149
+
150
+ // Guard(s)
151
+ const guardGroup = !guards
152
+ ? []
153
+ : guards.map((guard) => {
154
+ const guardInstance = Injector.get<IGuard>(guard);
155
+
156
+ return Object.freeze({
157
+ class: guard,
158
+ funcName: "enforce",
159
+ func: guardInstance.enforce.bind(guardInstance)
160
+ });
161
+ });
162
+
163
+ // Before dispatcher(s)
164
+ const beforeDispatcherGroup = !beforeDispatchers
165
+ ? []
166
+ : beforeDispatchers.map((beforeDispatcher) => {
167
+ const beforeDispatcherInstance = Injector.get<IDispatcher>(beforeDispatcher);
168
+
169
+ return Object.freeze({
170
+ class: beforeDispatcher,
171
+ funcName: "execute",
172
+ func: beforeDispatcherInstance.execute.bind(beforeDispatcherInstance)
173
+ });
174
+ });
175
+
176
+ // Controller(s)
177
+ const routerGroup = new RouterGroup();
178
+
179
+ controllers &&
180
+ controllers.map((controllerConstructor) =>
181
+ controllerCreator(controllerConstructor, routerGroup, `${options.prefix || ""}/${modulePrefix || ""}`)
182
+ );
183
+
184
+ const { allowLogsMethods } = Object.freeze({
185
+ allowLogsMethods: options?.log?.methods
186
+ });
187
+
188
+ // After dispatcher(s)
189
+ const afterDispatcherGroup = !afterDispatchers
190
+ ? []
191
+ : afterDispatchers.map((afterDispatcher) => {
192
+ const afterDispatcherInstance = Injector.get<IDispatcher>(afterDispatcher);
193
+
194
+ return Object.freeze({
195
+ class: afterDispatcher,
196
+ funcName: "execute",
197
+ func: afterDispatcherInstance.execute.bind(afterDispatcherInstance)
198
+ });
199
+ });
200
+
201
+ Bun.serve({
202
+ port: options.port,
203
+ fetch: async (request) => {
204
+ const start = performance.now();
205
+ const url = new URL(request.url);
206
+ const reqHeaders = request.headers;
207
+ const resHeaders = new Headers();
208
+ const query = Qs.parse(url.searchParams.toString(), options.queryParser);
209
+
210
+ try {
211
+ // Execute middleware(s)
212
+ for (let i = 0; i < middlewareGroup.length; i++) {
213
+ const middlewareArguments = [];
214
+ const middlewareCollection = middlewareGroup[i];
215
+ const middlewareMetadata: Record<string, TArgumentsMetadata> =
216
+ Reflect.getOwnMetadata(argumentsKey, middlewareCollection.class, middlewareCollection.funcName) || {};
217
+
218
+ if (middlewareMetadata) {
219
+ for (const [_key, argsMetadata] of Object.entries(middlewareMetadata)) {
220
+ switch (argsMetadata.type) {
221
+ case EArgumentTypes.requestHeaders:
222
+ middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
223
+ ? reqHeaders
224
+ : await argumentsResolution(
225
+ reqHeaders,
226
+ argsMetadata.zodSchema,
227
+ argsMetadata.index,
228
+ middlewareCollection.funcName
229
+ );
230
+ break;
231
+ case EArgumentTypes.body:
232
+ middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
233
+ ? await request[argsMetadata.parser || "json"]()
234
+ : await argumentsResolution(
235
+ await request[argsMetadata.parser || "json"](),
236
+ argsMetadata.zodSchema,
237
+ argsMetadata.index,
238
+ middlewareCollection.funcName
239
+ );
240
+ break;
241
+ case EArgumentTypes.query:
242
+ middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
243
+ ? query
244
+ : await argumentsResolution(
245
+ query,
246
+ argsMetadata.zodSchema,
247
+ argsMetadata.index,
248
+ middlewareCollection.funcName
249
+ );
250
+ break;
251
+ case EArgumentTypes.request:
252
+ middlewareArguments[argsMetadata.index] = request;
253
+ break;
254
+ case EArgumentTypes.responseHeaders:
255
+ middlewareArguments[argsMetadata.index] = resHeaders;
256
+ break;
257
+ }
258
+ }
259
+ }
260
+
261
+ const middlewareResult = await middlewareCollection.func(...middlewareArguments);
262
+
263
+ if (!(middlewareResult instanceof Response)) {
264
+ continue;
265
+ }
266
+
267
+ return middlewareResult;
268
+ }
269
+
270
+ // Execute guard(s)
271
+ for (let i = 0; i < guardGroup.length; i++) {
272
+ const guardArguments = [];
273
+ const guardCollection = guardGroup[i];
274
+ const guardMetadata: Record<string, TArgumentsMetadata> =
275
+ Reflect.getOwnMetadata(argumentsKey, guardCollection.class, guardCollection.funcName) || {};
276
+
277
+ if (guardMetadata) {
278
+ for (const [_key, argsMetadata] of Object.entries(guardMetadata)) {
279
+ switch (argsMetadata.type) {
280
+ case EArgumentTypes.requestHeaders:
281
+ guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
282
+ ? reqHeaders
283
+ : await argumentsResolution(
284
+ reqHeaders,
285
+ argsMetadata.zodSchema,
286
+ argsMetadata.index,
287
+ guardCollection.funcName
288
+ );
289
+ break;
290
+ case EArgumentTypes.body:
291
+ guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
292
+ ? await request[argsMetadata.parser || "json"]()
293
+ : await argumentsResolution(
294
+ await request[argsMetadata.parser || "json"](),
295
+ argsMetadata.zodSchema,
296
+ argsMetadata.index,
297
+ guardCollection.funcName
298
+ );
299
+ break;
300
+ case EArgumentTypes.query:
301
+ guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
302
+ ? query
303
+ : await argumentsResolution(
304
+ query,
305
+ argsMetadata.zodSchema,
306
+ argsMetadata.index,
307
+ guardCollection.funcName
308
+ );
309
+ break;
310
+ case EArgumentTypes.request:
311
+ guardArguments[argsMetadata.index] = request;
312
+ break;
313
+ case EArgumentTypes.responseHeaders:
314
+ guardArguments[argsMetadata.index] = resHeaders;
315
+ break;
316
+ }
317
+ }
318
+ }
319
+
320
+ const guardResult = await guardCollection.func(...guardArguments);
321
+
322
+ if (typeof guardResult !== "boolean" || !guardResult) {
323
+ throw new HttpClientError({
324
+ httpCode: 401,
325
+ message: "Unauthorization.",
326
+ data: undefined
327
+ });
328
+ }
329
+ }
330
+
331
+ const result = routerGroup.find(url.pathname, request.method as keyof THttpMethods);
332
+
333
+ if (!result) {
334
+ throw new HttpClientError({
335
+ httpCode: 404,
336
+ message: "Route not found.",
337
+ data: undefined
338
+ });
339
+ }
340
+
341
+ const params = result.params;
342
+
343
+ let responseBody = undefined;
344
+
345
+ // Execute before dispatcher(s)
346
+ for (let i = 0; i < beforeDispatcherGroup.length; i++) {
347
+ const beforeDispatcherArguments = [];
348
+ const beforeDispatcherCollection = beforeDispatcherGroup[i];
349
+ const beforeDispatcherMetadata: Record<string, TArgumentsMetadata> =
350
+ Reflect.getOwnMetadata(
351
+ argumentsKey,
352
+ beforeDispatcherCollection.class,
353
+ beforeDispatcherCollection.funcName
354
+ ) || {};
355
+
356
+ if (beforeDispatcherMetadata) {
357
+ for (const [_key, argsMetadata] of Object.entries(beforeDispatcherMetadata)) {
358
+ switch (argsMetadata.type) {
359
+ case EArgumentTypes.requestHeaders:
360
+ beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
361
+ ? reqHeaders
362
+ : await argumentsResolution(
363
+ reqHeaders,
364
+ argsMetadata.zodSchema,
365
+ argsMetadata.index,
366
+ beforeDispatcherCollection.funcName
367
+ );
368
+ break;
369
+ case EArgumentTypes.body:
370
+ beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
371
+ ? await request[argsMetadata.parser || "json"]()
372
+ : await argumentsResolution(
373
+ await request[argsMetadata.parser || "json"](),
374
+ argsMetadata.zodSchema,
375
+ argsMetadata.index,
376
+ beforeDispatcherCollection.funcName
377
+ );
378
+ break;
379
+ case EArgumentTypes.params:
380
+ beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
381
+ ? params
382
+ : await argumentsResolution(
383
+ params,
384
+ argsMetadata.zodSchema,
385
+ argsMetadata.index,
386
+ beforeDispatcherCollection.funcName
387
+ );
388
+ break;
389
+ case EArgumentTypes.query:
390
+ beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
391
+ ? query
392
+ : await argumentsResolution(
393
+ query,
394
+ argsMetadata.zodSchema,
395
+ argsMetadata.index,
396
+ beforeDispatcherCollection.funcName
397
+ );
398
+ break;
399
+ case EArgumentTypes.param:
400
+ beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
401
+ ? !(argsMetadata.key in params)
402
+ ? undefined
403
+ : params[argsMetadata.key]
404
+ : await argumentsResolution(
405
+ query,
406
+ argsMetadata.zodSchema,
407
+ argsMetadata.index,
408
+ beforeDispatcherCollection.funcName
409
+ );
410
+ break;
411
+ case EArgumentTypes.request:
412
+ beforeDispatcherArguments[argsMetadata.index] = request;
413
+ break;
414
+ case EArgumentTypes.responseHeaders:
415
+ beforeDispatcherArguments[argsMetadata.index] = resHeaders;
416
+ break;
417
+ }
418
+ }
419
+ }
420
+
421
+ await beforeDispatcherCollection.func(...beforeDispatcherArguments);
422
+ }
423
+
424
+ // Execute controller action
425
+ for (let i = 0; i < result.handlers.length; i++) {
426
+ const controllerActionArguments = [];
427
+ const handler = result.handlers[i];
428
+ const handlerMetadata: Record<string, TArgumentsMetadata> =
429
+ Reflect.getOwnMetadata(argumentsKey, handler.class, handler.funcName) || {};
430
+
431
+ if (handlerMetadata) {
432
+ for (const [_key, argsMetadata] of Object.entries(handlerMetadata)) {
433
+ switch (argsMetadata.type) {
434
+ case EArgumentTypes.requestHeaders:
435
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
436
+ ? reqHeaders
437
+ : await argumentsResolution(
438
+ reqHeaders,
439
+ argsMetadata.zodSchema,
440
+ argsMetadata.index,
441
+ handler.funcName
442
+ );
443
+ break;
444
+ case EArgumentTypes.body:
445
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
446
+ ? await request[argsMetadata.parser || "json"]()
447
+ : await argumentsResolution(
448
+ await request[argsMetadata.parser || "json"](),
449
+ argsMetadata.zodSchema,
450
+ argsMetadata.index,
451
+ handler.funcName
452
+ );
453
+ break;
454
+ case EArgumentTypes.params:
455
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
456
+ ? params
457
+ : await argumentsResolution(
458
+ params,
459
+ argsMetadata.zodSchema,
460
+ argsMetadata.index,
461
+ handler.funcName
462
+ );
463
+ break;
464
+ case EArgumentTypes.query:
465
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
466
+ ? query
467
+ : await argumentsResolution(
468
+ query,
469
+ argsMetadata.zodSchema,
470
+ argsMetadata.index,
471
+ handler.funcName
472
+ );
473
+ break;
474
+ case EArgumentTypes.param:
475
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
476
+ ? !(argsMetadata.key in params)
477
+ ? undefined
478
+ : params[argsMetadata.key]
479
+ : await argumentsResolution(
480
+ query,
481
+ argsMetadata.zodSchema,
482
+ argsMetadata.index,
483
+ handler.funcName
484
+ );
485
+ break;
486
+ case EArgumentTypes.request:
487
+ controllerActionArguments[argsMetadata.index] = request;
488
+ break;
489
+ case EArgumentTypes.responseHeaders:
490
+ controllerActionArguments[argsMetadata.index] = resHeaders;
491
+ break;
492
+ }
493
+ }
494
+ }
495
+
496
+ responseBody = await handler.func(...controllerActionArguments);
497
+ }
498
+
499
+ // Execute after dispatcher(s)
500
+ for (let i = 0; i < afterDispatcherGroup.length; i++) {
501
+ const afterDispatcherArguments = [];
502
+ const afterDispatcherCollection = afterDispatcherGroup[i];
503
+ const afterDispatcherMetadata: Record<string, TArgumentsMetadata> =
504
+ Reflect.getOwnMetadata(
505
+ argumentsKey,
506
+ afterDispatcherCollection.class,
507
+ afterDispatcherCollection.funcName
508
+ ) || {};
509
+
510
+ if (afterDispatcherMetadata) {
511
+ for (const [_key, argsMetadata] of Object.entries(afterDispatcherMetadata)) {
512
+ switch (argsMetadata.type) {
513
+ case EArgumentTypes.requestHeaders:
514
+ afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
515
+ ? reqHeaders
516
+ : await argumentsResolution(
517
+ reqHeaders,
518
+ argsMetadata.zodSchema,
519
+ argsMetadata.index,
520
+ afterDispatcherCollection.funcName
521
+ );
522
+ break;
523
+ case EArgumentTypes.body:
524
+ afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
525
+ ? await request[argsMetadata.parser || "json"]()
526
+ : await argumentsResolution(
527
+ await request[argsMetadata.parser || "json"](),
528
+ argsMetadata.zodSchema,
529
+ argsMetadata.index,
530
+ afterDispatcherCollection.funcName
531
+ );
532
+ break;
533
+ case EArgumentTypes.params:
534
+ afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
535
+ ? params
536
+ : await argumentsResolution(
537
+ params,
538
+ argsMetadata.zodSchema,
539
+ argsMetadata.index,
540
+ afterDispatcherCollection.funcName
541
+ );
542
+ break;
543
+ case EArgumentTypes.query:
544
+ afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
545
+ ? query
546
+ : await argumentsResolution(
547
+ query,
548
+ argsMetadata.zodSchema,
549
+ argsMetadata.index,
550
+ afterDispatcherCollection.funcName
551
+ );
552
+ break;
553
+ case EArgumentTypes.param:
554
+ afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
555
+ ? !(argsMetadata.key in params)
556
+ ? undefined
557
+ : params[argsMetadata.key]
558
+ : await argumentsResolution(
559
+ query,
560
+ argsMetadata.zodSchema,
561
+ argsMetadata.index,
562
+ afterDispatcherCollection.funcName
563
+ );
564
+ break;
565
+ case EArgumentTypes.request:
566
+ afterDispatcherArguments[argsMetadata.index] = request;
567
+ break;
568
+ case EArgumentTypes.responseHeaders:
569
+ afterDispatcherArguments[argsMetadata.index] = resHeaders;
570
+ break;
571
+ }
572
+ }
573
+ }
574
+
575
+ await afterDispatcherCollection.func(...afterDispatcherArguments);
576
+ }
577
+
578
+ return responseBody instanceof Response
579
+ ? responseBody
580
+ : new Response(
581
+ !responseBody
582
+ ? undefined
583
+ : JSON.stringify({
584
+ httpCode: 200,
585
+ message: "SUCCESS",
586
+ data: responseBody
587
+ }),
588
+ {
589
+ status: !responseBody ? 204 : 200,
590
+ statusText: "SUCCESS",
591
+ headers: resHeaders
592
+ }
593
+ );
594
+ } catch (error) {
595
+ return jsonErrorInfer(error, resHeaders);
596
+ } finally {
597
+ if (allowLogsMethods) {
598
+ const end = performance.now();
599
+ const convertedPID = `${process.pid}`.yellow;
600
+ const convertedMethod = `${request.method.yellow}`.bgBlue;
601
+ const convertedReqIp = `${
602
+ request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "<Unknown>"
603
+ }`.yellow;
604
+ const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
605
+
606
+ allowLogsMethods.includes(request.method.toUpperCase() as (typeof allowLogsMethods)[number]) &&
607
+ console.info(
608
+ `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${url.pathname.blue} - Time: ${convertedTime}`
609
+ );
610
+ }
611
+ }
612
+ }
613
+ });
614
+ };
615
+
616
+ export default BoolFactory;