swagger-router
Advanced tools
Comparing version 0.5.1 to 0.5.2
259
lib/node.js
"use strict"; | ||
var P = require('bluebird'); | ||
const P = require('bluebird'); | ||
// Work around recursive structure in ** terminal nodes | ||
function printableValue(value) { | ||
const res = {}; | ||
if (!value || !(value instanceof Object)) { | ||
return value; | ||
} | ||
Object.keys(value).forEach((key) => { | ||
const val = value[key]; | ||
if (key === 'methods') { | ||
const newMethods = {}; | ||
Object.keys(val).forEach((method) => { | ||
newMethods[method] = `<${val[method].name}>`; | ||
}); | ||
res.methods = newMethods; | ||
} else if (key !== 'specRoot') { | ||
// Omit the specRoot, as it tends to be huge & contains reference | ||
// circles. | ||
res[key] = val; | ||
} | ||
}); | ||
return res; | ||
} | ||
const _keyPrefix = '/'; | ||
const _keyPrefixRegExp = /^\//; | ||
/* | ||
@@ -10,41 +36,37 @@ * A node in the lookup graph. | ||
*/ | ||
function Node(value) { | ||
// The value for a path ending on this node. Public property. | ||
this.value = value || null; | ||
class Node { | ||
constructor(value) { | ||
// The value for a path ending on this node. Public property. | ||
this.value = value || null; | ||
// Internal properties. | ||
this._children = {}; | ||
this._paramName = null; | ||
this._parent = null; | ||
} | ||
// Internal properties. | ||
this._children = {}; | ||
this._paramName = null; | ||
this._parent = null; | ||
} | ||
Node.prototype._keyPrefix = '/'; | ||
Node.prototype._keyPrefixRegExp = /^\//; | ||
Node.prototype.setChild = function(key, child) { | ||
var self = this; | ||
if (key.constructor === String) { | ||
this._children[this._keyPrefix + key] = child; | ||
} else if (key.name && key.pattern | ||
&& key.modifier !== '+' | ||
&& key.pattern.constructor === String) { | ||
// A named but plain key. | ||
child._paramName = key.name; | ||
this._children[this._keyPrefix + key.pattern] = child; | ||
} else if (key.modifier === '+') { | ||
child._paramName = key.name; | ||
this._children['**'] = child; | ||
} else { | ||
// Setting up a wildcard match | ||
child._paramName = key.name; | ||
this._children['*'] = child; | ||
setChild(key, child) { | ||
if (key.constructor === String) { | ||
this._children[_keyPrefix + key] = child; | ||
} else if (key.name && key.pattern | ||
&& key.modifier !== '+' | ||
&& key.pattern.constructor === String) { | ||
// A named but plain key. | ||
child._paramName = key.name; | ||
this._children[_keyPrefix + key.pattern] = child; | ||
} else if (key.modifier === '+') { | ||
child._paramName = key.name; | ||
this._children['**'] = child; | ||
} else { | ||
// Setting up a wildcard match | ||
child._paramName = key.name; | ||
this._children['*'] = child; | ||
} | ||
} | ||
}; | ||
Node.prototype.getChild = function(segment, params) { | ||
if (segment.constructor === String) { | ||
// Fast path | ||
var res = this._children[this._keyPrefix + segment]; | ||
if (!res) { | ||
if (segment !== '') { | ||
getChild(segment, params) { | ||
if (segment.constructor === String) { | ||
// Fast path | ||
let res = this._children[_keyPrefix + segment]; | ||
if (!res && segment !== '') { | ||
// Fall back to the wildcard match, but only if the segment is | ||
@@ -57,3 +79,3 @@ // non-empty. | ||
if (params[res._paramName]) { | ||
params[res._paramName] += '/' + encodeURIComponent(segment); | ||
params[res._paramName] += `/${encodeURIComponent(segment)}`; | ||
} else { | ||
@@ -66,67 +88,62 @@ params[res._paramName] = encodeURIComponent(segment); | ||
} | ||
} | ||
if (res) { | ||
if (res._paramName) { | ||
params[res._paramName] = segment; | ||
if (res) { | ||
if (res._paramName) { | ||
params[res._paramName] = segment; | ||
} | ||
return res; | ||
} else { | ||
return null; | ||
} | ||
return res; | ||
} else { | ||
return null; | ||
// Fall-back cases for internal use during tree construction. These cases | ||
// are never used for actual routing. | ||
} else if (segment.pattern) { | ||
// Unwrap the pattern | ||
return this.getChild(segment.pattern, params); | ||
} else if (this._children['*'] | ||
&& this._children['*']._paramName === segment.name) { | ||
// XXX: also compare modifier! | ||
return this._children['*'] || null; | ||
} | ||
} | ||
// Fall-back cases for internal use during tree construction. These cases | ||
// are never used for actual routing. | ||
} else if (segment.pattern) { | ||
// Unwrap the pattern | ||
return this.getChild(segment.pattern, params); | ||
} else if (this._children['*'] | ||
&& this._children['*']._paramName === segment.name) { | ||
// XXX: also compare modifier! | ||
return this._children['*'] || null; | ||
hasChildren() { | ||
return Object.keys(this._children).length || this._children['*']; | ||
} | ||
}; | ||
Node.prototype.hasChildren = function() { | ||
return Object.keys(this._children).length || this._children['*']; | ||
}; | ||
keys() { | ||
if (this._children['*'] || this._children['**']) { | ||
return []; | ||
} else { | ||
const res = []; | ||
Object.keys(this._children).forEach((key) => { | ||
// Only list '' if there are children (for paths like | ||
// /double//slash) | ||
if (key !== _keyPrefix || this._children[key].hasChildren()) { | ||
res.push(key.replace(_keyPrefixRegExp, '')); | ||
} | ||
}); | ||
return res.sort(); | ||
} | ||
} | ||
Node.prototype.keys = function() { | ||
var self = this; | ||
if (this._children['*'] || this._children['**']) { | ||
return []; | ||
} else { | ||
var res = []; | ||
Object.keys(this._children).forEach(function(key) { | ||
// Only list '' if there are children (for paths like | ||
// /double//slash) | ||
if (key !== self._keyPrefix || self._children[key].hasChildren()) { | ||
res.push(key.replace(self._keyPrefixRegExp, '')); | ||
} | ||
}); | ||
return res.sort(); | ||
// Shallow clone, allows sharing of subtrees in DAG | ||
clone() { | ||
const c = new Node(); | ||
c._children = this._children; | ||
c._paramName = this._paramName; | ||
return c; | ||
} | ||
}; | ||
// Shallow clone, allows sharing of subtrees in DAG | ||
Node.prototype.clone = function() { | ||
var c = new Node(); | ||
c._children = this._children; | ||
c._paramName = this._paramName; | ||
return c; | ||
}; | ||
// Call promise-returning fn for each node value, with the path to the value | ||
Node.prototype.visitAsync = function(fn, path) { | ||
path = path || []; | ||
var self = this; | ||
// First value, then each of the children (one by one) | ||
return fn(self.value, path) | ||
.then(function() { | ||
return P.resolve(Object.keys(self._children)) | ||
.each(function(childKey) { | ||
var segment = childKey.replace(/^\//, ''); | ||
var child = self._children[childKey]; | ||
if (child === self) { | ||
// Call promise-returning fn for each node value, with the path to the value | ||
visitAsync(fn, path) { | ||
path = path || []; | ||
// First value, then each of the children (one by one) | ||
return fn(this.value, path) | ||
.then(() => P.resolve(Object.keys(this._children)) | ||
.each((childKey) => { | ||
const segment = childKey.replace(/^\//, ''); | ||
const child = this._children[childKey]; | ||
if (child === this) { | ||
// Don't enter an infinite loop on ** | ||
@@ -137,46 +154,22 @@ return; | ||
} | ||
}); | ||
}); | ||
}; | ||
})); | ||
} | ||
// Work around recursive structure in ** terminal nodes | ||
function printableValue(value) { | ||
var res = {}; | ||
if (!value || !(value instanceof Object)) { | ||
return value; | ||
toJSON() { | ||
if (this._children['**'] === this) { | ||
return { | ||
value: printableValue(this.value), | ||
_children: '<recursive>', | ||
_paramName: this._paramName | ||
}; | ||
} else { | ||
return { | ||
value: printableValue(this.value), | ||
_children: this._children, | ||
_paramName: this._paramName | ||
}; | ||
} | ||
} | ||
Object.keys(value).forEach(function(key) { | ||
var val = value[key]; | ||
if (key === 'methods') { | ||
var newMethods = {}; | ||
Object.keys(val).forEach(function(method) { | ||
newMethods[method] = '<' + val[method].name + '>'; | ||
}); | ||
res.methods = newMethods; | ||
} else if (key !== 'specRoot') { | ||
// Omit the specRoot, as it tends to be huge & contains reference | ||
// circles. | ||
res[key] = val; | ||
} | ||
}); | ||
return res; | ||
} | ||
Node.prototype.toJSON = function() { | ||
if (this._children['**'] === this) { | ||
return { | ||
value: printableValue(this.value), | ||
_children: '<recursive>', | ||
_paramName: this._paramName | ||
}; | ||
} else { | ||
return { | ||
value: printableValue(this.value), | ||
_children: this._children, | ||
_paramName: this._paramName | ||
}; | ||
} | ||
}; | ||
module.exports = Node; |
"use strict"; | ||
var URI = require('./uri'); | ||
var url = require('url'); | ||
var TAssembly = require('tassembly'); | ||
var expressionCompiler = require('template-expression-compiler'); | ||
var utils = require('./utils'); | ||
const URI = require('./uri'); | ||
const TAssembly = require('tassembly'); | ||
const expressionCompiler = require('template-expression-compiler'); | ||
const utils = require('./utils'); | ||
var compilerOptions = { | ||
const compilerOptions = { | ||
ctxMap: { | ||
@@ -27,11 +26,11 @@ $: 'rm', | ||
expression = expression.replace(/\n/g, ' ').trim(); | ||
compilerOptions.modelPrefix = (part && 'rm.request.' + part) || 'm'; | ||
compilerOptions.modelPrefix = (part && `rm.request.${part}`) || 'm'; | ||
return expressionCompiler.parse(expression, compilerOptions); | ||
} | ||
var globalMethods = { | ||
default: function(val, defVal) { | ||
const globalMethods = { | ||
default(val, defVal) { | ||
return val || defVal; | ||
}, | ||
merge: function(destination, source) { | ||
merge(destination, source) { | ||
destination = destination || {}; | ||
@@ -45,4 +44,4 @@ source = source || {}; | ||
var result = Object.assign({}, destination); | ||
Object.keys(source).forEach(function(keyName) { | ||
const result = Object.assign({}, destination); | ||
Object.keys(source).forEach((keyName) => { | ||
if (result[keyName] === undefined) { | ||
@@ -54,3 +53,3 @@ result[keyName] = source[keyName]; | ||
}, | ||
strip: function(object, properties) { | ||
strip(object, properties) { | ||
if (typeof object !== 'object') { | ||
@@ -64,3 +63,3 @@ throw new Error('Illegal argument. ' + | ||
} else if (Array.isArray(properties)) { | ||
properties.forEach(function(prop) { | ||
properties.forEach((prop) => { | ||
delete object[prop]; | ||
@@ -75,3 +74,3 @@ }); | ||
filter: function(object, pred) { | ||
filter(object, pred) { | ||
if (typeof object !== 'object') { | ||
@@ -82,4 +81,4 @@ throw new Error('Illegal argument. ' + | ||
if (Array.isArray(pred)) { | ||
var res = {}; | ||
pred.forEach(function(key) { | ||
const res = {}; | ||
pred.forEach((key) => { | ||
res[key] = object[key]; | ||
@@ -97,7 +96,5 @@ }); | ||
requestTemplate: function(spec, options) { | ||
var tpl = new Template(spec, options); | ||
return function(model) { | ||
return tpl.expand(model); | ||
}; | ||
requestTemplate(spec, options) { | ||
const tpl = new Template(spec, options); | ||
return (model) => tpl.expand(model); | ||
}, | ||
@@ -111,3 +108,3 @@ | ||
*/ | ||
date: function(date, format) { | ||
date(date, format) { | ||
@@ -124,3 +121,3 @@ function isValidDate(d) { | ||
if (!isValidDate(date)) { | ||
var origDate = date; | ||
const origDate = date; | ||
@@ -136,6 +133,7 @@ if (typeof date === 'string' && /^\d+$/.test(date)) { | ||
if (!isValidDate(date)) { | ||
throw new Error('Invalid date: ' + origDate); | ||
throw new Error(`Invalid date: ${origDate}`); | ||
} | ||
} | ||
/* eslint-disable indent */ | ||
switch (format) { | ||
@@ -147,10 +145,11 @@ case 'rfc822': | ||
default: | ||
throw new Error('Unsupported date format: ' + format); | ||
throw new Error(`Unsupported date format: ${format}`); | ||
} | ||
/* eslint-enable indent */ | ||
}, | ||
// Private helpers | ||
_optionalPath: function(element) { | ||
_optionalPath(element) { | ||
if (element !== undefined) { | ||
return '/' + encodeURIComponent(element); | ||
return `/${encodeURIComponent(element)}`; | ||
} else { | ||
@@ -160,3 +159,3 @@ return ''; | ||
}, | ||
_encodeURIComponent: function(s) { | ||
_encodeURIComponent(s) { | ||
s = (s === undefined || s === null) ? '' : s; | ||
@@ -173,8 +172,9 @@ if (/[^\w_-]/.test(s)) { | ||
options = options || {}; | ||
var result = []; | ||
var templateNest = 0; | ||
var startIndex = 0; | ||
var currentTemplate; | ||
var inDoubleBrace = false; | ||
for (var index = 0; index < templateSpec.length; index++) { | ||
const result = []; | ||
let templateNest = 0; | ||
let startIndex = 0; | ||
let currentTemplate; | ||
let inDoubleBrace = false; | ||
let index; | ||
for (index = 0; index < templateSpec.length; index++) { | ||
if (templateSpec[index] === '{') { | ||
@@ -202,5 +202,5 @@ if (templateNest === 0) { // We are either entering a new template | ||
} else if (/^\//.test(currentTemplate)) { | ||
currentTemplate = '_optionalPath(' + currentTemplate.substring(1) + ')'; | ||
currentTemplate = `_optionalPath(${currentTemplate.substring(1)})`; | ||
} else { | ||
currentTemplate = '_encodeURIComponent(' + currentTemplate + ')'; | ||
currentTemplate = `_encodeURIComponent(${currentTemplate})`; | ||
} | ||
@@ -214,3 +214,3 @@ } | ||
var compiledExpression = compileExpression(currentTemplate, options.part); | ||
const compiledExpression = compileExpression(currentTemplate, options.part); | ||
result.push(['raw', compiledExpression]); | ||
@@ -232,10 +232,11 @@ startIndex = index + 1; | ||
function compileTAssembly(template, reqPart, globals) { | ||
var res = ''; | ||
var stringCb = function(bit) { | ||
let resolveTemplate; | ||
let res = ''; | ||
const stringCb = (bit) => { | ||
if (bit !== undefined && bit !== null) { | ||
res += '' + bit; | ||
res += `${bit}`; | ||
} | ||
}; | ||
var options = { | ||
const options = { | ||
nestedTemplate: true, | ||
@@ -246,4 +247,5 @@ errorHandler: null, | ||
}; | ||
try { | ||
var resolveTemplate = TAssembly.compile(template, options); | ||
resolveTemplate = TAssembly.compile(template, options); | ||
} catch (e) { | ||
@@ -255,4 +257,4 @@ e.template = template; | ||
return function(context) { | ||
var childContext = { | ||
return (context) => { | ||
const childContext = { | ||
rc: context.rc, | ||
@@ -268,3 +270,3 @@ rm: context.rm, | ||
resolveTemplate(childContext); | ||
var value = res; | ||
const value = res; | ||
res = ''; // Prepare for the next request. | ||
@@ -278,3 +280,3 @@ return value; | ||
// a static string & a variable substitution. | ||
var simpleTemplate = new RegExp('^(?:\\/(?:[a-zA-Z_\\.-]+|' | ||
const simpleTemplate = new RegExp('^(?:\\/(?:[a-zA-Z_\\.-]+|' | ||
// Sequence of plain path segments (above) or simple templated | ||
@@ -296,14 +298,10 @@ // segments (below). | ||
if (simpleTemplate.test(uri) && uri.indexOf('{') >= 0) { | ||
var pathTemplate = new URI(uri, {}, true); | ||
return function(context) { | ||
return pathTemplate.expand(context.rm.request.params); | ||
}; | ||
const pathTemplate = new URI(uri, {}, true); | ||
return (context) => pathTemplate.expand(context.rm.request.params); | ||
} else if (/\{/.test(uri)) { | ||
var tassemblyTemplate = splitAndPrepareTAssemblyTemplate(uri); | ||
const tassemblyTemplate = splitAndPrepareTAssemblyTemplate(uri); | ||
// console.log('tass', spec.uri, tassemblyTemplate); | ||
return compileTAssembly(tassemblyTemplate, 'params', globals); | ||
} else { | ||
return function(context) { | ||
return uri; | ||
}; | ||
return () => uri; | ||
} | ||
@@ -319,4 +317,4 @@ } | ||
if (subSpec && subSpec.constructor === Object) { | ||
var res = {}; | ||
Object.keys(subSpec).forEach(function(key) { | ||
const res = {}; | ||
Object.keys(subSpec).forEach((key) => { | ||
res[key] = replaceComplexTemplates(part, subSpec[key], globals); | ||
@@ -326,9 +324,7 @@ }); | ||
} else if (Array.isArray(subSpec)) { | ||
return subSpec.map(function(elem) { | ||
return replaceComplexTemplates(part, elem, globals); | ||
}); | ||
return subSpec.map((elem) => replaceComplexTemplates(part, elem, globals)); | ||
} else if (subSpec && subSpec.constructor === String || subSpec === '') { | ||
if (/\{[^\}]+\}/.test(subSpec)) { | ||
// There is a template, now we need to check it for special stuff we replace | ||
var tAssemblyTemplates = splitAndPrepareTAssemblyTemplate(subSpec, { part: part }); | ||
const tAssemblyTemplates = splitAndPrepareTAssemblyTemplate(subSpec, { part }); | ||
if (tAssemblyTemplates.length === 1 | ||
@@ -342,16 +338,14 @@ && tAssemblyTemplates[0].length === 2 | ||
// Compile a function | ||
var resolver = compileTAssembly(tAssemblyTemplates, part, globals); | ||
const resolver = compileTAssembly(tAssemblyTemplates, part, globals); | ||
// Replace the complex template with a function call | ||
var fnName = 'fn_' + globals._i++; | ||
const fnName = `fn_${globals._i++}`; | ||
globals[fnName] = resolver; | ||
return 'rc.g.' + fnName + '(c)'; | ||
return `rc.g.${fnName}(c)`; | ||
} | ||
} else { | ||
// If it's not templated - wrap it into braces to let tassembly add it | ||
return "'" + subSpec + "'"; | ||
return `'${subSpec}'`; | ||
} | ||
} else { | ||
// Other literals: Number, booleans | ||
return subSpec; | ||
} | ||
// Other literals: Number, booleans | ||
return subSpec; | ||
@@ -378,71 +372,72 @@ } | ||
*/ | ||
function Template(origSpec, globalsInit) { | ||
var self = this; | ||
var globals = Object.assign({}, globalMethods, globalsInit); | ||
var spec = _cloneSpec(origSpec); | ||
globals._i = 0; | ||
class Template { | ||
constructor(origSpec, globalsInit) { | ||
const globals = Object.assign({}, globalMethods, globalsInit); | ||
let spec = _cloneSpec(origSpec); | ||
globals._i = 0; | ||
if (typeof spec === 'string') { | ||
spec = replaceComplexTemplates(undefined, spec, globals); | ||
} else { | ||
Object.keys(spec).forEach(function(part) { | ||
if (part === 'uri') { | ||
globals._uri = createURIResolver(spec.uri, globals); | ||
spec.uri = 'rc.g._uri(c)'; | ||
} else { | ||
spec[part] = replaceComplexTemplates(part, spec[part], globals); | ||
} | ||
}); | ||
} | ||
var completeTAssemblyTemplate = expressionCompiler.stringify(spec); | ||
// console.log(origSpec, completeTAssemblyTemplate); | ||
var res = null; | ||
var objectCb = function(bit) { | ||
if (res === null) { | ||
res = bit; | ||
if (typeof spec === 'string') { | ||
spec = replaceComplexTemplates(undefined, spec, globals); | ||
} else { | ||
Object.keys(spec).forEach((part) => { | ||
if (part === 'uri') { | ||
globals._uri = createURIResolver(spec.uri, globals); | ||
spec.uri = 'rc.g._uri(c)'; | ||
} else { | ||
spec[part] = replaceComplexTemplates(part, spec[part], globals); | ||
} | ||
}); | ||
} | ||
}; | ||
var resolver; | ||
try { | ||
resolver = TAssembly.compile([['raw', completeTAssemblyTemplate]], { | ||
nestedTemplate: true, | ||
globals: globals, | ||
cb: objectCb, | ||
errorHandler: null, | ||
}); | ||
} catch (e) { | ||
e.spec = origSpec; | ||
e.tassembly = completeTAssemblyTemplate; | ||
throw e; | ||
} | ||
const completeTAssemblyTemplate = expressionCompiler.stringify(spec); | ||
// console.log(origSpec, completeTAssemblyTemplate); | ||
let res = null; | ||
const objectCb = (bit) => { | ||
if (res === null) { | ||
res = bit; | ||
} | ||
}; | ||
var c = { | ||
rc: null, | ||
rm: null, | ||
g: globals, | ||
cb: objectCb, | ||
m: null, | ||
}; | ||
c.rc = c; | ||
self.expand = function(m) { | ||
c.rm = m; | ||
c.m = m; | ||
let resolver; | ||
try { | ||
resolver(c); | ||
resolver = TAssembly.compile([['raw', completeTAssemblyTemplate]], { | ||
nestedTemplate: true, | ||
globals, | ||
cb: objectCb, | ||
errorHandler: null, | ||
}); | ||
} catch (e) { | ||
e.expression_tassembly = completeTAssemblyTemplate; | ||
e.expression_spec = origSpec; | ||
res = null; | ||
e.spec = origSpec; | ||
e.tassembly = completeTAssemblyTemplate; | ||
throw e; | ||
} | ||
var ret = res; | ||
res = null; | ||
// ensure a reasonable fallback for ret.method | ||
if (ret && ret.hasOwnProperty('method')) { | ||
ret.method = ret.method || 'get'; | ||
} | ||
return ret; | ||
}; | ||
const c = { | ||
rc: null, | ||
rm: null, | ||
g: globals, | ||
cb: objectCb, | ||
m: null, | ||
}; | ||
c.rc = c; | ||
this.expand = (m) => { | ||
c.rm = m; | ||
c.m = m; | ||
try { | ||
resolver(c); | ||
} catch (e) { | ||
e.expression_tassembly = completeTAssemblyTemplate; | ||
e.expression_spec = origSpec; | ||
res = null; | ||
throw e; | ||
} | ||
const ret = res; | ||
res = null; | ||
// ensure a reasonable fallback for ret.method | ||
if (ret && {}.hasOwnProperty.call(ret, 'method')) { | ||
ret.method = ret.method || 'get'; | ||
} | ||
return ret; | ||
}; | ||
} | ||
} | ||
@@ -449,0 +444,0 @@ |
"use strict"; | ||
var URI = require('./uri'); | ||
var Node = require('./node'); | ||
var utils = require('./utils'); | ||
const URI = require('./uri'); | ||
const Node = require('./node'); | ||
const utils = require('./utils'); | ||
/* | ||
* The main router object | ||
*/ | ||
function Router(options) { | ||
// Options: | ||
// - specHandler(spec) -> spec' | ||
// - pathHandler(pathSpec) -> pathSpec' | ||
this._options = options || {}; | ||
this._root = new Node(); | ||
} | ||
class Router { | ||
constructor(options) { | ||
// Options: | ||
// - specHandler(spec) -> spec' | ||
// - pathHandler(pathSpec) -> pathSpec' | ||
this._options = options || {}; | ||
this._root = new Node(); | ||
} | ||
// XXX modules: variant that builds a prefix tree from a path array, but pass | ||
// in a spec instead of a value | ||
Router.prototype._buildTree = function(path, value) { | ||
var node = new Node(); | ||
if (path.length) { | ||
var segment = path[0]; | ||
if (segment.modifier === '+') { | ||
// Set up a recursive match and end the traversal | ||
var recursionNode = new Node(); | ||
recursionNode.value = value; | ||
recursionNode.setChild(segment, recursionNode); | ||
node.setChild(segment, recursionNode); | ||
// XXX modules: variant that builds a prefix tree from a path array, but pass | ||
// in a spec instead of a value | ||
_buildTree(path, value) { | ||
const node = new Node(); | ||
if (path.length) { | ||
const segment = path[0]; | ||
if (segment.modifier === '+') { | ||
// Set up a recursive match and end the traversal | ||
const recursionNode = new Node(); | ||
recursionNode.value = value; | ||
recursionNode.setChild(segment, recursionNode); | ||
node.setChild(segment, recursionNode); | ||
} else { | ||
const subTree = this._buildTree(path.slice(1), value); | ||
node.setChild(segment, subTree); | ||
if (segment.modifier === '/') { | ||
// Set the value for each optional path segment ({/foo}) | ||
node.value = value; | ||
subTree.value = value; | ||
} | ||
} | ||
} else { | ||
var subTree = this._buildTree(path.slice(1), value); | ||
node.setChild(segment, subTree); | ||
if (segment.modifier === '/') { | ||
// Set the value for each optional path segment ({/foo}) | ||
node.value = value; | ||
subTree.value = value; | ||
} | ||
node.value = value; | ||
} | ||
} else { | ||
node.value = value; | ||
return node; | ||
} | ||
return node; | ||
}; | ||
specToTree(spec) { | ||
const root = new Node(); | ||
Object.keys(spec.paths).forEach((pathPattern) => { | ||
const path = utils.parsePath(pathPattern, true); | ||
this._extend(path, root, spec.paths[pathPattern]); | ||
}); | ||
return root; | ||
} | ||
Router.prototype.specToTree = function(spec) { | ||
var root = new Node(); | ||
var self = this; | ||
Object.keys(spec.paths).forEach(function(pathPattern) { | ||
var path = utils.parsePath(pathPattern, true); | ||
self._extend(path, root, spec.paths[pathPattern]); | ||
}); | ||
return root; | ||
}; | ||
setTree(tree) { | ||
this._root = tree; | ||
} | ||
Router.prototype.setTree = function(tree) { | ||
this._root = tree; | ||
}; | ||
delSpec() { | ||
// Possible implementation: | ||
// - Perform a *recursive* lookup for each leaf node. | ||
// - Walk up the tree and remove nodes as long as `.hasChildren()` is | ||
// false. | ||
// This will work okay in a tree, but would clash with subtree sharing in | ||
// a graph. We should perform some benchmarks to see if subtree sharing is | ||
// worth it. Until then we probably don't need spec deletion anyway, as we | ||
// can always re-build the entire router from scratch. | ||
throw new Error("Not implemented"); | ||
} | ||
Router.prototype.delSpec = function delSpec(spec, prefix) { | ||
// Possible implementation: | ||
// - Perform a *recursive* lookup for each leaf node. | ||
// - Walk up the tree and remove nodes as long as `.hasChildren()` is | ||
// false. | ||
// This will work okay in a tree, but would clash with subtree sharing in | ||
// a graph. We should perform some benchmarks to see if subtree sharing is | ||
// worth it. Until then we probably don't need spec deletion anyway, as we | ||
// can always re-build the entire router from scratch. | ||
throw new Error("Not implemented"); | ||
}; | ||
// Extend an existing route tree with a new path by walking the existing tree | ||
// and inserting new subtrees at the desired location. | ||
Router.prototype._extend = function route(path, node, value) { | ||
var params = {}; | ||
for (var i = 0; i < path.length; i++) { | ||
var nextNode = node.getChild(path[i], params); | ||
if (!nextNode || !nextNode.getChild) { | ||
// Found our extension point | ||
node.setChild(path[i], this._buildTree(path.slice(i + 1), value)); | ||
return; | ||
} else { | ||
node = nextNode; | ||
// Extend an existing route tree with a new path by walking the existing tree | ||
// and inserting new subtrees at the desired location. | ||
_extend(path, node, value) { | ||
const params = {}; | ||
for (let i = 0; i < path.length; i++) { | ||
const nextNode = node.getChild(path[i], params); | ||
if (!nextNode || !nextNode.getChild) { | ||
// Found our extension point | ||
node.setChild(path[i], this._buildTree(path.slice(i + 1), value)); | ||
return; | ||
} else { | ||
node = nextNode; | ||
} | ||
} | ||
if (value !== undefined) { | ||
node.value = value; | ||
} | ||
} | ||
if (value !== undefined) { | ||
node.value = value; | ||
} | ||
}; | ||
// Lookup worker. | ||
Router.prototype._lookup = function route(path, node) { | ||
var params = {}; | ||
var prevNode; | ||
var permissions = []; | ||
var filters = []; | ||
for (var i = 0; i < path.length; i++) { | ||
if (!node || !node.getChild) { | ||
return null; | ||
// Lookup worker. | ||
_lookup(path, node) { | ||
const params = {}; | ||
let prevNode; | ||
let permissions = []; | ||
let filters = []; | ||
for (let i = 0; i < path.length; i++) { | ||
if (!node || !node.getChild) { | ||
return null; | ||
} | ||
prevNode = node; | ||
if (node.value) { | ||
if (node.value.security) { | ||
permissions = permissions.concat(node.value.security); | ||
} | ||
if (node.value.filters) { | ||
filters = filters.concat(node.value.filters); | ||
} | ||
} | ||
node = node.getChild(path[i], params); | ||
} | ||
prevNode = node; | ||
if (node.value) { | ||
if (node && node.value) { | ||
if (node.value.security) { | ||
@@ -109,65 +120,55 @@ permissions = permissions.concat(node.value.security); | ||
} | ||
node = node.getChild(path[i], params); | ||
} | ||
if (node && node.value) { | ||
if (node.value.security) { | ||
permissions = permissions.concat(node.value.security); | ||
if (node || prevNode && path[path.length - 1] === '') { | ||
if (path[path.length - 1] === '') { | ||
// Pass in a listing | ||
params._ls = prevNode.keys(); | ||
} | ||
return { | ||
params, | ||
value: (node && node.value || null), | ||
permissions, | ||
filters | ||
}; | ||
} else { | ||
return null; | ||
} | ||
if (node.value.filters) { | ||
filters = filters.concat(node.value.filters); | ||
} | ||
} | ||
if (node || prevNode && path[path.length - 1] === '') { | ||
if (path[path.length - 1] === '') { | ||
// Pass in a listing | ||
params._ls = prevNode.keys(); | ||
/* | ||
* Look up a path in the router, and return either null or the configured | ||
* object. | ||
* | ||
* @param {string|array} path | ||
* @return {null|object} with object being | ||
* { | ||
* params: { | ||
* someParam: 'pathcomponent' | ||
* }, | ||
* value: theValue, | ||
* permissions: [somePermission] | ||
* } | ||
*/ | ||
lookup(path) { | ||
if (!path) { | ||
throw new Error('Path expected!'); | ||
} else if (path.constructor === String) { | ||
path = utils.parsePath(path); | ||
} else if (path.constructor === URI) { | ||
path = path.path; | ||
} | ||
return { | ||
params: params, | ||
value: (node && node.value || null), | ||
permissions: permissions, | ||
filters: filters | ||
}; | ||
} else { | ||
return null; | ||
const res = this._lookup(path, this._root); | ||
if (res) { | ||
return { | ||
params: res.params, | ||
value: res.value, | ||
permissions: res.permissions, | ||
filters: res.filters | ||
}; | ||
} else { | ||
return res; | ||
} | ||
} | ||
}; | ||
} | ||
/* | ||
* Look up a path in the router, and return either null or the configured | ||
* object. | ||
* | ||
* @param {string|array} path | ||
* @return {null|object} with object being | ||
* { | ||
* params: { | ||
* someParam: 'pathcomponent' | ||
* }, | ||
* value: theValue, | ||
* permissions: [somePermission] | ||
* } | ||
*/ | ||
Router.prototype.lookup = function route(path) { | ||
if (!path) { | ||
throw new Error('Path expected!'); | ||
} else if (path.constructor === String) { | ||
path = utils.parsePath(path); | ||
} else if (path.constructor === URI) { | ||
path = path.path; | ||
} | ||
var res = this._lookup(path, this._root); | ||
if (res) { | ||
return { | ||
params: res.params, | ||
value: res.value, | ||
permissions: res.permissions, | ||
filters: res.filters | ||
}; | ||
} else { | ||
return res; | ||
} | ||
}; | ||
module.exports = Router; | ||
module.exports = Router; |
344
lib/uri.js
"use strict"; | ||
var utils = require('./utils'); | ||
const utils = require('./utils'); | ||
/** | ||
@@ -15,206 +16,205 @@ * Represents a URI object which can optionally contain and | ||
*/ | ||
function URI(uri, params, asPattern) { | ||
// Initialise all fields to make an object monomorphic | ||
this.params = params || {}; | ||
this.protoHost = null; | ||
this.path = null; | ||
this._pathMetadata = {}; | ||
class URI { | ||
constructor(uri, params, asPattern) { | ||
// Initialise all fields to make an object monomorphic | ||
this.params = params || {}; | ||
this.protoHost = null; | ||
this.path = null; | ||
this._pathMetadata = {}; | ||
if (typeof uri === 'string') { | ||
var protoHostMatch = /^[^\/]+:(?:\/\/)?[^\/]+/.exec(uri); | ||
if (protoHostMatch) { | ||
this.protoHost = protoHostMatch[0]; | ||
uri = uri.substring(this.protoHost.length); | ||
} | ||
this.path = utils.parsePath(uri, asPattern); | ||
} else if (Array.isArray(uri)) { | ||
if (!asPattern) { | ||
// Ensure that all path segments are strings | ||
for (var i = 0; i < uri.length; i++) { | ||
uri[i] = '' + uri[i]; | ||
if (typeof uri === 'string') { | ||
const protoHostMatch = /^[^\/]+:(?:\/\/)?[^\/]+/.exec(uri); | ||
if (protoHostMatch) { | ||
this.protoHost = protoHostMatch[0]; | ||
uri = uri.substring(this.protoHost.length); | ||
} | ||
this.path = utils.parsePath(uri, asPattern); | ||
} else if (Array.isArray(uri)) { | ||
if (!asPattern) { | ||
// Ensure that all path segments are strings | ||
for (let i = 0; i < uri.length; i++) { | ||
uri[i] = `${uri[i]}`; | ||
} | ||
} | ||
this.path = uri; | ||
} else if (uri && uri.constructor === URI) { | ||
this.protoHost = uri.protoHost; | ||
// this.path is considered immutable, so can be shared with other URI | ||
// instances | ||
this.path = uri.path; | ||
} else if (uri !== '') { | ||
throw new Error(`Invalid path passed into URI constructor: ${uri}`); | ||
} | ||
this.path = uri; | ||
} else if (uri && uri.constructor === URI) { | ||
this.protoHost = uri.protoHost; | ||
// this.path is considered immutable, so can be shared with other URI | ||
// instances | ||
this.path = uri.path; | ||
} else if (uri !== '') { | ||
throw new Error('Invalid path passed into URI constructor: ' + uri); | ||
} | ||
} | ||
/** | ||
* Builds and returns the full, bounded string path for this URI object | ||
* | ||
* @return {String} the complete path of this URI object | ||
* @param {object} options { | ||
* format {string} Either 'simplePattern' or 'fullPattern'. [optional] | ||
* params {object} parameters to use during serialization | ||
* } | ||
* @return {string} URI path | ||
*/ | ||
URI.prototype.toString = function(options) { | ||
// b/c | ||
if (!options || options.constructor === String) { | ||
options = { format: options }; | ||
} | ||
var params = options.params || this.params; | ||
var uriStr = this.protoHost || ''; | ||
for (var i = 0; i < this.path.length; i++) { | ||
var segment = this.path[i]; | ||
if (segment && segment.constructor === Object) { | ||
var segmentValue = params[segment.name]; | ||
if (segmentValue === undefined) { | ||
segmentValue = segment.pattern; | ||
} | ||
/** | ||
* Builds and returns the full, bounded string path for this URI object | ||
* | ||
* @return {String} the complete path of this URI object | ||
* @param {object} options { | ||
* format {string} Either 'simplePattern' or 'fullPattern'. [optional] | ||
* params {object} parameters to use during serialization | ||
* } | ||
* @return {string} URI path | ||
*/ | ||
toString(options) { | ||
// b/c | ||
if (!options || options.constructor === String) { | ||
options = { format: options }; | ||
} | ||
const params = options.params || this.params; | ||
let uriStr = this.protoHost || ''; | ||
for (let i = 0; i < this.path.length; i++) { | ||
const segment = this.path[i]; | ||
if (segment && segment.constructor === Object) { | ||
let segmentValue = params[segment.name]; | ||
if (segmentValue === undefined) { | ||
segmentValue = segment.pattern; | ||
} | ||
if (segmentValue !== undefined) { | ||
if (!options.format || options.format === 'simplePattern' || !segment.name) { | ||
if (segment.modifier === '+') { | ||
uriStr += '/' + segmentValue; | ||
if (segmentValue !== undefined) { | ||
if (!options.format || options.format === 'simplePattern' || !segment.name) { | ||
if (segment.modifier === '+') { | ||
uriStr += `/${segmentValue}`; | ||
} else { | ||
// Normal mode | ||
uriStr += `/${encodeURIComponent(segmentValue)}`; | ||
} | ||
} else { | ||
// Normal mode | ||
uriStr += '/' + encodeURIComponent(segmentValue); | ||
uriStr += `/{${segment.modifier || ''}` | ||
+ `${encodeURIComponent(segment.name)}:` | ||
+ `${encodeURIComponent(segmentValue)}}`; | ||
} | ||
} else if (options.format && !segment.modifier) { | ||
uriStr += `/{${encodeURIComponent(segment.name)}}`; | ||
} else if (options.format) { | ||
uriStr += `{${segment.modifier || ''}${encodeURIComponent(segment.name)}}`; | ||
} else { | ||
uriStr += '/{' + (segment.modifier || '') | ||
+ encodeURIComponent(segment.name) + ':' | ||
+ encodeURIComponent(segmentValue) + '}'; | ||
if (segment.modifier === '+') { | ||
// Add trailing slash | ||
uriStr += '/'; | ||
} | ||
// Omit optional segment & return | ||
return uriStr; | ||
} | ||
} else if (options.format && !segment.modifier) { | ||
uriStr += '/{' + encodeURIComponent(segment.name) + '}'; | ||
} else if (options.format) { | ||
uriStr += '{' + (segment.modifier || '') | ||
+ encodeURIComponent(segment.name) + '}'; | ||
} else if (this._pathMetadata | ||
&& this._pathMetadata[i] | ||
&& this._pathMetadata[i] === '+') { | ||
uriStr += `/${utils.encodeReserved(segment)}`; | ||
} else { | ||
if (segment.modifier === '+') { | ||
// Add trailing slash | ||
uriStr += '/'; | ||
} | ||
// Omit optional segment & return | ||
return uriStr; | ||
uriStr += `/${encodeURIComponent(segment)}`; | ||
} | ||
} else if (this._pathMetadata | ||
&& this._pathMetadata[i] | ||
&& this._pathMetadata[i] === '+') { | ||
uriStr += '/' + utils.encodeReserved(segment); | ||
} else { | ||
uriStr += '/' + encodeURIComponent(segment); | ||
} | ||
return uriStr; | ||
} | ||
return uriStr; | ||
}; | ||
/** | ||
* Expand all parameters in the URI and return a new URI. | ||
* @param {object} params (optional) Parameters to use for expansion. Uses | ||
* URI-assigned parameters if not supplied. | ||
* @return {URI} | ||
*/ | ||
URI.prototype.expand = function(params) { | ||
if (!params) { | ||
params = this.params; | ||
} | ||
var res = []; | ||
var pathMetadata = {}; | ||
var uri; | ||
for (var i = 0; i < this.path.length; i++) { | ||
var segment = this.path[i]; | ||
if (segment && segment.constructor === Object) { | ||
var segmentValue = params[segment.name]; | ||
if (segmentValue === undefined) { | ||
segmentValue = segment.pattern; | ||
/** | ||
* Expand all parameters in the URI and return a new URI. | ||
* @param {object} params (optional) Parameters to use for expansion. Uses | ||
* URI-assigned parameters if not supplied. | ||
* @return {URI} | ||
*/ | ||
expand(params) { | ||
if (!params) { | ||
params = this.params; | ||
} | ||
let res = []; | ||
const pathMetadata = {}; | ||
for (let i = 0; i < this.path.length; i++) { | ||
const segment = this.path[i]; | ||
if (segment && segment.constructor === Object) { | ||
let segmentValue = params[segment.name]; | ||
if (segmentValue === undefined) { | ||
if (segment.modifier) { | ||
// Skip over this optional segment. | ||
continue; | ||
} else { | ||
segmentValue = ''; | ||
segmentValue = segment.pattern; | ||
if (segmentValue === undefined) { | ||
if (segment.modifier) { | ||
// Skip over this optional segment. | ||
continue; | ||
} else { | ||
segmentValue = ''; | ||
} | ||
} | ||
} | ||
} | ||
if (segment.modifier === '+') { | ||
// Res will become a path array, so we must split path elements | ||
var oldResLen = res.length; | ||
res = res.concat(('' + segmentValue).split('/')); | ||
// Set up metadata for all path elements under {+} template | ||
for (var j = oldResLen; j < res.length; j++) { | ||
pathMetadata[j] = '+'; | ||
if (segment.modifier === '+') { | ||
// Res will become a path array, so we must split path elements | ||
const oldResLen = res.length; | ||
res = res.concat((`${segmentValue}`).split('/')); | ||
// Set up metadata for all path elements under {+} template | ||
for (let j = oldResLen; j < res.length; j++) { | ||
pathMetadata[j] = '+'; | ||
} | ||
} else { | ||
res.push(`${segmentValue}`); | ||
} | ||
} else { | ||
res.push('' + segmentValue); | ||
res.push(segment); | ||
} | ||
} else { | ||
res.push(segment); | ||
} | ||
const uri = new URI(res); | ||
uri.protoHost = this.protoHost; | ||
// FIXME: handle this in the constructor! | ||
uri._pathMetadata = pathMetadata; | ||
return uri; | ||
} | ||
uri = new URI(res); | ||
uri.protoHost = this.protoHost; | ||
// FIXME: handle this in the constructor! | ||
uri._pathMetadata = pathMetadata; | ||
return uri; | ||
}; | ||
/** | ||
* Checks if the URI starts with the given path prefix | ||
* | ||
* @param {String|URI} pathOrURI the prefix path to check for | ||
* @return {Boolean} whether this URI starts with the given prefix path | ||
*/ | ||
URI.prototype.startsWith = function(pathOrURI) { | ||
var uri; | ||
if (!pathOrURI) { | ||
return true; | ||
} | ||
if (pathOrURI.constructor === URI) { | ||
uri = pathOrURI; | ||
} else { | ||
uri = new URI(pathOrURI); | ||
} | ||
// if our URI is shorter than the one we are | ||
// comparing to, it doesn't start with that prefix | ||
if (this.path.length < uri.path.length) { | ||
return false; | ||
} | ||
// check each component | ||
for (var idx = 0; idx < uri.path.length; idx++) { | ||
var mySeg = this.path[idx]; | ||
var otherSeg = uri.path[idx]; | ||
if (mySeg.constructor === Object && otherSeg.constructor === Object) { | ||
// both path are named variables | ||
// nothing to do | ||
continue; | ||
} else if (mySeg.constructor === Object) { | ||
// we have a named variable, but there is a string | ||
// given in the prefix | ||
if (mySeg.pattern && mySeg.pattern !== otherSeg) { | ||
// they differ | ||
/** | ||
* Checks if the URI starts with the given path prefix | ||
* | ||
* @param {String|URI} pathOrURI the prefix path to check for | ||
* @return {Boolean} whether this URI starts with the given prefix path | ||
*/ | ||
startsWith(pathOrURI) { | ||
let uri; | ||
if (!pathOrURI) { | ||
return true; | ||
} | ||
if (pathOrURI.constructor === URI) { | ||
uri = pathOrURI; | ||
} else { | ||
uri = new URI(pathOrURI); | ||
} | ||
// if our URI is shorter than the one we are | ||
// comparing to, it doesn't start with that prefix | ||
if (this.path.length < uri.path.length) { | ||
return false; | ||
} | ||
// check each component | ||
for (let idx = 0; idx < uri.path.length; idx++) { | ||
const mySeg = this.path[idx]; | ||
const otherSeg = uri.path[idx]; | ||
if (mySeg.constructor === Object && otherSeg.constructor === Object) { | ||
// both path are named variables | ||
// nothing to do | ||
continue; | ||
} else if (mySeg.constructor === Object) { | ||
// we have a named variable, but there is a string | ||
// given in the prefix | ||
if (mySeg.pattern && mySeg.pattern !== otherSeg) { | ||
// they differ | ||
return false; | ||
} | ||
} else if (otherSeg.constructor === Object) { | ||
// we have a fixed string, but a variable has been | ||
// given in the prefix - nothing to do | ||
continue; | ||
} else if (mySeg !== otherSeg) { | ||
// both are strings, but they differ | ||
return false; | ||
} | ||
} else if (otherSeg.constructor === Object) { | ||
// we have a fixed string, but a variable has been | ||
// given in the prefix - nothing to do | ||
continue; | ||
} else if (mySeg !== otherSeg) { | ||
// both are strings, but they differ | ||
return false; | ||
} | ||
// ok, no differences found | ||
return true; | ||
} | ||
// ok, no differences found | ||
return true; | ||
}; | ||
// For util.inspect, console.log & co | ||
inspect() { | ||
// Quote the string | ||
return JSON.stringify(this.toString()); | ||
} | ||
} | ||
// For JSON.stringify | ||
URI.prototype.toJSON = URI.prototype.toString; | ||
// For util.inspect, console.log & co | ||
URI.prototype.inspect = function() { | ||
// Quote the string | ||
return JSON.stringify(this.toString()); | ||
}; | ||
module.exports = URI; |
"use strict"; | ||
var url = require('url'); | ||
const utils = {}; | ||
var utils = {}; | ||
function robustDecodeURIComponent(uri) { | ||
@@ -14,6 +12,6 @@ if (!/%/.test(uri)) { | ||
} catch (e) { | ||
return uri.replace(/(%[0-9a-fA-F][0-9a-fA-F])+/g, function(m) { | ||
return uri.replace(/(%[0-9a-fA-F][0-9a-fA-F])+/g, (m) => { | ||
try { | ||
return decodeURIComponent(m); | ||
} catch (e) { | ||
} catch (error) { | ||
return m; | ||
@@ -27,8 +25,10 @@ } | ||
// jscs:disable | ||
var splitRe = /(\/)(?:\{([\+])?([^:\}\/]+)(?::([^}]+))?\}|([^\/\{]*))|(?:{([\/\+]))([^:\}\/]+)(?::([^}]+))?\}/g; | ||
/* eslint-disable max-len */ | ||
const splitRe = /(\/)(?:\{([\+])?([^:\}\/]+)(?::([^}]+))?\}|([^\/\{]*))|(?:{([\/\+]))([^:\}\/]+)(?::([^}]+))?\}/g; | ||
/* eslint-enable max-len */ | ||
// jscs:enable | ||
function parsePattern(pattern) { | ||
var res = []; | ||
const res = []; | ||
splitRe.lastIndex = 0; | ||
var m; | ||
let m; | ||
do { | ||
@@ -67,3 +67,3 @@ m = splitRe.exec(pattern); | ||
// Parse a path or pattern | ||
utils.parsePath = function(path, isPattern) { | ||
utils.parsePath = (path, isPattern) => { | ||
if (Array.isArray(path)) { | ||
@@ -75,5 +75,5 @@ return path; | ||
} | ||
var bits = path.split('/'); | ||
const bits = path.split('/'); | ||
if (/%/.test(path)) { | ||
for (var i = 0; i < bits.length; i++) { | ||
for (let i = 0; i < bits.length; i++) { | ||
if (/%/.test(bits[i])) { | ||
@@ -90,3 +90,3 @@ bits[i] = robustDecodeURIComponent(bits[i]); | ||
var unescapes = { | ||
const unescapes = { | ||
'%5B': '[', | ||
@@ -105,4 +105,4 @@ '%5D': ']', | ||
*/ | ||
utils.encodeReserved = function(string) { | ||
var res = encodeURI(string); | ||
utils.encodeReserved = (string) => { | ||
const res = encodeURI(string); | ||
if (!/[\[\]%]/.test(string)) { | ||
@@ -113,29 +113,23 @@ return res; | ||
// double percent escapes. | ||
return res.replace(/%5B|%5D|%25/gi, function(m) { | ||
return unescapes[m]; | ||
}); | ||
return res.replace(/%5B|%5D|%25/gi, (m) => unescapes[m]); | ||
} | ||
}; | ||
utils.toRFC822Date = function(date) { | ||
var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | ||
utils.toRFC822Date = (date) => { | ||
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | ||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; | ||
var days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | ||
const days = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; | ||
function numpad(x, digits) { | ||
var result = Math.floor(x).toString(); | ||
let result = Math.floor(x).toString(); | ||
while (result.length < digits) { | ||
result = '0' + result; | ||
result = `0${result}`; | ||
} | ||
return result; | ||
} | ||
return days[date.getUTCDay()] + ", " | ||
+ numpad(date.getUTCDate(), 2) + " " | ||
+ months[date.getUTCMonth()] + " " | ||
+ date.getUTCFullYear() + " " | ||
+ numpad(date.getUTCHours(), 2) + ":" | ||
+ numpad(date.getUTCMinutes(), 2) + ":" | ||
+ numpad(date.getUTCSeconds(), 2) + " +0000"; | ||
return `${days[date.getUTCDay()]}, ${numpad(date.getUTCDate(), 2)} ` | ||
+ `${months[date.getUTCMonth()]} ${date.getUTCFullYear()} ${numpad(date.getUTCHours(), 2)}` | ||
+ `:${numpad(date.getUTCMinutes(), 2)}:${numpad(date.getUTCSeconds(), 2)} +0000`; | ||
}; | ||
module.exports = utils; | ||
module.exports = utils; |
{ | ||
"name": "swagger-router", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"description": "An efficient swagger 2 based router with support for multiple APIs. For use in RESTBase.", | ||
@@ -39,9 +39,11 @@ "main": "index.js", | ||
"devDependencies": { | ||
"mocha": "^2.5.3", | ||
"mocha": "^3.1.0", | ||
"mocha-jshint": "^2.3.1", | ||
"istanbul": "^0.4.3", | ||
"istanbul": "^0.4.5", | ||
"mocha-lcov-reporter": "^1.2.0", | ||
"coveralls": "^2.11.9", | ||
"mocha-jscs": "^5.0.0" | ||
"coveralls": "^2.11.14", | ||
"mocha-jscs": "^5.0.1", | ||
"mocha-eslint":"^3.0.1", | ||
"eslint-config-node-services": "^1.0.4" | ||
} | ||
} |
@@ -7,1 +7,3 @@ 'use strict'; | ||
require('mocha-jscs')(); | ||
// Run eslint as part of normal testing | ||
require('mocha-eslint')([ './lib', './index.js' ]); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
93009
25
8
2132