@bool-ts/core 1.6.14 → 1.7.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 (68) hide show
  1. package/.prettierrc +11 -11
  2. package/LICENSE +21 -21
  3. package/__test/controller.ts +93 -79
  4. package/__test/dispatcher.ts +16 -0
  5. package/__test/firstGuard.ts +10 -10
  6. package/__test/firstMiddleware.ts +15 -9
  7. package/__test/index.ts +8 -8
  8. package/__test/interfaces.ts +7 -7
  9. package/__test/module.ts +28 -30
  10. package/__test/repository.ts +16 -16
  11. package/__test/secondGuard.ts +10 -10
  12. package/__test/secondMiddleware.ts +15 -9
  13. package/__test/service.ts +20 -20
  14. package/bun.lockb +0 -0
  15. package/dist/decorators/arguments.d.ts +3 -3
  16. package/dist/decorators/arguments.js +3 -3
  17. package/dist/decorators/dispatcher.js +0 -3
  18. package/dist/decorators/index.d.ts +1 -1
  19. package/dist/decorators/index.js +1 -1
  20. package/dist/decorators/middleware.js +0 -3
  21. package/dist/decorators/module.d.ts +2 -4
  22. package/dist/decorators/module.js +5 -12
  23. package/dist/hooks/factory.d.ts +41 -2
  24. package/dist/hooks/factory.js +496 -404
  25. package/dist/hooks/injector.d.ts +14 -1
  26. package/dist/hooks/injector.js +3 -3
  27. package/dist/http/index.d.ts +1 -1
  28. package/dist/http/index.js +2 -1
  29. package/dist/interfaces/dispatcher.d.ts +3 -2
  30. package/dist/interfaces/middleware.d.ts +3 -2
  31. package/dist/keys/index.d.ts +2 -1
  32. package/dist/keys/index.js +2 -1
  33. package/jsconfig.json +27 -27
  34. package/package.json +3 -3
  35. package/src/decorators/arguments.ts +286 -286
  36. package/src/decorators/controller.ts +21 -21
  37. package/src/decorators/dispatcher.ts +14 -18
  38. package/src/decorators/guard.ts +18 -18
  39. package/src/decorators/http.ts +81 -81
  40. package/src/decorators/index.ts +29 -29
  41. package/src/decorators/inject.ts +13 -13
  42. package/src/decorators/injectable.ts +8 -8
  43. package/src/decorators/middleware.ts +14 -18
  44. package/src/decorators/module.ts +84 -94
  45. package/src/decorators/zodSchema.ts +19 -19
  46. package/src/entities/index.ts +5 -5
  47. package/src/entities/route.ts +327 -327
  48. package/src/entities/router.ts +35 -35
  49. package/src/entities/routerGroup.ts +34 -34
  50. package/src/hooks/factory.ts +990 -809
  51. package/src/hooks/index.ts +2 -2
  52. package/src/hooks/injector.ts +57 -57
  53. package/src/http/clientError.ts +45 -45
  54. package/src/http/index.ts +62 -61
  55. package/src/http/serverError.ts +27 -27
  56. package/src/index.ts +9 -9
  57. package/src/interfaces/context.ts +4 -4
  58. package/src/interfaces/controller.ts +1 -1
  59. package/src/interfaces/dispatcher.ts +4 -3
  60. package/src/interfaces/guard.ts +3 -3
  61. package/src/interfaces/index.ts +6 -6
  62. package/src/interfaces/middleware.ts +4 -3
  63. package/src/interfaces/module.ts +1 -1
  64. package/src/keys/index.ts +23 -22
  65. package/test.http +31 -31
  66. package/tsconfig.json +108 -108
  67. package/__test/afterDispatcher.ts +0 -9
  68. package/__test/beforeDispatcher.ts +0 -9
@@ -1,809 +1,990 @@
1
- import type { TArgumentsMetadata, TControllerMetadata, THttpMetadata, TModuleMetadata } from "../decorators";
2
- import type { IContext, IGuard, IMiddleware } from "../interfaces";
3
- import type { IDispatcher } from "../interfaces/dispatcher";
4
-
5
- import "colors";
6
- import "reflect-metadata";
7
-
8
- import Qs from "qs";
9
- import * as Zod from "zod";
10
-
11
- import { Router, RouterGroup } from "../entities";
12
- import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
13
- import {
14
- argumentsKey,
15
- bodyArgsKey,
16
- configKey,
17
- contextArgsKey,
18
- controllerHttpKey,
19
- controllerKey,
20
- moduleKey,
21
- paramArgsKey,
22
- paramsArgsKey,
23
- queryArgsKey,
24
- requestArgsKey,
25
- requestHeaderArgsKey,
26
- requestHeadersArgsKey,
27
- responseHeadersArgsKey,
28
- routeModelArgsKey
29
- } from "../keys";
30
- import { Injector } from "./injector";
31
-
32
- export type TBoolFactoryOptions = Required<{
33
- port: number;
34
- }> &
35
- Partial<{
36
- config: Record<string | symbol, any> | (() => Record<string | symbol, any>);
37
- prefix: string;
38
- debug: boolean;
39
- log: Partial<{
40
- methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
41
- }>;
42
- queryParser: Parameters<typeof Qs.parse>[1];
43
- }>;
44
-
45
- export const controllerCreator = (
46
- controllerConstructor: new (...args: any[]) => unknown,
47
- group: RouterGroup,
48
- prefix?: string
49
- ) => {
50
- if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
51
- throw Error(`${controllerConstructor.name} is not a controller.`);
52
- }
53
-
54
- const controller = Injector.get(controllerConstructor);
55
-
56
- if (!controller) {
57
- throw Error("Can not initialize controller.");
58
- }
59
-
60
- const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(controllerKey, controllerConstructor) || {
61
- prefix: "/",
62
- httpMetadata: []
63
- };
64
- const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []) as THttpMetadata;
65
- const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
66
-
67
- routesMetadata.forEach((routeMetadata) => {
68
- if (typeof routeMetadata.descriptor.value !== "function") {
69
- return;
70
- }
71
-
72
- const route = router.route(routeMetadata.path);
73
- const handler = routeMetadata.descriptor.value.bind(controller);
74
- const routeArgument = Object.freeze({
75
- class: controllerConstructor,
76
- funcName: routeMetadata.methodName,
77
- func: handler
78
- });
79
-
80
- switch (routeMetadata.httpMethod) {
81
- case "GET":
82
- return route.get(routeArgument);
83
- case "POST":
84
- return route.post(routeArgument);
85
- case "PUT":
86
- return route.put(routeArgument);
87
- case "PATCH":
88
- return route.patch(routeArgument);
89
- case "DELETE":
90
- return route.delete(routeArgument);
91
- case "OPTIONS":
92
- return route.options(routeArgument);
93
- }
94
- });
95
-
96
- return group.add(router);
97
- };
98
-
99
- export const argumentsResolution = async (
100
- data: unknown,
101
- zodSchema: Zod.Schema,
102
- argumentIndex: number,
103
- funcName: string | symbol
104
- ) => {
105
- try {
106
- const validation = await zodSchema.safeParseAsync(data);
107
-
108
- if (!validation.success) {
109
- throw new HttpClientError({
110
- httpCode: 400,
111
- message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
112
- data: validation.error.issues
113
- });
114
- }
115
-
116
- return validation.data;
117
- } catch (error) {
118
- if (error instanceof HttpClientError) {
119
- throw error;
120
- }
121
-
122
- throw new HttpServerError({
123
- httpCode: 500,
124
- message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
125
- data: !(error instanceof Error)
126
- ? error
127
- : [
128
- {
129
- message: error.message,
130
- code: error.name,
131
- cause: error.cause
132
- }
133
- ]
134
- });
135
- }
136
- };
137
-
138
- export const BoolFactory = async (target: new (...args: any[]) => unknown, options: TBoolFactoryOptions) => {
139
- try {
140
- if (!Reflect.getOwnMetadataKeys(target).includes(moduleKey)) {
141
- throw Error(`${target.name} is not a module.`);
142
- }
143
-
144
- const moduleMetadata = Reflect.getOwnMetadata(moduleKey, target) as TModuleMetadata;
145
-
146
- if (!moduleMetadata) {
147
- return Bun.serve({
148
- port: options.port,
149
- fetch: () => new Response()
150
- });
151
- }
152
-
153
- const {
154
- loaders,
155
- middlewares,
156
- guards,
157
- beforeDispatchers,
158
- controllers,
159
- afterDispatchers,
160
- dependencies,
161
- prefix: modulePrefix,
162
- config: moduleConfig
163
- } = moduleMetadata;
164
-
165
- // Configuration(s)
166
- const { allowLogsMethods, config } = Object.freeze({
167
- allowLogsMethods: options?.log?.methods,
168
- config: {
169
- ...(typeof options.config !== "function" ? options.config : await options.config()),
170
- ...(typeof moduleConfig !== "function"
171
- ? typeof moduleConfig !== "object"
172
- ? undefined
173
- : moduleConfig
174
- : await moduleConfig())
175
- }
176
- });
177
-
178
- // Register config like an injection
179
- Injector.set(configKey, config);
180
-
181
- if (loaders) {
182
- const loaderFunctions = [];
183
-
184
- for (const [key, func] of Object.entries(loaders)) {
185
- loaderFunctions.push(async () => {
186
- try {
187
- const result = await func({ config });
188
- console.info(`INFO! Loader [${key}] initialized successfully.`);
189
- return result;
190
- } catch (error) {
191
- console.error(`WARNING! Loader [${key}] initialization failed.`);
192
- options.debug && console.error(error);
193
- throw error;
194
- }
195
- });
196
- }
197
-
198
- const results = await Promise.all(loaderFunctions.map((func) => func()));
199
-
200
- for (let i = 0; i < results.length; i++) {
201
- const [key, value] = results[i];
202
- Injector.set(key, value);
203
- }
204
- }
205
-
206
- // Dependencies
207
- !dependencies || dependencies.map((dependency) => Injector.get(dependency));
208
-
209
- // Middleware(s)
210
- const middlewareGroup = !middlewares
211
- ? []
212
- : middlewares.map((middleware) => {
213
- const middlewareInstance = Injector.get<IMiddleware>(middleware);
214
-
215
- return Object.freeze({
216
- class: middleware,
217
- funcName: "enforce",
218
- func: middlewareInstance.enforce.bind(middlewareInstance)
219
- });
220
- });
221
-
222
- // Guard(s)
223
- const guardGroup = !guards
224
- ? []
225
- : guards.map((guard) => {
226
- const guardInstance = Injector.get<IGuard>(guard);
227
-
228
- return Object.freeze({
229
- class: guard,
230
- funcName: "enforce",
231
- func: guardInstance.enforce.bind(guardInstance)
232
- });
233
- });
234
-
235
- // Before dispatcher(s)
236
- const beforeDispatcherGroup = !beforeDispatchers
237
- ? []
238
- : beforeDispatchers.map((beforeDispatcher) => {
239
- const beforeDispatcherInstance = Injector.get<IDispatcher>(beforeDispatcher);
240
-
241
- return Object.freeze({
242
- class: beforeDispatcher,
243
- funcName: "execute",
244
- func: beforeDispatcherInstance.execute.bind(beforeDispatcherInstance)
245
- });
246
- });
247
-
248
- // Controller(s)
249
- const routerGroup = new RouterGroup();
250
-
251
- controllers &&
252
- controllers.map((controllerConstructor) =>
253
- controllerCreator(controllerConstructor, routerGroup, `${options.prefix || ""}/${modulePrefix || ""}`)
254
- );
255
-
256
- // After dispatcher(s)
257
- const afterDispatcherGroup = !afterDispatchers
258
- ? []
259
- : afterDispatchers.map((afterDispatcher) => {
260
- const afterDispatcherInstance = Injector.get<IDispatcher>(afterDispatcher);
261
-
262
- return Object.freeze({
263
- class: afterDispatcher,
264
- funcName: "execute",
265
- func: afterDispatcherInstance.execute.bind(afterDispatcherInstance)
266
- });
267
- });
268
-
269
- Bun.serve({
270
- port: options.port,
271
- fetch: async (request, server) => {
272
- const { headers } = request;
273
- const start = performance.now();
274
- const url = new URL(request.url);
275
-
276
- const context: Record<symbol, any> = {
277
- [requestHeadersArgsKey]: headers,
278
- [responseHeadersArgsKey]: new Headers(),
279
- [queryArgsKey]: Qs.parse(url.searchParams.toString(), options.queryParser)
280
- };
281
-
282
- const contextHook = {
283
- get(key) {
284
- return context[key];
285
- },
286
- set(key, value) {
287
- if (key in context) {
288
- throw Error(`${String(key)} already exists in context.`);
289
- }
290
-
291
- context[key] = value;
292
- }
293
- } satisfies IContext;
294
-
295
- try {
296
- // Execute middleware(s)
297
- for (let i = 0; i < middlewareGroup.length; i++) {
298
- const middlewareArguments = [];
299
- const middlewareCollection = middlewareGroup[i];
300
- const middlewareMetadata: Record<string, TArgumentsMetadata> =
301
- Reflect.getOwnMetadata(argumentsKey, middlewareCollection.class, middlewareCollection.funcName) ||
302
- {};
303
-
304
- if (middlewareMetadata) {
305
- for (const [_key, argsMetadata] of Object.entries(middlewareMetadata)) {
306
- switch (argsMetadata.type) {
307
- case requestArgsKey:
308
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
309
- ? request
310
- : await argumentsResolution(
311
- request,
312
- argsMetadata.zodSchema,
313
- argsMetadata.index,
314
- middlewareCollection.funcName
315
- );
316
- break;
317
- case bodyArgsKey:
318
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
319
- ? await request[argsMetadata.parser || "json"]()
320
- : await argumentsResolution(
321
- await request[argsMetadata.parser || "json"](),
322
- argsMetadata.zodSchema,
323
- argsMetadata.index,
324
- middlewareCollection.funcName
325
- );
326
- break;
327
- case contextArgsKey:
328
- middlewareArguments[argsMetadata.index] = !argsMetadata.key
329
- ? contextHook
330
- : contextHook.get(argsMetadata.key);
331
- break;
332
- case requestHeadersArgsKey:
333
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
334
- ? headers
335
- : await argumentsResolution(
336
- headers.toJSON(),
337
- argsMetadata.zodSchema,
338
- argsMetadata.index,
339
- middlewareCollection.funcName
340
- );
341
- break;
342
- case responseHeadersArgsKey:
343
- middlewareArguments[argsMetadata.index] = context[argsMetadata.type];
344
- break;
345
- case requestHeaderArgsKey:
346
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
347
- ? headers.get(argsMetadata.key) || undefined
348
- : await argumentsResolution(
349
- headers.get(argsMetadata.key) || undefined,
350
- argsMetadata.zodSchema,
351
- argsMetadata.index,
352
- middlewareCollection.funcName
353
- );
354
- break;
355
- case routeModelArgsKey:
356
- break;
357
- default:
358
- middlewareArguments[argsMetadata.index] = !argsMetadata.zodSchema
359
- ? !(argsMetadata.type in context)
360
- ? undefined
361
- : context[argsMetadata.type]
362
- : await argumentsResolution(
363
- !(argsMetadata.type in context) ? undefined : context[argsMetadata.type],
364
- argsMetadata.zodSchema,
365
- argsMetadata.index,
366
- middlewareCollection.funcName
367
- );
368
- break;
369
- }
370
- }
371
- }
372
-
373
- const middlewareResult = await middlewareCollection.func(...middlewareArguments);
374
-
375
- if (!(middlewareResult instanceof Response)) {
376
- continue;
377
- }
378
-
379
- return middlewareResult;
380
- }
381
-
382
- // Execute guard(s)
383
- for (let i = 0; i < guardGroup.length; i++) {
384
- const guardArguments = [];
385
- const guardCollection = guardGroup[i];
386
- const guardMetadata: Record<string, TArgumentsMetadata> =
387
- Reflect.getOwnMetadata(argumentsKey, guardCollection.class, guardCollection.funcName) || {};
388
-
389
- if (guardMetadata) {
390
- for (const [_key, argsMetadata] of Object.entries(guardMetadata)) {
391
- switch (argsMetadata.type) {
392
- case requestArgsKey:
393
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
394
- ? request
395
- : await argumentsResolution(
396
- request,
397
- argsMetadata.zodSchema,
398
- argsMetadata.index,
399
- guardCollection.funcName
400
- );
401
- break;
402
- case bodyArgsKey:
403
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
404
- ? await request[argsMetadata.parser || "json"]()
405
- : await argumentsResolution(
406
- await request[argsMetadata.parser || "json"](),
407
- argsMetadata.zodSchema,
408
- argsMetadata.index,
409
- guardCollection.funcName
410
- );
411
- break;
412
- case contextArgsKey:
413
- guardArguments[argsMetadata.index] = !argsMetadata.key
414
- ? contextHook
415
- : contextHook.get(argsMetadata.key);
416
- break;
417
- case requestHeadersArgsKey:
418
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
419
- ? headers
420
- : await argumentsResolution(
421
- headers.toJSON(),
422
- argsMetadata.zodSchema,
423
- argsMetadata.index,
424
- guardCollection.funcName
425
- );
426
- break;
427
- case responseHeadersArgsKey:
428
- guardArguments[argsMetadata.index] = context[argsMetadata.type];
429
- break;
430
- case requestHeaderArgsKey:
431
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
432
- ? headers.get(argsMetadata.key) || undefined
433
- : await argumentsResolution(
434
- headers.get(argsMetadata.key) || undefined,
435
- argsMetadata.zodSchema,
436
- argsMetadata.index,
437
- guardCollection.funcName
438
- );
439
- break;
440
- case routeModelArgsKey:
441
- break;
442
- default:
443
- guardArguments[argsMetadata.index] = !argsMetadata.zodSchema
444
- ? context[argsMetadata.type]
445
- : await argumentsResolution(
446
- context[argsMetadata.type],
447
- argsMetadata.zodSchema,
448
- argsMetadata.index,
449
- guardCollection.funcName
450
- );
451
- break;
452
- }
453
- }
454
- }
455
-
456
- const guardResult = await guardCollection.func(...guardArguments);
457
-
458
- if (typeof guardResult !== "boolean" || !guardResult) {
459
- throw new HttpClientError({
460
- httpCode: 401,
461
- message: "Unauthorization.",
462
- data: undefined
463
- });
464
- }
465
- }
466
-
467
- const result = routerGroup.find(url.pathname, request.method as keyof THttpMethods);
468
-
469
- if (!result) {
470
- throw new HttpClientError({
471
- httpCode: 404,
472
- message: "Route not found.",
473
- data: undefined
474
- });
475
- }
476
-
477
- context[paramsArgsKey] = result.parameters;
478
- context[routeModelArgsKey] = result.model;
479
-
480
- let responseBody = undefined;
481
-
482
- // Execute before dispatcher(s)
483
- for (let i = 0; i < beforeDispatcherGroup.length; i++) {
484
- const beforeDispatcherArguments = [];
485
- const beforeDispatcherCollection = beforeDispatcherGroup[i];
486
- const beforeDispatcherMetadata: Record<string, TArgumentsMetadata> =
487
- Reflect.getOwnMetadata(
488
- argumentsKey,
489
- beforeDispatcherCollection.class,
490
- beforeDispatcherCollection.funcName
491
- ) || {};
492
-
493
- if (beforeDispatcherMetadata) {
494
- for (const [_key, argsMetadata] of Object.entries(beforeDispatcherMetadata)) {
495
- switch (argsMetadata.type) {
496
- case requestArgsKey:
497
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
498
- ? request
499
- : await argumentsResolution(
500
- request,
501
- argsMetadata.zodSchema,
502
- argsMetadata.index,
503
- beforeDispatcherCollection.funcName
504
- );
505
- break;
506
- case bodyArgsKey:
507
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
508
- ? await request[argsMetadata.parser || "json"]()
509
- : await argumentsResolution(
510
- await request[argsMetadata.parser || "json"](),
511
- argsMetadata.zodSchema,
512
- argsMetadata.index,
513
- beforeDispatcherCollection.funcName
514
- );
515
- break;
516
- case contextArgsKey:
517
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.key
518
- ? contextHook
519
- : contextHook.get(argsMetadata.key);
520
- break;
521
- case requestHeadersArgsKey:
522
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
523
- ? headers
524
- : await argumentsResolution(
525
- headers.toJSON(),
526
- argsMetadata.zodSchema,
527
- argsMetadata.index,
528
- beforeDispatcherCollection.funcName
529
- );
530
- break;
531
- case responseHeadersArgsKey:
532
- beforeDispatcherArguments[argsMetadata.index] = context[argsMetadata.type];
533
- break;
534
- case requestHeaderArgsKey:
535
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
536
- ? headers.get(argsMetadata.key) || undefined
537
- : await argumentsResolution(
538
- headers.get(argsMetadata.key) || undefined,
539
- argsMetadata.zodSchema,
540
- argsMetadata.index,
541
- beforeDispatcherCollection.funcName
542
- );
543
- break;
544
- case paramArgsKey:
545
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
546
- ? context[paramArgsKey][argsMetadata.key] || undefined
547
- : await argumentsResolution(
548
- context[paramArgsKey][argsMetadata.key],
549
- argsMetadata.zodSchema,
550
- argsMetadata.index,
551
- beforeDispatcherCollection.funcName
552
- );
553
- break;
554
- case routeModelArgsKey:
555
- beforeDispatcherArguments[argsMetadata.index] = context[routeModelArgsKey];
556
- break;
557
- default:
558
- beforeDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
559
- ? context[argsMetadata.type]
560
- : await argumentsResolution(
561
- context[argsMetadata.type],
562
- argsMetadata.zodSchema,
563
- argsMetadata.index,
564
- beforeDispatcherCollection.funcName
565
- );
566
- break;
567
- }
568
- }
569
- }
570
-
571
- await beforeDispatcherCollection.func(...beforeDispatcherArguments);
572
- }
573
-
574
- // Execute controller action
575
- const controllerActionArguments: any[] = [];
576
- const controllerActionCollection = result.model;
577
- const controllerActionMetadata: Record<string, TArgumentsMetadata> =
578
- Reflect.getOwnMetadata(
579
- argumentsKey,
580
- controllerActionCollection.class,
581
- controllerActionCollection.funcName
582
- ) || {};
583
-
584
- if (controllerActionMetadata) {
585
- for (const [_key, argsMetadata] of Object.entries(controllerActionMetadata)) {
586
- switch (argsMetadata.type) {
587
- case requestArgsKey:
588
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
589
- ? request
590
- : await argumentsResolution(
591
- request,
592
- argsMetadata.zodSchema,
593
- argsMetadata.index,
594
- controllerActionCollection.funcName
595
- );
596
- break;
597
- case bodyArgsKey:
598
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
599
- ? await request[argsMetadata.parser || "json"]()
600
- : await argumentsResolution(
601
- await request[argsMetadata.parser || "json"](),
602
- argsMetadata.zodSchema,
603
- argsMetadata.index,
604
- controllerActionCollection.funcName
605
- );
606
- break;
607
- case contextArgsKey:
608
- controllerActionArguments[argsMetadata.index] = !argsMetadata.key
609
- ? contextHook
610
- : contextHook.get(argsMetadata.key);
611
- break;
612
- case requestHeadersArgsKey:
613
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
614
- ? headers
615
- : await argumentsResolution(
616
- headers.toJSON(),
617
- argsMetadata.zodSchema,
618
- argsMetadata.index,
619
- controllerActionCollection.funcName
620
- );
621
- break;
622
- case responseHeadersArgsKey:
623
- controllerActionArguments[argsMetadata.index] = context[argsMetadata.type];
624
- break;
625
- case requestHeaderArgsKey:
626
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
627
- ? headers.get(argsMetadata.key) || undefined
628
- : await argumentsResolution(
629
- headers.get(argsMetadata.key) || undefined,
630
- argsMetadata.zodSchema,
631
- argsMetadata.index,
632
- controllerActionCollection.funcName
633
- );
634
- break;
635
- case paramArgsKey:
636
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
637
- ? context[paramArgsKey][argsMetadata.key] || undefined
638
- : await argumentsResolution(
639
- context[paramArgsKey][argsMetadata.key],
640
- argsMetadata.zodSchema,
641
- argsMetadata.index,
642
- controllerActionCollection.funcName
643
- );
644
- break;
645
- case routeModelArgsKey:
646
- controllerActionArguments[argsMetadata.index] = context[routeModelArgsKey];
647
- break;
648
- default:
649
- controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
650
- ? context[argsMetadata.type]
651
- : await argumentsResolution(
652
- context[argsMetadata.type],
653
- argsMetadata.zodSchema,
654
- argsMetadata.index,
655
- controllerActionCollection.funcName
656
- );
657
- break;
658
- }
659
- }
660
- }
661
-
662
- responseBody = await controllerActionCollection.func(...controllerActionArguments);
663
-
664
- // Execute after dispatcher(s)
665
- for (let i = 0; i < afterDispatcherGroup.length; i++) {
666
- const afterDispatcherArguments = [];
667
- const afterDispatcherCollection = afterDispatcherGroup[i];
668
- const afterDispatcherMetadata: Record<string, TArgumentsMetadata> =
669
- Reflect.getOwnMetadata(
670
- argumentsKey,
671
- afterDispatcherCollection.class,
672
- afterDispatcherCollection.funcName
673
- ) || {};
674
-
675
- if (afterDispatcherMetadata) {
676
- for (const [_key, argsMetadata] of Object.entries(afterDispatcherMetadata)) {
677
- switch (argsMetadata.type) {
678
- case requestArgsKey:
679
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
680
- ? request
681
- : await argumentsResolution(
682
- request,
683
- argsMetadata.zodSchema,
684
- argsMetadata.index,
685
- afterDispatcherCollection.funcName
686
- );
687
- break;
688
- case bodyArgsKey:
689
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
690
- ? await request[argsMetadata.parser || "json"]()
691
- : await argumentsResolution(
692
- await request[argsMetadata.parser || "json"](),
693
- argsMetadata.zodSchema,
694
- argsMetadata.index,
695
- afterDispatcherCollection.funcName
696
- );
697
- break;
698
- case contextArgsKey:
699
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.key
700
- ? contextHook
701
- : contextHook.get(argsMetadata.key);
702
- break;
703
- case requestHeadersArgsKey:
704
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
705
- ? headers
706
- : await argumentsResolution(
707
- headers.toJSON(),
708
- argsMetadata.zodSchema,
709
- argsMetadata.index,
710
- afterDispatcherCollection.funcName
711
- );
712
- break;
713
- case responseHeadersArgsKey:
714
- afterDispatcherArguments[argsMetadata.index] = context[argsMetadata.type];
715
- break;
716
- case requestHeaderArgsKey:
717
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
718
- ? headers.get(argsMetadata.key) || undefined
719
- : await argumentsResolution(
720
- headers.get(argsMetadata.key) || undefined,
721
- argsMetadata.zodSchema,
722
- argsMetadata.index,
723
- afterDispatcherCollection.funcName
724
- );
725
- break;
726
- case paramArgsKey:
727
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
728
- ? context[paramArgsKey][argsMetadata.key] || undefined
729
- : await argumentsResolution(
730
- context[paramArgsKey][argsMetadata.key],
731
- argsMetadata.zodSchema,
732
- argsMetadata.index,
733
- afterDispatcherCollection.funcName
734
- );
735
- break;
736
- case routeModelArgsKey:
737
- afterDispatcherArguments[argsMetadata.index] = context[routeModelArgsKey];
738
- break;
739
- default:
740
- afterDispatcherArguments[argsMetadata.index] = !argsMetadata.zodSchema
741
- ? context[argsMetadata.type]
742
- : await argumentsResolution(
743
- context[argsMetadata.type],
744
- argsMetadata.zodSchema,
745
- argsMetadata.index,
746
- afterDispatcherCollection.funcName
747
- );
748
- break;
749
- }
750
- }
751
- }
752
-
753
- await afterDispatcherCollection.func(...afterDispatcherArguments);
754
- }
755
-
756
- // Set default header(s)
757
- context[responseHeadersArgsKey].set("X-Powered-By", "Bool Typescript");
758
-
759
- return responseBody instanceof Response
760
- ? responseBody
761
- : new Response(
762
- !responseBody
763
- ? undefined
764
- : JSON.stringify({
765
- httpCode: 200,
766
- message: "SUCCESS",
767
- data: responseBody
768
- }),
769
- {
770
- status: !responseBody ? 204 : 200,
771
- statusText: "SUCCESS",
772
- headers: context[responseHeadersArgsKey]
773
- }
774
- );
775
- } catch (error) {
776
- options.debug && console.error(error);
777
-
778
- // Set default header(s)
779
- context[responseHeadersArgsKey].set("X-Powered-By", "Bool Typescript");
780
-
781
- return jsonErrorInfer(error, context[responseHeadersArgsKey]);
782
- } finally {
783
- if (allowLogsMethods) {
784
- const end = performance.now();
785
- const convertedPID = `${process.pid}`.yellow;
786
- const convertedMethod = `${request.method.yellow}`.bgBlue;
787
- const convertedReqIp = `${
788
- request.headers.get("x-forwarded-for") ||
789
- request.headers.get("x-real-ip") ||
790
- server.requestIP(request)?.address ||
791
- "<Unknown>"
792
- }`.yellow;
793
- const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
794
-
795
- allowLogsMethods.includes(request.method.toUpperCase() as (typeof allowLogsMethods)[number]) &&
796
- console.info(
797
- `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${url.pathname.blue} - Time: ${convertedTime}`
798
- );
799
- }
800
- }
801
- }
802
- });
803
- } catch (error) {
804
- options.debug && console.error(error);
805
- throw error;
806
- }
807
- };
808
-
809
- export default BoolFactory;
1
+ import type { TArgumentsMetadata, TControllerMetadata, THttpMetadata, TModuleMetadata } from "../decorators";
2
+ import type { IContext, IGuard, IMiddleware } from "../interfaces";
3
+ import type { IDispatcher } from "../interfaces/dispatcher";
4
+
5
+ import "colors";
6
+ import "reflect-metadata";
7
+
8
+ import Qs from "qs";
9
+ import * as Zod from "zod";
10
+
11
+ import type { Server } from "bun";
12
+ import { Router, RouterGroup } from "../entities";
13
+ import { HttpClientError, HttpServerError, jsonErrorInfer, type THttpMethods } from "../http";
14
+ import {
15
+ argumentsKey,
16
+ configKey,
17
+ contextArgsKey,
18
+ controllerHttpKey,
19
+ controllerKey,
20
+ moduleKey,
21
+ paramArgsKey,
22
+ paramsArgsKey,
23
+ queryArgsKey,
24
+ requestArgsKey,
25
+ requestBodyArgsKey,
26
+ requestHeaderArgsKey,
27
+ requestHeadersArgsKey,
28
+ responseBodyArgsKey,
29
+ responseHeadersArgsKey,
30
+ routeModelArgsKey
31
+ } from "../keys";
32
+ import { Injector } from "./injector";
33
+
34
+ export type TGroupElementModel<
35
+ TFuncName extends keyof TClass,
36
+ TClass extends Object = Object,
37
+ TFunc = TClass[TFuncName]
38
+ > = Readonly<{
39
+ class: TClass;
40
+ func: TFunc;
41
+ funcName: TFuncName;
42
+ }>;
43
+
44
+ export type TBoolFactoryOptions = Required<{
45
+ port: number;
46
+ }> &
47
+ Partial<{
48
+ config: Record<string | symbol, any> | (() => Record<string | symbol, any>);
49
+ prefix: string;
50
+ debug: boolean;
51
+ log: Partial<{
52
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
53
+ }>;
54
+ queryParser: Parameters<typeof Qs.parse>[1];
55
+ }>;
56
+
57
+ export const responseConverter = (response: Response) => {
58
+ response.headers.set("X-Powered-By", "Bool Typescript");
59
+
60
+ return response;
61
+ };
62
+
63
+ export const controllerCreator = (
64
+ controllerConstructor: new (...args: any[]) => unknown,
65
+ group: RouterGroup,
66
+ injector: Injector,
67
+ prefix?: string
68
+ ) => {
69
+ if (!Reflect.getOwnMetadataKeys(controllerConstructor).includes(controllerKey)) {
70
+ throw Error(`${controllerConstructor.name} is not a controller.`);
71
+ }
72
+
73
+ const controller = injector.get(controllerConstructor);
74
+
75
+ if (!controller) {
76
+ throw Error("Can not initialize controller.");
77
+ }
78
+
79
+ const controllerMetadata: TControllerMetadata = Reflect.getOwnMetadata(controllerKey, controllerConstructor) || {
80
+ prefix: "/",
81
+ httpMetadata: []
82
+ };
83
+ const routesMetadata = (Reflect.getOwnMetadata(controllerHttpKey, controllerConstructor) || []) as THttpMetadata;
84
+ const router = new Router(`/${prefix || ""}/${controllerMetadata.prefix}`);
85
+
86
+ routesMetadata.forEach((routeMetadata) => {
87
+ if (typeof routeMetadata.descriptor.value !== "function") {
88
+ return;
89
+ }
90
+
91
+ const route = router.route(routeMetadata.path);
92
+ const handler = routeMetadata.descriptor.value.bind(controller);
93
+ const routeArgument = Object.freeze({
94
+ class: controllerConstructor,
95
+ funcName: routeMetadata.methodName,
96
+ func: handler
97
+ });
98
+
99
+ switch (routeMetadata.httpMethod) {
100
+ case "GET":
101
+ return route.get(routeArgument);
102
+ case "POST":
103
+ return route.post(routeArgument);
104
+ case "PUT":
105
+ return route.put(routeArgument);
106
+ case "PATCH":
107
+ return route.patch(routeArgument);
108
+ case "DELETE":
109
+ return route.delete(routeArgument);
110
+ case "OPTIONS":
111
+ return route.options(routeArgument);
112
+ }
113
+ });
114
+
115
+ return group.add(router);
116
+ };
117
+
118
+ export const argumentsResolution = async (
119
+ data: unknown,
120
+ zodSchema: Zod.Schema,
121
+ argumentIndex: number,
122
+ funcName: string | symbol
123
+ ) => {
124
+ try {
125
+ const validation = await zodSchema.safeParseAsync(data);
126
+
127
+ if (!validation.success) {
128
+ throw new HttpClientError({
129
+ httpCode: 400,
130
+ message: `Validation at the [${funcName.toString()}] method fails at positional argument [${argumentIndex}].`,
131
+ data: validation.error.issues
132
+ });
133
+ }
134
+
135
+ return validation.data;
136
+ } catch (error) {
137
+ if (error instanceof HttpClientError) {
138
+ throw error;
139
+ }
140
+
141
+ throw new HttpServerError({
142
+ httpCode: 500,
143
+ message: `Validation at the [${funcName.toString()}] method error at positional argument [${argumentIndex}].`,
144
+ data: !(error instanceof Error)
145
+ ? error
146
+ : [
147
+ {
148
+ message: error.message,
149
+ code: error.name,
150
+ cause: error.cause
151
+ }
152
+ ]
153
+ });
154
+ }
155
+ };
156
+
157
+ export const moduleResolution = async (module: new (...args: any[]) => unknown, options: TBoolFactoryOptions) => {
158
+ if (!Reflect.getOwnMetadataKeys(module).includes(moduleKey)) {
159
+ throw Error(`${module.name} is not a module.`);
160
+ }
161
+
162
+ const injector = new Injector();
163
+ const moduleMetadata = Reflect.getOwnMetadata(moduleKey, module) as TModuleMetadata;
164
+
165
+ if (!moduleMetadata) {
166
+ return;
167
+ }
168
+
169
+ const {
170
+ loaders,
171
+ middlewares,
172
+ guards,
173
+ dispatchers,
174
+ controllers,
175
+ dependencies,
176
+ prefix: modulePrefix,
177
+ config: moduleConfig
178
+ } = moduleMetadata;
179
+
180
+ // Configuration(s)
181
+ const { config } = Object.freeze({
182
+ config: {
183
+ ...(typeof options.config !== "function" ? options.config : await options.config()),
184
+ ...(typeof moduleConfig !== "function"
185
+ ? typeof moduleConfig !== "object"
186
+ ? undefined
187
+ : moduleConfig
188
+ : await moduleConfig())
189
+ }
190
+ });
191
+
192
+ // Register config like an injection
193
+ injector.set(configKey, config);
194
+
195
+ if (loaders) {
196
+ const loaderFunctions = [];
197
+
198
+ for (const [key, func] of Object.entries(loaders)) {
199
+ loaderFunctions.push(async () => {
200
+ try {
201
+ const result = await func({ config });
202
+ console.info(`INFO! Loader [${key}] initialized successfully.`);
203
+ return result;
204
+ } catch (error) {
205
+ console.error(`WARNING! Loader [${key}] initialization failed.`);
206
+ options.debug && console.error(error);
207
+ throw error;
208
+ }
209
+ });
210
+ }
211
+
212
+ const results = await Promise.all(loaderFunctions.map((func) => func()));
213
+
214
+ for (let i = 0; i < results.length; i++) {
215
+ const [key, value] = results[i];
216
+ injector.set(key, value);
217
+ }
218
+ }
219
+
220
+ // Dependencies
221
+ !dependencies || dependencies.map((dependency) => injector.get(dependency));
222
+
223
+ // Middleware(s)
224
+ const startMiddlewareGroup: Array<TGroupElementModel<"start", IMiddleware, NonNullable<IMiddleware["start"]>>> = [];
225
+ const endMiddlewareGroup: Array<TGroupElementModel<"end", IMiddleware, NonNullable<IMiddleware["end"]>>> = [];
226
+
227
+ if (middlewares) {
228
+ for (let i = 0; i < middlewares.length; i++) {
229
+ const instance = injector.get<IMiddleware>(middlewares[i]);
230
+
231
+ if (instance.start && typeof instance.start === "function") {
232
+ startMiddlewareGroup.push(
233
+ Object.freeze({
234
+ class: middlewares[i] as IMiddleware,
235
+ funcName: "start",
236
+ func: instance.start.bind(instance)
237
+ })
238
+ );
239
+ }
240
+
241
+ if (instance.end && typeof instance.end === "function") {
242
+ endMiddlewareGroup.push(
243
+ Object.freeze({
244
+ class: middlewares[i] as IMiddleware,
245
+ funcName: "end",
246
+ func: instance.end.bind(instance)
247
+ })
248
+ );
249
+ }
250
+ }
251
+ }
252
+
253
+ // Guard(s)
254
+ const guardGroup = !guards
255
+ ? []
256
+ : guards.map((guard) => {
257
+ const guardInstance = injector.get<IGuard>(guard);
258
+
259
+ return Object.freeze({
260
+ class: guard,
261
+ funcName: "enforce",
262
+ func: guardInstance.enforce.bind(guardInstance)
263
+ });
264
+ });
265
+
266
+ // Before dispatcher(s)
267
+ const openDispatcherGroup: Array<TGroupElementModel<"open", IDispatcher, NonNullable<IDispatcher["open"]>>> = [];
268
+ const closeDispatcherGroup: Array<TGroupElementModel<"close", IDispatcher, NonNullable<IDispatcher["close"]>>> = [];
269
+
270
+ if (dispatchers) {
271
+ for (let i = 0; i < dispatchers.length; i++) {
272
+ const instance = injector.get<IDispatcher>(dispatchers[i]);
273
+
274
+ if (instance.open && typeof instance.open === "function") {
275
+ openDispatcherGroup.push(
276
+ Object.freeze({
277
+ class: dispatchers[i] as IDispatcher,
278
+ funcName: "open",
279
+ func: instance.open.bind(instance)
280
+ })
281
+ );
282
+ }
283
+
284
+ if (instance.close && typeof instance.close === "function") {
285
+ closeDispatcherGroup.push(
286
+ Object.freeze({
287
+ class: dispatchers[i] as IDispatcher,
288
+ funcName: "close",
289
+ func: instance.close.bind(instance)
290
+ })
291
+ );
292
+ }
293
+ }
294
+ }
295
+
296
+ // Controller(s)
297
+ const routerGroup = new RouterGroup();
298
+
299
+ controllers &&
300
+ controllers.map((controllerConstructor) =>
301
+ controllerCreator(controllerConstructor, routerGroup, injector, `${options.prefix || ""}/${modulePrefix || ""}`)
302
+ );
303
+
304
+ return Object.freeze({
305
+ prefix: moduleMetadata.prefix,
306
+ injector: injector,
307
+ startMiddlewareGroup,
308
+ endMiddlewareGroup,
309
+ guardGroup,
310
+ openDispatcherGroup,
311
+ closeDispatcherGroup,
312
+ routerGroup
313
+ });
314
+ };
315
+
316
+ const fetcher = async (
317
+ bun: Required<{
318
+ request: Request;
319
+ server: Server;
320
+ }>,
321
+ bool: Required<{
322
+ query: Record<string, unknown>;
323
+ route: NonNullable<ReturnType<RouterGroup["find"]>>;
324
+ moduleResolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
325
+ }>,
326
+ options: TBoolFactoryOptions
327
+ ) => {
328
+ const {
329
+ query,
330
+ route: { parameters, model },
331
+ moduleResolution: { startMiddlewareGroup, endMiddlewareGroup, guardGroup, openDispatcherGroup, closeDispatcherGroup }
332
+ } = bool;
333
+ const { request, server: _server } = bun;
334
+
335
+ const context: Record<symbol, any> = {
336
+ [requestHeadersArgsKey]: request.headers,
337
+ [responseHeadersArgsKey]: new Headers(),
338
+ [queryArgsKey]: query,
339
+ [paramsArgsKey]: parameters,
340
+ [routeModelArgsKey]: model
341
+ };
342
+
343
+ const contextHook = {
344
+ get(key) {
345
+ return context[key];
346
+ },
347
+ set(key, value) {
348
+ if (key in context) {
349
+ throw Error(`${String(key)} already exists in context.`);
350
+ }
351
+
352
+ context[key] = value;
353
+ }
354
+ } satisfies IContext;
355
+
356
+ // Execute start middleware(s)
357
+ for (let i = 0; i < startMiddlewareGroup.length; i++) {
358
+ const args = [];
359
+ const collection = startMiddlewareGroup[i];
360
+ const metadata: Record<string, TArgumentsMetadata> =
361
+ Reflect.getOwnMetadata(argumentsKey, collection.class, collection.funcName) || {};
362
+
363
+ if (metadata) {
364
+ for (const [_key, argsMetadata] of Object.entries(metadata)) {
365
+ switch (argsMetadata.type) {
366
+ case requestArgsKey:
367
+ args[argsMetadata.index] = !argsMetadata.zodSchema
368
+ ? request
369
+ : await argumentsResolution(
370
+ request,
371
+ argsMetadata.zodSchema,
372
+ argsMetadata.index,
373
+ collection.funcName
374
+ );
375
+ break;
376
+ case requestBodyArgsKey:
377
+ args[argsMetadata.index] = !argsMetadata.zodSchema
378
+ ? await request[argsMetadata.parser || "json"]()
379
+ : await argumentsResolution(
380
+ await request[argsMetadata.parser || "json"](),
381
+ argsMetadata.zodSchema,
382
+ argsMetadata.index,
383
+ collection.funcName
384
+ );
385
+ break;
386
+ case contextArgsKey:
387
+ args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
388
+ break;
389
+ case requestHeadersArgsKey:
390
+ args[argsMetadata.index] = !argsMetadata.zodSchema
391
+ ? request.headers
392
+ : await argumentsResolution(
393
+ request.headers.toJSON(),
394
+ argsMetadata.zodSchema,
395
+ argsMetadata.index,
396
+ collection.funcName
397
+ );
398
+ break;
399
+ case responseHeadersArgsKey:
400
+ args[argsMetadata.index] = context[argsMetadata.type];
401
+ break;
402
+ case requestHeaderArgsKey:
403
+ args[argsMetadata.index] = !argsMetadata.zodSchema
404
+ ? request.headers.get(argsMetadata.key) || undefined
405
+ : await argumentsResolution(
406
+ request.headers.get(argsMetadata.key) || undefined,
407
+ argsMetadata.zodSchema,
408
+ argsMetadata.index,
409
+ collection.funcName
410
+ );
411
+ break;
412
+ case routeModelArgsKey:
413
+ break;
414
+ default:
415
+ args[argsMetadata.index] = !argsMetadata.zodSchema
416
+ ? !(argsMetadata.type in context)
417
+ ? undefined
418
+ : context[argsMetadata.type]
419
+ : await argumentsResolution(
420
+ !(argsMetadata.type in context) ? undefined : context[argsMetadata.type],
421
+ argsMetadata.zodSchema,
422
+ argsMetadata.index,
423
+ collection.funcName
424
+ );
425
+ break;
426
+ }
427
+ }
428
+ }
429
+
430
+ context[responseBodyArgsKey] = await collection.func(...args);
431
+
432
+ if (context[responseBodyArgsKey] instanceof Response) {
433
+ return responseConverter(context[responseBodyArgsKey]);
434
+ }
435
+ }
436
+
437
+ // Execute guard(s)
438
+ for (let i = 0; i < guardGroup.length; i++) {
439
+ const args = [];
440
+ const collection = guardGroup[i];
441
+ const metadata: Record<string, TArgumentsMetadata> =
442
+ Reflect.getOwnMetadata(argumentsKey, collection.class, collection.funcName) || {};
443
+
444
+ if (metadata) {
445
+ for (const [_key, argsMetadata] of Object.entries(metadata)) {
446
+ switch (argsMetadata.type) {
447
+ case requestArgsKey:
448
+ args[argsMetadata.index] = !argsMetadata.zodSchema
449
+ ? request
450
+ : await argumentsResolution(
451
+ request,
452
+ argsMetadata.zodSchema,
453
+ argsMetadata.index,
454
+ collection.funcName
455
+ );
456
+ break;
457
+ case requestBodyArgsKey:
458
+ args[argsMetadata.index] = !argsMetadata.zodSchema
459
+ ? await request[argsMetadata.parser || "json"]()
460
+ : await argumentsResolution(
461
+ await request[argsMetadata.parser || "json"](),
462
+ argsMetadata.zodSchema,
463
+ argsMetadata.index,
464
+ collection.funcName
465
+ );
466
+ break;
467
+ case contextArgsKey:
468
+ args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
469
+ break;
470
+ case requestHeadersArgsKey:
471
+ args[argsMetadata.index] = !argsMetadata.zodSchema
472
+ ? request.headers
473
+ : await argumentsResolution(
474
+ request.headers.toJSON(),
475
+ argsMetadata.zodSchema,
476
+ argsMetadata.index,
477
+ collection.funcName
478
+ );
479
+ break;
480
+ case responseHeadersArgsKey:
481
+ args[argsMetadata.index] = context[argsMetadata.type];
482
+ break;
483
+ case requestHeaderArgsKey:
484
+ args[argsMetadata.index] = !argsMetadata.zodSchema
485
+ ? request.headers.get(argsMetadata.key) || undefined
486
+ : await argumentsResolution(
487
+ request.headers.get(argsMetadata.key) || undefined,
488
+ argsMetadata.zodSchema,
489
+ argsMetadata.index,
490
+ collection.funcName
491
+ );
492
+ break;
493
+ case routeModelArgsKey:
494
+ break;
495
+ default:
496
+ args[argsMetadata.index] = !argsMetadata.zodSchema
497
+ ? context[argsMetadata.type]
498
+ : await argumentsResolution(
499
+ context[argsMetadata.type],
500
+ argsMetadata.zodSchema,
501
+ argsMetadata.index,
502
+ collection.funcName
503
+ );
504
+ break;
505
+ }
506
+ }
507
+ }
508
+
509
+ const guardResult = await collection.func(...args);
510
+
511
+ if (typeof guardResult !== "boolean" || !guardResult) {
512
+ throw new HttpClientError({
513
+ httpCode: 401,
514
+ message: "Unauthorization.",
515
+ data: undefined
516
+ });
517
+ }
518
+ }
519
+
520
+ // Execute open dispatcher(s)
521
+ for (let i = 0; i < openDispatcherGroup.length; i++) {
522
+ const args = [];
523
+ const collection = openDispatcherGroup[i];
524
+ const metadata: Record<string, TArgumentsMetadata> =
525
+ Reflect.getOwnMetadata(argumentsKey, collection.class, collection.funcName) || {};
526
+
527
+ if (metadata) {
528
+ for (const [_key, argsMetadata] of Object.entries(metadata)) {
529
+ switch (argsMetadata.type) {
530
+ case requestArgsKey:
531
+ args[argsMetadata.index] = !argsMetadata.zodSchema
532
+ ? request
533
+ : await argumentsResolution(
534
+ request,
535
+ argsMetadata.zodSchema,
536
+ argsMetadata.index,
537
+ collection.funcName
538
+ );
539
+ break;
540
+ case requestBodyArgsKey:
541
+ args[argsMetadata.index] = !argsMetadata.zodSchema
542
+ ? await request[argsMetadata.parser || "json"]()
543
+ : await argumentsResolution(
544
+ await request[argsMetadata.parser || "json"](),
545
+ argsMetadata.zodSchema,
546
+ argsMetadata.index,
547
+ collection.funcName
548
+ );
549
+ break;
550
+ case contextArgsKey:
551
+ args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
552
+ break;
553
+ case requestHeadersArgsKey:
554
+ args[argsMetadata.index] = !argsMetadata.zodSchema
555
+ ? request.headers
556
+ : await argumentsResolution(
557
+ request.headers.toJSON(),
558
+ argsMetadata.zodSchema,
559
+ argsMetadata.index,
560
+ collection.funcName
561
+ );
562
+ break;
563
+ case responseHeadersArgsKey:
564
+ args[argsMetadata.index] = context[argsMetadata.type];
565
+ break;
566
+ case requestHeaderArgsKey:
567
+ args[argsMetadata.index] = !argsMetadata.zodSchema
568
+ ? request.headers.get(argsMetadata.key) || undefined
569
+ : await argumentsResolution(
570
+ request.headers.get(argsMetadata.key) || undefined,
571
+ argsMetadata.zodSchema,
572
+ argsMetadata.index,
573
+ collection.funcName
574
+ );
575
+ break;
576
+ case paramArgsKey:
577
+ args[argsMetadata.index] = !argsMetadata.zodSchema
578
+ ? context[paramsArgsKey][argsMetadata.key] || undefined
579
+ : await argumentsResolution(
580
+ context[paramsArgsKey][argsMetadata.key],
581
+ argsMetadata.zodSchema,
582
+ argsMetadata.index,
583
+ collection.funcName
584
+ );
585
+ break;
586
+ case routeModelArgsKey:
587
+ args[argsMetadata.index] = context[routeModelArgsKey];
588
+ break;
589
+ default:
590
+ args[argsMetadata.index] = !argsMetadata.zodSchema
591
+ ? context[argsMetadata.type]
592
+ : await argumentsResolution(
593
+ context[argsMetadata.type],
594
+ argsMetadata.zodSchema,
595
+ argsMetadata.index,
596
+ collection.funcName
597
+ );
598
+ break;
599
+ }
600
+ }
601
+ }
602
+
603
+ context[responseBodyArgsKey] = await collection.func(...args);
604
+ }
605
+
606
+ // Execute controller action
607
+ const controllerActionArguments: any[] = [];
608
+ const controllerActionCollection = model;
609
+ const controllerActionMetadata: Record<string, TArgumentsMetadata> =
610
+ Reflect.getOwnMetadata(argumentsKey, controllerActionCollection.class, controllerActionCollection.funcName) || {};
611
+
612
+ if (controllerActionMetadata) {
613
+ for (const [_key, argsMetadata] of Object.entries(controllerActionMetadata)) {
614
+ switch (argsMetadata.type) {
615
+ case requestArgsKey:
616
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
617
+ ? request
618
+ : await argumentsResolution(
619
+ request,
620
+ argsMetadata.zodSchema,
621
+ argsMetadata.index,
622
+ controllerActionCollection.funcName
623
+ );
624
+ break;
625
+ case requestBodyArgsKey:
626
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
627
+ ? await request[argsMetadata.parser || "json"]()
628
+ : await argumentsResolution(
629
+ await request[argsMetadata.parser || "json"](),
630
+ argsMetadata.zodSchema,
631
+ argsMetadata.index,
632
+ controllerActionCollection.funcName
633
+ );
634
+ break;
635
+ case contextArgsKey:
636
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.key
637
+ ? contextHook
638
+ : contextHook.get(argsMetadata.key);
639
+ break;
640
+ case requestHeadersArgsKey:
641
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
642
+ ? request.headers
643
+ : await argumentsResolution(
644
+ request.headers.toJSON(),
645
+ argsMetadata.zodSchema,
646
+ argsMetadata.index,
647
+ controllerActionCollection.funcName
648
+ );
649
+ break;
650
+ case responseHeadersArgsKey:
651
+ controllerActionArguments[argsMetadata.index] = context[argsMetadata.type];
652
+ break;
653
+ case requestHeaderArgsKey:
654
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
655
+ ? request.headers.get(argsMetadata.key) || undefined
656
+ : await argumentsResolution(
657
+ request.headers.get(argsMetadata.key) || undefined,
658
+ argsMetadata.zodSchema,
659
+ argsMetadata.index,
660
+ controllerActionCollection.funcName
661
+ );
662
+ break;
663
+ case paramArgsKey:
664
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
665
+ ? context[paramsArgsKey][argsMetadata.key] || undefined
666
+ : await argumentsResolution(
667
+ context[paramsArgsKey][argsMetadata.key],
668
+ argsMetadata.zodSchema,
669
+ argsMetadata.index,
670
+ controllerActionCollection.funcName
671
+ );
672
+ break;
673
+ case routeModelArgsKey:
674
+ controllerActionArguments[argsMetadata.index] = context[routeModelArgsKey];
675
+ break;
676
+ default:
677
+ controllerActionArguments[argsMetadata.index] = !argsMetadata.zodSchema
678
+ ? context[argsMetadata.type]
679
+ : await argumentsResolution(
680
+ context[argsMetadata.type],
681
+ argsMetadata.zodSchema,
682
+ argsMetadata.index,
683
+ controllerActionCollection.funcName
684
+ );
685
+ break;
686
+ }
687
+ }
688
+ }
689
+
690
+ context[responseBodyArgsKey] = await controllerActionCollection.func(...controllerActionArguments);
691
+
692
+ // Execute close dispatcher(s)
693
+ for (let i = 0; i < closeDispatcherGroup.length; i++) {
694
+ const args = [];
695
+ const collection = closeDispatcherGroup[i];
696
+ const metadata: Record<string, TArgumentsMetadata> =
697
+ Reflect.getOwnMetadata(argumentsKey, collection.class, collection.funcName) || {};
698
+
699
+ if (metadata) {
700
+ for (const [_key, argsMetadata] of Object.entries(metadata)) {
701
+ switch (argsMetadata.type) {
702
+ case requestArgsKey:
703
+ args[argsMetadata.index] = !argsMetadata.zodSchema
704
+ ? request
705
+ : await argumentsResolution(
706
+ request,
707
+ argsMetadata.zodSchema,
708
+ argsMetadata.index,
709
+ collection.funcName
710
+ );
711
+ break;
712
+ case requestBodyArgsKey:
713
+ args[argsMetadata.index] = !argsMetadata.zodSchema
714
+ ? await request[argsMetadata.parser || "json"]()
715
+ : await argumentsResolution(
716
+ await request[argsMetadata.parser || "json"](),
717
+ argsMetadata.zodSchema,
718
+ argsMetadata.index,
719
+ collection.funcName
720
+ );
721
+ break;
722
+ case contextArgsKey:
723
+ args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
724
+ break;
725
+ case requestHeadersArgsKey:
726
+ args[argsMetadata.index] = !argsMetadata.zodSchema
727
+ ? request.headers
728
+ : await argumentsResolution(
729
+ request.headers.toJSON(),
730
+ argsMetadata.zodSchema,
731
+ argsMetadata.index,
732
+ collection.funcName
733
+ );
734
+ break;
735
+ case responseHeadersArgsKey:
736
+ args[argsMetadata.index] = context[argsMetadata.type];
737
+ break;
738
+ case requestHeaderArgsKey:
739
+ args[argsMetadata.index] = !argsMetadata.zodSchema
740
+ ? request.headers.get(argsMetadata.key) || undefined
741
+ : await argumentsResolution(
742
+ request.headers.get(argsMetadata.key) || undefined,
743
+ argsMetadata.zodSchema,
744
+ argsMetadata.index,
745
+ collection.funcName
746
+ );
747
+ break;
748
+ case paramArgsKey:
749
+ args[argsMetadata.index] = !argsMetadata.zodSchema
750
+ ? context[paramsArgsKey][argsMetadata.key] || undefined
751
+ : await argumentsResolution(
752
+ context[paramsArgsKey][argsMetadata.key],
753
+ argsMetadata.zodSchema,
754
+ argsMetadata.index,
755
+ collection.funcName
756
+ );
757
+ break;
758
+ case routeModelArgsKey:
759
+ args[argsMetadata.index] = context[routeModelArgsKey];
760
+ break;
761
+ default:
762
+ args[argsMetadata.index] = !argsMetadata.zodSchema
763
+ ? context[argsMetadata.type]
764
+ : await argumentsResolution(
765
+ context[argsMetadata.type],
766
+ argsMetadata.zodSchema,
767
+ argsMetadata.index,
768
+ collection.funcName
769
+ );
770
+ break;
771
+ }
772
+ }
773
+ }
774
+
775
+ await collection.func(...args);
776
+ }
777
+
778
+ // Execute end middleware(s)
779
+ for (let i = 0; i < endMiddlewareGroup.length; i++) {
780
+ const args = [];
781
+ const collection = endMiddlewareGroup[i];
782
+ const metadata: Record<string, TArgumentsMetadata> =
783
+ Reflect.getOwnMetadata(argumentsKey, collection.class, collection.funcName) || {};
784
+
785
+ if (metadata) {
786
+ for (const [_key, argsMetadata] of Object.entries(metadata)) {
787
+ switch (argsMetadata.type) {
788
+ case requestArgsKey:
789
+ args[argsMetadata.index] = !argsMetadata.zodSchema
790
+ ? request
791
+ : await argumentsResolution(
792
+ request,
793
+ argsMetadata.zodSchema,
794
+ argsMetadata.index,
795
+ collection.funcName
796
+ );
797
+ break;
798
+ case requestBodyArgsKey:
799
+ args[argsMetadata.index] = !argsMetadata.zodSchema
800
+ ? await request[argsMetadata.parser || "json"]()
801
+ : await argumentsResolution(
802
+ await request[argsMetadata.parser || "json"](),
803
+ argsMetadata.zodSchema,
804
+ argsMetadata.index,
805
+ collection.funcName
806
+ );
807
+ break;
808
+ case contextArgsKey:
809
+ args[argsMetadata.index] = !argsMetadata.key ? contextHook : contextHook.get(argsMetadata.key);
810
+ break;
811
+ case requestHeadersArgsKey:
812
+ args[argsMetadata.index] = !argsMetadata.zodSchema
813
+ ? request.headers
814
+ : await argumentsResolution(
815
+ request.headers.toJSON(),
816
+ argsMetadata.zodSchema,
817
+ argsMetadata.index,
818
+ collection.funcName
819
+ );
820
+ break;
821
+ case responseHeadersArgsKey:
822
+ args[argsMetadata.index] = context[argsMetadata.type];
823
+ break;
824
+ case requestHeaderArgsKey:
825
+ args[argsMetadata.index] = !argsMetadata.zodSchema
826
+ ? request.headers.get(argsMetadata.key) || undefined
827
+ : await argumentsResolution(
828
+ request.headers.get(argsMetadata.key) || undefined,
829
+ argsMetadata.zodSchema,
830
+ argsMetadata.index,
831
+ collection.funcName
832
+ );
833
+ break;
834
+ case routeModelArgsKey:
835
+ break;
836
+ default:
837
+ args[argsMetadata.index] = !argsMetadata.zodSchema
838
+ ? !(argsMetadata.type in context)
839
+ ? undefined
840
+ : context[argsMetadata.type]
841
+ : await argumentsResolution(
842
+ !(argsMetadata.type in context) ? undefined : context[argsMetadata.type],
843
+ argsMetadata.zodSchema,
844
+ argsMetadata.index,
845
+ collection.funcName
846
+ );
847
+ break;
848
+ }
849
+ }
850
+ }
851
+
852
+ context[responseBodyArgsKey] = await collection.func(...args);
853
+ }
854
+
855
+ if (context[responseBodyArgsKey] instanceof Response) {
856
+ return responseConverter(context[responseBodyArgsKey]);
857
+ }
858
+
859
+ return responseConverter(
860
+ new Response(
861
+ !context[responseBodyArgsKey]
862
+ ? undefined
863
+ : JSON.stringify({
864
+ httpCode: 200,
865
+ message: "SUCCESS",
866
+ data: context[responseBodyArgsKey]
867
+ }),
868
+ {
869
+ status: !context[responseBodyArgsKey] ? 204 : 200,
870
+ statusText: "SUCCESS",
871
+ headers: context[responseHeadersArgsKey]
872
+ }
873
+ )
874
+ );
875
+ };
876
+
877
+ export const BoolFactory = async (
878
+ modules: new (...args: any[]) => unknown | Array<new (...args: any[]) => unknown>,
879
+ options: TBoolFactoryOptions
880
+ ) => {
881
+ try {
882
+ const modulesConverted = !Array.isArray(modules) ? [modules] : modules;
883
+ const { allowLogsMethods } = Object.freeze({
884
+ allowLogsMethods: options?.log?.methods
885
+ });
886
+
887
+ const moduleResolutions = await Promise.all(
888
+ modulesConverted.map((moduleConverted) => moduleResolution(moduleConverted, options))
889
+ );
890
+
891
+ const availableModuleResolutions = moduleResolutions.filter(
892
+ (moduleResolution) => typeof moduleResolution !== "undefined"
893
+ );
894
+
895
+ const prefixs = [
896
+ ...new Set(availableModuleResolutions.map((availableModuleResolution) => availableModuleResolution.prefix))
897
+ ];
898
+
899
+ if (prefixs.length !== availableModuleResolutions.length) {
900
+ throw Error(`Module prefix should be unique.`);
901
+ }
902
+
903
+ Bun.serve({
904
+ port: options.port,
905
+ fetch: async (request, server) => {
906
+ const start = performance.now();
907
+ const url = new URL(request.url);
908
+ const query = Qs.parse(url.searchParams.toString(), options.queryParser);
909
+
910
+ try {
911
+ let collection:
912
+ | undefined
913
+ | Required<{
914
+ route: NonNullable<ReturnType<RouterGroup["find"]>>;
915
+ resolution: NonNullable<Awaited<ReturnType<typeof moduleResolution>>>;
916
+ }>;
917
+
918
+ for (let i = 0; i < availableModuleResolutions.length; i++) {
919
+ const routeResult = availableModuleResolutions[i].routerGroup.find(
920
+ url.pathname,
921
+ request.method as keyof THttpMethods
922
+ );
923
+
924
+ if (routeResult) {
925
+ collection = Object.freeze({
926
+ route: routeResult,
927
+ resolution: availableModuleResolutions[i]
928
+ });
929
+ break;
930
+ }
931
+ }
932
+
933
+ if (!collection) {
934
+ return responseConverter(
935
+ new Response(
936
+ JSON.stringify({
937
+ httpCode: 404,
938
+ message: "Route not found",
939
+ data: undefined
940
+ })
941
+ )
942
+ );
943
+ }
944
+
945
+ return await fetcher(
946
+ {
947
+ request,
948
+ server
949
+ },
950
+ {
951
+ query: query,
952
+ route: collection.route,
953
+ moduleResolution: collection.resolution
954
+ },
955
+ options
956
+ );
957
+ } catch (error) {
958
+ options.debug && console.error(error);
959
+
960
+ return responseConverter(jsonErrorInfer(error));
961
+ } finally {
962
+ if (allowLogsMethods) {
963
+ const end = performance.now();
964
+ const convertedPID = `${process.pid}`.yellow;
965
+ const convertedMethod = `${request.method.yellow}`.bgBlue;
966
+ const convertedReqIp = `${
967
+ request.headers.get("x-forwarded-for") ||
968
+ request.headers.get("x-real-ip") ||
969
+ server.requestIP(request)?.address ||
970
+ "<Unknown>"
971
+ }`.yellow;
972
+ const convertedTime = `${Math.round((end - start + Number.EPSILON) * 10 ** 2) / 10 ** 2}ms`.yellow;
973
+
974
+ allowLogsMethods.includes(request.method.toUpperCase() as (typeof allowLogsMethods)[number]) &&
975
+ console.info(
976
+ `PID: ${convertedPID} - Method: ${convertedMethod} - IP: ${convertedReqIp} - ${
977
+ new URL(request.url).pathname.blue
978
+ } - Time: ${convertedTime}`
979
+ );
980
+ }
981
+ }
982
+ }
983
+ });
984
+ } catch (error) {
985
+ options.debug && console.error(error);
986
+ throw error;
987
+ }
988
+ };
989
+
990
+ export default BoolFactory;