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.
@@ -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
- useHttps;
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(config) {
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
- this.useHttps = useHttps || false;
32
- this.sslCert = sslCert;
33
- this.sslKey = sslKey;
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 = 3e3, host = "localhost" } = this.config;
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 (this.debug) console.log(`${method} ${url}`);
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 (this.debug) {
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: this.debug ? error.message : "An unexpected error occurred"
218
+ message: isDebug ? error.message : "An unexpected error occurred"
197
219
  }
198
220
  });
199
221
  } finally {
200
- if (this.debug) {
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 (this.debug) {
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 (this.debug)
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 (this.debug) console.log(`Body size exceeded limit: ${bodySize} > ${MAX_BODY_SIZE}`);
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 (this.debug) console.log(`Request body complete: ${bodySize} bytes`);
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
- res.setHeader("Access-Control-Allow-Origin", "*");
288
- } else if (allowedDomains.includes("*")) {
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,9 @@
1
+ // src/utils/getTruthyEnvValue.ts
2
+ function getTruthyEnvValue(value) {
3
+ if (value === "true" || value === true) return true;
4
+ return false;
5
+ }
6
+
7
+ export {
8
+ getTruthyEnvValue
9
+ };
@@ -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
@@ -1,4 +1,5 @@
1
+ #!/usr/bin/env node
1
2
  export { MikroServe } from './MikroServe.mjs';
3
+ export { Context } from './interfaces/index.mjs';
2
4
  import 'node:http';
3
5
  import 'node:https';
4
- import './interfaces/index.mjs';
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
+ #!/usr/bin/env node
1
2
  export { MikroServe } from './MikroServe.js';
3
+ export { Context } from './interfaces/index.js';
2
4
  import 'node:http';
3
5
  import 'node:https';
4
- import './interfaces/index.js';