/** * Normalizes a given URL by performing the following operations: * * 1. Ensures that the URL has a protocol by defaulting to 'wss://' if no protocol is provided. * 2. Creates a `URL` object to easily manipulate and normalize the URL components. * 3. Normalizes the pathname by: * - Replacing multiple consecutive slashes with a single slash. * - Removing the trailing slash if it exists. * 4. Removes the port number if it is the default port for the protocol: * - Port `80` for 'ws:' (WebSocket) protocol. * - Port `443` for 'wss:' (WebSocket Secure) protocol. * 5. Sorts the query parameters alphabetically. * 6. Clears any fragment (hash) identifier from the URL. * * @param urlString - The URL string to be normalized. * @returns A normalized URL string. */ export function normalizeWebSocketURL(urlString: string): string { // If the URL string does not contain a protocol (e.g., "http://", "https://"), // prepend "wss://" (WebSocket Secure) by default. if (urlString.indexOf('://') === -1) urlString = 'wss://' + urlString // Create a URL object from the provided URL string. const url = new URL(urlString) // Normalize the pathname by replacing multiple consecutive slashes with a single slash. url.pathname = url.pathname.replace(/\/+/g, '/') // Remove the trailing slash from the pathname if it exists. if (url.pathname.endsWith('/')) url.pathname = url.pathname.slice(0, -1) // Remove the port number if it is 80 for "ws:" protocol or 443 for "wss:" protocol, as these are default ports. if ( (url.port === '80' && url.protocol === 'ws:') || (url.port === '443' && url.protocol === 'wss:') ) url.port = '' // Sort the search parameters alphabetically. url.searchParams.sort() // Clear any hash fragment from the URL. url.hash = '' // Return the normalized URL as a string. return url.toString() } export const isValidUrl = (url: string) => { try { new URL(url) return true } catch (_) { return false } } export const isValidImageUrl = (url: string) => { const regex = /\.(jpeg|jpg|gif|png)$/ return regex.test(url) } export const isReachable = async (url: string) => { try { const response = await fetch(url, { method: 'HEAD' }) return response.ok } catch (error) { return false } } /** * Extracts a filename from a given URL. * * @param url - The URL from which to extract the filename. * @returns The filename extracted from the URL. If no filename can be extracted, a default name is provided. */ export const getFilenameFromUrl = (url: string): string => { // Create a URL object to parse the provided URL string const urlObj = new URL(url) // Extract the pathname from the URL object const pathname = urlObj.pathname // Extract the filename from the pathname. The filename is the last segment after the last '/' // If pathname is empty or does not end with a filename, use 'downloaded_file' as the default const filename = pathname.substring(pathname.lastIndexOf('/') + 1) || 'downloaded_file' // Return the extracted filename return filename }