This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

232
node_modules/@hapi/accept/lib/header.js generated vendored Executable file
View 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
View 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
View 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
View 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;
};