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

call

Package Overview
Dependencies
Maintainers
1
Versions
17
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

call - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

lib/router.js

313

lib/index.js

@@ -6,3 +6,3 @@ // Load modules

var Regex = require('./regex');
var Sort = require('./sort');
var Router = require('./router');

@@ -24,4 +24,5 @@

this.routes = {}; // Key: HTTP method or * for catch-all, value: sorted array of routes
this.vhosts = null; // {} where Key: hostname, value: see this.routes
this.routes = {}; // Key: HTTP method or * for catch-all, value: sorted array of routes
this.ids = {}; // Key: route id, value: record
this.vhosts = null; // {} where Key: hostname, value: see this.routes

@@ -49,3 +50,3 @@ this.specials = {

var table = (vhost === '*' ? self.routes : self.vhosts[vhost]);
table[method] = table[method] || [];
table[method] = table[method] || { routes: [], router: new Router() };

@@ -58,21 +59,22 @@ var analysis = config.analysis || this.analyze(config.path);

params: analysis.params,
fingerprint: analysis.fingerprint
fingerprint: analysis.fingerprint,
settings: this.settings
};
// Check for existing route with same fingerprint
// Add route
var altFingerprint = (record.segments[record.segments.length - 1].isEmptyOk ? record.fingerprint.substring(0, record.fingerprint.length - 2) : '');
table[method].forEach(function (existing) {
table[method].router.add(analysis.segments, record);
table[method].routes.push(record);
table[method].routes.sort(internals.sort);
Hoek.assert(record.fingerprint !== existing.fingerprint, 'New route: ' + config.path + ' conflicts with existing: ' + existing.path);
Hoek.assert(altFingerprint !== existing.fingerprint, 'New route: ' + config.path + ' conflicts with existing: ' + existing.path);
var last = record.segments[record.segments.length - 1];
if (last.empty) {
table[method].router.add(analysis.segments.slice(0, -1), record);
}
var altExistingFingerprint = (existing.segments[existing.segments.length - 1].isEmptyOk ? existing.fingerprint.substring(0, existing.fingerprint.length - 2) : '');
Hoek.assert(record.fingerprint !== altExistingFingerprint, 'New route: ' + config.path + ' conflicts with existing: ' + existing.path);
});
if (config.id) {
Hoek.assert(!this.ids[config.id], 'Route id', config.id, 'for path', config.path, 'conflicts with existing path', this.ids[config.id] && this.ids[config.id].path);
this.ids[config.id] = record;
}
// Add and sort
table[method].push(record);
table[method].sort(Sort.sort);
return record;

@@ -92,12 +94,10 @@ };

var pathSegments = path.split('/');
var vhost = (this.vhosts && hostname && this.vhosts[hostname]);
var route = (vhost && this._lookup(path, vhost, method, pathSegments)) ||
this._lookup(path, this.routes, method, pathSegments) ||
(method === 'head' && vhost && this._lookup(path, vhost, 'get', pathSegments)) ||
(method === 'head' && this._lookup(path, this.routes, 'get', pathSegments)) ||
var route = (vhost && this._lookup(path, vhost, method)) ||
this._lookup(path, this.routes, method) ||
(method === 'head' && vhost && this._lookup(path, vhost, 'get')) ||
(method === 'head' && this._lookup(path, this.routes, 'get')) ||
(method === 'options' && this.specials.options) ||
(vhost && this._lookup(path, vhost, '*', pathSegments)) ||
this._lookup(path, this.routes, '*', pathSegments) ||
(vhost && this._lookup(path, vhost, '*')) ||
this._lookup(path, this.routes, '*') ||
this.specials.notFound || Boom.notFound();

@@ -109,109 +109,40 @@

internals.Router.prototype._lookup = function (path, table, method, pathSegments) {
internals.Router.prototype._lookup = function (path, table, method) {
var match = false;
var routes = table[method];
if (routes) {
for (var i = 0, il = routes.length; !match && i < il; ++i) {
var record = routes[i];
match = this._match(record, path, pathSegments); // Returns Error, false, or result object
if (match) {
if (match.isBoom) {
match = this.specials.badRequest || match;
}
else {
match.route = record.route;
}
}
}
var set = table[method];
if (!set) {
return null;
}
return match;
};
internals.Router.prototype._match = function (record, path, pathSegments) {
var result = {
params: {},
paramsArray: []
};
// Literal comparison
if (!record.params.length) {
return (record.path === (this.settings.isCaseSensitive ? path : path.toLowerCase()) ? result : false);
var match = set.router.match(path, this.settings); // Returns Error, null, or result object
if (!match) {
return null;
}
// Mismatching segment count
var pl = pathSegments.length - 1;
var sl = record.segments.length;
var last = record.segments[sl - 1];
if (pl !== sl && // Different count
(pl !== sl - 1 || (!last.isEmptyOk && !last.isWildcard)) && // Not short one with empty or wildcard allowed
(pl < sl || !last.isWildcard)) {
return false;
if (match.isBoom) {
return this.specials.badRequest || match;
}
// Parameter matching
var match = true;
for (var i = 0; match && (match instanceof Error === false) && i < record.segments.length; ++i) {
var segment = record.segments[i];
if (segment.isWildcard) {
match = internals.setParam(segment.name, pathSegments.slice(i + 1).join('/'), result, true);
}
else if (segment.count) {
match = internals.setParam(segment.name, pathSegments.slice(i + 1, i + 1 + segment.count).join('/'), result, false);
i += (segment.count - 1);
}
else if (segment.name) {
if (segment.extract) {
var partial = pathSegments[i + 1].match(segment.extract);
if (!partial) {
match = false;
}
else {
match = internals.setParam(segment.name, partial[1], result, segment.isEmptyOk);
}
var assignments = {};
var array = [];
for (var i = 0, il = match.array.length; i < il; ++i) {
var name = match.record.params[i];
var value = match.array[i];
if (value !== undefined) {
if (assignments[name] !== undefined) {
assignments[name] += '/' + value;
}
else {
match = internals.setParam(segment.name, pathSegments[i + 1], result, segment.isEmptyOk);
assignments[name] = value;
}
}
else {
match = (segment.literal === (this.settings.isCaseSensitive ? pathSegments[i + 1] : pathSegments[i + 1].toLowerCase()));
}
}
if (match !== true) { // Can be Error
return match;
}
if (i + 1 === il ||
name !== match.record.params[i + 1]) {
return result;
};
internals.setParam = function (name, value, result, isEmptyOk) {
if (!isEmptyOk && !value) {
return false;
array.push(assignments[name]);
}
}
}
if (isEmptyOk && !value) {
return true;
}
try {
var decoded = decodeURIComponent(value);
result.params[name] = decoded;
result.paramsArray.push(decoded);
return true;
}
catch (err) {
return Boom.badRequest('Invalid request path');
}
return { params: assignments, paramsArray: array, route: match.record.route };
};

@@ -256,3 +187,3 @@

var segments = [];
var params = {};
var params = [];
var fingers = [];

@@ -262,22 +193,48 @@

var segment = pathParts[i];
var param = segment.match(internals.pathRegex.parseParam);
if (param) {
// Parameter
// Literal
var pre = param[1];
var name = param[2];
var isMulti = !!param[3];
var multiCount = param[4] && parseInt(param[4], 10);
var isEmptyOk = !!param[5];
var post = param[6];
if (segment.indexOf('{') === -1) {
segment = this.settings.isCaseSensitive ? segment : segment.toLowerCase();
fingers.push(segment);
segments.push({ literal: segment });
continue;
}
Hoek.assert(!params[name], 'Cannot repeat the same parameter name:', name, 'in:', path);
params[name] = true;
// Parameter
if (isMulti) {
if (multiCount) {
for (var m = 0; m < multiCount; ++m) {
var parts = [];
segment.replace(internals.pathRegex.parseParam, function (match, literal, name, wilcard, count, empty) {
if (literal) {
parts.push(literal);
}
else {
parts.push({
name: name,
wilcard: !!wilcard,
count: count && parseInt(count, 10),
empty: !!empty
});
}
return '';
});
if (parts.length === 1) {
// Simple parameter
var item = parts[0];
Hoek.assert(params.indexOf(item.name) === -1, 'Cannot repeat the same parameter name:', item.name, 'in:', path);
params.push(item.name);
if (item.wilcard) {
if (item.count) {
for (var m = 0; m < item.count; ++m) {
fingers.push('?');
segments.push({ name: name, count: multiCount });
segments.push({ param: true });
if (m) {
params.push(item.name);
}
}

@@ -287,29 +244,42 @@ }

fingers.push('#');
segments.push({ isWildcard: true, name: name });
segments.push({ param: true, wildcard: true });
}
}
else {
fingers.push(pre + '?' + post);
var segmentMeta = {
name: name,
isEmptyOk: isEmptyOk
};
fingers.push('?');
segments.push({ param: true, empty: item.empty });
}
}
else {
if (pre || post) {
segmentMeta.mixed = true;
segmentMeta.pre = pre;
segmentMeta.post = post;
segmentMeta.extract = new RegExp('^' + Hoek.escapeRegex(pre) + '(.' + (isEmptyOk ? '*' : '+') + ')' + Hoek.escapeRegex(post) + '$', (!this.settings.isCaseSensitive ? 'i' : ''));
// Mixed parameter
var seg = {
param: true,
length: parts.length,
first: typeof parts[0] !== 'string',
segments: [],
};
var finger = '';
var regex = '^';
for (var p = 0, pl = parts.length; p < pl; ++p) {
var part = parts[p];
if (typeof part === 'string') {
finger += part;
regex += Hoek.escapeRegex(part);
seg.segments.push(part);
}
else {
Hoek.assert(params.indexOf(part.name) === -1, 'Cannot repeat the same parameter name:', part.name, 'in:', path);
params.push(part.name);
segments.push(segmentMeta);
finger += '?';
regex += '(.' + (part.empty ? '*' : '+') + ')';
}
}
}
else {
// Literal
segment = this.settings.isCaseSensitive ? segment : segment.toLowerCase();
fingers.push(segment);
segments.push({ literal: segment });
seg.mixed = new RegExp(regex + '$', (!this.settings.isCaseSensitive ? 'i' : '')),
fingers.push(finger);
segments.push(seg);
}

@@ -319,5 +289,6 @@ }

return {
path: path,
segments: segments,
fingerprint: '/' + fingers.join('/'),
params: Object.keys(params)
params: params
}

@@ -338,3 +309,3 @@ };

table[method].forEach(function (record) {
table[method].routes.forEach(function (record) {

@@ -357,1 +328,33 @@ result.push(record.route);

};
internals.sort = function (a, b) {
var aFirst = -1;
var bFirst = 1;
var as = a.segments;
var bs = b.segments;
if (as.length !== bs.length) {
return (as.length > bs.length ? bFirst : aFirst);
}
for (var i = 0, il = as.length; ; ++i) {
if (as[i].literal) {
if (bs[i].literal) {
if (as[i].literal === bs[i].literal) {
continue;
}
return (as[i].literal > bs[i].literal ? bFirst : aFirst);
}
return aFirst;
}
else if (bs[i].literal) {
return bFirst;
}
return (as[i].wildcard ? bFirst : aFirst);
}
};

@@ -19,3 +19,3 @@ // Load modules

var empty = '(^\\/$)';
var empty = '(?:^\\/$)';

@@ -29,18 +29,19 @@ var legalChars = '[\\w\\!\\$&\'\\(\\)\\*\\+\\,;\\=\\:@\\-\\.~]';

var midParam = '(\\{\\w+(\\*[1-9]\\d*)?\\})'; // {p}, {p*2}
var endParam = '(\\/(\\{\\w+((\\*([1-9]\\d*)?)|(\\?))?\\})?)?'; // {p}, {p*2}, {p*}, {p?}
var mixParam = '(\\{\\w+\\??\\})'; // {p}, {p?}
var midParam = '(?:\\{\\w+(?:\\*[1-9]\\d*)?\\})'; // {p}, {p*2}
var endParam = '(?:\\/(?:\\{\\w+(?:(?:\\*(?:[1-9]\\d*)?)|(?:\\?))?\\})?)?'; // {p}, {p*2}, {p*}, {p?}
var literalParam = '(' + literal + mixParam + literalOptional + ')|(' + literalOptional + mixParam + literal + ')';
var partialParam = '(?:\\{\\w+\\??\\})'; // {p}, {p?}
var mixedParam = '(?:(?:' + literal + partialParam + ')+' + literalOptional + ')|(?:' + partialParam + '(?:' + literal + partialParam + ')+' + literalOptional + ')|(?:' + partialParam + literal + ')';
var segmentContent = '(' + literal + '|' + midParam + '|' + literalParam + ')';
var segmentContent = '(?:' + literal + '|' + midParam + '|' + mixedParam + ')';
var segment = '\\/' + segmentContent;
var segments = '(' + segment + ')*';
var segments = '(?:' + segment + ')*';
var path = '(^' + segments + endParam + '$)';
var path = '(?:^' + segments + endParam + '$)';
var parseParam = '^(' + literalOptional + ')' + '\\{(\\w+)(?:(\\*)(\\d+)?)?(\\?)?\\}' + '(' + literalOptional + ')$';
// 1:literal 2:name 3:* 4:count 5:?
var parseParam = '(' + literal + ')|(?:\\{(\\w+)(?:(\\*)(\\d+)?)?(\\?)?\\})';
var expressions = {
parseParam: new RegExp(parseParam), // $1: literal-pre, $2: name, $3: *, $4: segments, $5: empty-ok, $6: literal-post
parseParam: new RegExp(parseParam, 'g'),
validatePath: new RegExp(empty + '|' + path),

@@ -47,0 +48,0 @@ validatePathEncoded: /%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g

{
"name": "call",
"description": "HTTP Router",
"version": "1.0.0",
"version": "2.0.0",
"repository": "git://github.com/hapijs/call",

@@ -12,3 +12,3 @@ "main": "index",

"engines": {
"node": ">=0.10.30"
"node": ">=0.10.32"
},

@@ -20,3 +20,4 @@ "dependencies": {

"devDependencies": {
"lab": "4.x.x"
"code": "1.x.x",
"lab": "5.x.x"
},

@@ -23,0 +24,0 @@ "scripts": {

@@ -5,2 +5,3 @@ // Load modules

var Call = require('../');
var Code = require('code');

@@ -18,3 +19,3 @@

var it = lab.it;
var expect = Lab.expect;
var expect = Code.expect;

@@ -29,7 +30,7 @@

router.add({ method: 'get', path: '/a' }, '/a');
router.add({ method: 'get', path: '/b' }, '/b');
router.add({ method: 'get', path: '/a{b?}c{d}' }, '/a{b?}c{d}');
expect(router.route('get', '/').route).to.equal('/');
expect(router.route('get', '/a').route).to.equal('/a');
expect(router.route('get', '/b').route).to.equal('/b');
expect(router.route('get', '/abcd').route).to.equal('/a{b?}c{d}');

@@ -53,3 +54,3 @@ done();

it('matches routes in right order', function (done) {
describe('sort', function () {

@@ -90,5 +91,13 @@ var paths = [

'/{a}/b/{p*}',
'/{p*}'
'/{p*}',
'/m/n/{p*}',
'/m/{n}/{o}',
'/n/{p}/{o*}'
];
var router = new Call.Router();
for (var i = 0, il = paths.length; i < il; ++i) {
router.add({ method: 'get', path: paths[i] }, paths[i]);
}
var requests = [

@@ -125,18 +134,24 @@ ['/', '/'],

['/a/c/b/d', '/a/{p}/b/{x}'],
['/a/b/c/d/e', '/{p*5}'],
['/a/b/c/d/e', '/a/b/{p*}'],
['/a/b/c/d/e/f', '/a/b/{p*}'],
['/x/b/c/d/e/f/g', '/{a}/b/{p*}'],
['/x/y/c/d/e/f/g', '/{p*}']
['/x/y/c/d/e/f/g', '/{p*}'],
['/m/n/o', '/m/n/{p*}'],
['/m/o/p', '/m/{n}/{o}'],
['/n/a/b/c', '/n/{p}/{o*}'],
['/n/a', '/n/{p}/{o*}']
];
var router = new Call.Router();
for (var i = 0, il = paths.length; i < il; ++i) {
router.add({ method: 'get', path: paths[i] }, paths[i]);
}
var test = function (path, route) {
it('matches \'' + path + '\' to \'' + route + '\'', function (done) {
expect(router.route('get', path).route).to.equal(route);
done();
});
};
for (i = 0, il = requests.length; i < il; ++i) {
expect(router.route('get', requests[i][0]).route).to.equal(requests[i][1]);
test(requests[i][0], requests[i][1]);
}
done();
});

@@ -146,2 +161,10 @@

it('adds a route with id', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b/{c}', id: 'a' });
expect(router.ids.a.path).to.equal('/a/b/{c}');
done();
});
it('throws on duplicate route', function (done) {

@@ -154,3 +177,3 @@

router.add({ method: 'get', path: '/a/b/{c}' });
});
}).to.throw('New route /a/b/{c} conflicts with existing /a/b/{c}');

@@ -160,2 +183,14 @@ done();

it('throws on duplicate route (id)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b', id: '1' });
expect(function () {
router.add({ method: 'get', path: '/b', id: '1' });
}).to.throw('Route id 1 for path /b conflicts with existing path /a/b');
done();
});
it('throws on duplicate route (optional param in first)', function (done) {

@@ -168,3 +203,3 @@

router.add({ method: 'get', path: '/a/b' });
}).to.throw('New route: /a/b conflicts with existing: /a/b/{c?}');
}).to.throw('New route /a/b conflicts with existing /a/b/{c?}');

@@ -181,6 +216,148 @@ done();

router.add({ method: 'get', path: '/a/b/{c?}' });
}).to.throw('New route: /a/b/{c?} conflicts with existing: /a/b');
}).to.throw('New route /a/b/{c?} conflicts with existing /a/b');
done();
});
it('throws on duplicate route (same fingerprint)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/test/{p1}/{p2}/end' });
expect(function () {
router.add({ method: 'get', path: '/test/{p*2}/end' });
}).to.throw('New route /test/{p*2}/end conflicts with existing /test/{p1}/{p2}/end');
done();
});
it('throws on duplicate route (case insensitive)', function (done) {
var router = new Call.Router({ isCaseSensitive: false });
router.add({ method: 'get', path: '/test/a' });
expect(function () {
router.add({ method: 'get', path: '/test/A' });
}).to.throw('New route /test/A conflicts with existing /test/a');
done();
});
it('throws on duplicate route (wildcards)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b/{c*}' });
expect(function () {
router.add({ method: 'get', path: '/a/b/{c*}' });
}).to.throw('New route /a/b/{c*} conflicts with existing /a/b/{c*}');
done();
});
it('throws on duplicate route (mixed)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b/a{c}' });
expect(function () {
router.add({ method: 'get', path: '/a/b/a{c}' });
}).to.throw('New route /a/b/a{c} conflicts with existing /a/b/a{c}');
done();
});
it('throws on duplicate route (/a/{p}/{q*}, /a/{p*})', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/{p}/{q*}' });
expect(function () {
router.add({ method: 'get', path: '/a/{p*}' });
}).to.throw('New route /a/{p*} conflicts with existing /a/{p}/{q*}');
done();
});
it('throws on duplicate route (/a/{p*}, /a/{p}/{q*})', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/{p*}' });
expect(function () {
router.add({ method: 'get', path: '/a/{p}/{q*}' });
}).to.throw('New route /a/{p}/{q*} conflicts with existing /a/{p*}');
done();
});
it('allows route to differ in just case', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/test/a' });
expect(function () {
router.add({ method: 'get', path: '/test/A' });
}).to.not.throw();
done();
});
it('throws on duplicate route (different param name)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/test/{p}' });
expect(function () {
router.add({ method: 'get', path: '/test/{P}' });
}).to.throw('New route /test/{P} conflicts with existing /test/{p}');
done();
});
it('throws on duplicate parameter name', function (done) {
var router = new Call.Router();
expect(function () {
router.add({ method: 'get', path: '/test/{p}/{p}' });
}).to.throw('Cannot repeat the same parameter name: p in: /test/{p}/{p}');
done();
});
it('throws on invalid path', function (done) {
var router = new Call.Router();
expect(function () {
router.add({ method: 'get', path: '/%/%' });
}).to.throw('Invalid path: /%/%');
done();
});
it('throws on duplicate route (same vhost)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b/{c}', vhost: 'example.com' });
expect(function () {
router.add({ method: 'get', path: '/a/b/{c}', vhost: 'example.com' });
}).to.throw('New route /a/b/{c} conflicts with existing /a/b/{c}');
done();
});
it('allows duplicate route (different vhost)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/b/{c}', vhost: 'one.example.com' });
expect(function () {
router.add({ method: 'get', path: '/a/b/{c}', vhost: 'two.example.com' });
}).to.not.throw();
done();
});
});

@@ -237,6 +414,6 @@

},
'/path/{param*}': {
'/path/{x*}': {
'/a/b/c/d': false,
'/path/a/b/to': {
param: 'a/b/to'
x: 'a/b/to'
},

@@ -273,2 +450,10 @@ '/path/': {},

},
'/mixedCase/|false': {
'/mixedcase/': true,
'/mixedCase/': true
},
'/mixedCase/|true': {
'/mixedcase/': false,
'/mixedCase/': true
},
'/{p*}': {

@@ -307,2 +492,62 @@ '/path/': {

'/a/b/': false
},
'/a/{b}/c|false': {
'/a/1/c': {
b: '1'
},
'/A/1/c': {
b: '1'
}
},
'/a/{B}/c|false': {
'/a/1/c': {
B: '1'
},
'/A/1/c': {
B: '1'
}
},
'/a/{b}/c|true': {
'/a/1/c': {
b: '1'
},
'/A/1/c': false
},
'/a/{B}/c|true': {
'/a/1/c': {
B: '1'
},
'/A/1/c': false
},
'/aB/{p}|true': {
'/aB/4': {
p: '4'
},
'/ab/4': false
},
'/aB/{p}|false': {
'/aB/4': {
p: '4'
},
'/ab/4': {
p: '4'
}
},
'/{a}b{c?}d{e}|true': {
'/abcde': {
a: 'a',
c: 'c',
e: 'e'
},
'/abde': {
a: 'a',
e: 'e'
},
'/abxyzde': {
a: 'a',
c: 'xyz',
e: 'e'
},
'/aBcde': false,
'/bcde': false
}

@@ -418,2 +663,26 @@ };

});
it('fails to match bad request (mixed)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a{p}' });
expect(router.route('get', '/a%p').output.statusCode).to.equal(400);
done();
});
it('fails to match bad request (wildcard)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/{p*}' });
expect(router.route('get', '/%p').output.statusCode).to.equal(400);
done();
});
it('fails to match bad request (deep)', function (done) {
var router = new Call.Router();
router.add({ method: 'get', path: '/a/{p}' });
expect(router.route('get', '/a/%p').output.statusCode).to.equal(400);
done();
});
});

@@ -557,2 +826,2 @@

});
});
});
// Load modules
var Code = require('code');
var Lab = require('lab');

@@ -17,3 +18,3 @@ var Regex = require('../lib/regex');

var it = lab.it;
var expect = Lab.expect;
var expect = Code.expect;

@@ -74,4 +75,9 @@

'/d/a{p}b/e': true,
'/a{p}.{x}': false,
'/a{p}.{x}': true,
'/{p}{x}': false,
'/a{p}{x}': false,
'/a{p}{x}b': false,
'/{p}{x}b': false,
'/{p?}{x}b': false,
'/{a}b{c?}d{e}': true,
'/a{p?}': true,

@@ -78,0 +84,0 @@ '/{p*}d': false,

Sorry, the diff of this file is not supported yet

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