/** * Node.js-compatible `url` for Bare. * * bare-url is WHATWG-only (no url.resolve / url.parse / url.format). * docker-modem (via dockerode) requires the legacy Node API: * url.resolve, url.parse, url.format, url.URL * * Mapped from package.json imports under the "bare" condition. */ 'use strict' let URLImpl let URLSearchParamsImpl try { const bare = require('bare-url') URLImpl = bare.URL || bare URLSearchParamsImpl = bare.URLSearchParams || (typeof globalThis !== 'undefined' && globalThis.URLSearchParams) || null } catch { URLImpl = globalThis.URL URLSearchParamsImpl = globalThis.URLSearchParams } const URL = URLImpl function isAbsoluteUrl(s) { return /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(s) } /** * Minimal Node url.parse compatible object. * @param {string} urlStr * @param {boolean} [parseQueryString] */ function parse(urlStr, parseQueryString) { const input = String(urlStr ?? '') try { let u if (isAbsoluteUrl(input)) { u = new URL(input) } else if (input.startsWith('//')) { u = new URL('http:' + input) } else { // Path-only / relative — give a dummy base then re-map u = new URL(input, 'http://peardock.invalid') } const dummy = u.hostname === 'peardock.invalid' && !isAbsoluteUrl(input) const auth = u.username || u.password ? decodeURIComponent(u.username) + (u.password ? ':' + decodeURIComponent(u.password) : '') : null const search = u.search || null let query = null if (search) { if (parseQueryString && URLSearchParamsImpl) { query = Object.fromEntries(new URLSearchParamsImpl(u.searchParams || search)) } else { query = search.startsWith('?') ? search.slice(1) : search } } const pathname = u.pathname || null const path = (pathname || '') + (search || '') || null return { protocol: dummy ? null : u.protocol || null, slashes: dummy ? null : true, auth: dummy ? null : auth, host: dummy ? null : u.host || null, port: dummy ? null : u.port || null, hostname: dummy ? null : u.hostname || null, hash: u.hash || null, search, query, pathname, path, href: dummy ? input : u.href, } } catch { return { protocol: null, slashes: null, auth: null, host: null, port: null, hostname: null, hash: null, search: null, query: null, pathname: null, path: null, href: input, } } } /** * Minimal Node url.format. * @param {string|object} urlObj */ function format(urlObj) { if (urlObj == null) return '' if (typeof urlObj === 'string') return urlObj if (typeof URLImpl === 'function' && urlObj instanceof URLImpl) return urlObj.href let protocol = urlObj.protocol || '' if (protocol && protocol[protocol.length - 1] !== ':') protocol += ':' let auth = '' if (urlObj.auth) { auth = String(urlObj.auth).replace(/@/g, '') + '@' } let host = '' if (urlObj.host != null && urlObj.host !== '') { host = String(urlObj.host) } else if (urlObj.hostname != null && urlObj.hostname !== '') { host = String(urlObj.hostname) if (urlObj.port != null && urlObj.port !== '') host += ':' + urlObj.port } let pathname = urlObj.pathname != null ? String(urlObj.pathname) : '' if (pathname && pathname[0] !== '/' && host) pathname = '/' + pathname let search = '' if (urlObj.search != null && urlObj.search !== '') { search = String(urlObj.search) if (search[0] !== '?') search = '?' + search } else if (urlObj.query != null && urlObj.query !== '') { if (typeof urlObj.query === 'string') { search = urlObj.query[0] === '?' ? urlObj.query : '?' + urlObj.query } else if (typeof urlObj.query === 'object' && URLSearchParamsImpl) { const q = new URLSearchParamsImpl(urlObj.query).toString() if (q) search = '?' + q } } let hash = urlObj.hash != null ? String(urlObj.hash) : '' if (hash && hash[0] !== '#') hash = '#' + hash // Match Node quirks roughly: with host → scheme://host/path; without → scheme:/path if (host) { const slashes = protocol ? '//' : '' return protocol + slashes + auth + host + pathname + search + hash } if (protocol) { // Node: url.format({ protocol: 'http:', pathname: '/x' }) => 'http:/x' return protocol + (pathname.startsWith('/') ? pathname : '/' + pathname) + search + hash } return pathname + search + hash } /** * Node url.resolve(from, to) * @param {string} from * @param {string} to */ function resolve(from, to) { const base = String(from ?? '') const rel = String(to ?? '') if (!rel) { try { return isAbsoluteUrl(base) ? new URL(base).href : format(parse(base)) } catch { return base } } if (isAbsoluteUrl(rel)) { try { return new URL(rel).href } catch { return rel } } try { if (isAbsoluteUrl(base)) { return new URL(rel, base).href } // Path-only base (e.g. "/v1.41/" + "containers/json") const origin = 'http://peardock.invalid' const b = base.startsWith('/') ? base : '/' + base const resolved = new URL(rel, origin + b) return resolved.pathname + resolved.search + resolved.hash } catch { if (rel.startsWith('/')) return rel const slash = base.endsWith('/') ? '' : '/' return base + slash + rel } } function pathToFileURL(filepath) { if (typeof URLImpl !== 'function') throw new Error('URL not available') const path = require('path') let resolved = path.resolve(String(filepath)) // Windows drive letters if (/^[a-zA-Z]:[\\/]/.test(resolved)) { resolved = '/' + resolved.replace(/\\/g, '/') } else if (!resolved.startsWith('/')) { resolved = '/' + resolved } return new URLImpl('file://' + encodeURI(resolved).replace(/#/g, '%23')) } function fileURLToPath(url) { const u = typeof url === 'string' ? new URLImpl(url) : url if (u.protocol !== 'file:') { throw new TypeError('Must be a file URL') } let p = decodeURIComponent(u.pathname) // /C:/... → C:\... on windows-like if (/^\/[a-zA-Z]:\//.test(p)) { p = p.slice(1).replace(/\//g, require('path').sep) } return p } function domainToASCII(domain) { return String(domain ?? '') } function domainToUnicode(domain) { return String(domain ?? '') } function urlToHttpOptions(url) { const u = typeof url === 'string' ? new URLImpl(url) : url return { protocol: u.protocol, hostname: u.hostname, hash: u.hash, search: u.search, pathname: u.pathname, path: u.pathname + u.search, href: u.href, port: u.port, auth: u.username ? u.username + (u.password ? ':' + u.password : '') : '', } } module.exports = { URL, Url: URL, URLSearchParams: URLSearchParamsImpl, parse, format, resolve, pathToFileURL, fileURLToPath, domainToASCII, domainToUnicode, urlToHttpOptions, }