turbo-express-js 1.0.10 → 1.0.12
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.
- package/README.md +6 -6
- package/changelog.md +5 -1
- package/index.d.ts +83 -84
- package/index.test.js +5 -2
- package/lib/middlewares/cors_middleware.js +31 -7
- package/lib/services/FormDataService.js +104 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
> Made with ❤️ by Mike Karypidis
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
---
|
|
11
11
|
|
|
12
12
|
## ✨ Features
|
|
13
13
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
---
|
|
25
25
|
|
|
26
26
|
## 🔧 Installation
|
|
27
27
|
|
|
@@ -31,7 +31,7 @@ To install TurboServer, simply run the following command:
|
|
|
31
31
|
npm install turbo-express-js
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
---
|
|
35
35
|
|
|
36
36
|
## 👌 Getting Started
|
|
37
37
|
|
|
@@ -52,7 +52,7 @@ Here's a simple example of how to create a basic server using TurboServer:
|
|
|
52
52
|
});
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
---
|
|
56
56
|
|
|
57
57
|
# 🔬 Comparison: Express.js vs Turbo Express JS
|
|
58
58
|
|
|
@@ -69,7 +69,7 @@ Here's a simple example of how to create a basic server using TurboServer:
|
|
|
69
69
|
| Locales (i18n) | ❌ | ✅ |
|
|
70
70
|
| Performance Focus | ✅ | ✅ |
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
---
|
|
73
73
|
|
|
74
74
|
## 📦 TypeScript Support
|
|
75
75
|
|
|
@@ -103,7 +103,7 @@ app.listen(3000, () => {
|
|
|
103
103
|
});
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
---
|
|
107
107
|
|
|
108
108
|
# Clustering
|
|
109
109
|
|
package/changelog.md
CHANGED
package/index.d.ts
CHANGED
|
@@ -1,9 +1,52 @@
|
|
|
1
|
-
// index.d.ts
|
|
2
1
|
/// <reference types="node" />
|
|
3
2
|
|
|
4
3
|
import { IncomingMessage, ServerResponse } from "http";
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
// Auto-imported types for req, res in route callbacks
|
|
6
|
+
export interface RequestInterface {
|
|
7
|
+
IncomingMessage: import("http").IncomingMessage;
|
|
8
|
+
content_type: string;
|
|
9
|
+
current_locale: string;
|
|
10
|
+
ValidationError: any;
|
|
11
|
+
bodyFetched: boolean;
|
|
12
|
+
|
|
13
|
+
params(): Map<string, any>;
|
|
14
|
+
paramsObj(): Record<string, any>;
|
|
15
|
+
queries(): URLSearchParams;
|
|
16
|
+
queriesObj(): Record<string, any>;
|
|
17
|
+
headers(): Record<string, string | string[] | undefined>;
|
|
18
|
+
method(): string;
|
|
19
|
+
url(): string;
|
|
20
|
+
httpVersion(): string;
|
|
21
|
+
remoteAddress(): string;
|
|
22
|
+
remotePort(): number;
|
|
23
|
+
localAddress(): string;
|
|
24
|
+
localPort(): number;
|
|
25
|
+
|
|
26
|
+
body(): Promise<any>;
|
|
27
|
+
body_hash(): any;
|
|
28
|
+
files(): any[];
|
|
29
|
+
|
|
30
|
+
setState(key: string, value: any): void;
|
|
31
|
+
useState(key: string): any;
|
|
32
|
+
getStateObj(): Record<string, any>;
|
|
33
|
+
|
|
34
|
+
getText(key: string): string | null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ResponseInterface {
|
|
38
|
+
OutgoingMessage: import("http").ServerResponse;
|
|
39
|
+
|
|
40
|
+
status(code: number): this;
|
|
41
|
+
send(body: string, contentType?: string): void;
|
|
42
|
+
json(data: object): void;
|
|
43
|
+
header(key: string, value: string): void;
|
|
44
|
+
}
|
|
45
|
+
export type TurboExpressCallback = (
|
|
46
|
+
req: RequestInterface,
|
|
47
|
+
res: ResponseInterface,
|
|
48
|
+
next?: Function
|
|
49
|
+
) => void;
|
|
7
50
|
|
|
8
51
|
declare class TurboServer {
|
|
9
52
|
constructor(clusterslength?: number);
|
|
@@ -28,26 +71,26 @@ declare class TurboServer {
|
|
|
28
71
|
|
|
29
72
|
Request: typeof RequestInterface;
|
|
30
73
|
Response: typeof ResponseInterface;
|
|
31
|
-
|
|
32
|
-
use(path: string, ...callbacks:
|
|
33
|
-
useEndMiddleware(path: string, ...callbacks:
|
|
34
|
-
|
|
35
|
-
get(path: string, ...callbacks:
|
|
36
|
-
post(path: string, ...callbacks:
|
|
37
|
-
put(path: string, ...callbacks:
|
|
38
|
-
delete(path: string, ...callbacks:
|
|
39
|
-
head(path: string, ...callbacks:
|
|
40
|
-
patch(path: string, ...callbacks:
|
|
41
|
-
options(path: string, ...callbacks:
|
|
42
|
-
link(path: string, ...callbacks:
|
|
43
|
-
unlink(path: string, ...callbacks:
|
|
44
|
-
view(path: string, ...callbacks:
|
|
45
|
-
lock(path: string, ...callbacks:
|
|
46
|
-
unlock(path: string, ...callbacks:
|
|
47
|
-
copy(path: string, ...callbacks:
|
|
48
|
-
purge(path: string, ...callbacks:
|
|
49
|
-
propfind(path: string, ...callbacks:
|
|
50
|
-
all(path: string, ...callbacks:
|
|
74
|
+
|
|
75
|
+
use(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
76
|
+
useEndMiddleware(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
77
|
+
|
|
78
|
+
get(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
79
|
+
post(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
80
|
+
put(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
81
|
+
delete(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
82
|
+
head(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
83
|
+
patch(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
84
|
+
options(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
85
|
+
link(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
86
|
+
unlink(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
87
|
+
view(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
88
|
+
lock(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
89
|
+
unlock(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
90
|
+
copy(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
91
|
+
purge(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
92
|
+
propfind(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
93
|
+
all(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
51
94
|
|
|
52
95
|
getServer(): import("http").Server;
|
|
53
96
|
setLocales(localeDirectory: string): void;
|
|
@@ -57,68 +100,24 @@ declare class TurboServer {
|
|
|
57
100
|
|
|
58
101
|
declare class Router {
|
|
59
102
|
constructor(path?: string);
|
|
60
|
-
|
|
61
|
-
get(path: string, ...callbacks: Function[]): void;
|
|
62
|
-
post(path: string, ...callbacks: Function[]): void;
|
|
63
|
-
put(path: string, ...callbacks: Function[]): void;
|
|
64
|
-
delete(path: string, ...callbacks: Function[]): void;
|
|
65
|
-
head(path: string, ...callbacks: Function[]): void;
|
|
66
|
-
patch(path: string, ...callbacks: Function[]): void;
|
|
67
|
-
options(path: string, ...callbacks: Function[]): void;
|
|
68
|
-
link(path: string, ...callbacks: Function[]): void;
|
|
69
|
-
unlink(path: string, ...callbacks: Function[]): void;
|
|
70
|
-
view(path: string, ...callbacks: Function[]): void;
|
|
71
|
-
lock(path: string, ...callbacks: Function[]): void;
|
|
72
|
-
unlock(path: string, ...callbacks: Function[]): void;
|
|
73
|
-
copy(path: string, ...callbacks: Function[]): void;
|
|
74
|
-
purge(path: string, ...callbacks: Function[]): void;
|
|
75
|
-
propfind(path: string, ...callbacks: Function[]): void;
|
|
76
|
-
|
|
77
|
-
use(path: string, ...callbacks: Function[]): void;
|
|
78
|
-
}
|
|
79
103
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
method(): string;
|
|
98
|
-
url(): string;
|
|
99
|
-
httpVersion(): string;
|
|
100
|
-
remoteAddress(): string;
|
|
101
|
-
remotePort(): number;
|
|
102
|
-
localAddress(): string;
|
|
103
|
-
localPort(): number;
|
|
104
|
-
|
|
105
|
-
body(): Promise<any>;
|
|
106
|
-
body_hash(): any;
|
|
107
|
-
files(): any[];
|
|
108
|
-
|
|
109
|
-
setState(key: string, value: any): void;
|
|
110
|
-
useState(key: string): any;
|
|
111
|
-
getStateObj(): Record<string, any>;
|
|
112
|
-
|
|
113
|
-
getText(key: string): string | null;
|
|
104
|
+
get(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
105
|
+
post(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
106
|
+
put(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
107
|
+
delete(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
108
|
+
head(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
109
|
+
patch(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
110
|
+
options(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
111
|
+
link(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
112
|
+
unlink(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
113
|
+
view(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
114
|
+
lock(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
115
|
+
unlock(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
116
|
+
copy(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
117
|
+
purge(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
118
|
+
propfind(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
119
|
+
|
|
120
|
+
use(path: string, ...callbacks: TurboExpressCallback[]): void;
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
|
|
117
|
-
OutgoingMessage: ServerResponse;
|
|
118
|
-
models: Map<string, any>;
|
|
119
|
-
|
|
120
|
-
status(code: number): this;
|
|
121
|
-
send(body: string, contentType?: string): void;
|
|
122
|
-
json(data: object): void;
|
|
123
|
-
header(key: string, value: string): void;
|
|
124
|
-
}
|
|
123
|
+
export = TurboServer;
|
package/index.test.js
CHANGED
|
@@ -6,6 +6,9 @@ const app = new TurboExpress();
|
|
|
6
6
|
|
|
7
7
|
// console.log("testing development...")
|
|
8
8
|
|
|
9
|
-
app.get("
|
|
9
|
+
app.get("/", ()=>{ })
|
|
10
|
+
app.post("/test", TurboExpress.Cors({ origins: ["*"] }), async (req, res) => {
|
|
11
|
+
res.json({ query: req.queriesObj(), params: req.paramsObj(), body: await req.body() })
|
|
12
|
+
})
|
|
10
13
|
|
|
11
|
-
app.listen(
|
|
14
|
+
app.listen(3000, () => console.log("Service is running"));
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @PerfecTEvolutioN
|
|
5
5
|
* @project - TurboExpress
|
|
6
6
|
* @name TurboExpress
|
|
7
|
-
* @namespace TurboExpress::middlewares::
|
|
7
|
+
* @namespace TurboExpress::middlewares::cors_middleware
|
|
8
8
|
* @license - MIT
|
|
9
9
|
* @author - Mike Karypidis
|
|
10
10
|
* @version - 1.0.0
|
|
@@ -23,14 +23,34 @@ const ResponseInterface = require("../types/ResponseInterface");
|
|
|
23
23
|
* @param {ResponseInterface} res
|
|
24
24
|
* @param {*} next
|
|
25
25
|
*/
|
|
26
|
-
module.exports = function cors_middleware
|
|
26
|
+
module.exports = function cors_middleware(req, res, next)
|
|
27
27
|
{
|
|
28
|
-
const
|
|
28
|
+
const config = this || {};
|
|
29
|
+
const origins = Array.isArray(config.origins) && config.origins.length ? config.origins : ["*"];
|
|
30
|
+
const methods = Array.isArray(config.methods) && config.methods.length ? config.methods : ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"];
|
|
31
|
+
const headers = Array.isArray(config.headers) ? config.headers : [];
|
|
32
|
+
const credentials = config.credentials === true;
|
|
33
|
+
const maxAge = Number.isSafeInteger(config.maxAge) ? config.maxAge : 3600;
|
|
34
|
+
|
|
35
|
+
const requestHeaders = req.headers();
|
|
36
|
+
const requestOrigin = requestHeaders.origin;
|
|
37
|
+
const requestedHeaders = requestHeaders["access-control-request-headers"];
|
|
38
|
+
const requestMethod = String(req.IncomingMessage?.method || "GET").toUpperCase();
|
|
39
|
+
const wildcard = origins.includes("*");
|
|
40
|
+
|
|
41
|
+
if(requestOrigin && !wildcard && !origins.includes(requestOrigin)) return next();
|
|
42
|
+
|
|
43
|
+
if(wildcard && !credentials) {
|
|
44
|
+
res.header("Access-Control-Allow-Origin", "*");
|
|
45
|
+
} else if(requestOrigin) {
|
|
46
|
+
res.header("Access-Control-Allow-Origin", requestOrigin);
|
|
47
|
+
res.header("Vary", "Origin");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if(credentials) res.header("Access-Control-Allow-Credentials", "true");
|
|
29
51
|
|
|
30
|
-
res.header("Access-Control-Allow-Origin", origins.join(","));
|
|
31
|
-
res.header("Access-Control-Allow-Credentials", credentials);
|
|
32
52
|
res.header("Access-Control-Allow-Methods", methods.join(","));
|
|
33
|
-
res.header("Access-Control-Max-Age",
|
|
53
|
+
res.header("Access-Control-Max-Age", String(maxAge));
|
|
34
54
|
|
|
35
55
|
let allowedHeaders = "";
|
|
36
56
|
const headers_req = req.headers();
|
|
@@ -45,6 +65,10 @@ module.exports = function cors_middleware (req, res, next)
|
|
|
45
65
|
0, (allowedHeaders + headers.join(",")).length - 1
|
|
46
66
|
));
|
|
47
67
|
|
|
48
|
-
|
|
68
|
+
if(requestMethod === "OPTIONS") {
|
|
69
|
+
res.status(204);
|
|
70
|
+
return res.send("");
|
|
71
|
+
}
|
|
49
72
|
|
|
73
|
+
return next();
|
|
50
74
|
}
|
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
const multiparty = require("multiparty")
|
|
20
20
|
const FileType = require("../types/FileType");
|
|
21
21
|
|
|
22
|
+
const INTEGER_PATTERN = /^-?(?:0|[1-9]\d*)$/;
|
|
23
|
+
const DECIMAL_PATTERN = /^-?(?:0|[1-9]\d*)\.\d+(?:[eE][+-]?\d+)?$/;
|
|
24
|
+
const SCIENTIFIC_PATTERN = /^-?(?:0|[1-9]\d*)(?:\.\d+)?[eE][+-]?\d+$/;
|
|
25
|
+
const MAX_JSON_FIELD_LENGTH = 1024 * 1024; // 1 MB
|
|
22
26
|
|
|
23
27
|
module.exports = class FormDataService
|
|
24
28
|
{
|
|
@@ -71,16 +75,110 @@ module.exports = class FormDataService
|
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
|
|
74
|
-
#getValueByType
|
|
78
|
+
#getValueByType(value)
|
|
75
79
|
{
|
|
76
|
-
|
|
77
|
-
if
|
|
78
|
-
if(value
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
// Multiparty normally returns strings, but do not destroy
|
|
81
|
+
// unexpected values if its behavior changes in the future.
|
|
82
|
+
if (typeof value !== "string") {
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Preserve empty strings and whitespace exactly.
|
|
87
|
+
if (value === "" || value.trim() === "") {
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Do not trim before conversion. Whitespace may be meaningful.
|
|
92
|
+
if (value !== value.trim()) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const normalized = value.toLowerCase();
|
|
97
|
+
|
|
98
|
+
switch (normalized) {
|
|
99
|
+
case "true":
|
|
100
|
+
return true;
|
|
101
|
+
|
|
102
|
+
case "false":
|
|
103
|
+
return false;
|
|
104
|
+
|
|
105
|
+
case "null":
|
|
106
|
+
case "nil":
|
|
107
|
+
return null;
|
|
108
|
+
|
|
109
|
+
default:
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Strict integer conversion.
|
|
114
|
+
if (INTEGER_PATTERN.test(value)) {
|
|
115
|
+
const parsed = Number(value);
|
|
116
|
+
|
|
117
|
+
// Prevent database identifiers and large integers from being corrupted.
|
|
118
|
+
if (!Number.isSafeInteger(parsed)) {
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// "-0" has special behavior in JavaScript; preserve it as entered.
|
|
123
|
+
if (Object.is(parsed, -0)) {
|
|
124
|
+
return value;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return parsed;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Strict decimal and scientific-notation conversion.
|
|
131
|
+
if (
|
|
132
|
+
DECIMAL_PATTERN.test(value) ||
|
|
133
|
+
SCIENTIFIC_PATTERN.test(value)
|
|
134
|
+
) {
|
|
135
|
+
const parsed = Number(value);
|
|
136
|
+
|
|
137
|
+
if (!Number.isFinite(parsed)) {
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Avoid converting high-precision decimal values that Number
|
|
142
|
+
// cannot represent reliably.
|
|
143
|
+
if (this.#countSignificantDigits(value) > 15) {
|
|
144
|
+
return value;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Parse explicitly structured JSON values.
|
|
151
|
+
if (
|
|
152
|
+
value.length <= MAX_JSON_FIELD_LENGTH &&
|
|
153
|
+
(
|
|
154
|
+
(value.startsWith("{") && value.endsWith("}")) ||
|
|
155
|
+
(value.startsWith("[") && value.endsWith("]"))
|
|
156
|
+
)
|
|
157
|
+
) {
|
|
158
|
+
try {
|
|
159
|
+
return JSON.parse(value);
|
|
160
|
+
} catch {
|
|
161
|
+
// Invalid JSON remains the original string.
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Dates, UUIDs, AFMs, phone numbers, postal codes,
|
|
167
|
+
// hexadecimal values and ordinary text remain strings.
|
|
81
168
|
return value;
|
|
82
169
|
}
|
|
83
170
|
|
|
171
|
+
#countSignificantDigits(value)
|
|
172
|
+
{
|
|
173
|
+
const coefficient = value
|
|
174
|
+
.replace(/^[+-]/, "")
|
|
175
|
+
.split(/[eE]/, 1)[0]
|
|
176
|
+
.replace(".", "")
|
|
177
|
+
.replace(/^0+/, "");
|
|
178
|
+
|
|
179
|
+
return coefficient.length;
|
|
180
|
+
}
|
|
181
|
+
|
|
84
182
|
}
|
|
85
183
|
|
|
86
184
|
|