UNPKG

wttp-core

Version:

Core contracts, interfaces, and TypeScript types for the Web3 Transfer Protocol (WTTP).

284 lines 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.wURL = void 0; const tslib_1 = require("tslib"); const parse_uri_1 = tslib_1.__importDefault(require("parse-uri")); /** * Determines if a URL string is relative according to native URL behavior * @param url - The URL string to test * @returns true if the URL is relative (doesn't start with a protocol scheme) * @internal */ function isRelativeUrl(url) { // Match native URL behavior: anything that doesn't start with a protocol is relative // This includes paths starting with :, /, ?, #, ., or regular paths return !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url); } // in the case of ipv6, this script fails due to a failure to parse ipv6 urls with the parse-uri library /** * Extended URL class that supports string aliases in the port position * * wURL (wide URL) extends the native URL class to handle scenarios where the port position * contains non-numeric values like blockchain chain identifiers, environment names, or other aliases. * * Key features: * - Maintains 100% compatibility with native URL behavior * - Supports aliases like "mainnet", "sepolia", or large chain IDs (>65535) in port position * - Preserves all URL operations (relative resolution, property access, etc.) * - Custom toString() that displays the alias instead of processed port * - IPv6 address support with alias extraction * * @example * ```typescript * // Standard URL with valid port * const url1 = new wURL('https://api.example.com:8080/data'); * console.log(url1.port); // "8080" * console.log(url1.alias); // "8080" * * // URL with chain ID alias * const url2 = new wURL('wttp://contract.eth:11155111/api'); * console.log(url2.port); // "" (empty, invalid for native URL) * console.log(url2.alias); // "11155111" * * // URL with string alias * const url3 = new wURL('https://api.example.com:mainnet/users'); * console.log(url3.alias); // "mainnet" * console.log(url3.toString()); // "https://api.example.com:mainnet/users" * * // Relative URL resolution with alias inheritance * const base = new wURL('wttp://example.com:sepolia/base/'); * const relative = new wURL('../api/data', base); * console.log(relative.alias); // "sepolia" (inherited from base) * ``` */ class wURL extends URL { /** * Creates a new wURL instance * * @param url - The URL string, URL object, or wURL object to parse * @param base - Optional base URL for resolving relative URLs * * @throws {Error} When the URL is invalid or malformed * @throws {Error} When IPv6 format is invalid * @throws {Error} When alias format contains too many colons * * @example * ```typescript * // Absolute URLs * const url1 = new wURL('https://example.com:mainnet/api'); * const url2 = new wURL('wttp://contract.eth:11155111/data'); * * // Relative URLs with base * const base = new wURL('https://api.example.com:staging/v1/'); * const endpoint = new wURL('users/profile', base); * console.log(endpoint.alias); // "staging" (inherited from base) * * // IPv6 with alias * const ipv6 = new wURL('https://[::1]:development/local'); * console.log(ipv6.alias); // "development" * ``` */ constructor(url, base) { // Pre-process the URL to extract alias and make it URL-compatible const { processedUrl, extractedAlias } = wURL.preprocessUrl(url); let processedBaseUrl = { processedUrl: "", extractedAlias: "" }; let urlAlias = extractedAlias; // Handle relative URL resolution with alias inheritance if (base && isRelativeUrl(processedUrl)) { processedBaseUrl = wURL.preprocessUrl(base); // For relative URLs, inherit the base alias if no alias in relative URL if (base instanceof wURL) { // Direct access to alias property for wURL instances urlAlias = base.alias; } else { // Extract alias from string/URL base urlAlias = processedBaseUrl.extractedAlias; } } else { // For absolute URLs, don't pass base to avoid conflicts base = undefined; } // Construct the native URL with processed components const baseUrl = processedBaseUrl.processedUrl || base; super(new URL(processedUrl, baseUrl)); /** * The alias/chain identifier extracted from the port position * * This property stores the original value from the port position, whether it's: * - A valid port number (≤65535): alias equals port * - An invalid large number (>65535): alias stores the full number * - A string identifier: alias stores the string value * * @example * ```typescript * const url = new wURL('https://api.example.com:mainnet/data'); * console.log(url.alias); // "mainnet" * * const chainUrl = new wURL('wttp://contract.eth:11155111/api'); * console.log(chainUrl.alias); // "11155111" * ``` */ this.alias = ""; this.alias = urlAlias; } /** * Returns the string representation of the URL with alias displayed * * When the alias differs from the port, this method replaces the port * in the URL string with the alias for display purposes. * * @returns The URL string with alias in the port position * * @example * ```typescript * const url = new wURL('https://api.example.com:mainnet/data'); * console.log(url.toString()); // "https://api.example.com:mainnet/data" * console.log(url.href); // "https://api.example.com/data" (native URL) * * const validPort = new wURL('https://api.example.com:8080/data'); * console.log(validPort.toString()); // "https://api.example.com:8080/data" * ``` */ toString() { // If alias matches port, use native toString (no modification needed) if (this.alias === this.port) { return super.toString(); } // Replace the port in href with the alias for display const portString = this.port ? `:${this.port}` : ""; const aliasString = this.alias ? `:${this.alias}` : ""; const hostString = `${this.hostname}${portString}`; return this.href.replace(hostString, this.hostname + aliasString); } /** * Preprocesses a URL to extract alias and make it compatible with native URL constructor * * This static method handles the complex logic of: * - Detecting relative vs absolute URLs * - Parsing port vs alias in the authority section * - Handling IPv6 addresses with brackets * - Extracting aliases while preserving URL structure * - Creating URL strings safe for native URL constructor * * @param url - The URL to preprocess * @param base - Optional base URL (currently unused but kept for API consistency) * @returns Object containing the processed URL and extracted alias * * @throws {Error} When the URL is invalid * @throws {Error} When IPv6 format is malformed * @throws {Error} When authority contains too many colons (ambiguous parsing) * * @internal This method is primarily for internal use but exposed for testing * * @example * ```typescript * // Valid port - returned as-is * wURL.preprocessUrl('https://example.com:8080/path'); * // Returns: { processedUrl: 'https://example.com:8080/path', extractedAlias: '8080' } * * // Invalid large port - alias extracted, port removed * wURL.preprocessUrl('wttp://contract.eth:11155111/api'); * // Returns: { processedUrl: 'wttp://contract.eth/api', extractedAlias: '11155111' } * * // String alias - extracted and removed * wURL.preprocessUrl('https://api.example.com:mainnet/data'); * // Returns: { processedUrl: 'https://api.example.com/data', extractedAlias: 'mainnet' } * * // IPv6 with alias * wURL.preprocessUrl('https://[::1]:development/local'); * // Returns: { processedUrl: 'https://[::1]/local', extractedAlias: 'development' } * ``` */ static preprocessUrl(url, base) { const urlString = url.toString(); // Relative URLs can be returned as-is (no alias extraction needed) if (isRelativeUrl(urlString)) { return { processedUrl: urlString, extractedAlias: "" }; } // Parse the URL using parse-uri library for authority extraction let parsedUrl; try { parsedUrl = (0, parse_uri_1.default)(urlString, { strictMode: false }); } catch (error) { throw new Error(`Invalid URL: ${urlString}`); } if (!parsedUrl) { throw new Error(`Invalid URL: ${urlString}`); } const port = parsedUrl.port; // If port is valid (numeric and ≤65535), return original URL // Both port and alias will be the same value if (port !== "" && parseInt(port) <= 65535) { return { processedUrl: urlString, extractedAlias: port }; } let extractedAlias = ""; // Handle URLs with invalid ports or string aliases // Need to extract from authority and remove for native URL compatibility const authSplit = parsedUrl.authority.split("@"); // Handle userinfo@host:port format const hostPart = authSplit[authSplit.length - 1]; // Get the host:port part // No port/alias present, or IPv6 without port (ends with ]) if (!hostPart.includes(":") || hostPart.endsWith("]")) { return { processedUrl: urlString, extractedAlias: "" }; } // IPv6 vs regular hostname handling const isIPv6 = hostPart.startsWith("["); let actualHost; if (isIPv6) { // IPv6 format: [address]:port or [address]:alias // Find the closing bracket to separate address from port const bracketEnd = hostPart.indexOf("]"); if (bracketEnd === -1) { throw new Error(`Invalid IPv6 format: ${parsedUrl.authority}`); } actualHost = hostPart.substring(0, bracketEnd + 1); // Include the ] const portPart = hostPart.substring(bracketEnd + 2); // Skip ]: extractedAlias = portPart || ""; } else { // Regular hostname format: hostname:port or hostname:alias const colonSplit = hostPart.split(":"); if (colonSplit.length === 2) { actualHost = colonSplit[0]; extractedAlias = colonSplit[1]; } else if (colonSplit.length > 2) { // Too many colons - ambiguous parsing throw new Error(`Invalid alias format: ${parsedUrl.authority}`); } else { actualHost = hostPart; extractedAlias = ""; } } // Remove the alias from the URL to make it compatible with native URL constructor if (extractedAlias) { const extractedUrl = urlString.replace(`${actualHost}:${extractedAlias}`, actualHost); // Normalize through URL constructor to ensure proper formatting const processedUrl = new URL(extractedUrl).href; return { processedUrl, extractedAlias }; } else { return { processedUrl: urlString, extractedAlias: "" }; } } } exports.wURL = wURL; //# sourceMappingURL=wurl.js.map