You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					293 lines
				
				9.1 KiB
			
		
		
			
		
	
	
					293 lines
				
				9.1 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'use strict'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const URL = require('url')
							 | 
						||
| 
								 | 
							
								const { normalizeIPv6, normalizeIPv4, removeDotSegments, recomposeAuthority, normalizeComponentEncoding } = require('./lib/utils')
							 | 
						||
| 
								 | 
							
								const SCHEMES = require('./lib/schemes')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function normalize (uri, options) {
							 | 
						||
| 
								 | 
							
								  if (typeof uri === 'string') {
							 | 
						||
| 
								 | 
							
								    uri = serialize(parse(uri, options), options)
							 | 
						||
| 
								 | 
							
								  } else if (typeof uri === 'object') {
							 | 
						||
| 
								 | 
							
								    uri = parse(serialize(uri, options), options)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return uri
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function resolve (baseURI, relativeURI, options) {
							 | 
						||
| 
								 | 
							
								  const schemelessOptions = Object.assign({ scheme: 'null' }, options)
							 | 
						||
| 
								 | 
							
								  const resolved = resolveComponents(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true)
							 | 
						||
| 
								 | 
							
								  return serialize(resolved, { ...schemelessOptions, skipEscape: true })
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function resolveComponents (base, relative, options, skipNormalization) {
							 | 
						||
| 
								 | 
							
								  const target = {}
							 | 
						||
| 
								 | 
							
								  if (!skipNormalization) {
							 | 
						||
| 
								 | 
							
								    base = parse(serialize(base, options), options) // normalize base components
							 | 
						||
| 
								 | 
							
								    relative = parse(serialize(relative, options), options) // normalize relative components
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  options = options || {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (!options.tolerant && relative.scheme) {
							 | 
						||
| 
								 | 
							
								    target.scheme = relative.scheme
							 | 
						||
| 
								 | 
							
								    // target.authority = relative.authority;
							 | 
						||
| 
								 | 
							
								    target.userinfo = relative.userinfo
							 | 
						||
| 
								 | 
							
								    target.host = relative.host
							 | 
						||
| 
								 | 
							
								    target.port = relative.port
							 | 
						||
| 
								 | 
							
								    target.path = removeDotSegments(relative.path || '')
							 | 
						||
| 
								 | 
							
								    target.query = relative.query
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) {
							 | 
						||
| 
								 | 
							
								      // target.authority = relative.authority;
							 | 
						||
| 
								 | 
							
								      target.userinfo = relative.userinfo
							 | 
						||
| 
								 | 
							
								      target.host = relative.host
							 | 
						||
| 
								 | 
							
								      target.port = relative.port
							 | 
						||
| 
								 | 
							
								      target.path = removeDotSegments(relative.path || '')
							 | 
						||
| 
								 | 
							
								      target.query = relative.query
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      if (!relative.path) {
							 | 
						||
| 
								 | 
							
								        target.path = base.path
							 | 
						||
| 
								 | 
							
								        if (relative.query !== undefined) {
							 | 
						||
| 
								 | 
							
								          target.query = relative.query
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          target.query = base.query
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        if (relative.path.charAt(0) === '/') {
							 | 
						||
| 
								 | 
							
								          target.path = removeDotSegments(relative.path)
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
							 | 
						||
| 
								 | 
							
								            target.path = '/' + relative.path
							 | 
						||
| 
								 | 
							
								          } else if (!base.path) {
							 | 
						||
| 
								 | 
							
								            target.path = relative.path
							 | 
						||
| 
								 | 
							
								          } else {
							 | 
						||
| 
								 | 
							
								            target.path = base.path.slice(0, base.path.lastIndexOf('/') + 1) + relative.path
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          target.path = removeDotSegments(target.path)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        target.query = relative.query
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // target.authority = base.authority;
							 | 
						||
| 
								 | 
							
								      target.userinfo = base.userinfo
							 | 
						||
| 
								 | 
							
								      target.host = base.host
							 | 
						||
| 
								 | 
							
								      target.port = base.port
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    target.scheme = base.scheme
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  target.fragment = relative.fragment
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return target
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function equal (uriA, uriB, options) {
							 | 
						||
| 
								 | 
							
								  if (typeof uriA === 'string') {
							 | 
						||
| 
								 | 
							
								    uriA = unescape(uriA)
							 | 
						||
| 
								 | 
							
								    uriA = serialize(normalizeComponentEncoding(parse(uriA, options), true), { ...options, skipEscape: true })
							 | 
						||
| 
								 | 
							
								  } else if (typeof uriA === 'object') {
							 | 
						||
| 
								 | 
							
								    uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (typeof uriB === 'string') {
							 | 
						||
| 
								 | 
							
								    uriB = unescape(uriB)
							 | 
						||
| 
								 | 
							
								    uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), { ...options, skipEscape: true })
							 | 
						||
| 
								 | 
							
								  } else if (typeof uriB === 'object') {
							 | 
						||
| 
								 | 
							
								    uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return uriA.toLowerCase() === uriB.toLowerCase()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function serialize (cmpts, opts) {
							 | 
						||
| 
								 | 
							
								  const components = {
							 | 
						||
| 
								 | 
							
								    host: cmpts.host,
							 | 
						||
| 
								 | 
							
								    scheme: cmpts.scheme,
							 | 
						||
| 
								 | 
							
								    userinfo: cmpts.userinfo,
							 | 
						||
| 
								 | 
							
								    port: cmpts.port,
							 | 
						||
| 
								 | 
							
								    path: cmpts.path,
							 | 
						||
| 
								 | 
							
								    query: cmpts.query,
							 | 
						||
| 
								 | 
							
								    nid: cmpts.nid,
							 | 
						||
| 
								 | 
							
								    nss: cmpts.nss,
							 | 
						||
| 
								 | 
							
								    uuid: cmpts.uuid,
							 | 
						||
| 
								 | 
							
								    fragment: cmpts.fragment,
							 | 
						||
| 
								 | 
							
								    reference: cmpts.reference,
							 | 
						||
| 
								 | 
							
								    resourceName: cmpts.resourceName,
							 | 
						||
| 
								 | 
							
								    secure: cmpts.secure,
							 | 
						||
| 
								 | 
							
								    error: ''
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const options = Object.assign({}, opts)
							 | 
						||
| 
								 | 
							
								  const uriTokens = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // find scheme handler
							 | 
						||
| 
								 | 
							
								  const schemeHandler = SCHEMES[(options.scheme || components.scheme || '').toLowerCase()]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // perform scheme specific serialization
							 | 
						||
| 
								 | 
							
								  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(components, options)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (components.path !== undefined) {
							 | 
						||
| 
								 | 
							
								    if (!options.skipEscape) {
							 | 
						||
| 
								 | 
							
								      components.path = escape(components.path)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (components.scheme !== undefined) {
							 | 
						||
| 
								 | 
							
								        components.path = components.path.split('%3A').join(':')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      components.path = unescape(components.path)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (options.reference !== 'suffix' && components.scheme) {
							 | 
						||
| 
								 | 
							
								    uriTokens.push(components.scheme)
							 | 
						||
| 
								 | 
							
								    uriTokens.push(':')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const authority = recomposeAuthority(components, options)
							 | 
						||
| 
								 | 
							
								  if (authority !== undefined) {
							 | 
						||
| 
								 | 
							
								    if (options.reference !== 'suffix') {
							 | 
						||
| 
								 | 
							
								      uriTokens.push('//')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    uriTokens.push(authority)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (components.path && components.path.charAt(0) !== '/') {
							 | 
						||
| 
								 | 
							
								      uriTokens.push('/')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (components.path !== undefined) {
							 | 
						||
| 
								 | 
							
								    let s = components.path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
							 | 
						||
| 
								 | 
							
								      s = removeDotSegments(s)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (authority === undefined) {
							 | 
						||
| 
								 | 
							
								      s = s.replace(/^\/\//, '/%2F') // don't allow the path to start with "//"
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    uriTokens.push(s)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (components.query !== undefined) {
							 | 
						||
| 
								 | 
							
								    uriTokens.push('?')
							 | 
						||
| 
								 | 
							
								    uriTokens.push(components.query)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (components.fragment !== undefined) {
							 | 
						||
| 
								 | 
							
								    uriTokens.push('#')
							 | 
						||
| 
								 | 
							
								    uriTokens.push(components.fragment)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return uriTokens.join('')
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const URI_PARSE = /^(?:([^:/?#]+):)?(?:\/\/((?:([^/?#@]*)@)?(\[[^/?#\]]+\]|[^/?#:]*)(?::(\d*))?))?([^?#]*)(?:\?([^#]*))?(?:#((?:.|\n|\r)*))?/i
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function parse (uri, opts) {
							 | 
						||
| 
								 | 
							
								  const options = Object.assign({}, opts)
							 | 
						||
| 
								 | 
							
								  const parsed = {
							 | 
						||
| 
								 | 
							
								    scheme: undefined,
							 | 
						||
| 
								 | 
							
								    userinfo: undefined,
							 | 
						||
| 
								 | 
							
								    host: '',
							 | 
						||
| 
								 | 
							
								    port: undefined,
							 | 
						||
| 
								 | 
							
								    path: '',
							 | 
						||
| 
								 | 
							
								    query: undefined,
							 | 
						||
| 
								 | 
							
								    fragment: undefined
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  const gotEncoding = uri.indexOf('%') !== -1
							 | 
						||
| 
								 | 
							
								  if (options.reference === 'suffix') uri = (options.scheme ? options.scheme + ':' : '') + '//' + uri
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  const matches = uri.match(URI_PARSE)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (matches) {
							 | 
						||
| 
								 | 
							
								    // store each component
							 | 
						||
| 
								 | 
							
								    parsed.scheme = matches[1]
							 | 
						||
| 
								 | 
							
								    parsed.userinfo = matches[3]
							 | 
						||
| 
								 | 
							
								    parsed.host = matches[4]
							 | 
						||
| 
								 | 
							
								    parsed.port = parseInt(matches[5], 10)
							 | 
						||
| 
								 | 
							
								    parsed.path = matches[6] || ''
							 | 
						||
| 
								 | 
							
								    parsed.query = matches[7]
							 | 
						||
| 
								 | 
							
								    parsed.fragment = matches[8]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // fix port number
							 | 
						||
| 
								 | 
							
								    if (isNaN(parsed.port)) {
							 | 
						||
| 
								 | 
							
								      parsed.port = matches[5]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (parsed.host) {
							 | 
						||
| 
								 | 
							
								      const ipv4result = normalizeIPv4(parsed.host)
							 | 
						||
| 
								 | 
							
								      if (ipv4result.isIPV4 === false) {
							 | 
						||
| 
								 | 
							
								        parsed.host = normalizeIPv6(ipv4result.host, { isIPV4: false }).host.toLowerCase()
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        parsed.host = ipv4result.host
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && !parsed.path && parsed.query === undefined) {
							 | 
						||
| 
								 | 
							
								      parsed.reference = 'same-document'
							 | 
						||
| 
								 | 
							
								    } else if (parsed.scheme === undefined) {
							 | 
						||
| 
								 | 
							
								      parsed.reference = 'relative'
							 | 
						||
| 
								 | 
							
								    } else if (parsed.fragment === undefined) {
							 | 
						||
| 
								 | 
							
								      parsed.reference = 'absolute'
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      parsed.reference = 'uri'
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // check for reference errors
							 | 
						||
| 
								 | 
							
								    if (options.reference && options.reference !== 'suffix' && options.reference !== parsed.reference) {
							 | 
						||
| 
								 | 
							
								      parsed.error = parsed.error || 'URI is not a ' + options.reference + ' reference.'
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // find scheme handler
							 | 
						||
| 
								 | 
							
								    const schemeHandler = SCHEMES[(options.scheme || parsed.scheme || '').toLowerCase()]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // check if scheme can't handle IRIs
							 | 
						||
| 
								 | 
							
								    if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
							 | 
						||
| 
								 | 
							
								      // if host component is a domain name
							 | 
						||
| 
								 | 
							
								      if (parsed.host && (options.domainHost || (schemeHandler && schemeHandler.domainHost))) {
							 | 
						||
| 
								 | 
							
								        // convert Unicode IDN -> ASCII IDN
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          parsed.host = URL.domainToASCII(parsed.host.toLowerCase())
							 | 
						||
| 
								 | 
							
								        } catch (e) {
							 | 
						||
| 
								 | 
							
								          parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      // convert IRI -> URI
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
							 | 
						||
| 
								 | 
							
								      if (gotEncoding && parsed.scheme !== undefined) {
							 | 
						||
| 
								 | 
							
								        parsed.scheme = unescape(parsed.scheme)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (gotEncoding && parsed.userinfo !== undefined) {
							 | 
						||
| 
								 | 
							
								        parsed.userinfo = unescape(parsed.userinfo)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (gotEncoding && parsed.host !== undefined) {
							 | 
						||
| 
								 | 
							
								        parsed.host = unescape(parsed.host)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (parsed.path !== undefined && parsed.path.length) {
							 | 
						||
| 
								 | 
							
								        parsed.path = encodeURI(parsed.path)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      if (parsed.fragment !== undefined && parsed.fragment.length) {
							 | 
						||
| 
								 | 
							
								        parsed.fragment = encodeURI(decodeURI(parsed.fragment))
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // perform scheme specific parsing
							 | 
						||
| 
								 | 
							
								    if (schemeHandler && schemeHandler.parse) {
							 | 
						||
| 
								 | 
							
								      schemeHandler.parse(parsed, options)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  } else {
							 | 
						||
| 
								 | 
							
								    parsed.error = parsed.error || 'URI can not be parsed.'
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return parsed
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const fastUri = {
							 | 
						||
| 
								 | 
							
								  normalize,
							 | 
						||
| 
								 | 
							
								  resolve,
							 | 
						||
| 
								 | 
							
								  resolveComponents,
							 | 
						||
| 
								 | 
							
								  equal,
							 | 
						||
| 
								 | 
							
								  serialize,
							 | 
						||
| 
								 | 
							
								  parse
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = fastUri
							 | 
						||
| 
								 | 
							
								module.exports.default = fastUri
							 | 
						||
| 
								 | 
							
								module.exports.fastUri = fastUri
							 |