tsoa
This commit is contained in:
120
node_modules/@hapi/call/lib/decode.js
generated
vendored
Normal file
120
node_modules/@hapi/call/lib/decode.js
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
'use strict';
|
||||
|
||||
// Adapted from:
|
||||
// Copyright (c) 2017-2019 Justin Ridgewell, MIT Licensed, https://github.com/jridgewell/safe-decode-string-component
|
||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>, MIT Licensed, http://bjoern.hoehrmann.de/utf-8/decoder/dfa/
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.decode = function (string) {
|
||||
|
||||
let percentPos = string.indexOf('%');
|
||||
if (percentPos === -1) {
|
||||
return string;
|
||||
}
|
||||
|
||||
let decoded = '';
|
||||
let last = 0;
|
||||
let codepoint = 0;
|
||||
let startOfOctets = percentPos;
|
||||
let state = internals.utf8.accept;
|
||||
|
||||
while (percentPos > -1 &&
|
||||
percentPos < string.length) {
|
||||
|
||||
const high = internals.resolveHex(string[percentPos + 1], 4);
|
||||
const low = internals.resolveHex(string[percentPos + 2], 0);
|
||||
const byte = high | low;
|
||||
const type = internals.utf8.data[byte];
|
||||
state = internals.utf8.data[256 + state + type];
|
||||
codepoint = (codepoint << 6) | (byte & internals.utf8.data[364 + type]);
|
||||
|
||||
if (state === internals.utf8.accept) {
|
||||
decoded += string.slice(last, startOfOctets);
|
||||
decoded += codepoint <= 0xFFFF
|
||||
? String.fromCharCode(codepoint)
|
||||
: String.fromCharCode(0xD7C0 + (codepoint >> 10), 0xDC00 + (codepoint & 0x3FF));
|
||||
|
||||
codepoint = 0;
|
||||
last = percentPos + 3;
|
||||
percentPos = string.indexOf('%', last);
|
||||
startOfOctets = percentPos;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state === internals.utf8.reject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
percentPos += 3;
|
||||
|
||||
if (percentPos >= string.length ||
|
||||
string[percentPos] !== '%') {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded + string.slice(last);
|
||||
};
|
||||
|
||||
|
||||
internals.resolveHex = function (char, shift) {
|
||||
|
||||
const i = internals.hex[char];
|
||||
return i === undefined ? 255 : i << shift;
|
||||
};
|
||||
|
||||
|
||||
internals.hex = {
|
||||
'0': 0, '1': 1, '2': 2, '3': 3, '4': 4,
|
||||
'5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
|
||||
'a': 10, 'A': 10, 'b': 11, 'B': 11, 'c': 12,
|
||||
'C': 12, 'd': 13, 'D': 13, 'e': 14, 'E': 14,
|
||||
'f': 15, 'F': 15
|
||||
};
|
||||
|
||||
|
||||
internals.utf8 = {
|
||||
accept: 12,
|
||||
reject: 0,
|
||||
data: [
|
||||
|
||||
// Maps bytes to character to a transition
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 7,
|
||||
10, 9, 9, 9, 11, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
|
||||
// Maps a state to a new state when adding a transition
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 0, 0, 0, 0, 24, 36, 48, 60, 72, 84, 96,
|
||||
0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 48, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 48, 48, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
// Maps the current transition to a mask that needs to apply to the byte
|
||||
|
||||
0x7F, 0x3F, 0x3F, 0x3F, 0x00, 0x1F, 0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07
|
||||
]
|
||||
};
|
||||
385
node_modules/@hapi/call/lib/index.js
generated
vendored
Executable file
385
node_modules/@hapi/call/lib/index.js
generated
vendored
Executable file
@@ -0,0 +1,385 @@
|
||||
'use strict';
|
||||
|
||||
const Boom = require('@hapi/boom');
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
const Decode = require('./decode');
|
||||
const Regex = require('./regex');
|
||||
const Segment = require('./segment');
|
||||
|
||||
|
||||
const internals = {
|
||||
pathRegex: Regex.generate(),
|
||||
defaults: {
|
||||
isCaseSensitive: true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.Router = internals.Router = function (options) {
|
||||
|
||||
this.settings = Hoek.applyToDefaults(internals.defaults, options || {});
|
||||
|
||||
this.routes = new Map(); // Key: HTTP method or * for catch-all, value: sorted array of routes
|
||||
this.ids = new Map(); // Key: route id, value: record
|
||||
this.vhosts = null; // Map where Key: hostname, value: see this.routes
|
||||
|
||||
this.specials = {
|
||||
badRequest: null,
|
||||
notFound: null,
|
||||
options: null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.add = function (config, route) {
|
||||
|
||||
const method = config.method.toLowerCase();
|
||||
|
||||
const vhost = config.vhost || '*';
|
||||
if (vhost !== '*') {
|
||||
this.vhosts = this.vhosts ?? new Map();
|
||||
if (!this.vhosts.has(vhost)) {
|
||||
this.vhosts.set(vhost, new Map());
|
||||
}
|
||||
}
|
||||
|
||||
const table = vhost === '*' ? this.routes : this.vhosts.get(vhost);
|
||||
if (!table.has(method)) {
|
||||
table.set(method, { routes: [], router: new Segment() });
|
||||
}
|
||||
|
||||
const analysis = config.analysis ?? this.analyze(config.path);
|
||||
const record = {
|
||||
path: config.path,
|
||||
route: route || config.path,
|
||||
segments: analysis.segments,
|
||||
params: analysis.params,
|
||||
fingerprint: analysis.fingerprint,
|
||||
settings: this.settings
|
||||
};
|
||||
|
||||
// Add route
|
||||
|
||||
const map = table.get(method);
|
||||
map.router.add(analysis.segments, record);
|
||||
map.routes.push(record);
|
||||
map.routes.sort(internals.sort);
|
||||
|
||||
const last = record.segments[record.segments.length - 1];
|
||||
if (last.empty) {
|
||||
map.router.add(analysis.segments.slice(0, -1), record);
|
||||
}
|
||||
|
||||
if (config.id) {
|
||||
Hoek.assert(!this.ids.has(config.id), 'Route id', config.id, 'for path', config.path, 'conflicts with existing path', this.ids.has(config.id) && this.ids.get(config.id).path);
|
||||
this.ids.set(config.id, record);
|
||||
}
|
||||
|
||||
return record;
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.special = function (type, route) {
|
||||
|
||||
Hoek.assert(Object.keys(this.specials).indexOf(type) !== -1, 'Unknown special route type:', type);
|
||||
|
||||
this.specials[type] = { route };
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.route = function (method, path, hostname) {
|
||||
|
||||
const segments = path.length === 1 ? [''] : path.split('/').slice(1);
|
||||
|
||||
const vhost = this.vhosts && hostname && this.vhosts.get(hostname);
|
||||
const route = vhost && this._lookup(path, segments, vhost, method) ||
|
||||
this._lookup(path, segments, this.routes, method) ||
|
||||
method === 'head' && vhost && this._lookup(path, segments, vhost, 'get') ||
|
||||
method === 'head' && this._lookup(path, segments, this.routes, 'get') ||
|
||||
method === 'options' && this.specials.options ||
|
||||
vhost && this._lookup(path, segments, vhost, '*') ||
|
||||
this._lookup(path, segments, this.routes, '*') ||
|
||||
this.specials.notFound || Boom.notFound();
|
||||
|
||||
return route;
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype._lookup = function (path, segments, table, method) {
|
||||
|
||||
const set = table.get(method);
|
||||
if (!set) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = set.router.lookup(path, segments, this.settings);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assignments = {};
|
||||
const array = [];
|
||||
for (let i = 0; i < match.array.length; ++i) {
|
||||
const name = match.record.params[i];
|
||||
const value = Decode.decode(match.array[i]);
|
||||
if (value === null) {
|
||||
return this.specials.badRequest ?? Boom.badRequest('Invalid request path');
|
||||
}
|
||||
|
||||
if (assignments[name] !== undefined) {
|
||||
assignments[name] = assignments[name] + '/' + value;
|
||||
}
|
||||
else {
|
||||
assignments[name] = value;
|
||||
}
|
||||
|
||||
if (i + 1 === match.array.length || // Only include the last segment of a multi-segment param
|
||||
name !== match.record.params[i + 1]) {
|
||||
|
||||
array.push(assignments[name]);
|
||||
}
|
||||
}
|
||||
|
||||
return { params: assignments, paramsArray: array, route: match.record.route };
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.normalize = function (path) {
|
||||
|
||||
if (path &&
|
||||
path.indexOf('%') !== -1) {
|
||||
|
||||
// Uppercase %encoded values
|
||||
|
||||
const uppercase = path.replace(/%[0-9a-fA-F][0-9a-fA-F]/g, (encoded) => encoded.toUpperCase());
|
||||
|
||||
// Decode non-reserved path characters: a-z A-Z 0-9 _!$&'()*+,;=:@-.~
|
||||
// ! (%21) $ (%24) & (%26) ' (%27) ( (%28) ) (%29) * (%2A) + (%2B) , (%2C) - (%2D) . (%2E)
|
||||
// 0-9 (%30-39) : (%3A) ; (%3B) = (%3D)
|
||||
// @ (%40) A-Z (%41-5A) _ (%5F) a-z (%61-7A) ~ (%7E)
|
||||
|
||||
const decoded = uppercase.replace(/%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g, (encoded) => String.fromCharCode(parseInt(encoded.substring(1), 16)));
|
||||
|
||||
path = decoded;
|
||||
}
|
||||
|
||||
// Normalize path segments
|
||||
|
||||
if (path &&
|
||||
(path.indexOf('/.') !== -1 || path[0] === '.')) {
|
||||
|
||||
const hasLeadingSlash = path[0] === '/';
|
||||
const segments = path.split('/');
|
||||
const normalized = [];
|
||||
let segment;
|
||||
|
||||
for (let i = 0; i < segments.length; ++i) {
|
||||
segment = segments[i];
|
||||
if (segment === '..') {
|
||||
normalized.pop();
|
||||
}
|
||||
else if (segment !== '.') {
|
||||
normalized.push(segment);
|
||||
}
|
||||
}
|
||||
|
||||
if (segment === '.' ||
|
||||
segment === '..') { // Add trailing slash when needed
|
||||
|
||||
normalized.push('');
|
||||
}
|
||||
|
||||
path = normalized.join('/');
|
||||
|
||||
if (path[0] !== '/' &&
|
||||
hasLeadingSlash) {
|
||||
|
||||
path = '/' + path;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.analyze = function (path) {
|
||||
|
||||
Hoek.assert(internals.pathRegex.validatePath.test(path), 'Invalid path:', path);
|
||||
Hoek.assert(!internals.pathRegex.validatePathEncoded.test(path), 'Path cannot contain encoded non-reserved path characters:', path);
|
||||
|
||||
const pathParts = path.split('/');
|
||||
const segments = [];
|
||||
const params = [];
|
||||
const fingers = [];
|
||||
|
||||
for (let i = 1; i < pathParts.length; ++i) { // Skip first empty segment
|
||||
let segment = pathParts[i];
|
||||
|
||||
// Literal
|
||||
|
||||
if (segment.indexOf('{') === -1) {
|
||||
segment = this.settings.isCaseSensitive ? segment : segment.toLowerCase();
|
||||
fingers.push(segment);
|
||||
segments.push({ literal: segment });
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parameter
|
||||
|
||||
const parts = internals.parseParams(segment);
|
||||
if (parts.length === 1) {
|
||||
|
||||
// Simple parameter
|
||||
|
||||
const 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.wildcard) {
|
||||
if (item.count) {
|
||||
for (let j = 0; j < item.count; ++j) {
|
||||
fingers.push('?');
|
||||
segments.push({});
|
||||
if (j) {
|
||||
params.push(item.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
fingers.push('#');
|
||||
segments.push({ wildcard: true });
|
||||
}
|
||||
}
|
||||
else {
|
||||
fingers.push('?');
|
||||
segments.push({ empty: item.empty });
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Mixed parameter
|
||||
|
||||
const seg = {
|
||||
length: parts.length,
|
||||
first: typeof parts[0] !== 'string',
|
||||
segments: []
|
||||
};
|
||||
|
||||
let finger = '';
|
||||
let regex = '^';
|
||||
for (let j = 0; j < parts.length; ++j) {
|
||||
const part = parts[j];
|
||||
if (typeof part === 'string') {
|
||||
finger = finger + part;
|
||||
regex = 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);
|
||||
|
||||
finger = finger + '?';
|
||||
regex = regex + '(.' + (part.empty ? '*' : '+') + ')';
|
||||
}
|
||||
}
|
||||
|
||||
seg.mixed = new RegExp(regex + '$', !this.settings.isCaseSensitive ? 'i' : '');
|
||||
fingers.push(finger);
|
||||
segments.push(seg);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
segments,
|
||||
fingerprint: '/' + fingers.join('/'),
|
||||
params
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
internals.parseParams = function (segment) {
|
||||
|
||||
const parts = [];
|
||||
segment.replace(internals.pathRegex.parseParam, ($0, literal, name, wildcard, count, empty) => {
|
||||
|
||||
if (literal) {
|
||||
parts.push(literal);
|
||||
}
|
||||
else {
|
||||
parts.push({
|
||||
name,
|
||||
wildcard: !!wildcard,
|
||||
count: count && parseInt(count, 10),
|
||||
empty: !!empty
|
||||
});
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
return parts;
|
||||
};
|
||||
|
||||
|
||||
internals.Router.prototype.table = function (host) {
|
||||
|
||||
const result = [];
|
||||
const collect = (table) => {
|
||||
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const map of table.values()) {
|
||||
for (const record of map.routes) {
|
||||
result.push(record.route);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.vhosts) {
|
||||
const vhosts = host ? [].concat(host) : [...this.vhosts.keys()];
|
||||
for (const vhost of vhosts) {
|
||||
collect(this.vhosts.get(vhost));
|
||||
}
|
||||
}
|
||||
|
||||
collect(this.routes);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
internals.sort = function (a, b) {
|
||||
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
|
||||
const as = a.segments;
|
||||
const bs = b.segments;
|
||||
|
||||
if (as.length !== bs.length) {
|
||||
return as.length > bs.length ? bFirst : aFirst;
|
||||
}
|
||||
|
||||
for (let i = 0; ; ++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;
|
||||
}
|
||||
|
||||
if (bs[i].literal) {
|
||||
return bFirst;
|
||||
}
|
||||
|
||||
return as[i].wildcard ? bFirst : aFirst;
|
||||
}
|
||||
};
|
||||
47
node_modules/@hapi/call/lib/regex.js
generated
vendored
Executable file
47
node_modules/@hapi/call/lib/regex.js
generated
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.generate = function () {
|
||||
|
||||
/*
|
||||
/path/{param}/path/{param?}
|
||||
/path/{param*2}/path
|
||||
/path/{param*2}
|
||||
/path/x{param}x
|
||||
/{param*}
|
||||
*/
|
||||
|
||||
const empty = '(?:^\\/$)';
|
||||
|
||||
const legalChars = '[\\w\\!\\$&\'\\(\\)\\*\\+\\,;\\=\\:@\\-\\.~]';
|
||||
const encoded = '%[A-F0-9]{2}';
|
||||
|
||||
const literalChar = '(?:' + legalChars + '|' + encoded + ')';
|
||||
const literal = literalChar + '+';
|
||||
const literalOptional = literalChar + '*';
|
||||
|
||||
const midParam = '(?:\\{\\w+(?:\\*[1-9]\\d*)?\\})'; // {p}, {p*2}
|
||||
const endParam = '(?:\\/(?:\\{\\w+(?:(?:\\*(?:[1-9]\\d*)?)|(?:\\?))?\\})?)?'; // {p}, {p*2}, {p*}, {p?}
|
||||
|
||||
const partialParam = '(?:\\{\\w+\\??\\})'; // {p}, {p?}
|
||||
const mixedParam = '(?:(?:' + literal + partialParam + ')+' + literalOptional + ')|(?:' + partialParam + '(?:' + literal + partialParam + ')+' + literalOptional + ')|(?:' + partialParam + literal + ')';
|
||||
|
||||
const segmentContent = '(?:' + literal + '|' + midParam + '|' + mixedParam + ')';
|
||||
const segment = '\\/' + segmentContent;
|
||||
const segments = '(?:' + segment + ')*';
|
||||
|
||||
const path = '(?:^' + segments + endParam + '$)';
|
||||
|
||||
// 1:literal 2:name 3:* 4:count 5:?
|
||||
const parseParam = '(' + literal + ')|(?:\\{(\\w+)(?:(\\*)(\\d+)?)?(\\?)?\\})';
|
||||
|
||||
const expressions = {
|
||||
parseParam: new RegExp(parseParam, 'g'),
|
||||
validatePath: new RegExp(empty + '|' + path),
|
||||
validatePathEncoded: /%(?:2[146-9A-E]|3[\dABD]|4[\dA-F]|5[\dAF]|6[1-9A-F]|7[\dAE])/g
|
||||
};
|
||||
|
||||
return expressions;
|
||||
};
|
||||
246
node_modules/@hapi/call/lib/segment.js
generated
vendored
Executable file
246
node_modules/@hapi/call/lib/segment.js
generated
vendored
Executable file
@@ -0,0 +1,246 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports = module.exports = internals.Segment = function () {
|
||||
|
||||
this._edge = null; // { segment, record }
|
||||
this._fulls = null; // { path: { segment, record }
|
||||
this._literals = null; // { literal: { segment, <node> } }
|
||||
this._param = null; // <node>
|
||||
this._mixed = null; // [{ segment, <node> }]
|
||||
this._wildcard = null; // { segment, record }
|
||||
};
|
||||
|
||||
|
||||
internals.Segment.prototype.add = function (segments, record) {
|
||||
|
||||
/*
|
||||
{ literal: 'x' } -> x
|
||||
{ empty: false } -> {p}
|
||||
{ wildcard: true } -> {p*}
|
||||
{ mixed: /regex/ } -> a{p}b
|
||||
*/
|
||||
|
||||
const current = segments[0];
|
||||
const remaining = segments.slice(1);
|
||||
const isEdge = !remaining.length;
|
||||
|
||||
const literals = [];
|
||||
let isLiteral = true;
|
||||
for (let i = 0; i < segments.length && isLiteral; ++i) {
|
||||
isLiteral = segments[i].literal !== undefined;
|
||||
literals.push(segments[i].literal);
|
||||
}
|
||||
|
||||
if (isLiteral) {
|
||||
this._fulls = this._fulls ?? new Map();
|
||||
let literal = '/' + literals.join('/');
|
||||
if (!record.settings.isCaseSensitive) {
|
||||
literal = literal.toLowerCase();
|
||||
}
|
||||
|
||||
Hoek.assert(!this._fulls.has(literal), 'New route', record.path, 'conflicts with existing', this._fulls.get(literal)?.record.path);
|
||||
this._fulls.set(literal, { segment: current, record });
|
||||
}
|
||||
else if (current.literal !== undefined) { // Can be empty string
|
||||
|
||||
// Literal
|
||||
|
||||
this._literals = this._literals ?? new Map();
|
||||
const currentLiteral = record.settings.isCaseSensitive ? current.literal : current.literal.toLowerCase();
|
||||
if (!this._literals.has(currentLiteral)) {
|
||||
this._literals.set(currentLiteral, new internals.Segment());
|
||||
}
|
||||
|
||||
this._literals.get(currentLiteral).add(remaining, record);
|
||||
}
|
||||
else if (current.wildcard) {
|
||||
|
||||
// Wildcard
|
||||
|
||||
Hoek.assert(!this._wildcard, 'New route', record.path, 'conflicts with existing', this._wildcard?.record.path);
|
||||
Hoek.assert(!this._param || !this._param._wildcard, 'New route', record.path, 'conflicts with existing', this._param?._wildcard?.record.path);
|
||||
this._wildcard = { segment: current, record };
|
||||
}
|
||||
else if (current.mixed) {
|
||||
|
||||
// Mixed
|
||||
|
||||
this._mixed = this._mixed ?? [];
|
||||
|
||||
let mixed = this._mixedLookup(current);
|
||||
if (!mixed) {
|
||||
mixed = { segment: current, node: new internals.Segment() };
|
||||
this._mixed.push(mixed);
|
||||
this._mixed.sort(internals.mixed);
|
||||
}
|
||||
|
||||
if (isEdge) {
|
||||
Hoek.assert(!mixed.node._edge, 'New route', record.path, 'conflicts with existing', mixed.node._edge?.record.path);
|
||||
mixed.node._edge = { segment: current, record };
|
||||
}
|
||||
else {
|
||||
mixed.node.add(remaining, record);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// Parameter
|
||||
|
||||
this._param = this._param ?? new internals.Segment();
|
||||
|
||||
if (isEdge) {
|
||||
Hoek.assert(!this._param._edge, 'New route', record.path, 'conflicts with existing', this._param._edge?.record.path);
|
||||
this._param._edge = { segment: current, record };
|
||||
}
|
||||
else {
|
||||
Hoek.assert(!this._wildcard || !remaining[0].wildcard, 'New route', record.path, 'conflicts with existing', this._wildcard?.record.path);
|
||||
this._param.add(remaining, record);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
internals.Segment.prototype._mixedLookup = function (segment) {
|
||||
|
||||
for (let i = 0; i < this._mixed.length; ++i) {
|
||||
if (internals.mixed({ segment }, this._mixed[i]) === 0) {
|
||||
return this._mixed[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
internals.mixed = function (a, b) {
|
||||
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
|
||||
const as = a.segment;
|
||||
const bs = b.segment;
|
||||
|
||||
if (as.length !== bs.length) {
|
||||
return as.length > bs.length ? aFirst : bFirst;
|
||||
}
|
||||
|
||||
if (as.first !== bs.first) {
|
||||
return as.first ? bFirst : aFirst;
|
||||
}
|
||||
|
||||
for (let i = 0; i < as.segments.length; ++i) {
|
||||
const am = as.segments[i];
|
||||
const bm = bs.segments[i];
|
||||
|
||||
if (am === bm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (am.length === bm.length) {
|
||||
return am > bm ? bFirst : aFirst;
|
||||
}
|
||||
|
||||
return am.length < bm.length ? bFirst : aFirst;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
||||
internals.Segment.prototype.lookup = function (path, segments, options) {
|
||||
|
||||
let match = null;
|
||||
|
||||
// Literal edge
|
||||
|
||||
if (this._fulls) {
|
||||
match = this._fulls.get(options.isCaseSensitive ? path : path.toLowerCase());
|
||||
if (match) {
|
||||
return { record: match.record, array: [] };
|
||||
}
|
||||
}
|
||||
|
||||
// Literal node
|
||||
|
||||
const current = segments[0];
|
||||
const nextPath = path.slice(current.length + 1);
|
||||
const remainder = segments.length > 1 ? segments.slice(1) : null;
|
||||
|
||||
if (this._literals) {
|
||||
const literal = options.isCaseSensitive ? current : current.toLowerCase();
|
||||
match = this._literals.get(literal);
|
||||
if (match) {
|
||||
const record = internals.deeper(match, nextPath, remainder, [], options);
|
||||
if (record) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mixed
|
||||
|
||||
if (this._mixed) {
|
||||
for (let i = 0; i < this._mixed.length; ++i) {
|
||||
match = this._mixed[i];
|
||||
const params = current.match(match.segment.mixed);
|
||||
if (params) {
|
||||
const array = [];
|
||||
for (let j = 1; j < params.length; ++j) {
|
||||
array.push(params[j]);
|
||||
}
|
||||
|
||||
const record = internals.deeper(match.node, nextPath, remainder, array, options);
|
||||
if (record) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Param
|
||||
|
||||
if (this._param) {
|
||||
if (current || this._param._edge?.segment.empty) {
|
||||
const record = internals.deeper(this._param, nextPath, remainder, [current], options);
|
||||
if (record) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wildcard
|
||||
|
||||
if (this._wildcard) {
|
||||
return { record: this._wildcard.record, array: [path.slice(1)] };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
internals.deeper = function (match, path, segments, array, options) {
|
||||
|
||||
if (!segments) {
|
||||
if (match._edge) {
|
||||
return { record: match._edge.record, array };
|
||||
}
|
||||
|
||||
if (match._wildcard) {
|
||||
return { record: match._wildcard.record, array };
|
||||
}
|
||||
}
|
||||
else {
|
||||
const result = match.lookup(path, segments, options);
|
||||
if (result) {
|
||||
return { record: result.record, array: array.concat(result.array) };
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
Reference in New Issue
Block a user