@ndiinginc/router 4.9.0 → 4.10.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.
package/README.md CHANGED
@@ -367,18 +367,6 @@ const server = app.listen(0); // port acak
367
367
  console.log(server.address().port);
368
368
  ```
369
369
 
370
- ## Benchmark
371
-
372
- Hello world dengan satu route `GET /`, diuji memakai `autocannon -c 100 -d 40 -p 10` di mesin lokal.
373
-
374
- | | Req/sec (avg) | Latency (avg) |
375
- | --- | --- | --- |
376
- | Fastify | 31.475 | 31,27 ms |
377
- | **@ndiinginc/router** | 29.735 | 33,12 ms |
378
- | Express | 20.211 | 48,93 ms |
379
-
380
- Hasil bervariasi tergantung mesin, versi Node.js, dan jumlah route. Benchmark ini hanya menggambarkan overhead dasar, bukan performa aplikasi nyata.
381
-
382
370
  ## Testing
383
371
 
384
372
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndiinginc/router",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
@@ -21,6 +21,7 @@
21
21
  "@ndiinginc/commit": "^1.0.1",
22
22
  "@ndiinginc/test": "^1.17.0",
23
23
  "@types/jest": "^30.0.0",
24
- "@types/node": "^26.6.5"
24
+ "@types/node": "^26.6.5",
25
+ "autocannon": "^8.0.0"
25
26
  }
26
27
  }
package/src/app.js CHANGED
@@ -45,13 +45,16 @@ class App extends Router {
45
45
  res.statusCode = 404;
46
46
  err = new Error(http.STATUS_CODES[res.statusCode]);
47
47
  }
48
+
48
49
  if (res.statusCode < 400) {
49
50
  res.statusCode = 500;
50
51
  }
52
+
51
53
  const error = { message: err.message };
52
54
  if (process.env.NODE_ENV === "development") {
53
55
  error.stack = err.stack;
54
56
  }
57
+
55
58
  res.json({ error });
56
59
  }
57
60
  }
@@ -72,6 +75,7 @@ class App extends Router {
72
75
  await handler(req, res);
73
76
  }
74
77
  };
78
+
75
79
  if (this.requestTimeout) {
76
80
  await this.withTimeout(req, res, handlerPromise);
77
81
  } else {
@@ -79,6 +83,7 @@ class App extends Router {
79
83
  }
80
84
  } catch (error) {
81
85
  err = error;
86
+
82
87
  try {
83
88
  for (const handler of errorHandlers) {
84
89
  if (res.writableEnded) break;
package/src/polyfill.js CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  // Polyfill for Map.prototype.getOrInsert
2
3
  if (!Map.prototype.getOrInsert) {
3
4
  Map.prototype.getOrInsert = function (key, defaultValue) {
package/src/request.js CHANGED
@@ -21,6 +21,7 @@ class Request extends http.IncomingMessage {
21
21
  if (!this._query) {
22
22
  this._query = parseQueryString(this._queryString);
23
23
  }
24
+
24
25
  return this._query;
25
26
  }
26
27
 
@@ -31,6 +32,7 @@ class Request extends http.IncomingMessage {
31
32
  if (!this._controller) {
32
33
  this._controller = new AbortController();
33
34
  }
35
+
34
36
  return this._controller;
35
37
  }
36
38
  }
package/src/response.js CHANGED
@@ -5,10 +5,12 @@ class Response extends http.ServerResponse {
5
5
  this.statusCode = code;
6
6
  return this;
7
7
  }
8
+
8
9
  send(chunk) {
9
10
  if (this.writableEnded) return;
10
11
  this.end(chunk, null, null);
11
12
  }
13
+
12
14
  json(value) {
13
15
  if (!this.headersSent) {
14
16
  this.setHeader("Content-Type", "application/json; charset=utf-8");
package/src/router.js CHANGED
@@ -1,14 +1,11 @@
1
1
  const { decodeParam } = require("./util");
2
2
  const { Node } = require("./node");
3
- const { Cache } = require("./cache");
4
3
 
5
4
  class Router {
6
5
  constructor(options = {}) {
7
6
  this.requestTimeout = options.requestTimeout ?? 0;
8
- this.cacheSize = options.cacheSize ?? 1000;
9
7
  this.root = new Node();
10
8
  this.seq = 0;
11
- this.cache = new Cache(this.cacheSize);
12
9
  }
13
10
 
14
11
  split(path) {
@@ -16,7 +13,6 @@ class Router {
16
13
  }
17
14
 
18
15
  add(method, path, ...handlers) {
19
- this.cache.clear();
20
16
 
21
17
  if (typeof path === "function") {
22
18
  handlers = [path, ...handlers];
@@ -178,10 +174,6 @@ class Router {
178
174
  }
179
175
 
180
176
  find(method, path) {
181
- const key = `${method} ${path}`;
182
-
183
- const cached = this.cache.get(key);
184
- if (cached) return cached;
185
177
 
186
178
  const segments = this.split(path);
187
179
 
@@ -208,19 +200,13 @@ class Router {
208
200
  this.collectHandler(leaf, method, handlers);
209
201
  }
210
202
 
211
- Object.freeze(finalParams);
212
-
213
- const result = {
203
+ return {
214
204
  matched: res.matched,
215
205
  params: finalParams,
216
206
  paramsLength: Object.keys(finalParams).length,
217
207
  handlers: Object.values(handlers),
218
208
  errorHandlers: Object.values(errorHandlers),
219
209
  };
220
-
221
- this.cache.set(key, result);
222
-
223
- return result;
224
210
  }
225
211
  }
226
212
  exports.Router = Router;
package/src/util.js CHANGED
@@ -1,4 +1,5 @@
1
- const EMPTY_PARAMS = Object.freeze(Object.create(null));
1
+ // const EMPTY_PARAMS = Object.freeze(Object.create(null));
2
+ const EMPTY_PARAMS = Object.create(null)
2
3
  exports.EMPTY_PARAMS = EMPTY_PARAMS;
3
4
 
4
5
  function decodeParam(raw) {
@@ -12,6 +13,7 @@ exports.decodeParam = decodeParam;
12
13
 
13
14
  function decodeQuery(raw) {
14
15
  const normalized = raw.replace(/\+/g, " ");
16
+
15
17
  try {
16
18
  return decodeURIComponent(normalized);
17
19
  } catch (error) {
@@ -21,18 +23,23 @@ function decodeQuery(raw) {
21
23
 
22
24
  function parseQueryString(queryString) {
23
25
  if (queryString === "") return Object.create(null);
26
+
24
27
  const regex = /([^=&]*)=([^&]*)/g;
25
- let match;
26
28
  const result = Object.create(null);
29
+
30
+ let match;
27
31
  while ((match = regex.exec(queryString))) {
28
32
  const [, rawName, rawValue] = match;
33
+
29
34
  const name = decodeQuery(rawName);
30
35
  const value = decodeQuery(rawValue);
36
+
31
37
  if (Object.hasOwn(result, name)) {
32
38
  if (Array.isArray(result[name])) result[name].push(value);
33
39
  else result[name] = [result[name], value];
34
40
  } else result[name] = value;
35
41
  }
42
+
36
43
  return result;
37
44
  }
38
45
  exports.parseQueryString = parseQueryString;
@@ -41,8 +48,10 @@ function parseUrl(url) {
41
48
  if (!(url.includes("?") || url.includes("#"))) {
42
49
  return { path: url, queryString: "" };
43
50
  }
51
+
44
52
  const regex = /^([^?#]*)(\?([^#]*))?(#.*)?$/;
45
53
  const [, path = "/", , queryString = ""] = regex.exec(url);
54
+
46
55
  return { path, queryString };
47
56
  }
48
57
  exports.parseUrl = parseUrl;
package/src/cache.js DELETED
@@ -1,28 +0,0 @@
1
- class Cache {
2
- constructor(size = 0) {
3
- this.size = size;
4
- this.map = new Map();
5
- }
6
-
7
- get(key) {
8
- if (!this.map.has(key)) return undefined;
9
- const value = this.map.get(key);
10
- this.map.delete(key);
11
- this.map.set(key, value);
12
- return value;
13
- }
14
-
15
- set(key, value) {
16
- if (this.size <= 0) return; // disabled
17
- if (this.map.has(key)) this.map.delete(key);
18
- else if (this.map.size >= this.size) {
19
- this.map.delete(this.map.keys().next().value);
20
- }
21
- this.map.set(key, value);
22
- }
23
-
24
- clear() {
25
- this.map.clear();
26
- }
27
- }
28
- exports.Cache = Cache;