@ndiinginc/router 4.15.0 → 4.16.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndiinginc/router",
3
- "version": "4.15.0",
3
+ "version": "4.16.0",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
package/src/app.js CHANGED
@@ -61,12 +61,12 @@ class App extends Router {
61
61
  * @param {Response} res
62
62
  */
63
63
  async handle(req, res) {
64
- const { params, handlers, errorHandlers } = this.find(req.method, req.path);
65
- req.params = this.decodeParams(params);
64
+ const result = this.find(req.method, req.path);
65
+ req.params = result.params;
66
66
  let err;
67
67
  try {
68
68
  const handlerPromise = async () => {
69
- for (const handler of Object.values(handlers)) {
69
+ for (const handler of result.handlers) {
70
70
  if (res.writableEnded) break;
71
71
  await handler(req, res);
72
72
  }
@@ -79,7 +79,7 @@ class App extends Router {
79
79
  } catch (error) {
80
80
  err = error;
81
81
  try {
82
- for (const handler of Object.values(errorHandlers)) {
82
+ for (const handler of result.errorHandlers) {
83
83
  if (res.writableEnded) break;
84
84
  await handler(err, req, res);
85
85
  }
package/src/node.js CHANGED
@@ -1,12 +1,12 @@
1
1
  class Node {
2
2
  /**
3
- * @param {String} segment
3
+ * @param {String} prefix
4
4
  */
5
- constructor(segment = "") {
6
- this.segment = segment;
7
- if (segment.startsWith(":")) {
5
+ constructor(prefix = "") {
6
+ this.prefix = prefix;
7
+ if (prefix.startsWith(":")) {
8
8
  /**@type {String}*/
9
- this.name = segment.slice(1);
9
+ this.name = prefix.slice(1);
10
10
  }
11
11
  /**@type {Map<string,Node>}*/
12
12
  this.children = new Map();
@@ -19,25 +19,25 @@ class Node {
19
19
  }
20
20
 
21
21
  /**
22
- * @param {String} segment
22
+ * @param {String} prefix
23
23
  * @returns {Node}
24
24
  */
25
- add(segment) {
26
- if (segment === "*") {
25
+ add(prefix) {
26
+ if (prefix === "*") {
27
27
  if (!this.wildcard) this.wildcard = new Node("*");
28
28
  return this.wildcard;
29
29
  }
30
- if (segment.startsWith(":")) {
31
- return this.params.getOrInsertComputed(segment, (segment) => new Node(segment));
30
+ if (prefix.startsWith(":")) {
31
+ return this.params.getOrInsertComputed(prefix, (prefix) => new Node(prefix));
32
32
  }
33
- return this.children.getOrInsertComputed(segment, (segment) => new Node(segment));
33
+ return this.children.getOrInsertComputed(prefix, (prefix) => new Node(prefix));
34
34
  }
35
35
 
36
36
  /**
37
- * @param {String} segment
37
+ * @param {String} prefix
38
38
  */
39
- remove(segment) {
40
- this.children.delete(segment);
39
+ remove(prefix) {
40
+ this.children.delete(prefix);
41
41
  }
42
42
 
43
43
  /**
@@ -56,11 +56,11 @@ class Node {
56
56
  if (node.wildcard) {
57
57
  this.add("*").merge(node.wildcard, offset);
58
58
  }
59
- for (const [segment, child] of node.params) {
60
- this.add(segment).merge(child, offset);
59
+ for (const [prefix, child] of node.params) {
60
+ this.add(prefix).merge(child, offset);
61
61
  }
62
- for (const [segment, child] of node.children) {
63
- this.add(segment).merge(child, offset);
62
+ for (const [prefix, child] of node.children) {
63
+ this.add(prefix).merge(child, offset);
64
64
  }
65
65
  }
66
66
 
package/src/response.js CHANGED
@@ -1,31 +1,41 @@
1
1
  const http = require("http");
2
+ const fs = require("fs");
3
+ const path = require("path");
2
4
 
3
5
  class Response extends http.ServerResponse {
4
- /**
5
- * @param {Number} code
6
- * @returns {Response}
7
- */
8
6
  status(code) {
9
7
  this.statusCode = code;
10
8
  return this;
11
9
  }
12
-
13
- /**
14
- * @param {String} chunk
15
- */
16
10
  send(chunk) {
17
- if (this.writableEnded) return;
18
- this.end(chunk, null, null);
11
+ this.setHeader("Content-Type", "text/plain; charset=utf-8");
12
+ this.end(chunk);
19
13
  }
20
14
 
21
- /**
22
- * @param {Object} value
23
- */
24
15
  json(value) {
25
- if (!this.headersSent) {
26
- this.setHeader("Content-Type", "application/json; charset=utf-8");
27
- }
28
- this.send(JSON.stringify(value));
16
+ this.setHeader("Content-Type", "application/json; charset=utf-8");
17
+ this.end(JSON.stringify(value));
18
+ }
19
+
20
+ download(filepath, filename) {
21
+ fs.stat(filepath, (err, stats) => {
22
+ if (err || !stats.isFile()) {
23
+ this.statusCode = 404;
24
+ return this.end();
25
+ }
26
+ filename=filename||path.basename(filepath)
27
+ this.setHeader("Content-Type", "application/octet-stream");
28
+ this.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
29
+ this.setHeader("Content-Length", stats.size);
30
+ const stream = fs.createReadStream(filepath);
31
+ stream.on("error", (err) => {
32
+ if (!this.headersSent) {
33
+ this.statusCode = 500;
34
+ this.end();
35
+ }
36
+ });
37
+ stream.pipe(this);
38
+ });
29
39
  }
30
40
  }
31
41
  exports.Response = Response;
package/src/router.js CHANGED
@@ -41,19 +41,19 @@ class Router {
41
41
  * @returns
42
42
  */
43
43
  walk(path, node) {
44
- for (const [segment, child] of node.children) {
45
- const lcp = this.lcp(segment, path);
44
+ for (const [prefix, child] of node.children) {
45
+ const lcp = this.lcp(prefix, path);
46
46
  if (lcp === 0) continue;
47
- if (lcp === segment.length) {
47
+ if (lcp === prefix.length) {
48
48
  if (lcp === path.length) return child;
49
49
  return this.walk(path.slice(lcp), child);
50
50
  }
51
- const newNode = new Node(segment.slice(0, lcp));
52
- newNode.add(segment.slice(lcp)).merge(child);
51
+ const newNode = new Node(prefix.slice(0, lcp));
52
+ newNode.add(prefix.slice(lcp)).merge(child);
53
53
  const rest = path.slice(lcp);
54
54
  if (rest.length) newNode.add(rest);
55
- node.add(newNode.segment).merge(newNode);
56
- node.remove(segment);
55
+ node.add(newNode.prefix).merge(newNode);
56
+ node.remove(prefix);
57
57
  }
58
58
  return node.add(path);
59
59
  }
@@ -75,8 +75,8 @@ class Router {
75
75
  if (path[i] === ":") {
76
76
  let j = i + 1;
77
77
  while (j < path.length && path[j] !== "/") j++;
78
- let segment = path.slice(i, j);
79
- node = node.add(segment);
78
+ let prefix = path.slice(i, j);
79
+ node = node.add(prefix);
80
80
  i = j;
81
81
  } else if (path[i] === "*") {
82
82
  let j = i + 1;
@@ -229,25 +229,29 @@ class Router {
229
229
  }
230
230
  if (nodes.length > state.nodes.length) {
231
231
  state.nodes = nodes.slice();
232
- params = { ...params };
232
+ state.params = { ...params };
233
233
  }
234
234
  nodes.pop();
235
235
  return { nodes: state.nodes, params: state.params, matched: false };
236
236
  }
237
- for (const [segment, child] of node.children) {
238
- if (path.startsWith(segment)) {
239
- const res = this.search(method, path.slice(segment.length), child, nodes, params, state);
240
- if (res.matched) return res;
237
+ if (node.children.size) {
238
+ for (const [prefix, child] of node.children) {
239
+ if (path.startsWith(prefix)) {
240
+ const res = this.search(method, path.slice(prefix.length), child, nodes, params, state);
241
+ if (res.matched) return res;
242
+ }
241
243
  }
242
244
  }
243
- for (const [, child] of node.params) {
244
- const slashIdx = path.indexOf("/");
245
- const value = slashIdx === -1 ? path : path.slice(0, slashIdx);
246
- const rest = slashIdx === -1 ? "" : path.slice(slashIdx);
247
- if (value.length > 0) {
248
- const next = { ...params, [child.name]: value };
249
- const result = this.search(method, rest, child, nodes, next, state);
250
- if (result.matched) return result;
245
+ if (node.params.size) {
246
+ for (const [, child] of node.params) {
247
+ const index = path.indexOf("/");
248
+ const value = index === -1 ? path : path.slice(0, index);
249
+ if (value.length) {
250
+ const rest = index === -1 ? "" : path.slice(index);
251
+ const next = { ...params, [child.name]: value };
252
+ const result = this.search(method, rest, child, nodes, next, state);
253
+ if (result.matched) return result;
254
+ }
251
255
  }
252
256
  }
253
257
  if (node.wildcard && (node.wildcard.handlers.get(method) || node.wildcard.handlers.get("ALL"))) {
@@ -256,7 +260,7 @@ class Router {
256
260
  }
257
261
  if (nodes.length > state.nodes.length) {
258
262
  state.nodes = nodes.slice();
259
- params = { ...params };
263
+ state.params = { ...params };
260
264
  }
261
265
  nodes.pop();
262
266
  return { nodes: state.nodes, params: state.params, matched: false };
@@ -269,8 +273,8 @@ class Router {
269
273
  */
270
274
  collectHandler(node, method, handlers) {
271
275
  if (node.handlers.has(method)) {
272
- for (const { seq, handler } of node.handlers.get(method)) {
273
- handlers[seq] = handler;
276
+ for (const entry of node.handlers.get(method)) {
277
+ handlers[entry.seq] = entry.handler;
274
278
  }
275
279
  }
276
280
  }
@@ -287,8 +291,11 @@ class Router {
287
291
 
288
292
  decodeParams(obj) {
289
293
  const params = Object.create(null);
290
- for (const key of Object.keys(obj)) {
291
- params[key] = decodeParam(obj[key]);
294
+ const keys = Object.keys(obj);
295
+ if (keys.length) {
296
+ for (const key of keys) {
297
+ params[key] = decodeParam(obj[key]);
298
+ }
292
299
  }
293
300
  return params;
294
301
  }
@@ -306,13 +313,17 @@ class Router {
306
313
  }
307
314
  const handlers = {};
308
315
  const errorHandlers = {};
316
+ if (res.matched) {
317
+ this.collectHandler(res.nodes.at(-1), method, handlers);
318
+ }
309
319
  for (const node of res.nodes) {
310
320
  this.collectMiddleware(node, handlers, errorHandlers);
311
321
  }
312
- if (res.matched) {
313
- this.collectHandler(res.nodes[res.nodes.length - 1], method, handlers);
314
- }
315
- return { params: res.params, handlers, errorHandlers };
322
+ return {
323
+ params: this.decodeParams(res.params),
324
+ handlers: Object.values(handlers),
325
+ errorHandlers: Object.values(errorHandlers),
326
+ };
316
327
  }
317
328
  }
318
329
  exports.Router = Router;