You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					713 lines
				
				22 KiB
			
		
		
			
		
	
	
					713 lines
				
				22 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  Char codes:
							 | 
						||
| 
								 | 
							
								    '#': 35
							 | 
						||
| 
								 | 
							
								    '*': 42
							 | 
						||
| 
								 | 
							
								    '-': 45
							 | 
						||
| 
								 | 
							
								    '.': 46
							 | 
						||
| 
								 | 
							
								    '/': 47
							 | 
						||
| 
								 | 
							
								    ':': 58
							 | 
						||
| 
								 | 
							
								    ';': 59
							 | 
						||
| 
								 | 
							
								    '?': 63
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const assert = require('assert')
							 | 
						||
| 
								 | 
							
								const http = require('http')
							 | 
						||
| 
								 | 
							
								const fastDecode = require('fast-decode-uri-component')
							 | 
						||
| 
								 | 
							
								const isRegexSafe = require('safe-regex2')
							 | 
						||
| 
								 | 
							
								const { flattenNode, compressFlattenedNode, prettyPrintFlattenedNode, prettyPrintRoutesArray } = require('./lib/pretty-print')
							 | 
						||
| 
								 | 
							
								const Node = require('./node')
							 | 
						||
| 
								 | 
							
								const Constrainer = require('./lib/constrainer')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const NODE_TYPES = Node.prototype.types
							 | 
						||
| 
								 | 
							
								const httpMethods = http.METHODS
							 | 
						||
| 
								 | 
							
								const FULL_PATH_REGEXP = /^https?:\/\/.*?\//
							 | 
						||
| 
								 | 
							
								const OPTIONAL_PARAM_REGEXP = /(\/:[^/()]*?)\?(\/?)/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (!isRegexSafe(FULL_PATH_REGEXP)) {
							 | 
						||
| 
								 | 
							
								  throw new Error('the FULL_PATH_REGEXP is not safe, update this module')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								if (!isRegexSafe(OPTIONAL_PARAM_REGEXP)) {
							 | 
						||
| 
								 | 
							
								  throw new Error('the OPTIONAL_PARAM_REGEXP is not safe, update this module')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function Router (opts) {
							 | 
						||
| 
								 | 
							
								  if (!(this instanceof Router)) {
							 | 
						||
| 
								 | 
							
								    return new Router(opts)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  opts = opts || {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (opts.defaultRoute) {
							 | 
						||
| 
								 | 
							
								    assert(typeof opts.defaultRoute === 'function', 'The default route must be a function')
							 | 
						||
| 
								 | 
							
								    this.defaultRoute = opts.defaultRoute
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this.defaultRoute = null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (opts.onBadUrl) {
							 | 
						||
| 
								 | 
							
								    assert(typeof opts.onBadUrl === 'function', 'The bad url handler must be a function')
							 | 
						||
| 
								 | 
							
								    this.onBadUrl = opts.onBadUrl
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this.onBadUrl = null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (opts.buildPrettyMeta) {
							 | 
						||
| 
								 | 
							
								    assert(typeof opts.buildPrettyMeta === 'function', 'buildPrettyMeta must be a function')
							 | 
						||
| 
								 | 
							
								    this.buildPrettyMeta = opts.buildPrettyMeta
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    this.buildPrettyMeta = defaultBuildPrettyMeta
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.caseSensitive = opts.caseSensitive === undefined ? true : opts.caseSensitive
							 | 
						||
| 
								 | 
							
								  this.ignoreTrailingSlash = opts.ignoreTrailingSlash || false
							 | 
						||
| 
								 | 
							
								  this.maxParamLength = opts.maxParamLength || 100
							 | 
						||
| 
								 | 
							
								  this.allowUnsafeRegex = opts.allowUnsafeRegex || false
							 | 
						||
| 
								 | 
							
								  this.constrainer = new Constrainer(opts.constraints)
							 | 
						||
| 
								 | 
							
								  this.trees = {}
							 | 
						||
| 
								 | 
							
								  this.routes = []
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.on = function on (method, path, opts, handler, store) {
							 | 
						||
| 
								 | 
							
								  if (typeof opts === 'function') {
							 | 
						||
| 
								 | 
							
								    if (handler !== undefined) {
							 | 
						||
| 
								 | 
							
								      store = handler
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    handler = opts
							 | 
						||
| 
								 | 
							
								    opts = {}
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  // path validation
							 | 
						||
| 
								 | 
							
								  assert(typeof path === 'string', 'Path should be a string')
							 | 
						||
| 
								 | 
							
								  assert(path.length > 0, 'The path could not be empty')
							 | 
						||
| 
								 | 
							
								  assert(path[0] === '/' || path[0] === '*', 'The first character of a path should be `/` or `*`')
							 | 
						||
| 
								 | 
							
								  // handler validation
							 | 
						||
| 
								 | 
							
								  assert(typeof handler === 'function', 'Handler should be a function')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // path ends with optional parameter
							 | 
						||
| 
								 | 
							
								  const optionalParamMatch = path.match(OPTIONAL_PARAM_REGEXP)
							 | 
						||
| 
								 | 
							
								  if (optionalParamMatch) {
							 | 
						||
| 
								 | 
							
								    assert(path.length === optionalParamMatch.index + optionalParamMatch[0].length, 'Optional Parameter needs to be the last parameter of the path')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const pathFull = path.replace(OPTIONAL_PARAM_REGEXP, '$1$2')
							 | 
						||
| 
								 | 
							
								    const pathOptional = path.replace(OPTIONAL_PARAM_REGEXP, '$2')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.on(method, pathFull, opts, handler, store)
							 | 
						||
| 
								 | 
							
								    this.on(method, pathOptional, opts, handler, store)
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this._on(method, path, opts, handler, store)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this.ignoreTrailingSlash && path !== '/' && !path.endsWith('*')) {
							 | 
						||
| 
								 | 
							
								    if (path.endsWith('/')) {
							 | 
						||
| 
								 | 
							
								      this._on(method, path.slice(0, -1), opts, handler, store)
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      this._on(method, path + '/', opts, handler, store)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype._on = function _on (method, path, opts, handler, store) {
							 | 
						||
| 
								 | 
							
								  if (Array.isArray(method)) {
							 | 
						||
| 
								 | 
							
								    for (var k = 0; k < method.length; k++) {
							 | 
						||
| 
								 | 
							
								      this._on(method[k], path, opts, handler, store)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  assert(typeof method === 'string', 'Method should be a string')
							 | 
						||
| 
								 | 
							
								  assert(httpMethods.indexOf(method) !== -1, `Method '${method}' is not an http method.`)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  let constraints = {}
							 | 
						||
| 
								 | 
							
								  if (opts.constraints !== undefined) {
							 | 
						||
| 
								 | 
							
								    assert(typeof opts.constraints === 'object' && opts.constraints !== null, 'Constraints should be an object')
							 | 
						||
| 
								 | 
							
								    if (Object.keys(opts.constraints).length !== 0) {
							 | 
						||
| 
								 | 
							
								      constraints = opts.constraints
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.constrainer.validateConstraints(constraints)
							 | 
						||
| 
								 | 
							
								  // Let the constrainer know if any constraints are being used now
							 | 
						||
| 
								 | 
							
								  this.constrainer.noteUsage(constraints)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const params = []
							 | 
						||
| 
								 | 
							
								  var j = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  this.routes.push({
							 | 
						||
| 
								 | 
							
								    method: method,
							 | 
						||
| 
								 | 
							
								    path: path,
							 | 
						||
| 
								 | 
							
								    opts: opts,
							 | 
						||
| 
								 | 
							
								    handler: handler,
							 | 
						||
| 
								 | 
							
								    store: store
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (var i = 0, len = path.length; i < len; i++) {
							 | 
						||
| 
								 | 
							
								    // search for parametric or wildcard routes
							 | 
						||
| 
								 | 
							
								    // parametric route
							 | 
						||
| 
								 | 
							
								    if (path.charCodeAt(i) === 58) {
							 | 
						||
| 
								 | 
							
								      if (i !== len - 1 && path.charCodeAt(i + 1) === 58) {
							 | 
						||
| 
								 | 
							
								        // It's a double colon. Let's just replace it with a single colon and go ahead
							 | 
						||
| 
								 | 
							
								        path = path.slice(0, i) + path.slice(i + 1)
							 | 
						||
| 
								 | 
							
								        len = path.length
							 | 
						||
| 
								 | 
							
								        continue
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var nodeType = NODE_TYPES.PARAM
							 | 
						||
| 
								 | 
							
								      j = i + 1
							 | 
						||
| 
								 | 
							
								      var staticPart = path.slice(0, i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (this.caseSensitive === false) {
							 | 
						||
| 
								 | 
							
								        staticPart = staticPart.toLowerCase()
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // add the static part of the route to the tree
							 | 
						||
| 
								 | 
							
								      this._insert(method, staticPart, NODE_TYPES.STATIC, null, null, null, null, constraints)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // isolate the parameter name
							 | 
						||
| 
								 | 
							
								      var isRegex = false
							 | 
						||
| 
								 | 
							
								      while (i < len && path.charCodeAt(i) !== 47) {
							 | 
						||
| 
								 | 
							
								        isRegex = isRegex || path[i] === '('
							 | 
						||
| 
								 | 
							
								        if (isRegex) {
							 | 
						||
| 
								 | 
							
								          i = getClosingParenthensePosition(path, i) + 1
							 | 
						||
| 
								 | 
							
								          break
							 | 
						||
| 
								 | 
							
								        } else if (path.charCodeAt(i) !== 45 && path.charCodeAt(i) !== 46) {
							 | 
						||
| 
								 | 
							
								          i++
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          break
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (isRegex && (i === len || path.charCodeAt(i) === 47)) {
							 | 
						||
| 
								 | 
							
								        nodeType = NODE_TYPES.REGEX
							 | 
						||
| 
								 | 
							
								      } else if (i < len && path.charCodeAt(i) !== 47) {
							 | 
						||
| 
								 | 
							
								        nodeType = NODE_TYPES.MULTI_PARAM
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var parameter = path.slice(j, i)
							 | 
						||
| 
								 | 
							
								      var regex = isRegex ? parameter.slice(parameter.indexOf('('), i) : null
							 | 
						||
| 
								 | 
							
								      if (isRegex) {
							 | 
						||
| 
								 | 
							
								        regex = new RegExp(regex)
							 | 
						||
| 
								 | 
							
								        if (!this.allowUnsafeRegex) {
							 | 
						||
| 
								 | 
							
								          assert(isRegexSafe(regex), `The regex '${regex.toString()}' is not safe!`)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      params.push(parameter.slice(0, isRegex ? parameter.indexOf('(') : i))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      path = path.slice(0, j) + path.slice(i)
							 | 
						||
| 
								 | 
							
								      i = j
							 | 
						||
| 
								 | 
							
								      len = path.length
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // if the path is ended
							 | 
						||
| 
								 | 
							
								      if (i === len) {
							 | 
						||
| 
								 | 
							
								        var completedPath = path.slice(0, i)
							 | 
						||
| 
								 | 
							
								        if (this.caseSensitive === false) {
							 | 
						||
| 
								 | 
							
								          completedPath = completedPath.toLowerCase()
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return this._insert(method, completedPath, nodeType, params, handler, store, regex, constraints)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // add the parameter and continue with the search
							 | 
						||
| 
								 | 
							
								      staticPart = path.slice(0, i)
							 | 
						||
| 
								 | 
							
								      if (this.caseSensitive === false) {
							 | 
						||
| 
								 | 
							
								        staticPart = staticPart.toLowerCase()
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      this._insert(method, staticPart, nodeType, params, null, null, regex, constraints)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      i--
							 | 
						||
| 
								 | 
							
								    // wildcard route
							 | 
						||
| 
								 | 
							
								    } else if (path.charCodeAt(i) === 42) {
							 | 
						||
| 
								 | 
							
								      this._insert(method, path.slice(0, i), NODE_TYPES.STATIC, null, null, null, null, constraints)
							 | 
						||
| 
								 | 
							
								      // add the wildcard parameter
							 | 
						||
| 
								 | 
							
								      params.push('*')
							 | 
						||
| 
								 | 
							
								      return this._insert(method, path.slice(0, len), NODE_TYPES.MATCH_ALL, params, handler, store, null, constraints)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this.caseSensitive === false) {
							 | 
						||
| 
								 | 
							
								    path = path.toLowerCase()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // static route
							 | 
						||
| 
								 | 
							
								  this._insert(method, path, NODE_TYPES.STATIC, params, handler, store, null, constraints)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype._insert = function _insert (method, path, kind, params, handler, store, regex, constraints) {
							 | 
						||
| 
								 | 
							
								  const route = path
							 | 
						||
| 
								 | 
							
								  var prefix = ''
							 | 
						||
| 
								 | 
							
								  var pathLen = 0
							 | 
						||
| 
								 | 
							
								  var prefixLen = 0
							 | 
						||
| 
								 | 
							
								  var len = 0
							 | 
						||
| 
								 | 
							
								  var max = 0
							 | 
						||
| 
								 | 
							
								  var node = null
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Boot the tree for this method if it doesn't exist yet
							 | 
						||
| 
								 | 
							
								  var currentNode = this.trees[method]
							 | 
						||
| 
								 | 
							
								  if (typeof currentNode === 'undefined') {
							 | 
						||
| 
								 | 
							
								    currentNode = new Node({ method: method, constrainer: this.constrainer })
							 | 
						||
| 
								 | 
							
								    this.trees[method] = currentNode
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    prefix = currentNode.prefix
							 | 
						||
| 
								 | 
							
								    prefixLen = prefix.length
							 | 
						||
| 
								 | 
							
								    pathLen = path.length
							 | 
						||
| 
								 | 
							
								    len = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // search for the longest common prefix
							 | 
						||
| 
								 | 
							
								    max = pathLen < prefixLen ? pathLen : prefixLen
							 | 
						||
| 
								 | 
							
								    while (len < max && path[len] === prefix[len]) len++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // the longest common prefix is smaller than the current prefix
							 | 
						||
| 
								 | 
							
								    // let's split the node and add a new child
							 | 
						||
| 
								 | 
							
								    if (len < prefixLen) {
							 | 
						||
| 
								 | 
							
								      node = currentNode.split(len)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      // 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) {
							 | 
						||
| 
								 | 
							
								        assert(!currentNode.getHandler(constraints), `Method '${method}' already declared for route '${route}' with constraints '${JSON.stringify(constraints)}'`)
							 | 
						||
| 
								 | 
							
								        currentNode.addHandler(handler, params, store, constraints)
							 | 
						||
| 
								 | 
							
								        currentNode.kind = kind
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        node = new Node({
							 | 
						||
| 
								 | 
							
								          method: method,
							 | 
						||
| 
								 | 
							
								          prefix: path.slice(len),
							 | 
						||
| 
								 | 
							
								          kind: kind,
							 | 
						||
| 
								 | 
							
								          handlers: null,
							 | 
						||
| 
								 | 
							
								          regex: regex,
							 | 
						||
| 
								 | 
							
								          constrainer: this.constrainer
							 | 
						||
| 
								 | 
							
								        })
							 | 
						||
| 
								 | 
							
								        node.addHandler(handler, params, store, constraints)
							 | 
						||
| 
								 | 
							
								        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)
							 | 
						||
| 
								 | 
							
								      // 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) {
							 | 
						||
| 
								 | 
							
								        currentNode = node
							 | 
						||
| 
								 | 
							
								        continue
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // there are not children within the given label, let's create a new one!
							 | 
						||
| 
								 | 
							
								      node = new Node({ method: method, prefix: path, kind: kind, handlers: null, regex: regex, constrainer: this.constrainer })
							 | 
						||
| 
								 | 
							
								      node.addHandler(handler, params, store, constraints)
							 | 
						||
| 
								 | 
							
								      currentNode.addChild(node)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // the node already exist
							 | 
						||
| 
								 | 
							
								    } else if (handler) {
							 | 
						||
| 
								 | 
							
								      assert(!currentNode.getHandler(constraints), `Method '${method}' already declared for route '${route}' with constraints '${JSON.stringify(constraints)}'`)
							 | 
						||
| 
								 | 
							
								      currentNode.addHandler(handler, params, store, constraints)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.reset = function reset () {
							 | 
						||
| 
								 | 
							
								  this.trees = {}
							 | 
						||
| 
								 | 
							
								  this.routes = []
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.off = function off (method, path) {
							 | 
						||
| 
								 | 
							
								  var self = this
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Array.isArray(method)) {
							 | 
						||
| 
								 | 
							
								    return method.map(function (method) {
							 | 
						||
| 
								 | 
							
								      return self.off(method, path)
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // method validation
							 | 
						||
| 
								 | 
							
								  assert(typeof method === 'string', 'Method should be a string')
							 | 
						||
| 
								 | 
							
								  assert(httpMethods.indexOf(method) !== -1, `Method '${method}' is not an http method.`)
							 | 
						||
| 
								 | 
							
								  // path validation
							 | 
						||
| 
								 | 
							
								  assert(typeof path === 'string', 'Path should be a string')
							 | 
						||
| 
								 | 
							
								  assert(path.length > 0, 'The path could not be empty')
							 | 
						||
| 
								 | 
							
								  assert(path[0] === '/' || path[0] === '*', 'The first character of a path should be `/` or `*`')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // path ends with optional parameter
							 | 
						||
| 
								 | 
							
								  const optionalParamMatch = path.match(OPTIONAL_PARAM_REGEXP)
							 | 
						||
| 
								 | 
							
								  if (optionalParamMatch) {
							 | 
						||
| 
								 | 
							
								    assert(path.length === optionalParamMatch.index + optionalParamMatch[0].length, 'Optional Parameter needs to be the last parameter of the path')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    const pathFull = path.replace(OPTIONAL_PARAM_REGEXP, '$1$2')
							 | 
						||
| 
								 | 
							
								    const pathOptional = path.replace(OPTIONAL_PARAM_REGEXP, '$2')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    this.off(method, pathFull)
							 | 
						||
| 
								 | 
							
								    this.off(method, pathOptional)
							 | 
						||
| 
								 | 
							
								    return
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Rebuild tree without the specific route
							 | 
						||
| 
								 | 
							
								  const ignoreTrailingSlash = this.ignoreTrailingSlash
							 | 
						||
| 
								 | 
							
								  var newRoutes = self.routes.filter(function (route) {
							 | 
						||
| 
								 | 
							
								    if (!ignoreTrailingSlash) {
							 | 
						||
| 
								 | 
							
								      return !(method === route.method && path === route.path)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (path.endsWith('/')) {
							 | 
						||
| 
								 | 
							
								      const routeMatches = path === route.path || path.slice(0, -1) === route.path
							 | 
						||
| 
								 | 
							
								      return !(method === route.method && routeMatches)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const routeMatches = path === route.path || (path + '/') === route.path
							 | 
						||
| 
								 | 
							
								    return !(method === route.method && routeMatches)
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								  if (ignoreTrailingSlash) {
							 | 
						||
| 
								 | 
							
								    newRoutes = newRoutes.filter(function (route, i, ar) {
							 | 
						||
| 
								 | 
							
								      if (route.path.endsWith('/') && i < ar.length - 1) {
							 | 
						||
| 
								 | 
							
								        return route.path.slice(0, -1) !== ar[i + 1].path
							 | 
						||
| 
								 | 
							
								      } else if (route.path.endsWith('/') === false && i < ar.length - 1) {
							 | 
						||
| 
								 | 
							
								        return (route.path + '/') !== ar[i + 1].path
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      return true
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  self.reset()
							 | 
						||
| 
								 | 
							
								  newRoutes.forEach(function (route) {
							 | 
						||
| 
								 | 
							
								    self.on(route.method, route.path, route.opts, route.handler, route.store)
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.lookup = function lookup (req, res, ctx) {
							 | 
						||
| 
								 | 
							
								  var handle = this.find(req.method, sanitizeUrl(req.url), this.constrainer.deriveConstraints(req, ctx))
							 | 
						||
| 
								 | 
							
								  if (handle === null) return this._defaultRoute(req, res, ctx)
							 | 
						||
| 
								 | 
							
								  return ctx === undefined
							 | 
						||
| 
								 | 
							
								    ? handle.handler(req, res, handle.params, handle.store)
							 | 
						||
| 
								 | 
							
								    : handle.handler.call(ctx, req, res, handle.params, handle.store)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.find = function find (method, path, derivedConstraints) {
							 | 
						||
| 
								 | 
							
								  var currentNode = this.trees[method]
							 | 
						||
| 
								 | 
							
								  if (currentNode === undefined) return null
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (path.charCodeAt(0) !== 47) { // 47 is '/'
							 | 
						||
| 
								 | 
							
								    path = path.replace(FULL_PATH_REGEXP, '/')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var originalPath = path
							 | 
						||
| 
								 | 
							
								  var originalPathLength = path.length
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (this.caseSensitive === false) {
							 | 
						||
| 
								 | 
							
								    path = path.toLowerCase()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var maxParamLength = this.maxParamLength
							 | 
						||
| 
								 | 
							
								  var wildcardNode = null
							 | 
						||
| 
								 | 
							
								  var pathLenWildcard = 0
							 | 
						||
| 
								 | 
							
								  var decoded = null
							 | 
						||
| 
								 | 
							
								  var pindex = 0
							 | 
						||
| 
								 | 
							
								  var params = null
							 | 
						||
| 
								 | 
							
								  var i = 0
							 | 
						||
| 
								 | 
							
								  var idxInOriginalPath = 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (true) {
							 | 
						||
| 
								 | 
							
								    var pathLen = path.length
							 | 
						||
| 
								 | 
							
								    var prefix = currentNode.prefix
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // found the route
							 | 
						||
| 
								 | 
							
								    if (pathLen === 0 || path === prefix) {
							 | 
						||
| 
								 | 
							
								      var handle = derivedConstraints !== undefined ? currentNode.getMatchingHandler(derivedConstraints) : currentNode.unconstrainedHandler
							 | 
						||
| 
								 | 
							
								      if (handle !== null && handle !== undefined) {
							 | 
						||
| 
								 | 
							
								        var paramsObj = {}
							 | 
						||
| 
								 | 
							
								        if (handle.paramsLength > 0) {
							 | 
						||
| 
								 | 
							
								          var paramNames = handle.params
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								          for (i = 0; i < handle.paramsLength; i++) {
							 | 
						||
| 
								 | 
							
								            paramsObj[paramNames[i]] = params[i]
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								          handler: handle.handler,
							 | 
						||
| 
								 | 
							
								          params: paramsObj,
							 | 
						||
| 
								 | 
							
								          store: handle.store
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var prefixLen = prefix.length
							 | 
						||
| 
								 | 
							
								    var len = 0
							 | 
						||
| 
								 | 
							
								    var previousPath = path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // search for the longest common prefix
							 | 
						||
| 
								 | 
							
								    i = pathLen < prefixLen ? pathLen : prefixLen
							 | 
						||
| 
								 | 
							
								    while (len < i && path.charCodeAt(len) === prefix.charCodeAt(len)) len++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (len === prefixLen) {
							 | 
						||
| 
								 | 
							
								      path = path.slice(len)
							 | 
						||
| 
								 | 
							
								      pathLen = path.length
							 | 
						||
| 
								 | 
							
								      idxInOriginalPath += len
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var node = currentNode.findMatchingChild(derivedConstraints, path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node === null) {
							 | 
						||
| 
								 | 
							
								      node = currentNode.parametricBrother
							 | 
						||
| 
								 | 
							
								      if (node === null) {
							 | 
						||
| 
								 | 
							
								        return this._getWildcardNode(wildcardNode, originalPath, pathLenWildcard, derivedConstraints, params)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var goBack = previousPath.charCodeAt(0) === 47 ? previousPath : '/' + previousPath
							 | 
						||
| 
								 | 
							
								      if (originalPath.indexOf(goBack) === -1) {
							 | 
						||
| 
								 | 
							
								        // we need to know the outstanding path so far from the originalPath since the last encountered "/" and assign it to previousPath.
							 | 
						||
| 
								 | 
							
								        // e.g originalPath: /aa/bbb/cc, path: bb/cc
							 | 
						||
| 
								 | 
							
								        // outstanding path: /bbb/cc
							 | 
						||
| 
								 | 
							
								        var pathDiff = originalPath.slice(0, originalPathLength - pathLen)
							 | 
						||
| 
								 | 
							
								        previousPath = pathDiff.slice(pathDiff.lastIndexOf('/') + 1, pathDiff.length) + path
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      idxInOriginalPath = idxInOriginalPath -
							 | 
						||
| 
								 | 
							
								        (previousPath.length - path.length)
							 | 
						||
| 
								 | 
							
								      path = previousPath
							 | 
						||
| 
								 | 
							
								      pathLen = previousPath.length
							 | 
						||
| 
								 | 
							
								      len = prefixLen
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var kind = node.kind
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // static route
							 | 
						||
| 
								 | 
							
								    if (kind === NODE_TYPES.STATIC) {
							 | 
						||
| 
								 | 
							
								      // if exist, save the wildcard child
							 | 
						||
| 
								 | 
							
								      if (currentNode.wildcardChild !== null) {
							 | 
						||
| 
								 | 
							
								        wildcardNode = currentNode.wildcardChild
							 | 
						||
| 
								 | 
							
								        pathLenWildcard = pathLen
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      currentNode = node
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (len !== prefixLen) {
							 | 
						||
| 
								 | 
							
								      return this._getWildcardNode(wildcardNode, originalPath, pathLenWildcard, derivedConstraints, params)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // if exist, save the wildcard child
							 | 
						||
| 
								 | 
							
								    if (currentNode.wildcardChild !== null) {
							 | 
						||
| 
								 | 
							
								      wildcardNode = currentNode.wildcardChild
							 | 
						||
| 
								 | 
							
								      pathLenWildcard = pathLen
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // parametric route
							 | 
						||
| 
								 | 
							
								    if (kind === NODE_TYPES.PARAM) {
							 | 
						||
| 
								 | 
							
								      currentNode = node
							 | 
						||
| 
								 | 
							
								      i = path.indexOf('/')
							 | 
						||
| 
								 | 
							
								      if (i === -1) i = pathLen
							 | 
						||
| 
								 | 
							
								      if (i > maxParamLength) return null
							 | 
						||
| 
								 | 
							
								      decoded = fastDecode(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								      if (decoded === null) {
							 | 
						||
| 
								 | 
							
								        return this.onBadUrl !== null
							 | 
						||
| 
								 | 
							
								          ? this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								          : null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      params || (params = [])
							 | 
						||
| 
								 | 
							
								      params[pindex++] = decoded
							 | 
						||
| 
								 | 
							
								      path = path.slice(i)
							 | 
						||
| 
								 | 
							
								      idxInOriginalPath += i
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // wildcard route
							 | 
						||
| 
								 | 
							
								    if (kind === NODE_TYPES.MATCH_ALL) {
							 | 
						||
| 
								 | 
							
								      decoded = fastDecode(originalPath.slice(idxInOriginalPath))
							 | 
						||
| 
								 | 
							
								      if (decoded === null) {
							 | 
						||
| 
								 | 
							
								        return this.onBadUrl !== null
							 | 
						||
| 
								 | 
							
								          ? this._onBadUrl(originalPath.slice(idxInOriginalPath))
							 | 
						||
| 
								 | 
							
								          : null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      params || (params = [])
							 | 
						||
| 
								 | 
							
								      params[pindex] = decoded
							 | 
						||
| 
								 | 
							
								      currentNode = node
							 | 
						||
| 
								 | 
							
								      path = ''
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // parametric(regex) route
							 | 
						||
| 
								 | 
							
								    if (kind === NODE_TYPES.REGEX) {
							 | 
						||
| 
								 | 
							
								      currentNode = node
							 | 
						||
| 
								 | 
							
								      i = path.indexOf('/')
							 | 
						||
| 
								 | 
							
								      if (i === -1) i = pathLen
							 | 
						||
| 
								 | 
							
								      if (i > maxParamLength) return null
							 | 
						||
| 
								 | 
							
								      decoded = fastDecode(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								      if (decoded === null) {
							 | 
						||
| 
								 | 
							
								        return this.onBadUrl !== null
							 | 
						||
| 
								 | 
							
								          ? this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								          : null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (!node.regex.test(decoded)) return null
							 | 
						||
| 
								 | 
							
								      params || (params = [])
							 | 
						||
| 
								 | 
							
								      params[pindex++] = decoded
							 | 
						||
| 
								 | 
							
								      path = path.slice(i)
							 | 
						||
| 
								 | 
							
								      idxInOriginalPath += i
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // multiparametric route
							 | 
						||
| 
								 | 
							
								    if (kind === NODE_TYPES.MULTI_PARAM) {
							 | 
						||
| 
								 | 
							
								      currentNode = node
							 | 
						||
| 
								 | 
							
								      i = 0
							 | 
						||
| 
								 | 
							
								      if (node.regex !== null) {
							 | 
						||
| 
								 | 
							
								        var matchedParameter = path.match(node.regex)
							 | 
						||
| 
								 | 
							
								        if (matchedParameter === null) return null
							 | 
						||
| 
								 | 
							
								        i = matchedParameter[1].length
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        while (i < pathLen && path.charCodeAt(i) !== 47 && path.charCodeAt(i) !== 45 && path.charCodeAt(i) !== 46) i++
							 | 
						||
| 
								 | 
							
								        if (i > maxParamLength) return null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      decoded = fastDecode(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								      if (decoded === null) {
							 | 
						||
| 
								 | 
							
								        return this.onBadUrl !== null
							 | 
						||
| 
								 | 
							
								          ? this._onBadUrl(originalPath.slice(idxInOriginalPath, idxInOriginalPath + i))
							 | 
						||
| 
								 | 
							
								          : null
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      params || (params = [])
							 | 
						||
| 
								 | 
							
								      params[pindex++] = decoded
							 | 
						||
| 
								 | 
							
								      path = path.slice(i)
							 | 
						||
| 
								 | 
							
								      idxInOriginalPath += i
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    wildcardNode = null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype._getWildcardNode = function (node, path, len, derivedConstraints, params) {
							 | 
						||
| 
								 | 
							
								  if (node === null) return null
							 | 
						||
| 
								 | 
							
								  var decoded = fastDecode(path.slice(-len))
							 | 
						||
| 
								 | 
							
								  if (decoded === null) {
							 | 
						||
| 
								 | 
							
								    return this.onBadUrl !== null
							 | 
						||
| 
								 | 
							
								      ? this._onBadUrl(path.slice(-len))
							 | 
						||
| 
								 | 
							
								      : null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var handle = derivedConstraints !== undefined ? node.getMatchingHandler(derivedConstraints) : node.unconstrainedHandler
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (handle !== null && handle !== undefined) {
							 | 
						||
| 
								 | 
							
								    var paramsObj = {}
							 | 
						||
| 
								 | 
							
								    if (handle.paramsLength > 0 && params !== null) {
							 | 
						||
| 
								 | 
							
								      var paramNames = handle.params
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      for (i = 0; i < handle.paramsLength; i++) {
							 | 
						||
| 
								 | 
							
								        paramsObj[paramNames[i]] = params[i]
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // we must override params[*] to decoded
							 | 
						||
| 
								 | 
							
								    paramsObj['*'] = decoded
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    return {
							 | 
						||
| 
								 | 
							
								      handler: handle.handler,
							 | 
						||
| 
								 | 
							
								      params: paramsObj,
							 | 
						||
| 
								 | 
							
								      store: handle.store
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return null
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype._defaultRoute = function (req, res, ctx) {
							 | 
						||
| 
								 | 
							
								  if (this.defaultRoute !== null) {
							 | 
						||
| 
								 | 
							
								    return ctx === undefined
							 | 
						||
| 
								 | 
							
								      ? this.defaultRoute(req, res)
							 | 
						||
| 
								 | 
							
								      : this.defaultRoute.call(ctx, req, res)
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    res.statusCode = 404
							 | 
						||
| 
								 | 
							
								    res.end()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype._onBadUrl = function (path) {
							 | 
						||
| 
								 | 
							
								  const onBadUrl = this.onBadUrl
							 | 
						||
| 
								 | 
							
								  return {
							 | 
						||
| 
								 | 
							
								    handler: (req, res, ctx) => onBadUrl(path, req, res),
							 | 
						||
| 
								 | 
							
								    params: {},
							 | 
						||
| 
								 | 
							
								    store: null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.prettyPrint = function (opts = {}) {
							 | 
						||
| 
								 | 
							
								  opts.commonPrefix = opts.commonPrefix === undefined ? true : opts.commonPrefix // default to original behaviour
							 | 
						||
| 
								 | 
							
								  if (!opts.commonPrefix) return prettyPrintRoutesArray.call(this, this.routes, opts)
							 | 
						||
| 
								 | 
							
								  const root = {
							 | 
						||
| 
								 | 
							
								    prefix: '/',
							 | 
						||
| 
								 | 
							
								    nodes: [],
							 | 
						||
| 
								 | 
							
								    children: {}
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  for (const node of Object.values(this.trees)) {
							 | 
						||
| 
								 | 
							
								    if (node) {
							 | 
						||
| 
								 | 
							
								      flattenNode(root, node)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  compressFlattenedNode(root)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return prettyPrintFlattenedNode.call(this, root, '', true, opts)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								for (var i in http.METHODS) {
							 | 
						||
| 
								 | 
							
								  /* eslint no-prototype-builtins: "off" */
							 | 
						||
| 
								 | 
							
								  if (!http.METHODS.hasOwnProperty(i)) continue
							 | 
						||
| 
								 | 
							
								  const m = http.METHODS[i]
							 | 
						||
| 
								 | 
							
								  const methodName = m.toLowerCase()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Router.prototype[methodName]) throw new Error('Method already exists: ' + methodName)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  Router.prototype[methodName] = function (path, handler, store) {
							 | 
						||
| 
								 | 
							
								    return this.on(m, path, handler, store)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Router.prototype.all = function (path, handler, store) {
							 | 
						||
| 
								 | 
							
								  this.on(httpMethods, path, handler, store)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = Router
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function sanitizeUrl (url) {
							 | 
						||
| 
								 | 
							
								  for (var i = 0, len = url.length; i < len; i++) {
							 | 
						||
| 
								 | 
							
								    var charCode = url.charCodeAt(i)
							 | 
						||
| 
								 | 
							
								    // Some systems do not follow RFC and separate the path and query
							 | 
						||
| 
								 | 
							
								    // string with a `;` character (code 59), e.g. `/foo;jsessionid=123456`.
							 | 
						||
| 
								 | 
							
								    // Thus, we need to split on `;` as well as `?` and `#`.
							 | 
						||
| 
								 | 
							
								    if (charCode === 63 || charCode === 59 || charCode === 35) {
							 | 
						||
| 
								 | 
							
								      return url.slice(0, i)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return url
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function getClosingParenthensePosition (path, idx) {
							 | 
						||
| 
								 | 
							
								  // `path.indexOf()` will always return the first position of the closing parenthese,
							 | 
						||
| 
								 | 
							
								  // but it's inefficient for grouped or wrong regexp expressions.
							 | 
						||
| 
								 | 
							
								  // see issues #62 and #63 for more info
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var parentheses = 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  while (idx < path.length) {
							 | 
						||
| 
								 | 
							
								    idx++
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // ignore skipped chars
							 | 
						||
| 
								 | 
							
								    if (path[idx] === '\\') {
							 | 
						||
| 
								 | 
							
								      idx++
							 | 
						||
| 
								 | 
							
								      continue
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (path[idx] === ')') {
							 | 
						||
| 
								 | 
							
								      parentheses--
							 | 
						||
| 
								 | 
							
								    } else if (path[idx] === '(') {
							 | 
						||
| 
								 | 
							
								      parentheses++
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!parentheses) return idx
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  throw new TypeError('Invalid regexp expression in "' + path + '"')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function defaultBuildPrettyMeta (route) {
							 | 
						||
| 
								 | 
							
								  // buildPrettyMeta function must return an object, which will be parsed into key/value pairs for display
							 | 
						||
| 
								 | 
							
								  if (!route) return {}
							 | 
						||
| 
								 | 
							
								  if (!route.store) return {}
							 | 
						||
| 
								 | 
							
								  return Object.assign({}, route.store)
							 | 
						||
| 
								 | 
							
								}
							 |