@limetech/lime-elements 40.0.3 → 40.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/cjs/lime-elements.cjs.js +1 -1
- package/dist/cjs/limel-chart.cjs.entry.js +60 -10
- package/dist/cjs/limel-form.cjs.entry.js +1590 -335
- package/dist/cjs/limel-prosemirror-adapter.cjs.entry.js +6 -5
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/chart/chart.js +83 -12
- package/dist/esm/lime-elements.js +1 -1
- package/dist/esm/limel-chart.entry.js +60 -10
- package/dist/esm/limel-form.entry.js +1590 -335
- package/dist/esm/limel-prosemirror-adapter.entry.js +6 -5
- package/dist/esm/loader.js +1 -1
- package/dist/lime-elements/lime-elements.esm.js +1 -1
- package/dist/lime-elements/p-1b25f158.entry.js +13 -0
- package/dist/lime-elements/p-73d9f0f6.entry.js +1 -0
- package/dist/lime-elements/{p-72a1d432.entry.js → p-d3d71a68.entry.js} +1 -1
- package/dist/types/components/chart/chart.d.ts +28 -4
- package/dist/types/components.d.ts +19 -4
- package/package.json +2 -2
- package/dist/lime-elements/p-21b54196.entry.js +0 -13
- package/dist/lime-elements/p-9ed9d0de.entry.js +0 -1
|
@@ -19825,6 +19825,9 @@ function requireUtils$1 () {
|
|
|
19825
19825
|
/** @type {(value: string) => boolean} */
|
|
19826
19826
|
const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
19827
19827
|
|
|
19828
|
+
/** @type {(value: string) => boolean} */
|
|
19829
|
+
const isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
19830
|
+
|
|
19828
19831
|
/** @type {(value: string) => boolean} */
|
|
19829
19832
|
const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
19830
19833
|
|
|
@@ -19832,10 +19835,13 @@ function requireUtils$1 () {
|
|
|
19832
19835
|
const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
19833
19836
|
|
|
19834
19837
|
/** @type {(value: string) => boolean} */
|
|
19835
|
-
const isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
19838
|
+
const isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
19836
19839
|
|
|
19837
19840
|
/** @type {(value: string) => boolean} */
|
|
19838
|
-
const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[
|
|
19841
|
+
const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
19842
|
+
|
|
19843
|
+
/** @type {(value: string) => boolean} */
|
|
19844
|
+
const isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
19839
19845
|
|
|
19840
19846
|
/**
|
|
19841
19847
|
* @param {Array<string>} input
|
|
@@ -19868,12 +19874,14 @@ function requireUtils$1 () {
|
|
|
19868
19874
|
return acc
|
|
19869
19875
|
}
|
|
19870
19876
|
|
|
19871
|
-
/**
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
+
/** @type {(value: string) => boolean} */
|
|
19878
|
+
const isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
19879
|
+
|
|
19880
|
+
/** @type {(value: string) => boolean} */
|
|
19881
|
+
const isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
19882
|
+
|
|
19883
|
+
/** @type {(value: string) => boolean} */
|
|
19884
|
+
const isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
19877
19885
|
|
|
19878
19886
|
/**
|
|
19879
19887
|
* @param {string} value
|
|
@@ -19881,89 +19889,129 @@ function requireUtils$1 () {
|
|
|
19881
19889
|
*/
|
|
19882
19890
|
const nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
19883
19891
|
|
|
19892
|
+
/** @type {(value: string) => boolean} */
|
|
19893
|
+
const nonSimpleDomainChars = RegExp.prototype.test.bind(/[^\d!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
19894
|
+
|
|
19895
|
+
/** @type {(value: string) => boolean} */
|
|
19896
|
+
const numericLastLabel = RegExp.prototype.test.bind(/(?:^|\.)\d+$/u);
|
|
19897
|
+
|
|
19884
19898
|
/**
|
|
19885
|
-
*
|
|
19899
|
+
* True when `new URL('http://' + domain).hostname` may differ from `domain`,
|
|
19900
|
+
* i.e. when the caller must fall back to the WHATWG parser.
|
|
19901
|
+
*
|
|
19902
|
+
* Same purpose as `nonSimpleDomain`, but digits are allowed: for this character
|
|
19903
|
+
* set `hostname` is the identity, *except* that an all-numeric final label is
|
|
19904
|
+
* read as IPv4 shorthand ("1.2.3" -> "1.2.0.3", "127.1" -> "127.0.0.1",
|
|
19905
|
+
* "a.b.1" throws), which is why `nonSimpleDomain` excludes digits outright.
|
|
19906
|
+
* Rejecting that one shape lets the common `mail2.example.org` case stay on the
|
|
19907
|
+
* fast path.
|
|
19908
|
+
*
|
|
19909
|
+
* @param {string} domain - must already be lowercased
|
|
19886
19910
|
* @returns {boolean}
|
|
19887
19911
|
*/
|
|
19888
|
-
function
|
|
19889
|
-
|
|
19890
|
-
return true
|
|
19912
|
+
function nonSimpleMailtoDomain (domain) {
|
|
19913
|
+
return nonSimpleDomainChars(domain) || numericLastLabel(domain)
|
|
19891
19914
|
}
|
|
19892
19915
|
|
|
19893
19916
|
/**
|
|
19894
|
-
* @param {
|
|
19895
|
-
* @param {Array<string>} address
|
|
19896
|
-
* @param {GetIPV6Result} output
|
|
19917
|
+
* @param {string} zone
|
|
19897
19918
|
* @returns {boolean}
|
|
19898
19919
|
*/
|
|
19899
|
-
function
|
|
19900
|
-
if (
|
|
19901
|
-
|
|
19902
|
-
|
|
19903
|
-
|
|
19904
|
-
|
|
19905
|
-
|
|
19906
|
-
|
|
19920
|
+
function isZoneIdentifier (zone) {
|
|
19921
|
+
if (zone.length === 0) return false
|
|
19922
|
+
|
|
19923
|
+
for (let i = 0; i < zone.length; i++) {
|
|
19924
|
+
if (isZoneCharacter(zone[i])) continue
|
|
19925
|
+
if (zone[i] === '%' && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
19926
|
+
i += 2;
|
|
19927
|
+
continue
|
|
19907
19928
|
}
|
|
19908
|
-
|
|
19929
|
+
return false
|
|
19909
19930
|
}
|
|
19931
|
+
|
|
19910
19932
|
return true
|
|
19911
19933
|
}
|
|
19912
19934
|
|
|
19913
19935
|
/**
|
|
19914
|
-
*
|
|
19915
|
-
*
|
|
19936
|
+
* Compresses the longest run of zero hextets to "::" per RFC 5952. A run of a
|
|
19937
|
+
* single zero hextet is left uncompressed. On ties the leftmost run wins.
|
|
19938
|
+
*
|
|
19939
|
+
* @param {string[]} hextets
|
|
19940
|
+
* @returns {string}
|
|
19916
19941
|
*/
|
|
19917
|
-
function
|
|
19918
|
-
let
|
|
19919
|
-
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
for (let i = 0; i < input.length; i++) {
|
|
19930
|
-
const cursor = input[i];
|
|
19931
|
-
if (cursor === '[' || cursor === ']') { continue }
|
|
19932
|
-
if (cursor === ':') {
|
|
19933
|
-
if (endipv6Encountered === true) {
|
|
19934
|
-
endIpv6 = true;
|
|
19935
|
-
}
|
|
19936
|
-
if (!consume(buffer, address, output)) { break }
|
|
19937
|
-
if (++tokenCount > 7) {
|
|
19938
|
-
// not valid
|
|
19939
|
-
output.error = true;
|
|
19940
|
-
break
|
|
19942
|
+
function compressIPv6ZeroRun (hextets) {
|
|
19943
|
+
let bestStart = -1;
|
|
19944
|
+
let bestLength = 0;
|
|
19945
|
+
let runStart = -1;
|
|
19946
|
+
let runLength = 0;
|
|
19947
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
19948
|
+
if (hextets[i] === '0') {
|
|
19949
|
+
if (runStart === -1) runStart = i;
|
|
19950
|
+
runLength++;
|
|
19951
|
+
if (runLength > bestLength) {
|
|
19952
|
+
bestLength = runLength;
|
|
19953
|
+
bestStart = runStart;
|
|
19941
19954
|
}
|
|
19942
|
-
if (i > 0 && input[i - 1] === ':') {
|
|
19943
|
-
endipv6Encountered = true;
|
|
19944
|
-
}
|
|
19945
|
-
address.push(':');
|
|
19946
|
-
continue
|
|
19947
|
-
} else if (cursor === '%') {
|
|
19948
|
-
if (!consume(buffer, address, output)) { break }
|
|
19949
|
-
// switch to zone detection
|
|
19950
|
-
consume = consumeIsZone;
|
|
19951
19955
|
} else {
|
|
19952
|
-
|
|
19953
|
-
|
|
19956
|
+
runStart = -1;
|
|
19957
|
+
runLength = 0;
|
|
19954
19958
|
}
|
|
19955
19959
|
}
|
|
19956
|
-
|
|
19957
|
-
|
|
19958
|
-
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19960
|
+
|
|
19961
|
+
if (bestLength < 2) return hextets.join(':')
|
|
19962
|
+
|
|
19963
|
+
const head = hextets.slice(0, bestStart).join(':');
|
|
19964
|
+
const tail = hextets.slice(bestStart + bestLength).join(':');
|
|
19965
|
+
return head + '::' + tail
|
|
19966
|
+
}
|
|
19967
|
+
|
|
19968
|
+
/**
|
|
19969
|
+
* Validates an IPv6 address against the alternatives in RFC 3986 section
|
|
19970
|
+
* 3.2.2 and returns the same address with leading hextet zeroes removed.
|
|
19971
|
+
* An embedded IPv4 address counts as two hextets and is only valid at the end.
|
|
19972
|
+
*
|
|
19973
|
+
* @param {string} input
|
|
19974
|
+
* @returns {string|undefined}
|
|
19975
|
+
*/
|
|
19976
|
+
function normalizeIPv6Address (input) {
|
|
19977
|
+
const compression = input.indexOf('::');
|
|
19978
|
+
if (compression !== -1 && input.indexOf('::', compression + 1) !== -1) return undefined
|
|
19979
|
+
|
|
19980
|
+
const left = compression === -1 ? input.split(':') : input.slice(0, compression).split(':');
|
|
19981
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(':');
|
|
19982
|
+
if (compression !== -1) {
|
|
19983
|
+
if (left.length === 1 && left[0] === '') left.length = 0;
|
|
19984
|
+
if (right.length === 1 && right[0] === '') right.length = 0;
|
|
19985
|
+
}
|
|
19986
|
+
|
|
19987
|
+
const parts = left.concat(right);
|
|
19988
|
+
let hextetCount = 0;
|
|
19989
|
+
for (let i = 0; i < parts.length; i++) {
|
|
19990
|
+
const part = parts[i];
|
|
19991
|
+
if (part === '') return undefined
|
|
19992
|
+
|
|
19993
|
+
if (part.indexOf('.') !== -1) {
|
|
19994
|
+
if (i !== parts.length - 1 || (compression !== -1 && right.length === 0) || !isIPv4(part)) return undefined
|
|
19995
|
+
hextetCount += 2;
|
|
19996
|
+
continue
|
|
19963
19997
|
}
|
|
19998
|
+
|
|
19999
|
+
if (!isHextet(part)) return undefined
|
|
20000
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
20001
|
+
hextetCount++;
|
|
19964
20002
|
}
|
|
19965
|
-
|
|
19966
|
-
|
|
20003
|
+
|
|
20004
|
+
if (compression === -1) {
|
|
20005
|
+
if (hextetCount !== 8) return undefined
|
|
20006
|
+
return compressIPv6ZeroRun(parts)
|
|
20007
|
+
}
|
|
20008
|
+
if (hextetCount >= 8) return undefined
|
|
20009
|
+
|
|
20010
|
+
// expand "::" then re-compress the longest run for a canonical result
|
|
20011
|
+
const expanded = parts.slice(0, left.length);
|
|
20012
|
+
for (let i = hextetCount; i < 8; i++) expanded.push('0');
|
|
20013
|
+
for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
|
|
20014
|
+
return compressIPv6ZeroRun(expanded)
|
|
19967
20015
|
}
|
|
19968
20016
|
|
|
19969
20017
|
/**
|
|
@@ -19971,26 +20019,49 @@ function requireUtils$1 () {
|
|
|
19971
20019
|
* @property {string} host - The normalized host.
|
|
19972
20020
|
* @property {string} [escapedHost] - The escaped host.
|
|
19973
20021
|
* @property {boolean} isIPV6 - Indicates if the host is an IPv6 address.
|
|
20022
|
+
* @property {boolean} [isIPVFuture] - Indicates if the host is an IPvFuture literal.
|
|
20023
|
+
* @property {boolean} [error] - Indicates if a bracketed IP literal is malformed.
|
|
19974
20024
|
*/
|
|
19975
20025
|
|
|
19976
20026
|
/**
|
|
20027
|
+
* Validates and normalizes a bracketed IP literal. Raw zone separators remain
|
|
20028
|
+
* accepted for backwards compatibility, while encoded separators and zone
|
|
20029
|
+
* contents follow RFC 6874.
|
|
20030
|
+
*
|
|
19977
20031
|
* @param {string} host
|
|
19978
20032
|
* @returns {NormalizeIPv6Result}
|
|
19979
20033
|
*/
|
|
19980
20034
|
function normalizeIPv6 (host) {
|
|
19981
|
-
|
|
19982
|
-
const
|
|
20035
|
+
const bracketed = host[0] === '[' && host[host.length - 1] === ']';
|
|
20036
|
+
const hasBracket = host[0] === '[' || host[host.length - 1] === ']';
|
|
20037
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true }
|
|
19983
20038
|
|
|
19984
|
-
|
|
19985
|
-
|
|
19986
|
-
|
|
19987
|
-
|
|
19988
|
-
|
|
19989
|
-
|
|
19990
|
-
|
|
19991
|
-
return { host
|
|
19992
|
-
}
|
|
19993
|
-
|
|
20039
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
20040
|
+
if (bracketed && isIPvFuture(input)) {
|
|
20041
|
+
input = input.toLowerCase();
|
|
20042
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true }
|
|
20043
|
+
}
|
|
20044
|
+
|
|
20045
|
+
if (findToken(input, ':') < 2) {
|
|
20046
|
+
return { host, isIPV6: false, error: bracketed }
|
|
20047
|
+
}
|
|
20048
|
+
|
|
20049
|
+
let zoneIdentifier = '';
|
|
20050
|
+
const zoneSeparator = input.indexOf('%');
|
|
20051
|
+
if (zoneSeparator !== -1) {
|
|
20052
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === '%25' ? 3 : 1;
|
|
20053
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
20054
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true }
|
|
20055
|
+
input = input.slice(0, zoneSeparator);
|
|
20056
|
+
}
|
|
20057
|
+
|
|
20058
|
+
const address = normalizeIPv6Address(input);
|
|
20059
|
+
if (address === undefined) return { host, isIPV6: false, error: true }
|
|
20060
|
+
|
|
20061
|
+
return {
|
|
20062
|
+
host: address + (zoneIdentifier ? '%' + zoneIdentifier : ''),
|
|
20063
|
+
escapedHost: address + (zoneIdentifier ? '%25' + zoneIdentifier : ''),
|
|
20064
|
+
isIPV6: true
|
|
19994
20065
|
}
|
|
19995
20066
|
}
|
|
19996
20067
|
|
|
@@ -20116,7 +20187,7 @@ function requireUtils$1 () {
|
|
|
20116
20187
|
|
|
20117
20188
|
/**
|
|
20118
20189
|
* Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
|
|
20119
|
-
* Reserved delimiters such as `%2F`
|
|
20190
|
+
* Reserved delimiters such as `%2F` stay escaped; `%2E` is unreserved.
|
|
20120
20191
|
*
|
|
20121
20192
|
* @param {string} input
|
|
20122
20193
|
* @param {boolean} [decodeUnreserved=false]
|
|
@@ -20257,6 +20328,60 @@ function requireUtils$1 () {
|
|
|
20257
20328
|
return output
|
|
20258
20329
|
}
|
|
20259
20330
|
|
|
20331
|
+
/**
|
|
20332
|
+
* Serializes a path without rewriting reserved data. Raw RFC 3986 path
|
|
20333
|
+
* characters remain literal, valid escapes are preserved and uppercased, and
|
|
20334
|
+
* everything else is UTF-8 percent-encoded. In a path-noscheme, a colon in the
|
|
20335
|
+
* first segment must be escaped so the result cannot be parsed as a scheme.
|
|
20336
|
+
*
|
|
20337
|
+
* @param {string} input
|
|
20338
|
+
* @param {boolean} [pathNoScheme=false]
|
|
20339
|
+
* @returns {string}
|
|
20340
|
+
*/
|
|
20341
|
+
function serializePathEncoding (input, pathNoScheme = false) {
|
|
20342
|
+
let output = '';
|
|
20343
|
+
let firstSegment = pathNoScheme && input[0] !== '/';
|
|
20344
|
+
|
|
20345
|
+
for (let i = 0; i < input.length; i++) {
|
|
20346
|
+
const ch = input[i];
|
|
20347
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
20348
|
+
const hex = input.slice(i + 1, i + 3);
|
|
20349
|
+
if (isHexPair(hex)) {
|
|
20350
|
+
output += '%' + hex.toUpperCase();
|
|
20351
|
+
i += 2;
|
|
20352
|
+
continue
|
|
20353
|
+
}
|
|
20354
|
+
}
|
|
20355
|
+
|
|
20356
|
+
if (ch === '/') {
|
|
20357
|
+
firstSegment = false;
|
|
20358
|
+
}
|
|
20359
|
+
|
|
20360
|
+
if (isPathCharacter(ch) && (ch !== ':' || !firstSegment)) {
|
|
20361
|
+
output += ch;
|
|
20362
|
+
} else {
|
|
20363
|
+
const code = input.charCodeAt(i);
|
|
20364
|
+
if (code < 0x80) {
|
|
20365
|
+
output += BYTE_HEX[code];
|
|
20366
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
20367
|
+
output += percentEncodeNonAscii(code);
|
|
20368
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
20369
|
+
const low = input.charCodeAt(i + 1);
|
|
20370
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
20371
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
20372
|
+
i++;
|
|
20373
|
+
} else {
|
|
20374
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
20375
|
+
}
|
|
20376
|
+
} else {
|
|
20377
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
20378
|
+
}
|
|
20379
|
+
}
|
|
20380
|
+
}
|
|
20381
|
+
|
|
20382
|
+
return output
|
|
20383
|
+
}
|
|
20384
|
+
|
|
20260
20385
|
/**
|
|
20261
20386
|
* Normalizes the percent-encoding of a query or fragment component.
|
|
20262
20387
|
*
|
|
@@ -20314,6 +20439,85 @@ function requireUtils$1 () {
|
|
|
20314
20439
|
return output
|
|
20315
20440
|
}
|
|
20316
20441
|
|
|
20442
|
+
/**
|
|
20443
|
+
* Percent-encodes a URI component using its RFC 3986 literal character set.
|
|
20444
|
+
* Existing valid escapes are preserved and normalized to uppercase hex.
|
|
20445
|
+
*
|
|
20446
|
+
* @param {string} input
|
|
20447
|
+
* @param {(value: string) => boolean} isAllowed
|
|
20448
|
+
* @returns {string}
|
|
20449
|
+
*/
|
|
20450
|
+
function encodeComponent (input, isAllowed) {
|
|
20451
|
+
let output = '';
|
|
20452
|
+
|
|
20453
|
+
for (let i = 0; i < input.length; i++) {
|
|
20454
|
+
const ch = input[i];
|
|
20455
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
20456
|
+
const hex = input.slice(i + 1, i + 3);
|
|
20457
|
+
if (isHexPair(hex)) {
|
|
20458
|
+
output += '%' + hex.toUpperCase();
|
|
20459
|
+
i += 2;
|
|
20460
|
+
continue
|
|
20461
|
+
}
|
|
20462
|
+
}
|
|
20463
|
+
|
|
20464
|
+
if (isAllowed(ch)) {
|
|
20465
|
+
output += ch;
|
|
20466
|
+
} else {
|
|
20467
|
+
const code = input.charCodeAt(i);
|
|
20468
|
+
if (code < 0x80) {
|
|
20469
|
+
output += BYTE_HEX[code];
|
|
20470
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
20471
|
+
output += percentEncodeNonAscii(code);
|
|
20472
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
20473
|
+
const low = input.charCodeAt(i + 1);
|
|
20474
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
20475
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
20476
|
+
i++;
|
|
20477
|
+
} else {
|
|
20478
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
20479
|
+
}
|
|
20480
|
+
} else {
|
|
20481
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
20482
|
+
}
|
|
20483
|
+
}
|
|
20484
|
+
}
|
|
20485
|
+
|
|
20486
|
+
return output
|
|
20487
|
+
}
|
|
20488
|
+
|
|
20489
|
+
/**
|
|
20490
|
+
* Encodes userinfo while preserving its RFC 3986 §3.2.1 literal characters.
|
|
20491
|
+
* In particular, authority delimiters such as `@`, `/`, `?`, and `#` are data.
|
|
20492
|
+
*
|
|
20493
|
+
* @param {string} input
|
|
20494
|
+
* @returns {string}
|
|
20495
|
+
*/
|
|
20496
|
+
function encodeUserinfo (input) {
|
|
20497
|
+
return encodeComponent(input, isUserinfoCharacter)
|
|
20498
|
+
}
|
|
20499
|
+
|
|
20500
|
+
/**
|
|
20501
|
+
* Encodes query data using the RFC 3986 §3.4 grammar. A literal `#` must be
|
|
20502
|
+
* escaped because it would otherwise begin the fragment component.
|
|
20503
|
+
*
|
|
20504
|
+
* @param {string} input
|
|
20505
|
+
* @returns {string}
|
|
20506
|
+
*/
|
|
20507
|
+
function encodeQuery (input) {
|
|
20508
|
+
return encodeComponent(input, isQueryFragmentCharacter)
|
|
20509
|
+
}
|
|
20510
|
+
|
|
20511
|
+
/**
|
|
20512
|
+
* Encodes fragment data using the RFC 3986 §3.5 grammar.
|
|
20513
|
+
*
|
|
20514
|
+
* @param {string} input
|
|
20515
|
+
* @returns {string}
|
|
20516
|
+
*/
|
|
20517
|
+
function encodeFragment (input) {
|
|
20518
|
+
return encodeComponent(input, isQueryFragmentCharacter)
|
|
20519
|
+
}
|
|
20520
|
+
|
|
20317
20521
|
/**
|
|
20318
20522
|
* Escapes a component while preserving existing valid percent escapes.
|
|
20319
20523
|
*
|
|
@@ -20363,15 +20567,21 @@ function requireUtils$1 () {
|
|
|
20363
20567
|
const uriTokens = [];
|
|
20364
20568
|
|
|
20365
20569
|
if (component.userinfo !== undefined) {
|
|
20366
|
-
uriTokens.push(component.userinfo);
|
|
20570
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
20367
20571
|
uriTokens.push('@');
|
|
20368
20572
|
}
|
|
20369
20573
|
|
|
20370
20574
|
if (component.host !== undefined) {
|
|
20371
|
-
let host =
|
|
20575
|
+
let host = component.host;
|
|
20372
20576
|
if (!isIPv4(host)) {
|
|
20373
|
-
|
|
20374
|
-
if (ipV6res.isIPV6
|
|
20577
|
+
let ipV6res = normalizeIPv6(host);
|
|
20578
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
20579
|
+
// Decode only unreserved bytes, once. In particular, keep %25 encoded
|
|
20580
|
+
// so it cannot introduce a second escape during recomposition.
|
|
20581
|
+
host = normalizePercentEncoding(host, true);
|
|
20582
|
+
ipV6res = normalizeIPv6(host);
|
|
20583
|
+
}
|
|
20584
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
20375
20585
|
host = `[${ipV6res.escapedHost}]`;
|
|
20376
20586
|
} else {
|
|
20377
20587
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -20381,19 +20591,30 @@ function requireUtils$1 () {
|
|
|
20381
20591
|
}
|
|
20382
20592
|
|
|
20383
20593
|
if (typeof component.port === 'number' || typeof component.port === 'string') {
|
|
20594
|
+
const port = String(component.port);
|
|
20595
|
+
if (!isPort(port)) {
|
|
20596
|
+
throw new TypeError('URI port is malformed.')
|
|
20597
|
+
}
|
|
20384
20598
|
uriTokens.push(':');
|
|
20385
|
-
uriTokens.push(
|
|
20599
|
+
uriTokens.push(port);
|
|
20386
20600
|
}
|
|
20387
20601
|
|
|
20388
20602
|
return uriTokens.length ? uriTokens.join('') : undefined
|
|
20389
20603
|
}
|
|
20390
20604
|
utils$1 = {
|
|
20605
|
+
BYTE_HEX,
|
|
20606
|
+
percentEncodeNonAscii,
|
|
20391
20607
|
nonSimpleDomain,
|
|
20608
|
+
nonSimpleMailtoDomain,
|
|
20392
20609
|
recomposeAuthority,
|
|
20393
20610
|
reescapeHostDelimiters,
|
|
20394
20611
|
normalizePercentEncoding,
|
|
20395
20612
|
normalizePathEncoding,
|
|
20613
|
+
serializePathEncoding,
|
|
20396
20614
|
normalizeQueryFragmentEncoding,
|
|
20615
|
+
encodeUserinfo,
|
|
20616
|
+
encodeQuery,
|
|
20617
|
+
encodeFragment,
|
|
20397
20618
|
escapePreservingEscapes,
|
|
20398
20619
|
removeDotSegments,
|
|
20399
20620
|
isIPv4,
|
|
@@ -20411,11 +20632,11 @@ function requireSchemes$1 () {
|
|
|
20411
20632
|
if (hasRequiredSchemes$1) return schemes$1;
|
|
20412
20633
|
hasRequiredSchemes$1 = 1;
|
|
20413
20634
|
|
|
20414
|
-
const { isUUID } = requireUtils$1();
|
|
20415
|
-
const URN_REG =
|
|
20635
|
+
const { isUUID, BYTE_HEX, percentEncodeNonAscii, nonSimpleMailtoDomain } = requireUtils$1();
|
|
20636
|
+
const URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
20416
20637
|
|
|
20417
20638
|
const supportedSchemeNames = /** @type {const} */ (['http', 'https', 'ws',
|
|
20418
|
-
'wss', 'urn', 'urn:uuid']);
|
|
20639
|
+
'wss', 'urn', 'urn:uuid', 'mailto']);
|
|
20419
20640
|
|
|
20420
20641
|
/** @typedef {supportedSchemeNames[number]} SchemeName */
|
|
20421
20642
|
|
|
@@ -20524,9 +20745,14 @@ function requireSchemes$1 () {
|
|
|
20524
20745
|
|
|
20525
20746
|
// reconstruct path from resource name
|
|
20526
20747
|
if (wsComponent.resourceName) {
|
|
20527
|
-
const
|
|
20748
|
+
const queryIndex = wsComponent.resourceName.indexOf('?');
|
|
20749
|
+
const path = queryIndex === -1
|
|
20750
|
+
? wsComponent.resourceName
|
|
20751
|
+
: wsComponent.resourceName.slice(0, queryIndex);
|
|
20528
20752
|
wsComponent.path = (path && path !== '/' ? path : undefined);
|
|
20529
|
-
wsComponent.query =
|
|
20753
|
+
wsComponent.query = queryIndex === -1
|
|
20754
|
+
? undefined
|
|
20755
|
+
: wsComponent.resourceName.slice(queryIndex + 1);
|
|
20530
20756
|
wsComponent.resourceName = undefined;
|
|
20531
20757
|
}
|
|
20532
20758
|
|
|
@@ -20543,7 +20769,7 @@ function requireSchemes$1 () {
|
|
|
20543
20769
|
return urnComponent
|
|
20544
20770
|
}
|
|
20545
20771
|
const matches = urnComponent.path.match(URN_REG);
|
|
20546
|
-
if (matches) {
|
|
20772
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
20547
20773
|
const scheme = options.scheme || urnComponent.scheme || 'urn';
|
|
20548
20774
|
urnComponent.nid = matches[1].toLowerCase();
|
|
20549
20775
|
urnComponent.nss = matches[2];
|
|
@@ -20632,18 +20858,389 @@ function requireSchemes$1 () {
|
|
|
20632
20858
|
serialize: ws.serialize
|
|
20633
20859
|
});
|
|
20634
20860
|
|
|
20635
|
-
const urn = /** @type {SchemeHandler} */ ({
|
|
20636
|
-
scheme: 'urn',
|
|
20637
|
-
parse: urnParse,
|
|
20638
|
-
serialize: urnSerialize,
|
|
20639
|
-
skipNormalize: true
|
|
20640
|
-
});
|
|
20861
|
+
const urn = /** @type {SchemeHandler} */ ({
|
|
20862
|
+
scheme: 'urn',
|
|
20863
|
+
parse: urnParse,
|
|
20864
|
+
serialize: urnSerialize,
|
|
20865
|
+
skipNormalize: true
|
|
20866
|
+
});
|
|
20867
|
+
|
|
20868
|
+
const urnuuid = /** @type {SchemeHandler} */ ({
|
|
20869
|
+
scheme: 'urn:uuid',
|
|
20870
|
+
parse: urnuuidParse,
|
|
20871
|
+
serialize: urnuuidSerialize,
|
|
20872
|
+
skipNormalize: true
|
|
20873
|
+
});
|
|
20874
|
+
|
|
20875
|
+
/**
|
|
20876
|
+
* Allow-sets for `encodeWithAllow`, each a `{ all, table, allowNonAscii }`
|
|
20877
|
+
* descriptor built by `allowSet`:
|
|
20878
|
+
*
|
|
20879
|
+
* - `LOCAL_PART` is the intersection of upstream uri-js's VCHAR and
|
|
20880
|
+
* NOT_PATH_NOSCHEME, minus ",": the serializer joins recipients with "," and
|
|
20881
|
+
* `mailtoParse` splits the path on it, so a comma left literal inside a local
|
|
20882
|
+
* part would silently become a recipient delimiter.
|
|
20883
|
+
* - `HFNAME` is the qchar allow-set used for header names/values.
|
|
20884
|
+
* - `DTEXT` is RFC 6068 dtext-no-obs restricted to characters that are also
|
|
20885
|
+
* safe in a URI path; `DTEXT_IRI` additionally permits non-ASCII for
|
|
20886
|
+
* `unicodeSupport`.
|
|
20887
|
+
*/
|
|
20888
|
+
const LOCAL_PART_ALLOWED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$'()*+=";
|
|
20889
|
+
const HFNAME_ALLOWED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$'()*+,;:@";
|
|
20890
|
+
const DTEXT_ALLOWED = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$'()*+;:";
|
|
20891
|
+
|
|
20892
|
+
/**
|
|
20893
|
+
* @typedef {Object} AllowSet
|
|
20894
|
+
* @property {RegExp} all - matches a whole string needing no encoding at all.
|
|
20895
|
+
* @property {Uint8Array} table - per-ASCII-code allow lookup for the slow path.
|
|
20896
|
+
* @property {boolean} allowNonAscii - pass non-ASCII code points through (IRI).
|
|
20897
|
+
*/
|
|
20898
|
+
|
|
20899
|
+
/**
|
|
20900
|
+
* `all` deliberately excludes "%" and (for the IRI variant) surrogates, so any
|
|
20901
|
+
* input needing escape normalization or surrogate repair falls to the slow path.
|
|
20902
|
+
*
|
|
20903
|
+
* @param {string} chars
|
|
20904
|
+
* @param {boolean} [allowNonAscii=false]
|
|
20905
|
+
* @returns {AllowSet}
|
|
20906
|
+
*/
|
|
20907
|
+
function allowSet (chars, allowNonAscii = false) {
|
|
20908
|
+
const table = new Uint8Array(128);
|
|
20909
|
+
for (let i = 0; i < chars.length; i++) {
|
|
20910
|
+
table[chars.charCodeAt(i)] = 1;
|
|
20911
|
+
}
|
|
20912
|
+
const escaped = chars.replace(/[\\\]^-]/gu, '\\$&');
|
|
20913
|
+
const cls = allowNonAscii ? `[${escaped}]|[^\\0-\\x7F\\uD800-\\uDFFF]` : `[${escaped}]`;
|
|
20914
|
+
return { all: new RegExp(`^(?:${cls})*$`, 'u'), table, allowNonAscii }
|
|
20915
|
+
}
|
|
20916
|
+
|
|
20917
|
+
const LOCAL_PART = allowSet(LOCAL_PART_ALLOWED);
|
|
20918
|
+
const HFNAME = allowSet(HFNAME_ALLOWED);
|
|
20919
|
+
const DTEXT = allowSet(DTEXT_ALLOWED);
|
|
20920
|
+
const DTEXT_IRI = allowSet(DTEXT_ALLOWED, true);
|
|
20921
|
+
|
|
20922
|
+
const HEX_PAIR = /^[\da-f]{2}$/iu;
|
|
20923
|
+
const MAILTO_DOMAIN_LITERAL = /^\[[\x21-\x5A\x5E-\x7E]*\]$/u;
|
|
20924
|
+
const MAILTO_DOMAIN_ERROR = 'URI mailto has an invalid recipient domain.';
|
|
20925
|
+
const HAS_SURROGATE = /[\uD800-\uDFFF]/u;
|
|
20926
|
+
|
|
20927
|
+
/**
|
|
20928
|
+
* @param {string} str
|
|
20929
|
+
* @returns {string}
|
|
20930
|
+
*/
|
|
20931
|
+
function decodeHex (str) {
|
|
20932
|
+
if (typeof str !== 'string' || str.indexOf('%') === -1) {
|
|
20933
|
+
return str
|
|
20934
|
+
}
|
|
20935
|
+
try {
|
|
20936
|
+
return decodeURIComponent(str)
|
|
20937
|
+
} catch {
|
|
20938
|
+
return str
|
|
20939
|
+
}
|
|
20940
|
+
}
|
|
20941
|
+
|
|
20942
|
+
/**
|
|
20943
|
+
* Replaces lone surrogates with U+FFFD, leaving valid pairs intact. Only called
|
|
20944
|
+
* when `HAS_SURROGATE` matched, so the scan cost is not on the common path.
|
|
20945
|
+
* (`String.prototype.toWellFormed` would do this natively but is Node 20+.)
|
|
20946
|
+
*
|
|
20947
|
+
* @param {string} input
|
|
20948
|
+
* @returns {string}
|
|
20949
|
+
*/
|
|
20950
|
+
function replaceLoneSurrogates (input) {
|
|
20951
|
+
let result = '';
|
|
20952
|
+
for (let i = 0; i < input.length; i++) {
|
|
20953
|
+
const code = input.charCodeAt(i);
|
|
20954
|
+
if (code >= 0xD800 && code <= 0xDBFF && i + 1 < input.length) {
|
|
20955
|
+
const low = input.charCodeAt(i + 1);
|
|
20956
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
20957
|
+
result += input[i] + input[i + 1];
|
|
20958
|
+
i++;
|
|
20959
|
+
continue
|
|
20960
|
+
}
|
|
20961
|
+
}
|
|
20962
|
+
result += (code >= 0xD800 && code <= 0xDFFF) ? '\uFFFD' : input[i];
|
|
20963
|
+
}
|
|
20964
|
+
return result
|
|
20965
|
+
}
|
|
20966
|
+
|
|
20967
|
+
/**
|
|
20968
|
+
* Percent-encodes everything outside `set`, preserving existing valid escapes
|
|
20969
|
+
* (uppercased) and repairing lone surrogates.
|
|
20970
|
+
*
|
|
20971
|
+
* ASCII uses `BYTE_HEX` rather than `encodeURIComponent`. That is equivalent
|
|
20972
|
+
* only because every character `encodeURIComponent` leaves unescaped
|
|
20973
|
+
* (`A-Za-z0-9-_.!~*'()`) is present in all of the allow-sets above, so no such
|
|
20974
|
+
* character ever reaches the encode branch. Re-check if a set is narrowed.
|
|
20975
|
+
*
|
|
20976
|
+
* @param {string} input
|
|
20977
|
+
* @param {AllowSet} set
|
|
20978
|
+
* @returns {string}
|
|
20979
|
+
*/
|
|
20980
|
+
function encodeWithAllow (input, set) {
|
|
20981
|
+
if (set.all.test(input)) {
|
|
20982
|
+
return input
|
|
20983
|
+
}
|
|
20984
|
+
|
|
20985
|
+
const table = set.table;
|
|
20986
|
+
let result = '';
|
|
20987
|
+
for (let i = 0; i < input.length; i++) {
|
|
20988
|
+
const code = input.charCodeAt(i);
|
|
20989
|
+
|
|
20990
|
+
if (code < 0x80) {
|
|
20991
|
+
if (table[code] === 1) {
|
|
20992
|
+
result += input[i];
|
|
20993
|
+
} else if (code === 0x25 && i + 2 < input.length && HEX_PAIR.test(input.slice(i + 1, i + 3))) {
|
|
20994
|
+
result += '%' + input.slice(i + 1, i + 3).toUpperCase();
|
|
20995
|
+
i += 2;
|
|
20996
|
+
} else {
|
|
20997
|
+
result += BYTE_HEX[code];
|
|
20998
|
+
}
|
|
20999
|
+
continue
|
|
21000
|
+
}
|
|
21001
|
+
|
|
21002
|
+
if (code < 0xD800 || code > 0xDFFF) {
|
|
21003
|
+
result += set.allowNonAscii ? input[i] : percentEncodeNonAscii(code);
|
|
21004
|
+
continue
|
|
21005
|
+
}
|
|
21006
|
+
|
|
21007
|
+
if (code <= 0xDBFF && i + 1 < input.length) {
|
|
21008
|
+
const low = input.charCodeAt(i + 1);
|
|
21009
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
21010
|
+
result += set.allowNonAscii
|
|
21011
|
+
? input[i] + input[i + 1]
|
|
21012
|
+
: percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
21013
|
+
i++;
|
|
21014
|
+
continue
|
|
21015
|
+
}
|
|
21016
|
+
}
|
|
21017
|
+
|
|
21018
|
+
result += set.allowNonAscii ? '\uFFFD' : percentEncodeNonAscii(0xFFFD);
|
|
21019
|
+
}
|
|
21020
|
+
return result
|
|
21021
|
+
}
|
|
21022
|
+
|
|
21023
|
+
/**
|
|
21024
|
+
* @param {string} domain
|
|
21025
|
+
* @param {import('../types/index').Options} [options]
|
|
21026
|
+
* @param {{error?:string}} [component]
|
|
21027
|
+
* @returns {string}
|
|
21028
|
+
*/
|
|
21029
|
+
function mailtoNormalizeDomain (domain, options, component) {
|
|
21030
|
+
const normalizedDomain = String(domain).toLowerCase();
|
|
21031
|
+
|
|
21032
|
+
if (MAILTO_DOMAIN_LITERAL.test(normalizedDomain)) {
|
|
21033
|
+
return normalizedDomain
|
|
21034
|
+
}
|
|
21035
|
+
|
|
21036
|
+
// `hostname` is the identity for these, so skip constructing a WHATWG URL.
|
|
21037
|
+
if (normalizedDomain !== '' && !nonSimpleMailtoDomain(normalizedDomain)) {
|
|
21038
|
+
return normalizedDomain
|
|
21039
|
+
}
|
|
21040
|
+
|
|
21041
|
+
try {
|
|
21042
|
+
const parsedDomain = new URL('http://' + normalizedDomain);
|
|
21043
|
+
if (
|
|
21044
|
+
parsedDomain.username ||
|
|
21045
|
+
parsedDomain.password ||
|
|
21046
|
+
parsedDomain.port ||
|
|
21047
|
+
parsedDomain.pathname !== '/' ||
|
|
21048
|
+
parsedDomain.search ||
|
|
21049
|
+
parsedDomain.hash ||
|
|
21050
|
+
!parsedDomain.hostname
|
|
21051
|
+
) {
|
|
21052
|
+
throw new Error(MAILTO_DOMAIN_ERROR)
|
|
21053
|
+
}
|
|
21054
|
+
|
|
21055
|
+
return options && options.unicodeSupport
|
|
21056
|
+
? normalizedDomain
|
|
21057
|
+
: parsedDomain.hostname
|
|
21058
|
+
} catch {
|
|
21059
|
+
if (component) {
|
|
21060
|
+
component.error = component.error || MAILTO_DOMAIN_ERROR;
|
|
21061
|
+
}
|
|
21062
|
+
return normalizedDomain
|
|
21063
|
+
}
|
|
21064
|
+
}
|
|
21065
|
+
|
|
21066
|
+
/**
|
|
21067
|
+
* Percent-encodes everything in a recipient domain that is not `dtext-no-obs`
|
|
21068
|
+
* (RFC 6068). Serialize-only: `mailtoNormalizeDomain` returns the domain
|
|
21069
|
+
* verbatim when validation fails, and `MAILTO_DOMAIN_LITERAL` accepts the full
|
|
21070
|
+
* RFC 5321 dcontent range (which includes "?", "#", "&" and "/"), so without
|
|
21071
|
+
* this an attacker-supplied recipient could inject header fields or a fragment
|
|
21072
|
+
* into the serialized URI. `mailtoParse` deliberately reports the raw domain
|
|
21073
|
+
* instead, so it must not use this.
|
|
21074
|
+
* @param {string} domain
|
|
21075
|
+
* @param {import('../types/index').Options} [options]
|
|
21076
|
+
* @returns {string}
|
|
21077
|
+
*/
|
|
21078
|
+
function mailtoEncodeDomain (domain, options) {
|
|
21079
|
+
const set = options && options.unicodeSupport ? DTEXT_IRI : DTEXT;
|
|
21080
|
+
|
|
21081
|
+
// Keep the delimiters of a domain literal, encode its contents.
|
|
21082
|
+
if (domain.length > 1 && domain[0] === '[' && domain[domain.length - 1] === ']') {
|
|
21083
|
+
return '[' + encodeWithAllow(domain.slice(1, -1), set) + ']'
|
|
21084
|
+
}
|
|
21085
|
+
|
|
21086
|
+
return encodeWithAllow(domain, set)
|
|
21087
|
+
}
|
|
21088
|
+
|
|
21089
|
+
/**
|
|
21090
|
+
* @param {import('../types/index').URIComponent} component
|
|
21091
|
+
* @param {import('../types/index').Options} options
|
|
21092
|
+
* @returns {import('../types/index').URIComponent}
|
|
21093
|
+
*/
|
|
21094
|
+
function mailtoParse (component, options) {
|
|
21095
|
+
const mailtoComponent = component;
|
|
21096
|
+
|
|
21097
|
+
// The handler sets `skipNormalize`, so path and query arrive raw. Everything
|
|
21098
|
+
// here goes through `decodeHex`, which subsumes the generic normalizers --
|
|
21099
|
+
// except that they also fold lone surrogates to U+FFFD, so do that here.
|
|
21100
|
+
let rawPath = mailtoComponent.path;
|
|
21101
|
+
let rawQuery = mailtoComponent.query;
|
|
21102
|
+
if (rawPath && HAS_SURROGATE.test(rawPath)) rawPath = replaceLoneSurrogates(rawPath);
|
|
21103
|
+
if (rawQuery && HAS_SURROGATE.test(rawQuery)) rawQuery = replaceLoneSurrogates(rawQuery);
|
|
21104
|
+
|
|
21105
|
+
const to = rawPath
|
|
21106
|
+
? (rawPath.indexOf(',') === -1 ? [rawPath] : rawPath.split(','))
|
|
21107
|
+
: [];
|
|
21108
|
+
mailtoComponent.path = undefined;
|
|
21109
|
+
|
|
21110
|
+
if (rawQuery) {
|
|
21111
|
+
// Null prototype: header names come from untrusted input, so `headers[name]`
|
|
21112
|
+
// must not resolve to inherited members ("constructor", "toString", ...),
|
|
21113
|
+
// and "__proto__" has to land as a plain own property instead of hitting
|
|
21114
|
+
// Object.prototype's setter and being silently dropped. Allocated lazily so
|
|
21115
|
+
// the common subject/body-only URI does not pay for it.
|
|
21116
|
+
/** @type {Record<string,string>|null} */
|
|
21117
|
+
let headers = null;
|
|
21118
|
+
let start = 0;
|
|
21119
|
+
while (start <= rawQuery.length) {
|
|
21120
|
+
let end = rawQuery.indexOf('&', start);
|
|
21121
|
+
if (end === -1) end = rawQuery.length;
|
|
21122
|
+
const token = rawQuery.slice(start, end);
|
|
21123
|
+
start = end + 1;
|
|
21124
|
+
|
|
21125
|
+
const eqIdx = token.indexOf('=');
|
|
21126
|
+
if (eqIdx !== token.lastIndexOf('=')) {
|
|
21127
|
+
mailtoComponent.error = mailtoComponent.error || 'URI mailto has malformed header fields.';
|
|
21128
|
+
continue
|
|
21129
|
+
}
|
|
21130
|
+
const name = eqIdx === -1 ? token : token.slice(0, eqIdx);
|
|
21131
|
+
const value = eqIdx === -1 ? '' : token.slice(eqIdx + 1);
|
|
21132
|
+
if (name === 'to') {
|
|
21133
|
+
const addrs = value.split(',');
|
|
21134
|
+
for (let j = 0; j < addrs.length; j++) to.push(addrs[j]);
|
|
21135
|
+
continue
|
|
21136
|
+
}
|
|
21137
|
+
if (name === 'subject') {
|
|
21138
|
+
mailtoComponent.subject = decodeHex(value);
|
|
21139
|
+
continue
|
|
21140
|
+
}
|
|
21141
|
+
if (name === 'body') {
|
|
21142
|
+
mailtoComponent.body = decodeHex(value);
|
|
21143
|
+
continue
|
|
21144
|
+
}
|
|
21145
|
+
if (headers === null) headers = /** @type {Record<string,string>} */ (Object.create(null));
|
|
21146
|
+
headers[decodeHex(name)] = decodeHex(value);
|
|
21147
|
+
}
|
|
21148
|
+
if (headers !== null) mailtoComponent.headers = headers;
|
|
21149
|
+
}
|
|
21150
|
+
|
|
21151
|
+
mailtoComponent.query = undefined;
|
|
21152
|
+
|
|
21153
|
+
for (let i = 0; i < to.length; i++) {
|
|
21154
|
+
const rawAddr = to[i];
|
|
21155
|
+
const atIdx = rawAddr.lastIndexOf('@');
|
|
21156
|
+
if (atIdx < 0) {
|
|
21157
|
+
mailtoComponent.error = mailtoComponent.error || MAILTO_DOMAIN_ERROR;
|
|
21158
|
+
to[i] = decodeHex(rawAddr);
|
|
21159
|
+
continue
|
|
21160
|
+
}
|
|
21161
|
+
const local = decodeHex(rawAddr.slice(0, atIdx));
|
|
21162
|
+
const domain = mailtoNormalizeDomain(decodeHex(rawAddr.slice(atIdx + 1)), options, mailtoComponent);
|
|
21163
|
+
to[i] = local + '@' + domain;
|
|
21164
|
+
}
|
|
21165
|
+
|
|
21166
|
+
if (to.length) mailtoComponent.to = to;
|
|
21167
|
+
|
|
21168
|
+
return mailtoComponent
|
|
21169
|
+
}
|
|
21170
|
+
|
|
21171
|
+
/**
|
|
21172
|
+
* @param {import('../types/index').URIComponent} component
|
|
21173
|
+
* @param {import('../types/index').Options} options
|
|
21174
|
+
* @returns {import('../types/index').URIComponent}
|
|
21175
|
+
*/
|
|
21176
|
+
function mailtoSerialize (component, options) {
|
|
21177
|
+
const mailtoComponent = component;
|
|
21178
|
+
const to = Array.isArray(mailtoComponent.to) ? mailtoComponent.to.slice() : [];
|
|
21179
|
+
if (to.length) {
|
|
21180
|
+
for (let i = 0; i < to.length; i++) {
|
|
21181
|
+
const addr = String(to[i]);
|
|
21182
|
+
const atIdx = addr.lastIndexOf('@');
|
|
21183
|
+
const rawLocal = atIdx >= 0 ? addr.slice(0, atIdx) : addr;
|
|
21184
|
+
const rawDomain = atIdx >= 0 ? addr.slice(atIdx + 1) : '';
|
|
21185
|
+
const local = encodeWithAllow(rawLocal, LOCAL_PART);
|
|
21186
|
+
const decodedDomain = decodeHex(rawDomain);
|
|
21187
|
+
// `component` is intentionally not passed: index.js's `serialize` writes
|
|
21188
|
+
// the error onto a private copy and returns only a string, so an error
|
|
21189
|
+
// recorded here would be unreachable. Safety comes from encoding instead.
|
|
21190
|
+
const normalizedDomain = mailtoNormalizeDomain(decodedDomain, options);
|
|
21191
|
+
to[i] = local + '@' + mailtoEncodeDomain(normalizedDomain, options);
|
|
21192
|
+
}
|
|
21193
|
+
// Every recipient is now fully encoded and only the joining commas are
|
|
21194
|
+
// literal, so bypass index.js's generic encoder, whose allow-set is
|
|
21195
|
+
// narrower than upstream's and would over-encode.
|
|
21196
|
+
mailtoComponent.path = to.join(',');
|
|
21197
|
+
options.skipEscape = true;
|
|
21198
|
+
} else {
|
|
21199
|
+
// No recipients: drop any caller-supplied path, matching upstream uri-js.
|
|
21200
|
+
// `to` is the only recipient source, and `mailtoParse` always moves the
|
|
21201
|
+
// path into it, so a mailto component never legitimately carries one.
|
|
21202
|
+
mailtoComponent.path = undefined;
|
|
21203
|
+
}
|
|
21204
|
+
|
|
21205
|
+
const headers = mailtoComponent.headers && typeof mailtoComponent.headers === 'object'
|
|
21206
|
+
? Object.assign(Object.create(null), mailtoComponent.headers)
|
|
21207
|
+
: Object.create(null);
|
|
21208
|
+
|
|
21209
|
+
if (mailtoComponent.subject) headers.subject = mailtoComponent.subject;
|
|
21210
|
+
if (mailtoComponent.body) headers.body = mailtoComponent.body;
|
|
21211
|
+
mailtoComponent.headers = headers;
|
|
21212
|
+
|
|
21213
|
+
// `headers` has a null prototype and was filled via Object.assign, so every
|
|
21214
|
+
// enumerable key is an own property and no hasOwnProperty guard is needed.
|
|
21215
|
+
let query = '';
|
|
21216
|
+
let count = 0;
|
|
21217
|
+
for (const name in headers) {
|
|
21218
|
+
if (count++ !== 0) query += '&';
|
|
21219
|
+
query += encodeWithAllow(name, HFNAME) + '=' + encodeWithAllow(String(headers[name]), HFNAME);
|
|
21220
|
+
}
|
|
21221
|
+
|
|
21222
|
+
if (count !== 0) {
|
|
21223
|
+
mailtoComponent.query = query;
|
|
21224
|
+
} else {
|
|
21225
|
+
mailtoComponent.headers = undefined;
|
|
21226
|
+
}
|
|
21227
|
+
|
|
21228
|
+
return mailtoComponent
|
|
21229
|
+
}
|
|
20641
21230
|
|
|
20642
|
-
const
|
|
20643
|
-
scheme: '
|
|
20644
|
-
parse:
|
|
20645
|
-
serialize:
|
|
20646
|
-
|
|
21231
|
+
const mailto = /** @type {SchemeHandler} */ ({
|
|
21232
|
+
scheme: 'mailto',
|
|
21233
|
+
parse: mailtoParse,
|
|
21234
|
+
serialize: mailtoSerialize,
|
|
21235
|
+
domainHost: false,
|
|
21236
|
+
unicodeSupport: true,
|
|
21237
|
+
// `mailtoParse` re-derives every component from the raw path/query via
|
|
21238
|
+
// `decodeHex`, which subsumes the generic normalizers, so running them first
|
|
21239
|
+
// is wasted work (~28% of parsing a URI with header fields).
|
|
21240
|
+
skipNormalize: true,
|
|
21241
|
+
// A recipient list has no dot segments to remove, and `removeDotSegments`
|
|
21242
|
+
// would rewrite a "./"-prefixed local part.
|
|
21243
|
+
absolutePath: true
|
|
20647
21244
|
});
|
|
20648
21245
|
|
|
20649
21246
|
const SCHEMES = /** @type {Record<SchemeName, SchemeHandler>} */ ({
|
|
@@ -20652,7 +21249,8 @@ function requireSchemes$1 () {
|
|
|
20652
21249
|
ws,
|
|
20653
21250
|
wss,
|
|
20654
21251
|
urn,
|
|
20655
|
-
'urn:uuid': urnuuid
|
|
21252
|
+
'urn:uuid': urnuuid,
|
|
21253
|
+
mailto
|
|
20656
21254
|
});
|
|
20657
21255
|
|
|
20658
21256
|
Object.setPrototypeOf(SCHEMES, null);
|
|
@@ -20685,9 +21283,24 @@ function requireFastUri$1 () {
|
|
|
20685
21283
|
if (hasRequiredFastUri$1) return fastUri$1.exports;
|
|
20686
21284
|
hasRequiredFastUri$1 = 1;
|
|
20687
21285
|
|
|
20688
|
-
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding,
|
|
21286
|
+
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = requireUtils$1();
|
|
20689
21287
|
const { SCHEMES, getSchemeHandler } = requireSchemes$1();
|
|
20690
21288
|
|
|
21289
|
+
const VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
21290
|
+
const MALFORMED_SCHEME_ERROR = 'URI scheme is malformed.';
|
|
21291
|
+
|
|
21292
|
+
/**
|
|
21293
|
+
* @param {string} scheme
|
|
21294
|
+
* @returns {string}
|
|
21295
|
+
*/
|
|
21296
|
+
function decodeValidScheme (scheme) {
|
|
21297
|
+
const decodedScheme = unescape(String(scheme));
|
|
21298
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
21299
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR)
|
|
21300
|
+
}
|
|
21301
|
+
return decodedScheme
|
|
21302
|
+
}
|
|
21303
|
+
|
|
20691
21304
|
/**
|
|
20692
21305
|
* @template {import('./types/index').URIComponent|string} T
|
|
20693
21306
|
* @param {T} uri
|
|
@@ -20711,12 +21324,50 @@ function requireFastUri$1 () {
|
|
|
20711
21324
|
*/
|
|
20712
21325
|
function resolve (baseURI, relativeURI, options) {
|
|
20713
21326
|
const schemelessOptions = options ? Object.assign({ scheme: 'null' }, options) : { scheme: 'null' };
|
|
20714
|
-
const {
|
|
20715
|
-
|
|
20716
|
-
|
|
21327
|
+
const {
|
|
21328
|
+
parsed: baseParsed,
|
|
21329
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
21330
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
21331
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
21332
|
+
malformedHost: baseMalformedHost,
|
|
21333
|
+
malformedScheme: baseMalformedScheme
|
|
21334
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
21335
|
+
const {
|
|
21336
|
+
parsed: relativeParsed,
|
|
21337
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
21338
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
21339
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
21340
|
+
malformedHost: relativeMalformedHost,
|
|
21341
|
+
malformedScheme: relativeMalformedScheme
|
|
21342
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
21343
|
+
if (
|
|
21344
|
+
baseMalformed ||
|
|
21345
|
+
relativeMalformed ||
|
|
21346
|
+
baseMalformedPercentEncoding ||
|
|
21347
|
+
relativeMalformedPercentEncoding ||
|
|
21348
|
+
baseMalformedSchemeSpecific ||
|
|
21349
|
+
relativeMalformedSchemeSpecific ||
|
|
21350
|
+
baseMalformedHost ||
|
|
21351
|
+
relativeMalformedHost ||
|
|
21352
|
+
baseMalformedScheme ||
|
|
21353
|
+
relativeMalformedScheme
|
|
21354
|
+
) {
|
|
20717
21355
|
throw new Error(baseParsed.error || relativeParsed.error || 'URI is malformed.')
|
|
20718
21356
|
}
|
|
20719
21357
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
21358
|
+
const resolvedSchemeHandler = getSchemeHandler((options && options.scheme) || resolved.scheme);
|
|
21359
|
+
const resolvedHost = resolved.host;
|
|
21360
|
+
const resolvedHostIsIP = resolvedHost !== undefined && resolvedHost !== '' &&
|
|
21361
|
+
(isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
21362
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
21363
|
+
// Percent escapes in an ASCII reg-name are encoded data. The WHATWG hostname
|
|
21364
|
+
// parser can reject them even though fast-uri preserves them safely as RFC
|
|
21365
|
+
// 3986 data. A raw non-ASCII host must still fail closed if conversion fails.
|
|
21366
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf('%') !== -1 &&
|
|
21367
|
+
!/\P{ASCII}/u.test(resolvedHost);
|
|
21368
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
21369
|
+
throw new Error(resolved.error)
|
|
21370
|
+
}
|
|
20720
21371
|
schemelessOptions.skipEscape = true;
|
|
20721
21372
|
return serialize(resolved, schemelessOptions)
|
|
20722
21373
|
}
|
|
@@ -20799,7 +21450,7 @@ function requireFastUri$1 () {
|
|
|
20799
21450
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
20800
21451
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
20801
21452
|
|
|
20802
|
-
return normalizedA !== undefined && normalizedB !== undefined && normalizedA
|
|
21453
|
+
return normalizedA !== undefined && normalizedB !== undefined && normalizedA === normalizedB
|
|
20803
21454
|
}
|
|
20804
21455
|
|
|
20805
21456
|
/**
|
|
@@ -20822,30 +21473,39 @@ function requireFastUri$1 () {
|
|
|
20822
21473
|
reference: cmpts.reference,
|
|
20823
21474
|
resourceName: cmpts.resourceName,
|
|
20824
21475
|
secure: cmpts.secure,
|
|
21476
|
+
to: cmpts.to,
|
|
21477
|
+
subject: cmpts.subject,
|
|
21478
|
+
body: cmpts.body,
|
|
21479
|
+
headers: cmpts.headers,
|
|
20825
21480
|
error: ''
|
|
20826
21481
|
};
|
|
20827
21482
|
const options = Object.assign({}, opts);
|
|
20828
21483
|
const uriTokens = [];
|
|
20829
21484
|
|
|
21485
|
+
if (component.scheme) {
|
|
21486
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
21487
|
+
}
|
|
21488
|
+
|
|
20830
21489
|
// find scheme handler
|
|
20831
21490
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
20832
21491
|
|
|
20833
21492
|
// perform scheme specific serialization
|
|
20834
21493
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
20835
21494
|
|
|
21495
|
+
const hasAuthority = component.userinfo !== undefined || component.host !== undefined || component.port !== undefined;
|
|
21496
|
+
const pathNoScheme = !options.skipEscape && component.scheme === undefined && !hasAuthority;
|
|
21497
|
+
|
|
20836
21498
|
if (component.path !== undefined) {
|
|
20837
21499
|
if (!options.skipEscape) {
|
|
20838
|
-
component.path =
|
|
20839
|
-
|
|
20840
|
-
if (component.scheme !== undefined) {
|
|
20841
|
-
component.path = component.path.split('%3A').join(':');
|
|
20842
|
-
}
|
|
21500
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
20843
21501
|
} else {
|
|
20844
21502
|
component.path = normalizePercentEncoding(component.path);
|
|
20845
21503
|
}
|
|
20846
21504
|
}
|
|
20847
21505
|
|
|
20848
21506
|
if (options.reference !== 'suffix' && component.scheme) {
|
|
21507
|
+
// Scheme handlers may replace the scheme during serialization.
|
|
21508
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
20849
21509
|
uriTokens.push(component.scheme, ':');
|
|
20850
21510
|
}
|
|
20851
21511
|
|
|
@@ -20868,6 +21528,13 @@ function requireFastUri$1 () {
|
|
|
20868
21528
|
s = removeDotSegments(s);
|
|
20869
21529
|
}
|
|
20870
21530
|
|
|
21531
|
+
// Dot-segment removal can expose a colon that was not originally in the
|
|
21532
|
+
// first segment (for example, "./a:b"). Reapply path-noscheme encoding so
|
|
21533
|
+
// the serialized relative reference cannot be reparsed as a URI scheme.
|
|
21534
|
+
if (pathNoScheme) {
|
|
21535
|
+
s = serializePathEncoding(s, true);
|
|
21536
|
+
}
|
|
21537
|
+
|
|
20871
21538
|
if (
|
|
20872
21539
|
authority === undefined &&
|
|
20873
21540
|
s[0] === '/' &&
|
|
@@ -20881,11 +21548,11 @@ function requireFastUri$1 () {
|
|
|
20881
21548
|
}
|
|
20882
21549
|
|
|
20883
21550
|
if (component.query !== undefined) {
|
|
20884
|
-
uriTokens.push('?', component.query);
|
|
21551
|
+
uriTokens.push('?', encodeQuery(component.query));
|
|
20885
21552
|
}
|
|
20886
21553
|
|
|
20887
21554
|
if (component.fragment !== undefined) {
|
|
20888
|
-
uriTokens.push('#', component.fragment);
|
|
21555
|
+
uriTokens.push('#', encodeFragment(component.fragment));
|
|
20889
21556
|
}
|
|
20890
21557
|
return uriTokens.join('')
|
|
20891
21558
|
}
|
|
@@ -20922,10 +21589,93 @@ function requireFastUri$1 () {
|
|
|
20922
21589
|
return undefined
|
|
20923
21590
|
}
|
|
20924
21591
|
|
|
21592
|
+
/**
|
|
21593
|
+
* Checks percent syntax without decoding the represented octets. RFC 3986
|
|
21594
|
+
* percent-encoding is byte-oriented, so sequences such as `%FF` are valid even
|
|
21595
|
+
* though they are not independently valid UTF-8.
|
|
21596
|
+
*
|
|
21597
|
+
* @param {string|undefined} component
|
|
21598
|
+
* @returns {boolean}
|
|
21599
|
+
*/
|
|
21600
|
+
function hasMalformedPercentEncoding (component) {
|
|
21601
|
+
if (component === undefined) return false
|
|
21602
|
+
|
|
21603
|
+
let percent = component.indexOf('%');
|
|
21604
|
+
while (percent !== -1) {
|
|
21605
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
21606
|
+
return true
|
|
21607
|
+
}
|
|
21608
|
+
percent = component.indexOf('%', percent + 3);
|
|
21609
|
+
}
|
|
21610
|
+
|
|
21611
|
+
return false
|
|
21612
|
+
}
|
|
21613
|
+
|
|
21614
|
+
/**
|
|
21615
|
+
* Whether the host is a bracketed IP literal (RFC 3986 `IP-literal`).
|
|
21616
|
+
* An unterminated `[` is not a literal, so it must still be validated as a
|
|
21617
|
+
* reg-name instead of being waved through as an IP.
|
|
21618
|
+
*
|
|
21619
|
+
* @param {string} host
|
|
21620
|
+
* @returns {boolean}
|
|
21621
|
+
*/
|
|
21622
|
+
function isIPLiteral (host) {
|
|
21623
|
+
return host[0] === '[' && host[host.length - 1] === ']'
|
|
21624
|
+
}
|
|
21625
|
+
|
|
21626
|
+
/**
|
|
21627
|
+
* @param {RegExpMatchArray} matches
|
|
21628
|
+
* @returns {boolean}
|
|
21629
|
+
*/
|
|
21630
|
+
function hasMalformedComponentPercentEncoding (matches) {
|
|
21631
|
+
// Bracketed IP literals use a raw "%" as the zone separator for historical
|
|
21632
|
+
// compatibility. Their parsing is intentionally left to normalizeIPv6.
|
|
21633
|
+
const host = matches[4];
|
|
21634
|
+
return hasMalformedPercentEncoding(matches[3]) ||
|
|
21635
|
+
(host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host)) ||
|
|
21636
|
+
hasMalformedPercentEncoding(matches[6]) ||
|
|
21637
|
+
hasMalformedPercentEncoding(matches[7]) ||
|
|
21638
|
+
hasMalformedPercentEncoding(matches[8])
|
|
21639
|
+
}
|
|
21640
|
+
|
|
21641
|
+
/**
|
|
21642
|
+
* @param {import('./types/index').URIComponent} parsed
|
|
21643
|
+
* @param {import('./types/index').Options} options
|
|
21644
|
+
* @param {{ domainHost?: boolean, unicodeSupport?: boolean }|undefined} schemeHandler
|
|
21645
|
+
* @param {boolean} isIP
|
|
21646
|
+
* @returns {boolean} whether host conversion failed
|
|
21647
|
+
*/
|
|
21648
|
+
function canonicalizeHost (parsed, options, schemeHandler, isIP) {
|
|
21649
|
+
const bracketedIPLiteral =
|
|
21650
|
+
parsed.host &&
|
|
21651
|
+
parsed.host[0] === '[' &&
|
|
21652
|
+
parsed.host[parsed.host.length - 1] === ']';
|
|
21653
|
+
|
|
21654
|
+
if (
|
|
21655
|
+
!options.unicodeSupport &&
|
|
21656
|
+
(!schemeHandler || !schemeHandler.unicodeSupport) &&
|
|
21657
|
+
parsed.host &&
|
|
21658
|
+
!isIPLiteral(parsed.host) &&
|
|
21659
|
+
(options.domainHost || (schemeHandler && schemeHandler.domainHost)) &&
|
|
21660
|
+
isIP === false &&
|
|
21661
|
+
!bracketedIPLiteral &&
|
|
21662
|
+
nonSimpleDomain(parsed.host)
|
|
21663
|
+
) {
|
|
21664
|
+
try {
|
|
21665
|
+
parsed.host = new URL('http://' + parsed.host).hostname;
|
|
21666
|
+
} catch (e) {
|
|
21667
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
21668
|
+
return true
|
|
21669
|
+
}
|
|
21670
|
+
}
|
|
21671
|
+
|
|
21672
|
+
return false
|
|
21673
|
+
}
|
|
21674
|
+
|
|
20925
21675
|
/**
|
|
20926
21676
|
* @param {string} uri
|
|
20927
21677
|
* @param {import('./types/index').Options} [opts]
|
|
20928
|
-
* @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean }}
|
|
21678
|
+
* @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
|
|
20929
21679
|
*/
|
|
20930
21680
|
function parseWithStatus (uri, opts) {
|
|
20931
21681
|
const options = Object.assign({}, opts);
|
|
@@ -20941,6 +21691,11 @@ function requireFastUri$1 () {
|
|
|
20941
21691
|
};
|
|
20942
21692
|
|
|
20943
21693
|
let malformedAuthorityOrPort = false;
|
|
21694
|
+
let malformedPercentEncoding = false;
|
|
21695
|
+
let malformedSchemeSpecific = false;
|
|
21696
|
+
let malformedHost = false;
|
|
21697
|
+
let malformedIPLiteral = false;
|
|
21698
|
+
let malformedScheme = false;
|
|
20944
21699
|
|
|
20945
21700
|
let isIP = false;
|
|
20946
21701
|
if (options.reference === 'suffix') {
|
|
@@ -20990,7 +21745,7 @@ function requireFastUri$1 () {
|
|
|
20990
21745
|
|
|
20991
21746
|
if (matches) {
|
|
20992
21747
|
// store each component
|
|
20993
|
-
parsed.scheme = matches[1]
|
|
21748
|
+
parsed.scheme = matches[1];
|
|
20994
21749
|
parsed.userinfo = matches[3];
|
|
20995
21750
|
parsed.host = matches[4];
|
|
20996
21751
|
parsed.port = parseInt(matches[5], 10);
|
|
@@ -20998,6 +21753,21 @@ function requireFastUri$1 () {
|
|
|
20998
21753
|
parsed.query = matches[7];
|
|
20999
21754
|
parsed.fragment = matches[8];
|
|
21000
21755
|
|
|
21756
|
+
if (parsed.scheme !== undefined) {
|
|
21757
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
21758
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
21759
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
21760
|
+
} else {
|
|
21761
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
21762
|
+
malformedScheme = true;
|
|
21763
|
+
}
|
|
21764
|
+
}
|
|
21765
|
+
|
|
21766
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
21767
|
+
if (malformedPercentEncoding) {
|
|
21768
|
+
parsed.error = parsed.error || 'URI contains malformed percent-encoding.';
|
|
21769
|
+
}
|
|
21770
|
+
|
|
21001
21771
|
// fix port number
|
|
21002
21772
|
if (isNaN(parsed.port)) {
|
|
21003
21773
|
parsed.port = matches[5];
|
|
@@ -21012,9 +21782,17 @@ function requireFastUri$1 () {
|
|
|
21012
21782
|
if (parsed.host) {
|
|
21013
21783
|
const ipv4result = isIPv4(parsed.host);
|
|
21014
21784
|
if (ipv4result === false) {
|
|
21785
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
21786
|
+
const hasIPLiteralBracket = parsed.host.indexOf('[') !== -1 || parsed.host.indexOf(']') !== -1;
|
|
21015
21787
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
21016
|
-
|
|
21017
|
-
|
|
21788
|
+
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
21789
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
21790
|
+
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
21791
|
+
|
|
21792
|
+
if (malformedIPLiteral) {
|
|
21793
|
+
parsed.error = parsed.error || 'URI host is malformed.';
|
|
21794
|
+
malformedAuthorityOrPort = true;
|
|
21795
|
+
}
|
|
21018
21796
|
} else {
|
|
21019
21797
|
isIP = true;
|
|
21020
21798
|
}
|
|
@@ -21037,27 +21815,16 @@ function requireFastUri$1 () {
|
|
|
21037
21815
|
// find scheme handler
|
|
21038
21816
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
21039
21817
|
|
|
21040
|
-
//
|
|
21041
|
-
if (!
|
|
21042
|
-
|
|
21043
|
-
if (parsed.host && (options.domainHost || (schemeHandler && schemeHandler.domainHost)) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
21044
|
-
// convert Unicode IDN -> ASCII IDN
|
|
21045
|
-
try {
|
|
21046
|
-
parsed.host = new URL('http://' + parsed.host).hostname;
|
|
21047
|
-
} catch (e) {
|
|
21048
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
21049
|
-
}
|
|
21050
|
-
}
|
|
21051
|
-
// convert IRI -> URI
|
|
21818
|
+
// convert Unicode IDN -> ASCII IDN when the effective scheme uses domain hosts
|
|
21819
|
+
if (!malformedIPLiteral) {
|
|
21820
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
21052
21821
|
}
|
|
21053
21822
|
|
|
21054
21823
|
if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
|
|
21055
21824
|
if (uri.indexOf('%') !== -1) {
|
|
21056
|
-
if (parsed.
|
|
21057
|
-
parsed.
|
|
21058
|
-
|
|
21059
|
-
if (parsed.host !== undefined) {
|
|
21060
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
21825
|
+
if (parsed.host !== undefined && !malformedIPLiteral) {
|
|
21826
|
+
const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
21827
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
21061
21828
|
}
|
|
21062
21829
|
}
|
|
21063
21830
|
if (parsed.path) {
|
|
@@ -21074,11 +21841,14 @@ function requireFastUri$1 () {
|
|
|
21074
21841
|
// perform scheme specific parsing
|
|
21075
21842
|
if (schemeHandler && schemeHandler.parse) {
|
|
21076
21843
|
schemeHandler.parse(parsed, options);
|
|
21844
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === undefined) {
|
|
21845
|
+
malformedSchemeSpecific = true;
|
|
21846
|
+
}
|
|
21077
21847
|
}
|
|
21078
21848
|
} else {
|
|
21079
21849
|
parsed.error = parsed.error || 'URI can not be parsed.';
|
|
21080
21850
|
}
|
|
21081
|
-
return { parsed, malformedAuthorityOrPort }
|
|
21851
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme }
|
|
21082
21852
|
}
|
|
21083
21853
|
|
|
21084
21854
|
/**
|
|
@@ -21102,13 +21872,17 @@ function requireFastUri$1 () {
|
|
|
21102
21872
|
/**
|
|
21103
21873
|
* @param {string} uri
|
|
21104
21874
|
* @param {import('./types/index').Options} [opts]
|
|
21105
|
-
* @returns {{ normalized: string, malformedAuthorityOrPort: boolean }}
|
|
21875
|
+
* @returns {{ normalized: string, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
|
|
21106
21876
|
*/
|
|
21107
21877
|
function normalizeStringWithStatus (uri, opts) {
|
|
21108
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
21878
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
21109
21879
|
return {
|
|
21110
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
21111
|
-
malformedAuthorityOrPort
|
|
21880
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
21881
|
+
malformedAuthorityOrPort,
|
|
21882
|
+
malformedPercentEncoding,
|
|
21883
|
+
malformedSchemeSpecific,
|
|
21884
|
+
malformedHost,
|
|
21885
|
+
malformedScheme
|
|
21112
21886
|
}
|
|
21113
21887
|
}
|
|
21114
21888
|
|
|
@@ -21118,14 +21892,18 @@ function requireFastUri$1 () {
|
|
|
21118
21892
|
* @returns {string|undefined}
|
|
21119
21893
|
*/
|
|
21120
21894
|
function normalizeComparableURI (uri, opts) {
|
|
21121
|
-
if (typeof uri
|
|
21122
|
-
|
|
21123
|
-
return malformedAuthorityOrPort ? undefined : normalized
|
|
21895
|
+
if (typeof uri !== 'string' && typeof uri !== 'object') {
|
|
21896
|
+
return undefined
|
|
21124
21897
|
}
|
|
21125
21898
|
|
|
21126
|
-
|
|
21127
|
-
|
|
21899
|
+
let value;
|
|
21900
|
+
try {
|
|
21901
|
+
value = typeof uri === 'string' ? uri : serialize(uri, opts);
|
|
21902
|
+
} catch {
|
|
21903
|
+
return undefined
|
|
21128
21904
|
}
|
|
21905
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
21906
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? undefined : normalized
|
|
21129
21907
|
}
|
|
21130
21908
|
|
|
21131
21909
|
const fastUri = {
|
|
@@ -35097,6 +35875,9 @@ function requireUtils () {
|
|
|
35097
35875
|
/** @type {(value: string) => boolean} */
|
|
35098
35876
|
const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
|
|
35099
35877
|
|
|
35878
|
+
/** @type {(value: string) => boolean} */
|
|
35879
|
+
const isPort = RegExp.prototype.test.bind(/^\d*$/u);
|
|
35880
|
+
|
|
35100
35881
|
/** @type {(value: string) => boolean} */
|
|
35101
35882
|
const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
35102
35883
|
|
|
@@ -35104,7 +35885,36 @@ function requireUtils () {
|
|
|
35104
35885
|
const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
35105
35886
|
|
|
35106
35887
|
/** @type {(value: string) => boolean} */
|
|
35107
|
-
const isPathCharacter = RegExp.prototype.test.bind(/^[
|
|
35888
|
+
const isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
|
|
35889
|
+
|
|
35890
|
+
/** @type {(value: string) => boolean} */
|
|
35891
|
+
const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
|
|
35892
|
+
|
|
35893
|
+
/** @type {(value: string) => boolean} */
|
|
35894
|
+
const isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
|
|
35895
|
+
|
|
35896
|
+
const BYTE_HEX = new Array(256);
|
|
35897
|
+
{
|
|
35898
|
+
const HEX_DIGITS = '0123456789ABCDEF';
|
|
35899
|
+
for (let i = 0; i < 256; i++) {
|
|
35900
|
+
BYTE_HEX[i] = '%' + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 0xF];
|
|
35901
|
+
}
|
|
35902
|
+
}
|
|
35903
|
+
function percentEncodeNonAscii (cp) {
|
|
35904
|
+
if (cp < 0x800) {
|
|
35905
|
+
return BYTE_HEX[0xC0 | (cp >> 6)] +
|
|
35906
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
35907
|
+
}
|
|
35908
|
+
if (cp < 0x10000) {
|
|
35909
|
+
return BYTE_HEX[0xE0 | (cp >> 12)] +
|
|
35910
|
+
BYTE_HEX[0x80 | ((cp >> 6) & 0x3F)] +
|
|
35911
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
35912
|
+
}
|
|
35913
|
+
return BYTE_HEX[0xF0 | (cp >> 18)] +
|
|
35914
|
+
BYTE_HEX[0x80 | ((cp >> 12) & 0x3F)] +
|
|
35915
|
+
BYTE_HEX[0x80 | ((cp >> 6) & 0x3F)] +
|
|
35916
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
35917
|
+
}
|
|
35108
35918
|
|
|
35109
35919
|
/**
|
|
35110
35920
|
* @param {Array<string>} input
|
|
@@ -35137,12 +35947,14 @@ function requireUtils () {
|
|
|
35137
35947
|
return acc
|
|
35138
35948
|
}
|
|
35139
35949
|
|
|
35140
|
-
/**
|
|
35141
|
-
|
|
35142
|
-
|
|
35143
|
-
|
|
35144
|
-
|
|
35145
|
-
|
|
35950
|
+
/** @type {(value: string) => boolean} */
|
|
35951
|
+
const isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
|
|
35952
|
+
|
|
35953
|
+
/** @type {(value: string) => boolean} */
|
|
35954
|
+
const isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
|
|
35955
|
+
|
|
35956
|
+
/** @type {(value: string) => boolean} */
|
|
35957
|
+
const isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
|
|
35146
35958
|
|
|
35147
35959
|
/**
|
|
35148
35960
|
* @param {string} value
|
|
@@ -35151,88 +35963,104 @@ function requireUtils () {
|
|
|
35151
35963
|
const nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
|
|
35152
35964
|
|
|
35153
35965
|
/**
|
|
35154
|
-
* @param {
|
|
35966
|
+
* @param {string} zone
|
|
35155
35967
|
* @returns {boolean}
|
|
35156
35968
|
*/
|
|
35157
|
-
function
|
|
35158
|
-
|
|
35969
|
+
function isZoneIdentifier (zone) {
|
|
35970
|
+
if (zone.length === 0) return false
|
|
35971
|
+
|
|
35972
|
+
for (let i = 0; i < zone.length; i++) {
|
|
35973
|
+
if (isZoneCharacter(zone[i])) continue
|
|
35974
|
+
if (zone[i] === '%' && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
|
|
35975
|
+
i += 2;
|
|
35976
|
+
continue
|
|
35977
|
+
}
|
|
35978
|
+
return false
|
|
35979
|
+
}
|
|
35980
|
+
|
|
35159
35981
|
return true
|
|
35160
35982
|
}
|
|
35161
35983
|
|
|
35162
35984
|
/**
|
|
35163
|
-
*
|
|
35164
|
-
*
|
|
35165
|
-
*
|
|
35166
|
-
* @
|
|
35985
|
+
* Compresses the longest run of zero hextets to "::" per RFC 5952. A run of a
|
|
35986
|
+
* single zero hextet is left uncompressed. On ties the leftmost run wins.
|
|
35987
|
+
*
|
|
35988
|
+
* @param {string[]} hextets
|
|
35989
|
+
* @returns {string}
|
|
35167
35990
|
*/
|
|
35168
|
-
function
|
|
35169
|
-
|
|
35170
|
-
|
|
35171
|
-
|
|
35172
|
-
|
|
35991
|
+
function compressIPv6ZeroRun (hextets) {
|
|
35992
|
+
let bestStart = -1;
|
|
35993
|
+
let bestLength = 0;
|
|
35994
|
+
let runStart = -1;
|
|
35995
|
+
let runLength = 0;
|
|
35996
|
+
for (let i = 0; i < hextets.length; i++) {
|
|
35997
|
+
if (hextets[i] === '0') {
|
|
35998
|
+
if (runStart === -1) runStart = i;
|
|
35999
|
+
runLength++;
|
|
36000
|
+
if (runLength > bestLength) {
|
|
36001
|
+
bestLength = runLength;
|
|
36002
|
+
bestStart = runStart;
|
|
36003
|
+
}
|
|
35173
36004
|
} else {
|
|
35174
|
-
|
|
35175
|
-
|
|
36005
|
+
runStart = -1;
|
|
36006
|
+
runLength = 0;
|
|
35176
36007
|
}
|
|
35177
|
-
buffer.length = 0;
|
|
35178
36008
|
}
|
|
35179
|
-
|
|
36009
|
+
|
|
36010
|
+
if (bestLength < 2) return hextets.join(':')
|
|
36011
|
+
|
|
36012
|
+
const head = hextets.slice(0, bestStart).join(':');
|
|
36013
|
+
const tail = hextets.slice(bestStart + bestLength).join(':');
|
|
36014
|
+
return head + '::' + tail
|
|
35180
36015
|
}
|
|
35181
36016
|
|
|
35182
36017
|
/**
|
|
36018
|
+
* Validates an IPv6 address against the alternatives in RFC 3986 section
|
|
36019
|
+
* 3.2.2 and returns the same address with leading hextet zeroes removed.
|
|
36020
|
+
* An embedded IPv4 address counts as two hextets and is only valid at the end.
|
|
36021
|
+
*
|
|
35183
36022
|
* @param {string} input
|
|
35184
|
-
* @returns {
|
|
36023
|
+
* @returns {string|undefined}
|
|
35185
36024
|
*/
|
|
35186
|
-
function
|
|
35187
|
-
|
|
35188
|
-
|
|
35189
|
-
|
|
35190
|
-
const
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
|
|
35194
|
-
|
|
35195
|
-
|
|
35196
|
-
|
|
35197
|
-
|
|
35198
|
-
|
|
35199
|
-
|
|
35200
|
-
|
|
35201
|
-
if (
|
|
35202
|
-
|
|
35203
|
-
|
|
35204
|
-
|
|
35205
|
-
|
|
35206
|
-
if (++tokenCount > 7) {
|
|
35207
|
-
// not valid
|
|
35208
|
-
output.error = true;
|
|
35209
|
-
break
|
|
35210
|
-
}
|
|
35211
|
-
if (i > 0 && input[i - 1] === ':') {
|
|
35212
|
-
endipv6Encountered = true;
|
|
35213
|
-
}
|
|
35214
|
-
address.push(':');
|
|
35215
|
-
continue
|
|
35216
|
-
} else if (cursor === '%') {
|
|
35217
|
-
if (!consume(buffer, address, output)) { break }
|
|
35218
|
-
// switch to zone detection
|
|
35219
|
-
consume = consumeIsZone;
|
|
35220
|
-
} else {
|
|
35221
|
-
buffer.push(cursor);
|
|
36025
|
+
function normalizeIPv6Address (input) {
|
|
36026
|
+
const compression = input.indexOf('::');
|
|
36027
|
+
if (compression !== -1 && input.indexOf('::', compression + 1) !== -1) return undefined
|
|
36028
|
+
|
|
36029
|
+
const left = compression === -1 ? input.split(':') : input.slice(0, compression).split(':');
|
|
36030
|
+
const right = compression === -1 ? [] : input.slice(compression + 2).split(':');
|
|
36031
|
+
if (compression !== -1) {
|
|
36032
|
+
if (left.length === 1 && left[0] === '') left.length = 0;
|
|
36033
|
+
if (right.length === 1 && right[0] === '') right.length = 0;
|
|
36034
|
+
}
|
|
36035
|
+
|
|
36036
|
+
const parts = left.concat(right);
|
|
36037
|
+
let hextetCount = 0;
|
|
36038
|
+
for (let i = 0; i < parts.length; i++) {
|
|
36039
|
+
const part = parts[i];
|
|
36040
|
+
if (part === '') return undefined
|
|
36041
|
+
|
|
36042
|
+
if (part.indexOf('.') !== -1) {
|
|
36043
|
+
if (i !== parts.length - 1 || (compression !== -1 && right.length === 0) || !isIPv4(part)) return undefined
|
|
36044
|
+
hextetCount += 2;
|
|
35222
36045
|
continue
|
|
35223
36046
|
}
|
|
36047
|
+
|
|
36048
|
+
if (!isHextet(part)) return undefined
|
|
36049
|
+
parts[i] = parseInt(part, 16).toString(16);
|
|
36050
|
+
hextetCount++;
|
|
35224
36051
|
}
|
|
35225
|
-
|
|
35226
|
-
|
|
35227
|
-
|
|
35228
|
-
|
|
35229
|
-
address.push(buffer.join(''));
|
|
35230
|
-
} else {
|
|
35231
|
-
address.push(stringArrayToHexStripped(buffer));
|
|
35232
|
-
}
|
|
36052
|
+
|
|
36053
|
+
if (compression === -1) {
|
|
36054
|
+
if (hextetCount !== 8) return undefined
|
|
36055
|
+
return compressIPv6ZeroRun(parts)
|
|
35233
36056
|
}
|
|
35234
|
-
|
|
35235
|
-
|
|
36057
|
+
if (hextetCount >= 8) return undefined
|
|
36058
|
+
|
|
36059
|
+
// expand "::" then re-compress the longest run for a canonical result
|
|
36060
|
+
const expanded = parts.slice(0, left.length);
|
|
36061
|
+
for (let i = hextetCount; i < 8; i++) expanded.push('0');
|
|
36062
|
+
for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
|
|
36063
|
+
return compressIPv6ZeroRun(expanded)
|
|
35236
36064
|
}
|
|
35237
36065
|
|
|
35238
36066
|
/**
|
|
@@ -35240,26 +36068,49 @@ function requireUtils () {
|
|
|
35240
36068
|
* @property {string} host - The normalized host.
|
|
35241
36069
|
* @property {string} [escapedHost] - The escaped host.
|
|
35242
36070
|
* @property {boolean} isIPV6 - Indicates if the host is an IPv6 address.
|
|
36071
|
+
* @property {boolean} [isIPVFuture] - Indicates if the host is an IPvFuture literal.
|
|
36072
|
+
* @property {boolean} [error] - Indicates if a bracketed IP literal is malformed.
|
|
35243
36073
|
*/
|
|
35244
36074
|
|
|
35245
36075
|
/**
|
|
36076
|
+
* Validates and normalizes a bracketed IP literal. Raw zone separators remain
|
|
36077
|
+
* accepted for backwards compatibility, while encoded separators and zone
|
|
36078
|
+
* contents follow RFC 6874.
|
|
36079
|
+
*
|
|
35246
36080
|
* @param {string} host
|
|
35247
36081
|
* @returns {NormalizeIPv6Result}
|
|
35248
36082
|
*/
|
|
35249
36083
|
function normalizeIPv6 (host) {
|
|
35250
|
-
|
|
35251
|
-
const
|
|
36084
|
+
const bracketed = host[0] === '[' && host[host.length - 1] === ']';
|
|
36085
|
+
const hasBracket = host[0] === '[' || host[host.length - 1] === ']';
|
|
36086
|
+
if (hasBracket && !bracketed) return { host, isIPV6: false, error: true }
|
|
35252
36087
|
|
|
35253
|
-
|
|
35254
|
-
|
|
35255
|
-
|
|
35256
|
-
|
|
35257
|
-
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
return { host
|
|
35261
|
-
}
|
|
35262
|
-
|
|
36088
|
+
let input = bracketed ? host.slice(1, -1) : host;
|
|
36089
|
+
if (bracketed && isIPvFuture(input)) {
|
|
36090
|
+
input = input.toLowerCase();
|
|
36091
|
+
return { host: `[${input}]`, escapedHost: input, isIPV6: false, isIPVFuture: true }
|
|
36092
|
+
}
|
|
36093
|
+
|
|
36094
|
+
if (findToken(input, ':') < 2) {
|
|
36095
|
+
return { host, isIPV6: false, error: bracketed }
|
|
36096
|
+
}
|
|
36097
|
+
|
|
36098
|
+
let zoneIdentifier = '';
|
|
36099
|
+
const zoneSeparator = input.indexOf('%');
|
|
36100
|
+
if (zoneSeparator !== -1) {
|
|
36101
|
+
const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === '%25' ? 3 : 1;
|
|
36102
|
+
zoneIdentifier = input.slice(zoneSeparator + separatorLength);
|
|
36103
|
+
if (!isZoneIdentifier(zoneIdentifier)) return { host, isIPV6: false, error: true }
|
|
36104
|
+
input = input.slice(0, zoneSeparator);
|
|
36105
|
+
}
|
|
36106
|
+
|
|
36107
|
+
const address = normalizeIPv6Address(input);
|
|
36108
|
+
if (address === undefined) return { host, isIPV6: false, error: true }
|
|
36109
|
+
|
|
36110
|
+
return {
|
|
36111
|
+
host: address + (zoneIdentifier ? '%' + zoneIdentifier : ''),
|
|
36112
|
+
escapedHost: address + (zoneIdentifier ? '%25' + zoneIdentifier : ''),
|
|
36113
|
+
isIPV6: true
|
|
35263
36114
|
}
|
|
35264
36115
|
}
|
|
35265
36116
|
|
|
@@ -35350,97 +36201,298 @@ function requireUtils () {
|
|
|
35350
36201
|
}
|
|
35351
36202
|
}
|
|
35352
36203
|
|
|
35353
|
-
// Rule 2E: Move normal path segment to output
|
|
35354
|
-
if ((nextSlash = input.indexOf('/', 1)) === -1) {
|
|
35355
|
-
output.push(input);
|
|
35356
|
-
break
|
|
36204
|
+
// Rule 2E: Move normal path segment to output
|
|
36205
|
+
if ((nextSlash = input.indexOf('/', 1)) === -1) {
|
|
36206
|
+
output.push(input);
|
|
36207
|
+
break
|
|
36208
|
+
} else {
|
|
36209
|
+
output.push(input.slice(0, nextSlash));
|
|
36210
|
+
input = input.slice(nextSlash);
|
|
36211
|
+
}
|
|
36212
|
+
}
|
|
36213
|
+
|
|
36214
|
+
return output.join('')
|
|
36215
|
+
}
|
|
36216
|
+
|
|
36217
|
+
/**
|
|
36218
|
+
* Re-escape RFC 3986 gen-delims that must not appear literally in the host.
|
|
36219
|
+
* After the URI regex parses, these characters cannot be literal in the host
|
|
36220
|
+
* field, so any that appear after decoding came from percent-encoding and
|
|
36221
|
+
* must be restored to prevent authority structure changes.
|
|
36222
|
+
*
|
|
36223
|
+
* @param {string} host
|
|
36224
|
+
* @param {boolean} isIP - true for IPv4/IPv6 hosts (skip colon re-escaping)
|
|
36225
|
+
* @returns {string}
|
|
36226
|
+
*/
|
|
36227
|
+
const HOST_DELIMS = { '@': '%40', '/': '%2F', '?': '%3F', '#': '%23', ':': '%3A' };
|
|
36228
|
+
const HOST_DELIM_RE = /[@/?#:]/g;
|
|
36229
|
+
const HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
36230
|
+
|
|
36231
|
+
function reescapeHostDelimiters (host, isIP) {
|
|
36232
|
+
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
|
|
36233
|
+
re.lastIndex = 0;
|
|
36234
|
+
return host.replace(re, (ch) => HOST_DELIMS[ch])
|
|
36235
|
+
}
|
|
36236
|
+
|
|
36237
|
+
/**
|
|
36238
|
+
* Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
|
|
36239
|
+
* Reserved delimiters such as `%2F` stay escaped; `%2E` is unreserved.
|
|
36240
|
+
*
|
|
36241
|
+
* @param {string} input
|
|
36242
|
+
* @param {boolean} [decodeUnreserved=false]
|
|
36243
|
+
* @returns {string}
|
|
36244
|
+
*/
|
|
36245
|
+
function normalizePercentEncoding (input, decodeUnreserved = false) {
|
|
36246
|
+
if (input.indexOf('%') === -1) {
|
|
36247
|
+
return input
|
|
36248
|
+
}
|
|
36249
|
+
|
|
36250
|
+
let output = '';
|
|
36251
|
+
|
|
36252
|
+
for (let i = 0; i < input.length; i++) {
|
|
36253
|
+
if (input[i] === '%' && i + 2 < input.length) {
|
|
36254
|
+
const hex = input.slice(i + 1, i + 3);
|
|
36255
|
+
if (isHexPair(hex)) {
|
|
36256
|
+
const normalizedHex = hex.toUpperCase();
|
|
36257
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
36258
|
+
|
|
36259
|
+
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
36260
|
+
output += decoded;
|
|
36261
|
+
} else {
|
|
36262
|
+
output += '%' + normalizedHex;
|
|
36263
|
+
}
|
|
36264
|
+
|
|
36265
|
+
i += 2;
|
|
36266
|
+
continue
|
|
36267
|
+
}
|
|
36268
|
+
}
|
|
36269
|
+
|
|
36270
|
+
output += input[i];
|
|
36271
|
+
}
|
|
36272
|
+
|
|
36273
|
+
return output
|
|
36274
|
+
}
|
|
36275
|
+
|
|
36276
|
+
/**
|
|
36277
|
+
* Normalizes path data without turning reserved escapes into live path syntax.
|
|
36278
|
+
* Valid escapes are uppercased, raw unsafe characters are escaped, and only
|
|
36279
|
+
* unreserved bytes that are not `.` are decoded.
|
|
36280
|
+
*
|
|
36281
|
+
* @param {string} input
|
|
36282
|
+
* @returns {string}
|
|
36283
|
+
*/
|
|
36284
|
+
function normalizePathEncoding (input) {
|
|
36285
|
+
let output = '';
|
|
36286
|
+
|
|
36287
|
+
for (let i = 0; i < input.length; i++) {
|
|
36288
|
+
const ch = input[i];
|
|
36289
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
36290
|
+
const hex = input.slice(i + 1, i + 3);
|
|
36291
|
+
if (isHexPair(hex)) {
|
|
36292
|
+
const normalizedHex = hex.toUpperCase();
|
|
36293
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
36294
|
+
|
|
36295
|
+
if (decoded !== '.' && isUnreserved(decoded)) {
|
|
36296
|
+
output += decoded;
|
|
36297
|
+
} else {
|
|
36298
|
+
output += '%' + normalizedHex;
|
|
36299
|
+
}
|
|
36300
|
+
|
|
36301
|
+
i += 2;
|
|
36302
|
+
continue
|
|
36303
|
+
}
|
|
36304
|
+
}
|
|
36305
|
+
|
|
36306
|
+
if (isPathCharacter(ch)) {
|
|
36307
|
+
output += ch;
|
|
35357
36308
|
} else {
|
|
35358
|
-
|
|
35359
|
-
|
|
36309
|
+
const code = input.charCodeAt(i);
|
|
36310
|
+
if (code < 0x80) {
|
|
36311
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
36312
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
36313
|
+
output += percentEncodeNonAscii(code);
|
|
36314
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
36315
|
+
const low = input.charCodeAt(i + 1);
|
|
36316
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
36317
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
36318
|
+
i++;
|
|
36319
|
+
} else {
|
|
36320
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36321
|
+
}
|
|
36322
|
+
} else {
|
|
36323
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36324
|
+
}
|
|
35360
36325
|
}
|
|
35361
36326
|
}
|
|
35362
36327
|
|
|
35363
|
-
return output
|
|
36328
|
+
return output
|
|
35364
36329
|
}
|
|
35365
36330
|
|
|
35366
36331
|
/**
|
|
35367
|
-
*
|
|
35368
|
-
*
|
|
35369
|
-
*
|
|
35370
|
-
* must be
|
|
36332
|
+
* Serializes a path without rewriting reserved data. Raw RFC 3986 path
|
|
36333
|
+
* characters remain literal, valid escapes are preserved and uppercased, and
|
|
36334
|
+
* everything else is UTF-8 percent-encoded. In a path-noscheme, a colon in the
|
|
36335
|
+
* first segment must be escaped so the result cannot be parsed as a scheme.
|
|
35371
36336
|
*
|
|
35372
|
-
* @param {string}
|
|
35373
|
-
* @param {boolean}
|
|
36337
|
+
* @param {string} input
|
|
36338
|
+
* @param {boolean} [pathNoScheme=false]
|
|
35374
36339
|
* @returns {string}
|
|
35375
36340
|
*/
|
|
35376
|
-
|
|
35377
|
-
|
|
35378
|
-
|
|
36341
|
+
function serializePathEncoding (input, pathNoScheme = false) {
|
|
36342
|
+
let output = '';
|
|
36343
|
+
let firstSegment = pathNoScheme && input[0] !== '/';
|
|
35379
36344
|
|
|
35380
|
-
|
|
35381
|
-
|
|
35382
|
-
|
|
35383
|
-
|
|
36345
|
+
for (let i = 0; i < input.length; i++) {
|
|
36346
|
+
const ch = input[i];
|
|
36347
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
36348
|
+
const hex = input.slice(i + 1, i + 3);
|
|
36349
|
+
if (isHexPair(hex)) {
|
|
36350
|
+
output += '%' + hex.toUpperCase();
|
|
36351
|
+
i += 2;
|
|
36352
|
+
continue
|
|
36353
|
+
}
|
|
36354
|
+
}
|
|
36355
|
+
|
|
36356
|
+
if (ch === '/') {
|
|
36357
|
+
firstSegment = false;
|
|
36358
|
+
}
|
|
36359
|
+
|
|
36360
|
+
if (isPathCharacter(ch) && (ch !== ':' || !firstSegment)) {
|
|
36361
|
+
output += ch;
|
|
36362
|
+
} else {
|
|
36363
|
+
const code = input.charCodeAt(i);
|
|
36364
|
+
if (code < 0x80) {
|
|
36365
|
+
output += BYTE_HEX[code];
|
|
36366
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
36367
|
+
output += percentEncodeNonAscii(code);
|
|
36368
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
36369
|
+
const low = input.charCodeAt(i + 1);
|
|
36370
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
36371
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
36372
|
+
i++;
|
|
36373
|
+
} else {
|
|
36374
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36375
|
+
}
|
|
36376
|
+
} else {
|
|
36377
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36378
|
+
}
|
|
36379
|
+
}
|
|
36380
|
+
}
|
|
36381
|
+
|
|
36382
|
+
return output
|
|
35384
36383
|
}
|
|
35385
36384
|
|
|
35386
36385
|
/**
|
|
35387
|
-
*
|
|
35388
|
-
*
|
|
36386
|
+
* Percent-encodes a URI component using its RFC 3986 literal character set.
|
|
36387
|
+
* Existing valid escapes are preserved and normalized to uppercase hex.
|
|
35389
36388
|
*
|
|
35390
36389
|
* @param {string} input
|
|
35391
|
-
* @param {boolean}
|
|
36390
|
+
* @param {(value: string) => boolean} isAllowed
|
|
35392
36391
|
* @returns {string}
|
|
35393
36392
|
*/
|
|
35394
|
-
function
|
|
35395
|
-
if (input.indexOf('%') === -1) {
|
|
35396
|
-
return input
|
|
35397
|
-
}
|
|
35398
|
-
|
|
36393
|
+
function encodeComponent (input, isAllowed) {
|
|
35399
36394
|
let output = '';
|
|
35400
36395
|
|
|
35401
36396
|
for (let i = 0; i < input.length; i++) {
|
|
35402
|
-
|
|
36397
|
+
const ch = input[i];
|
|
36398
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
35403
36399
|
const hex = input.slice(i + 1, i + 3);
|
|
35404
36400
|
if (isHexPair(hex)) {
|
|
35405
|
-
|
|
35406
|
-
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
35407
|
-
|
|
35408
|
-
if (decodeUnreserved && isUnreserved(decoded)) {
|
|
35409
|
-
output += decoded;
|
|
35410
|
-
} else {
|
|
35411
|
-
output += '%' + normalizedHex;
|
|
35412
|
-
}
|
|
35413
|
-
|
|
36401
|
+
output += '%' + hex.toUpperCase();
|
|
35414
36402
|
i += 2;
|
|
35415
36403
|
continue
|
|
35416
36404
|
}
|
|
35417
36405
|
}
|
|
35418
36406
|
|
|
35419
|
-
|
|
36407
|
+
if (isAllowed(ch)) {
|
|
36408
|
+
output += ch;
|
|
36409
|
+
} else {
|
|
36410
|
+
const code = input.charCodeAt(i);
|
|
36411
|
+
if (code < 0x80) {
|
|
36412
|
+
output += BYTE_HEX[code];
|
|
36413
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
36414
|
+
output += percentEncodeNonAscii(code);
|
|
36415
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
36416
|
+
const low = input.charCodeAt(i + 1);
|
|
36417
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
36418
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
36419
|
+
i++;
|
|
36420
|
+
} else {
|
|
36421
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36422
|
+
}
|
|
36423
|
+
} else {
|
|
36424
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36425
|
+
}
|
|
36426
|
+
}
|
|
35420
36427
|
}
|
|
35421
36428
|
|
|
35422
36429
|
return output
|
|
35423
36430
|
}
|
|
35424
36431
|
|
|
35425
36432
|
/**
|
|
35426
|
-
*
|
|
35427
|
-
*
|
|
35428
|
-
* unreserved bytes that are not `.` are decoded.
|
|
36433
|
+
* Encodes userinfo while preserving its RFC 3986 §3.2.1 literal characters.
|
|
36434
|
+
* In particular, authority delimiters such as `@`, `/`, `?`, and `#` are data.
|
|
35429
36435
|
*
|
|
35430
36436
|
* @param {string} input
|
|
35431
36437
|
* @returns {string}
|
|
35432
36438
|
*/
|
|
35433
|
-
function
|
|
36439
|
+
function encodeUserinfo (input) {
|
|
36440
|
+
return encodeComponent(input, isUserinfoCharacter)
|
|
36441
|
+
}
|
|
36442
|
+
|
|
36443
|
+
/**
|
|
36444
|
+
* Encodes query data using the RFC 3986 §3.4 grammar. A literal `#` must be
|
|
36445
|
+
* escaped because it would otherwise begin the fragment component.
|
|
36446
|
+
*
|
|
36447
|
+
* @param {string} input
|
|
36448
|
+
* @returns {string}
|
|
36449
|
+
*/
|
|
36450
|
+
function encodeQuery (input) {
|
|
36451
|
+
return encodeComponent(input, isQueryFragmentCharacter)
|
|
36452
|
+
}
|
|
36453
|
+
|
|
36454
|
+
/**
|
|
36455
|
+
* Encodes fragment data using the RFC 3986 §3.5 grammar.
|
|
36456
|
+
*
|
|
36457
|
+
* @param {string} input
|
|
36458
|
+
* @returns {string}
|
|
36459
|
+
*/
|
|
36460
|
+
function encodeFragment (input) {
|
|
36461
|
+
return encodeComponent(input, isQueryFragmentCharacter)
|
|
36462
|
+
}
|
|
36463
|
+
|
|
36464
|
+
function isEscapeSafe (cp) {
|
|
36465
|
+
return (
|
|
36466
|
+
(cp >= 0x30 && cp <= 0x39) ||
|
|
36467
|
+
(cp >= 0x41 && cp <= 0x5A) ||
|
|
36468
|
+
(cp >= 0x61 && cp <= 0x7A) ||
|
|
36469
|
+
cp === 0x2A || cp === 0x2B || cp === 0x2D || cp === 0x2E ||
|
|
36470
|
+
cp === 0x2F || cp === 0x40 || cp === 0x5F
|
|
36471
|
+
)
|
|
36472
|
+
}
|
|
36473
|
+
|
|
36474
|
+
/**
|
|
36475
|
+
* Normalizes the percent-encoding of a query or fragment component.
|
|
36476
|
+
*
|
|
36477
|
+
* Like `normalizePathEncoding`, but uses the query/fragment character set
|
|
36478
|
+
* (which additionally allows `?`) and decodes `.` since it has no dot-segment
|
|
36479
|
+
* meaning outside of a path.
|
|
36480
|
+
*
|
|
36481
|
+
* @param {string} input
|
|
36482
|
+
* @returns {string}
|
|
36483
|
+
*/
|
|
36484
|
+
function normalizeQueryFragmentEncoding (input) {
|
|
35434
36485
|
let output = '';
|
|
35435
36486
|
|
|
35436
36487
|
for (let i = 0; i < input.length; i++) {
|
|
35437
|
-
|
|
36488
|
+
const ch = input[i];
|
|
36489
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
35438
36490
|
const hex = input.slice(i + 1, i + 3);
|
|
35439
36491
|
if (isHexPair(hex)) {
|
|
35440
36492
|
const normalizedHex = hex.toUpperCase();
|
|
35441
36493
|
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
35442
36494
|
|
|
35443
|
-
if (
|
|
36495
|
+
if (isUnreserved(decoded)) {
|
|
35444
36496
|
output += decoded;
|
|
35445
36497
|
} else {
|
|
35446
36498
|
output += '%' + normalizedHex;
|
|
@@ -35451,10 +36503,25 @@ function requireUtils () {
|
|
|
35451
36503
|
}
|
|
35452
36504
|
}
|
|
35453
36505
|
|
|
35454
|
-
if (
|
|
35455
|
-
output +=
|
|
36506
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
36507
|
+
output += ch;
|
|
35456
36508
|
} else {
|
|
35457
|
-
|
|
36509
|
+
const code = input.charCodeAt(i);
|
|
36510
|
+
if (code < 0x80) {
|
|
36511
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
36512
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
36513
|
+
output += percentEncodeNonAscii(code);
|
|
36514
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
36515
|
+
const low = input.charCodeAt(i + 1);
|
|
36516
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
36517
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00));
|
|
36518
|
+
i++;
|
|
36519
|
+
} else {
|
|
36520
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36521
|
+
}
|
|
36522
|
+
} else {
|
|
36523
|
+
output += percentEncodeNonAscii(0xFFFD);
|
|
36524
|
+
}
|
|
35458
36525
|
}
|
|
35459
36526
|
}
|
|
35460
36527
|
|
|
@@ -35494,15 +36561,21 @@ function requireUtils () {
|
|
|
35494
36561
|
const uriTokens = [];
|
|
35495
36562
|
|
|
35496
36563
|
if (component.userinfo !== undefined) {
|
|
35497
|
-
uriTokens.push(component.userinfo);
|
|
36564
|
+
uriTokens.push(encodeUserinfo(component.userinfo));
|
|
35498
36565
|
uriTokens.push('@');
|
|
35499
36566
|
}
|
|
35500
36567
|
|
|
35501
36568
|
if (component.host !== undefined) {
|
|
35502
|
-
let host =
|
|
36569
|
+
let host = component.host;
|
|
35503
36570
|
if (!isIPv4(host)) {
|
|
35504
|
-
|
|
35505
|
-
if (ipV6res.isIPV6
|
|
36571
|
+
let ipV6res = normalizeIPv6(host);
|
|
36572
|
+
if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
|
|
36573
|
+
// Decode only unreserved bytes, once. In particular, keep %25 encoded
|
|
36574
|
+
// so it cannot introduce a second escape during recomposition.
|
|
36575
|
+
host = normalizePercentEncoding(host, true);
|
|
36576
|
+
ipV6res = normalizeIPv6(host);
|
|
36577
|
+
}
|
|
36578
|
+
if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) {
|
|
35506
36579
|
host = `[${ipV6res.escapedHost}]`;
|
|
35507
36580
|
} else {
|
|
35508
36581
|
host = reescapeHostDelimiters(host, false);
|
|
@@ -35512,8 +36585,12 @@ function requireUtils () {
|
|
|
35512
36585
|
}
|
|
35513
36586
|
|
|
35514
36587
|
if (typeof component.port === 'number' || typeof component.port === 'string') {
|
|
36588
|
+
const port = String(component.port);
|
|
36589
|
+
if (!isPort(port)) {
|
|
36590
|
+
throw new TypeError('URI port is malformed.')
|
|
36591
|
+
}
|
|
35515
36592
|
uriTokens.push(':');
|
|
35516
|
-
uriTokens.push(
|
|
36593
|
+
uriTokens.push(port);
|
|
35517
36594
|
}
|
|
35518
36595
|
|
|
35519
36596
|
return uriTokens.length ? uriTokens.join('') : undefined
|
|
@@ -35524,6 +36601,11 @@ function requireUtils () {
|
|
|
35524
36601
|
reescapeHostDelimiters,
|
|
35525
36602
|
normalizePercentEncoding,
|
|
35526
36603
|
normalizePathEncoding,
|
|
36604
|
+
serializePathEncoding,
|
|
36605
|
+
normalizeQueryFragmentEncoding,
|
|
36606
|
+
encodeUserinfo,
|
|
36607
|
+
encodeQuery,
|
|
36608
|
+
encodeFragment,
|
|
35527
36609
|
escapePreservingEscapes,
|
|
35528
36610
|
removeDotSegments,
|
|
35529
36611
|
isIPv4,
|
|
@@ -35542,7 +36624,7 @@ function requireSchemes () {
|
|
|
35542
36624
|
hasRequiredSchemes = 1;
|
|
35543
36625
|
|
|
35544
36626
|
const { isUUID } = requireUtils();
|
|
35545
|
-
const URN_REG =
|
|
36627
|
+
const URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
|
|
35546
36628
|
|
|
35547
36629
|
const supportedSchemeNames = /** @type {const} */ (['http', 'https', 'ws',
|
|
35548
36630
|
'wss', 'urn', 'urn:uuid']);
|
|
@@ -35654,9 +36736,14 @@ function requireSchemes () {
|
|
|
35654
36736
|
|
|
35655
36737
|
// reconstruct path from resource name
|
|
35656
36738
|
if (wsComponent.resourceName) {
|
|
35657
|
-
const
|
|
36739
|
+
const queryIndex = wsComponent.resourceName.indexOf('?');
|
|
36740
|
+
const path = queryIndex === -1
|
|
36741
|
+
? wsComponent.resourceName
|
|
36742
|
+
: wsComponent.resourceName.slice(0, queryIndex);
|
|
35658
36743
|
wsComponent.path = (path && path !== '/' ? path : undefined);
|
|
35659
|
-
wsComponent.query =
|
|
36744
|
+
wsComponent.query = queryIndex === -1
|
|
36745
|
+
? undefined
|
|
36746
|
+
: wsComponent.resourceName.slice(queryIndex + 1);
|
|
35660
36747
|
wsComponent.resourceName = undefined;
|
|
35661
36748
|
}
|
|
35662
36749
|
|
|
@@ -35673,7 +36760,7 @@ function requireSchemes () {
|
|
|
35673
36760
|
return urnComponent
|
|
35674
36761
|
}
|
|
35675
36762
|
const matches = urnComponent.path.match(URN_REG);
|
|
35676
|
-
if (matches) {
|
|
36763
|
+
if (matches && matches[0] === urnComponent.path) {
|
|
35677
36764
|
const scheme = options.scheme || urnComponent.scheme || 'urn';
|
|
35678
36765
|
urnComponent.nid = matches[1].toLowerCase();
|
|
35679
36766
|
urnComponent.nss = matches[2];
|
|
@@ -35815,9 +36902,24 @@ function requireFastUri () {
|
|
|
35815
36902
|
if (hasRequiredFastUri) return fastUri.exports;
|
|
35816
36903
|
hasRequiredFastUri = 1;
|
|
35817
36904
|
|
|
35818
|
-
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding,
|
|
36905
|
+
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = requireUtils();
|
|
35819
36906
|
const { SCHEMES, getSchemeHandler } = requireSchemes();
|
|
35820
36907
|
|
|
36908
|
+
const VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
36909
|
+
const MALFORMED_SCHEME_ERROR = 'URI scheme is malformed.';
|
|
36910
|
+
|
|
36911
|
+
/**
|
|
36912
|
+
* @param {string} scheme
|
|
36913
|
+
* @returns {string}
|
|
36914
|
+
*/
|
|
36915
|
+
function decodeValidScheme (scheme) {
|
|
36916
|
+
const decodedScheme = unescape(String(scheme));
|
|
36917
|
+
if (!VALID_SCHEME.test(decodedScheme)) {
|
|
36918
|
+
throw new TypeError(MALFORMED_SCHEME_ERROR)
|
|
36919
|
+
}
|
|
36920
|
+
return decodedScheme
|
|
36921
|
+
}
|
|
36922
|
+
|
|
35821
36923
|
/**
|
|
35822
36924
|
* @template {import('./types/index').URIComponent|string} T
|
|
35823
36925
|
* @param {T} uri
|
|
@@ -35841,12 +36943,50 @@ function requireFastUri () {
|
|
|
35841
36943
|
*/
|
|
35842
36944
|
function resolve (baseURI, relativeURI, options) {
|
|
35843
36945
|
const schemelessOptions = options ? Object.assign({ scheme: 'null' }, options) : { scheme: 'null' };
|
|
35844
|
-
const {
|
|
35845
|
-
|
|
35846
|
-
|
|
36946
|
+
const {
|
|
36947
|
+
parsed: baseParsed,
|
|
36948
|
+
malformedAuthorityOrPort: baseMalformed,
|
|
36949
|
+
malformedPercentEncoding: baseMalformedPercentEncoding,
|
|
36950
|
+
malformedSchemeSpecific: baseMalformedSchemeSpecific,
|
|
36951
|
+
malformedHost: baseMalformedHost,
|
|
36952
|
+
malformedScheme: baseMalformedScheme
|
|
36953
|
+
} = parseWithStatus(baseURI, schemelessOptions);
|
|
36954
|
+
const {
|
|
36955
|
+
parsed: relativeParsed,
|
|
36956
|
+
malformedAuthorityOrPort: relativeMalformed,
|
|
36957
|
+
malformedPercentEncoding: relativeMalformedPercentEncoding,
|
|
36958
|
+
malformedSchemeSpecific: relativeMalformedSchemeSpecific,
|
|
36959
|
+
malformedHost: relativeMalformedHost,
|
|
36960
|
+
malformedScheme: relativeMalformedScheme
|
|
36961
|
+
} = parseWithStatus(relativeURI, schemelessOptions);
|
|
36962
|
+
if (
|
|
36963
|
+
baseMalformed ||
|
|
36964
|
+
relativeMalformed ||
|
|
36965
|
+
baseMalformedPercentEncoding ||
|
|
36966
|
+
relativeMalformedPercentEncoding ||
|
|
36967
|
+
baseMalformedSchemeSpecific ||
|
|
36968
|
+
relativeMalformedSchemeSpecific ||
|
|
36969
|
+
baseMalformedHost ||
|
|
36970
|
+
relativeMalformedHost ||
|
|
36971
|
+
baseMalformedScheme ||
|
|
36972
|
+
relativeMalformedScheme
|
|
36973
|
+
) {
|
|
35847
36974
|
throw new Error(baseParsed.error || relativeParsed.error || 'URI is malformed.')
|
|
35848
36975
|
}
|
|
35849
36976
|
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
36977
|
+
const resolvedSchemeHandler = getSchemeHandler((options && options.scheme) || resolved.scheme);
|
|
36978
|
+
const resolvedHost = resolved.host;
|
|
36979
|
+
const resolvedHostIsIP = resolvedHost !== undefined && resolvedHost !== '' &&
|
|
36980
|
+
(isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
36981
|
+
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
36982
|
+
// Percent escapes in an ASCII reg-name are encoded data. The WHATWG hostname
|
|
36983
|
+
// parser can reject them even though fast-uri preserves them safely as RFC
|
|
36984
|
+
// 3986 data. A raw non-ASCII host must still fail closed if conversion fails.
|
|
36985
|
+
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf('%') !== -1 &&
|
|
36986
|
+
!/\P{ASCII}/u.test(resolvedHost);
|
|
36987
|
+
if (resolved.error && !encodedASCIIHost) {
|
|
36988
|
+
throw new Error(resolved.error)
|
|
36989
|
+
}
|
|
35850
36990
|
schemelessOptions.skipEscape = true;
|
|
35851
36991
|
return serialize(resolved, schemelessOptions)
|
|
35852
36992
|
}
|
|
@@ -35929,7 +37069,7 @@ function requireFastUri () {
|
|
|
35929
37069
|
const normalizedA = normalizeComparableURI(uriA, options);
|
|
35930
37070
|
const normalizedB = normalizeComparableURI(uriB, options);
|
|
35931
37071
|
|
|
35932
|
-
return normalizedA !== undefined && normalizedB !== undefined && normalizedA
|
|
37072
|
+
return normalizedA !== undefined && normalizedB !== undefined && normalizedA === normalizedB
|
|
35933
37073
|
}
|
|
35934
37074
|
|
|
35935
37075
|
/**
|
|
@@ -35957,25 +37097,30 @@ function requireFastUri () {
|
|
|
35957
37097
|
const options = Object.assign({}, opts);
|
|
35958
37098
|
const uriTokens = [];
|
|
35959
37099
|
|
|
37100
|
+
if (component.scheme) {
|
|
37101
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
37102
|
+
}
|
|
37103
|
+
|
|
35960
37104
|
// find scheme handler
|
|
35961
37105
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
35962
37106
|
|
|
35963
37107
|
// perform scheme specific serialization
|
|
35964
37108
|
if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
|
|
35965
37109
|
|
|
37110
|
+
const hasAuthority = component.userinfo !== undefined || component.host !== undefined || component.port !== undefined;
|
|
37111
|
+
const pathNoScheme = !options.skipEscape && component.scheme === undefined && !hasAuthority;
|
|
37112
|
+
|
|
35966
37113
|
if (component.path !== undefined) {
|
|
35967
37114
|
if (!options.skipEscape) {
|
|
35968
|
-
component.path =
|
|
35969
|
-
|
|
35970
|
-
if (component.scheme !== undefined) {
|
|
35971
|
-
component.path = component.path.split('%3A').join(':');
|
|
35972
|
-
}
|
|
37115
|
+
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
35973
37116
|
} else {
|
|
35974
37117
|
component.path = normalizePercentEncoding(component.path);
|
|
35975
37118
|
}
|
|
35976
37119
|
}
|
|
35977
37120
|
|
|
35978
37121
|
if (options.reference !== 'suffix' && component.scheme) {
|
|
37122
|
+
// Scheme handlers may replace the scheme during serialization.
|
|
37123
|
+
component.scheme = decodeValidScheme(component.scheme);
|
|
35979
37124
|
uriTokens.push(component.scheme, ':');
|
|
35980
37125
|
}
|
|
35981
37126
|
|
|
@@ -35998,6 +37143,13 @@ function requireFastUri () {
|
|
|
35998
37143
|
s = removeDotSegments(s);
|
|
35999
37144
|
}
|
|
36000
37145
|
|
|
37146
|
+
// Dot-segment removal can expose a colon that was not originally in the
|
|
37147
|
+
// first segment (for example, "./a:b"). Reapply path-noscheme encoding so
|
|
37148
|
+
// the serialized relative reference cannot be reparsed as a URI scheme.
|
|
37149
|
+
if (pathNoScheme) {
|
|
37150
|
+
s = serializePathEncoding(s, true);
|
|
37151
|
+
}
|
|
37152
|
+
|
|
36001
37153
|
if (
|
|
36002
37154
|
authority === undefined &&
|
|
36003
37155
|
s[0] === '/' &&
|
|
@@ -36011,11 +37163,11 @@ function requireFastUri () {
|
|
|
36011
37163
|
}
|
|
36012
37164
|
|
|
36013
37165
|
if (component.query !== undefined) {
|
|
36014
|
-
uriTokens.push('?', component.query);
|
|
37166
|
+
uriTokens.push('?', encodeQuery(component.query));
|
|
36015
37167
|
}
|
|
36016
37168
|
|
|
36017
37169
|
if (component.fragment !== undefined) {
|
|
36018
|
-
uriTokens.push('#', component.fragment);
|
|
37170
|
+
uriTokens.push('#', encodeFragment(component.fragment));
|
|
36019
37171
|
}
|
|
36020
37172
|
return uriTokens.join('')
|
|
36021
37173
|
}
|
|
@@ -36052,10 +37204,86 @@ function requireFastUri () {
|
|
|
36052
37204
|
return undefined
|
|
36053
37205
|
}
|
|
36054
37206
|
|
|
37207
|
+
/**
|
|
37208
|
+
* Checks percent syntax without decoding the represented octets. RFC 3986
|
|
37209
|
+
* percent-encoding is byte-oriented, so sequences such as `%FF` are valid even
|
|
37210
|
+
* though they are not independently valid UTF-8.
|
|
37211
|
+
*
|
|
37212
|
+
* @param {string|undefined} component
|
|
37213
|
+
* @returns {boolean}
|
|
37214
|
+
*/
|
|
37215
|
+
function hasMalformedPercentEncoding (component) {
|
|
37216
|
+
if (component === undefined) return false
|
|
37217
|
+
|
|
37218
|
+
let percent = component.indexOf('%');
|
|
37219
|
+
while (percent !== -1) {
|
|
37220
|
+
if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
|
|
37221
|
+
return true
|
|
37222
|
+
}
|
|
37223
|
+
percent = component.indexOf('%', percent + 3);
|
|
37224
|
+
}
|
|
37225
|
+
|
|
37226
|
+
return false
|
|
37227
|
+
}
|
|
37228
|
+
|
|
37229
|
+
/**
|
|
37230
|
+
* Whether the host is a bracketed IP literal (RFC 3986 `IP-literal`).
|
|
37231
|
+
* An unterminated `[` is not a literal, so it must still be validated as a
|
|
37232
|
+
* reg-name instead of being waved through as an IP.
|
|
37233
|
+
*
|
|
37234
|
+
* @param {string} host
|
|
37235
|
+
* @returns {boolean}
|
|
37236
|
+
*/
|
|
37237
|
+
function isIPLiteral (host) {
|
|
37238
|
+
return host[0] === '[' && host[host.length - 1] === ']'
|
|
37239
|
+
}
|
|
37240
|
+
|
|
37241
|
+
/**
|
|
37242
|
+
* @param {RegExpMatchArray} matches
|
|
37243
|
+
* @returns {boolean}
|
|
37244
|
+
*/
|
|
37245
|
+
function hasMalformedComponentPercentEncoding (matches) {
|
|
37246
|
+
// Bracketed IP literals use a raw "%" as the zone separator for historical
|
|
37247
|
+
// compatibility. Their parsing is intentionally left to normalizeIPv6.
|
|
37248
|
+
const host = matches[4];
|
|
37249
|
+
return hasMalformedPercentEncoding(matches[3]) ||
|
|
37250
|
+
(host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host)) ||
|
|
37251
|
+
hasMalformedPercentEncoding(matches[6]) ||
|
|
37252
|
+
hasMalformedPercentEncoding(matches[7]) ||
|
|
37253
|
+
hasMalformedPercentEncoding(matches[8])
|
|
37254
|
+
}
|
|
37255
|
+
|
|
37256
|
+
/**
|
|
37257
|
+
* @param {import('./types/index').URIComponent} parsed
|
|
37258
|
+
* @param {import('./types/index').Options} options
|
|
37259
|
+
* @param {{ domainHost?: boolean, unicodeSupport?: boolean }|undefined} schemeHandler
|
|
37260
|
+
* @param {boolean} isIP
|
|
37261
|
+
* @returns {boolean} whether host conversion failed
|
|
37262
|
+
*/
|
|
37263
|
+
function canonicalizeHost (parsed, options, schemeHandler, isIP) {
|
|
37264
|
+
if (
|
|
37265
|
+
!options.unicodeSupport &&
|
|
37266
|
+
(!schemeHandler || !schemeHandler.unicodeSupport) &&
|
|
37267
|
+
parsed.host &&
|
|
37268
|
+
!isIPLiteral(parsed.host) &&
|
|
37269
|
+
(options.domainHost || (schemeHandler && schemeHandler.domainHost)) &&
|
|
37270
|
+
isIP === false &&
|
|
37271
|
+
nonSimpleDomain(parsed.host)
|
|
37272
|
+
) {
|
|
37273
|
+
try {
|
|
37274
|
+
parsed.host = new URL('http://' + parsed.host).hostname;
|
|
37275
|
+
} catch (e) {
|
|
37276
|
+
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
37277
|
+
return true
|
|
37278
|
+
}
|
|
37279
|
+
}
|
|
37280
|
+
return false
|
|
37281
|
+
}
|
|
37282
|
+
|
|
36055
37283
|
/**
|
|
36056
37284
|
* @param {string} uri
|
|
36057
37285
|
* @param {import('./types/index').Options} [opts]
|
|
36058
|
-
* @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean }}
|
|
37286
|
+
* @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
|
|
36059
37287
|
*/
|
|
36060
37288
|
function parseWithStatus (uri, opts) {
|
|
36061
37289
|
const options = Object.assign({}, opts);
|
|
@@ -36071,6 +37299,11 @@ function requireFastUri () {
|
|
|
36071
37299
|
};
|
|
36072
37300
|
|
|
36073
37301
|
let malformedAuthorityOrPort = false;
|
|
37302
|
+
let malformedPercentEncoding = false;
|
|
37303
|
+
let malformedSchemeSpecific = false;
|
|
37304
|
+
let malformedHost = false;
|
|
37305
|
+
let malformedIPLiteral = false;
|
|
37306
|
+
let malformedScheme = false;
|
|
36074
37307
|
|
|
36075
37308
|
let isIP = false;
|
|
36076
37309
|
if (options.reference === 'suffix') {
|
|
@@ -36128,6 +37361,21 @@ function requireFastUri () {
|
|
|
36128
37361
|
parsed.query = matches[7];
|
|
36129
37362
|
parsed.fragment = matches[8];
|
|
36130
37363
|
|
|
37364
|
+
if (parsed.scheme !== undefined) {
|
|
37365
|
+
const decodedScheme = unescape(parsed.scheme);
|
|
37366
|
+
if (VALID_SCHEME.test(decodedScheme)) {
|
|
37367
|
+
parsed.scheme = decodedScheme.toLowerCase();
|
|
37368
|
+
} else {
|
|
37369
|
+
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
37370
|
+
malformedScheme = true;
|
|
37371
|
+
}
|
|
37372
|
+
}
|
|
37373
|
+
|
|
37374
|
+
malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
|
|
37375
|
+
if (malformedPercentEncoding) {
|
|
37376
|
+
parsed.error = parsed.error || 'URI contains malformed percent-encoding.';
|
|
37377
|
+
}
|
|
37378
|
+
|
|
36131
37379
|
// fix port number
|
|
36132
37380
|
if (isNaN(parsed.port)) {
|
|
36133
37381
|
parsed.port = matches[5];
|
|
@@ -36142,9 +37390,17 @@ function requireFastUri () {
|
|
|
36142
37390
|
if (parsed.host) {
|
|
36143
37391
|
const ipv4result = isIPv4(parsed.host);
|
|
36144
37392
|
if (ipv4result === false) {
|
|
37393
|
+
const bracketedIPLiteral = isIPLiteral(parsed.host);
|
|
37394
|
+
const hasIPLiteralBracket = parsed.host.indexOf('[') !== -1 || parsed.host.indexOf(']') !== -1;
|
|
36145
37395
|
const ipv6result = normalizeIPv6(parsed.host);
|
|
36146
|
-
|
|
36147
|
-
|
|
37396
|
+
isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
|
|
37397
|
+
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
37398
|
+
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
37399
|
+
|
|
37400
|
+
if (malformedIPLiteral) {
|
|
37401
|
+
parsed.error = parsed.error || 'URI host is malformed.';
|
|
37402
|
+
malformedAuthorityOrPort = true;
|
|
37403
|
+
}
|
|
36148
37404
|
} else {
|
|
36149
37405
|
isIP = true;
|
|
36150
37406
|
}
|
|
@@ -36167,49 +37423,40 @@ function requireFastUri () {
|
|
|
36167
37423
|
// find scheme handler
|
|
36168
37424
|
const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
|
|
36169
37425
|
|
|
36170
|
-
//
|
|
36171
|
-
if (!
|
|
36172
|
-
|
|
36173
|
-
if (parsed.host && (options.domainHost || (schemeHandler && schemeHandler.domainHost)) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
36174
|
-
// convert Unicode IDN -> ASCII IDN
|
|
36175
|
-
try {
|
|
36176
|
-
parsed.host = new URL('http://' + parsed.host).hostname;
|
|
36177
|
-
} catch (e) {
|
|
36178
|
-
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
36179
|
-
}
|
|
36180
|
-
}
|
|
36181
|
-
// convert IRI -> URI
|
|
37426
|
+
// convert Unicode IDN -> ASCII IDN when the effective scheme uses domain hosts
|
|
37427
|
+
if (!malformedIPLiteral) {
|
|
37428
|
+
malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
|
|
36182
37429
|
}
|
|
36183
37430
|
|
|
36184
37431
|
if (!schemeHandler || (schemeHandler && !schemeHandler.skipNormalize)) {
|
|
36185
37432
|
if (uri.indexOf('%') !== -1) {
|
|
36186
|
-
if (parsed.
|
|
36187
|
-
parsed.
|
|
36188
|
-
|
|
36189
|
-
if (parsed.host !== undefined) {
|
|
36190
|
-
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP);
|
|
37433
|
+
if (parsed.host !== undefined && !malformedIPLiteral) {
|
|
37434
|
+
const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
|
|
37435
|
+
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
36191
37436
|
}
|
|
36192
37437
|
}
|
|
36193
37438
|
if (parsed.path) {
|
|
36194
37439
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
36195
37440
|
}
|
|
37441
|
+
if (parsed.query) {
|
|
37442
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
37443
|
+
}
|
|
36196
37444
|
if (parsed.fragment) {
|
|
36197
|
-
|
|
36198
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
36199
|
-
} catch {
|
|
36200
|
-
parsed.error = parsed.error || 'URI malformed';
|
|
36201
|
-
}
|
|
37445
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
36202
37446
|
}
|
|
36203
37447
|
}
|
|
36204
37448
|
|
|
36205
37449
|
// perform scheme specific parsing
|
|
36206
37450
|
if (schemeHandler && schemeHandler.parse) {
|
|
36207
37451
|
schemeHandler.parse(parsed, options);
|
|
37452
|
+
if (schemeHandler === SCHEMES.urn && parsed.nid === undefined) {
|
|
37453
|
+
malformedSchemeSpecific = true;
|
|
37454
|
+
}
|
|
36208
37455
|
}
|
|
36209
37456
|
} else {
|
|
36210
37457
|
parsed.error = parsed.error || 'URI can not be parsed.';
|
|
36211
37458
|
}
|
|
36212
|
-
return { parsed, malformedAuthorityOrPort }
|
|
37459
|
+
return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme }
|
|
36213
37460
|
}
|
|
36214
37461
|
|
|
36215
37462
|
/**
|
|
@@ -36233,13 +37480,17 @@ function requireFastUri () {
|
|
|
36233
37480
|
/**
|
|
36234
37481
|
* @param {string} uri
|
|
36235
37482
|
* @param {import('./types/index').Options} [opts]
|
|
36236
|
-
* @returns {{ normalized: string, malformedAuthorityOrPort: boolean }}
|
|
37483
|
+
* @returns {{ normalized: string, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
|
|
36237
37484
|
*/
|
|
36238
37485
|
function normalizeStringWithStatus (uri, opts) {
|
|
36239
|
-
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts);
|
|
37486
|
+
const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
|
|
36240
37487
|
return {
|
|
36241
|
-
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
|
36242
|
-
malformedAuthorityOrPort
|
|
37488
|
+
normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
|
|
37489
|
+
malformedAuthorityOrPort,
|
|
37490
|
+
malformedPercentEncoding,
|
|
37491
|
+
malformedSchemeSpecific,
|
|
37492
|
+
malformedHost,
|
|
37493
|
+
malformedScheme
|
|
36243
37494
|
}
|
|
36244
37495
|
}
|
|
36245
37496
|
|
|
@@ -36249,14 +37500,18 @@ function requireFastUri () {
|
|
|
36249
37500
|
* @returns {string|undefined}
|
|
36250
37501
|
*/
|
|
36251
37502
|
function normalizeComparableURI (uri, opts) {
|
|
36252
|
-
if (typeof uri
|
|
36253
|
-
|
|
36254
|
-
return malformedAuthorityOrPort ? undefined : normalized
|
|
37503
|
+
if (typeof uri !== 'string' && typeof uri !== 'object') {
|
|
37504
|
+
return undefined
|
|
36255
37505
|
}
|
|
36256
37506
|
|
|
36257
|
-
|
|
36258
|
-
|
|
37507
|
+
let value;
|
|
37508
|
+
try {
|
|
37509
|
+
value = typeof uri === 'string' ? uri : serialize(uri, opts);
|
|
37510
|
+
} catch {
|
|
37511
|
+
return undefined
|
|
36259
37512
|
}
|
|
37513
|
+
const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
|
|
37514
|
+
return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? undefined : normalized
|
|
36260
37515
|
}
|
|
36261
37516
|
|
|
36262
37517
|
const fastUri$1 = {
|