source: frontend/node_modules/fast-uri/index.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 11.9 KB
Line 
1'use strict'
2
3const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require('./lib/utils')
4const { SCHEMES, getSchemeHandler } = require('./lib/schemes')
5
6/**
7 * @template {import('./types/index').URIComponent|string} T
8 * @param {T} uri
9 * @param {import('./types/index').Options} [options]
10 * @returns {T}
11 */
12function normalize (uri, options) {
13 if (typeof uri === 'string') {
14 uri = /** @type {T} */ (normalizeString(uri, options))
15 } else if (typeof uri === 'object') {
16 uri = /** @type {T} */ (parse(serialize(uri, options), options))
17 }
18 return uri
19}
20
21/**
22 * @param {string} baseURI
23 * @param {string} relativeURI
24 * @param {import('./types/index').Options} [options]
25 * @returns {string}
26 */
27function resolve (baseURI, relativeURI, options) {
28 const schemelessOptions = options ? Object.assign({ scheme: 'null' }, options) : { scheme: 'null' }
29 const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true)
30 schemelessOptions.skipEscape = true
31 return serialize(resolved, schemelessOptions)
32}
33
34/**
35 * @param {import ('./types/index').URIComponent} base
36 * @param {import ('./types/index').URIComponent} relative
37 * @param {import('./types/index').Options} [options]
38 * @param {boolean} [skipNormalization=false]
39 * @returns {import ('./types/index').URIComponent}
40 */
41function resolveComponent (base, relative, options, skipNormalization) {
42 /** @type {import('./types/index').URIComponent} */
43 const target = {}
44 if (!skipNormalization) {
45 base = parse(serialize(base, options), options) // normalize base component
46 relative = parse(serialize(relative, options), options) // normalize relative component
47 }
48 options = options || {}
49
50 if (!options.tolerant && relative.scheme) {
51 target.scheme = relative.scheme
52 // target.authority = relative.authority;
53 target.userinfo = relative.userinfo
54 target.host = relative.host
55 target.port = relative.port
56 target.path = removeDotSegments(relative.path || '')
57 target.query = relative.query
58 } else {
59 if (relative.userinfo !== undefined || relative.host !== undefined || relative.port !== undefined) {
60 // target.authority = relative.authority;
61 target.userinfo = relative.userinfo
62 target.host = relative.host
63 target.port = relative.port
64 target.path = removeDotSegments(relative.path || '')
65 target.query = relative.query
66 } else {
67 if (!relative.path) {
68 target.path = base.path
69 if (relative.query !== undefined) {
70 target.query = relative.query
71 } else {
72 target.query = base.query
73 }
74 } else {
75 if (relative.path[0] === '/') {
76 target.path = removeDotSegments(relative.path)
77 } else {
78 if ((base.userinfo !== undefined || base.host !== undefined || base.port !== undefined) && !base.path) {
79 target.path = '/' + relative.path
80 } else if (!base.path) {
81 target.path = relative.path
82 } else {
83 target.path = base.path.slice(0, base.path.lastIndexOf('/') + 1) + relative.path
84 }
85 target.path = removeDotSegments(target.path)
86 }
87 target.query = relative.query
88 }
89 // target.authority = base.authority;
90 target.userinfo = base.userinfo
91 target.host = base.host
92 target.port = base.port
93 }
94 target.scheme = base.scheme
95 }
96
97 target.fragment = relative.fragment
98
99 return target
100}
101
102/**
103 * @param {import ('./types/index').URIComponent|string} uriA
104 * @param {import ('./types/index').URIComponent|string} uriB
105 * @param {import ('./types/index').Options} options
106 * @returns {boolean}
107 */
108function equal (uriA, uriB, options) {
109 const normalizedA = normalizeComparableURI(uriA, options)
110 const normalizedB = normalizeComparableURI(uriB, options)
111
112 return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase()
113}
114
115/**
116 * @param {Readonly<import('./types/index').URIComponent>} cmpts
117 * @param {import('./types/index').Options} [opts]
118 * @returns {string}
119 */
120function serialize (cmpts, opts) {
121 const component = {
122 host: cmpts.host,
123 scheme: cmpts.scheme,
124 userinfo: cmpts.userinfo,
125 port: cmpts.port,
126 path: cmpts.path,
127 query: cmpts.query,
128 nid: cmpts.nid,
129 nss: cmpts.nss,
130 uuid: cmpts.uuid,
131 fragment: cmpts.fragment,
132 reference: cmpts.reference,
133 resourceName: cmpts.resourceName,
134 secure: cmpts.secure,
135 error: ''
136 }
137 const options = Object.assign({}, opts)
138 const uriTokens = []
139
140 // find scheme handler
141 const schemeHandler = getSchemeHandler(options.scheme || component.scheme)
142
143 // perform scheme specific serialization
144 if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options)
145
146 if (component.path !== undefined) {
147 if (!options.skipEscape) {
148 component.path = escapePreservingEscapes(component.path)
149
150 if (component.scheme !== undefined) {
151 component.path = component.path.split('%3A').join(':')
152 }
153 } else {
154 component.path = normalizePercentEncoding(component.path)
155 }
156 }
157
158 if (options.reference !== 'suffix' && component.scheme) {
159 uriTokens.push(component.scheme, ':')
160 }
161
162 const authority = recomposeAuthority(component)
163 if (authority !== undefined) {
164 if (options.reference !== 'suffix') {
165 uriTokens.push('//')
166 }
167
168 uriTokens.push(authority)
169
170 if (component.path && component.path[0] !== '/') {
171 uriTokens.push('/')
172 }
173 }
174 if (component.path !== undefined) {
175 let s = component.path
176
177 if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
178 s = removeDotSegments(s)
179 }
180
181 if (
182 authority === undefined &&
183 s[0] === '/' &&
184 s[1] === '/'
185 ) {
186 // don't allow the path to start with "//"
187 s = '/%2F' + s.slice(2)
188 }
189
190 uriTokens.push(s)
191 }
192
193 if (component.query !== undefined) {
194 uriTokens.push('?', component.query)
195 }
196
197 if (component.fragment !== undefined) {
198 uriTokens.push('#', component.fragment)
199 }
200 return uriTokens.join('')
201}
202
203const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u
204
205/**
206 * @param {import('./types/index').URIComponent} parsed
207 * @param {RegExpMatchArray} matches
208 * @returns {string|undefined}
209 */
210function getParseError (parsed, matches) {
211 if (matches[2] !== undefined && parsed.path && parsed.path[0] !== '/') {
212 return 'URI path must start with "/" when authority is present.'
213 }
214
215 if (typeof parsed.port === 'number' && (parsed.port < 0 || parsed.port > 65535)) {
216 return 'URI port is malformed.'
217 }
218
219 return undefined
220}
221
222/**
223 * @param {string} uri
224 * @param {import('./types/index').Options} [opts]
225 * @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean }}
226 */
227function parseWithStatus (uri, opts) {
228 const options = Object.assign({}, opts)
229 /** @type {import('./types/index').URIComponent} */
230 const parsed = {
231 scheme: undefined,
232 userinfo: undefined,
233 host: '',
234 port: undefined,
235 path: '',
236 query: undefined,
237 fragment: undefined
238 }
239
240 let malformedAuthorityOrPort = false
241
242 let isIP = false
243 if (options.reference === 'suffix') {
244 if (options.scheme) {
245 uri = options.scheme + ':' + uri
246 } else {
247 uri = '//' + uri
248 }
249 }
250
251 const matches = uri.match(URI_PARSE)
252
253 if (matches) {
254 // store each component
255 parsed.scheme = matches[1]
256 parsed.userinfo = matches[3]
257 parsed.host = matches[4]
258 parsed.port = parseInt(matches[5], 10)
259 parsed.path = matches[6] || ''
260 parsed.query = matches[7]
261 parsed.fragment = matches[8]
262
263 // fix port number
264 if (isNaN(parsed.port)) {
265 parsed.port = matches[5]
266 }
267
268 const parseError = getParseError(parsed, matches)
269 if (parseError !== undefined) {
270 parsed.error = parsed.error || parseError
271 malformedAuthorityOrPort = true
272 }
273
274 if (parsed.host) {
275 const ipv4result = isIPv4(parsed.host)
276 if (ipv4result === false) {
277 const ipv6result = normalizeIPv6(parsed.host)
278 parsed.host = ipv6result.host.toLowerCase()
279 isIP = ipv6result.isIPV6
280 } else {
281 isIP = true
282 }
283 }
284 if (parsed.scheme === undefined && parsed.userinfo === undefined && parsed.host === undefined && parsed.port === undefined && parsed.query === undefined && !parsed.path) {
285 parsed.reference = 'same-document'
286 } else if (parsed.scheme === undefined) {
287 parsed.reference = 'relative'
288 } else if (parsed.fragment === undefined) {
289 parsed.reference = 'absolute'
290 } else {
291 parsed.reference = 'uri'
292 }
293
294 // check for reference errors
295 if (options.reference && options.reference !== 'suffix' && options.reference !== parsed.reference) {
296 parsed.error = parsed.error || 'URI is not a ' + options.reference + ' reference.'
297 }
298
299 // find scheme handler
300 const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme)
301
302 // check if scheme can't handle IRIs
303 if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
304 // if host component is a domain name
305 if (parsed.host && (options.domainHost || (schemeHandler && schemeHandler.domainHost)) && isIP === false && nonSimpleDomain(parsed.host)) {
306 // convert Unicode IDN -> ASCII IDN
307 try {
308 parsed.host = URL.domainToASCII(parsed.host.toLowerCase())
309 } catch (e) {
310 parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e
311 }
312 }
313 // convert IRI -> URI
314 }
315
316 if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
317 if (uri.indexOf('%') !== -1) {
318 if (parsed.scheme !== undefined) {
319 parsed.scheme = unescape(parsed.scheme)
320 }
321 if (parsed.host !== undefined) {
322 parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP)
323 }
324 }
325 if (parsed.path) {
326 parsed.path = normalizePathEncoding(parsed.path)
327 }
328 if (parsed.fragment) {
329 try {
330 parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment))
331 } catch {
332 parsed.error = parsed.error || 'URI malformed'
333 }
334 }
335 }
336
337 // perform scheme specific parsing
338 if (schemeHandler && schemeHandler.parse) {
339 schemeHandler.parse(parsed, options)
340 }
341 } else {
342 parsed.error = parsed.error || 'URI can not be parsed.'
343 }
344 return { parsed, malformedAuthorityOrPort }
345}
346
347/**
348 * @param {string} uri
349 * @param {import('./types/index').Options} [opts]
350 * @returns
351 */
352function parse (uri, opts) {
353 return parseWithStatus(uri, opts).parsed
354}
355
356/**
357 * @param {string} uri
358 * @param {import('./types/index').Options} [opts]
359 * @returns {string}
360 */
361function normalizeString (uri, opts) {
362 return normalizeStringWithStatus(uri, opts).normalized
363}
364
365/**
366 * @param {string} uri
367 * @param {import('./types/index').Options} [opts]
368 * @returns {{ normalized: string, malformedAuthorityOrPort: boolean }}
369 */
370function normalizeStringWithStatus (uri, opts) {
371 const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts)
372 return {
373 normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
374 malformedAuthorityOrPort
375 }
376}
377
378/**
379 * @param {import ('./types/index').URIComponent|string} uri
380 * @param {import('./types/index').Options} [opts]
381 * @returns {string|undefined}
382 */
383function normalizeComparableURI (uri, opts) {
384 if (typeof uri === 'string') {
385 const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts)
386 return malformedAuthorityOrPort ? undefined : normalized
387 }
388
389 if (typeof uri === 'object') {
390 return serialize(uri, opts)
391 }
392}
393
394const fastUri = {
395 SCHEMES,
396 normalize,
397 resolve,
398 resolveComponent,
399 equal,
400 serialize,
401 parse
402}
403
404module.exports = fastUri
405module.exports.default = fastUri
406module.exports.fastUri = fastUri
Note: See TracBrowser for help on using the repository browser.