bare-url 0.3.6 → 0.3.8
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/index.js +3 -3
- package/lib/errors.js +80 -0
- package/lib/parse.js +287 -7
- package/lib/serialize.js +51 -2
- package/package.json +3 -2
package/index.js
CHANGED
|
@@ -71,9 +71,9 @@ const URL = exports.URL = class URL {
|
|
|
71
71
|
|
|
72
72
|
get host () {
|
|
73
73
|
if (this._url.host === null) return ''
|
|
74
|
-
if (this._url.port === null) return this._url.host
|
|
74
|
+
if (this._url.port === null) return serialize.host(this._url.host)
|
|
75
75
|
|
|
76
|
-
return this._url.host + ':' + this._url.port
|
|
76
|
+
return serialize.host(this._url.host) + ':' + this._url.port.toString(10)
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
set host (value) {
|
|
@@ -87,7 +87,7 @@ const URL = exports.URL = class URL {
|
|
|
87
87
|
get hostname () {
|
|
88
88
|
if (this._url.host === null) return ''
|
|
89
89
|
|
|
90
|
-
return this._url.host
|
|
90
|
+
return serialize.host(this._url.host)
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
set hostname (value) {
|
package/lib/errors.js
CHANGED
|
@@ -44,4 +44,84 @@ module.exports = class URLError extends Error {
|
|
|
44
44
|
static PORT_INVALID (msg = 'Invalid URL') {
|
|
45
45
|
return new URLError(msg, 'PORT_INVALID', URLError.PORT_INVALID)
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
// https://url.spec.whatwg.org/#validation-error-domain-to-ascii
|
|
49
|
+
static DOMAIN_TO_ASCII (msg = 'Invalid URL') {
|
|
50
|
+
return new URLError(msg, 'DOMAIN_TO_ASCII', URLError.DOMAIN_TO_ASCII)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// https://url.spec.whatwg.org/#domain-invalid-code-point
|
|
54
|
+
static DOMAIN_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
55
|
+
return new URLError(msg, 'DOMAIN_INVALID_CODE_POINT', URLError.DOMAIN_INVALID_CODE_POINT)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// https://url.spec.whatwg.org/#host-invalid-code-point
|
|
59
|
+
static HOST_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
60
|
+
return new URLError(msg, 'HOST_INVALID_CODE_POINT', URLError.HOST_INVALID_CODE_POINT)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// https://url.spec.whatwg.org/#ipv4-too-many-parts
|
|
64
|
+
static IPV4_TOO_MANY_PARTS (msg = 'Invalid URL') {
|
|
65
|
+
return new URLError(msg, 'IPV4_TOO_MANY_PARTS', URLError.IPV4_TOO_MANY_PARTS)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// https://url.spec.whatwg.org/#ipv4-non-numeric-part
|
|
69
|
+
static IPV4_NON_NUMERIC_PART (msg = 'Invalid URL') {
|
|
70
|
+
return new URLError(msg, 'IPV4_NON_NUMERIC_PART', URLError.IPV4_NON_NUMERIC_PART)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// https://url.spec.whatwg.org/#ipv4-out-of-range-part
|
|
74
|
+
static IPV4_OUT_OF_RANGE_PART (msg = 'Invalid URL') {
|
|
75
|
+
return new URLError(msg, 'IPV4_OUT_OF_RANGE_PART', URLError.IPV4_OUT_OF_RANGE_PART)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// https://url.spec.whatwg.org/#ipv6-unclosed
|
|
79
|
+
static IPV6_UNCLOSED (msg = 'Invalid URL') {
|
|
80
|
+
return new URLError(msg, 'IPV6_UNCLOSED', URLError.IPV6_UNCLOSED)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// https://url.spec.whatwg.org/#ipv6-invalid-compression
|
|
84
|
+
static IPV6_INVALID_COMPRESSION (msg = 'Invalid URL') {
|
|
85
|
+
return new URLError(msg, 'IPV6_INVALID_COMPRESSION', URLError.IPV6_INVALID_COMPRESSION)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// https://url.spec.whatwg.org/#ipv6-too-many-pieces
|
|
89
|
+
static IPV6_TOO_MANY_PIECES (msg = 'Invalid URL') {
|
|
90
|
+
return new URLError(msg, 'IPV6_TOO_MANY_PIECES', URLError.IPV6_TOO_MANY_PIECES)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// https://url.spec.whatwg.org/#ipv6-multiple-compression
|
|
94
|
+
static IPV6_MULTIPLE_COMPRESSION (msg = 'Invalid URL') {
|
|
95
|
+
return new URLError(msg, 'IPV6_MULTIPLE_COMPRESSION', URLError.IPV6_MULTIPLE_COMPRESSION)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// https://url.spec.whatwg.org/#ipv6-invalid-code-point
|
|
99
|
+
static IPV6_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
100
|
+
return new URLError(msg, 'IPV6_INVALID_CODE_POINT', URLError.IPV6_INVALID_CODE_POINT)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// https://url.spec.whatwg.org/#ipv6-too-few-pieces
|
|
104
|
+
static IPV6_TOO_FEW_PIECES (msg = 'Invalid URL') {
|
|
105
|
+
return new URLError(msg, 'IPV6_TOO_FEW_PIECES', URLError.IPV6_TOO_FEW_PIECES)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-too-many-pieces
|
|
109
|
+
static IPV4_IN_IPV6_TOO_MANY_PIECES (msg = 'Invalid URL') {
|
|
110
|
+
return new URLError(msg, 'IPV4_IN_IPV6_TOO_MANY_PIECES', URLError.IPV4_IN_IPV6_TOO_MANY_PIECES)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-invalid-code-point
|
|
114
|
+
static IPV4_IN_IPV6_INVALID_CODE_POINT (msg = 'Invalid URL') {
|
|
115
|
+
return new URLError(msg, 'IPV4_IN_IPV6_INVALID_CODE_POINT', URLError.IPV4_IN_IPV6_INVALID_CODE_POINT)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-out-of-range-part
|
|
119
|
+
static IPV4_IN_IPV6_OUT_OF_RANGE_PART (msg = 'Invalid URL') {
|
|
120
|
+
return new URLError(msg, 'IPV4_IN_IPV6_OUT_OF_RANGE_PART', URLError.IPV4_IN_IPV6_OUT_OF_RANGE_PART)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// https://url.spec.whatwg.org/#ipv4-in-ipv6-too-few-parts
|
|
124
|
+
static IPV4_IN_IPV6_TOO_FEW_PARTS (msg = 'Invalid URL') {
|
|
125
|
+
return new URLError(msg, 'IPV4_IN_IPV6_TOO_FEW_PARTS', URLError.IPV4_IN_IPV6_TOO_FEW_PARTS)
|
|
126
|
+
}
|
|
47
127
|
}
|
package/lib/parse.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const tr46 = require('tr46')
|
|
1
2
|
const constants = require('./constants')
|
|
2
3
|
const errors = require('./errors')
|
|
3
4
|
const infra = require('./infra')
|
|
@@ -118,7 +119,7 @@ module.exports = function parse (
|
|
|
118
119
|
pointer--
|
|
119
120
|
} else {
|
|
120
121
|
state = constants.STATE_FILE
|
|
121
|
-
pointer
|
|
122
|
+
pointer--
|
|
122
123
|
}
|
|
123
124
|
break
|
|
124
125
|
|
|
@@ -440,16 +441,16 @@ module.exports = function parse (
|
|
|
440
441
|
if (isDoubleDotPathSegment(buffer)) {
|
|
441
442
|
shortenPath(url)
|
|
442
443
|
|
|
443
|
-
if (c !== 0x2f && !isSpecial(url) && c === 0x5c) {
|
|
444
|
+
if (c !== 0x2f && !(isSpecial(url) && c === 0x5c)) {
|
|
444
445
|
url.path.push('')
|
|
445
446
|
}
|
|
446
447
|
} else if (isSingleDotPathSegment(buffer)) {
|
|
447
|
-
if (c !== 0x2f && !isSpecial(url) && c === 0x5c) {
|
|
448
|
+
if (c !== 0x2f && !(isSpecial(url) && c === 0x5c)) {
|
|
448
449
|
url.path.push('')
|
|
449
450
|
}
|
|
450
451
|
} else {
|
|
451
452
|
if (url.scheme === 'file' && url.path.length === 0 && isWindowsDriveLetter(buffer)) {
|
|
452
|
-
buffer
|
|
453
|
+
buffer = buffer.substring(0, 1) + ':' + buffer.substring(2)
|
|
453
454
|
}
|
|
454
455
|
|
|
455
456
|
url.path.push(buffer)
|
|
@@ -512,9 +513,241 @@ module.exports = function parse (
|
|
|
512
513
|
return url
|
|
513
514
|
}
|
|
514
515
|
|
|
515
|
-
// https://url.spec.whatwg.org/#host-
|
|
516
|
-
function parseHost (
|
|
517
|
-
|
|
516
|
+
// https://url.spec.whatwg.org/#concept-host-parser
|
|
517
|
+
function parseHost (input, isOpaque = false) {
|
|
518
|
+
if (input.charCodeAt(0) === 0x5b) {
|
|
519
|
+
if (input.charCodeAt(input.length - 1) !== 0x5d) throw errors.IPV6_UNCLOSED()
|
|
520
|
+
|
|
521
|
+
return parseIPv6(input.substring(1, input.length - 1))
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (isOpaque) return parseOpaqueHost(input)
|
|
525
|
+
|
|
526
|
+
const domain = percentDecode(Buffer.from(input, 'utf8'))
|
|
527
|
+
|
|
528
|
+
const asciiDomain = domainToASCII(domain.toString('utf8'), false)
|
|
529
|
+
|
|
530
|
+
for (const codePoint of asciiDomain) {
|
|
531
|
+
if (isForbiddenDomainCodePoint(codePoint.codePointAt(0))) {
|
|
532
|
+
throw errors.DOMAIN_INVALID_CODE_POINT()
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (endsInANumber(asciiDomain)) return parseIPv4(asciiDomain)
|
|
537
|
+
|
|
538
|
+
return asciiDomain
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// https://url.spec.whatwg.org/#ends-in-a-number-checker
|
|
542
|
+
function endsInANumber (input) {
|
|
543
|
+
const parts = input.split('.')
|
|
544
|
+
|
|
545
|
+
const last = parts[parts.length - 1]
|
|
546
|
+
|
|
547
|
+
if (last === '') {
|
|
548
|
+
if (parts.length === 1) return false
|
|
549
|
+
parts.pop()
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
try {
|
|
553
|
+
parseIPv4Number(last)
|
|
554
|
+
|
|
555
|
+
return true
|
|
556
|
+
} catch {
|
|
557
|
+
return false
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// https://url.spec.whatwg.org/#concept-ipv4-parser
|
|
562
|
+
function parseIPv4 (input) {
|
|
563
|
+
const parts = input.split('.')
|
|
564
|
+
|
|
565
|
+
if (parts[parts.length - 1] === '') {
|
|
566
|
+
if (parts.length > 1) parts.pop()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (parts.length > 4) throw errors.IPV4_TOO_MANY_PARTS()
|
|
570
|
+
|
|
571
|
+
const numbers = []
|
|
572
|
+
|
|
573
|
+
for (const part of parts) {
|
|
574
|
+
numbers.push(parseIPv4Number(part))
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
for (let i = 0, n = numbers.length - 1; i < n; i++) {
|
|
578
|
+
if (numbers[i] > 255) {
|
|
579
|
+
throw errors.IPV4_OUT_OF_RANGE_PART()
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (numbers[numbers.length - 1] > 256 ** (5 - numbers.length)) {
|
|
584
|
+
throw errors.IPV4_OUT_OF_RANGE_PART()
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
let ipv4 = numbers.pop()
|
|
588
|
+
let counter = 0
|
|
589
|
+
|
|
590
|
+
for (const n of numbers) ipv4 += n * 256 ** (3 - counter++)
|
|
591
|
+
|
|
592
|
+
return ipv4
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// https://url.spec.whatwg.org/#ipv4-number-parser
|
|
596
|
+
function parseIPv4Number (input) {
|
|
597
|
+
if (input === '') throw errors.IPV4_NON_NUMERIC_PART()
|
|
598
|
+
|
|
599
|
+
input = input.toLowerCase()
|
|
600
|
+
|
|
601
|
+
let R = 10
|
|
602
|
+
|
|
603
|
+
if (input.length >= 2) {
|
|
604
|
+
if (input[0] === '0') {
|
|
605
|
+
if (input[1] === 'x') {
|
|
606
|
+
input.substring(2)
|
|
607
|
+
R = 16
|
|
608
|
+
} else {
|
|
609
|
+
input.substring(1)
|
|
610
|
+
R = 8
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
if (input === '') return 0
|
|
616
|
+
|
|
617
|
+
const n = parseInt(input, R)
|
|
618
|
+
|
|
619
|
+
if (n.toString(R) !== input) throw errors.IPV4_NON_NUMERIC_PART()
|
|
620
|
+
|
|
621
|
+
return n
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// https://url.spec.whatwg.org/#concept-ipv6-parser
|
|
625
|
+
function parseIPv6 (input) {
|
|
626
|
+
const address = [0, 0, 0, 0, 0, 0, 0, 0]
|
|
627
|
+
let pieceIndex = 0
|
|
628
|
+
let compress = null
|
|
629
|
+
let pointer = 0
|
|
630
|
+
|
|
631
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
632
|
+
if (input.charCodeAt(pointer + 1) !== 0x3a) {
|
|
633
|
+
throw errors.IPV6_INVALID_COMPRESSION()
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
pointer += 2
|
|
637
|
+
compress = ++pieceIndex
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
while (pointer < input.length) {
|
|
641
|
+
if (pieceIndex === 8) throw errors.IPV6_TOO_MANY_PIECES()
|
|
642
|
+
|
|
643
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
644
|
+
if (compress !== null) throw errors.IPV6_MULTIPLE_COMPRESSION()
|
|
645
|
+
|
|
646
|
+
pointer++
|
|
647
|
+
compress = ++pieceIndex
|
|
648
|
+
continue
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
let value = 0
|
|
652
|
+
let length = 0
|
|
653
|
+
|
|
654
|
+
while (length < 4 && infra.isASCIIHexDigit(input.charCodeAt(pointer))) {
|
|
655
|
+
value = value * 0x10 + parseInt(input[pointer], 16)
|
|
656
|
+
|
|
657
|
+
pointer++
|
|
658
|
+
length++
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (input.charCodeAt(pointer) === 0x2e) {
|
|
662
|
+
if (length === 0) throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
663
|
+
|
|
664
|
+
pointer -= length
|
|
665
|
+
|
|
666
|
+
if (pieceIndex > 6) throw errors.IPV4_IN_IPV6_TOO_MANY_PIECES()
|
|
667
|
+
|
|
668
|
+
let numbersSeen = 0
|
|
669
|
+
|
|
670
|
+
while (pointer < input.length) {
|
|
671
|
+
let ipv4Piece = null
|
|
672
|
+
|
|
673
|
+
if (numbersSeen > 0) {
|
|
674
|
+
if (input.charCodeAt(pointer) === 0x2e && numbersSeen < 4) {
|
|
675
|
+
pointer++
|
|
676
|
+
} else {
|
|
677
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if (!infra.isASCIIDigit(input.charCodeAt(pointer))) {
|
|
682
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
while (infra.isASCIIDigit(input.charCodeAt(pointer))) {
|
|
686
|
+
const number = parseInt(input[pointer], 10)
|
|
687
|
+
|
|
688
|
+
if (ipv4Piece === null) {
|
|
689
|
+
ipv4Piece = number
|
|
690
|
+
} else if (ipv4Piece === 0) {
|
|
691
|
+
throw errors.IPV4_IN_IPV6_INVALID_CODE_POINT()
|
|
692
|
+
} else {
|
|
693
|
+
ipv4Piece = ipv4Piece * 10 + number
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (ipv4Piece > 255) throw errors.IPV4_IN_IPV6_OUT_OF_RANGE_PART()
|
|
697
|
+
|
|
698
|
+
pointer++
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
address[pieceIndex] = address[pieceIndex] * 0x100 + ipv4Piece
|
|
702
|
+
|
|
703
|
+
numbersSeen++
|
|
704
|
+
|
|
705
|
+
if (numbersSeen === 2 || numbersSeen === 4) pieceIndex++
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (numbersSeen !== 4) throw errors.IPV4_IN_IPV6_TOO_FEW_PARTS()
|
|
709
|
+
|
|
710
|
+
break
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (input.charCodeAt(pointer) === 0x3a) {
|
|
714
|
+
pointer++
|
|
715
|
+
|
|
716
|
+
if (pointer === input.length) {
|
|
717
|
+
throw errors.IPV6_INVALID_CODE_POINT()
|
|
718
|
+
}
|
|
719
|
+
} else if (pointer !== input.length) {
|
|
720
|
+
throw errors.IPV6_INVALID_CODE_POINT()
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
address[pieceIndex++] = value
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
if (compress !== null) {
|
|
727
|
+
let swaps = pieceIndex - compress
|
|
728
|
+
|
|
729
|
+
pieceIndex = 7
|
|
730
|
+
|
|
731
|
+
while (pieceIndex !== 0 && swaps > 0) {
|
|
732
|
+
[address[pieceIndex], address[compress + swaps - 1]] = [address[compress + swaps - 1], address[pieceIndex]]
|
|
733
|
+
|
|
734
|
+
pieceIndex--
|
|
735
|
+
swaps--
|
|
736
|
+
}
|
|
737
|
+
} else if (pieceIndex !== 8) {
|
|
738
|
+
throw errors.IPV6_TOO_FEW_PIECES()
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return address
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// https://url.spec.whatwg.org/#concept-opaque-host-parser
|
|
745
|
+
function parseOpaqueHost (input) {
|
|
746
|
+
const bytes = Buffer.from(input, 'utf8')
|
|
747
|
+
|
|
748
|
+
if (bytes.some(isForbiddenHostCodePoint)) throw errors.HOST_INVALID_CODE_POINT()
|
|
749
|
+
|
|
750
|
+
return percentEncodeAfterEncoding(bytes, c0ControlPercentEncodeSet)
|
|
518
751
|
}
|
|
519
752
|
|
|
520
753
|
// https://url.spec.whatwg.org/#special-scheme
|
|
@@ -605,6 +838,31 @@ function isDoubleDotPathSegment (segment) {
|
|
|
605
838
|
return segment === '..' || decodeURIComponent(segment) === '..'
|
|
606
839
|
}
|
|
607
840
|
|
|
841
|
+
// https://url.spec.whatwg.org/#forbidden-host-code-point
|
|
842
|
+
function isForbiddenHostCodePoint (c) {
|
|
843
|
+
return c === 0x0 || c === 0x9 || c === 0xa || c === 0xd || c === 0x20 || c === 0x23 || c === 0x2f || c === 0x3a || c === 0x3c || c === 0x3e || c === 0x3f || c === 0x40 || c === 0x5b || c === 0x5c || c === 0x5d || c === 0x5e || c === 0x7c
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// https://url.spec.whatwg.org/#forbidden-domain-code-point
|
|
847
|
+
function isForbiddenDomainCodePoint (c) {
|
|
848
|
+
return isForbiddenHostCodePoint(c) || c <= 0x1f || c === 0x25 || c === 0x7f
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// https://url.spec.whatwg.org/#concept-domain-to-ascii
|
|
852
|
+
function domainToASCII (domain, beStrict) {
|
|
853
|
+
const result = tr46.toASCII(domain, {
|
|
854
|
+
checkHyphens: false,
|
|
855
|
+
checkBidi: true,
|
|
856
|
+
checkJoiners: true,
|
|
857
|
+
useSTD3ASCIIRules: beStrict,
|
|
858
|
+
verifyDNSLength: beStrict
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
if (result === null || result === '') throw errors.DOMAIN_TO_ASCII()
|
|
862
|
+
|
|
863
|
+
return result
|
|
864
|
+
}
|
|
865
|
+
|
|
608
866
|
// https://url.spec.whatwg.org/#percent-encode
|
|
609
867
|
function percentEncode (byte) {
|
|
610
868
|
return `%${byte.toString(16).toUpperCase().padStart(2, '0')}`
|
|
@@ -625,6 +883,28 @@ function percentEncodeAfterEncoding (bytes, percentEncodeSet) {
|
|
|
625
883
|
return output
|
|
626
884
|
}
|
|
627
885
|
|
|
886
|
+
// https://url.spec.whatwg.org/#percent-decode
|
|
887
|
+
function percentDecode (input) {
|
|
888
|
+
const output = Buffer.alloc(input.byteLength)
|
|
889
|
+
let outputIndex = 0
|
|
890
|
+
|
|
891
|
+
for (let i = 0, n = input.byteLength; i < n; i++) {
|
|
892
|
+
const byte = input[i]
|
|
893
|
+
|
|
894
|
+
if (byte !== 0x25) {
|
|
895
|
+
output[outputIndex++] = byte
|
|
896
|
+
} else if (byte === 0x25 && (!infra.isASCIIHexDigit(input[i + 1]) || !infra.isASCIIHexDigit(input[i + 2]))) {
|
|
897
|
+
output[outputIndex++] = byte
|
|
898
|
+
} else {
|
|
899
|
+
const bytePoint = parseInt(String.fromCodePoint(input[i + 1], input[i + 2]), 16)
|
|
900
|
+
output[outputIndex++] = bytePoint
|
|
901
|
+
i += 2
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
return output.subarray(0, outputIndex)
|
|
906
|
+
}
|
|
907
|
+
|
|
628
908
|
// https://url.spec.whatwg.org/#string-utf-8-percent-encode
|
|
629
909
|
function UTF8PercentEncode (input, percentEncodeSet) {
|
|
630
910
|
return percentEncodeAfterEncoding(Buffer.from(input, 'utf8'), percentEncodeSet)
|
package/lib/serialize.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// https://url.spec.whatwg.org/#url-serializing
|
|
2
|
-
module.exports = function serialize (url, excludeFragment = false) {
|
|
2
|
+
module.exports = exports = function serialize (url, excludeFragment = false) {
|
|
3
3
|
let output = url.scheme + ':'
|
|
4
4
|
|
|
5
5
|
if (url.host !== null) {
|
|
@@ -15,7 +15,7 @@ module.exports = function serialize (url, excludeFragment = false) {
|
|
|
15
15
|
output += '@'
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
output += url.host
|
|
18
|
+
output += serializeHost(url.host)
|
|
19
19
|
|
|
20
20
|
if (url.port !== null) {
|
|
21
21
|
output += ':' + url.port.toString(10)
|
|
@@ -47,3 +47,52 @@ module.exports = function serialize (url, excludeFragment = false) {
|
|
|
47
47
|
|
|
48
48
|
return output
|
|
49
49
|
}
|
|
50
|
+
|
|
51
|
+
// https://url.spec.whatwg.org/#concept-host-serializer
|
|
52
|
+
const serializeHost = exports.host = function serializeHost (host) {
|
|
53
|
+
if (typeof host === 'number') return serializeIPv4(host)
|
|
54
|
+
if (typeof host !== 'string') return '[' + serializeIPv6(host) + ']'
|
|
55
|
+
|
|
56
|
+
return host
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// https://url.spec.whatwg.org/#concept-ipv4-serializer
|
|
60
|
+
function serializeIPv4 (address) {
|
|
61
|
+
let output = ''
|
|
62
|
+
let n = address
|
|
63
|
+
|
|
64
|
+
for (let i = 1; i <= 4; i++) {
|
|
65
|
+
output = (n % 256).toString(10) + output
|
|
66
|
+
|
|
67
|
+
if (i !== 4) output = '.' + output
|
|
68
|
+
|
|
69
|
+
n = n / 256 | 0
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return output
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// https://url.spec.whatwg.org/#concept-ipv6-serializer
|
|
76
|
+
function serializeIPv6 (address) {
|
|
77
|
+
let output = ''
|
|
78
|
+
const compress = 8 // TODO Find first longest 0 sequence
|
|
79
|
+
let ignore0 = false
|
|
80
|
+
|
|
81
|
+
for (let pieceIndex = 0; pieceIndex <= 7; pieceIndex++) {
|
|
82
|
+
if (ignore0 && address[pieceIndex] === 0) continue
|
|
83
|
+
|
|
84
|
+
ignore0 = false
|
|
85
|
+
|
|
86
|
+
if (compress === pieceIndex) {
|
|
87
|
+
output += pieceIndex === 0 ? '::' : ':'
|
|
88
|
+
ignore0 = true
|
|
89
|
+
continue
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
output += address[pieceIndex].toString(16)
|
|
93
|
+
|
|
94
|
+
if (pieceIndex !== 7) output += ':'
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return output
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-url",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "URL parser for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"homepage": "https://github.com/holepunchto/bare-url",
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"bare-os": "^2.0.0",
|
|
25
|
-
"bare-path": "^2.0.0"
|
|
25
|
+
"bare-path": "^2.0.0",
|
|
26
|
+
"tr46": "^5.0.0"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"brittle": "^3.3.2",
|