@catbee/utils
Version:
A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.
260 lines (255 loc) • 8.03 kB
JavaScript
/*
* The MIT License
*
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
;
var url = require('url');
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
function appendQueryParams(url$1, params) {
const urlObj = new url.URL(url$1);
for (const [key, value] of Object.entries(params)) {
urlObj.searchParams.set(key, value.toString());
}
return urlObj.toString();
}
__name(appendQueryParams, "appendQueryParams");
function parseQueryString(query) {
const cleanQuery = query.startsWith("?") ? query.slice(1) : query;
return Object.fromEntries(new url.URLSearchParams(cleanQuery));
}
__name(parseQueryString, "parseQueryString");
function isValidUrl(url$1, requireHttps = false) {
try {
const parsedUrl = new url.URL(url$1);
if (!parsedUrl.protocol || !parsedUrl.hostname) return false;
if (requireHttps && parsedUrl.protocol !== "https:") return false;
return true;
} catch {
return false;
}
}
__name(isValidUrl, "isValidUrl");
function getDomain(url$1, removeSubdomains = false) {
try {
const { hostname } = new url.URL(url$1);
if (!removeSubdomains) {
return hostname;
}
const parts = hostname.split(".");
if (parts.length <= 2) return hostname;
const secondLevelDomains = [
"co",
"com",
"org",
"net",
"gov",
"edu",
"ac"
];
const sld = parts[parts.length - 2];
if (secondLevelDomains.includes(sld) && parts.length > 2) {
return parts.slice(-3).join(".");
}
return parts.slice(-2).join(".");
} catch {
return "";
}
}
__name(getDomain, "getDomain");
function joinPaths(...segments) {
let result = segments.filter((s) => s !== void 0 && s !== null).map((segment, index) => {
if (index === 0) return segment.replace(/\/+$/, "");
return segment.replace(/^\/+|\/+$/g, "");
}).filter(Boolean).join("/");
if (segments[0] === "" && !result.startsWith("/")) result = "/" + result;
return result;
}
__name(joinPaths, "joinPaths");
function normalizeUrl(url$1, base) {
try {
let normalized = url$1.startsWith("//") ? `https:${url$1}` : url$1;
const parsedUrl = base ? new url.URL(normalized, base) : new url.URL(normalized);
parsedUrl.pathname = parsedUrl.pathname.replace(/\/+/g, "/");
parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, "");
parsedUrl.hostname = parsedUrl.hostname.toLowerCase();
return parsedUrl.toString();
} catch {
return url$1;
}
}
__name(normalizeUrl, "normalizeUrl");
function updateQueryParam(url$1, key, value) {
try {
const urlObj = new url.URL(url$1);
urlObj.searchParams.set(key, String(value));
return urlObj.toString();
} catch {
return url$1;
}
}
__name(updateQueryParam, "updateQueryParam");
function createUrlBuilder(baseUrl) {
return {
/**
* Creates a full URL with the given path and query parameters.
*
* @param {string} path - The path to append to the base URL.
* @param {Record<string, any>} [params] - Query parameters to add.
* @returns {string} The complete URL.
*/
path(path, params) {
const url = joinPaths(baseUrl, path);
return params ? appendQueryParams(url, params) : url;
},
/**
* Creates a full URL with query parameters but no additional path.
*
* @param {Record<string, any>} params - Query parameters to add.
* @returns {string} The complete URL with query parameters.
*/
query(params) {
return appendQueryParams(baseUrl, params);
}
};
}
__name(createUrlBuilder, "createUrlBuilder");
function extractQueryParams(url$1, paramNames) {
try {
const parsedUrl = new url.URL(url$1);
const result = {};
for (const name of paramNames) {
const value = parsedUrl.searchParams.get(name);
if (value !== null) {
result[name] = value;
}
}
return result;
} catch {
return {};
}
}
__name(extractQueryParams, "extractQueryParams");
function removeQueryParams(url$1, paramsToRemove) {
try {
const parsedUrl = new url.URL(url$1);
for (const param of paramsToRemove) {
parsedUrl.searchParams.delete(param);
}
return parsedUrl.toString();
} catch {
return url$1;
}
}
__name(removeQueryParams, "removeQueryParams");
function getExtension(url$1) {
try {
const { pathname } = new url.URL(url$1);
const match = pathname.match(/\.([^./\\?#]+)$/);
return match ? match[1].toLowerCase() : "";
} catch {
const match = url$1.match(/\.([^./\\?#]+)$/);
return match ? match[1].toLowerCase() : "";
}
}
__name(getExtension, "getExtension");
function parseTypedQueryParams(url$1, converters) {
try {
const { searchParams } = new url.URL(url$1);
const result = {};
for (const [key, value] of searchParams.entries()) {
if (converters && key in converters) {
try {
result[key] = converters[key](value);
} catch {
}
} else {
result[key] = value;
}
}
return result;
} catch {
return {};
}
}
__name(parseTypedQueryParams, "parseTypedQueryParams");
function getSubdomain(url$1) {
try {
const { hostname } = new url.URL(url$1);
const parts = hostname.split(".");
if (parts.length <= 2) return "";
return parts.slice(0, -2).join(".");
} catch {
return "";
}
}
__name(getSubdomain, "getSubdomain");
function isRelativeUrl(url$1) {
if (!url$1 || typeof url$1 !== "string") return false;
try {
new url.URL(url$1);
return false;
} catch {
return url$1.startsWith("/") || url$1.startsWith("./") || url$1.startsWith("../");
}
}
__name(isRelativeUrl, "isRelativeUrl");
function toAbsoluteUrl(relativeUrl, baseUrl) {
try {
return new url.URL(relativeUrl, baseUrl).toString();
} catch {
return relativeUrl;
}
}
__name(toAbsoluteUrl, "toAbsoluteUrl");
function sanitizeUrl(url$1, allowedProtocols = [
"http",
"https"
]) {
try {
const parsedUrl = new url.URL(url$1);
const protocol = parsedUrl.protocol.replace(":", "");
if (!allowedProtocols.includes(protocol)) {
return null;
}
return parsedUrl.toString();
} catch {
return null;
}
}
__name(sanitizeUrl, "sanitizeUrl");
exports.appendQueryParams = appendQueryParams;
exports.createUrlBuilder = createUrlBuilder;
exports.extractQueryParams = extractQueryParams;
exports.getDomain = getDomain;
exports.getExtension = getExtension;
exports.getSubdomain = getSubdomain;
exports.isRelativeUrl = isRelativeUrl;
exports.isValidUrl = isValidUrl;
exports.joinPaths = joinPaths;
exports.normalizeUrl = normalizeUrl;
exports.parseQueryString = parseQueryString;
exports.parseTypedQueryParams = parseTypedQueryParams;
exports.removeQueryParams = removeQueryParams;
exports.sanitizeUrl = sanitizeUrl;
exports.toAbsoluteUrl = toAbsoluteUrl;
exports.updateQueryParam = updateQueryParam;