identifier-js 0.0.4 → 0.0.6
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 +17 -12
- package/package.json +1 -1
- package/tests/to-absolute.test.js +1 -1
- package/tests/validation-hostnames.test.js +94 -0
- package/tests/validation-ip-port.test.js +246 -0
package/index.js
CHANGED
|
@@ -2,25 +2,26 @@
|
|
|
2
2
|
// a valid URI is always a valid IRI
|
|
3
3
|
const { recursiveCompile } = require('url-templates');
|
|
4
4
|
const patterns = new Map();
|
|
5
|
-
// RFC3986/RFC3987 common rules
|
|
5
|
+
// RFC3986/RFC3987 common rules + https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2:~:text=DNS%29%2E-,A,of%20%5BRFC1123%5D%2E
|
|
6
6
|
const commonRules = {
|
|
7
7
|
scheme: '[a-zA-Z][a-zA-Z0-9+.-]*',
|
|
8
|
-
port: '[
|
|
8
|
+
port: '(?:0|[1-9]\\d{0,3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])?',
|
|
9
9
|
IP_literal: '\\[(?:{IPv6address}|{IPvFuture})\\]',
|
|
10
10
|
IPv6address: '(?:(?:{h16}:){6}{ls32}|::(?:{h16}:){5}{ls32}|(?:(?:{h16})?)::(?:{h16}:){4}{ls32}|(?:(?:{h16}:)?{h16})?::(?:{h16}:){3}{ls32}|(?:(?:{h16}:){0,2}{h16})?::(?:{h16}:){2}{ls32}|(?:(?:{h16}:){0,3}{h16})?::(?:{h16}:){1}{ls32}|(?:(?:{h16}:){0,4}{h16})?::{ls32}|(?:(?:{h16}:){0,5}{h16})?::{h16}|(?:(?:{h16}:){0,6}{h16})?::)',
|
|
11
11
|
ls32: '(?:{h16}:{h16}|{IPv4address})',
|
|
12
|
-
h16: '{
|
|
12
|
+
h16: '{hex_digit}{1,4}',
|
|
13
13
|
IPv4address: '(?:{dec_octet}\\.){3}{dec_octet}',
|
|
14
14
|
dec_octet: '(?:\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])',
|
|
15
|
-
IPvFuture: 'v{
|
|
15
|
+
IPvFuture: 'v{hex_digit}+\\.(?:{unreserved}|{sub_delims}|:)+',
|
|
16
16
|
unreserved: '[a-zA-Z0-9_.~-]',
|
|
17
17
|
reserved: '(?:{gen_delims}|{sub_delims})',
|
|
18
|
-
pct_encoded: '%{
|
|
18
|
+
pct_encoded: '%{hex_digit}{2}',
|
|
19
19
|
gen_delims: '[:/?#[\\]@]',
|
|
20
20
|
sub_delims: "[!&'()*+,;=$]",
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
hex_digit: '[0-9A-Fa-f]',
|
|
22
|
+
alpha_digit: '[a-zA-Z0-9]',
|
|
23
|
+
UUID: '{hex_digit}{8}-{hex_digit}{4}-{hex_digit}{4}-{hex_digit}{4}-{hex_digit}{12}',
|
|
24
|
+
UUID_v4: '{hex_digit}{8}-{hex_digit}{4}-4{hex_digit}{3}-[89abAB]{hex_digit}{3}-{hex_digit}{12}',
|
|
24
25
|
};
|
|
25
26
|
// RFC3986 rules
|
|
26
27
|
const uriRules = {
|
|
@@ -31,9 +32,10 @@ const uriRules = {
|
|
|
31
32
|
hier_part: '(?:\/\/{authority}{path_abempty}|{path_absolute}|{path_rootless}|{path_empty})',
|
|
32
33
|
relative_part: '(?:\/\/{authority}{path_abempty}|{path_absolute}|{path_noscheme}|{path_empty})',
|
|
33
34
|
authority: '(?:{userinfo}@)?{host}(?::{port})?',
|
|
34
|
-
host: '(?:{IP_literal}|{IPv4address}|{reg_name})',
|
|
35
35
|
userinfo: '(?:{unreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
36
|
-
|
|
36
|
+
host: '(?:{IP_literal}|{IPv4address}|{reg_name})',
|
|
37
|
+
a_label: '(?:{alpha_digit})(?:(?:{alpha_digit}|-){0,61}(?:{alpha_digit}))?',
|
|
38
|
+
reg_name: "(?:(?=.{1,255}(?:[:/?#]|$))(?:{a_label})(?:\\.{a_label})*)",
|
|
37
39
|
path: '(?:{path_abempty}|{path_absolute}|{path_noscheme}|{path_rootless}|{path_empty})',
|
|
38
40
|
path_abempty: '(?:\/{segment})*',
|
|
39
41
|
path_absolute: '\/(?:{segment_nz}(?:\/{segment})*)?',
|
|
@@ -58,7 +60,9 @@ const iriRules = {
|
|
|
58
60
|
iauthority: '(?:{iuserinfo}@)?{ihost}(?::{port})?',
|
|
59
61
|
iuserinfo: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
60
62
|
ihost: '(?:{IP_literal}|{IPv4address}|{ireg_name})',
|
|
61
|
-
|
|
63
|
+
separator: '[\\x2E\\uFF0E\\u3002\\uFF61]',
|
|
64
|
+
u_label: '(?:{uchar})(?:(?:{uchar}|-){0,61}(?:{uchar}))?',
|
|
65
|
+
ireg_name: '(?:(?=.{1,255}(?:[:/?#]|$))(?:{u_label})(?:{separator}(?:{u_label}))*)',
|
|
62
66
|
ipath: '(?:{ipath_abempty}|{ipath_absolute}|{ipath_noscheme}|{ipath_rootless}|{ipath_empty})',
|
|
63
67
|
ipath_empty: '',
|
|
64
68
|
ipath_rootless: '{isegment_nz}(?:\/{isegment})*',
|
|
@@ -71,8 +75,9 @@ const iriRules = {
|
|
|
71
75
|
iquery: '(?:{ipchar}|{iprivate}|\/|\\?)*',
|
|
72
76
|
ifragment: '(?:{ipchar}|\/|\\?)*',
|
|
73
77
|
ipchar: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:|@)',
|
|
74
|
-
iunreserved: '(?:
|
|
78
|
+
iunreserved: '(?:{unreserved}|{ucschar})',
|
|
75
79
|
iprivate: '[\\uE000-\\uF8FF\\u{F0000}-\\u{FFFFD}\\u{100000}-\\u{10FFFD}]',
|
|
80
|
+
uchar: '[\\u200C\\u200D\\u00B7\\u0375\\u30FB\\u05F3\\u05F4\\p{L}\\p{N}\\p{Mn}\\p{Mc}]',
|
|
76
81
|
ucschar: '[\\xA0-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFEF\\u{10000}-\\u{1FFFD}\\u{20000}-\\u{2FFFD}\\u{30000}-\\u{3FFFD}\\u{40000}-\\u{4FFFD}\\u{50000}-\\u{5FFFD}\\u{60000}-\\u{6FFFD}\\u{70000}-\\u{7FFFD}\\u{80000}-\\u{8FFFD}\\u{90000}-\\u{9FFFD}\\u{A0000}-\\u{AFFFD}\\u{B0000}-\\u{BFFFD}\\u{C0000}-\\u{CFFFD}\\u{D0000}-\\u{DFFFD}\\u{E1000}-\\u{EFFFD}]',
|
|
77
82
|
};
|
|
78
83
|
// pattern RFC group names
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import id from '../index.js';
|
|
|
3
3
|
|
|
4
4
|
describe('toAbsoluteReference', () => {
|
|
5
5
|
test('Base with a fragment', () => {
|
|
6
|
-
expect(id.toAbsoluteReference('http://exampl
|
|
6
|
+
expect(id.toAbsoluteReference('http://examplé.org/rosé#dasd')).to.equal('http://examplé.org/rosé');
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
test('Scheme is required', () => {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import id from '../index.js';
|
|
3
|
+
|
|
4
|
+
describe('isUri with hostnames', () => {
|
|
5
|
+
test('Valid label - alphanumeric', () => {
|
|
6
|
+
expect(id.isUri('https://example')).to.equal(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('Valid label - hyphen in middle', () => {
|
|
10
|
+
expect(id.isUri('https://exa-mple')).to.equal(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('Invalid label - hyphen at start', () => {
|
|
14
|
+
expect(() => id.isUri('https://-example')).to.throw(Error, 'Invalid URI: https://-example');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('Invalid label - hyphen at end', () => {
|
|
18
|
+
expect(() => id.isUri('https://example-')).to.throw(Error, 'Invalid URI: https://example-');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('Valid multiple labels', () => {
|
|
22
|
+
expect(id.isUri('https://example-domain.com')).to.equal(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('Invalid - leading dot', () => {
|
|
26
|
+
expect(() => id.isUri('https://.example.com')).to.throw(Error, 'Invalid URI: https://.example.com');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Invalid - trailing dot', () => {
|
|
30
|
+
expect(() => id.isUri('https://example.com.')).to.throw(Error, 'Invalid URI: https://example.com.');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Invalid - consecutive dots', () => {
|
|
34
|
+
expect(() => id.isUri('https://example..com')).to.throw(Error, 'Invalid URI: https://example..com');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Invalid - unicode character', () => {
|
|
38
|
+
expect(() => id.isUri('https://exämple')).to.throw(Error, 'Invalid URI: https://exämple');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('isIri with hostnames', () => {
|
|
43
|
+
test('Valid label - alphanumeric', () => {
|
|
44
|
+
expect(id.isIri('https://example')).to.equal(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('Valid label - hyphen in middle', () => {
|
|
48
|
+
expect(id.isIri('https://exa-mple')).to.equal(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('Invalid label - hyphen at start', () => {
|
|
52
|
+
expect(() => id.isIri('https://-example')).to.throw(Error, 'Invalid IRI: https://-example');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Invalid label - hyphen at end', () => {
|
|
56
|
+
expect(() => id.isIri('https://example-')).to.throw(Error, 'Invalid IRI: https://example-');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('Valid unicode label - Latin extended', () => {
|
|
60
|
+
expect(id.isIri('https://exämple')).to.equal(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('Valid unicode label - Chinese', () => {
|
|
64
|
+
expect(id.isIri('https://例子')).to.equal(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('Valid unicode label - Hindi', () => {
|
|
68
|
+
expect(id.isIri('https://उदाहरण')).to.equal(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('Valid unicode label - Japanese', () => {
|
|
72
|
+
expect(id.isIri('https://例え.テスト')).to.equal(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('Valid label with middle dot', () => {
|
|
76
|
+
expect(id.isIri('https://exa·mple')).to.equal(true);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('Invalid - leading dot', () => {
|
|
80
|
+
expect(() => id.isIri('https://.example')).to.throw(Error, 'Invalid IRI: https://.example');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('Invalid - trailing dot', () => {
|
|
84
|
+
expect(() => id.isIri('https://example.')).to.throw(Error, 'Invalid IRI: https://example.');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('Invalid - consecutive dots', () => {
|
|
88
|
+
expect(() => id.isIri('https://example..test')).to.throw(Error, 'Invalid IRI: https://example..test');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('Invalid - emoji in label', () => {
|
|
92
|
+
expect(() => id.isIri('https://example😀')).to.throw(Error, 'Invalid IRI: https://example😀');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import id from '../index.js';
|
|
3
|
+
|
|
4
|
+
describe('isUri – IPv4 host validation', () => {
|
|
5
|
+
test('Valid full IPv4 address', () => {
|
|
6
|
+
expect(id.isUri('https://1.2.3.4')).to.equal(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('Invalid IPv4 address, but valid as hostname', () => {
|
|
10
|
+
expect(id.isUri('https://999.999.999.999')).to.equal(true);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe('isIri – IPv4 host validation', () => {
|
|
15
|
+
test('Valid full IPv4 address', () => {
|
|
16
|
+
expect(id.isIri('https://1.2.3.4')).to.equal(true);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('Invalid IPv4 address, but valid as hostname', () => {
|
|
20
|
+
expect(id.isIri('https://999.999.999.999')).to.equal(true);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('isUri – IPv6 host validation', () => {
|
|
25
|
+
test('Valid full (uncompressed) IPv6 address', () => {
|
|
26
|
+
expect(id.isUri('https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]')).to.equal(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Valid full IPv6 address (no zero compression)', () => {
|
|
30
|
+
expect(id.isUri('https://[2001:db8:85a3:0:0:8a2e:370:7334]')).to.equal(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Valid IPv6 address with zero compression (::)', () => {
|
|
34
|
+
expect(id.isUri('https://[2001:db8:85a3::8a2e:370:7334]')).to.equal(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Valid loopback IPv6 address (::1)', () => {
|
|
38
|
+
expect(id.isUri('https://[::1]')).to.equal(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Valid unspecified IPv6 address (::)', () => {
|
|
42
|
+
expect(id.isUri('https://[::]')).to.equal(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('Valid compressed IPv6 address (trailing compression)', () => {
|
|
46
|
+
expect(id.isUri('https://[2001:db8::]')).to.equal(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Valid IPv6 with embedded IPv4 (IPv4-mapped)', () => {
|
|
50
|
+
expect(id.isUri('https://[::ffff:192.168.1.1]')).to.equal(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('Valid IPv6 with embedded IPv4 (mixed notation)', () => {
|
|
54
|
+
expect(id.isUri('https://[2001:db8::192.168.1.1]')).to.equal(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Valid link-local IPv6 address', () => {
|
|
58
|
+
expect(id.isUri('https://[fe80::1]')).to.equal(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('Valid maximum-length IPv6 address', () => {
|
|
62
|
+
expect(id.isUri('https://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]')).to.equal(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('Invalid IPv6 – excessive colon sequence', () => {
|
|
66
|
+
expect(() => id.isUri('https://[2001:db8:85a3:::8a2e:370:7334]')).to.throw(Error, 'Invalid URI: https://[2001:db8:85a3:::8a2e:370:7334]');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Invalid IPv6 – multiple zero compressions (::)', () => {
|
|
70
|
+
expect(() => id.isUri('https://[2001:db8:85a3::8a2e::7334]')).to.throw(Error, 'Invalid URI: https://[2001:db8:85a3::8a2e::7334]');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('Invalid IPv6 – segment exceeds 16-bit limit', () => {
|
|
74
|
+
expect(() => id.isUri('https://[12345::]')).to.throw(Error, 'Invalid URI: https://[12345::]');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('Invalid IPv6 – insufficient segments without compression', () => {
|
|
78
|
+
expect(() => id.isUri('https://[2001:db8:85a3:8a2e:370:7334]')).to.throw(Error, 'Invalid URI: https://[2001:db8:85a3:8a2e:370:7334]');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('Invalid IPv6 – too many segments', () => {
|
|
82
|
+
expect(() => id.isUri('https://[2001:db8:85a3:0000:0000:8a2e:0370:7334:1234]')).to.throw(Error, 'Invalid URI: https://[2001:db8:85a3:0000:0000:8a2e:0370:7334:1234]');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('Invalid IPv6 – embedded IPv4 out of range', () => {
|
|
86
|
+
expect(() => id.isUri('https://[::ffff:999.168.1.1]')).to.throw(Error, 'Invalid URI: https://[::ffff:999.168.1.1]');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('Invalid IPv6 – non-hexadecimal character in segment', () => {
|
|
90
|
+
expect(() => id.isUri('https://[2001:db8::g1]')).to.throw(Error, 'Invalid URI: https://[2001:db8::g1]');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Invalid IPv6 – leading colon without compression', () => {
|
|
94
|
+
expect(() => id.isUri('https://[:2001:db8::1]')).to.throw(Error, 'Invalid URI: https://[:2001:db8::1]');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('Invalid IPv6 – trailing colon without compression', () => {
|
|
98
|
+
expect(() => id.isUri('https://[2001:db8::1:]')).to.throw(Error, 'Invalid URI: https://[2001:db8::1:]');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('Invalid IPv6 – empty address literal', () => {
|
|
102
|
+
expect(() => id.isUri('https://[]')).to.throw(Error, 'Invalid URI: https://[]');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('isIri – IPv6 host validation', () => {
|
|
107
|
+
test('Valid full (uncompressed) IPv6 address', () => {
|
|
108
|
+
expect(id.isIri('https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]')).to.equal(true);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('Valid full IPv6 address (no zero compression)', () => {
|
|
112
|
+
expect(id.isIri('https://[2001:db8:85a3:0:0:8a2e:370:7334]')).to.equal(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('Valid IPv6 address with zero compression (::)', () => {
|
|
116
|
+
expect(id.isIri('https://[2001:db8:85a3::8a2e:370:7334]')).to.equal(true);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('Valid loopback IPv6 address (::1)', () => {
|
|
120
|
+
expect(id.isIri('https://[::1]')).to.equal(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test('Valid unspecified IPv6 address (::)', () => {
|
|
124
|
+
expect(id.isIri('https://[::]')).to.equal(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('Valid compressed IPv6 address (trailing compression)', () => {
|
|
128
|
+
expect(id.isIri('https://[2001:db8::]')).to.equal(true);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('Valid IPv6 with embedded IPv4 (IPv4-mapped)', () => {
|
|
132
|
+
expect(id.isIri('https://[::ffff:192.168.1.1]')).to.equal(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('Valid IPv6 with embedded IPv4 (mixed notation)', () => {
|
|
136
|
+
expect(id.isIri('https://[2001:db8::192.168.1.1]')).to.equal(true);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('Valid link-local IPv6 address', () => {
|
|
140
|
+
expect(id.isIri('https://[fe80::1]')).to.equal(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test('Valid maximum-length IPv6 address', () => {
|
|
144
|
+
expect(id.isIri('https://[ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff]')).to.equal(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('Invalid IPv6 – excessive colon sequence', () => {
|
|
148
|
+
expect(() => id.isIri('https://[2001:db8:85a3:::8a2e:370:7334]')).to.throw(Error, 'Invalid IRI: https://[2001:db8:85a3:::8a2e:370:7334]');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('Invalid IPv6 – multiple zero compressions (::)', () => {
|
|
152
|
+
expect(() => id.isIri('https://[2001:db8:85a3::8a2e::7334]')).to.throw(Error, 'Invalid IRI: https://[2001:db8:85a3::8a2e::7334]');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('Invalid IPv6 – segment exceeds 16-bit limit', () => {
|
|
156
|
+
expect(() => id.isIri('https://[12345::]')).to.throw(Error, 'Invalid IRI: https://[12345::]');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('Invalid IPv6 – insufficient segments without compression', () => {
|
|
160
|
+
expect(() => id.isIri('https://[2001:db8:85a3:8a2e:370:7334]')).to.throw(Error, 'Invalid IRI: https://[2001:db8:85a3:8a2e:370:7334]');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('Invalid IPv6 – too many segments', () => {
|
|
164
|
+
expect(() => id.isIri('https://[2001:db8:85a3:0000:0000:8a2e:0370:7334:1234]')).to.throw(Error, 'Invalid IRI: https://[2001:db8:85a3:0000:0000:8a2e:0370:7334:1234]');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('Invalid IPv6 – embedded IPv4 out of range', () => {
|
|
168
|
+
expect(() => id.isIri('https://[::ffff:999.168.1.1]')).to.throw(Error, 'Invalid IRI: https://[::ffff:999.168.1.1]');
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test('Invalid IPv6 – non-hexadecimal character in segment', () => {
|
|
172
|
+
expect(() => id.isIri('https://[2001:db8::g1]')).to.throw(Error, 'Invalid IRI: https://[2001:db8::g1]');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('Invalid IPv6 – leading colon without compression', () => {
|
|
176
|
+
expect(() => id.isIri('https://[:2001:db8::1]')).to.throw(Error, 'Invalid IRI: https://[:2001:db8::1]');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('Invalid IPv6 – trailing colon without compression', () => {
|
|
180
|
+
expect(() => id.isIri('https://[2001:db8::1:]')).to.throw(Error, 'Invalid IRI: https://[2001:db8::1:]');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('Invalid IPv6 – empty address literal', () => {
|
|
184
|
+
expect(() => id.isIri('https://[]')).to.throw(Error, 'Invalid IRI: https://[]');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('isUri – port validation', () => {
|
|
189
|
+
test('Valid port', () => {
|
|
190
|
+
expect(id.isUri('https://example.com:80')).to.equal(true);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Valid port zero', () => {
|
|
194
|
+
expect(id.isUri('https://example.com:0')).to.equal(true);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('Valid port at max edge', () => {
|
|
198
|
+
expect(id.isUri('https://example.com:65535')).to.equal(true);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('Valid port empty', () => {
|
|
202
|
+
expect(id.isUri('https://example.com:/')).to.equal(true);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('Invalid "space" port', () => {
|
|
206
|
+
expect(() => id.isUri('https://example.com: /')).to.throw(Error, 'Invalid URI: https://example.com: /');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('Invalid port char', () => {
|
|
210
|
+
expect(() => id.isUri('https://example.com:ff')).to.throw(Error, 'Invalid URI: https://example.com:ff');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('Invalid port beyond edge', () => {
|
|
214
|
+
expect(() => id.isUri('https://example.com:65536')).to.throw(Error, 'Invalid URI: https://example.com:65536');
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('isIri – port validation', () => {
|
|
219
|
+
test('Valid port', () => {
|
|
220
|
+
expect(id.isIri('https://example.com:80')).to.equal(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('Valid port zero', () => {
|
|
224
|
+
expect(id.isIri('https://example.com:0')).to.equal(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('Valid port at max edge', () => {
|
|
228
|
+
expect(id.isIri('https://example.com:65535')).to.equal(true);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test('Valid port empty', () => {
|
|
232
|
+
expect(id.isIri('https://example.com:/')).to.equal(true);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test('Invalid "space" port', () => {
|
|
236
|
+
expect(() => id.isIri('https://example.com: /')).to.throw(Error, 'Invalid IRI: https://example.com: /');
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('Invalid port char', () => {
|
|
240
|
+
expect(() => id.isIri('https://example.com:ff')).to.throw(Error, 'Invalid IRI: https://example.com:ff');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('Invalid port beyond edge', () => {
|
|
244
|
+
expect(() => id.isIri('https://example.com:65536')).to.throw(Error, 'Invalid IRI: https://example.com:65536');
|
|
245
|
+
});
|
|
246
|
+
});
|