"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "normalizeUrl", {
    enumerable: true,
    get: function() {
        return normalizeUrl;
    }
});
const DATA_URL_DEFAULT_MIME_TYPE = "text/plain";
const DATA_URL_DEFAULT_CHARSET = "us-ascii";
const testParameter = (name, filters)=>filters.some((filter)=>filter instanceof RegExp ? filter.test(name) : filter === name);
const supportedProtocols = new Set([
    "https:",
    "http:",
    "file:"
]);
const hasCustomProtocol = (urlString)=>{
    try {
        const { protocol } = new URL(urlString);
        return protocol.endsWith(":") && !supportedProtocols.has(protocol);
    } catch  {
        return false;
    }
};
const normalizeDataURL = (urlString, { stripHash })=>{
    const match = /^data:(?<type>[^,]*?),(?<data>[^#]*?)(?:#(?<hash>.*))?$/.exec(urlString);
    if (!match) {
        throw new Error(`Invalid URL: ${urlString}`);
    }
    let { type, data, hash } = match.groups;
    const mediaType = type.split(";");
    hash = stripHash ? "" : hash;
    let isBase64 = false;
    if (mediaType[mediaType.length - 1] === "base64") {
        mediaType.pop();
        isBase64 = true;
    }
    const mimeType = mediaType.shift()?.toLowerCase() ?? "";
    const attributes = mediaType.map((attribute)=>{
        let [key, value = ""] = attribute.split("=").map((string)=>string.trim());
        if (key === "charset") {
            value = value.toLowerCase();
            if (value === DATA_URL_DEFAULT_CHARSET) {
                return "";
            }
        }
        return `${key}${value ? `=${value}` : ""}`;
    }).filter(Boolean);
    const normalizedMediaType = [
        ...attributes
    ];
    if (isBase64) {
        normalizedMediaType.push("base64");
    }
    if (normalizedMediaType.length > 0 || mimeType && mimeType !== DATA_URL_DEFAULT_MIME_TYPE) {
        normalizedMediaType.unshift(mimeType);
    }
    return `data:${normalizedMediaType.join(";")},${isBase64 ? data.trim() : data}${hash ? `#${hash}` : ""}`;
};
function normalizeUrl(urlString, options) {
    options = {
        defaultProtocol: "http",
        normalizeProtocol: true,
        forceHttp: false,
        forceHttps: false,
        stripAuthentication: true,
        stripHash: false,
        stripTextFragment: true,
        stripWWW: true,
        removeQueryParameters: [
            /^utm_\w+/i
        ],
        removeTrailingSlash: true,
        removeSingleSlash: true,
        removeDirectoryIndex: false,
        removeExplicitPort: false,
        sortQueryParameters: true,
        ...options
    };
    if (typeof options.defaultProtocol === "string" && !options.defaultProtocol.endsWith(":")) {
        options.defaultProtocol = `${options.defaultProtocol}:`;
    }
    urlString = urlString.trim();
    if (/^data:/i.test(urlString)) {
        return normalizeDataURL(urlString, options);
    }
    if (hasCustomProtocol(urlString)) {
        return urlString;
    }
    const hasRelativeProtocol = urlString.startsWith("//");
    const isRelativeUrl = !hasRelativeProtocol && /^\.*\//.test(urlString);
    if (!isRelativeUrl) {
        urlString = urlString.replace(/^(?!(?:\w+:)?\/\/)|^\/\//, options.defaultProtocol);
    }
    const urlObject = new URL(urlString);
    if (options.forceHttp && options.forceHttps) {
        throw new Error("The `forceHttp` and `forceHttps` options cannot be used together");
    }
    if (options.forceHttp && urlObject.protocol === "https:") {
        urlObject.protocol = "http:";
    }
    if (options.forceHttps && urlObject.protocol === "http:") {
        urlObject.protocol = "https:";
    }
    if (options.stripAuthentication) {
        urlObject.username = "";
        urlObject.password = "";
    }
    if (options.stripHash) {
        urlObject.hash = "";
    } else if (options.stripTextFragment) {
        urlObject.hash = urlObject.hash.replace(/#?:~:text.*?$/i, "");
    }
    if (urlObject.pathname) {
        const protocolRegex = /\b[a-z][a-z\d+\-.]{1,50}:\/\//g;
        let lastIndex = 0;
        let result = "";
        for(;;){
            const match = protocolRegex.exec(urlObject.pathname);
            if (!match) {
                break;
            }
            const protocol = match[0];
            const protocolAtIndex = match.index;
            const intermediate = urlObject.pathname.slice(lastIndex, protocolAtIndex);
            result += intermediate.replace(/\/{2,}/g, "/");
            result += protocol;
            lastIndex = protocolAtIndex + protocol.length;
        }
        const remnant = urlObject.pathname.slice(lastIndex, urlObject.pathname.length);
        result += remnant.replace(/\/{2,}/g, "/");
        urlObject.pathname = result;
    }
    if (urlObject.pathname) {
        try {
            urlObject.pathname = decodeURI(urlObject.pathname);
        } catch  {}
    }
    if (options.removeDirectoryIndex === true) {
        options.removeDirectoryIndex = [
            /^index\.[a-z]+$/
        ];
    }
    if (Array.isArray(options.removeDirectoryIndex) && options.removeDirectoryIndex.length > 0) {
        let pathComponents = urlObject.pathname.split("/");
        const lastComponent = pathComponents[pathComponents.length - 1];
        if (testParameter(lastComponent, options.removeDirectoryIndex)) {
            pathComponents = pathComponents.slice(0, -1);
            urlObject.pathname = pathComponents.slice(1).join("/") + "/";
        }
    }
    if (urlObject.hostname) {
        urlObject.hostname = urlObject.hostname.replace(/\.$/, "");
        if (options.stripWWW && /^www\.(?!www\.)[a-z\-\d]{1,63}\.[a-z.\-\d]{2,63}$/.test(urlObject.hostname)) {
            urlObject.hostname = urlObject.hostname.replace(/^www\./, "");
        }
    }
    if (Array.isArray(options.removeQueryParameters)) {
        for (const key of [
            ...urlObject.searchParams.keys()
        ]){
            if (testParameter(key, options.removeQueryParameters)) {
                urlObject.searchParams.delete(key);
            }
        }
    }
    if (!Array.isArray(options.keepQueryParameters) && options.removeQueryParameters === true) {
        urlObject.search = "";
    }
    if (Array.isArray(options.keepQueryParameters) && options.keepQueryParameters.length > 0) {
        for (const key of [
            ...urlObject.searchParams.keys()
        ]){
            if (!testParameter(key, options.keepQueryParameters)) {
                urlObject.searchParams.delete(key);
            }
        }
    }
    if (options.sortQueryParameters) {
        urlObject.searchParams.sort();
        try {
            urlObject.search = decodeURIComponent(urlObject.search);
        } catch  {}
    }
    if (options.removeTrailingSlash) {
        urlObject.pathname = urlObject.pathname.replace(/\/$/, "");
    }
    if (options.removeExplicitPort && urlObject.port) {
        urlObject.port = "";
    }
    const oldUrlString = urlString;
    urlString = urlObject.toString();
    if (!options.removeSingleSlash && urlObject.pathname === "/" && !oldUrlString.endsWith("/") && urlObject.hash === "") {
        urlString = urlString.replace(/\/$/, "");
    }
    if ((options.removeTrailingSlash || urlObject.pathname === "/") && urlObject.hash === "" && options.removeSingleSlash) {
        urlString = urlString.replace(/\/$/, "");
    }
    if (hasRelativeProtocol && !options.normalizeProtocol) {
        urlString = urlString.replace(/^http:\/\//, "//");
    }
    if (options.stripProtocol) {
        urlString = urlString.replace(/^(?:https?:)?\/\//, "");
    }
    return urlString;
}

//# sourceMappingURL=normalize-url.js.map