170 lines
5.2 KiB
Plaintext
170 lines
5.2 KiB
Plaintext
"use strict";
|
|
/*!
|
|
* content-type
|
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
|
* MIT Licensed
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.format = format;
|
|
exports.parse = parse;
|
|
const TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]*$/;
|
|
const TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
/**
|
|
* RegExp to match chars that must be quoted-pair in RFC 9110 sec 5.6.4
|
|
*/
|
|
const QUOTE_REGEXP = /[\\"]/g;
|
|
/**
|
|
* RegExp to match type in RFC 9110 sec 8.3.1
|
|
*
|
|
* media-type = type "/" subtype
|
|
* type = token
|
|
* subtype = token
|
|
*/
|
|
const TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
|
/**
|
|
* Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`.
|
|
*/
|
|
const NullObject = /* @__PURE__ */ (() => {
|
|
const C = function () { };
|
|
C.prototype = Object.create(null);
|
|
return C;
|
|
})();
|
|
/**
|
|
* Format an object into a `Content-Type` header.
|
|
*/
|
|
function format(obj) {
|
|
const { type, parameters } = obj;
|
|
if (!type || !TYPE_REGEXP.test(type)) {
|
|
throw new TypeError(`Invalid type: ${type}`);
|
|
}
|
|
let result = type;
|
|
if (parameters) {
|
|
for (const param of Object.keys(parameters)) {
|
|
if (!TOKEN_REGEXP.test(param)) {
|
|
throw new TypeError(`Invalid parameter name: ${param}`);
|
|
}
|
|
result += `; ${param}=${qstring(parameters[param])}`;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* Parse a `Content-Type` header.
|
|
*/
|
|
function parse(header, options) {
|
|
const len = header.length;
|
|
let index = skipOWS(header, 0, len);
|
|
const valueStart = index;
|
|
index = skipValue(header, index, len);
|
|
const valueEnd = trailingOWS(header, valueStart, index);
|
|
const type = header.slice(valueStart, valueEnd).toLowerCase();
|
|
const parameters = options?.parameters === false
|
|
? new NullObject()
|
|
: parseParameters(header, index, len);
|
|
return { type, parameters };
|
|
}
|
|
const SP = 32; // " "
|
|
const HTAB = 9; // "\t"
|
|
const SEMI = 59; // ";"
|
|
const EQ = 61; // "="
|
|
const DQUOTE = 34; // '"'
|
|
const BSLASH = 92; // "\\"
|
|
/**
|
|
* Parses the parameters of a `Content-Type` header starting at the given index.
|
|
*/
|
|
function parseParameters(header, index, len) {
|
|
const parameters = new NullObject();
|
|
parameter: while (index < len) {
|
|
index = skipOWS(header, index + 1 /* Skip over ; */, len);
|
|
const keyStart = index;
|
|
while (index < len) {
|
|
const code = header.charCodeAt(index);
|
|
if (code === SEMI)
|
|
continue parameter;
|
|
if (code === EQ) {
|
|
const keyEnd = trailingOWS(header, keyStart, index);
|
|
const key = header.slice(keyStart, keyEnd).toLowerCase();
|
|
index = skipOWS(header, index + 1, len);
|
|
if (index < len && header.charCodeAt(index) === DQUOTE) {
|
|
index++;
|
|
let value = "";
|
|
while (index < len) {
|
|
const code = header.charCodeAt(index++);
|
|
if (code === DQUOTE) {
|
|
index = skipValue(header, index, len);
|
|
if (parameters[key] === undefined)
|
|
parameters[key] = value;
|
|
break;
|
|
}
|
|
if (code === BSLASH && index < len) {
|
|
value += header[index++];
|
|
continue;
|
|
}
|
|
value += String.fromCharCode(code);
|
|
}
|
|
continue parameter;
|
|
}
|
|
const valueStart = index;
|
|
index = skipValue(header, index, len);
|
|
if (parameters[key] === undefined) {
|
|
const valueEnd = trailingOWS(header, valueStart, index);
|
|
parameters[key] = header.slice(valueStart, valueEnd);
|
|
}
|
|
continue parameter;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
return parameters;
|
|
}
|
|
/**
|
|
* Skip over characters until a semicolon.
|
|
*/
|
|
function skipValue(str, index, len) {
|
|
while (index < len) {
|
|
const char = str.charCodeAt(index);
|
|
if (char === SEMI)
|
|
break;
|
|
index++;
|
|
}
|
|
return index;
|
|
}
|
|
/**
|
|
* Skip optional whitespace (OWS) in an HTTP header value.
|
|
*
|
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
|
*/
|
|
function skipOWS(header, index, len) {
|
|
while (index < len) {
|
|
const char = header.charCodeAt(index);
|
|
if (char !== SP && char !== HTAB)
|
|
break;
|
|
index++;
|
|
}
|
|
return index;
|
|
}
|
|
/**
|
|
* Trim optional whitespace (OWS) from the end of a substring.
|
|
*
|
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
|
*/
|
|
function trailingOWS(header, start, end) {
|
|
while (end > start) {
|
|
const char = header.charCodeAt(end - 1);
|
|
if (char !== SP && char !== HTAB)
|
|
break;
|
|
end--;
|
|
}
|
|
return end;
|
|
}
|
|
/**
|
|
* Serialize a parameter value.
|
|
*/
|
|
function qstring(str) {
|
|
if (TOKEN_REGEXP.test(str))
|
|
return str;
|
|
if (TEXT_REGEXP.test(str))
|
|
return `"${str.replace(QUOTE_REGEXP, "\\$&")}"`;
|
|
throw new TypeError(`Invalid parameter value: ${str}`);
|
|
}
|
|
//# sourceMappingURL=index.js.map |