"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 stopChar = options?.comma === true ? COMMA : 65536; // Sentinel for "no stop char".
    const len = header.length;
    let index = skipOWS(header, options?.start ?? 0, len);
    const valueStart = index;
    index = skipValue(header, index, len, stopChar);
    const valueEnd = trailingOWS(header, valueStart, index);
    const type = header.slice(valueStart, valueEnd).toLowerCase();
    if (options?.parameters === false) {
        return { type, index, parameters: new NullObject() };
    }
    return parseParameters(header, type, index, len, stopChar);
}
const SP = 32; // " "
const HTAB = 9; // "\t"
const SEMI = 59; // ";"
const EQ = 61; // "="
const DQUOTE = 34; // '"'
const BSLASH = 92; // "\\"
const COMMA = 44; // ","
/**
 * Parses the parameters of a `Content-Type` header starting at the given index.
 */
function parseParameters(header, type, index, len, stopChar) {
    const parameters = new NullObject();
    parameter: while (index < len) {
        if (header.charCodeAt(index) === stopChar)
            break;
        index = skipOWS(header, index + 1 /* Skip over ; */, len);
        const keyStart = index;
        while (index < len) {
            const code = header.charCodeAt(index);
            if (code === stopChar)
                break parameter;
            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, stopChar);
                            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, stopChar);
                if (parameters[key] === undefined) {
                    const valueEnd = trailingOWS(header, valueStart, index);
                    parameters[key] = header.slice(valueStart, valueEnd);
                }
                continue parameter;
            }
            index++;
        }
    }
    return { type, index, parameters };
}
/**
 * Skip over characters until a semicolon or other exit character.
 */
function skipValue(str, index, len, stopChar) {
    while (index < len) {
        const code = str.charCodeAt(index);
        if (code === SEMI || code === stopChar)
            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