UNPKG

@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.

243 lines (239 loc) 7.51 kB
/* * 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. */ import { URL, URLSearchParams } from 'url'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); function appendQueryParams(url, params) { const urlObj = new URL(url); 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 URLSearchParams(cleanQuery)); } __name(parseQueryString, "parseQueryString"); function isValidUrl(url, requireHttps = false) { try { const parsedUrl = new URL(url); 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, removeSubdomains = false) { try { const { hostname } = new URL(url); 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, base) { try { let normalized = url.startsWith("//") ? `https:${url}` : url; const parsedUrl = base ? new URL(normalized, base) : new URL(normalized); parsedUrl.pathname = parsedUrl.pathname.replace(/\/+/g, "/"); parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, ""); parsedUrl.hostname = parsedUrl.hostname.toLowerCase(); return parsedUrl.toString(); } catch { return url; } } __name(normalizeUrl, "normalizeUrl"); function updateQueryParam(url, key, value) { try { const urlObj = new URL(url); urlObj.searchParams.set(key, String(value)); return urlObj.toString(); } catch { return url; } } __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, paramNames) { try { const parsedUrl = new URL(url); 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, paramsToRemove) { try { const parsedUrl = new URL(url); for (const param of paramsToRemove) { parsedUrl.searchParams.delete(param); } return parsedUrl.toString(); } catch { return url; } } __name(removeQueryParams, "removeQueryParams"); function getExtension(url) { try { const { pathname } = new URL(url); const match = pathname.match(/\.([^./\\?#]+)$/); return match ? match[1].toLowerCase() : ""; } catch { const match = url.match(/\.([^./\\?#]+)$/); return match ? match[1].toLowerCase() : ""; } } __name(getExtension, "getExtension"); function parseTypedQueryParams(url, converters) { try { const { searchParams } = new URL(url); 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) { try { const { hostname } = new URL(url); const parts = hostname.split("."); if (parts.length <= 2) return ""; return parts.slice(0, -2).join("."); } catch { return ""; } } __name(getSubdomain, "getSubdomain"); function isRelativeUrl(url) { if (!url || typeof url !== "string") return false; try { new URL(url); return false; } catch { return url.startsWith("/") || url.startsWith("./") || url.startsWith("../"); } } __name(isRelativeUrl, "isRelativeUrl"); function toAbsoluteUrl(relativeUrl, baseUrl) { try { return new URL(relativeUrl, baseUrl).toString(); } catch { return relativeUrl; } } __name(toAbsoluteUrl, "toAbsoluteUrl"); function sanitizeUrl(url, allowedProtocols = [ "http", "https" ]) { try { const parsedUrl = new URL(url); const protocol = parsedUrl.protocol.replace(":", ""); if (!allowedProtocols.includes(protocol)) { return null; } return parsedUrl.toString(); } catch { return null; } } __name(sanitizeUrl, "sanitizeUrl"); export { appendQueryParams, createUrlBuilder, extractQueryParams, getDomain, getExtension, getSubdomain, isRelativeUrl, isValidUrl, joinPaths, normalizeUrl, parseQueryString, parseTypedQueryParams, removeQueryParams, sanitizeUrl, toAbsoluteUrl, updateQueryParam };