find-my-way
Advanced tools
Comparing version 1.10.4 to 1.11.0
@@ -11,2 +11,3 @@ 'use strict' | ||
findMyWay.on('GET', '/user/:id', () => true) | ||
findMyWay.on('GET', '/user/:id/static', () => true) | ||
findMyWay.on('GET', '/customer/:name-:surname', () => true) | ||
@@ -32,2 +33,5 @@ findMyWay.on('GET', '/at/:hour(^\\d+)h:minute(^\\d+)m', () => true) | ||
}) | ||
.add('lookup long dynamic route', function () { | ||
findMyWay.lookup({ method: 'GET', url: '/user/qwertyuiopasdfghjklzxcvbnm/static' }, null) | ||
}) | ||
.add('find static route', function () { | ||
@@ -48,2 +52,5 @@ findMyWay.find('GET', '/') | ||
}) | ||
.add('find long dynamic route', function () { | ||
findMyWay.find('GET', '/user/qwertyuiopasdfghjklzxcvbnm/static') | ||
}) | ||
.on('cycle', function (event) { | ||
@@ -50,0 +57,0 @@ console.log(String(event.target)) |
'use strict' | ||
const http = require('http') | ||
const router = require('./')() | ||
const router = require('./')({ | ||
defaultRoute: (req, res) => { | ||
res.end('not found') | ||
} | ||
}) | ||
@@ -10,4 +14,11 @@ router.on('GET', '/test', (req, res, params) => { | ||
router.on('GET', '/:test', (req, res, params) => { | ||
res.end(JSON.stringify(params)) | ||
}) | ||
router.on('GET', '/text/hello', (req, res, params) => { | ||
res.end('{"winter":"is here"}') | ||
}) | ||
const server = http.createServer((req, res) => { | ||
console.log(req.url) | ||
router.lookup(req, res) | ||
@@ -14,0 +25,0 @@ }) |
145
index.js
@@ -14,6 +14,7 @@ 'use strict' | ||
const assert = require('assert') | ||
const http = require('http') | ||
const fastDecode = require('fast-decode-uri-component') | ||
const Node = require('./node') | ||
const NODE_TYPES = Node.prototype.types | ||
const httpMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS', 'TRACE', 'CONNECT'] | ||
var errored = false | ||
const httpMethods = http.METHODS | ||
@@ -86,2 +87,3 @@ function Router (opts) { | ||
j = i + 1 | ||
// add the static part of the route to the tree | ||
this._insert(method, path.slice(0, i), 0, null, null, null, null) | ||
@@ -122,2 +124,3 @@ | ||
} | ||
// add the parameter and continue with the search | ||
this._insert(method, path.slice(0, i), nodeType, params, null, null, regex) | ||
@@ -129,2 +132,3 @@ | ||
this._insert(method, path.slice(0, i), 0, null, null, null, null) | ||
// add the wildcard parameter | ||
params.push('*') | ||
@@ -158,4 +162,5 @@ return this._insert(method, path.slice(0, len), 2, params, handler, store, null) | ||
// the longest common prefix is smaller than the current prefix | ||
// let's split the node and add a new child | ||
if (len < prefixLen) { | ||
// split the node in the radix tree and add it to the parent | ||
node = new Node( | ||
@@ -173,13 +178,9 @@ prefix.slice(len), | ||
// reset the parent | ||
currentNode.children = [node] | ||
currentNode.numberOfChildren = 1 | ||
currentNode.prefix = prefix.slice(0, len) | ||
currentNode.label = currentNode.prefix[0] | ||
currentNode.handlers = new Node.Handlers() | ||
currentNode.kind = NODE_TYPES.STATIC | ||
currentNode.regex = null | ||
currentNode.wildcardChild = null | ||
currentNode | ||
.reset(prefix.slice(0, len)) | ||
.addChild(node) | ||
// if the longest common prefix has the same length of the current path | ||
// the handler should be added to the current node, to a child otherwise | ||
if (len === pathLen) { | ||
// add the handler to the parent node | ||
assert(!currentNode.getHandler(method), `Method '${method}' already declared for route '${route}'`) | ||
@@ -189,23 +190,26 @@ currentNode.setHandler(method, handler, params, store) | ||
} else { | ||
// create a child node and add an handler to it | ||
node = new Node(path.slice(len), [], kind, null, regex) | ||
node = new Node(path.slice(len), {}, kind, null, regex) | ||
node.setHandler(method, handler, params, store) | ||
// add the child to the parent | ||
currentNode.add(node) | ||
currentNode.addChild(node) | ||
} | ||
// the longest common prefix is smaller than the path length, | ||
// but is higher than the prefix | ||
} else if (len < pathLen) { | ||
// remove the prefix | ||
path = path.slice(len) | ||
node = currentNode.findByLabel(path[0]) | ||
// check if there is a child with the label extracted from the new path | ||
node = currentNode.findByLabel(path) | ||
// there is a child within the given label, we must go deepen in the tree | ||
if (node) { | ||
// we must go deeper in the tree | ||
currentNode = node | ||
continue | ||
} | ||
// create a new child node | ||
node = new Node(path, [], kind, null, regex) | ||
// there are not children within the given label, let's create a new one! | ||
node = new Node(path, {}, kind, null, regex) | ||
node.setHandler(method, handler, params, store) | ||
// add the child to the parent | ||
currentNode.add(node) | ||
currentNode.addChild(node) | ||
// the node already exist | ||
} else if (handler) { | ||
// the node already exist | ||
assert(!currentNode.getHandler(method), `Method '${method}' already declared for route '${route}'`) | ||
@@ -271,3 +275,3 @@ currentNode.setHandler(method, handler, params, store) | ||
var handle = this.find(req.method, sanitizeUrl(req.url)) | ||
if (handle == null) return this._defaultRoute(req, res) | ||
if (handle === null) return this._defaultRoute(req, res) | ||
return handle.handler(req, res, handle.params, handle.store) | ||
@@ -297,3 +301,3 @@ } | ||
var handle = currentNode.handlers[method] | ||
if (handle != null) { | ||
if (handle !== null) { | ||
var paramsObj = {} | ||
@@ -318,3 +322,3 @@ if (handle.paramsLength > 0) { | ||
i = pathLen < prefixLen ? pathLen : prefixLen | ||
while (len < i && path[len] === prefix[len]) len++ | ||
while (len < i && path.charCodeAt(len) === prefix.charCodeAt(len)) len++ | ||
@@ -326,3 +330,3 @@ if (len === prefixLen) { | ||
var node = currentNode.find(path, method) | ||
var node = currentNode.findChild(path, method) | ||
if (node === null) { | ||
@@ -363,9 +367,7 @@ node = currentNode.parametricBrother | ||
currentNode = node | ||
i = 0 | ||
while (i < pathLen && path.charCodeAt(i) !== 47) i++ | ||
i = path.indexOf('/') | ||
if (i === -1) i = pathLen | ||
if (i > maxParamLength) return null | ||
decoded = fastDecode(path.slice(0, i)) | ||
if (errored === true) { | ||
return null | ||
} | ||
if (decoded === null) return null | ||
params[pindex++] = decoded | ||
@@ -379,5 +381,3 @@ path = path.slice(i) | ||
decoded = fastDecode(path) | ||
if (errored === true) { | ||
return null | ||
} | ||
if (decoded === null) return null | ||
params[pindex] = decoded | ||
@@ -392,9 +392,7 @@ currentNode = node | ||
currentNode = node | ||
i = 0 | ||
while (i < pathLen && path.charCodeAt(i) !== 47) i++ | ||
i = path.indexOf('/') | ||
if (i === -1) i = pathLen | ||
if (i > maxParamLength) return null | ||
decoded = fastDecode(path.slice(0, i)) | ||
if (errored === true) { | ||
return null | ||
} | ||
if (decoded === null) return null | ||
if (!node.regex.test(decoded)) return null | ||
@@ -419,5 +417,3 @@ params[pindex++] = decoded | ||
decoded = fastDecode(path.slice(0, i)) | ||
if (errored === true) { | ||
return null | ||
} | ||
if (decoded === null) return null | ||
params[pindex++] = decoded | ||
@@ -445,38 +441,13 @@ path = path.slice(i) | ||
Router.prototype.get = function (path, handler, store) { | ||
return this.on('GET', path, handler, store) | ||
} | ||
for (var i in http.METHODS) { | ||
const m = http.METHODS[i] | ||
const methodName = m.toLowerCase() | ||
Router.prototype.delete = function (path, handler, store) { | ||
return this.on('DELETE', path, handler, store) | ||
} | ||
if (Router.prototype[methodName]) throw new Error('Method already exists: ' + methodName) | ||
Router.prototype.head = function (path, handler, store) { | ||
return this.on('HEAD', path, handler, store) | ||
Router.prototype[methodName] = function (path, handler, store) { | ||
return this.on(m, path, handler, store) | ||
} | ||
} | ||
Router.prototype.patch = function (path, handler, store) { | ||
return this.on('PATCH', path, handler, store) | ||
} | ||
Router.prototype.post = function (path, handler, store) { | ||
return this.on('POST', path, handler, store) | ||
} | ||
Router.prototype.put = function (path, handler, store) { | ||
return this.on('PUT', path, handler, store) | ||
} | ||
Router.prototype.options = function (path, handler, store) { | ||
return this.on('OPTIONS', path, handler, store) | ||
} | ||
Router.prototype.trace = function (path, handler, store) { | ||
return this.on('TRACE', path, handler, store) | ||
} | ||
Router.prototype.connect = function (path, handler, store) { | ||
return this.on('CONNECT', path, handler, store) | ||
} | ||
Router.prototype.all = function (path, handler, store) { | ||
@@ -498,19 +469,8 @@ this.on(httpMethods, path, handler, store) | ||
function fastDecode (path) { | ||
errored = false | ||
try { | ||
return decodeURIComponent(path) | ||
} catch (err) { | ||
errored = true | ||
} | ||
} | ||
function getWildcardNode (node, method, path, len) { | ||
if (node === null) return null | ||
var decoded = fastDecode(path.slice(-len)) | ||
if (errored === true) { | ||
return null | ||
} | ||
if (decoded === null) return null | ||
var handle = node.handlers[method] | ||
if (handle != null) { | ||
if (handle !== null) { | ||
return { | ||
@@ -525,11 +485,2 @@ handler: handle.handler, | ||
/** | ||
* Returns the position of the last closing parenthese of the regexp expression. | ||
* | ||
* @param {String} path | ||
* @param {Number} idx | ||
* @returns {Number} | ||
* @throws {TypeError} when a closing parenthese is missing | ||
* @private | ||
*/ | ||
function getClosingParenthensePosition (path, idx) { | ||
@@ -536,0 +487,0 @@ // `path.indexOf()` will always return the first position of the closing parenthese, |
135
node.js
'use strict' | ||
const assert = require('assert') | ||
const http = require('http') | ||
const Handlers = buildHandlers() | ||
const types = { | ||
@@ -15,6 +19,6 @@ STATIC: 0, | ||
this.label = this.prefix[0] | ||
this.children = children || [] | ||
this.numberOfChildren = this.children.length | ||
this.children = children || {} | ||
this.numberOfChildren = Object.keys(this.children).length | ||
this.kind = kind || this.types.STATIC | ||
this.handlers = handlers || new Handlers() | ||
this.handlers = new Handlers(handlers) | ||
this.regex = regex || null | ||
@@ -29,16 +33,38 @@ this.wildcardChild = null | ||
Node.prototype.add = function (node) { | ||
if (node.kind === this.types.MATCH_ALL) { | ||
this.wildcardChild = node | ||
Node.prototype.getLabel = function () { | ||
return this.prefix[0] | ||
} | ||
Node.prototype.addChild = function (node) { | ||
var label = '' | ||
switch (node.kind) { | ||
case this.types.STATIC: | ||
label = node.getLabel() | ||
break | ||
case this.types.PARAM: | ||
case this.types.REGEX: | ||
case this.types.MULTI_PARAM: | ||
label = ':' | ||
break | ||
case this.types.MATCH_ALL: | ||
this.wildcardChild = node | ||
label = '*' | ||
break | ||
default: | ||
throw new Error(`Unknown node kind: ${node.kind}`) | ||
} | ||
this.children.push(node) | ||
this.children.sort((n1, n2) => n1.kind - n2.kind) | ||
this.numberOfChildren++ | ||
assert( | ||
this.children[label] === undefined, | ||
`There is already a child with label '${label}'` | ||
) | ||
// Search for a parametric brother and store it in a variable | ||
this.children[label] = node | ||
this.numberOfChildren = Object.keys(this.children).length | ||
const labels = Object.keys(this.children) | ||
var parametricBrother = null | ||
for (var i = 0; i < this.numberOfChildren; i++) { | ||
const child = this.children[i] | ||
if ([this.types.PARAM, this.types.REGEX, this.types.MULTI_PARAM].indexOf(child.kind) > -1) { | ||
for (var i = 0; i < labels.length; i++) { | ||
const child = this.children[labels[i]] | ||
if (child.label === ':') { | ||
parametricBrother = child | ||
@@ -49,30 +75,41 @@ break | ||
// Save the parametric brother inside a static child | ||
for (i = 0; i < this.numberOfChildren; i++) { | ||
if (this.children[i].kind === this.types.STATIC && parametricBrother) { | ||
this.children[i].parametricBrother = parametricBrother | ||
// Save the parametric brother inside a static children | ||
for (i = 0; i < labels.length; i++) { | ||
const child = this.children[labels[i]] | ||
if (child.kind === this.types.STATIC && parametricBrother) { | ||
child.parametricBrother = parametricBrother | ||
} | ||
} | ||
return this | ||
} | ||
Node.prototype.findByLabel = function (label) { | ||
for (var i = 0; i < this.numberOfChildren; i++) { | ||
var child = this.children[i] | ||
if (child.label === label) { | ||
return child | ||
} | ||
} | ||
return null | ||
Node.prototype.reset = function (prefix) { | ||
this.prefix = prefix | ||
this.children = {} | ||
this.kind = this.types.STATIC | ||
this.handlers = new Handlers() | ||
this.numberOfChildren = 0 | ||
this.regex = null | ||
this.wildcardChild = null | ||
return this | ||
} | ||
Node.prototype.find = function (path, method) { | ||
for (var i = 0; i < this.numberOfChildren; i++) { | ||
var child = this.children[i] | ||
if ( | ||
(child.numberOfChildren !== 0 || child.handlers[method] !== null) && | ||
(child.kind !== 0 || path.slice(0, child.prefix.length) === child.prefix) | ||
) { | ||
Node.prototype.findByLabel = function (path) { | ||
return this.children[path[0]] | ||
} | ||
Node.prototype.findChild = function (path, method) { | ||
var child = this.children[path[0]] | ||
if (child !== undefined && (child.numberOfChildren > 0 || child.handlers[method] !== null)) { | ||
if (path.slice(0, child.prefix.length) === child.prefix) { | ||
return child | ||
} | ||
} | ||
child = this.children[':'] || this.children['*'] | ||
if (child !== undefined && (child.numberOfChildren > 0 || child.handlers[method] !== null)) { | ||
return child | ||
} | ||
return null | ||
@@ -84,2 +121,7 @@ } | ||
assert( | ||
this.handlers[method] !== undefined, | ||
`There is already an handler with method '${method}'` | ||
) | ||
this.handlers[method] = { | ||
@@ -124,7 +166,8 @@ handler: handler, | ||
prefix = `${prefix}${tail ? ' ' : '│ '}` | ||
for (var i = 0; i < this.numberOfChildren - 1; i++) { | ||
tree += this.children[i].prettyPrint(prefix, false) | ||
const labels = Object.keys(this.children) | ||
for (var i = 0; i < labels.length - 1; i++) { | ||
tree += this.children[labels[i]].prettyPrint(prefix, false) | ||
} | ||
if (this.numberOfChildren > 0) { | ||
tree += this.children[this.numberOfChildren - 1].prettyPrint(prefix, true) | ||
if (labels.length > 0) { | ||
tree += this.children[labels[labels.length - 1]].prettyPrint(prefix, true) | ||
} | ||
@@ -134,13 +177,11 @@ return tree | ||
function Handlers (handlers) { | ||
handlers = handlers || {} | ||
this.DELETE = handlers.DELETE || null | ||
this.GET = handlers.GET || null | ||
this.HEAD = handlers.HEAD || null | ||
this.PATCH = handlers.PATCH || null | ||
this.POST = handlers.POST || null | ||
this.PUT = handlers.PUT || null | ||
this.OPTIONS = handlers.OPTIONS || null | ||
this.TRACE = handlers.TRACE || null | ||
this.CONNECT = handlers.CONNECT || null | ||
function buildHandlers (handlers) { | ||
var code = `handlers = handlers || {} | ||
` | ||
for (var i in http.METHODS) { | ||
var m = http.METHODS[i] | ||
code += `this['${m}'] = handlers['${m}'] || null | ||
` | ||
} | ||
return new Function('handlers', code) // eslint-disable-line | ||
} | ||
@@ -147,0 +188,0 @@ |
{ | ||
"name": "find-my-way", | ||
"version": "1.10.4", | ||
"version": "1.11.0", | ||
"description": "Crazy fast http radix based router", | ||
@@ -31,6 +31,10 @@ "main": "index.js", | ||
"coveralls": "^3.0.0", | ||
"pre-commit": "^1.2.2", | ||
"request": "^2.83.0", | ||
"standard": "^10.0.3", | ||
"tap": "^11.0.0" | ||
}, | ||
"dependencies": { | ||
"fast-decode-uri-component": "^1.0.0" | ||
} | ||
} |
@@ -140,2 +140,6 @@ # find-my-way | ||
<a name="supported-methods"></a> | ||
##### Supported methods | ||
The router is able to route all HTTP methods defined by [`http` core module](https://nodejs.org/api/http.html#http_http_methods). | ||
<a name="off"></a> | ||
@@ -182,2 +186,4 @@ #### off(method, path) | ||
If you want an even nicer api, you can also use the shorthand methods to declare your routes. | ||
For each HTTP supported method, there's the shorthand method. For example: | ||
```js | ||
@@ -191,4 +197,3 @@ router.get(path, handler [, store]) | ||
router.options(path, handler [, store]) | ||
router.trace(path, handler [, store]) | ||
router.connect(path, handler [, store]) | ||
// ... | ||
``` | ||
@@ -195,0 +200,0 @@ |
@@ -36,1 +36,17 @@ 'use strict' | ||
}) | ||
test('parametric with common prefix', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.on('GET', '/test', noop) | ||
findMyWay.on('GET', '/:test', (req, res, params) => { | ||
t.deepEqual( | ||
{ test: 'text' }, | ||
params | ||
) | ||
}) | ||
findMyWay.on('GET', '/text/hello', noop) | ||
findMyWay.lookup({ url: '/text', method: 'GET' }) | ||
}) |
@@ -714,1 +714,23 @@ 'use strict' | ||
}) | ||
test('register all known HTTP methods', t => { | ||
t.plan(6) | ||
const findMyWay = FindMyWay() | ||
const http = require('http') | ||
const handlers = {} | ||
for (var i in http.METHODS) { | ||
var m = http.METHODS[i] | ||
handlers[m] = function myHandler () {} | ||
findMyWay.on(m, '/test', handlers[m]) | ||
} | ||
t.ok(findMyWay.find('COPY', '/test')) | ||
t.equal(findMyWay.find('COPY', '/test').handler, handlers.COPY) | ||
t.ok(findMyWay.find('SUBSCRIBE', '/test')) | ||
t.equal(findMyWay.find('SUBSCRIBE', '/test').handler, handlers.SUBSCRIBE) | ||
t.ok(findMyWay.find('M-SEARCH', '/test')) | ||
t.equal(findMyWay.find('M-SEARCH', '/test').handler, handlers['M-SEARCH']) | ||
}) |
'use strict' | ||
const http = require('http') | ||
const t = require('tap') | ||
@@ -7,105 +8,26 @@ const test = t.test | ||
test('should support `.get` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
t.test('should support shorthand', t => { | ||
t.plan(http.METHODS.length) | ||
findMyWay.get('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
for (var i in http.METHODS) { | ||
const m = http.METHODS[i] | ||
const methodName = m.toLowerCase() | ||
findMyWay.lookup({ method: 'GET', url: '/test' }, null) | ||
}) | ||
t.test('`.' + methodName + '`', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
test('should support `.delete` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay[methodName]('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.delete('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'DELETE', url: '/test' }, null) | ||
findMyWay.lookup({ method: m, url: '/test' }, null) | ||
}) | ||
} | ||
}) | ||
test('should support `.head` shorthand', t => { | ||
t.plan(1) | ||
test('should support `.all` shorthand', t => { | ||
t.plan(11) | ||
const findMyWay = FindMyWay() | ||
findMyWay.head('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'HEAD', url: '/test' }, null) | ||
}) | ||
test('should support `.patch` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.patch('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'PATCH', url: '/test' }, null) | ||
}) | ||
test('should support `.post` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.post('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'POST', url: '/test' }, null) | ||
}) | ||
test('should support `.put` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.put('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'PUT', url: '/test' }, null) | ||
}) | ||
test('should support `.options` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.options('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'OPTIONS', url: '/test' }, null) | ||
}) | ||
test('should support `.trace` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.trace('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'TRACE', url: '/test' }, null) | ||
}) | ||
test('should support `.connect` shorthand', t => { | ||
t.plan(1) | ||
const findMyWay = FindMyWay() | ||
findMyWay.connect('/test', () => { | ||
t.ok('inside the handler') | ||
}) | ||
findMyWay.lookup({ method: 'CONNECT', url: '/test' }, null) | ||
}) | ||
test('should support `.connect` shorthand', t => { | ||
t.plan(9) | ||
const findMyWay = FindMyWay() | ||
findMyWay.all('/test', () => { | ||
@@ -124,2 +46,4 @@ t.ok('inside the handler') | ||
findMyWay.lookup({ method: 'CONNECT', url: '/test' }, null) | ||
findMyWay.lookup({ method: 'COPY', url: '/test' }, null) | ||
findMyWay.lookup({ method: 'SUBSCRIBE', url: '/test' }, null) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
109574
249
1
6
3213
7
+ Addedfast-decode-uri-component@1.0.1(transitive)