Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

find-my-way

Package Overview
Dependencies
Maintainers
2
Versions
112
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

find-my-way - npm Package Compare versions

Comparing version 4.0.0 to 4.1.0

test/issue-182.test.js

10

index.d.ts

@@ -60,13 +60,13 @@ import { IncomingMessage, ServerResponse } from 'http';

interface ConstraintStrategy<V extends HTTPVersion> {
interface ConstraintStrategy<V extends HTTPVersion, T = string> {
name: string,
mustMatchWhenDerived?: boolean,
storage() : {
get(version: String) : Handler<V> | null,
set(version: String, store: Handler<V>) : void,
del(version: String) : void,
get(value: T) : Handler<V> | null,
set(value: T, handler: Handler<V>) : void,
del(value: T) : void,
empty() : void
},
validate(value: unknown): void,
deriveConstraint<Context>(req: Req<V>, ctx?: Context) : String,
deriveConstraint<Context>(req: Req<V>, ctx?: Context) : T,
}

@@ -73,0 +73,0 @@

@@ -18,3 +18,3 @@ 'use strict'

const isRegexSafe = require('safe-regex2')
const { flattenNode, compressFlattenedNode, prettyPrintFlattenedNode } = require('./lib/pretty-print')
const { flattenNode, compressFlattenedNode, prettyPrintFlattenedNode, prettyPrintRoutesArray } = require('./lib/pretty-print')
const Node = require('./node')

@@ -575,3 +575,5 @@ const Constrainer = require('./lib/constrainer')

Router.prototype.prettyPrint = function () {
Router.prototype.prettyPrint = function (opts = {}) {
opts.commonPrefix = opts.commonPrefix === undefined ? true : opts.commonPrefix // default to original behaviour
if (!opts.commonPrefix) return prettyPrintRoutesArray(this.routes)
const root = {

@@ -578,0 +580,0 @@ prefix: '/',

@@ -0,3 +1,151 @@

'use strict'
/* eslint-disable no-multi-spaces */
const indent = ' '
const branchIndent = '│ '
const midBranchIndent = '├── '
const endBranchIndent = '└── '
const wildcardDelimiter = '*'
const pathDelimiter = '/'
const pathRegExp = /(?=\/)/
/* eslint-enable */
function prettyPrintRoutesArray (routeArray) {
const mergedRouteArray = []
let tree = ''
routeArray.sort((a, b) => {
if (!a.path || !b.path) return 0
return a.path.localeCompare(b.path)
})
// merge alike paths
for (let i = 0; i < routeArray.length; i++) {
const route = routeArray[i]
const pathExists = mergedRouteArray.find(r => route.path === r.path)
if (pathExists) {
// path already declared, add new method and break out of loop
pathExists.handlers.push({
method: route.method,
opts: route.opts.constraints || undefined
})
continue
}
const routeHandler = {
method: route.method,
opts: route.opts.constraints || undefined
}
mergedRouteArray.push({
path: route.path,
methods: [route.method],
opts: [route.opts],
handlers: [routeHandler],
parents: [],
branchLevel: 1
})
}
// insert root level path if none defined
if (!mergedRouteArray.filter(r => r.path === pathDelimiter).length) {
const rootPath = {
path: pathDelimiter,
truncatedPath: '',
methods: [],
opts: [],
handlers: [{}],
parents: [pathDelimiter]
}
// if wildcard route exists, insert root level after wildcard
if (mergedRouteArray.filter(r => r.path === wildcardDelimiter).length) {
mergedRouteArray.splice(1, 0, rootPath)
} else {
mergedRouteArray.unshift(rootPath)
}
}
// build tree
const routeTree = buildRouteTree(mergedRouteArray)
// draw tree
routeTree.forEach((rootBranch, idx) => {
tree += drawBranch(rootBranch, null, idx === routeTree.length - 1, false, true)
tree += '\n' // newline characters inserted at beginning of drawing function to allow for nested paths
})
return tree
}
function buildRouteTree (mergedRouteArray, rootPath) {
rootPath = rootPath || pathDelimiter
const result = []
const temp = { result }
mergedRouteArray.forEach((route, idx) => {
let splitPath = route.path.split(pathRegExp)
// add preceding slash for proper nesting
if (splitPath[0] !== pathDelimiter) {
// handle wildcard route
if (splitPath[0] !== wildcardDelimiter) splitPath = [pathDelimiter, splitPath[0].slice(1), ...splitPath.slice(1)]
}
// build tree
splitPath.reduce((acc, path, pidx) => {
if (!acc[path]) {
acc[path] = { result: [] }
const pathSeg = { path, children: acc[path].result }
if (pidx === splitPath.length - 1) pathSeg.handlers = route.handlers
acc.result.push(pathSeg)
}
return acc[path]
}, temp)
})
// unfold root object from array
return result
}
function drawBranch (pathSeg, prefix, endBranch, noPrefix, rootBranch) {
let branch = ''
if (!noPrefix && !rootBranch) branch += '\n'
if (!noPrefix) branch += `${prefix || ''}${endBranch ? endBranchIndent : midBranchIndent}`
branch += `${pathSeg.path}`
if (pathSeg.handlers) {
const flatHandlers = pathSeg.handlers.reduce((acc, curr) => {
const match = acc.findIndex(h => JSON.stringify(h.opts) === JSON.stringify(curr.opts))
if (match !== -1) {
acc[match].method = [acc[match].method, curr.method].join(', ')
} else {
acc.push(curr)
}
return acc
}, [])
flatHandlers.forEach((handler, idx) => {
if (idx > 0) branch += `${noPrefix ? '' : prefix}${endBranch ? indent : branchIndent}${pathSeg.path}`
branch += ` (${handler.method || '-'})`
if (handler.opts && JSON.stringify(handler.opts) !== '{}') branch += ` ${JSON.stringify(handler.opts)}`
if (flatHandlers.length > 1 && idx !== flatHandlers.length - 1) branch += '\n'
})
}
if (!noPrefix) prefix = `${prefix || ''}${endBranch ? indent : branchIndent}`
pathSeg.children.forEach((child, idx) => {
const endBranch = idx === pathSeg.children.length - 1
const skipPrefix = (!pathSeg.handlers && pathSeg.children.length === 1)
branch += drawBranch(child, prefix, endBranch, skipPrefix)
})
return branch
}
function prettyPrintFlattenedNode (flattenedNode, prefix, tail) {
var paramName = ''
let paramName = ''
const printHandlers = []

@@ -11,35 +159,45 @@

printHandlers.forEach((handler, index) => {
let suffix = `(${handler.method}`
if (Object.keys(handler.constraints).length > 0) {
suffix += ' ' + JSON.stringify(handler.constraints)
}
suffix += ')'
if (printHandlers.length) {
printHandlers.forEach((handler, index) => {
let suffix = `(${handler.method || '-'})`
if (Object.keys(handler.constraints).length > 0) {
suffix += ' ' + JSON.stringify(handler.constraints)
}
let name = ''
if (flattenedNode.prefix.includes(':')) {
var params = handler.params
name = params[params.length - 1]
if (index > 0) {
name = ':' + name
let name = ''
// find locations of parameters in prefix
const paramIndices = flattenedNode.prefix.split('').map((ch, idx) => ch === ':' ? idx : null).filter(idx => idx !== null)
if (paramIndices.length) {
let prevLoc = 0
paramIndices.forEach((loc, idx) => {
// find parameter in prefix
name += flattenedNode.prefix.slice(prevLoc, loc + 1)
// insert parameters
name += handler.params[handler.params.length - paramIndices.length + idx]
if (idx === paramIndices.length - 1) name += flattenedNode.prefix.slice(loc + 1)
prevLoc = loc + 1
})
} else {
// there are no parameters, return full object
name = flattenedNode.prefix
}
} else if (index > 0) {
name = flattenedNode.prefix
}
if (index === 0) {
paramName += name + ` ${suffix}`
return
} else {
paramName += '\n'
}
if (index === 0) {
paramName += `${name} ${suffix}`
return
} else {
paramName += '\n'
}
paramName += prefix + ' ' + name + ` ${suffix}`
})
paramName += `${prefix}${tail ? indent : branchIndent}${name} ${suffix}`
})
} else {
paramName = flattenedNode.prefix
}
var tree = `${prefix}${tail ? '└── ' : '├── '}${flattenedNode.prefix}${paramName}\n`
let tree = `${prefix}${tail ? endBranchIndent : midBranchIndent}${paramName}\n`
prefix = `${prefix}${tail ? ' ' : '│ '}`
prefix = `${prefix}${tail ? indent : branchIndent}`
const labels = Object.keys(flattenedNode.children)
for (var i = 0; i < labels.length; i++) {
for (let i = 0; i < labels.length; i++) {
const child = flattenedNode.children[labels[i]]

@@ -58,3 +216,4 @@ tree += prettyPrintFlattenedNode(child, prefix, i === (labels.length - 1))

for (const child of Object.values(node.children)) {
const childPrefixSegments = child.prefix.split(/(?=\/)/) // split on the slash separator but use a regex to lookahead and not actually match it, preserving it in the returned string segments
// split on the slash separator but use a regex to lookahead and not actually match it, preserving it in the returned string segments
const childPrefixSegments = child.prefix.split(pathRegExp)
let cursor = flattened

@@ -74,3 +233,2 @@ let parent

}
flattenNode(cursor, child)

@@ -101,2 +259,2 @@ }

module.exports = { flattenNode, compressFlattenedNode, prettyPrintFlattenedNode }
module.exports = { flattenNode, compressFlattenedNode, prettyPrintFlattenedNode, prettyPrintRoutesArray }

@@ -123,2 +123,3 @@ 'use strict'

kind: this.kind,
method: this.method,
handlers: this.handlers.slice(0),

@@ -125,0 +126,0 @@ regex: this.regex,

{
"name": "find-my-way",
"version": "4.0.0",
"version": "4.1.0",
"description": "Crazy fast http radix based router",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -412,3 +412,3 @@ # find-my-way

<a name="pretty-print"></a>
#### prettyPrint()
#### prettyPrint([{ commonPrefix: false }])
Prints the representation of the internal radix tree, useful for debugging.

@@ -418,11 +418,27 @@ ```js

findMyWay.on('GET', '/test/hello', () => {})
findMyWay.on('GET', '/hello/world', () => {})
findMyWay.on('GET', '/testing', () => {})
findMyWay.on('GET', '/testing/:param', () => {})
findMyWay.on('PUT', '/update', () => {})
console.log(findMyWay.prettyPrint())
// └── /
// ├── test (GET)
// │ └── /hello (GET)
// └── hello/world (GET)
// ├── test (GET)
// │ ├── /hello (GET)
// │ └── ing (GET)
// │ └── /:param (GET)
// └── update (PUT)
```
`prettyPrint` accepts an optional setting to use the internal routes array to render the tree.
```js
console.log(findMyWay.prettyPrint({ commonPrefix: false }))
// └── / (-)
// ├── test (GET)
// │ └── /hello (GET)
// ├── testing (GET)
// │ └── /:param (GET)
// └── update (PUT)
```
<a name="routes"></a>

@@ -429,0 +445,0 @@ #### routes

@@ -125,6 +125,6 @@ 'use strict'

const expected = `└── /test (GET)
/test (GET {"host":"auth.fastify.io"})
/test (GET) {"host":"auth.fastify.io"}
└── /:hello (GET)
:hello (GET {"version":"1.1.2"})
:hello (GET {"version":"2.0.0"})
/:hello (GET) {"version":"1.1.2"}
/:hello (GET) {"version":"2.0.0"}
`

@@ -135,1 +135,108 @@

})
test('pretty print - multiple parameters are drawn appropriately', t => {
t.plan(2)
const findMyWay = FindMyWay()
findMyWay.on('GET', '/test', () => {})
// routes with a nested parameter (i.e. no handler for the /:param) were breaking the display
findMyWay.on('GET', '/test/:hello/there/:ladies', () => {})
findMyWay.on('GET', '/test/:hello/there/:ladies/and/:gents', () => {})
findMyWay.on('GET', '/test/are/:you/:ready/to/:rock', () => {})
const tree = findMyWay.prettyPrint()
const expected = `└── /test (GET)
└── /
├── :hello/there/:ladies (GET)
│ └── /and/:gents (GET)
└── are/:you/:ready/to/:rock (GET)
`
t.is(typeof tree, 'string')
t.equal(tree, expected)
})
test('pretty print commonPrefix - use routes array to draw flattened routes', t => {
t.plan(4)
const findMyWay = FindMyWay()
findMyWay.on('GET', '/test', () => {})
findMyWay.on('GET', '/test/hello', () => {})
findMyWay.on('GET', '/testing', () => {})
findMyWay.on('GET', '/testing/:param', () => {})
findMyWay.on('PUT', '/update', () => {})
const radixTree = findMyWay.prettyPrint({ commonPrefix: true })
const arrayTree = findMyWay.prettyPrint({ commonPrefix: false })
const radixExpected = `└── /
├── test (GET)
│ ├── /hello (GET)
│ └── ing (GET)
│ └── /:param (GET)
└── update (PUT)
`
const arrayExpected = `└── / (-)
├── test (GET)
│ └── /hello (GET)
├── testing (GET)
│ └── /:param (GET)
└── update (PUT)
`
t.is(typeof radixTree, 'string')
t.is(typeof arrayTree, 'string')
t.equal(radixTree, radixExpected)
t.equal(arrayTree, arrayExpected)
})
test('pretty print commonPrefix - handle wildcard root', t => {
t.plan(2)
const findMyWay = FindMyWay()
findMyWay.on('OPTIONS', '*', () => {})
findMyWay.on('GET', '/test/hello', () => {})
findMyWay.on('GET', '/testing', () => {})
findMyWay.on('GET', '/testing/:param', () => {})
findMyWay.on('PUT', '/update', () => {})
const arrayTree = findMyWay.prettyPrint({ commonPrefix: false })
const arrayExpected = `├── * (OPTIONS)
└── / (-)
├── test/hello (GET)
├── testing (GET)
│ └── /:param (GET)
└── update (PUT)
`
t.is(typeof arrayTree, 'string')
t.equal(arrayTree, arrayExpected)
})
test('pretty print commonPrefix - handle constrained routes', t => {
t.plan(2)
const findMyWay = FindMyWay()
findMyWay.on('GET', '/test', () => {})
findMyWay.on('GET', '/test', { constraints: { host: 'auth.fastify.io' } }, () => {})
findMyWay.on('GET', '/test/:hello', () => {})
findMyWay.on('PUT', '/test/:hello', () => {})
findMyWay.on('GET', '/test/:hello', { constraints: { version: '1.1.2' } }, () => {})
findMyWay.on('GET', '/test/:hello', { constraints: { version: '2.0.0' } }, () => {})
const arrayTree = findMyWay.prettyPrint({ commonPrefix: false })
const arrayExpected = `└── / (-)
└── test (GET)
test (GET) {"host":"auth.fastify.io"}
└── /:hello (GET, PUT)
/:hello (GET) {"version":"1.1.2"}
/:hello (GET) {"version":"2.0.0"}
`
t.is(typeof arrayTree, 'string')
t.equal(arrayTree, arrayExpected)
})

@@ -114,1 +114,59 @@ import { expectType } from 'tsd'

}
// Custom Constraint
{
let handler: Router.Handler<Router.HTTPVersion.V1>
interface AcceptAndContentType { accept?: string, contentType?: string }
const customConstraintWithObject: Router.ConstraintStrategy<Router.HTTPVersion.V1, AcceptAndContentType> = {
name: "customConstraintWithObject",
deriveConstraint<Context>(req: Router.Req<Router.HTTPVersion.V1>, ctx: Context | undefined): AcceptAndContentType {
return {
accept: req.headers.accept,
contentType: req.headers["content-type"]
}
},
validate(value: unknown): void {},
storage () {
return {
get (version) { return handler },
set (version, handler) {},
del (version) {},
empty () {}
}
}
}
const storageWithObject = customConstraintWithObject.storage()
const acceptAndContentType: AcceptAndContentType = { accept: 'application/json', contentType: 'application/xml' }
expectType<AcceptAndContentType>(customConstraintWithObject.deriveConstraint(http1Req, http1Res))
expectType<void>(storageWithObject.empty())
expectType<void>(storageWithObject.del(acceptAndContentType));
expectType<Router.Handler<Router.HTTPVersion.V1> | null>(storageWithObject.get(acceptAndContentType));
expectType<void>(storageWithObject.set(acceptAndContentType, () => {}));
const customConstraintWithDefault: Router.ConstraintStrategy<Router.HTTPVersion.V1> = {
name: "customConstraintWithObject",
deriveConstraint<Context>(req: Router.Req<Router.HTTPVersion.V1>, ctx: Context | undefined): string {
return req.headers.accept ?? ''
},
validate(value: unknown): void {},
storage () {
return {
get (version) { return handler },
set (version, handler) {},
del (version) {},
empty () {}
}
}
}
const storageWithDefault = customConstraintWithDefault.storage()
expectType<string>(customConstraintWithDefault.deriveConstraint(http1Req, http1Res))
expectType<void>(storageWithDefault.empty())
expectType<void>(storageWithDefault.del(''));
expectType<Router.Handler<Router.HTTPVersion.V1> | null>(storageWithDefault.get(''));
expectType<void>(storageWithDefault.set('', () => {}));
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc