@socketsupply/socket
Version:
A Cross-Platform, Native Runtime for Desktop and Mobile Apps — Create apps using HTML, CSS, and JavaScript. Written from the ground up to be small and maintainable.
323 lines (267 loc) • 7.24 kB
JavaScript
import { URLPattern } from './urlpattern/urlpattern.js'
import url from './url/url.js'
import qs from '../querystring.js'
const {
URL,
URLSearchParams,
parseURL,
serializeURLOrigin,
serializeHost
} = url
for (const key in globalThis.URL) {
if (!URL[key]) {
URL[key] = globalThis.URL[key].bind(globalThis.URL)
}
}
URL.resolve = resolve
URL.parse = parse
URL.format = format
URL.prototype[Symbol.for('socket.runtime.util.inspect.custom')] = function () {
return [
'URL {',
` protocol: ${this.protocol || null},`,
` username: ${this.username || null},`,
` password: ${this.password || null},`,
` hostname: ${this.hostname || null},`,
` pathname: ${this.pathname || null},`,
` search: ${this.search || null},`,
` hash: ${this.hash || null}`,
'}'
].join('\n')
}
/**
* @type {Set & { handlers: Set<string> }}
*/
export const protocols = new Set([
'socket:',
'node:',
'npm:',
'ipc:',
// android
'android.resource:',
'content:',
// web standard & reserved
'bitcoin:',
'file:',
'ftp:',
'ftps:',
'geo:',
'git:',
'http:',
'https:',
'im:',
'ipfs:',
'irc:',
'ircs:',
'magnet:',
'mailto:',
'matrix:',
'mms:',
'news:',
'nntp:',
'openpgp4fpr:',
'sftp:',
'sip:',
'sms:',
'smsto:',
'ssh:',
'tel:',
'urn:',
'webcal:',
'wtai:',
'xmpp:'
])
protocols.handlers = new Set()
if (globalThis.__args?.config && typeof globalThis.__args.config === 'object') {
const protocolHandlers = String(globalThis.__args.config['webview_protocol-handlers'] || '')
.split(' ')
.filter(Boolean)
const webviewURLProtocols = String(globalThis.__args.config.webview_url_protocols || '')
.split(' ')
.filter(Boolean)
for (const value of webviewURLProtocols) {
const scheme = value.replace(':', '')
if (scheme) {
protocols.add(scheme + ':')
protocols.handlers.add(scheme)
}
}
for (const value of protocolHandlers) {
const scheme = value.replace(':', '')
if (scheme) {
protocols.add(scheme + ':')
protocols.handlers.add(scheme)
}
}
for (const key in globalThis.__args.config) {
if (key.startsWith('webview_protocol-handlers_')) {
const scheme = key.replace('webview_protocol-handlers_', '').replace(':', '')
if (scheme) {
protocols.add(scheme + ':')
protocols.handlers.add(scheme)
}
}
}
}
export function parse (input, options = null) {
let parsed = null
if (URL.canParse(input)) {
parsed = new URL(input)
}
if (options?.strict === true && !URL.canParse(input)) {
return null
}
if (URL.canParse(input, 'socket://')) {
parsed = new URL(input, globalThis.location.origin)
}
if (!parsed) {
return null
}
parsed = {
hash: parsed.hash || null,
host: parsed.hostname || null,
hostname: parsed.hostname || null,
origin: parsed.origin || null,
auth: [parsed.username, parsed.password].filter(Boolean).join(':') || null,
password: parsed.password || null,
pathname: parsed.pathname || null,
path: parsed.pathname || null,
port: parsed.port || null,
protocol: parsed.protocol || null,
search: parsed.search || null,
searchParams: parsed.searchParams,
username: parsed.username || null,
[Symbol.toStringTag]: 'URL (Parsed)'
}
if (options === true) {
// for nodejs compat
parsed.query = Object.fromEntries(parsed.searchParams.entries())
} else if (parsed.search) {
parsed.query = parsed.search.slice(1) ?? null
}
if (!input.startsWith(parsed.protocol)) {
parsed.protocol = null
parsed.hostname = null
parsed.origin = null
parsed.host = null
parsed.href = `${parsed.pathname || ''}${parsed.search || ''}${parsed.hash || ''}`
} else {
parsed.href = `${parsed.protocol}//`
if (parsed.username) {
parsed.href += [parsed.username, parsed.password].filter(Boolean).join(':')
if (parsed.hostname) {
parsed.href += '@'
}
}
parsed.href += `${parsed.hostname || ''}${parsed.pathname || ''}${parsed.search || ''}${parsed.hash || ''}`
}
return parsed
}
// lifted from node
export function resolve (from, to) {
const resolved = new URL(to, new URL(from, 'resolve://'))
if (resolved.protocol === 'resolve:') {
const { pathname, search, hash } = resolved
return pathname + search + hash
}
return resolved.toString()
}
export function format (input) {
if (!input || (typeof input !== 'string' && typeof input !== 'object')) {
throw new TypeError(
`The 'input' argument must be one of type object or string. Received: ${input}`
)
}
if (typeof input === 'string') {
if (!URL.canParse(input)) {
return ''
}
return new URL(input).toString()
}
let formatted = ''
if (!input.hostname) {
return ''
}
if (input.protocol) {
formatted += `${input.protocol}//`
}
if (input.username) {
formatted += encodeURIComponent(input.username)
if (input.password) {
formatted += `:${encodeURIComponent(input.password)}`
}
formatted += '@'
}
formatted += input.hostname
if (input.port) {
formatted += `:${input.port}`
}
if (input.pathname) {
formatted += input.pathname
}
if (input.query && typeof input.query === 'object') {
formatted += `?${qs.stringify(input.query)}`
} else if (input.query && typeof input.query === 'string') {
if (!input.query.startsWith('?')) {
formatted += '?'
}
formatted += encodeURIComponent(decodeURIComponent(input.query))
}
if (input.hash && typeof input.hash === 'string') {
if (!input.hash.startsWith('#')) {
formatted += '#'
}
formatted += input.hash
}
return formatted
}
export function fileURLToPath (url) {
if (typeof url === 'string') {
url = new URL(url, globalThis.location.origin)
}
if (!(url instanceof URL)) {
throw new TypeError(
`Expecting 'url' to be a URL or string. Received: ${url}`
)
}
if (url.protocol !== 'file:' && url.protocol !== 'socket:') {
throw new TypeError(
`Expecting 'url' to have a 'file:' or 'socket:' URL scheme. Received: ${url.protocol}`
)
}
return url.pathname
}
url.serializeURLOrigin = function (input) {
const { scheme, protocol, host } = input
if (globalThis.__args.config.meta_application_protocol) {
if (
protocol &&
globalThis.__args.config.meta_application_protocol === protocol.slice(0, -1)
) {
return `${protocol}//${serializeHost(host)}`
}
if (
scheme &&
globalThis.__args.config.meta_application_protocol === scheme
) {
return `${scheme}://${serializeHost(host)}`
}
}
if (protocols.has(protocol)) {
return `${protocol}//${serializeHost(host)}`
}
if (protocols.has(`${scheme}:`)) {
return `${scheme}://${serializeHost(host)}`
}
return serializeURLOrigin(input)
}
const descriptors = Object.getOwnPropertyDescriptors(URL.prototype)
Object.defineProperties(URL.prototype, {
...descriptors,
origin: {
...descriptors.origin,
get () { return url.serializeURLOrigin(this) }
}
})
export default URL
export { URL, URLSearchParams, parseURL, URLPattern }