UNPKG

@wroud/navigation

Version:

A flexible, pattern-matching navigation system for JavaScript applications with built-in routing, browser integration, and navigation state management

136 lines 3.97 kB
/** * Path utilities for URL pattern matching */ /** * Splits a string (URL or pattern) into its path part and query part. */ export function splitPathAndQuery(value) { const qIndex = value.indexOf("?"); if (qIndex === -1) { return { path: value, query: "" }; } return { path: value.slice(0, qIndex), query: value.slice(qIndex + 1) }; } /** * Parse query parameter definitions from a pattern's query string. * Format: "key=:param<type>&key2=:param2" */ export function parseQueryPatternDefs(queryPattern) { if (!queryPattern) return []; const defs = []; for (const part of queryPattern.split("&")) { const eqIndex = part.indexOf("="); if (eqIndex === -1) continue; const key = part.slice(0, eqIndex); let valuePart = part.slice(eqIndex + 1); if (!valuePart.startsWith(":")) continue; // Check for required flag (trailing !) const required = valuePart.endsWith("!"); if (required) { valuePart = valuePart.slice(0, -1); } const paramName = extractParamName(valuePart); const paramType = extractParamType(valuePart); defs.push({ key, paramName, paramType, required }); } return defs; } /** * Parse a URL query string into a key-value map. * Handles repeated keys by keeping the last value. */ export function parseQueryString(query) { if (!query) return {}; const params = new URLSearchParams(query); const result = {}; for (const [key, value] of params) { result[key] = value; } return result; } /** * Splits a URL or pattern string into segments */ export function splitPath(path) { // Handle root path specially if (path === "/" || path === "") { return [""]; } // Remove leading/trailing slashes and split return path.replace(/^\/|\/$/g, "").split("/"); } /** * Checks if a segment is a parameter (starts with ":") */ export function isParameterSegment(segment) { return segment.startsWith(":"); } /** * Checks if a segment is a wildcard parameter (starts with ":" and ends with "*") */ export function isWildcardSegment(segment) { return segment.startsWith(":") && segment.endsWith("*"); } /** * Extracts parameter name from a parameter segment */ export function extractParamName(segment) { let name = segment.slice(1); // remove initial ':' if (name.endsWith("*")) { name = name.slice(0, -1); } const typeStart = name.indexOf("<"); if (typeStart !== -1) { name = name.slice(0, typeStart); } return name; } export function extractParamType(segment) { const startIndex = segment.indexOf("<"); const endIndex = segment.indexOf(">"); if (startIndex === -1 || endIndex === -1) { return "string"; } return segment.slice(startIndex + 1, endIndex); } const pathCache = new Map(); const MAX_CACHE_SIZE = 100; /** * Evicts the oldest entry from a cache if it exceeds the size limit */ function evictCacheIfNeeded(cache) { if (cache.size >= MAX_CACHE_SIZE) { // Remove the first entry in the iterator const firstKey = cache.keys().next().value; if (firstKey) { cache.delete(firstKey); } } } /** * Joins segments into a URL path with caching for common paths */ export function joinPath(segments) { if (segments.length === 0) { return "/"; } // For small segment arrays, use a cache key if (segments.length <= 5) { const cacheKey = segments.join("/"); const cached = pathCache.get(cacheKey); if (cached) { return cached; } const result = "/" + cacheKey; // Use the shared cache management function evictCacheIfNeeded(pathCache); pathCache.set(cacheKey, result); return result; } return "/" + segments.join("/"); } //# sourceMappingURL=path-utils.js.map