@bool-ts/core 1.0.10 → 1.1.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.
@@ -1,8 +1,18 @@
1
1
  import "reflect-metadata";
2
2
  import "colors";
3
+ export type TBoolFactoryOptions = Partial<{
4
+ debug: boolean;
5
+ log: Partial<{
6
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
7
+ }>;
8
+ queryParser: Partial<{
9
+ depth: 10;
10
+ arrayLimit: 50;
11
+ }>;
12
+ }>;
3
13
  /**
4
14
  *
5
15
  * @param target
6
16
  */
7
- export declare const BoolFactory: (target: new (...args: any[]) => unknown) => import("express-serve-static-core").Express;
17
+ export declare const BoolFactory: (target: new (...args: any[]) => unknown, options?: TBoolFactoryOptions) => import("express-serve-static-core").Express;
8
18
  export default BoolFactory;
@@ -5,6 +5,7 @@ import * as ResponseTime from "response-time";
5
5
  import { controllerKey, controllerRoutesKey, moduleKey } from "../decorators";
6
6
  import { default as ExpressApp, Router, json, urlencoded } from "express";
7
7
  import { Injector } from "./injector";
8
+ import { errorInfer } from "../http";
8
9
  /**
9
10
  *
10
11
  * @param target
@@ -46,7 +47,7 @@ const controllerCreator = (target, router = Router()) => {
46
47
  *
47
48
  * @param target
48
49
  */
49
- export const BoolFactory = (target) => {
50
+ export const BoolFactory = (target, options) => {
50
51
  if (!Reflect.getOwnMetadataKeys(target).includes(moduleKey)) {
51
52
  throw Error(`${target.name} is not a module.`);
52
53
  }
@@ -58,19 +59,13 @@ export const BoolFactory = (target) => {
58
59
  const allowMethods = !metadata?.allowMethods ?
59
60
  ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] : metadata.allowMethods;
60
61
  const app = ExpressApp();
61
- const configs = Object.freeze({
62
- allowLogsMethods: [
63
- "GET",
64
- "POST",
65
- "PUT",
66
- "PATCH",
67
- "DELETE"
68
- ]
62
+ const factoryOptions = Object.freeze({
63
+ allowLogsMethods: !options?.log?.methods ? ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] : options.log.methods
69
64
  });
70
65
  app.set("etag", "strong");
71
66
  app.set("query parser", (query) => Qs.parse(query, {
72
- ["depth"]: 10,
73
- ["arrayLimit"]: 50
67
+ depth: !options?.queryParser?.depth || options.queryParser.depth < 0 ? 10 : options.queryParser.depth,
68
+ arrayLimit: !options?.queryParser?.arrayLimit || options.queryParser.arrayLimit < 0 ? 50 : options.queryParser.arrayLimit
74
69
  }));
75
70
  app.use(urlencoded({
76
71
  extended: true,
@@ -103,13 +98,16 @@ export const BoolFactory = (target) => {
103
98
  },
104
99
  // Error catcher
105
100
  (err, req, res, next) => {
106
- console.error(err);
107
- next();
101
+ errorInfer(res, err);
102
+ if (!options?.debug) {
103
+ return;
104
+ }
105
+ console.error("Headers:", JSON.stringify(req.headers), "\nBody:", JSON.stringify(req.body), "\nError:", JSON.stringify(err));
108
106
  },
109
107
  // Response time log
110
108
  ResponseTime.default((req, res, time) => {
111
109
  const requestMethod = req.method.toUpperCase();
112
- if (!configs.allowLogsMethods.includes(requestMethod)) {
110
+ if (!factoryOptions.allowLogsMethods.includes(requestMethod)) {
113
111
  return;
114
112
  }
115
113
  const convertedMethod = `${requestMethod.yellow}`.bgBlue;
@@ -122,8 +120,13 @@ export const BoolFactory = (target) => {
122
120
  if (!allowOrigins.includes("*")) {
123
121
  if (!allowOrigins.includes(req.headers.origin || "*")) {
124
122
  return res.status(403).json({
125
- ["httpCode"]: 403,
126
- ["data"]: "Invalid origin."
123
+ httpCode: 403,
124
+ data: {
125
+ origin: {
126
+ code: "origin:invalid:0x00001",
127
+ message: "Invalid origin."
128
+ }
129
+ }
127
130
  });
128
131
  }
129
132
  }
@@ -133,7 +136,9 @@ export const BoolFactory = (target) => {
133
136
  res.header("Access-Control-Allow-Methods", allowMethods.join(", "));
134
137
  next();
135
138
  });
136
- app.use(routers);
139
+ if (routers.length > 0) {
140
+ app.use(routers);
141
+ }
137
142
  return app;
138
143
  };
139
144
  export default BoolFactory;
@@ -0,0 +1,40 @@
1
+ export declare const httpClientErrors: Readonly<{
2
+ 400: "BAD_REQUEST";
3
+ 401: "UNAUTHORIZED";
4
+ 402: "PAYMENT_REQUIRED";
5
+ 403: "FORBIDDEN";
6
+ 404: "NOT_FOUND";
7
+ 405: "METHOD_NOT_ALLOWED";
8
+ 406: "NOT_ACCEPTABLE";
9
+ 407: "PROXY_AUTHENCATION_REQUIRED";
10
+ 408: "REQUEST_TIMEOUT";
11
+ 409: "CONFLICT";
12
+ 410: "GONE";
13
+ 411: "LENGTH_REQUIRED";
14
+ 412: "PRECONDITION_FAILED";
15
+ 413: "PAYLOAD_TOO_LARGE";
16
+ 414: "URI_TOO_LONG";
17
+ 415: "UNSUPPORTED_MEDIA_TYPE";
18
+ 416: "RANGE_NOT_SATISFIABLE";
19
+ 417: "EXPECTATION_FAILED";
20
+ 418: "IM_A_TEAPOT";
21
+ 421: "MISDIRECTED_REQUEST";
22
+ 422: "UNPROCESSABLE_ENTITY";
23
+ 423: "LOCKED";
24
+ 424: "FAILED_DEPENDENCY";
25
+ 425: "TOO_EARLY_";
26
+ 426: "UPGRAGE_REQUIRED";
27
+ 428: "PRECONDITION_REQUIRED";
28
+ 429: "TOO_MANY_REQUESTS";
29
+ 431: "REQUEST_HEADER_FIELDS_TOO_LARGE";
30
+ 451: "UNAVAILABLE_FOR_LEGAL_REASONS";
31
+ }>;
32
+ export declare class HttpClientError<T extends keyof typeof httpClientErrors = keyof typeof httpClientErrors, K = any> extends Error {
33
+ readonly httpCode: T;
34
+ readonly message: typeof httpClientErrors[T];
35
+ readonly data: K;
36
+ constructor({ httpCode, data }: {
37
+ ["httpCode"]: T;
38
+ ["data"]: K;
39
+ });
40
+ }
@@ -0,0 +1,42 @@
1
+ export const httpClientErrors = Object.freeze({
2
+ 400: "BAD_REQUEST",
3
+ 401: "UNAUTHORIZED",
4
+ 402: "PAYMENT_REQUIRED",
5
+ 403: "FORBIDDEN",
6
+ 404: "NOT_FOUND",
7
+ 405: "METHOD_NOT_ALLOWED",
8
+ 406: "NOT_ACCEPTABLE",
9
+ 407: "PROXY_AUTHENCATION_REQUIRED",
10
+ 408: "REQUEST_TIMEOUT",
11
+ 409: "CONFLICT",
12
+ 410: "GONE",
13
+ 411: "LENGTH_REQUIRED",
14
+ 412: "PRECONDITION_FAILED",
15
+ 413: "PAYLOAD_TOO_LARGE",
16
+ 414: "URI_TOO_LONG",
17
+ 415: "UNSUPPORTED_MEDIA_TYPE",
18
+ 416: "RANGE_NOT_SATISFIABLE",
19
+ 417: "EXPECTATION_FAILED",
20
+ 418: "IM_A_TEAPOT",
21
+ 421: "MISDIRECTED_REQUEST",
22
+ 422: "UNPROCESSABLE_ENTITY",
23
+ 423: "LOCKED",
24
+ 424: "FAILED_DEPENDENCY",
25
+ 425: "TOO_EARLY_",
26
+ 426: "UPGRAGE_REQUIRED",
27
+ 428: "PRECONDITION_REQUIRED",
28
+ 429: "TOO_MANY_REQUESTS",
29
+ 431: "REQUEST_HEADER_FIELDS_TOO_LARGE",
30
+ 451: "UNAVAILABLE_FOR_LEGAL_REASONS"
31
+ });
32
+ export class HttpClientError extends Error {
33
+ httpCode;
34
+ message;
35
+ data;
36
+ constructor({ httpCode, data }) {
37
+ super();
38
+ this.httpCode = httpCode;
39
+ this.message = httpClientErrors[httpCode];
40
+ this.data = data;
41
+ }
42
+ }
@@ -0,0 +1,4 @@
1
+ import { Response } from "express";
2
+ export declare const errorInfer: (res: Response, data: any) => void;
3
+ export * from "./clientError";
4
+ export * from "./serverError";
@@ -0,0 +1,58 @@
1
+ import { HttpClientError } from "./clientError";
2
+ import { HttpServerError } from "./serverError";
3
+ export const errorInfer = (res, data) => {
4
+ if (res.headersSent) {
5
+ return;
6
+ }
7
+ try {
8
+ if (data instanceof HttpClientError) {
9
+ res.status(data.httpCode).json(data);
10
+ return;
11
+ }
12
+ if (data instanceof HttpServerError) {
13
+ res.status(data.httpCode).json(data);
14
+ return;
15
+ }
16
+ if (typeof data === "object") {
17
+ res.status(500).json({
18
+ httpCode: 500,
19
+ message: "INTERNAL SERVER ERROR",
20
+ data: data
21
+ });
22
+ return;
23
+ }
24
+ switch (typeof data) {
25
+ case "string":
26
+ res.status(500).json({
27
+ httpCode: 500,
28
+ message: "INTERNAL SERVER ERROR",
29
+ data: {
30
+ message: data
31
+ }
32
+ });
33
+ return;
34
+ case "number":
35
+ res.status(500).json({
36
+ httpCode: 500,
37
+ message: "INTERNAL SERVER ERROR",
38
+ data: {
39
+ code: data
40
+ }
41
+ });
42
+ return;
43
+ default:
44
+ res.status(500).json({
45
+ httpCode: 500,
46
+ message: "INTERNAL SERVER ERROR",
47
+ data: undefined
48
+ });
49
+ return;
50
+ }
51
+ }
52
+ catch (error) {
53
+ console.error(error);
54
+ res.end();
55
+ }
56
+ };
57
+ export * from "./clientError";
58
+ export * from "./serverError";
@@ -0,0 +1,22 @@
1
+ export declare const httpServerErrors: Readonly<{
2
+ 500: "INTERNAL_SERVER_ERROR";
3
+ 501: "NOT_IMPLEMENTED";
4
+ 502: "BAD_GATEWAY";
5
+ 503: "SERVICE_UNAVAILABLE";
6
+ 504: "GATEWAY_TIMEOUT";
7
+ 505: "HTTP_VERSION_NOT_SUPPORTED";
8
+ 506: "VARIANT_ALSO_NEGOTIATES";
9
+ 507: "INSUFFICIENT_STORAGE";
10
+ 508: "LOOP_DETECTED";
11
+ 510: "NOT_EXTENDED";
12
+ 511: "NETWORK_AUTHENTICATION_REQUIRED";
13
+ }>;
14
+ export declare class HttpServerError<T extends keyof typeof httpServerErrors = keyof typeof httpServerErrors, K = any> extends Error {
15
+ readonly httpCode: T;
16
+ readonly message: typeof httpServerErrors[T];
17
+ readonly data: K;
18
+ constructor({ httpCode, data }: {
19
+ ["httpCode"]: T;
20
+ ["data"]: K;
21
+ });
22
+ }
@@ -0,0 +1,24 @@
1
+ export const httpServerErrors = Object.freeze({
2
+ 500: "INTERNAL_SERVER_ERROR",
3
+ 501: "NOT_IMPLEMENTED",
4
+ 502: "BAD_GATEWAY",
5
+ 503: "SERVICE_UNAVAILABLE",
6
+ 504: "GATEWAY_TIMEOUT",
7
+ 505: "HTTP_VERSION_NOT_SUPPORTED",
8
+ 506: "VARIANT_ALSO_NEGOTIATES",
9
+ 507: "INSUFFICIENT_STORAGE",
10
+ 508: "LOOP_DETECTED",
11
+ 510: "NOT_EXTENDED",
12
+ 511: "NETWORK_AUTHENTICATION_REQUIRED"
13
+ });
14
+ export class HttpServerError extends Error {
15
+ httpCode;
16
+ message;
17
+ data;
18
+ constructor({ httpCode, data }) {
19
+ super();
20
+ this.httpCode = httpCode;
21
+ this.message = httpServerErrors[httpCode];
22
+ this.data = data;
23
+ }
24
+ }
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ import "reflect-metadata";
2
2
  export * from "./interfaces";
3
3
  export * from "./hooks";
4
4
  export * from "./decorators";
5
+ export * from "./http";
package/dist/index.js CHANGED
@@ -2,3 +2,4 @@ import "reflect-metadata";
2
2
  export * from "./interfaces";
3
3
  export * from "./hooks";
4
4
  export * from "./decorators";
5
+ export * from "./http";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bool-ts/core",
3
- "version": "1.0.10",
3
+ "version": "1.1.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -7,6 +7,19 @@ import * as ResponseTime from "response-time";
7
7
  import { type IControllerRoute, type TModuleOptions, controllerKey, controllerRoutesKey, moduleKey } from "../decorators";
8
8
  import { default as ExpressApp, Router, json, urlencoded, Request, Response, NextFunction, Errback } from "express";
9
9
  import { Injector } from "./injector";
10
+ import { errorInfer } from "../http";
11
+
12
+
13
+ export type TBoolFactoryOptions = Partial<{
14
+ debug: boolean;
15
+ log: Partial<{
16
+ methods: Array<"GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS">;
17
+ }>;
18
+ queryParser: Partial<{
19
+ depth: 10,
20
+ arrayLimit: 50
21
+ }>;
22
+ }>;
10
23
 
11
24
 
12
25
  /**
@@ -62,7 +75,8 @@ const controllerCreator = (
62
75
  * @param target
63
76
  */
64
77
  export const BoolFactory = (
65
- target: new (...args: any[]) => unknown
78
+ target: new (...args: any[]) => unknown,
79
+ options?: TBoolFactoryOptions
66
80
  ) => {
67
81
  if (!Reflect.getOwnMetadataKeys(target).includes(moduleKey)) {
68
82
  throw Error(`${target.name} is not a module.`);
@@ -76,20 +90,14 @@ export const BoolFactory = (
76
90
  const allowMethods = !metadata?.allowMethods ?
77
91
  ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] : metadata.allowMethods;
78
92
  const app = ExpressApp();
79
- const configs = Object.freeze({
80
- allowLogsMethods: [
81
- "GET",
82
- "POST",
83
- "PUT",
84
- "PATCH",
85
- "DELETE"
86
- ]
93
+ const factoryOptions = Object.freeze({
94
+ allowLogsMethods: !options?.log?.methods ? ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"] : options.log.methods
87
95
  });
88
96
 
89
97
  app.set("etag", "strong");
90
98
  app.set("query parser", (query: string) => Qs.parse(query, {
91
- ["depth"]: 10,
92
- ["arrayLimit"]: 50
99
+ depth: !options?.queryParser?.depth || options.queryParser.depth < 0 ? 10 : options.queryParser.depth,
100
+ arrayLimit: !options?.queryParser?.arrayLimit || options.queryParser.arrayLimit < 0 ? 50 : options.queryParser.arrayLimit
93
101
  }));
94
102
 
95
103
  app.use(
@@ -127,15 +135,19 @@ export const BoolFactory = (
127
135
  },
128
136
  // Error catcher
129
137
  (err: Errback, req: Request, res: Response, next: NextFunction) => {
130
- console.error(err);
138
+ errorInfer(res, err);
131
139
 
132
- next();
140
+ if (!options?.debug) {
141
+ return;
142
+ }
143
+
144
+ console.error("Headers:", JSON.stringify(req.headers), "\nBody:", JSON.stringify(req.body), "\nError:", JSON.stringify(err));
133
145
  },
134
146
  // Response time log
135
147
  ResponseTime.default((req: Request, res: Response, time: number) => {
136
148
  const requestMethod = req.method.toUpperCase();
137
149
 
138
- if (!configs.allowLogsMethods.includes(requestMethod)) {
150
+ if (!factoryOptions.allowLogsMethods.includes(requestMethod)) {
139
151
  return;
140
152
  }
141
153
 
@@ -152,8 +164,13 @@ export const BoolFactory = (
152
164
  if (!allowOrigins.includes("*")) {
153
165
  if (!allowOrigins.includes(req.headers.origin || "*")) {
154
166
  return res.status(403).json({
155
- ["httpCode"]: 403,
156
- ["data"]: "Invalid origin."
167
+ httpCode: 403,
168
+ data: {
169
+ origin: {
170
+ code: "origin:invalid:0x00001",
171
+ message: "Invalid origin."
172
+ }
173
+ }
157
174
  });
158
175
  }
159
176
  }
@@ -166,7 +183,9 @@ export const BoolFactory = (
166
183
  next();
167
184
  });
168
185
 
169
- app.use(routers);
186
+ if (routers.length > 0) {
187
+ app.use(routers);
188
+ }
170
189
 
171
190
  return app;
172
191
  }
@@ -0,0 +1,57 @@
1
+ import * as ExpressJS from "express";
2
+
3
+
4
+ export const httpClientErrors = Object.freeze({
5
+ 400: "BAD_REQUEST",
6
+ 401: "UNAUTHORIZED",
7
+ 402: "PAYMENT_REQUIRED",
8
+ 403: "FORBIDDEN",
9
+ 404: "NOT_FOUND",
10
+ 405: "METHOD_NOT_ALLOWED",
11
+ 406: "NOT_ACCEPTABLE",
12
+ 407: "PROXY_AUTHENCATION_REQUIRED",
13
+ 408: "REQUEST_TIMEOUT",
14
+ 409: "CONFLICT",
15
+ 410: "GONE",
16
+ 411: "LENGTH_REQUIRED",
17
+ 412: "PRECONDITION_FAILED",
18
+ 413: "PAYLOAD_TOO_LARGE",
19
+ 414: "URI_TOO_LONG",
20
+ 415: "UNSUPPORTED_MEDIA_TYPE",
21
+ 416: "RANGE_NOT_SATISFIABLE",
22
+ 417: "EXPECTATION_FAILED",
23
+ 418: "IM_A_TEAPOT",
24
+ 421: "MISDIRECTED_REQUEST",
25
+ 422: "UNPROCESSABLE_ENTITY",
26
+ 423: "LOCKED",
27
+ 424: "FAILED_DEPENDENCY",
28
+ 425: "TOO_EARLY_",
29
+ 426: "UPGRAGE_REQUIRED",
30
+ 428: "PRECONDITION_REQUIRED",
31
+ 429: "TOO_MANY_REQUESTS",
32
+ 431: "REQUEST_HEADER_FIELDS_TOO_LARGE",
33
+ 451: "UNAVAILABLE_FOR_LEGAL_REASONS"
34
+ });
35
+
36
+ export class HttpClientError<
37
+ T extends keyof typeof httpClientErrors = keyof typeof httpClientErrors,
38
+ K = any
39
+ > extends Error {
40
+ public readonly httpCode: T;
41
+ public readonly message: typeof httpClientErrors[T];
42
+ public readonly data: K;
43
+
44
+ constructor({
45
+ httpCode,
46
+ data
47
+ }: {
48
+ ["httpCode"]: T;
49
+ ["data"]: K;
50
+ }) {
51
+ super();
52
+
53
+ this.httpCode = httpCode;
54
+ this.message = httpClientErrors[httpCode];
55
+ this.data = data;
56
+ }
57
+ }
@@ -0,0 +1,68 @@
1
+ import { Response } from "express";
2
+ import { HttpClientError } from "./clientError";
3
+ import { HttpServerError } from "./serverError";
4
+
5
+
6
+ export const errorInfer = (res: Response, data: any) => {
7
+ if (res.headersSent) {
8
+ return;
9
+ }
10
+
11
+ try {
12
+ if (data instanceof HttpClientError) {
13
+ res.status(data.httpCode).json(data);
14
+ return;
15
+ }
16
+
17
+ if (data instanceof HttpServerError) {
18
+ res.status(data.httpCode).json(data);
19
+ return;
20
+ }
21
+
22
+ if (typeof data === "object") {
23
+ res.status(500).json({
24
+ httpCode: 500,
25
+ message: "INTERNAL SERVER ERROR",
26
+ data: data
27
+ });
28
+ return;
29
+ }
30
+
31
+ switch (typeof data) {
32
+ case "string":
33
+ res.status(500).json({
34
+ httpCode: 500,
35
+ message: "INTERNAL SERVER ERROR",
36
+ data: {
37
+ message: data
38
+ }
39
+ });
40
+ return;
41
+
42
+ case "number":
43
+ res.status(500).json({
44
+ httpCode: 500,
45
+ message: "INTERNAL SERVER ERROR",
46
+ data: {
47
+ code: data
48
+ }
49
+ });
50
+ return;
51
+
52
+ default:
53
+ res.status(500).json({
54
+ httpCode: 500,
55
+ message: "INTERNAL SERVER ERROR",
56
+ data: undefined
57
+ });
58
+ return;
59
+ }
60
+ }
61
+ catch (error) {
62
+ console.error(error);
63
+ res.end();
64
+ }
65
+ }
66
+
67
+ export * from "./clientError";
68
+ export * from "./serverError";
@@ -0,0 +1,39 @@
1
+ import * as ExpressJS from "express";
2
+
3
+
4
+ export const httpServerErrors = Object.freeze({
5
+ 500: "INTERNAL_SERVER_ERROR",
6
+ 501: "NOT_IMPLEMENTED",
7
+ 502: "BAD_GATEWAY",
8
+ 503: "SERVICE_UNAVAILABLE",
9
+ 504: "GATEWAY_TIMEOUT",
10
+ 505: "HTTP_VERSION_NOT_SUPPORTED",
11
+ 506: "VARIANT_ALSO_NEGOTIATES",
12
+ 507: "INSUFFICIENT_STORAGE",
13
+ 508: "LOOP_DETECTED",
14
+ 510: "NOT_EXTENDED",
15
+ 511: "NETWORK_AUTHENTICATION_REQUIRED"
16
+ });
17
+
18
+ export class HttpServerError<
19
+ T extends keyof typeof httpServerErrors = keyof typeof httpServerErrors,
20
+ K = any
21
+ > extends Error {
22
+ public readonly httpCode: T;
23
+ public readonly message: typeof httpServerErrors[T];
24
+ public readonly data: K;
25
+
26
+ constructor({
27
+ httpCode,
28
+ data
29
+ }: {
30
+ ["httpCode"]: T;
31
+ ["data"]: K;
32
+ }) {
33
+ super();
34
+
35
+ this.httpCode = httpCode;
36
+ this.message = httpServerErrors[httpCode];
37
+ this.data = data;
38
+ }
39
+ }
package/src/index.ts CHANGED
@@ -3,3 +3,4 @@ import "reflect-metadata";
3
3
  export * from "./interfaces";
4
4
  export * from "./hooks";
5
5
  export * from "./decorators";
6
+ export * from "./http";