identifier-js 0.0.13 → 0.0.15
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 +24 -5
- package/normalization.md +299 -0
- package/package.json +41 -41
- package/readme.md +433 -64
- package/docs/rfc3986-rfc3987-abnf.yaml +0 -295
- package/docs/rfc9110-rfc9112-abnf.yaml +0 -267
- package/tests/normalize.test-off.js +0 -21
- package/tests/parse.test.js +0 -472
- package/tests/performance.js +0 -46
- package/tests/resolve.rfc.test.js +0 -63
- package/tests/resolve.test.js +0 -16
- package/tests/to-absolute.test.js +0 -28
- package/tests/to-relative.test.js +0 -50
- package/tests/validation-ip-port.test.js +0 -278
- package/tests/validation-scheme-specific.test.js +0 -604
- package/tests/validation.test.js +0 -278
- package/tests/vitest-setup.js +0 -19
package/readme.md
CHANGED
|
@@ -2,109 +2,478 @@
|
|
|
2
2
|
|
|
3
3
|
title: Identifier JS
|
|
4
4
|
|
|
5
|
-
description:
|
|
5
|
+
description: An RFC 3986 and RFC 3987 parser, validator, and reference resolver for Node.js and browser bundles.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
# Identifier JS
|
|
10
10
|
|
|
11
|
-
|
|
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)
|
|
11
|
+
`identifier-js` is a fully RFC [3986](https://www.rfc-editor.org/rfc/rfc3986) and RFC [3987](https://www.rfc-editor.org/rfc/rfc3987) compliant URI/IRI parser, validator, resolver, and composer. It provides:
|
|
17
12
|
|
|
18
|
-
|
|
13
|
+
- URI and IRI validation;
|
|
14
|
+
- parsed identifier components;
|
|
15
|
+
- RFC 3986 reference resolution and dot-segment removal;
|
|
16
|
+
- relative-reference generation with round-trip guarantees for supported forms;
|
|
17
|
+
- UUID and UUIDv4 lexical validation;
|
|
18
|
+
- lazily compiled and cached regular expressions.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
### Requirements
|
|
20
|
+
The package is synchronous, CommonJS, and supports Node.js 18 or newer. Browser use requires a bundler or runtime that supports CommonJS dependencies and Unicode regular expressions.
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
## Install
|
|
25
23
|
|
|
26
24
|
```bash title="console"
|
|
27
|
-
npm
|
|
25
|
+
npm install identifier-js
|
|
28
26
|
```
|
|
29
27
|
|
|
30
28
|
## API
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
Every validator returns `true` or throws at the first detected violation. Parsing and reference operations also throw when their input does not satisfy the required grammar.
|
|
31
|
+
|
|
32
|
+
### Validate URI syntax
|
|
33
|
+
|
|
34
|
+
Validate a complete URI, a URI reference, or an absolute URI without a fragment.
|
|
35
|
+
|
|
36
|
+
<details>
|
|
37
|
+
<summary><strong>API and examples</strong></summary>
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
isUri(value: string): true
|
|
41
|
+
isUriReference(value: string): true
|
|
42
|
+
isAbsoluteUri(value: string): true
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const { isUri, isUriReference, isAbsoluteUri } = require('identifier-js');
|
|
47
|
+
|
|
48
|
+
console.log(isUri('https://example.com/path?query#fragment')); // true
|
|
49
|
+
console.log(isUriReference('../asset?version=2')); // true
|
|
50
|
+
console.log(isAbsoluteUri('https://example.com/path?query')); // true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
An `absolute-URI` is the fragment-free grammar defined by RFC 3986. Use `isUri` when a fragment is allowed.
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
### Parse URI components
|
|
58
|
+
|
|
59
|
+
Parse URI syntax into scheme, authority, userinfo, host, port, path, query, and fragment components.
|
|
60
|
+
|
|
61
|
+
<details>
|
|
62
|
+
<summary><strong>API and examples</strong></summary>
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
parseUri(value: string): IdentifierComponents
|
|
66
|
+
parseUriReference(value: string): RelativeIdentifierComponents
|
|
67
|
+
parseAbsoluteUri(value: string): AbsoluteIdentifierComponents
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const { parseUri } = require('identifier-js');
|
|
72
|
+
|
|
73
|
+
console.log(parseUri('https://user@example.com:8443/a?b#c'));
|
|
74
|
+
// {
|
|
75
|
+
// scheme: 'https',
|
|
76
|
+
// authority: 'user@example.com:8443',
|
|
77
|
+
// userinfo: 'user',
|
|
78
|
+
// host: 'example.com',
|
|
79
|
+
// port: '8443',
|
|
80
|
+
// path: '/a',
|
|
81
|
+
// query: 'b',
|
|
82
|
+
// fragment: 'c'
|
|
83
|
+
// }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Absent optional components are returned as `undefined`. Present but empty query and fragment components are returned as empty strings.
|
|
87
|
+
|
|
88
|
+
</details>
|
|
89
|
+
|
|
90
|
+
### Validate IRI syntax
|
|
91
|
+
|
|
92
|
+
Validate Unicode-capable identifiers using RFC 3987 grammar.
|
|
93
|
+
|
|
94
|
+
<details>
|
|
95
|
+
<summary><strong>API and examples</strong></summary>
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
isIri(value: string): true
|
|
99
|
+
isIriReference(value: string): true
|
|
100
|
+
isAbsoluteIri(value: string): true
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```js
|
|
104
|
+
const { isIri, isIriReference } = require('identifier-js');
|
|
105
|
+
|
|
106
|
+
console.log(isIri('https://例え.テスト/資料?項目=値#概要')); // true
|
|
107
|
+
console.log(isIriReference('../résumé')); // true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
IRI support permits RFC 3987 Unicode ranges in applicable components. Complete IDNA processing and conversion to a transport URI are separate responsibilities.
|
|
111
|
+
|
|
112
|
+
</details>
|
|
113
|
+
|
|
114
|
+
### Parse IRI components
|
|
115
|
+
|
|
116
|
+
Parse an IRI while preserving its Unicode component values.
|
|
117
|
+
|
|
118
|
+
<details>
|
|
119
|
+
<summary><strong>API and examples</strong></summary>
|
|
33
120
|
|
|
34
|
-
|
|
121
|
+
```ts
|
|
122
|
+
parseIri(value: string): IdentifierComponents
|
|
123
|
+
parseIriReference(value: string): RelativeIdentifierComponents
|
|
124
|
+
parseAbsoluteIri(value: string): AbsoluteIdentifierComponents
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
const { parseIri } = require('identifier-js');
|
|
129
|
+
|
|
130
|
+
console.log(parseIri('https://usér@例え.テスト:8443/résumé?lang=fr#profil'));
|
|
131
|
+
// {
|
|
132
|
+
// scheme: 'https',
|
|
133
|
+
// authority: 'usér@例え.テスト:8443',
|
|
134
|
+
// userinfo: 'usér',
|
|
135
|
+
// host: '例え.テスト',
|
|
136
|
+
// port: '8443',
|
|
137
|
+
// path: '/résumé',
|
|
138
|
+
// query: 'lang=fr',
|
|
139
|
+
// fragment: 'profil'
|
|
140
|
+
// }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
</details>
|
|
144
|
+
|
|
145
|
+
### Resolve a reference
|
|
146
|
+
|
|
147
|
+
Resolve a URI or IRI reference against an absolute base using RFC 3986 §5.
|
|
148
|
+
|
|
149
|
+
<details>
|
|
150
|
+
<summary><strong>API and examples</strong></summary>
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
resolveReference(
|
|
154
|
+
reference: string,
|
|
155
|
+
base: string,
|
|
156
|
+
strict?: boolean,
|
|
157
|
+
returnParts?: boolean
|
|
158
|
+
): string | Record<string, string | undefined>
|
|
159
|
+
```
|
|
35
160
|
|
|
36
|
-
|
|
161
|
+
```js
|
|
162
|
+
const { resolveReference } = require('identifier-js');
|
|
37
163
|
|
|
38
|
-
|
|
39
|
-
|
|
164
|
+
console.log(resolveReference('../images/logo.svg', 'https://example.com/docs/api/page'));
|
|
165
|
+
// https://example.com/docs/images/logo.svg
|
|
40
166
|
|
|
41
|
-
|
|
42
|
-
|
|
167
|
+
console.log(resolveReference('?page=2', 'https://example.com/items?page=1#current'));
|
|
168
|
+
// https://example.com/items?page=2
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`strict` defaults to `true`. In strict mode, a reference containing a scheme replaces the base identifier even when both schemes are equal. `returnParts` defaults to `false`; when enabled at runtime, the function returns the resolved component object.
|
|
172
|
+
|
|
173
|
+
Empty authorities, queries, and fragments are preserved during recomposition.
|
|
43
174
|
|
|
44
|
-
|
|
45
|
-
- isAbsoluteUri: (value: string) => boolean
|
|
175
|
+
</details>
|
|
46
176
|
|
|
47
|
-
|
|
177
|
+
### Produce absolute and relative forms
|
|
48
178
|
|
|
49
|
-
|
|
50
|
-
- parseUri: (value: string) => IdentifierComponents
|
|
179
|
+
Remove a base fragment or derive a relative reference that resolves back to a target.
|
|
51
180
|
|
|
52
|
-
|
|
53
|
-
|
|
181
|
+
<details>
|
|
182
|
+
<summary><strong>API and examples</strong></summary>
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
toAbsoluteReference(reference: string): string
|
|
186
|
+
toRelativeReference(target: string, base: string): string
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
const { toAbsoluteReference, toRelativeReference } = require('identifier-js');
|
|
191
|
+
|
|
192
|
+
console.log(toAbsoluteReference('https://example.com/a/../b#section'));
|
|
193
|
+
// https://example.com/b
|
|
194
|
+
|
|
195
|
+
const target = 'https://example.com/docs/images/logo.svg';
|
|
196
|
+
const base = 'https://example.com/docs/api/page';
|
|
197
|
+
const relative = toRelativeReference(target, base);
|
|
198
|
+
console.log(relative); // ../images/logo.svg
|
|
199
|
+
```
|
|
54
200
|
|
|
55
|
-
|
|
56
|
-
- parseAbsoluteUri: (value: string) => AbsoluteIdentifierComponents
|
|
201
|
+
When no safe rootless relative form can round-trip to the target, `toRelativeReference` returns the absolute target. Different schemes or authorities also return the target unchanged.
|
|
57
202
|
|
|
58
|
-
|
|
203
|
+
</details>
|
|
59
204
|
|
|
60
|
-
|
|
205
|
+
### Normalization status
|
|
206
|
+
|
|
207
|
+
`normalizeReference` is reserved for future normalization policy and currently returns its input unchanged.
|
|
208
|
+
|
|
209
|
+
<details>
|
|
210
|
+
<summary><strong>Current behavior and research</strong></summary>
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
normalizeReference(reference: string): string
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
const { normalizeReference } = require('identifier-js');
|
|
218
|
+
|
|
219
|
+
console.log(normalizeReference('HTTP://Example.COM/a/../b'));
|
|
220
|
+
// HTTP://Example.COM/a/../b
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
URI/IRI normalization has multiple standards-defined levels and scheme-specific tradeoffs. See [`normalization.md`](normalization.md) for implementation options, unsafe transformations, suggested profiles, and acceptance scenarios.
|
|
224
|
+
|
|
225
|
+
</details>
|
|
226
|
+
|
|
227
|
+
### Validate UUID text
|
|
228
|
+
|
|
229
|
+
Validate the canonical UUID text shape or the stricter UUIDv4 version and variant fields.
|
|
230
|
+
|
|
231
|
+
<details>
|
|
232
|
+
<summary><strong>API and examples</strong></summary>
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
isUUID(value: string): true
|
|
236
|
+
isUUIDv4(value: string): true
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
const { isUUID, isUUIDv4 } = require('identifier-js');
|
|
241
|
+
|
|
242
|
+
console.log(isUUID('99c17cbb-656f-564a-940f-1a4568f03487')); // true
|
|
243
|
+
console.log(isUUIDv4('123e4567-e89b-42d3-9456-426614174000')); // true
|
|
244
|
+
```
|
|
61
245
|
|
|
62
|
-
|
|
63
|
-
- isIri: (value: string) => boolean
|
|
246
|
+
`isUUID` validates the `8-4-4-4-12` hexadecimal layout without restricting the version or variant fields. `isUUIDv4` requires version `4` and the RFC variant nibble `8`, `9`, `a`, or `b`.
|
|
64
247
|
|
|
65
|
-
|
|
66
|
-
- isIriReference: (value: string) => boolean
|
|
248
|
+
</details>
|
|
67
249
|
|
|
68
|
-
|
|
69
|
-
- isAbsoluteIri: (value: string) => boolean
|
|
250
|
+
## Processing model
|
|
70
251
|
|
|
71
|
-
|
|
252
|
+
The parser builds its validation logic from declarative RFC grammar fragments:
|
|
72
253
|
|
|
73
|
-
|
|
74
|
-
|
|
254
|
+
1. Select generic URI/IRI rules or the package's scheme-specific hostname policy.
|
|
255
|
+
2. Recursively expand grammar references through `url-templates`.
|
|
256
|
+
3. Add named capture groups when parsing is requested.
|
|
257
|
+
4. Compile the complete expression with Unicode support.
|
|
258
|
+
5. Cache the expression by operation, grammar rule, and scheme-policy class.
|
|
259
|
+
6. Validate with `RegExp.test()` or parse with `RegExp.exec()`.
|
|
260
|
+
7. Resolve references by component inheritance, path merging, dot-segment removal, and definedness-preserving recomposition.
|
|
75
261
|
|
|
76
|
-
|
|
77
|
-
|
|
262
|
+
<details>
|
|
263
|
+
<summary><strong>Lazy compilation and cache behavior</strong></summary>
|
|
78
264
|
|
|
79
|
-
|
|
80
|
-
- parseAbsoluteIri: (value: string) => AbsoluteIdentifierComponents
|
|
265
|
+
Parsing and validation use separate cached expressions because parsing requires named groups and validation does not. Generic and scheme-specific identifiers also use separate entries.
|
|
81
266
|
|
|
82
|
-
|
|
267
|
+
The first call for an operation/rule/policy combination includes recursive grammar expansion and regular-expression compilation. Later calls reuse the cached expression and are considerably faster. No regular expressions are generated during package import.
|
|
83
268
|
|
|
84
|
-
|
|
85
|
-
- normalizeReference: (reference: string) => string
|
|
269
|
+
</details>
|
|
86
270
|
|
|
87
|
-
|
|
88
|
-
-
|
|
271
|
+
<details>
|
|
272
|
+
<summary><strong>Scheme-specific hostname policy</strong></summary>
|
|
89
273
|
|
|
90
|
-
|
|
91
|
-
- strict (default: `true`) enables strict resolution behavior.
|
|
92
|
-
- returnParts (default: `false`) returns structured components instead of a string.
|
|
274
|
+
The following schemes trigger DNS-style ASCII or Unicode label rules instead of the fully generic `reg-name` grammar:
|
|
93
275
|
|
|
94
|
-
|
|
95
|
-
-
|
|
276
|
+
- `http`
|
|
277
|
+
- `https`
|
|
278
|
+
- `ws`
|
|
279
|
+
- `wss`
|
|
280
|
+
- `file`
|
|
281
|
+
|
|
282
|
+
Matching is case-insensitive. Other valid schemes use generic RFC 3986/3987 registered-name syntax.
|
|
283
|
+
|
|
284
|
+
This policy validates label shape and selected Unicode character classes. It is not complete IDNA processing and does not replace normalization, contextual, bidi, registry, or Punycode validation.
|
|
285
|
+
|
|
286
|
+
</details>
|
|
287
|
+
|
|
288
|
+
## Real-world use cases
|
|
289
|
+
|
|
290
|
+
### Follow HTTP redirects and resource locations
|
|
291
|
+
|
|
292
|
+
HTTP `Location` values are URI references and can be relative to the original target URI.
|
|
293
|
+
|
|
294
|
+
<details>
|
|
295
|
+
<summary><strong>Example and context</strong></summary>
|
|
296
|
+
|
|
297
|
+
```js
|
|
298
|
+
const { resolveReference } = require('identifier-js');
|
|
299
|
+
|
|
300
|
+
const requestUrl = 'https://example.com/account/profile';
|
|
301
|
+
const location = '../login?return=profile';
|
|
302
|
+
console.log(resolveReference(location, requestUrl));
|
|
303
|
+
// https://example.com/login?return=profile
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
RFC 9110 uses URI references in `Location`, `Content-Location`, and `Referer`. Correct resolution requires component parsing, path merging, query inheritance rules, and dot-segment removal.
|
|
307
|
+
|
|
308
|
+
</details>
|
|
309
|
+
|
|
310
|
+
### Keep document trees portable
|
|
311
|
+
|
|
312
|
+
Relative references let documents and assets move together without rewriting every internal link.
|
|
313
|
+
|
|
314
|
+
<details>
|
|
315
|
+
<summary><strong>Example and context</strong></summary>
|
|
316
|
+
|
|
317
|
+
```js
|
|
318
|
+
const { resolveReference, toRelativeReference } = require('identifier-js');
|
|
319
|
+
|
|
320
|
+
const documentUrl = 'https://example.com/manual/chapters/intro.html';
|
|
321
|
+
const imageUrl = 'https://example.com/manual/images/diagram.svg';
|
|
322
|
+
const relative = toRelativeReference(imageUrl, documentUrl);
|
|
323
|
+
console.log(relative); // ../images/diagram.svg
|
|
324
|
+
console.log(resolveReference(relative, documentUrl)); // original image URL
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
RFC 3986 identifies portable hypertext document trees as a central use for relative references.
|
|
328
|
+
|
|
329
|
+
</details>
|
|
330
|
+
|
|
331
|
+
### Process internationalized identifiers
|
|
332
|
+
|
|
333
|
+
IRI parsing preserves native-script host, path, query, and fragment text for interfaces and internationalized content.
|
|
334
|
+
|
|
335
|
+
<details>
|
|
336
|
+
<summary><strong>Example and context</strong></summary>
|
|
337
|
+
|
|
338
|
+
```js
|
|
339
|
+
const { parseIri } = require('identifier-js');
|
|
340
|
+
|
|
341
|
+
const parts = parseIri('https://例え.テスト/検索?q=資料#結果');
|
|
342
|
+
console.log(parts.host); // 例え.テスト
|
|
343
|
+
console.log(parts.path); // /検索
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
RFC 3987 uses IRIs for internationalized identification while requiring mapping to URIs when a protocol accepts only URI syntax. Cache lookup, browser history, XML identifiers, and indexing are cited comparison contexts.
|
|
347
|
+
|
|
348
|
+
</details>
|
|
349
|
+
|
|
350
|
+
### Validate and route protocol identifiers
|
|
351
|
+
|
|
352
|
+
Component parsing supports policy decisions without unsafe string splitting.
|
|
353
|
+
|
|
354
|
+
<details>
|
|
355
|
+
<summary><strong>Example and context</strong></summary>
|
|
356
|
+
|
|
357
|
+
```js
|
|
358
|
+
const { parseUri } = require('identifier-js');
|
|
359
|
+
|
|
360
|
+
const parts = parseUri('wss://example.com:8443/events?channel=updates');
|
|
361
|
+
console.log(parts.scheme); // wss
|
|
362
|
+
console.log(parts.host); // example.com
|
|
363
|
+
console.log(parts.port); // 8443
|
|
364
|
+
console.log(parts.path); // /events
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Applications can inspect scheme, authority, path, and query before selecting a connector, enforcing an allowlist, constructing an HTTP request target, or routing to a service. Protocol-specific security and semantic validation remains the application's responsibility.
|
|
368
|
+
|
|
369
|
+
</details>
|
|
370
|
+
|
|
371
|
+
### Validate externally supplied UUID text
|
|
372
|
+
|
|
373
|
+
Lexical UUID checks are useful at API, configuration, and storage boundaries.
|
|
374
|
+
|
|
375
|
+
<details>
|
|
376
|
+
<summary><strong>Example and context</strong></summary>
|
|
377
|
+
|
|
378
|
+
```js
|
|
379
|
+
const { isUUIDv4 } = require('identifier-js');
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
isUUIDv4('123e4567-e89b-42d3-9456-426614174000');
|
|
383
|
+
console.log('valid UUIDv4');
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error(error.message);
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
RFC 9562 lists database keys, filenames, system identifiers, and transaction identifiers among common UUID uses. UUID validation does not establish authorization, unpredictability, uniqueness, or safe use as a capability token.
|
|
390
|
+
|
|
391
|
+
</details>
|
|
392
|
+
|
|
393
|
+
## Intentional behavior and limitations
|
|
394
|
+
|
|
395
|
+
<details>
|
|
396
|
+
<summary><strong>Validation and parsing boundaries</strong></summary>
|
|
397
|
+
|
|
398
|
+
- Validators return `true` or throw; they do not return `false`.
|
|
399
|
+
- URI functions reject non-ASCII characters where RFC 3986 permits only URI syntax. Use the IRI functions for RFC 3987 Unicode ranges.
|
|
400
|
+
- `absolute-URI` and `absolute-IRI` exclude fragments by definition. The complete `URI` and `IRI` functions permit fragments.
|
|
401
|
+
- Scheme-specific processing currently specializes hostname syntax; it does not implement every protocol rule for HTTP, WebSocket, or `file` identifiers.
|
|
402
|
+
- Scheme-specific Unicode labels are not complete IDNA validation.
|
|
403
|
+
- Ports are restricted to an empty value or the numeric range 0–65535. Generic RFC 3986 syntax itself permits any sequence of digits.
|
|
404
|
+
- Generic registered names may contain syntax that DNS-style hostnames reject.
|
|
405
|
+
- Parsing separates components before any application-level percent decoding.
|
|
406
|
+
- The library does not perform network, DNS, filesystem, registry, or authorization checks.
|
|
407
|
+
|
|
408
|
+
</details>
|
|
409
|
+
|
|
410
|
+
<details>
|
|
411
|
+
<summary><strong>Resolution and conversion boundaries</strong></summary>
|
|
412
|
+
|
|
413
|
+
- Resolution uses IRI grammar, so Unicode references and bases are accepted.
|
|
414
|
+
- Empty authorities, queries, and fragments remain distinct from absent components.
|
|
415
|
+
- `strict = false` enables RFC 3986 backward-compatible handling when a reference repeats the base scheme.
|
|
416
|
+
- `toAbsoluteReference` requires an identifier containing a scheme and removes its fragment through empty-reference resolution.
|
|
417
|
+
- `toRelativeReference` compares scheme and authority text exactly; it does not normalize them first.
|
|
418
|
+
- `toRelativeReference` may return an absolute target when a rootless relative path cannot preserve identity.
|
|
419
|
+
- `normalizeReference` is intentionally an identity function until a normalization contract is selected.
|
|
420
|
+
|
|
421
|
+
</details>
|
|
422
|
+
|
|
423
|
+
<details>
|
|
424
|
+
<summary><strong>UUID boundaries</strong></summary>
|
|
425
|
+
|
|
426
|
+
- `isUUID` validates canonical hexadecimal layout only; it does not enforce a known version or the RFC variant.
|
|
427
|
+
- `isUUIDv4` validates the version and variant fields but does not assess random-number quality.
|
|
428
|
+
- A syntactically valid UUID is not proof of uniqueness, integrity, authenticity, or authorization.
|
|
429
|
+
|
|
430
|
+
</details>
|
|
431
|
+
|
|
432
|
+
## Tests
|
|
433
|
+
|
|
434
|
+
The current suite contains 418 active tests covering URI/IRI validation and parsing, scheme-specific hosts, IPv4, IPv6, ports, UUIDs, RFC 3986 resolution examples, empty components, absolute conversion, and relative-reference round trips.
|
|
435
|
+
|
|
436
|
+
The reference-conversion changes were additionally checked against 2,646 combinations of paths, absent/empty/non-empty queries, and absent/empty/non-empty fragments.
|
|
437
|
+
|
|
438
|
+
<details>
|
|
439
|
+
<summary><strong>Tests</strong></summary>
|
|
440
|
+
|
|
441
|
+
The test suite and supporting ABNF source documents are maintained separately as public workspace data, so they are not included in the package or canonical repository. Users and contributors who need them can materialize them into a cloned repository with [gh-workspace-data](https://github.com/SorinGFS/gh-workspace-data).
|
|
442
|
+
|
|
443
|
+
Install the GitHub CLI extension once:
|
|
444
|
+
|
|
445
|
+
```sh
|
|
446
|
+
gh extension install SorinGFS/gh-workspace-data
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
Then run the workspace-data commands from the repository:
|
|
450
|
+
|
|
451
|
+
```sh
|
|
452
|
+
gh workspace-data init
|
|
453
|
+
gh workspace-data load
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
The tests are materialized as ordinary local files under `#/public/tests/`, and the supporting documents are available under `#/public/docs/`. Both remain excluded from the canonical Git repository.
|
|
457
|
+
|
|
458
|
+
Run the materialized suite with:
|
|
459
|
+
|
|
460
|
+
```sh
|
|
461
|
+
npm test
|
|
462
|
+
```
|
|
96
463
|
|
|
97
|
-
|
|
98
|
-
- toRelativeReference: (target: string, base: string) => string
|
|
464
|
+
When Vitest is not yet declared, the first run installs it and replaces the one-time bootstrap command with the materialized test runner. Later runs invoke that runner directly.
|
|
99
465
|
|
|
100
|
-
|
|
466
|
+
</details>
|
|
101
467
|
|
|
102
|
-
|
|
103
|
-
- isUUID: (value: string) => boolean
|
|
468
|
+
## Authoritative references
|
|
104
469
|
|
|
105
|
-
|
|
106
|
-
-
|
|
470
|
+
- [RFC 3986 — Uniform Resource Identifier: Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986)
|
|
471
|
+
- [RFC 3987 — Internationalized Resource Identifiers](https://www.rfc-editor.org/rfc/rfc3987)
|
|
472
|
+
- [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
|
473
|
+
- [RFC 6455 — The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)
|
|
474
|
+
- [RFC 8089 — The `file` URI Scheme](https://www.rfc-editor.org/rfc/rfc8089)
|
|
475
|
+
- [RFC 9562 — Universally Unique IDentifiers](https://www.rfc-editor.org/rfc/rfc9562)
|
|
107
476
|
|
|
108
|
-
##
|
|
477
|
+
## Disclaimer
|
|
109
478
|
|
|
110
|
-
|
|
479
|
+
Validation establishes conformance with this implementation's syntax and policy. It does not establish that an identifier is registered, reachable, trustworthy, safe to dereference, or appropriate for a particular protocol operation.
|