tsoa
This commit is contained in:
232
node_modules/@hapi/accept/lib/header.js
generated
vendored
Executable file
232
node_modules/@hapi/accept/lib/header.js
generated
vendored
Executable file
@@ -0,0 +1,232 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Boom = require('@hapi/boom');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.selection = function (header, preferences, options) {
|
||||
|
||||
const selections = exports.selections(header, preferences, options);
|
||||
return selections.length ? selections[0] : '';
|
||||
};
|
||||
|
||||
|
||||
exports.selections = function (header, preferences, options) {
|
||||
|
||||
Hoek.assert(!preferences || Array.isArray(preferences), 'Preferences must be an array');
|
||||
|
||||
return internals.parse(header || '', preferences, options);
|
||||
};
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.3 (https://tools.ietf.org/html/rfc7231#section-5.3.3)
|
||||
//
|
||||
// Accept-Charset = *( "," OWS ) ( ( charset / "*" ) [ weight ] ) *( OWS "," [ OWS ( ( charset / "*" ) [ weight ] ) ] )
|
||||
// charset = token
|
||||
//
|
||||
// Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.4 (https://tools.ietf.org/html/rfc7231#section-5.3.4)
|
||||
//
|
||||
// Accept-Encoding = [ ( "," / ( codings [ weight ] ) ) *( OWS "," [ OWS ( codings [ weight ] ) ] ) ]
|
||||
// codings = content-coding / "identity" / "*"
|
||||
// content-coding = token
|
||||
//
|
||||
// Accept-Encoding: compress, gzip
|
||||
// Accept-Encoding:
|
||||
// Accept-Encoding: *
|
||||
// Accept-Encoding: compress;q=0.5, gzip;q=1.0
|
||||
// Accept-Encoding: gzip;q=1.0, identity; q=0.5, *;q=0
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.5 (https://tools.ietf.org/html/rfc7231#section-5.3.5)
|
||||
//
|
||||
// Accept-Language = *( "," OWS ) ( language-range [ weight ] ) *( OWS "," [ OWS ( language-range [ weight ] ) ] )
|
||||
// language-range = ( 1*8ALPHA *( "-" 1*8alphanum ) ) / "*" ; [RFC4647], Section 2.1
|
||||
// alphanum = ALPHA / DIGIT
|
||||
//
|
||||
// Accept-Language: da, en-gb;q=0.8, en;q=0.7
|
||||
|
||||
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
||||
// / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
||||
// / DIGIT / ALPHA
|
||||
// ; any VCHAR, except delimiters
|
||||
// OWS = *( SP / HTAB )
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.1 (https://tools.ietf.org/html/rfc7231#section-5.3.1)
|
||||
//
|
||||
// The weight is normalized to a real number in the range 0 through 1,
|
||||
// where 0.001 is the least preferred and 1 is the most preferred; a
|
||||
// value of 0 means "not acceptable". If no "q" parameter is present,
|
||||
// the default weight is 1.
|
||||
//
|
||||
// weight = OWS ";" OWS "q=" qvalue
|
||||
// qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
|
||||
|
||||
|
||||
internals.parse = function (raw, preferences, options) {
|
||||
|
||||
// Normalize header (remove spaces and tabs)
|
||||
|
||||
const header = raw.replace(/[ \t]/g, '');
|
||||
|
||||
// Normalize preferences
|
||||
|
||||
const lowers = new Map();
|
||||
if (preferences) {
|
||||
let pos = 0;
|
||||
for (const preference of preferences) {
|
||||
const lower = preference.toLowerCase();
|
||||
lowers.set(lower, { orig: preference, pos: pos++ });
|
||||
|
||||
if (options.prefixMatch) {
|
||||
const parts = lower.split('-');
|
||||
while (parts.pop(), parts.length > 0) {
|
||||
const joined = parts.join('-');
|
||||
if (!lowers.has(joined)) {
|
||||
lowers.set(joined, { orig: preference, pos: pos++ });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse selections
|
||||
|
||||
const parts = header.split(',');
|
||||
const selections = [];
|
||||
const map = new Set();
|
||||
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
const part = parts[i];
|
||||
if (!part) { // Ignore empty parts or leading commas
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
|
||||
const params = part.split(';');
|
||||
if (params.length > 2) {
|
||||
throw Boom.badRequest(`Invalid ${options.type} header`);
|
||||
}
|
||||
|
||||
let token = params[0].toLowerCase();
|
||||
if (!token) {
|
||||
throw Boom.badRequest(`Invalid ${options.type} header`);
|
||||
}
|
||||
|
||||
if (options.equivalents?.has(token)) {
|
||||
token = options.equivalents.get(token);
|
||||
}
|
||||
|
||||
const selection = {
|
||||
token,
|
||||
pos: i,
|
||||
q: 1
|
||||
};
|
||||
|
||||
if (preferences &&
|
||||
lowers.has(token)) {
|
||||
|
||||
selection.pref = lowers.get(token).pos;
|
||||
}
|
||||
|
||||
map.add(selection.token);
|
||||
|
||||
// Parse q=value
|
||||
|
||||
if (params.length === 2) {
|
||||
const q = params[1];
|
||||
const [key, value] = q.split('=');
|
||||
|
||||
if (!value ||
|
||||
key !== 'q' && key !== 'Q') {
|
||||
|
||||
throw Boom.badRequest(`Invalid ${options.type} header`);
|
||||
}
|
||||
|
||||
const score = parseFloat(value);
|
||||
if (score === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Number.isFinite(score) &&
|
||||
score <= 1 &&
|
||||
score >= 0.001) {
|
||||
|
||||
selection.q = score;
|
||||
}
|
||||
}
|
||||
|
||||
selections.push(selection); // Only add allowed selections (q !== 0)
|
||||
}
|
||||
|
||||
// Sort selection based on q and then position in header
|
||||
|
||||
selections.sort(internals.sort);
|
||||
|
||||
// Extract tokens
|
||||
|
||||
const values = selections.map((selection) => selection.token);
|
||||
|
||||
if (options.default &&
|
||||
!map.has(options.default)) {
|
||||
|
||||
values.push(options.default);
|
||||
}
|
||||
|
||||
if (!preferences?.length) {
|
||||
return values;
|
||||
}
|
||||
|
||||
const preferred = [];
|
||||
for (const selection of values) {
|
||||
if (selection === '*') {
|
||||
for (const [preference, value] of lowers) {
|
||||
if (!map.has(preference)) {
|
||||
preferred.push(value.orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const lower = selection.toLowerCase();
|
||||
if (lowers.has(lower)) {
|
||||
preferred.push(lowers.get(lower).orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preferred;
|
||||
};
|
||||
|
||||
|
||||
internals.sort = function (a, b) {
|
||||
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
|
||||
if (b.q !== a.q) {
|
||||
return b.q - a.q;
|
||||
}
|
||||
|
||||
if (b.pref !== a.pref) {
|
||||
if (a.pref === undefined) {
|
||||
return bFirst;
|
||||
}
|
||||
|
||||
if (b.pref === undefined) {
|
||||
return aFirst;
|
||||
}
|
||||
|
||||
return a.pref - b.pref;
|
||||
}
|
||||
|
||||
return a.pos - b.pos;
|
||||
};
|
||||
114
node_modules/@hapi/accept/lib/index.d.ts
generated
vendored
Executable file
114
node_modules/@hapi/accept/lib/index.d.ts
generated
vendored
Executable file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Identifies the best character-set for an HTTP response based on the HTTP request Accept-Charset header.
|
||||
*
|
||||
* @param header - the HTTP Accept-Charset header content.
|
||||
* @param preferences - an optional array of character-set strings in order of server preference.
|
||||
*
|
||||
* @return a string with the preferred accepted character-set.
|
||||
*/
|
||||
export function charset(header?: string, preferences?: readonly string[]): string;
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the character-sets in the HTTP request Accept-Charset header based on client preference from most to least desired.
|
||||
*
|
||||
* @param header - the HTTP Accept-Charset header content.
|
||||
*
|
||||
* @return an array of strings of character-sets sorted from the most to the least desired.
|
||||
*/
|
||||
export function charsets(header?: string): string[];
|
||||
|
||||
|
||||
/**
|
||||
* Identifies the best encoding for an HTTP response based on the HTTP request Accept-Encoding header.
|
||||
*
|
||||
* @param header - the HTTP Accept-Encoding header content.
|
||||
* @param preferences - an optional array of encoding strings in order of server preference.
|
||||
*
|
||||
* @return a string with the preferred accepted encoding.
|
||||
*/
|
||||
export function encoding(header?: string, preferences?: readonly string[]): string;
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the encodings in the HTTP request Accept-Encoding header based on client preference from most to least desired.
|
||||
*
|
||||
* @param header - the HTTP Accept-Encoding header content.
|
||||
*
|
||||
* @return an array of strings of encodings sorted from the most to the least desired.
|
||||
*/
|
||||
export function encodings(header?: string): string[];
|
||||
|
||||
|
||||
/**
|
||||
* Identifies the best language for an HTTP response based on the HTTP request Accept-Language header.
|
||||
*
|
||||
* @param header - the HTTP Accept-Language header content.
|
||||
* @param preferences - an optional array of language strings in order of server preference.
|
||||
*
|
||||
* @return a string with the preferred accepted language.
|
||||
*/
|
||||
export function language(header?: string, preferences?: readonly string[]): string;
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the languages in the HTTP request Accept-Language header based on client preference from most to least desired.
|
||||
*
|
||||
* @param header - the HTTP Accept-Language header content.
|
||||
*
|
||||
* @return an array of strings of languages sorted from the most to the least desired.
|
||||
*/
|
||||
export function languages(header?: string): string[];
|
||||
|
||||
|
||||
/**
|
||||
* Identifies the best media-type for an HTTP response based on the HTTP request Accept header.
|
||||
*
|
||||
* @param header - the HTTP Accept header content.
|
||||
* @param preferences - an optional array of media-type strings in order of server preference.
|
||||
*
|
||||
* @return a string with the preferred accepted media-type.
|
||||
*/
|
||||
export function mediaType(header?: string, preferences?: readonly string[]): string;
|
||||
|
||||
|
||||
/**
|
||||
* Sorts the media-types in the HTTP request Accept header based on client preference from most to least desired.
|
||||
*
|
||||
* @param header - the HTTP Accept header content.
|
||||
* @param preferences - an optional array of media-type strings in order of server preference.
|
||||
*
|
||||
* @return an array of strings of media-types sorted from the most to the least desired.
|
||||
*/
|
||||
export function mediaTypes(header?: string, preferences?: readonly string[]): string[];
|
||||
|
||||
|
||||
/**
|
||||
* Parses the Accept-* headers of an HTTP request and returns an array of client preferences for each header.
|
||||
*
|
||||
* @param headers - the HTTP request headers object.
|
||||
*
|
||||
* @return an object with a key for each accept header result.
|
||||
*/
|
||||
export function parseAll(headers: parseAll.Headers): parseAll.Result;
|
||||
|
||||
export namespace parseAll {
|
||||
|
||||
interface Headers {
|
||||
|
||||
readonly 'accept-charset'?: string;
|
||||
readonly 'accept-encoding'?: string;
|
||||
readonly 'accept-language'?: string;
|
||||
readonly accept?: string;
|
||||
|
||||
readonly [header: string]: any;
|
||||
}
|
||||
|
||||
interface Result {
|
||||
|
||||
charsets: string[];
|
||||
encodings: string[];
|
||||
languages: string[];
|
||||
mediaTypes: string[];
|
||||
}
|
||||
}
|
||||
48
node_modules/@hapi/accept/lib/index.js
generated
vendored
Executable file
48
node_modules/@hapi/accept/lib/index.js
generated
vendored
Executable file
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
|
||||
const Header = require('./header');
|
||||
const Media = require('./media');
|
||||
|
||||
|
||||
const internals = {
|
||||
options: {
|
||||
charset: {
|
||||
type: 'accept-charset'
|
||||
},
|
||||
encoding: {
|
||||
type: 'accept-encoding',
|
||||
default: 'identity',
|
||||
equivalents: new Map([
|
||||
['x-compress', 'compress'],
|
||||
['x-gzip', 'gzip']
|
||||
])
|
||||
},
|
||||
language: {
|
||||
type: 'accept-language',
|
||||
prefixMatch: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
for (const type in internals.options) {
|
||||
exports[type] = (header, preferences) => Header.selection(header, preferences, internals.options[type]);
|
||||
|
||||
exports[`${type}s`] = (header, preferences) => Header.selections(header, preferences, internals.options[type]);
|
||||
}
|
||||
|
||||
|
||||
exports.mediaType = (header, preferences) => Media.selection(header, preferences);
|
||||
|
||||
exports.mediaTypes = (header, preferences) => Media.selections(header, preferences);
|
||||
|
||||
|
||||
exports.parseAll = function (requestHeaders) {
|
||||
|
||||
return {
|
||||
charsets: exports.charsets(requestHeaders['accept-charset']),
|
||||
encodings: exports.encodings(requestHeaders['accept-encoding']),
|
||||
languages: exports.languages(requestHeaders['accept-language']),
|
||||
mediaTypes: exports.mediaTypes(requestHeaders.accept)
|
||||
};
|
||||
};
|
||||
322
node_modules/@hapi/accept/lib/media.js
generated
vendored
Executable file
322
node_modules/@hapi/accept/lib/media.js
generated
vendored
Executable file
@@ -0,0 +1,322 @@
|
||||
'use strict';
|
||||
|
||||
const Hoek = require('@hapi/hoek');
|
||||
const Boom = require('@hapi/boom');
|
||||
|
||||
|
||||
const internals = {};
|
||||
|
||||
|
||||
exports.selection = function (header, preferences) {
|
||||
|
||||
const selections = exports.selections(header, preferences);
|
||||
return selections.length ? selections[0] : '';
|
||||
};
|
||||
|
||||
|
||||
exports.selections = function (header, preferences) {
|
||||
|
||||
Hoek.assert(!preferences || Array.isArray(preferences), 'Preferences must be an array');
|
||||
|
||||
return internals.parse(header, preferences);
|
||||
};
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.2 (https://tools.ietf.org/html/rfc7231#section-5.3.2)
|
||||
//
|
||||
// Accept = [ ( "," / ( media-range [ accept-params ] ) ) *( OWS "," [ OWS ( media-range [ accept-params ] ) ] ) ]
|
||||
// media-range = ( "*/*" / ( type "/*" ) / ( type "/" subtype ) ) *( OWS ";" OWS parameter )
|
||||
// accept-params = weight *accept-ext
|
||||
// accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
|
||||
// type = token
|
||||
// subtype = token
|
||||
// parameter = token "=" ( token / quoted-string )
|
||||
//
|
||||
// quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
||||
// qdtext = HTAB / SP /%x21 / %x23-5B / %x5D-7E / obs-text
|
||||
// obs-text = %x80-FF
|
||||
// quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
||||
// VCHAR = %x21-7E ; visible (printing) characters
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
// OWS = *( SP / HTAB )
|
||||
//
|
||||
// Accept: audio/*; q=0.2, audio/basic
|
||||
// Accept: text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c
|
||||
// Accept: text/plain, application/json;q=0.5, text/html, */*; q = 0.1
|
||||
// Accept: text/plain, application/json;q=0.5, text/html, text/drop;q=0
|
||||
// Accept: text/*, text/plain, text/plain;format=flowed, */*
|
||||
// Accept: text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5
|
||||
|
||||
|
||||
// RFC 7231 Section 5.3.1 (https://tools.ietf.org/html/rfc7231#section-5.3.1)
|
||||
//
|
||||
// The weight is normalized to a real number in the range 0 through 1,
|
||||
// where 0.001 is the least preferred and 1 is the most preferred; a
|
||||
// value of 0 means "not acceptable". If no "q" parameter is present,
|
||||
// the default weight is 1.
|
||||
//
|
||||
// weight = OWS ";" OWS "q=" qvalue
|
||||
// qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
|
||||
|
||||
|
||||
// */* type/* type/subtype
|
||||
internals.validMediaRx = /^(?:\*\/\*)|(?:[\w\!#\$%&'\*\+\-\.\^`\|~]+\/\*)|(?:[\w\!#\$%&'\*\+\-\.\^`\|~]+\/[\w\!#\$%&'\*\+\-\.\^`\|~]+)$/;
|
||||
|
||||
|
||||
internals.parse = function (raw, preferences) {
|
||||
|
||||
// Normalize header (remove spaces and temporary remove quoted strings)
|
||||
|
||||
const { header, quoted } = internals.normalize(raw);
|
||||
|
||||
// Parse selections
|
||||
|
||||
const parts = header.split(',');
|
||||
const selections = [];
|
||||
const map = {};
|
||||
|
||||
for (let i = 0; i < parts.length; ++i) {
|
||||
const part = parts[i];
|
||||
if (!part) { // Ignore empty parts or leading commas
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse parameters
|
||||
|
||||
const pairs = part.split(';');
|
||||
const token = pairs.shift().toLowerCase();
|
||||
|
||||
if (!internals.validMediaRx.test(token)) { // Ignore invalid types
|
||||
continue;
|
||||
}
|
||||
|
||||
const selection = {
|
||||
token,
|
||||
params: {},
|
||||
exts: {},
|
||||
pos: i
|
||||
};
|
||||
|
||||
// Parse key=value
|
||||
|
||||
let target = 'params';
|
||||
for (const pair of pairs) {
|
||||
const kv = pair.split('=');
|
||||
if (kv.length !== 2 ||
|
||||
!kv[1]) {
|
||||
|
||||
throw Boom.badRequest(`Invalid accept header`);
|
||||
}
|
||||
|
||||
const key = kv[0];
|
||||
let value = kv[1];
|
||||
|
||||
if (key === 'q' ||
|
||||
key === 'Q') {
|
||||
|
||||
target = 'exts';
|
||||
|
||||
value = parseFloat(value);
|
||||
if (!Number.isFinite(value) ||
|
||||
value > 1 ||
|
||||
(value < 0.001 && value !== 0)) {
|
||||
|
||||
value = 1;
|
||||
}
|
||||
|
||||
selection.q = value;
|
||||
}
|
||||
else {
|
||||
if (value[0] === '"') {
|
||||
value = `"${quoted[value]}"`;
|
||||
}
|
||||
|
||||
selection[target][kv[0]] = value;
|
||||
}
|
||||
}
|
||||
|
||||
const params = Object.keys(selection.params);
|
||||
selection.original = [''].concat(params.map((key) => `${key}=${selection.params[key]}`)).join(';');
|
||||
selection.specificity = params.length;
|
||||
|
||||
if (selection.q === undefined) { // Default no preference to q=1 (top preference)
|
||||
selection.q = 1;
|
||||
}
|
||||
|
||||
const tparts = selection.token.split('/');
|
||||
selection.type = tparts[0];
|
||||
selection.subtype = tparts[1];
|
||||
|
||||
map[selection.token] = selection;
|
||||
|
||||
if (selection.q) { // Skip denied selections (q=0)
|
||||
selections.push(selection);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort selection based on q and then position in header
|
||||
|
||||
selections.sort(internals.sort);
|
||||
|
||||
return internals.preferences(map, selections, preferences);
|
||||
};
|
||||
|
||||
|
||||
internals.normalize = function (raw) {
|
||||
|
||||
raw = raw || '*/*';
|
||||
|
||||
const normalized = {
|
||||
header: raw,
|
||||
quoted: {}
|
||||
};
|
||||
|
||||
if (raw.includes('"')) {
|
||||
let i = 0;
|
||||
normalized.header = raw.replace(/="([^"]*)"/g, ($0, $1) => {
|
||||
|
||||
const key = '"' + ++i;
|
||||
normalized.quoted[key] = $1;
|
||||
return '=' + key;
|
||||
});
|
||||
}
|
||||
|
||||
normalized.header = normalized.header.replace(/[ \t]/g, '');
|
||||
return normalized;
|
||||
};
|
||||
|
||||
|
||||
internals.sort = function (a, b) {
|
||||
|
||||
// Sort by quality score
|
||||
|
||||
if (b.q !== a.q) {
|
||||
return b.q - a.q;
|
||||
}
|
||||
|
||||
// Sort by type
|
||||
|
||||
if (a.type !== b.type) {
|
||||
return internals.innerSort(a, b, 'type');
|
||||
}
|
||||
|
||||
// Sort by subtype
|
||||
|
||||
if (a.subtype !== b.subtype) {
|
||||
return internals.innerSort(a, b, 'subtype');
|
||||
}
|
||||
|
||||
// Sort by specificity
|
||||
|
||||
if (a.specificity !== b.specificity) {
|
||||
return b.specificity - a.specificity;
|
||||
}
|
||||
|
||||
return a.pos - b.pos;
|
||||
};
|
||||
|
||||
|
||||
internals.innerSort = function (a, b, key) {
|
||||
|
||||
const aFirst = -1;
|
||||
const bFirst = 1;
|
||||
|
||||
if (a[key] === '*') {
|
||||
return bFirst;
|
||||
}
|
||||
|
||||
if (b[key] === '*') {
|
||||
return aFirst;
|
||||
}
|
||||
|
||||
return a[key] < b[key] ? aFirst : bFirst; // Group alphabetically
|
||||
};
|
||||
|
||||
|
||||
internals.preferences = function (map, selections, preferences) {
|
||||
|
||||
// Return selections if no preferences
|
||||
|
||||
if (!preferences?.length) {
|
||||
return selections.map((selection) => selection.token + selection.original);
|
||||
}
|
||||
|
||||
// Map wildcards and filter selections to preferences
|
||||
|
||||
const lowers = Object.create(null);
|
||||
const flat = Object.create(null);
|
||||
let any = false;
|
||||
|
||||
for (const preference of preferences) {
|
||||
const lower = preference.toLowerCase();
|
||||
flat[lower] = preference;
|
||||
const parts = lower.split('/');
|
||||
const type = parts[0];
|
||||
const subtype = parts[1];
|
||||
|
||||
if (type === '*') {
|
||||
Hoek.assert(subtype === '*', 'Invalid media type preference contains wildcard type with a subtype');
|
||||
any = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
lowers[type] = lowers[type] ?? Object.create(null);
|
||||
lowers[type][subtype] = preference;
|
||||
}
|
||||
|
||||
const preferred = [];
|
||||
for (const selection of selections) {
|
||||
const token = selection.token;
|
||||
const { type, subtype } = map[token];
|
||||
const subtypes = lowers[type];
|
||||
|
||||
// */*
|
||||
|
||||
if (type === '*') {
|
||||
for (const preference of Object.keys(flat)) {
|
||||
if (!map[preference]) {
|
||||
preferred.push(flat[preference]);
|
||||
}
|
||||
}
|
||||
|
||||
if (any) {
|
||||
preferred.push('*/*');
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// any
|
||||
|
||||
if (any) {
|
||||
preferred.push((flat[token] || token) + selection.original);
|
||||
continue;
|
||||
}
|
||||
|
||||
// type/subtype
|
||||
|
||||
if (subtype !== '*') {
|
||||
const pref = flat[token];
|
||||
if (pref ||
|
||||
(subtypes && subtypes['*'])) {
|
||||
|
||||
preferred.push((pref || token) + selection.original);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// type/*
|
||||
|
||||
if (subtypes) {
|
||||
for (const psub of Object.keys(subtypes)) {
|
||||
if (!map[`${type}/${psub}`]) {
|
||||
preferred.push(subtypes[psub]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return preferred;
|
||||
};
|
||||
Reference in New Issue
Block a user