@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 +1 -1
- package/src/app.js +4 -4
- package/src/node.js +18 -18
- package/src/response.js +27 -17
- package/src/router.js +42 -31
package/package.json
CHANGED
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
|
|
65
|
-
req.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
|
|
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
|
|
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}
|
|
3
|
+
* @param {String} prefix
|
|
4
4
|
*/
|
|
5
|
-
constructor(
|
|
6
|
-
this.
|
|
7
|
-
if (
|
|
5
|
+
constructor(prefix = "") {
|
|
6
|
+
this.prefix = prefix;
|
|
7
|
+
if (prefix.startsWith(":")) {
|
|
8
8
|
/**@type {String}*/
|
|
9
|
-
this.name =
|
|
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}
|
|
22
|
+
* @param {String} prefix
|
|
23
23
|
* @returns {Node}
|
|
24
24
|
*/
|
|
25
|
-
add(
|
|
26
|
-
if (
|
|
25
|
+
add(prefix) {
|
|
26
|
+
if (prefix === "*") {
|
|
27
27
|
if (!this.wildcard) this.wildcard = new Node("*");
|
|
28
28
|
return this.wildcard;
|
|
29
29
|
}
|
|
30
|
-
if (
|
|
31
|
-
return this.params.getOrInsertComputed(
|
|
30
|
+
if (prefix.startsWith(":")) {
|
|
31
|
+
return this.params.getOrInsertComputed(prefix, (prefix) => new Node(prefix));
|
|
32
32
|
}
|
|
33
|
-
return this.children.getOrInsertComputed(
|
|
33
|
+
return this.children.getOrInsertComputed(prefix, (prefix) => new Node(prefix));
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* @param {String}
|
|
37
|
+
* @param {String} prefix
|
|
38
38
|
*/
|
|
39
|
-
remove(
|
|
40
|
-
this.children.delete(
|
|
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 [
|
|
60
|
-
this.add(
|
|
59
|
+
for (const [prefix, child] of node.params) {
|
|
60
|
+
this.add(prefix).merge(child, offset);
|
|
61
61
|
}
|
|
62
|
-
for (const [
|
|
63
|
-
this.add(
|
|
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
|
-
|
|
18
|
-
this.end(chunk
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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 [
|
|
45
|
-
const lcp = this.lcp(
|
|
44
|
+
for (const [prefix, child] of node.children) {
|
|
45
|
+
const lcp = this.lcp(prefix, path);
|
|
46
46
|
if (lcp === 0) continue;
|
|
47
|
-
if (lcp ===
|
|
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(
|
|
52
|
-
newNode.add(
|
|
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.
|
|
56
|
-
node.remove(
|
|
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
|
|
79
|
-
node = node.add(
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
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
|
-
|
|
291
|
-
|
|
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
|
-
|
|
313
|
-
this.
|
|
314
|
-
|
|
315
|
-
|
|
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;
|