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