mikroserve 0.0.2 → 0.0.4
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 +178 -1
- package/lib/MikroServe.d.mts +21 -41
- package/lib/MikroServe.d.ts +21 -41
- package/lib/MikroServe.js +102 -78
- package/lib/MikroServe.mjs +3 -1
- package/lib/{chunk-GGGGATKH.mjs → chunk-6UXWR6LD.mjs} +81 -78
- package/lib/chunk-CQPU7577.mjs +9 -0
- package/lib/chunk-RP67R3W4.mjs +43 -0
- package/lib/index.d.mts +2 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +118 -78
- package/lib/index.mjs +19 -1
- package/lib/interfaces/index.d.mts +71 -207
- package/lib/interfaces/index.d.ts +71 -207
- package/lib/utils/configDefaults.d.mts +20 -0
- package/lib/utils/configDefaults.d.ts +20 -0
- package/lib/utils/configDefaults.js +72 -0
- package/lib/utils/configDefaults.mjs +9 -0
- package/lib/utils/getTruthyEnvValue.d.mts +6 -0
- package/lib/utils/getTruthyEnvValue.d.ts +6 -0
- package/lib/utils/getTruthyEnvValue.js +33 -0
- package/lib/utils/getTruthyEnvValue.mjs +6 -0
- package/package.json +5 -3
|
@@ -4,97 +4,121 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
Router
|
|
6
6
|
} from "./chunk-KJT4SET2.mjs";
|
|
7
|
+
import {
|
|
8
|
+
configDefaults
|
|
9
|
+
} from "./chunk-RP67R3W4.mjs";
|
|
7
10
|
|
|
8
11
|
// src/MikroServe.ts
|
|
9
12
|
import { readFileSync } from "node:fs";
|
|
10
13
|
import http from "node:http";
|
|
11
14
|
import https from "node:https";
|
|
15
|
+
import { MikroConf, parsers } from "mikroconf";
|
|
12
16
|
var MikroServe = class {
|
|
13
|
-
config;
|
|
14
17
|
rateLimiter;
|
|
15
18
|
router;
|
|
16
|
-
|
|
17
|
-
sslCert;
|
|
18
|
-
sslKey;
|
|
19
|
-
sslCa;
|
|
20
|
-
debug;
|
|
19
|
+
config;
|
|
21
20
|
/**
|
|
22
21
|
* @description Creates a new MikroServe instance.
|
|
23
|
-
* @param config - Server configuration options
|
|
24
22
|
*/
|
|
25
|
-
constructor(
|
|
23
|
+
constructor(options) {
|
|
24
|
+
const defaults = configDefaults();
|
|
25
|
+
const config = new MikroConf({
|
|
26
|
+
configFilePath: "mikroserve.config.json",
|
|
27
|
+
args: process.argv,
|
|
28
|
+
options: [
|
|
29
|
+
{ flag: "--port", path: "port", defaultValue: defaults.port },
|
|
30
|
+
{ flag: "--host", path: "host", defaultValue: defaults.host },
|
|
31
|
+
{ flag: "--https", path: "useHttps", defaultValue: defaults.useHttps, isFlag: true },
|
|
32
|
+
{ flag: "--cert", path: "sslCert", defaultValue: defaults.sslCert },
|
|
33
|
+
{ flag: "--key", path: "sslKey", defaultValue: defaults.sslKey },
|
|
34
|
+
{ flag: "--ca", path: "sslCa", defaultValue: defaults.sslCa },
|
|
35
|
+
{
|
|
36
|
+
flag: "--ratelimit",
|
|
37
|
+
path: "rateLimit.enabled",
|
|
38
|
+
defaultValue: defaults.rateLimit.enabled,
|
|
39
|
+
isFlag: true
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
flag: "--rps",
|
|
43
|
+
path: "rateLimit.requestsPerMinute",
|
|
44
|
+
defaultValue: defaults.rateLimit.requestsPerMinute
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
flag: "--allowed",
|
|
48
|
+
path: "allowedDomains",
|
|
49
|
+
defaultValue: defaults.allowedDomains,
|
|
50
|
+
parser: parsers.array
|
|
51
|
+
},
|
|
52
|
+
{ flag: "--debug", path: "debug", defaultValue: defaults.debug, isFlag: true }
|
|
53
|
+
],
|
|
54
|
+
config: options
|
|
55
|
+
}).get();
|
|
56
|
+
if (config.debug) console.log("Using configuration:", config);
|
|
26
57
|
this.config = config;
|
|
27
|
-
const { useHttps, sslCa, sslCert, sslKey, debug } = config;
|
|
28
|
-
const requestsPerMinute = config.rateLimit?.requestsPerMinute || 50;
|
|
29
|
-
this.rateLimiter = new RateLimiter(requestsPerMinute, 60);
|
|
30
58
|
this.router = new Router();
|
|
31
|
-
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
this.sslCa = sslCa;
|
|
35
|
-
this.debug = debug || false;
|
|
36
|
-
if (config.rateLimit?.enabled !== false) this.use(this.rateLimitMiddleware.bind(this));
|
|
59
|
+
const requestsPerMinute = config.rateLimit.requestsPerMinute || defaults.rateLimit.requestsPerMinute;
|
|
60
|
+
this.rateLimiter = new RateLimiter(requestsPerMinute, 60);
|
|
61
|
+
if (config.rateLimit.enabled === true) this.use(this.rateLimitMiddleware.bind(this));
|
|
37
62
|
}
|
|
38
63
|
/**
|
|
39
|
-
* Register a global middleware
|
|
64
|
+
* @description Register a global middleware.
|
|
40
65
|
*/
|
|
41
66
|
use(middleware) {
|
|
42
67
|
this.router.use(middleware);
|
|
43
68
|
return this;
|
|
44
69
|
}
|
|
45
70
|
/**
|
|
46
|
-
* Register a GET route
|
|
71
|
+
* @description Register a GET route.
|
|
47
72
|
*/
|
|
48
73
|
get(path, ...handlers) {
|
|
49
74
|
this.router.get(path, ...handlers);
|
|
50
75
|
return this;
|
|
51
76
|
}
|
|
52
77
|
/**
|
|
53
|
-
* Register a POST route
|
|
78
|
+
* @description Register a POST route.
|
|
54
79
|
*/
|
|
55
80
|
post(path, ...handlers) {
|
|
56
81
|
this.router.post(path, ...handlers);
|
|
57
82
|
return this;
|
|
58
83
|
}
|
|
59
84
|
/**
|
|
60
|
-
* Register a PUT route
|
|
85
|
+
* @description Register a PUT route.
|
|
61
86
|
*/
|
|
62
87
|
put(path, ...handlers) {
|
|
63
88
|
this.router.put(path, ...handlers);
|
|
64
89
|
return this;
|
|
65
90
|
}
|
|
66
91
|
/**
|
|
67
|
-
* Register a DELETE route
|
|
92
|
+
* @description Register a DELETE route.
|
|
68
93
|
*/
|
|
69
94
|
delete(path, ...handlers) {
|
|
70
95
|
this.router.delete(path, ...handlers);
|
|
71
96
|
return this;
|
|
72
97
|
}
|
|
73
98
|
/**
|
|
74
|
-
* Register a PATCH route
|
|
99
|
+
* @description Register a PATCH route.
|
|
75
100
|
*/
|
|
76
101
|
patch(path, ...handlers) {
|
|
77
102
|
this.router.patch(path, ...handlers);
|
|
78
103
|
return this;
|
|
79
104
|
}
|
|
80
105
|
/**
|
|
81
|
-
* Register an OPTIONS route
|
|
106
|
+
* @description Register an OPTIONS route.
|
|
82
107
|
*/
|
|
83
108
|
options(path, ...handlers) {
|
|
84
109
|
this.router.options(path, ...handlers);
|
|
85
110
|
return this;
|
|
86
111
|
}
|
|
87
112
|
/**
|
|
88
|
-
* Creates an HTTP/HTTPS server, sets up graceful shutdown, and starts listening.
|
|
89
|
-
* @returns Running HTTP/HTTPS server
|
|
113
|
+
* @description Creates an HTTP/HTTPS server, sets up graceful shutdown, and starts listening.
|
|
90
114
|
*/
|
|
91
115
|
start() {
|
|
92
116
|
const server = this.createServer();
|
|
93
|
-
const { port
|
|
117
|
+
const { port, host } = this.config;
|
|
94
118
|
this.setupGracefulShutdown(server);
|
|
95
119
|
server.listen(port, host, () => {
|
|
96
120
|
const address = server.address();
|
|
97
|
-
const protocol = this.useHttps ? "https" : "http";
|
|
121
|
+
const protocol = this.config.useHttps ? "https" : "http";
|
|
98
122
|
console.log(
|
|
99
123
|
`MikroServe running at ${protocol}://${address.address !== "::" ? address.address : "localhost"}:${address.port}`
|
|
100
124
|
);
|
|
@@ -102,19 +126,18 @@ var MikroServe = class {
|
|
|
102
126
|
return server;
|
|
103
127
|
}
|
|
104
128
|
/**
|
|
105
|
-
* Creates and configures a server instance without starting it.
|
|
106
|
-
* @returns Configured HTTP or HTTPS server instance
|
|
129
|
+
* @description Creates and configures a server instance without starting it.
|
|
107
130
|
*/
|
|
108
131
|
createServer() {
|
|
109
132
|
const boundRequestHandler = this.requestHandler.bind(this);
|
|
110
|
-
if (this.useHttps) {
|
|
111
|
-
if (!this.sslCert || !this.sslKey)
|
|
133
|
+
if (this.config.useHttps) {
|
|
134
|
+
if (!this.config.sslCert || !this.config.sslKey)
|
|
112
135
|
throw new Error("SSL certificate and key paths are required when useHttps is true");
|
|
113
136
|
try {
|
|
114
137
|
const httpsOptions = {
|
|
115
|
-
key: readFileSync(this.sslKey),
|
|
116
|
-
cert: readFileSync(this.sslCert),
|
|
117
|
-
...this.sslCa ? { ca: readFileSync(this.sslCa) } : {}
|
|
138
|
+
key: readFileSync(this.config.sslKey),
|
|
139
|
+
cert: readFileSync(this.config.sslCert),
|
|
140
|
+
...this.config.sslCa ? { ca: readFileSync(this.config.sslCa) } : {}
|
|
118
141
|
};
|
|
119
142
|
return https.createServer(httpsOptions, boundRequestHandler);
|
|
120
143
|
} catch (error) {
|
|
@@ -126,7 +149,7 @@ var MikroServe = class {
|
|
|
126
149
|
return http.createServer(boundRequestHandler);
|
|
127
150
|
}
|
|
128
151
|
/**
|
|
129
|
-
* Rate limiting middleware
|
|
152
|
+
* @description Rate limiting middleware.
|
|
130
153
|
*/
|
|
131
154
|
async rateLimitMiddleware(context, next) {
|
|
132
155
|
const ip = context.req.socket.remoteAddress || "unknown";
|
|
@@ -149,16 +172,17 @@ var MikroServe = class {
|
|
|
149
172
|
return next();
|
|
150
173
|
}
|
|
151
174
|
/**
|
|
152
|
-
* Request handler for HTTP and HTTPS servers.
|
|
175
|
+
* @description Request handler for HTTP and HTTPS servers.
|
|
153
176
|
*/
|
|
154
177
|
async requestHandler(req, res) {
|
|
155
178
|
const start = Date.now();
|
|
156
179
|
const method = req.method || "UNKNOWN";
|
|
157
180
|
const url = req.url || "/unknown";
|
|
181
|
+
const isDebug = this.config.debug;
|
|
158
182
|
try {
|
|
159
183
|
this.setCorsHeaders(res, req);
|
|
160
|
-
this.setSecurityHeaders(res, this.useHttps);
|
|
161
|
-
if (
|
|
184
|
+
this.setSecurityHeaders(res, this.config.useHttps);
|
|
185
|
+
if (isDebug) console.log(`${method} ${url}`);
|
|
162
186
|
if (req.method === "OPTIONS") {
|
|
163
187
|
res.statusCode = 204;
|
|
164
188
|
res.end();
|
|
@@ -167,9 +191,7 @@ var MikroServe = class {
|
|
|
167
191
|
try {
|
|
168
192
|
req.body = await this.parseBody(req);
|
|
169
193
|
} catch (error) {
|
|
170
|
-
if (
|
|
171
|
-
console.error("Body parsing error:", error.message);
|
|
172
|
-
}
|
|
194
|
+
if (isDebug) console.error("Body parsing error:", error.message);
|
|
173
195
|
return this.respond(res, {
|
|
174
196
|
statusCode: 400,
|
|
175
197
|
body: {
|
|
@@ -193,33 +215,22 @@ var MikroServe = class {
|
|
|
193
215
|
statusCode: 500,
|
|
194
216
|
body: {
|
|
195
217
|
error: "Internal Server Error",
|
|
196
|
-
message:
|
|
218
|
+
message: isDebug ? error.message : "An unexpected error occurred"
|
|
197
219
|
}
|
|
198
220
|
});
|
|
199
221
|
} finally {
|
|
200
|
-
if (this.
|
|
201
|
-
this.logDuration(start, method, url);
|
|
202
|
-
}
|
|
222
|
+
if (isDebug) this.logDuration(start, method, url);
|
|
203
223
|
}
|
|
204
224
|
}
|
|
205
225
|
/**
|
|
206
|
-
* Writes out a clean log to represent the duration of the request.
|
|
226
|
+
* @description Writes out a clean log to represent the duration of the request.
|
|
207
227
|
*/
|
|
208
228
|
logDuration(start, method, url) {
|
|
209
229
|
const duration = Date.now() - start;
|
|
210
230
|
console.log(`${method} ${url} completed in ${duration}ms`);
|
|
211
231
|
}
|
|
212
232
|
/**
|
|
213
|
-
* Parses the request body based on content type.
|
|
214
|
-
* @param req - HTTP request object
|
|
215
|
-
* @returns Promise resolving to the parsed body
|
|
216
|
-
* @throws Will throw if body cannot be parsed
|
|
217
|
-
*/
|
|
218
|
-
/**
|
|
219
|
-
* Parses the request body based on content type.
|
|
220
|
-
* @param req - HTTP request object
|
|
221
|
-
* @returns Promise resolving to the parsed body
|
|
222
|
-
* @throws Will throw if body cannot be parsed
|
|
233
|
+
* @description Parses the request body based on content type.
|
|
223
234
|
*/
|
|
224
235
|
async parseBody(req) {
|
|
225
236
|
return new Promise((resolve, reject) => {
|
|
@@ -227,17 +238,17 @@ var MikroServe = class {
|
|
|
227
238
|
let bodySize = 0;
|
|
228
239
|
const MAX_BODY_SIZE = 1024 * 1024;
|
|
229
240
|
let rejected = false;
|
|
241
|
+
const isDebug = this.config.debug;
|
|
230
242
|
const contentType = req.headers["content-type"] || "";
|
|
231
|
-
if (
|
|
243
|
+
if (isDebug) {
|
|
232
244
|
console.log("Content-Type:", contentType);
|
|
233
245
|
}
|
|
234
246
|
req.on("data", (chunk) => {
|
|
235
247
|
bodySize += chunk.length;
|
|
236
|
-
if (
|
|
237
|
-
console.log(`Received chunk: ${chunk.length} bytes, total size: ${bodySize}`);
|
|
248
|
+
if (isDebug) console.log(`Received chunk: ${chunk.length} bytes, total size: ${bodySize}`);
|
|
238
249
|
if (bodySize > MAX_BODY_SIZE && !rejected) {
|
|
239
250
|
rejected = true;
|
|
240
|
-
if (
|
|
251
|
+
if (isDebug) console.log(`Body size exceeded limit: ${bodySize} > ${MAX_BODY_SIZE}`);
|
|
241
252
|
reject(new Error("Request body too large"));
|
|
242
253
|
return;
|
|
243
254
|
}
|
|
@@ -245,7 +256,7 @@ var MikroServe = class {
|
|
|
245
256
|
});
|
|
246
257
|
req.on("end", () => {
|
|
247
258
|
if (rejected) return;
|
|
248
|
-
if (
|
|
259
|
+
if (isDebug) console.log(`Request body complete: ${bodySize} bytes`);
|
|
249
260
|
try {
|
|
250
261
|
if (bodyChunks.length > 0) {
|
|
251
262
|
const bodyString = Buffer.concat(bodyChunks).toString("utf8");
|
|
@@ -277,17 +288,14 @@ var MikroServe = class {
|
|
|
277
288
|
});
|
|
278
289
|
}
|
|
279
290
|
/**
|
|
280
|
-
* CORS middleware
|
|
291
|
+
* @description CORS middleware.
|
|
281
292
|
*/
|
|
282
|
-
// Update the setCorsHeaders method in MikroServe class to handle allowed domains
|
|
283
293
|
setCorsHeaders(res, req) {
|
|
284
294
|
const origin = req.headers.origin;
|
|
285
295
|
const { allowedDomains = ["*"] } = this.config;
|
|
286
|
-
if (!origin || allowedDomains.length === 0)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
290
|
-
} else if (allowedDomains.includes(origin)) {
|
|
296
|
+
if (!origin || allowedDomains.length === 0) res.setHeader("Access-Control-Allow-Origin", "*");
|
|
297
|
+
else if (allowedDomains.includes("*")) res.setHeader("Access-Control-Allow-Origin", "*");
|
|
298
|
+
else if (allowedDomains.includes(origin)) {
|
|
291
299
|
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
292
300
|
res.setHeader("Vary", "Origin");
|
|
293
301
|
}
|
|
@@ -296,7 +304,7 @@ var MikroServe = class {
|
|
|
296
304
|
res.setHeader("Access-Control-Max-Age", "86400");
|
|
297
305
|
}
|
|
298
306
|
/**
|
|
299
|
-
* Set security headers
|
|
307
|
+
* @description Set security headers.
|
|
300
308
|
*/
|
|
301
309
|
setSecurityHeaders(res, isHttps = false) {
|
|
302
310
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -309,9 +317,7 @@ var MikroServe = class {
|
|
|
309
317
|
res.setHeader("X-XSS-Protection", "1; mode=block");
|
|
310
318
|
}
|
|
311
319
|
/**
|
|
312
|
-
* Sends a response with appropriate headers.
|
|
313
|
-
* @param res - HTTP response object
|
|
314
|
-
* @param response - Response data and status code
|
|
320
|
+
* @description Sends a response with appropriate headers.
|
|
315
321
|
*/
|
|
316
322
|
respond(res, response) {
|
|
317
323
|
const headers = {
|
|
@@ -323,8 +329,7 @@ var MikroServe = class {
|
|
|
323
329
|
else res.end(JSON.stringify(response.body));
|
|
324
330
|
}
|
|
325
331
|
/**
|
|
326
|
-
* Sets up graceful shutdown handlers for a server.
|
|
327
|
-
* @param server - The HTTP/HTTPS server to add shutdown handlers to
|
|
332
|
+
* @description Sets up graceful shutdown handlers for a server.
|
|
328
333
|
*/
|
|
329
334
|
setupGracefulShutdown(server) {
|
|
330
335
|
const shutdown = (error) => {
|
|
@@ -332,9 +337,7 @@ var MikroServe = class {
|
|
|
332
337
|
if (error) console.error("Error:", error);
|
|
333
338
|
server.close(() => {
|
|
334
339
|
console.log("Server closed successfully");
|
|
335
|
-
setImmediate(() =>
|
|
336
|
-
process.exit(error ? 1 : 0);
|
|
337
|
-
});
|
|
340
|
+
setImmediate(() => process.exit(error ? 1 : 0));
|
|
338
341
|
});
|
|
339
342
|
};
|
|
340
343
|
process.on("SIGINT", () => shutdown());
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTruthyEnvValue
|
|
3
|
+
} from "./chunk-CQPU7577.mjs";
|
|
4
|
+
|
|
5
|
+
// src/utils/configDefaults.ts
|
|
6
|
+
var configDefaults = () => {
|
|
7
|
+
return {
|
|
8
|
+
port: Number(process.env.PORT) || 3e3,
|
|
9
|
+
host: process.env.HOST || "0.0.0.0",
|
|
10
|
+
useHttps: false,
|
|
11
|
+
sslCert: "",
|
|
12
|
+
sslKey: "",
|
|
13
|
+
sslCa: "",
|
|
14
|
+
debug: getTruthyEnvValue(process.env.DEBUG) || false,
|
|
15
|
+
rateLimit: {
|
|
16
|
+
enabled: true,
|
|
17
|
+
requestsPerMinute: 100
|
|
18
|
+
},
|
|
19
|
+
allowedDomains: ["*"]
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
var getDefaultConfig = () => {
|
|
23
|
+
const defaults = configDefaults();
|
|
24
|
+
return {
|
|
25
|
+
port: defaults.port,
|
|
26
|
+
host: defaults.host,
|
|
27
|
+
useHttps: defaults.useHttps,
|
|
28
|
+
sslCert: defaults.sslCert,
|
|
29
|
+
sslKey: defaults.sslKey,
|
|
30
|
+
sslCa: defaults.sslCa,
|
|
31
|
+
debug: defaults.debug,
|
|
32
|
+
rateLimit: {
|
|
33
|
+
enabled: defaults.rateLimit.enabled,
|
|
34
|
+
requestsPerMinute: defaults.rateLimit.requestsPerMinute
|
|
35
|
+
},
|
|
36
|
+
allowedDomains: defaults.allowedDomains
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export {
|
|
41
|
+
configDefaults,
|
|
42
|
+
getDefaultConfig
|
|
43
|
+
};
|
package/lib/index.d.mts
CHANGED