Socket
Socket
Sign inDemoInstall

path-parser

Package Overview
Dependencies
Maintainers
1
Versions
48
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

path-parser - npm Package Compare versions

Comparing version 1.0.4 to 2.0.0

yarn.lock

16

CHANGELOG.md

@@ -0,1 +1,17 @@

<a name="2.0.0"></a>
# [2.0.0](https://github.com/troch/path-parser/compare/v1.0.4...v2.0.0) (2016-10-12)
### Features
* add a 'delimited' option to partialMatch ([969c1bf](https://github.com/troch/path-parser/commit/969c1bf))
* add search-params as a dependency ([6ab04d7](https://github.com/troch/path-parser/commit/6ab04d7))
### BREAKING CHANGES
* `.match()` renamed to `.test()` and now takes an options object as a second parameter
* `.partialMatch()` renamed to `.partialTest()` and now takes an options object as a second parameter. Option `delimited` is by default true, meaning a partial will be successful if the partially matched path is consumed up to a delimiter (`?`, `/`, `.`, `;`)
<a name="1.0.4"></a>

@@ -2,0 +18,0 @@ ## [1.0.4](https://github.com/troch/path-parser/compare/v1.0.3...v1.0.4) (2016-06-30)

816

dist/amd/path-parser.js

@@ -1,364 +0,572 @@

define('Path', function () { 'use strict';
define('Path', ['search-params'], function (searchParams) { 'use strict';
var babelHelpers = {};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function babelHelpers_classCallCheck (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
function AsyncGenerator(gen) {
var front, back;
var babelHelpers_createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
var defaultOrConstrained = function defaultOrConstrained(match) {
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%]+') + ')';
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
var rules = [{
// An URL can contain a parameter :paramName
// - and _ are allowed but not in last position
name: 'url-parameter',
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(defaultOrConstrained(match[2]));
}
}, {
// Url parameter (splat)
name: 'url-parameter-splat',
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/,
regex: /([^\?]*)/
}, {
name: 'url-parameter-matrix',
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2]));
}
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter-bracket',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
// Delimiter /
name: 'delimiter',
pattern: /^(\/|\?)/,
regex: function regex(match) {
return new RegExp('\\' + match[0]);
}
}, {
// Sub delimiters
name: 'sub-delimiter',
pattern: /^(\!|\&|\-|_|\.|;)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}, {
// Unmatched fragment (until delimiter is found)
name: 'fragment',
pattern: /^([0-9a-zA-Z]+)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}];
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
var tokenise = function tokenise(str) {
var tokens = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
// Look for a matching rule
var matched = rules.some(function (rule) {
var match = str.match(rule.pattern);
if (!match) return false;
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
tokens.push({
type: rule.name,
match: match[0],
val: match.slice(1, 2),
otherVal: match.slice(2),
regex: rule.regex instanceof Function ? rule.regex(match) : rule.regex
});
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens);
return true;
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var get = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var set = function set(object, property, value, receiver) {
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent !== null) {
set(parent, property, value, receiver);
}
} else if ("value" in desc && desc.writable) {
desc.value = value;
} else {
var setter = desc.set;
if (setter !== undefined) {
setter.call(receiver, value);
}
}
return value;
};
var defaultOrConstrained = function defaultOrConstrained(match) {
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%]+') + ')';
};
var rules = [{
// An URL can contain a parameter :paramName
// - and _ are allowed but not in last position
name: 'url-parameter',
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(defaultOrConstrained(match[2]));
}
}, {
// Url parameter (splat)
name: 'url-parameter-splat',
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/,
regex: /([^\?]*)/
}, {
name: 'url-parameter-matrix',
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2]));
}
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter-bracket',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/
}, {
// Delimiter /
name: 'delimiter',
pattern: /^(\/|\?)/,
regex: function regex(match) {
return new RegExp('\\' + match[0]);
}
}, {
// Sub delimiters
name: 'sub-delimiter',
pattern: /^(\!|\&|\-|_|\.|;)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}, {
// Unmatched fragment (until delimiter is found)
name: 'fragment',
pattern: /^([0-9a-zA-Z]+)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}];
var exists = function exists(val) {
return val !== undefined && val !== null;
};
var tokenise = function tokenise(str) {
var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
// Look for a matching rule
var matched = rules.some(function (rule) {
var match = str.match(rule.pattern);
if (!match) return false;
tokens.push({
type: rule.name,
match: match[0],
val: match.slice(1, 2),
otherVal: match.slice(2),
regex: rule.regex instanceof Function ? rule.regex(match) : rule.regex
});
// If no rules matched, throw an error (possible malformed path)
if (!matched) {
throw new Error('Could not parse path.');
}
// Return tokens
return tokens;
};
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens);
return true;
});
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) {
if (!trailingSlash) return source;
return source.replace(/\\\/$/, '') + '(?:\\/)?';
};
// If no rules matched, throw an error (possible malformed path)
if (!matched) {
throw new Error('Could not parse path.');
}
// Return tokens
return tokens;
};
var withoutBrackets = function withoutBrackets(param) {
return param.replace(/\[\]$/, '');
};
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) {
if (!trailingSlash) return source;
return source.replace(/\\\/$/, '') + '(?:\\/)?';
};
var appendQueryParam = function appendQueryParam(params, param) {
var val = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2];
var upToDelimiter = function upToDelimiter(source, delimiter) {
if (!delimiter) return source;
return source.replace(/\\(\/|\?|\.|;)$/, '') + '(\\/|\\?|\\.|;|$)';
};
if (/\[\]$/.test(param)) {
param = withoutBrackets(param);
val = [val];
}
var existingVal = params[param];
var appendQueryParam = function appendQueryParam(params, param) {
var val = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val];
if (/\[\]$/.test(param)) {
param = searchParams.withoutBrackets(param);
val = [val];
}
var existingVal = params[param];
return params;
};
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val];
var parseQueryParams = function parseQueryParams(path) {
var searchPart = path.split('?')[1];
if (!searchPart) return {};
return params;
};
return searchPart.split('&').map(function (_) {
return _.split('=');
}).reduce(function (obj, m) {
return appendQueryParam(obj, m[0], m[1] ? decodeURIComponent(m[1]) : m[1]);
}, {});
};
var parseQueryParams = function parseQueryParams(path) {
var searchPart = searchParams.getSearch(path);
if (!searchPart) return {};
var toSerialisable = function toSerialisable(val) {
return val !== undefined && val !== null && val !== '' ? '=' + val : '';
};
return searchParams.toObject(searchParams.parse(searchPart));
};
var _serialise = function _serialise(key, val) {
return Array.isArray(val) ? val.map(function (v) {
function _serialise(key, val) {
if (Array.isArray(val)) {
return val.map(function (v) {
return _serialise(key, v);
}).join('&') : key + toSerialisable(val);
};
}).join('&');
}
var Path = (function () {
babelHelpers_createClass(Path, null, [{
key: 'createPath',
value: function createPath(path) {
return new Path(path);
}
}, {
key: 'serialise',
value: function serialise(key, val) {
return _serialise(key, val);
}
}]);
if (val === true) {
return key;
}
function Path(path) {
babelHelpers_classCallCheck(this, Path);
return key + '=' + val;
}
if (!path) throw new Error('Please supply a path');
this.path = path;
this.tokens = tokenise(path);
var Path = function () {
createClass(Path, null, [{
key: 'createPath',
value: function createPath(path) {
return new Path(path);
}
}, {
key: 'serialise',
value: function serialise(key, val) {
return _serialise(key, val);
}
}]);
this.hasUrlParams = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).length > 0;
this.hasSpatParam = this.tokens.filter(function (t) {
return (/splat$/.test(t.type)
);
}).length > 0;
this.hasMatrixParams = this.tokens.filter(function (t) {
return (/matrix$/.test(t.type)
);
}).length > 0;
this.hasQueryParams = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type)
);
}).length > 0;
// Extract named parameters from tokens
this.urlParams = !this.hasUrlParams ? [] : this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).map(function (t) {
return t.val.slice(0, 1);
})
// Flatten
.reduce(function (r, v) {
return r.concat(v);
});
// Query params
this.queryParams = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return t.type === 'query-parameter';
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
function Path(path) {
classCallCheck(this, Path);
this.queryParamsBr = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return (/-bracket$/.test(t.type)
);
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
if (!path) throw new Error('Please supply a path');
this.path = path;
this.tokens = tokenise(path);
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);
// Check if hasQueryParams
// Regular expressions for url part only (full and partial match)
this.source = this.tokens.filter(function (t) {
return t.regex !== undefined;
}).map(function (r) {
return r.regex.source;
}).join('');
this.hasUrlParams = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).length > 0;
this.hasSpatParam = this.tokens.filter(function (t) {
return (/splat$/.test(t.type)
);
}).length > 0;
this.hasMatrixParams = this.tokens.filter(function (t) {
return (/matrix$/.test(t.type)
);
}).length > 0;
this.hasQueryParams = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type)
);
}).length > 0;
// Extract named parameters from tokens
this.spatParams = this._getParams('url-parameter-splat');
this.urlParams = this._getParams(/^url-parameter/);
// Query params
this.queryParams = this._getParams('query-parameter');
this.queryParamsBr = this._getParams('query-parameter-bracket');
// All params
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);
// Check if hasQueryParams
// Regular expressions for url part only (full and partial match)
this.source = this.tokens.filter(function (t) {
return t.regex !== undefined;
}).map(function (r) {
return r.regex.source;
}).join('');
}
createClass(Path, [{
key: '_getParams',
value: function _getParams(type) {
var predicate = type instanceof RegExp ? function (t) {
return type.test(t.type);
} : function (t) {
return t.type === type;
};
return this.tokens.filter(predicate).map(function (t) {
return t.val[0];
});
}
}, {
key: '_isSpatParam',
value: function _isSpatParam(name) {
return this.spatParams.indexOf(name) !== -1;
}
}, {
key: '_urlTest',
value: function _urlTest(path, regex) {
var _this = this;
babelHelpers_createClass(Path, [{
key: '_urlMatch',
value: function _urlMatch(path, regex) {
var _this = this;
var match = path.match(regex);
if (!match) return null;else if (!this.urlParams.length) return {};
// Reduce named params to key-value pairs
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) {
params[_this.urlParams[i]] = decodeURIComponent(m);
return params;
}, {});
}
}, {
key: 'test',
value: function test(path, opts) {
var _this2 = this;
var match = path.match(regex);
if (!match) return null;else if (!this.urlParams.length) return {};
// Reduce named params to key-value pairs
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) {
params[_this.urlParams[i]] = decodeURIComponent(m);
return params;
}, {});
}
}, {
key: 'match',
value: function match(path) {
var _this2 = this;
var options = _extends({ trailingSlash: false }, opts);
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, options.trailingSlash);
// Check if exact match
var matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further
if (!matched || !this.hasQueryParams) return matched;
// Extract query params
var queryParams = parseQueryParams(path);
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) {
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1;
});
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
// Check if exact match
var matched = this._urlMatch(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further
if (!matched || !this.hasQueryParams) return matched;
// Extract query params
var queryParams = parseQueryParams(path);
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) {
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1;
if (unexpectedQueryParams.length === 0) {
// Extend url match
Object.keys(queryParams).forEach(function (p) {
return matched[p] = queryParams[p];
});
if (unexpectedQueryParams.length === 0) {
// Extend url match
Object.keys(queryParams).forEach(function (p) {
return matched[p] = queryParams[p];
});
return matched;
}
return matched;
}
return null;
}
}, {
key: 'partialTest',
value: function partialTest(path, opts) {
var _this3 = this;
return null;
}
}, {
key: 'partialMatch',
value: function partialMatch(path) {
var _this3 = this;
var options = _extends({ delimited: true }, opts);
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
var source = upToDelimiter(this.source, options.delimited);
var match = this._urlTest(path, new RegExp('^' + source));
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
if (!match) return match;
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
var match = this._urlMatch(path, new RegExp('^' + source));
if (!this.hasQueryParams) return match;
if (!match) return match;
var queryParams = parseQueryParams(path);
if (!this.hasQueryParams) return match;
Object.keys(queryParams).filter(function (p) {
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0;
}).forEach(function (p) {
return appendQueryParam(match, p, queryParams[p]);
});
var queryParams = parseQueryParams(path);
return match;
}
}, {
key: 'build',
value: function build() {
var _this4 = this;
Object.keys(queryParams).filter(function (p) {
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0;
}).forEach(function (p) {
return appendQueryParam(match, p, queryParams[p]);
});
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return match;
}
}, {
key: 'build',
value: function build() {
var params = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var opts = arguments.length <= 1 || arguments[1] === undefined ? { ignoreConstraints: false, ignoreSearch: false } : arguments[1];
var encodedParams = Object.keys(params).reduce(function (acc, key) {
// Use encodeURI in case of spats
if (params[key] === undefined) {
acc[key] = undefined;
} else {
acc[key] = Array.isArray(params[key]) ? params[key].map(encodeURI) : encodeURI(params[key]);
}
var options = _extends({ ignoreConstraints: false, ignoreSearch: false }, opts);
var encodedParams = Object.keys(params).reduce(function (acc, key) {
if (!exists(params[key])) {
return acc;
}, {});
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(function (p) {
return params[p] === undefined;
})) throw new Error('Missing parameters');
}
// Check constraints
if (!opts.ignoreConstraints) {
var constraintsPassed = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type)
);
}).every(function (t) {
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]);
});
var val = params[key];
var encode = _this4._isSpatParam(key) ? encodeURI : encodeURIComponent;
if (!constraintsPassed) throw new Error('Some parameters are of invalid format');
if (typeof val === 'boolean') {
acc[key] = val;
} else if (Array.isArray(val)) {
acc[key] = val.map(encode);
} else {
acc[key] = encode(val);
}
var base = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type) === false
return acc;
}, {});
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(function (p) {
return !exists(encodedParams[p]);
})) throw new Error('Missing parameters');
// Check constraints
if (!options.ignoreConstraints) {
var constraintsPassed = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type)
);
}).map(function (t) {
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]];
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match
);
}).join('');
}).every(function (t) {
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]);
});
if (opts.ignoreSearch) return base;
if (!constraintsPassed) throw new Error('Some parameters are of invalid format');
}
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) {
return p + '[]';
}));
var base = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type) === false
);
}).map(function (t) {
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]];
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match
);
}).join('');
var searchPart = queryParams.filter(function (p) {
return Object.keys(encodedParams).indexOf(withoutBrackets(p)) !== -1;
}).map(function (p) {
return _serialise(p, encodedParams[withoutBrackets(p)]);
}).join('&');
if (options.ignoreSearch) return base;
return base + (searchPart ? '?' + searchPart : '');
}
}]);
return Path;
})();
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) {
return p + '[]';
}));
var searchPart = queryParams.filter(function (p) {
return Object.keys(encodedParams).indexOf(searchParams.withoutBrackets(p)) !== -1;
}).map(function (p) {
return _serialise(p, encodedParams[searchParams.withoutBrackets(p)]);
}).join('&');
return base + (searchPart ? '?' + searchPart : '');
}
}]);
return Path;
}();
});
return Path;
});
'use strict';
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
Object.defineProperty(exports, "__esModule", {

@@ -9,2 +7,8 @@ value: true

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _searchParams = require('search-params');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

@@ -40,4 +44,3 @@

pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
}, {
// Query parameter: ?param1&param2

@@ -47,4 +50,3 @@ // ?:param1&:param2

pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
}, {
// Delimiter /

@@ -72,4 +74,8 @@ name: 'delimiter',

var exists = function exists(val) {
return val !== undefined && val !== null;
};
var tokenise = function tokenise(str) {
var tokens = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

@@ -106,11 +112,12 @@ // Look for a matching rule

var withoutBrackets = function withoutBrackets(param) {
return param.replace(/\[\]$/, '');
var upToDelimiter = function upToDelimiter(source, delimiter) {
if (!delimiter) return source;
return source.replace(/\\(\/|\?|\.|;)$/, '') + '(\\/|\\?|\\.|;|$)';
};
var appendQueryParam = function appendQueryParam(params, param) {
var val = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2];
var val = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (/\[\]$/.test(param)) {
param = withoutBrackets(param);
param = (0, _searchParams.withoutBrackets)(param);
val = [val];

@@ -126,23 +133,23 @@ }

var parseQueryParams = function parseQueryParams(path) {
var searchPart = path.split('?')[1];
var searchPart = (0, _searchParams.getSearch)(path);
if (!searchPart) return {};
return searchPart.split('&').map(function (_) {
return _.split('=');
}).reduce(function (obj, m) {
return appendQueryParam(obj, m[0], m[1] ? decodeURIComponent(m[1]) : m[1]);
}, {});
return (0, _searchParams.toObject)((0, _searchParams.parse)(searchPart));
};
var toSerialisable = function toSerialisable(val) {
return val !== undefined && val !== null && val !== '' ? '=' + val : '';
};
function _serialise(key, val) {
if (Array.isArray(val)) {
return val.map(function (v) {
return _serialise(key, v);
}).join('&');
}
var _serialise = function _serialise(key, val) {
return Array.isArray(val) ? val.map(function (v) {
return _serialise(key, v);
}).join('&') : key + toSerialisable(val);
};
if (val === true) {
return key;
}
var Path = (function () {
return key + '=' + val;
}
var Path = function () {
_createClass(Path, null, [{

@@ -184,30 +191,8 @@ key: 'createPath',

// Extract named parameters from tokens
this.urlParams = !this.hasUrlParams ? [] : this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).map(function (t) {
return t.val.slice(0, 1);
})
// Flatten
.reduce(function (r, v) {
return r.concat(v);
});
this.spatParams = this._getParams('url-parameter-splat');
this.urlParams = this._getParams(/^url-parameter/);
// Query params
this.queryParams = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return t.type === 'query-parameter';
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
this.queryParamsBr = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return (/-bracket$/.test(t.type)
);
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
this.queryParams = this._getParams('query-parameter');
this.queryParamsBr = this._getParams('query-parameter-bracket');
// All params
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);

@@ -224,4 +209,22 @@ // Check if hasQueryParams

_createClass(Path, [{
key: '_urlMatch',
value: function _urlMatch(path, regex) {
key: '_getParams',
value: function _getParams(type) {
var predicate = type instanceof RegExp ? function (t) {
return type.test(t.type);
} : function (t) {
return t.type === type;
};
return this.tokens.filter(predicate).map(function (t) {
return t.val[0];
});
}
}, {
key: '_isSpatParam',
value: function _isSpatParam(name) {
return this.spatParams.indexOf(name) !== -1;
}
}, {
key: '_urlTest',
value: function _urlTest(path, regex) {
var _this = this;

@@ -238,12 +241,11 @@

}, {
key: 'match',
value: function match(path) {
key: 'test',
value: function test(path, opts) {
var _this2 = this;
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
var options = _extends({ trailingSlash: false }, opts);
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
var source = optTrailingSlash(this.source, options.trailingSlash);
// Check if exact match
var matched = this._urlMatch(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
var matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further

@@ -269,12 +271,11 @@ if (!matched || !this.hasQueryParams) return matched;

}, {
key: 'partialMatch',
value: function partialMatch(path) {
key: 'partialTest',
value: function partialTest(path, opts) {
var _this3 = this;
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
var options = _extends({ delimited: true }, opts);
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
var match = this._urlMatch(path, new RegExp('^' + source));
var source = upToDelimiter(this.source, options.delimited);
var match = this._urlTest(path, new RegExp('^' + source));

@@ -298,21 +299,34 @@ if (!match) return match;

value: function build() {
var params = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var opts = arguments.length <= 1 || arguments[1] === undefined ? { ignoreConstraints: false, ignoreSearch: false } : arguments[1];
var _this4 = this;
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var options = _extends({ ignoreConstraints: false, ignoreSearch: false }, opts);
var encodedParams = Object.keys(params).reduce(function (acc, key) {
// Use encodeURI in case of spats
if (params[key] === undefined) {
acc[key] = undefined;
if (!exists(params[key])) {
return acc;
}
var val = params[key];
var encode = _this4._isSpatParam(key) ? encodeURI : encodeURIComponent;
if (typeof val === 'boolean') {
acc[key] = val;
} else if (Array.isArray(val)) {
acc[key] = val.map(encode);
} else {
acc[key] = Array.isArray(params[key]) ? params[key].map(encodeURI) : encodeURI(params[key]);
acc[key] = encode(val);
}
return acc;
}, {});
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(function (p) {
return params[p] === undefined;
return !exists(encodedParams[p]);
})) throw new Error('Missing parameters');
// Check constraints
if (!opts.ignoreConstraints) {
if (!options.ignoreConstraints) {
var constraintsPassed = this.tokens.filter(function (t) {

@@ -337,3 +351,3 @@ return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type)

if (opts.ignoreSearch) return base;
if (options.ignoreSearch) return base;

@@ -345,5 +359,5 @@ var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) {

var searchPart = queryParams.filter(function (p) {
return Object.keys(encodedParams).indexOf(withoutBrackets(p)) !== -1;
return Object.keys(encodedParams).indexOf((0, _searchParams.withoutBrackets)(p)) !== -1;
}).map(function (p) {
return _serialise(p, encodedParams[withoutBrackets(p)]);
return _serialise(p, encodedParams[(0, _searchParams.withoutBrackets)(p)]);
}).join('&');

@@ -356,5 +370,5 @@

return Path;
})();
}();
exports.default = Path;
module.exports = exports['default'];
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define('Path', factory) :
(global.Path = factory());
}(this, function () { 'use strict';
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('search-params')) :
typeof define === 'function' && define.amd ? define('Path', ['search-params'], factory) :
(global.Path = factory(global.searchParams));
}(this, (function (searchParams) { 'use strict';
var babelHelpers = {};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function babelHelpers_classCallCheck (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
function AsyncGenerator(gen) {
var front, back;
var babelHelpers_createClass = (function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
})();
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
var defaultOrConstrained = function defaultOrConstrained(match) {
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%]+') + ')';
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
var rules = [{
// An URL can contain a parameter :paramName
// - and _ are allowed but not in last position
name: 'url-parameter',
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(defaultOrConstrained(match[2]));
}
}, {
// Url parameter (splat)
name: 'url-parameter-splat',
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/,
regex: /([^\?]*)/
}, {
name: 'url-parameter-matrix',
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2]));
}
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter-bracket',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/
}, // regex: match => new RegExp('(?=(\?|.*&)' + match[0] + '(?=(\=|&|$)))')
{
// Delimiter /
name: 'delimiter',
pattern: /^(\/|\?)/,
regex: function regex(match) {
return new RegExp('\\' + match[0]);
}
}, {
// Sub delimiters
name: 'sub-delimiter',
pattern: /^(\!|\&|\-|_|\.|;)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}, {
// Unmatched fragment (until delimiter is found)
name: 'fragment',
pattern: /^([0-9a-zA-Z]+)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}];
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
var tokenise = function tokenise(str) {
var tokens = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
// Look for a matching rule
var matched = rules.some(function (rule) {
var match = str.match(rule.pattern);
if (!match) return false;
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
tokens.push({
type: rule.name,
match: match[0],
val: match.slice(1, 2),
otherVal: match.slice(2),
regex: rule.regex instanceof Function ? rule.regex(match) : rule.regex
});
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens);
return true;
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var get = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var set = function set(object, property, value, receiver) {
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent !== null) {
set(parent, property, value, receiver);
}
} else if ("value" in desc && desc.writable) {
desc.value = value;
} else {
var setter = desc.set;
if (setter !== undefined) {
setter.call(receiver, value);
}
}
return value;
};
var defaultOrConstrained = function defaultOrConstrained(match) {
return '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%]+') + ')';
};
var rules = [{
// An URL can contain a parameter :paramName
// - and _ are allowed but not in last position
name: 'url-parameter',
pattern: /^:([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(defaultOrConstrained(match[2]));
}
}, {
// Url parameter (splat)
name: 'url-parameter-splat',
pattern: /^\*([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/,
regex: /([^\?]*)/
}, {
name: 'url-parameter-matrix',
pattern: /^\;([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(<(.+?)>)?/,
regex: function regex(match) {
return new RegExp(';' + match[1] + '=' + defaultOrConstrained(match[2]));
}
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter-bracket',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})(?:\[\])/
}, {
// Query parameter: ?param1&param2
// ?:param1&:param2
name: 'query-parameter',
pattern: /^(?:\?|&)(?:\:)?([a-zA-Z0-9-_]*[a-zA-Z0-9]{1})/
}, {
// Delimiter /
name: 'delimiter',
pattern: /^(\/|\?)/,
regex: function regex(match) {
return new RegExp('\\' + match[0]);
}
}, {
// Sub delimiters
name: 'sub-delimiter',
pattern: /^(\!|\&|\-|_|\.|;)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}, {
// Unmatched fragment (until delimiter is found)
name: 'fragment',
pattern: /^([0-9a-zA-Z]+)/,
regex: function regex(match) {
return new RegExp(match[0]);
}
}];
var exists = function exists(val) {
return val !== undefined && val !== null;
};
var tokenise = function tokenise(str) {
var tokens = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
// Look for a matching rule
var matched = rules.some(function (rule) {
var match = str.match(rule.pattern);
if (!match) return false;
tokens.push({
type: rule.name,
match: match[0],
val: match.slice(1, 2),
otherVal: match.slice(2),
regex: rule.regex instanceof Function ? rule.regex(match) : rule.regex
});
// If no rules matched, throw an error (possible malformed path)
if (!matched) {
throw new Error('Could not parse path.');
}
// Return tokens
return tokens;
};
if (match[0].length < str.length) tokens = tokenise(str.substr(match[0].length), tokens);
return true;
});
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) {
if (!trailingSlash) return source;
return source.replace(/\\\/$/, '') + '(?:\\/)?';
};
// If no rules matched, throw an error (possible malformed path)
if (!matched) {
throw new Error('Could not parse path.');
}
// Return tokens
return tokens;
};
var withoutBrackets = function withoutBrackets(param) {
return param.replace(/\[\]$/, '');
};
var optTrailingSlash = function optTrailingSlash(source, trailingSlash) {
if (!trailingSlash) return source;
return source.replace(/\\\/$/, '') + '(?:\\/)?';
};
var appendQueryParam = function appendQueryParam(params, param) {
var val = arguments.length <= 2 || arguments[2] === undefined ? '' : arguments[2];
var upToDelimiter = function upToDelimiter(source, delimiter) {
if (!delimiter) return source;
return source.replace(/\\(\/|\?|\.|;)$/, '') + '(\\/|\\?|\\.|;|$)';
};
if (/\[\]$/.test(param)) {
param = withoutBrackets(param);
val = [val];
}
var existingVal = params[param];
var appendQueryParam = function appendQueryParam(params, param) {
var val = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val];
if (/\[\]$/.test(param)) {
param = searchParams.withoutBrackets(param);
val = [val];
}
var existingVal = params[param];
return params;
};
if (existingVal === undefined) params[param] = val;else params[param] = Array.isArray(existingVal) ? existingVal.concat(val) : [existingVal, val];
var parseQueryParams = function parseQueryParams(path) {
var searchPart = path.split('?')[1];
if (!searchPart) return {};
return params;
};
return searchPart.split('&').map(function (_) {
return _.split('=');
}).reduce(function (obj, m) {
return appendQueryParam(obj, m[0], m[1] ? decodeURIComponent(m[1]) : m[1]);
}, {});
};
var parseQueryParams = function parseQueryParams(path) {
var searchPart = searchParams.getSearch(path);
if (!searchPart) return {};
var toSerialisable = function toSerialisable(val) {
return val !== undefined && val !== null && val !== '' ? '=' + val : '';
};
return searchParams.toObject(searchParams.parse(searchPart));
};
var _serialise = function _serialise(key, val) {
return Array.isArray(val) ? val.map(function (v) {
function _serialise(key, val) {
if (Array.isArray(val)) {
return val.map(function (v) {
return _serialise(key, v);
}).join('&') : key + toSerialisable(val);
};
}).join('&');
}
var Path = (function () {
babelHelpers_createClass(Path, null, [{
key: 'createPath',
value: function createPath(path) {
return new Path(path);
}
}, {
key: 'serialise',
value: function serialise(key, val) {
return _serialise(key, val);
}
}]);
if (val === true) {
return key;
}
function Path(path) {
babelHelpers_classCallCheck(this, Path);
return key + '=' + val;
}
if (!path) throw new Error('Please supply a path');
this.path = path;
this.tokens = tokenise(path);
var Path = function () {
createClass(Path, null, [{
key: 'createPath',
value: function createPath(path) {
return new Path(path);
}
}, {
key: 'serialise',
value: function serialise(key, val) {
return _serialise(key, val);
}
}]);
this.hasUrlParams = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).length > 0;
this.hasSpatParam = this.tokens.filter(function (t) {
return (/splat$/.test(t.type)
);
}).length > 0;
this.hasMatrixParams = this.tokens.filter(function (t) {
return (/matrix$/.test(t.type)
);
}).length > 0;
this.hasQueryParams = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type)
);
}).length > 0;
// Extract named parameters from tokens
this.urlParams = !this.hasUrlParams ? [] : this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).map(function (t) {
return t.val.slice(0, 1);
})
// Flatten
.reduce(function (r, v) {
return r.concat(v);
});
// Query params
this.queryParams = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return t.type === 'query-parameter';
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
function Path(path) {
classCallCheck(this, Path);
this.queryParamsBr = !this.hasQueryParams ? [] : this.tokens.filter(function (t) {
return (/-bracket$/.test(t.type)
);
}).map(function (t) {
return t.val;
}).reduce(function (r, v) {
return r.concat(v);
}, []);
if (!path) throw new Error('Please supply a path');
this.path = path;
this.tokens = tokenise(path);
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);
// Check if hasQueryParams
// Regular expressions for url part only (full and partial match)
this.source = this.tokens.filter(function (t) {
return t.regex !== undefined;
}).map(function (r) {
return r.regex.source;
}).join('');
this.hasUrlParams = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type)
);
}).length > 0;
this.hasSpatParam = this.tokens.filter(function (t) {
return (/splat$/.test(t.type)
);
}).length > 0;
this.hasMatrixParams = this.tokens.filter(function (t) {
return (/matrix$/.test(t.type)
);
}).length > 0;
this.hasQueryParams = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type)
);
}).length > 0;
// Extract named parameters from tokens
this.spatParams = this._getParams('url-parameter-splat');
this.urlParams = this._getParams(/^url-parameter/);
// Query params
this.queryParams = this._getParams('query-parameter');
this.queryParamsBr = this._getParams('query-parameter-bracket');
// All params
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);
// Check if hasQueryParams
// Regular expressions for url part only (full and partial match)
this.source = this.tokens.filter(function (t) {
return t.regex !== undefined;
}).map(function (r) {
return r.regex.source;
}).join('');
}
createClass(Path, [{
key: '_getParams',
value: function _getParams(type) {
var predicate = type instanceof RegExp ? function (t) {
return type.test(t.type);
} : function (t) {
return t.type === type;
};
return this.tokens.filter(predicate).map(function (t) {
return t.val[0];
});
}
}, {
key: '_isSpatParam',
value: function _isSpatParam(name) {
return this.spatParams.indexOf(name) !== -1;
}
}, {
key: '_urlTest',
value: function _urlTest(path, regex) {
var _this = this;
babelHelpers_createClass(Path, [{
key: '_urlMatch',
value: function _urlMatch(path, regex) {
var _this = this;
var match = path.match(regex);
if (!match) return null;else if (!this.urlParams.length) return {};
// Reduce named params to key-value pairs
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) {
params[_this.urlParams[i]] = decodeURIComponent(m);
return params;
}, {});
}
}, {
key: 'test',
value: function test(path, opts) {
var _this2 = this;
var match = path.match(regex);
if (!match) return null;else if (!this.urlParams.length) return {};
// Reduce named params to key-value pairs
return match.slice(1, this.urlParams.length + 1).reduce(function (params, m, i) {
params[_this.urlParams[i]] = decodeURIComponent(m);
return params;
}, {});
}
}, {
key: 'match',
value: function match(path) {
var _this2 = this;
var options = _extends({ trailingSlash: false }, opts);
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, options.trailingSlash);
// Check if exact match
var matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further
if (!matched || !this.hasQueryParams) return matched;
// Extract query params
var queryParams = parseQueryParams(path);
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) {
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1;
});
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
// Check if exact match
var matched = this._urlMatch(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further
if (!matched || !this.hasQueryParams) return matched;
// Extract query params
var queryParams = parseQueryParams(path);
var unexpectedQueryParams = Object.keys(queryParams).filter(function (p) {
return _this2.queryParams.concat(_this2.queryParamsBr).indexOf(p) === -1;
if (unexpectedQueryParams.length === 0) {
// Extend url match
Object.keys(queryParams).forEach(function (p) {
return matched[p] = queryParams[p];
});
if (unexpectedQueryParams.length === 0) {
// Extend url match
Object.keys(queryParams).forEach(function (p) {
return matched[p] = queryParams[p];
});
return matched;
}
return matched;
}
return null;
}
}, {
key: 'partialTest',
value: function partialTest(path, opts) {
var _this3 = this;
return null;
}
}, {
key: 'partialMatch',
value: function partialMatch(path) {
var _this3 = this;
var options = _extends({ delimited: true }, opts);
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
var source = upToDelimiter(this.source, options.delimited);
var match = this._urlTest(path, new RegExp('^' + source));
var trailingSlash = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
if (!match) return match;
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
var source = optTrailingSlash(this.source, trailingSlash);
var match = this._urlMatch(path, new RegExp('^' + source));
if (!this.hasQueryParams) return match;
if (!match) return match;
var queryParams = parseQueryParams(path);
if (!this.hasQueryParams) return match;
Object.keys(queryParams).filter(function (p) {
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0;
}).forEach(function (p) {
return appendQueryParam(match, p, queryParams[p]);
});
var queryParams = parseQueryParams(path);
return match;
}
}, {
key: 'build',
value: function build() {
var _this4 = this;
Object.keys(queryParams).filter(function (p) {
return _this3.queryParams.concat(_this3.queryParamsBr).indexOf(p) >= 0;
}).forEach(function (p) {
return appendQueryParam(match, p, queryParams[p]);
});
var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return match;
}
}, {
key: 'build',
value: function build() {
var params = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var opts = arguments.length <= 1 || arguments[1] === undefined ? { ignoreConstraints: false, ignoreSearch: false } : arguments[1];
var encodedParams = Object.keys(params).reduce(function (acc, key) {
// Use encodeURI in case of spats
if (params[key] === undefined) {
acc[key] = undefined;
} else {
acc[key] = Array.isArray(params[key]) ? params[key].map(encodeURI) : encodeURI(params[key]);
}
var options = _extends({ ignoreConstraints: false, ignoreSearch: false }, opts);
var encodedParams = Object.keys(params).reduce(function (acc, key) {
if (!exists(params[key])) {
return acc;
}, {});
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(function (p) {
return params[p] === undefined;
})) throw new Error('Missing parameters');
}
// Check constraints
if (!opts.ignoreConstraints) {
var constraintsPassed = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type)
);
}).every(function (t) {
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]);
});
var val = params[key];
var encode = _this4._isSpatParam(key) ? encodeURI : encodeURIComponent;
if (!constraintsPassed) throw new Error('Some parameters are of invalid format');
if (typeof val === 'boolean') {
acc[key] = val;
} else if (Array.isArray(val)) {
acc[key] = val.map(encode);
} else {
acc[key] = encode(val);
}
var base = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type) === false
return acc;
}, {});
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(function (p) {
return !exists(encodedParams[p]);
})) throw new Error('Missing parameters');
// Check constraints
if (!options.ignoreConstraints) {
var constraintsPassed = this.tokens.filter(function (t) {
return (/^url-parameter/.test(t.type) && !/-splat$/.test(t.type)
);
}).map(function (t) {
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]];
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match
);
}).join('');
}).every(function (t) {
return new RegExp('^' + defaultOrConstrained(t.otherVal[0]) + '$').test(encodedParams[t.val]);
});
if (opts.ignoreSearch) return base;
if (!constraintsPassed) throw new Error('Some parameters are of invalid format');
}
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) {
return p + '[]';
}));
var base = this.tokens.filter(function (t) {
return (/^query-parameter/.test(t.type) === false
);
}).map(function (t) {
if (t.type === 'url-parameter-matrix') return ';' + t.val + '=' + encodedParams[t.val[0]];
return (/^url-parameter/.test(t.type) ? encodedParams[t.val[0]] : t.match
);
}).join('');
var searchPart = queryParams.filter(function (p) {
return Object.keys(encodedParams).indexOf(withoutBrackets(p)) !== -1;
}).map(function (p) {
return _serialise(p, encodedParams[withoutBrackets(p)]);
}).join('&');
if (options.ignoreSearch) return base;
return base + (searchPart ? '?' + searchPart : '');
}
}]);
return Path;
})();
var queryParams = this.queryParams.concat(this.queryParamsBr.map(function (p) {
return p + '[]';
}));
var searchPart = queryParams.filter(function (p) {
return Object.keys(encodedParams).indexOf(searchParams.withoutBrackets(p)) !== -1;
}).map(function (p) {
return _serialise(p, encodedParams[searchParams.withoutBrackets(p)]);
}).join('&');
return base + (searchPart ? '?' + searchPart : '');
}
}]);
return Path;
}();
}));
return Path;
})));

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

import { getSearch, withoutBrackets, parse, toObject } from 'search-params';
const defaultOrConstrained = (match) =>

@@ -57,2 +59,4 @@ '(' + (match ? match.replace(/(^<|>$)/g, '') : '[a-zA-Z0-9-_.~%]+') + ')';

const exists = (val) => val !== undefined && val !== null;
const tokenise = (str, tokens = []) => {

@@ -90,3 +94,6 @@ // Look for a matching rule

const withoutBrackets = param => param.replace(/\[\]$/, '');
const upToDelimiter = (source, delimiter) => {
if (!delimiter) return source;
return source.replace(/\\(\/|\?|\.|;)$/, '') + '(\\/|\\?|\\.|;|$)';
};

@@ -107,15 +114,20 @@ const appendQueryParam = (params, param, val = '') => {

const parseQueryParams = path => {
let searchPart = path.split('?')[1];
let searchPart = getSearch(path);
if (!searchPart) return {};
return searchPart
.split('&')
.map(_ => _.split('='))
.reduce((obj, m) => appendQueryParam(obj, m[0], m[1] ? decodeURIComponent(m[1]) : m[1]), {});
return toObject(parse(searchPart));
};
const toSerialisable = val => val !== undefined && val !== null && val !== '' ? `=${val}` : '';
function serialise(key, val) {
if (Array.isArray(val)) {
return val.map(v => serialise(key, v)).join('&');
}
const serialise = (key, val) => Array.isArray(val) ? val.map(v => serialise(key, v)).join('&') : key + toSerialisable(val);
if (val === true) {
return key;
}
return `${key}=${val}`;
}
export default class Path {

@@ -140,18 +152,8 @@ static createPath(path) {

// Extract named parameters from tokens
this.urlParams = !this.hasUrlParams ? [] : this.tokens
.filter(t => /^url-parameter/.test(t.type))
.map(t => t.val.slice(0, 1))
// Flatten
.reduce((r, v) => r.concat(v));
this.spatParams = this._getParams('url-parameter-splat');
this.urlParams = this._getParams(/^url-parameter/);
// Query params
this.queryParams = !this.hasQueryParams ? [] : this.tokens
.filter(t => t.type === 'query-parameter')
.map(t => t.val)
.reduce((r, v) => r.concat(v), []);
this.queryParamsBr = !this.hasQueryParams ? [] : this.tokens
.filter(t => /-bracket$/.test(t.type))
.map(t => t.val)
.reduce((r, v) => r.concat(v), []);
this.queryParams = this._getParams('query-parameter');
this.queryParamsBr = this._getParams('query-parameter-bracket');
// All params
this.params = this.urlParams.concat(this.queryParams).concat(this.queryParamsBr);

@@ -166,3 +168,17 @@ // Check if hasQueryParams

_urlMatch(path, regex) {
_getParams(type) {
const predicate = type instanceof RegExp
? t => type.test(t.type)
: t => t.type === type;
return this.tokens
.filter(predicate)
.map(t => t.val[0]);
}
_isSpatParam(name) {
return this.spatParams.indexOf(name) !== -1;
}
_urlTest(path, regex) {
let match = path.match(regex);

@@ -179,7 +195,8 @@ if (!match) return null;

match(path, trailingSlash = 0) {
test(path, opts) {
const options = { trailingSlash: false, ...opts };
// trailingSlash: falsy => non optional, truthy => optional
const source = optTrailingSlash(this.source, trailingSlash);
const source = optTrailingSlash(this.source, options.trailingSlash);
// Check if exact match
const matched = this._urlMatch(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
const matched = this._urlTest(path, new RegExp('^' + source + (this.hasQueryParams ? '(\\?.*$|$)' : '$')));
// If no match, or no query params, no need to go further

@@ -203,7 +220,8 @@ if (!matched || !this.hasQueryParams) return matched;

partialMatch(path, trailingSlash = 0) {
partialTest(path, opts) {
const options = { delimited: true, ...opts };
// Check if partial match (start of given path matches regex)
// trailingSlash: falsy => non optional, truthy => optional
let source = optTrailingSlash(this.source, trailingSlash);
let match = this._urlMatch(path, new RegExp('^' + source));
let source = upToDelimiter(this.source, options.delimited);
let match = this._urlTest(path, new RegExp('^' + source));

@@ -223,13 +241,21 @@ if (!match) return match;

build(params = {}, opts = {ignoreConstraints: false, ignoreSearch: false}) {
build(params = {}, opts = {}) {
const options = { ignoreConstraints: false, ignoreSearch: false, ...opts };
const encodedParams = Object.keys(params).reduce(
(acc, key) => {
// Use encodeURI in case of spats
if (params[key] === undefined) {
acc[key] = undefined;
if (!exists(params[key])) {
return acc;
}
const val = params[key];
const encode = this._isSpatParam(key) ? encodeURI : encodeURIComponent;
if (typeof val === 'boolean') {
acc[key] = val;
} else if (Array.isArray(val)) {
acc[key] = val.map(encode);
} else {
acc[key] = Array.isArray(params[key])
? params[key].map(encodeURI)
: encodeURI(params[key]);
acc[key] = encode(val);
}
return acc;

@@ -239,7 +265,8 @@ },

);
// Check all params are provided (not search parameters which are optional)
if (this.urlParams.some(p => params[p] === undefined)) throw new Error('Missing parameters');
if (this.urlParams.some(p => !exists(encodedParams[p]))) throw new Error('Missing parameters');
// Check constraints
if (!opts.ignoreConstraints) {
if (!options.ignoreConstraints) {
let constraintsPassed = this.tokens

@@ -260,3 +287,3 @@ .filter(t => /^url-parameter/.test(t.type) && !/-splat$/.test(t.type))

if (opts.ignoreSearch) return base;
if (options.ignoreSearch) return base;

@@ -263,0 +290,0 @@ const queryParams = this.queryParams.concat(this.queryParamsBr.map(p => p + '[]'));

{
"name": "path-parser",
"version": "1.0.4",
"version": "2.0.0",
"description": "A small utility to parse, match and generate paths",

@@ -38,22 +38,25 @@ "main": "dist/commonjs/path-parser.js",

"devDependencies": {
"babel-core": "^6.3.26",
"babel-eslint": "^4.1.6",
"babel-plugin-add-module-exports": "^0.1.2",
"babel-plugin-transform-class-properties": "^6.3.13",
"babel-plugin-transform-export-extensions": "^6.3.13",
"babel-plugin-transform-object-rest-spread": "^6.3.13",
"babel-preset-es2015": "^6.3.13",
"babel-preset-es2015-rollup": "^1.0.0",
"conventional-changelog": "^0.5.3",
"coveralls": "^2.11.2",
"eslint": "^1.10.3",
"babel-core": "~6.9.0",
"babel-eslint": "~7.0.0",
"babel-plugin-add-module-exports": "~0.2.1",
"babel-plugin-transform-class-properties": "~6.9.1",
"babel-plugin-transform-export-extensions": "~6.8.0",
"babel-plugin-transform-object-rest-spread": "~6.8.0",
"babel-preset-es2015": "~6.9.0",
"babel-preset-es2015-rollup": "~1.2.0",
"conventional-changelog": "~1.1.0",
"coveralls": "~2.11.14",
"eslint": "~3.7.1",
"isparta": "~4.0.0",
"mkdirp": "^0.5.1",
"mocha": "^2.2.5",
"rimraf": "^2.5.0",
"rollup": "^0.24.0",
"rollup-plugin-babel": "^2.3.5",
"should": "^6.0.3",
"yargs": "^3.31.0"
"mkdirp": "~0.5.1",
"mocha": "~3.1.2",
"rimraf": "~2.5.4",
"rollup": "~0.36.3",
"rollup-plugin-babel": "~2.6.1",
"should": "~11.1.1",
"yargs": "~6.0.0"
},
"dependencies": {
"search-params": "~1.3.0"
}
}

@@ -8,5 +8,5 @@ [![npm version](https://badge.fury.io/js/path-parser.svg)](http://badge.fury.io/js/path-parser)

A small utility to parse and build paths. It can be used to partially or fully
match paths against a defined pattern.
test paths against a defined pattern.
Partial match allows to determine if a given path starts with the defined pattern.
Partial testing allows to determine if a given path starts with the defined pattern.
It is used by [route-node](https://github.com/troch/route-node)

@@ -21,7 +21,7 @@

// Matching
p.match('/users/profile/00123') // => {id: "00123"}
// Partial matching: does this path
p.test('/users/profile/00123') // => {id: "00123"}
// Partial testing: does this path
// starts with that pattern?
p.partialMatch('/users/profile/00123/orders') // => {id: "00123"}
p.partialMatch('/profile/00123/orders') // => null
p.partialTest('/users/profile/00123/orders') // => {id: "00123"}
p.partialTest('/profile/00123/orders') // => null
// Building

@@ -68,3 +68,4 @@ p.build({id: '00123'}) // => "users/profile/00123"

When using `.match()` or `.partialMatch()`, you can path a second argument. If truthy, it will make trailing slashes optional.
`.test(path, options)` accepts an option object:
- `trailingSlash`: if truthy, it will make trailing slashes optional (default to `true`).

@@ -74,6 +75,19 @@ ```javascript

path.match('/my-path/') // => null
path.match('/my-path/', true) // => {}
path.test('/my-path/') // => null
path.test('/my-path/', { trailingSlash: true }) // => {}
```
## Partial test with delimiters
`.partialTest(path, options)` accepts an option object:
- `delimited`: if truthy, a partial test will only be successful if a delimiter is found at the end of a match (default to `true`, delimiters are `/`, `?`, `.` and `;`).
```javascript
var path = new Path('/my-path');
path.partialTest('/my-path/extended') // => {}
path.partialTest('/my-path-extended') // => null
path.partialTest('/my-path-extended', { delimited: false }) // => {}
```
## Related modules

@@ -80,0 +94,0 @@

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc