identifier-js 0.0.7 → 0.0.9
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/docs/rfc9110-rfc9112-abnf.yaml +267 -0
- package/index.js +27 -14
- package/package.json +3 -5
- package/readme.md +10 -3
- package/tests/validation-scheme-specific.test.js +554 -0
- package/tests/vitest-setup.js +2 -1
- package/tests/validation-hostnames.test.js +0 -94
- /package/docs/{rfc3986-rfc3987-abnf.yml → rfc3986-rfc3987-abnf.yaml} +0 -0
- /package/tests/{normalize.test.js → normalize.test-off.js} +0 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Accept: |
|
|
2
|
+
[ ( media-range [ weight ] ) *( OWS "," OWS ( media-range [ weight ] ) ) ]
|
|
3
|
+
Accept-Charset: >
|
|
4
|
+
[ ( ( token / "*" ) [ weight ] ) *( OWS "," OWS ( ( token / "*" ) [ weight ] ) ) ]
|
|
5
|
+
Accept-Encoding: |
|
|
6
|
+
[ ( codings [ weight ] ) *( OWS "," OWS ( codings [ weight ] ) ) ]
|
|
7
|
+
Accept-Language: >
|
|
8
|
+
[ ( language-range [ weight ] ) *( OWS "," OWS ( language-range [ weight ] ) ) ]
|
|
9
|
+
Accept-Ranges: acceptable-ranges
|
|
10
|
+
Allow: |
|
|
11
|
+
[ method *( OWS "," OWS method ) ]
|
|
12
|
+
Authentication-Info: |
|
|
13
|
+
[ auth-param *( OWS "," OWS auth-param ) ]
|
|
14
|
+
Authorization: credentials
|
|
15
|
+
BWS: OWS
|
|
16
|
+
Connection: |
|
|
17
|
+
[ connection-option *( OWS "," OWS connection-option ) ]
|
|
18
|
+
Content-Encoding: |
|
|
19
|
+
[ content-coding *( OWS "," OWS content-coding ) ]
|
|
20
|
+
Content-Language: |
|
|
21
|
+
[ language-tag *( OWS "," OWS language-tag ) ]
|
|
22
|
+
Content-Length: 1*DIGIT
|
|
23
|
+
Content-Location: absolute-URI / partial-URI
|
|
24
|
+
Content-Range: |
|
|
25
|
+
range-unit SP ( range-resp / unsatisfied-range )
|
|
26
|
+
Content-Type: media-type
|
|
27
|
+
Date: HTTP-date
|
|
28
|
+
ETag: entity-tag
|
|
29
|
+
Expect: |
|
|
30
|
+
[ expectation *( OWS "," OWS expectation ) ]
|
|
31
|
+
From: mailbox
|
|
32
|
+
GMT: '%x47.4D.54'
|
|
33
|
+
HTTP-date: IMF-fixdate / obs-date
|
|
34
|
+
Host: uri-host [ ":" port ]
|
|
35
|
+
IMF-fixdate: |
|
|
36
|
+
day-name "," SP date1 SP time-of-day SP GMT
|
|
37
|
+
If-Match: |
|
|
38
|
+
"*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
|
|
39
|
+
If-Modified-Since: HTTP-date
|
|
40
|
+
If-None-Match: |
|
|
41
|
+
"*" / [ entity-tag *( OWS "," OWS entity-tag ) ]
|
|
42
|
+
If-Range: entity-tag / HTTP-date
|
|
43
|
+
If-Unmodified-Since: HTTP-date
|
|
44
|
+
Last-Modified: HTTP-date
|
|
45
|
+
Location: URI-reference
|
|
46
|
+
Max-Forwards: 1*DIGIT
|
|
47
|
+
OWS: |
|
|
48
|
+
*( SP / HTAB )
|
|
49
|
+
Proxy-Authenticate: |
|
|
50
|
+
[ challenge *( OWS "," OWS challenge ) ]
|
|
51
|
+
Proxy-Authentication-Info: |
|
|
52
|
+
[ auth-param *( OWS "," OWS auth-param ) ]
|
|
53
|
+
Proxy-Authorization: credentials
|
|
54
|
+
RWS: |
|
|
55
|
+
1*( SP / HTAB )
|
|
56
|
+
Range: ranges-specifier
|
|
57
|
+
Referer: absolute-URI / partial-URI
|
|
58
|
+
Retry-After: HTTP-date / delay-seconds
|
|
59
|
+
Server: |
|
|
60
|
+
product *( RWS ( product / comment ) )
|
|
61
|
+
TE: |
|
|
62
|
+
[ t-codings *( OWS "," OWS t-codings ) ]
|
|
63
|
+
Trailer: |
|
|
64
|
+
[ field-name *( OWS "," OWS field-name ) ]
|
|
65
|
+
Upgrade: |
|
|
66
|
+
[ protocol *( OWS "," OWS protocol ) ]
|
|
67
|
+
User-Agent: |
|
|
68
|
+
product *( RWS ( product / comment ) )
|
|
69
|
+
Vary: |
|
|
70
|
+
[ ( "*" / field-name ) *( OWS "," OWS ( "*" / field-name ) ) ]
|
|
71
|
+
Via: |
|
|
72
|
+
[ ( received-protocol RWS received-by [ RWS comment ] )
|
|
73
|
+
*( OWS "," OWS ( received-protocol RWS received-by [ RWS comment ] ) ) ]
|
|
74
|
+
WWW-Authenticate: |
|
|
75
|
+
[ challenge *( OWS "," OWS challenge ) ]
|
|
76
|
+
absolute-path: |
|
|
77
|
+
1*( "/" segment )
|
|
78
|
+
acceptable-ranges: |
|
|
79
|
+
range-unit *( OWS "," OWS range-unit )
|
|
80
|
+
asctime-date: |
|
|
81
|
+
day-name SP date3 SP time-of-day SP year
|
|
82
|
+
auth-param: |
|
|
83
|
+
token BWS "=" BWS ( token / quoted-string )
|
|
84
|
+
auth-scheme: token
|
|
85
|
+
challenge: |
|
|
86
|
+
auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS "," OWS auth-param ) ] ) ]
|
|
87
|
+
codings: |
|
|
88
|
+
content-coding / "identity" / "*"
|
|
89
|
+
comment: |
|
|
90
|
+
"(" *( ctext / quoted-pair / comment ) ")"
|
|
91
|
+
complete-length: 1*DIGIT
|
|
92
|
+
connection-option: token
|
|
93
|
+
content-coding: token
|
|
94
|
+
credentials: |
|
|
95
|
+
auth-scheme [ 1*SP ( token68 / [ auth-param *( OWS "," OWS auth-param ) ] ) ]
|
|
96
|
+
ctext: |
|
|
97
|
+
HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
|
|
98
|
+
date1: day SP month SP year
|
|
99
|
+
date2: day "-" month "-" 2DIGIT
|
|
100
|
+
date3: |
|
|
101
|
+
month SP ( 2DIGIT / ( SP DIGIT ) )
|
|
102
|
+
day: 2DIGIT
|
|
103
|
+
day-name: >
|
|
104
|
+
%x4D.6F.6E / %x54.75.65 / %x57.65.64 / %x54.68.75 / %x46.72.69 / %x53.61.74 /
|
|
105
|
+
%x53.75.6E
|
|
106
|
+
day-name-l: >
|
|
107
|
+
%x4D.6F.6E.64.61.79 / %x54.75.65.73.64.61.79 / %x57.65.64.6E.65.73.64.61.79 /
|
|
108
|
+
%x54.68.75.72.73.64.61.79 / %x46.72.69.64.61.79 / %x53.61.74.75.72.64.61.79 /
|
|
109
|
+
%x53.75.6E.64.61.79
|
|
110
|
+
delay-seconds: 1*DIGIT
|
|
111
|
+
entity-tag: |
|
|
112
|
+
[ weak ] opaque-tag
|
|
113
|
+
etagc: |
|
|
114
|
+
"!" / %x23-7E / obs-text
|
|
115
|
+
expectation: |
|
|
116
|
+
token [ "=" ( token / quoted-string ) parameters ]
|
|
117
|
+
field-content: |
|
|
118
|
+
field-vchar [ 1*( SP / HTAB / field-vchar ) field-vchar ]
|
|
119
|
+
field-name: token
|
|
120
|
+
field-value: '*field-content'
|
|
121
|
+
field-vchar: VCHAR / obs-text
|
|
122
|
+
first-pos: 1*DIGIT
|
|
123
|
+
hour: 2DIGIT
|
|
124
|
+
http-URI: |
|
|
125
|
+
"http://" authority path-abempty [ "?" query ]
|
|
126
|
+
https-URI: |
|
|
127
|
+
"https://" authority path-abempty [ "?" query ]
|
|
128
|
+
incl-range: |
|
|
129
|
+
first-pos "-" last-pos
|
|
130
|
+
int-range: |
|
|
131
|
+
first-pos "-" [ last-pos ]
|
|
132
|
+
language-range: <language-range, see [RFC4647], Section 2.1>
|
|
133
|
+
language-tag: <Language-Tag, see [RFC5646], Section 2.1>
|
|
134
|
+
last-pos: 1*DIGIT
|
|
135
|
+
mailbox: <mailbox, see [RFC5322], Section 3.4>
|
|
136
|
+
media-range: |
|
|
137
|
+
( "*/*" / ( type "/*" ) / ( type "/" subtype ) ) parameters
|
|
138
|
+
media-type: |
|
|
139
|
+
type "/" subtype parameters
|
|
140
|
+
method: token
|
|
141
|
+
minute: 2DIGIT
|
|
142
|
+
month: >
|
|
143
|
+
%x4A.61.6E / %x46.65.62 / %x4D.61.72 / %x41.70.72 / %x4D.61.79 / %x4A.75.6E /
|
|
144
|
+
%x4A.75.6C / %x41.75.67 / %x53.65.70 / %x4F.63.74 / %x4E.6F.76 / %x44.65.63
|
|
145
|
+
obs-date: |
|
|
146
|
+
rfc850-date / asctime-date
|
|
147
|
+
obs-text: '%x80-FF'
|
|
148
|
+
opaque-tag: |
|
|
149
|
+
DQUOTE *etagc DQUOTE
|
|
150
|
+
other-range: |
|
|
151
|
+
1*( %x21-2B / %x2D-7E )
|
|
152
|
+
parameter: |
|
|
153
|
+
parameter-name "=" parameter-value
|
|
154
|
+
parameter-name: token
|
|
155
|
+
parameter-value: |
|
|
156
|
+
( token / quoted-string )
|
|
157
|
+
parameters: |
|
|
158
|
+
*( OWS ";" OWS [ parameter ] )
|
|
159
|
+
partial-URI: |
|
|
160
|
+
relative-part [ "?" query ]
|
|
161
|
+
product: |
|
|
162
|
+
token [ "/" product-version ]
|
|
163
|
+
product-version: token
|
|
164
|
+
protocol: |
|
|
165
|
+
protocol-name [ "/" protocol-version ]
|
|
166
|
+
protocol-name: token
|
|
167
|
+
protocol-version: token
|
|
168
|
+
pseudonym: token
|
|
169
|
+
qdtext: |
|
|
170
|
+
HTAB / SP / "!" / %x23-5B / %x5D-7E / obs-text
|
|
171
|
+
quoted-pair: |
|
|
172
|
+
"\" ( HTAB / SP / VCHAR / obs-text )
|
|
173
|
+
quoted-string: |
|
|
174
|
+
DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
|
175
|
+
qvalue: |
|
|
176
|
+
( "0" [ "." *3DIGIT ] ) / ( "1" [ "." *3"0" ] )
|
|
177
|
+
range-resp: |
|
|
178
|
+
incl-range "/" ( complete-length / "*" )
|
|
179
|
+
range-set: |
|
|
180
|
+
range-spec *( OWS "," OWS range-spec )
|
|
181
|
+
range-spec: |
|
|
182
|
+
int-range / suffix-range / other-range
|
|
183
|
+
range-unit: token
|
|
184
|
+
ranges-specifier: |
|
|
185
|
+
range-unit "=" range-set
|
|
186
|
+
received-by: |
|
|
187
|
+
pseudonym [ ":" port ]
|
|
188
|
+
received-protocol: |
|
|
189
|
+
[ protocol-name "/" ] protocol-version
|
|
190
|
+
rfc850-date: |
|
|
191
|
+
day-name-l "," SP date2 SP time-of-day SP GMT
|
|
192
|
+
second: 2DIGIT
|
|
193
|
+
subtype: token
|
|
194
|
+
suffix-length: 1*DIGIT
|
|
195
|
+
suffix-range: |
|
|
196
|
+
"-" suffix-length
|
|
197
|
+
t-codings: |
|
|
198
|
+
"trailers" / ( transfer-coding [ weight ] )
|
|
199
|
+
tchar: >
|
|
200
|
+
"!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" /
|
|
201
|
+
"|" / "~" / DIGIT / ALPHA
|
|
202
|
+
time-of-day: |
|
|
203
|
+
hour ":" minute ":" second
|
|
204
|
+
token: 1*tchar
|
|
205
|
+
token68: |
|
|
206
|
+
1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
|
|
207
|
+
transfer-coding: |
|
|
208
|
+
token *( OWS ";" OWS transfer-parameter )
|
|
209
|
+
transfer-parameter: |
|
|
210
|
+
token BWS "=" BWS ( token / quoted-string )
|
|
211
|
+
type: token
|
|
212
|
+
unsatisfied-range: |
|
|
213
|
+
"*/" complete-length
|
|
214
|
+
uri-host: |
|
|
215
|
+
host
|
|
216
|
+
weak: '%x57.2F'
|
|
217
|
+
weight: |
|
|
218
|
+
OWS ";" OWS "q=" qvalue
|
|
219
|
+
year: 4DIGIT
|
|
220
|
+
# RFC 9112
|
|
221
|
+
HTTP-message: |
|
|
222
|
+
start-line CRLF *( field-line CRLF ) CRLF [ message-body ]
|
|
223
|
+
HTTP-name: '%x48.54.54.50'
|
|
224
|
+
HTTP-version: |
|
|
225
|
+
HTTP-name "/" DIGIT "." DIGIT
|
|
226
|
+
Transfer-Encoding: |
|
|
227
|
+
[ transfer-coding *( OWS "," OWS transfer-coding ) ]
|
|
228
|
+
absolute-form: absolute-URI
|
|
229
|
+
asterisk-form: '*'
|
|
230
|
+
authority-form: |
|
|
231
|
+
uri-host ":" port
|
|
232
|
+
chunk: |
|
|
233
|
+
chunk-size [ chunk-ext ] CRLF chunk-data CRLF
|
|
234
|
+
chunk-data: |
|
|
235
|
+
1*OCTET
|
|
236
|
+
chunk-ext: |
|
|
237
|
+
*( BWS ";" BWS chunk-ext-name [ BWS "=" BWS chunk-ext-val ] )
|
|
238
|
+
chunk-ext-name: token
|
|
239
|
+
chunk-ext-val: |
|
|
240
|
+
token / quoted-string
|
|
241
|
+
chunk-size: |
|
|
242
|
+
1*HEXDIG
|
|
243
|
+
chunked-body: |
|
|
244
|
+
*chunk last-chunk trailer-section CRLF
|
|
245
|
+
field-line: |
|
|
246
|
+
field-name ":" OWS field-value OWS
|
|
247
|
+
last-chunk: |
|
|
248
|
+
1*"0" [ chunk-ext ] CRLF
|
|
249
|
+
message-body: |
|
|
250
|
+
*OCTET
|
|
251
|
+
obs-fold: |
|
|
252
|
+
OWS CRLF RWS
|
|
253
|
+
origin-form: |
|
|
254
|
+
absolute-path [ "?" query ]
|
|
255
|
+
reason-phrase: |
|
|
256
|
+
1*( HTAB / SP / VCHAR / obs-text )
|
|
257
|
+
request-line: |
|
|
258
|
+
method SP request-target SP HTTP-version
|
|
259
|
+
request-target: |
|
|
260
|
+
origin-form / absolute-form / authority-form / asterisk-form
|
|
261
|
+
start-line: |
|
|
262
|
+
request-line / status-line
|
|
263
|
+
status-code: 3DIGIT
|
|
264
|
+
status-line: |
|
|
265
|
+
HTTP-version SP status-code SP [ reason-phrase ]
|
|
266
|
+
trailer-section: |
|
|
267
|
+
*( field-line CRLF )
|
package/index.js
CHANGED
|
@@ -4,7 +4,8 @@ const { recursiveCompile } = require('url-templates');
|
|
|
4
4
|
const patterns = new Map();
|
|
5
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
|
+
implemented_schemes: '(?:[hH][tT][tT][pP][sS]?|[wW][sS][sS]?|[fF][iI][lL][eE]):',
|
|
8
|
+
scheme: '(?!{implemented_schemes})[a-zA-Z][a-zA-Z0-9+.-]*',
|
|
8
9
|
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
10
|
IP_literal: '\\[(?:{IPv6address}|{IPvFuture})\\]',
|
|
10
11
|
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})?::)',
|
|
@@ -34,8 +35,7 @@ const uriRules = {
|
|
|
34
35
|
authority: '(?:{userinfo}@)?{host}(?::{port})?',
|
|
35
36
|
userinfo: '(?:{unreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
36
37
|
host: '(?:{IP_literal}|{IPv4address}|{reg_name})',
|
|
37
|
-
|
|
38
|
-
reg_name: "(?:(?=.{1,255}(?:[:/?#]|$))(?:{a_label})(?:\\.{a_label})*)",
|
|
38
|
+
reg_name: '(?:{unreserved}|{pct_encoded}|{sub_delims})*',
|
|
39
39
|
path: '(?:{path_abempty}|{path_absolute}|{path_noscheme}|{path_rootless}|{path_empty})',
|
|
40
40
|
path_abempty: '(?:\/{segment})*',
|
|
41
41
|
path_absolute: '\/(?:{segment_nz}(?:\/{segment})*)?',
|
|
@@ -60,9 +60,7 @@ const iriRules = {
|
|
|
60
60
|
iauthority: '(?:{iuserinfo}@)?{ihost}(?::{port})?',
|
|
61
61
|
iuserinfo: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
62
62
|
ihost: '(?:{IP_literal}|{IPv4address}|{ireg_name})',
|
|
63
|
-
|
|
64
|
-
u_label: '(?:{uchar})(?:(?:{uchar}|-){0,61}(?:{uchar}))?',
|
|
65
|
-
ireg_name: '(?:(?=.{1,255}(?:[:/?#]|$))(?:{u_label})(?:{separator}(?:{u_label}))*)',
|
|
63
|
+
ireg_name: '(?:{iunreserved}|{pct_encoded}|{sub_delims})*',
|
|
66
64
|
ipath: '(?:{ipath_abempty}|{ipath_absolute}|{ipath_noscheme}|{ipath_rootless}|{ipath_empty})',
|
|
67
65
|
ipath_empty: '',
|
|
68
66
|
ipath_rootless: '{isegment_nz}(?:\/{isegment})*',
|
|
@@ -77,9 +75,18 @@ const iriRules = {
|
|
|
77
75
|
ipchar: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:|@)',
|
|
78
76
|
iunreserved: '(?:{unreserved}|{ucschar})',
|
|
79
77
|
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}]',
|
|
81
78
|
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}]',
|
|
82
79
|
};
|
|
80
|
+
// scheme specific URI reg_name and IRI ireg_name
|
|
81
|
+
const schemeSpecificRules = {
|
|
82
|
+
scheme: '(?:https?|wss?|file)',
|
|
83
|
+
reg_name: '(?:(?=.{1,255}(?:[:/?#]|$))(?:{a_label})(?:\\.{a_label})*)',
|
|
84
|
+
a_label: '(?:{alpha_digit})(?:(?:{alpha_digit}|-){0,61}(?:{alpha_digit}))?',
|
|
85
|
+
ireg_name: '(?:(?=.{1,255}(?:[:/?#]|$))(?:{u_label})(?:{u_separator}(?:{u_label}))*)',
|
|
86
|
+
u_label: '(?:{u_char})(?:(?:{u_char}|-){0,61}(?:{u_char}))?',
|
|
87
|
+
u_separator: '[\\x2E\\uFF0E\\u3002\\uFF61]',
|
|
88
|
+
u_char: '[\\p{L}\\p{N}\\p{Mn}\\p{Mc}\\u200C\\u200D\\u00B7\\u0375\\u30FB\\u05F3\\u05F4]',
|
|
89
|
+
};
|
|
83
90
|
// pattern RFC group names
|
|
84
91
|
const groupNames = {
|
|
85
92
|
scheme: 'scheme',
|
|
@@ -106,21 +113,26 @@ const groupNames = {
|
|
|
106
113
|
ipath_empty: 'path',
|
|
107
114
|
};
|
|
108
115
|
// all rules into one map
|
|
109
|
-
const
|
|
110
|
-
const
|
|
116
|
+
const isSpecificScheme = (string) => new RegExp('^' + schemeSpecificRules.scheme + ':').test(string);
|
|
117
|
+
const rules = (specific) => specific ? Object.assign({}, commonRules, uriRules, iriRules, schemeSpecificRules) : Object.assign({}, commonRules, uriRules, iriRules);
|
|
111
118
|
// parse (slower, it uses regex.exec and includes named capture groups)
|
|
112
119
|
const parse = (string, rule) => {
|
|
113
120
|
if (typeof string !== 'string') throw new TypeError(`Invalid ${rule.replace('_', '-')} type: must be a string.`);
|
|
114
|
-
|
|
115
|
-
const
|
|
121
|
+
const specific = isSpecificScheme(string) ? 's' : '';
|
|
122
|
+
const addNames = (key) => (groupNames[key] ? `(?<${groupNames[key]}>${rules(specific)[key]})` : rules(specific)[key]);
|
|
123
|
+
const ruleId = '_' + specific + rule;
|
|
124
|
+
if (!patterns.has(ruleId)) patterns.set(ruleId, new RegExp(`^${recursiveCompile(rules(specific), rule, addNames)}$`, 'u'));
|
|
125
|
+
const match = patterns.get(ruleId).exec(string);
|
|
116
126
|
if (match) return match.groups;
|
|
117
127
|
throw new SyntaxError(`Invalid ${rule.replace('_', '-')}: ${string}`);
|
|
118
128
|
};
|
|
119
129
|
// validate (faster, it uses regex.test and does not include named capture groups)
|
|
120
130
|
const validate = (string, rule) => {
|
|
121
131
|
if (typeof string !== 'string') throw new TypeError(`Invalid ${rule.replace('_', '-')} type: must be a string.`);
|
|
122
|
-
|
|
123
|
-
|
|
132
|
+
const specific = isSpecificScheme(string) ? 's' : '';
|
|
133
|
+
const ruleId = specific + rule;
|
|
134
|
+
if (!patterns.has(ruleId)) patterns.set(ruleId, new RegExp(`^${recursiveCompile(rules(specific), rule)}$`, 'u'));
|
|
135
|
+
if (patterns.get(ruleId).test(string)) return true;
|
|
124
136
|
throw new SyntaxError(`Invalid ${rule.replace('_', '-')}: ${string}`);
|
|
125
137
|
};
|
|
126
138
|
// compose as per RFC 3986 Section 5.3 (component recomposition)
|
|
@@ -271,8 +283,9 @@ module.exports = {
|
|
|
271
283
|
parseIri: (string) => parse(string, 'IRI'),
|
|
272
284
|
parseIriReference: (string) => parse(string, 'IRI_reference'),
|
|
273
285
|
parseAbsoluteIri: (string) => parse(string, 'absolute_IRI'),
|
|
274
|
-
resolveReference,
|
|
286
|
+
resolveReference,
|
|
275
287
|
normalizeReference: (string) => string, // not done yet
|
|
276
288
|
toAbsoluteReference: (string) => resolveReference('', string),
|
|
277
289
|
toRelativeReference,
|
|
278
290
|
};
|
|
291
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "identifier-js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "A RFC3986 / RFC3987 compliant fast parser/validator/resolver/composer for NodeJS and browser.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"IRI",
|
|
@@ -12,10 +12,8 @@
|
|
|
12
12
|
"uuid",
|
|
13
13
|
"parser",
|
|
14
14
|
"validator",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"RFC",
|
|
18
|
-
"3986"
|
|
15
|
+
"RFC3986",
|
|
16
|
+
"RFC3987"
|
|
19
17
|
],
|
|
20
18
|
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
21
19
|
"bugs": {
|
package/readme.md
CHANGED
|
@@ -8,7 +8,14 @@ description: A RFC3986 / RFC3987 compliant fast parser/validator/resolver/compos
|
|
|
8
8
|
|
|
9
9
|
## Overview
|
|
10
10
|
|
|
11
|
-
A fully RFC [3986](https://datatracker.ietf.org/doc/html/rfc3986.html)/[3897](https://datatracker.ietf.org/doc/html/rfc3987.html) compliant URI/IRI parser, validator, resolver and composer, along with other identifier utilities.
|
|
11
|
+
A fully RFC [3986](https://datatracker.ietf.org/doc/html/rfc3986.html)/[3897](https://datatracker.ietf.org/doc/html/rfc3987.html) compliant URI/IRI parser, validator, resolver and composer, along with other identifier utilities. This library implements the following [IANA registered](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml) schemes:
|
|
12
|
+
- `http` defined by [RFC9110, Section 4.2.1](https://datatracker.ietf.org/doc/html/rfc9110#section-4.2.1)
|
|
13
|
+
- `https` defined by [RFC9110, Section 4.2.2](https://datatracker.ietf.org/doc/html/rfc9110#section-4.2.2)
|
|
14
|
+
- `ws` defined by [RFC6455, Section 3](https://datatracker.ietf.org/doc/html/rfc6455#section-3)
|
|
15
|
+
- `wss` defined by [RFC6455, Section 3](https://datatracker.ietf.org/doc/html/rfc6455#section-3)
|
|
16
|
+
- `file` defined by [RFC8089, Section 2](https://datatracker.ietf.org/doc/html/rfc8089#section-2)
|
|
17
|
+
|
|
18
|
+
Other schemes that are `IANA registered` schemes and compliant with the generic `URI` or `IRI` syntax are also supported. As for the identifiers that are not `IANA registered`, but compliant with the generic `URI` or `IRI` syntax, the preferred schemes are `uri` and respectively `iri`.
|
|
12
19
|
|
|
13
20
|
## Install
|
|
14
21
|
|
|
@@ -81,8 +88,8 @@ Resolve a reference against a base identifier:
|
|
|
81
88
|
- resolveReference: (reference: string, base: string, strict?: boolean, returnParts?: boolean) => string
|
|
82
89
|
|
|
83
90
|
**Note:**
|
|
84
|
-
- strict enables strict resolution behavior.
|
|
85
|
-
- returnParts returns structured components instead of a string.
|
|
91
|
+
- strict (default: `true`) enables strict resolution behavior.
|
|
92
|
+
- returnParts (default: `false`) returns structured components instead of a string.
|
|
86
93
|
|
|
87
94
|
Convert a reference into absolute form:
|
|
88
95
|
- toAbsoluteReference: (reference: string) => string
|
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import id from '../index.js';
|
|
3
|
+
|
|
4
|
+
describe('isUri with hostnames', () => {
|
|
5
|
+
test('Valid character ! (sub-delims) in uri reg_name', () => {
|
|
6
|
+
expect(id.isUri('uri://exa!mple')).to.equal(true);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test('Valid character & (sub-delims) in uri reg_name', () => {
|
|
10
|
+
expect(id.isUri('uri://exa&mple')).to.equal(true);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('Valid character $ (sub-delims) in uri reg_name', () => {
|
|
14
|
+
expect(id.isUri('uri://exa$mple')).to.equal(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("Valid character ' (sub-delims) in uri reg_name", () => {
|
|
18
|
+
expect(id.isUri("uri://exa'mple")).to.equal(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('Valid character ( (sub-delims) in uri reg_name', () => {
|
|
22
|
+
expect(id.isUri('uri://exa(mple')).to.equal(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('Valid character ) (sub-delims) in uri reg_name', () => {
|
|
26
|
+
expect(id.isUri('uri://exa)mple')).to.equal(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Valid character * (sub-delims) in uri reg_name', () => {
|
|
30
|
+
expect(id.isUri('uri://exa*mple')).to.equal(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('Valid character + (sub-delims) in uri reg_name', () => {
|
|
34
|
+
expect(id.isUri('uri://exa+mple')).to.equal(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Valid character , (sub-delims) in uri reg_name', () => {
|
|
38
|
+
expect(id.isUri('uri://exa,mple')).to.equal(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Valid character ; (sub-delims) in uri reg_name', () => {
|
|
42
|
+
expect(id.isUri('uri://exa;mple')).to.equal(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('Valid character = (sub-delims) in uri reg_name', () => {
|
|
46
|
+
expect(id.isUri('uri://exa=mple')).to.equal(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('Valid character . multiple times (unreserved) in uri reg_name', () => {
|
|
50
|
+
expect(id.isUri('uri://exa..mple')).to.equal(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('Valid character ~ (unreserved) in uri reg_name', () => {
|
|
54
|
+
expect(id.isUri('uri://exa~mple')).to.equal(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('Valid character _ (unreserved) in uri reg_name', () => {
|
|
58
|
+
expect(id.isUri('uri://exa_mple')).to.equal(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('Valid character %20 (pct-encoded) in uri reg_name', () => {
|
|
62
|
+
expect(id.isUri('uri://exa%20mple')).to.equal(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('Invalid %GG (pct-encoded) in uri reg_name', () => {
|
|
66
|
+
expect(() => id.isUri('uri://exa%GGmple')).to.throw(Error, 'Invalid URI: uri://exa%GGmple');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Valid label - alphanumeric', () => {
|
|
70
|
+
expect(id.isUri('https://example')).to.equal(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('Valid label - hyphen in middle', () => {
|
|
74
|
+
expect(id.isUri('https://exa-mple')).to.equal(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('Invalid label - hyphen at start', () => {
|
|
78
|
+
expect(() => id.isUri('https://-example')).to.throw(Error, 'Invalid URI: https://-example');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('Invalid label - hyphen at end', () => {
|
|
82
|
+
expect(() => id.isUri('https://example-')).to.throw(Error, 'Invalid URI: https://example-');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('Valid multiple labels', () => {
|
|
86
|
+
expect(id.isUri('https://example-domain.com')).to.equal(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('Invalid - leading dot', () => {
|
|
90
|
+
expect(() => id.isUri('https://.example.com')).to.throw(Error, 'Invalid URI: https://.example.com');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('Invalid - trailing dot', () => {
|
|
94
|
+
expect(() => id.isUri('https://example.com.')).to.throw(Error, 'Invalid URI: https://example.com.');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('Invalid - consecutive dots', () => {
|
|
98
|
+
expect(() => id.isUri('https://example..com')).to.throw(Error, 'Invalid URI: https://example..com');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('Invalid - unicode character', () => {
|
|
102
|
+
expect(() => id.isUri('https://exämple')).to.throw(Error, 'Invalid URI: https://exämple');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('Invalid - % character (pct_encoded) in http(s) reg_name', () => {
|
|
106
|
+
expect(() => id.isUri('https://exa%20mple')).to.throw(Error, 'Invalid URI: https://exa%20mple');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('Invalid - % character (pct_encoded) in ws(s) reg_name', () => {
|
|
110
|
+
expect(() => id.isUri('wss://exa%20mple')).to.throw(Error, 'Invalid URI: wss://exa%20mple');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('Invalid - % character (pct_encoded) in file reg_name', () => {
|
|
114
|
+
expect(() => id.isUri('file://exa%20mple')).to.throw(Error, 'Invalid URI: file://exa%20mple');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('Invalid - ~ character (unreserved) in http(s) reg_name', () => {
|
|
118
|
+
expect(() => id.isUri('https://exa~mple')).to.throw(Error, 'Invalid URI: https://exa~mple');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('Invalid - ~ character (unreserved) in ws(s) reg_name', () => {
|
|
122
|
+
expect(() => id.isUri('wss://exa~mple')).to.throw(Error, 'Invalid URI: wss://exa~mple');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('Invalid - ~ character (unreserved) in file reg_name', () => {
|
|
126
|
+
expect(() => id.isUri('file://exa~mple')).to.throw(Error, 'Invalid URI: file://exa~mple');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('Invalid - _ character (unreserved) in http(s) reg_name', () => {
|
|
130
|
+
expect(() => id.isUri('https://exa_mple')).to.throw(Error, 'Invalid URI: https://exa_mple');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('Invalid - _ character (unreserved) in ws(s) reg_name', () => {
|
|
134
|
+
expect(() => id.isUri('wss://exa_mple')).to.throw(Error, 'Invalid URI: wss://exa_mple');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('Invalid - _ character (unreserved) in file reg_name', () => {
|
|
138
|
+
expect(() => id.isUri('file://exa_mple')).to.throw(Error, 'Invalid URI: file://exa_mple');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('Invalid - ! character (sub-delims) in http(s) reg_name', () => {
|
|
142
|
+
expect(() => id.isUri('https://exa!mple')).to.throw(Error, 'Invalid URI: https://exa!mple');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test('Invalid - ! character (sub-delims) in ws(s) reg_name', () => {
|
|
146
|
+
expect(() => id.isUri('wss://exa!mple')).to.throw(Error, 'Invalid URI: wss://exa!mple');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test('Invalid - ! character (sub-delims) in file reg_name', () => {
|
|
150
|
+
expect(() => id.isUri('file://exa!mple')).to.throw(Error, 'Invalid URI: file://exa!mple');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('Invalid - & character (sub-delims) in http(s) reg_name', () => {
|
|
154
|
+
expect(() => id.isUri('https://exa&mple')).to.throw(Error, 'Invalid URI: https://exa&mple');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('Invalid - & character (sub-delims) in ws(s) reg_name', () => {
|
|
158
|
+
expect(() => id.isUri('wss://exa&mple')).to.throw(Error, 'Invalid URI: wss://exa&mple');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('Invalid - & character (sub-delims) in file reg_name', () => {
|
|
162
|
+
expect(() => id.isUri('file://exa&mple')).to.throw(Error, 'Invalid URI: file://exa&mple');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test('Invalid - $ character (sub-delims) in http(s) reg_name', () => {
|
|
166
|
+
expect(() => id.isUri('https://exa$mple')).to.throw(Error, 'Invalid URI: https://exa$mple');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('Invalid - $ character (sub-delims) in ws(s) reg_name', () => {
|
|
170
|
+
expect(() => id.isUri('wss://exa$mple')).to.throw(Error, 'Invalid URI: wss://exa$mple');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test('Invalid - $ character (sub-delims) in file reg_name', () => {
|
|
174
|
+
expect(() => id.isUri('file://exa$mple')).to.throw(Error, 'Invalid URI: file://exa$mple');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("Invalid - ' character (sub-delims) in http(s) reg_name", () => {
|
|
178
|
+
expect(() => id.isUri("https://exa'mple")).to.throw(Error, "Invalid URI: https://exa'mple");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("Invalid - ' character (sub-delims) in ws(s) reg_name", () => {
|
|
182
|
+
expect(() => id.isUri("wss://exa'mple")).to.throw(Error, "Invalid URI: wss://exa'mple");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("Invalid - ' character (sub-delims) in file reg_name", () => {
|
|
186
|
+
expect(() => id.isUri("file://exa'mple")).to.throw(Error, "Invalid URI: file://exa'mple");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('Invalid - ( character (sub-delims) in http(s) reg_name', () => {
|
|
190
|
+
expect(() => id.isUri('https://exa(mple')).to.throw(Error, 'Invalid URI: https://exa(mple');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Invalid - ( character (sub-delims) in ws(s) reg_name', () => {
|
|
194
|
+
expect(() => id.isUri('wss://exa(mple')).to.throw(Error, 'Invalid URI: wss://exa(mple');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test('Invalid - ( character (sub-delims) in file reg_name', () => {
|
|
198
|
+
expect(() => id.isUri('file://exa(mple')).to.throw(Error, 'Invalid URI: file://exa(mple');
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test('Invalid - ) character (sub-delims) in http(s) reg_name', () => {
|
|
202
|
+
expect(() => id.isUri('https://exa)mple')).to.throw(Error, 'Invalid URI: https://exa)mple');
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('Invalid - ) character (sub-delims) in ws(s) reg_name', () => {
|
|
206
|
+
expect(() => id.isUri('wss://exa)mple')).to.throw(Error, 'Invalid URI: wss://exa)mple');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('Invalid - ) character (sub-delims) in file reg_name', () => {
|
|
210
|
+
expect(() => id.isUri('file://exa)mple')).to.throw(Error, 'Invalid URI: file://exa)mple');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('Invalid - * character (sub-delims) in http(s) reg_name', () => {
|
|
214
|
+
expect(() => id.isUri('https://exa*mple')).to.throw(Error, 'Invalid URI: https://exa*mple');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('Invalid - * character (sub-delims) in ws(s) reg_name', () => {
|
|
218
|
+
expect(() => id.isUri('wss://exa*mple')).to.throw(Error, 'Invalid URI: wss://exa*mple');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('Invalid - * character (sub-delims) in file reg_name', () => {
|
|
222
|
+
expect(() => id.isUri('file://exa*mple')).to.throw(Error, 'Invalid URI: file://exa*mple');
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('Invalid - + character (sub-delims) in http(s) reg_name', () => {
|
|
226
|
+
expect(() => id.isUri('https://exa+mple')).to.throw(Error, 'Invalid URI: https://exa+mple');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test('Invalid - + character (sub-delims) in ws(s) reg_name', () => {
|
|
230
|
+
expect(() => id.isUri('wss://exa+mple')).to.throw(Error, 'Invalid URI: wss://exa+mple');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test('Invalid - + character (sub-delims) in file reg_name', () => {
|
|
234
|
+
expect(() => id.isUri('file://exa+mple')).to.throw(Error, 'Invalid URI: file://exa+mple');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('Invalid - , character (sub-delims) in http(s) reg_name', () => {
|
|
238
|
+
expect(() => id.isUri('https://exa,mple')).to.throw(Error, 'Invalid URI: https://exa,mple');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('Invalid - , character (sub-delims) in ws(s) reg_name', () => {
|
|
242
|
+
expect(() => id.isUri('wss://exa,mple')).to.throw(Error, 'Invalid URI: wss://exa,mple');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('Invalid - , character (sub-delims) in file reg_name', () => {
|
|
246
|
+
expect(() => id.isUri('file://exa,mple')).to.throw(Error, 'Invalid URI: file://exa,mple');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test('Invalid - ; character (sub-delims) in http(s) reg_name', () => {
|
|
250
|
+
expect(() => id.isUri('https://exa;mple')).to.throw(Error, 'Invalid URI: https://exa;mple');
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('Invalid - ; character (sub-delims) in ws(s) reg_name', () => {
|
|
254
|
+
expect(() => id.isUri('wss://exa;mple')).to.throw(Error, 'Invalid URI: wss://exa;mple');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test('Invalid - ; character (sub-delims) in file reg_name', () => {
|
|
258
|
+
expect(() => id.isUri('file://exa;mple')).to.throw(Error, 'Invalid URI: file://exa;mple');
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
test('Invalid - = character (sub-delims) in http(s) reg_name', () => {
|
|
262
|
+
expect(() => id.isUri('https://exa=mple')).to.throw(Error, 'Invalid URI: https://exa=mple');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test('Invalid - = character (sub-delims) in ws(s) reg_name', () => {
|
|
266
|
+
expect(() => id.isUri('wss://exa=mple')).to.throw(Error, 'Invalid URI: wss://exa=mple');
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('Invalid - = character (sub-delims) in file reg_name', () => {
|
|
270
|
+
expect(() => id.isUri('file://exa=mple')).to.throw(Error, 'Invalid URI: file://exa=mple');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('isIri with hostnames', () => {
|
|
275
|
+
test('Valid character ! (sub-delims) in iri reg_name', () => {
|
|
276
|
+
expect(id.isIri('iri://exa!mple')).to.equal(true);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('Valid character & (sub-delims) in iri reg_name', () => {
|
|
280
|
+
expect(id.isIri('iri://exa&mple')).to.equal(true);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test('Valid character $ (sub-delims) in iri reg_name', () => {
|
|
284
|
+
expect(id.isIri('iri://exa$mple')).to.equal(true);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("Valid character ' (sub-delims) in iri reg_name", () => {
|
|
288
|
+
expect(id.isIri("iri://exa'mple")).to.equal(true);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test('Valid character ( (sub-delims) in iri reg_name', () => {
|
|
292
|
+
expect(id.isIri('iri://exa(mple')).to.equal(true);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('Valid character ) (sub-delims) in iri reg_name', () => {
|
|
296
|
+
expect(id.isIri('iri://exa)mple')).to.equal(true);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('Valid character * (sub-delims) in iri reg_name', () => {
|
|
300
|
+
expect(id.isIri('iri://exa*mple')).to.equal(true);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test('Valid character + (sub-delims) in iri reg_name', () => {
|
|
304
|
+
expect(id.isIri('iri://exa+mple')).to.equal(true);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('Valid character , (sub-delims) in iri reg_name', () => {
|
|
308
|
+
expect(id.isIri('iri://exa,mple')).to.equal(true);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test('Valid character ; (sub-delims) in iri reg_name', () => {
|
|
312
|
+
expect(id.isIri('iri://exa;mple')).to.equal(true);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test('Valid character = (sub-delims) in iri reg_name', () => {
|
|
316
|
+
expect(id.isIri('iri://exa=mple')).to.equal(true);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('Valid character . multiple times (unreserved) in iri reg_name', () => {
|
|
320
|
+
expect(id.isIri('iri://exa..mple')).to.equal(true);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test('Valid character _ (unreserved) in iri reg_name', () => {
|
|
324
|
+
expect(id.isIri('iri://exa_mple')).to.equal(true);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('Valid character %20 (pct-encoded) in iri reg_name', () => {
|
|
328
|
+
expect(id.isIri('iri://exa%20mple')).to.equal(true);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('Invalid %GG (invalid pct-encoded) in iri reg_name', () => {
|
|
332
|
+
expect(() => id.isIri('iri://exa%GGmple')).to.throw(Error, 'Invalid IRI: iri://exa%GGmple');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test('Valid label - alphanumeric', () => {
|
|
336
|
+
expect(id.isIri('https://example')).to.equal(true);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('Valid label - hyphen in middle', () => {
|
|
340
|
+
expect(id.isIri('https://exa-mple')).to.equal(true);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
test('Invalid label - hyphen at start', () => {
|
|
344
|
+
expect(() => id.isIri('https://-example')).to.throw(Error, 'Invalid IRI: https://-example');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test('Invalid label - hyphen at end', () => {
|
|
348
|
+
expect(() => id.isIri('https://example-')).to.throw(Error, 'Invalid IRI: https://example-');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('Valid unicode label - Latin extended', () => {
|
|
352
|
+
expect(id.isIri('https://exämple')).to.equal(true);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
test('Valid unicode label - Chinese', () => {
|
|
356
|
+
expect(id.isIri('https://例子')).to.equal(true);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test('Valid unicode label - Hindi', () => {
|
|
360
|
+
expect(id.isIri('https://उदाहरण')).to.equal(true);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test('Valid unicode label - Japanese', () => {
|
|
364
|
+
expect(id.isIri('https://例え.テスト')).to.equal(true);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('Valid label with middle dot', () => {
|
|
368
|
+
expect(id.isIri('https://exa·mple')).to.equal(true);
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test('Invalid - leading dot', () => {
|
|
372
|
+
expect(() => id.isIri('https://.example')).to.throw(Error, 'Invalid IRI: https://.example');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('Invalid - trailing dot', () => {
|
|
376
|
+
expect(() => id.isIri('https://example.')).to.throw(Error, 'Invalid IRI: https://example.');
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test('Invalid - consecutive dots', () => {
|
|
380
|
+
expect(() => id.isIri('https://example..test')).to.throw(Error, 'Invalid IRI: https://example..test');
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
test('Invalid - emoji in label', () => {
|
|
384
|
+
expect(() => id.isIri('https://example😀')).to.throw(Error, 'Invalid IRI: https://example😀');
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
test('Invalid - % character (pct_encoded) in http(s) reg_name', () => {
|
|
388
|
+
expect(() => id.isIri('https://exa%20mple')).to.throw(Error, 'Invalid IRI: https://exa%20mple');
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('Invalid - % character (pct_encoded) in ws(s) reg_name', () => {
|
|
392
|
+
expect(() => id.isIri('wss://exa%20mple')).to.throw(Error, 'Invalid IRI: wss://exa%20mple');
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test('Invalid - % character (pct_encoded) in file reg_name', () => {
|
|
396
|
+
expect(() => id.isIri('file://exa%20mple')).to.throw(Error, 'Invalid IRI: file://exa%20mple');
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('Invalid - ~ character (unreserved) in http(s) reg_name', () => {
|
|
400
|
+
expect(() => id.isIri('https://exa~mple')).to.throw(Error, 'Invalid IRI: https://exa~mple');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('Invalid - ~ character (unreserved) in ws(s) reg_name', () => {
|
|
404
|
+
expect(() => id.isIri('wss://exa~mple')).to.throw(Error, 'Invalid IRI: wss://exa~mple');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
test('Invalid - ~ character (unreserved) in file reg_name', () => {
|
|
408
|
+
expect(() => id.isIri('file://exa~mple')).to.throw(Error, 'Invalid IRI: file://exa~mple');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
test('Invalid - _ character (unreserved) in http(s) reg_name', () => {
|
|
412
|
+
expect(() => id.isIri('https://exa_mple')).to.throw(Error, 'Invalid IRI: https://exa_mple');
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test('Invalid - _ character (unreserved) in ws(s) reg_name', () => {
|
|
416
|
+
expect(() => id.isIri('wss://exa_mple')).to.throw(Error, 'Invalid IRI: wss://exa_mple');
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test('Invalid - _ character (unreserved) in file reg_name', () => {
|
|
420
|
+
expect(() => id.isIri('file://exa_mple')).to.throw(Error, 'Invalid IRI: file://exa_mple');
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
test('Invalid - ! character (sub-delims) in http(s) reg_name', () => {
|
|
424
|
+
expect(() => id.isIri('https://exa!mple')).to.throw(Error, 'Invalid IRI: https://exa!mple');
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test('Invalid - ! character (sub-delims) in ws(s) reg_name', () => {
|
|
428
|
+
expect(() => id.isIri('wss://exa!mple')).to.throw(Error, 'Invalid IRI: wss://exa!mple');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('Invalid - ! character (sub-delims) in file reg_name', () => {
|
|
432
|
+
expect(() => id.isIri('file://exa!mple')).to.throw(Error, 'Invalid IRI: file://exa!mple');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
test('Invalid - & character (sub-delims) in http(s) reg_name', () => {
|
|
436
|
+
expect(() => id.isIri('https://exa&mple')).to.throw(Error, 'Invalid IRI: https://exa&mple');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test('Invalid - & character (sub-delims) in ws(s) reg_name', () => {
|
|
440
|
+
expect(() => id.isIri('wss://exa&mple')).to.throw(Error, 'Invalid IRI: wss://exa&mple');
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
test('Invalid - & character (sub-delims) in file reg_name', () => {
|
|
444
|
+
expect(() => id.isIri('file://exa&mple')).to.throw(Error, 'Invalid IRI: file://exa&mple');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test('Invalid - $ character (sub-delims) in http(s) reg_name', () => {
|
|
448
|
+
expect(() => id.isIri('https://exa$mple')).to.throw(Error, 'Invalid IRI: https://exa$mple');
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test('Invalid - $ character (sub-delims) in ws(s) reg_name', () => {
|
|
452
|
+
expect(() => id.isIri('wss://exa$mple')).to.throw(Error, 'Invalid IRI: wss://exa$mple');
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
test('Invalid - $ character (sub-delims) in file reg_name', () => {
|
|
456
|
+
expect(() => id.isIri('file://exa$mple')).to.throw(Error, 'Invalid IRI: file://exa$mple');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test("Invalid - ' character (sub-delims) in http(s) reg_name", () => {
|
|
460
|
+
expect(() => id.isIri("https://exa'mple")).to.throw(Error, "Invalid IRI: https://exa'mple");
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test("Invalid - ' character (sub-delims) in ws(s) reg_name", () => {
|
|
464
|
+
expect(() => id.isIri("wss://exa'mple")).to.throw(Error, "Invalid IRI: wss://exa'mple");
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
test("Invalid - ' character (sub-delims) in file reg_name", () => {
|
|
468
|
+
expect(() => id.isIri("file://exa'mple")).to.throw(Error, "Invalid IRI: file://exa'mple");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
test('Invalid - ( character (sub-delims) in http(s) reg_name', () => {
|
|
472
|
+
expect(() => id.isIri('https://exa(mple')).to.throw(Error, 'Invalid IRI: https://exa(mple');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test('Invalid - ( character (sub-delims) in ws(s) reg_name', () => {
|
|
476
|
+
expect(() => id.isIri('wss://exa(mple')).to.throw(Error, 'Invalid IRI: wss://exa(mple');
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
test('Invalid - ( character (sub-delims) in file reg_name', () => {
|
|
480
|
+
expect(() => id.isIri('file://exa(mple')).to.throw(Error, 'Invalid IRI: file://exa(mple');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test('Invalid - ) character (sub-delims) in http(s) reg_name', () => {
|
|
484
|
+
expect(() => id.isIri('https://exa)mple')).to.throw(Error, 'Invalid IRI: https://exa)mple');
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test('Invalid - ) character (sub-delims) in ws(s) reg_name', () => {
|
|
488
|
+
expect(() => id.isIri('wss://exa)mple')).to.throw(Error, 'Invalid IRI: wss://exa)mple');
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test('Invalid - ) character (sub-delims) in file reg_name', () => {
|
|
492
|
+
expect(() => id.isIri('file://exa)mple')).to.throw(Error, 'Invalid IRI: file://exa)mple');
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
test('Invalid - * character (sub-delims) in http(s) reg_name', () => {
|
|
496
|
+
expect(() => id.isIri('https://exa*mple')).to.throw(Error, 'Invalid IRI: https://exa*mple');
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test('Invalid - * character (sub-delims) in ws(s) reg_name', () => {
|
|
500
|
+
expect(() => id.isIri('wss://exa*mple')).to.throw(Error, 'Invalid IRI: wss://exa*mple');
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
test('Invalid - * character (sub-delims) in file reg_name', () => {
|
|
504
|
+
expect(() => id.isIri('file://exa*mple')).to.throw(Error, 'Invalid IRI: file://exa*mple');
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test('Invalid - + character (sub-delims) in http(s) reg_name', () => {
|
|
508
|
+
expect(() => id.isIri('https://exa+mple')).to.throw(Error, 'Invalid IRI: https://exa+mple');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('Invalid - + character (sub-delims) in ws(s) reg_name', () => {
|
|
512
|
+
expect(() => id.isIri('wss://exa+mple')).to.throw(Error, 'Invalid IRI: wss://exa+mple');
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
test('Invalid - + character (sub-delims) in file reg_name', () => {
|
|
516
|
+
expect(() => id.isIri('file://exa+mple')).to.throw(Error, 'Invalid IRI: file://exa+mple');
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
test('Invalid - , character (sub-delims) in http(s) reg_name', () => {
|
|
520
|
+
expect(() => id.isIri('https://exa,mple')).to.throw(Error, 'Invalid IRI: https://exa,mple');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
test('Invalid - , character (sub-delims) in ws(s) reg_name', () => {
|
|
524
|
+
expect(() => id.isIri('wss://exa,mple')).to.throw(Error, 'Invalid IRI: wss://exa,mple');
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
test('Invalid - , character (sub-delims) in file reg_name', () => {
|
|
528
|
+
expect(() => id.isIri('file://exa,mple')).to.throw(Error, 'Invalid IRI: file://exa,mple');
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test('Invalid - ; character (sub-delims) in http(s) reg_name', () => {
|
|
532
|
+
expect(() => id.isIri('https://exa;mple')).to.throw(Error, 'Invalid IRI: https://exa;mple');
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('Invalid - ; character (sub-delims) in ws(s) reg_name', () => {
|
|
536
|
+
expect(() => id.isIri('wss://exa;mple')).to.throw(Error, 'Invalid IRI: wss://exa;mple');
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
test('Invalid - ; character (sub-delims) in file reg_name', () => {
|
|
540
|
+
expect(() => id.isIri('file://exa;mple')).to.throw(Error, 'Invalid IRI: file://exa;mple');
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
test('Invalid - = character (sub-delims) in http(s) reg_name', () => {
|
|
544
|
+
expect(() => id.isIri('https://exa=mple')).to.throw(Error, 'Invalid IRI: https://exa=mple');
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test('Invalid - = character (sub-delims) in ws(s) reg_name', () => {
|
|
548
|
+
expect(() => id.isIri('wss://exa=mple')).to.throw(Error, 'Invalid IRI: wss://exa=mple');
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
test('Invalid - = character (sub-delims) in file reg_name', () => {
|
|
552
|
+
expect(() => id.isIri('file://exa=mple')).to.throw(Error, 'Invalid IRI: file://exa=mple');
|
|
553
|
+
});
|
|
554
|
+
});
|
package/tests/vitest-setup.js
CHANGED
|
@@ -7,12 +7,13 @@ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
|
7
7
|
// Install vitest if not already installed
|
|
8
8
|
if (!packageJson.devDependencies?.vitest) {
|
|
9
9
|
console.log('Installing vitest...');
|
|
10
|
-
execSync('npm
|
|
10
|
+
execSync('npm i -D vitest', { stdio: 'inherit' });
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// Ensure the test script is set to vitest
|
|
14
14
|
if (packageJson.scripts?.test !== 'vitest') {
|
|
15
15
|
console.log('Updating test script to vitest...');
|
|
16
16
|
packageJson.scripts.test = 'vitest --watch=false';
|
|
17
|
+
packageJson.devDependencies = { vitest: '*' };
|
|
17
18
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
18
19
|
}
|
|
@@ -1,94 +0,0 @@
|
|
|
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
|
-
});
|
|
File without changes
|
|
File without changes
|