identifier-js 0.0.14 → 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/readme.md CHANGED
@@ -1,449 +1,479 @@
1
- ---
2
-
3
- title: Identifier JS
4
-
5
- description: An RFC 3986 and RFC 3987 parser, validator, and reference resolver for Node.js and browser bundles.
6
-
7
- ---
8
-
9
- # Identifier JS
10
-
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:
12
-
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
-
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.
21
-
22
- ## Install
23
-
24
- ```bash title="console"
25
- npm install identifier-js
26
- ```
27
-
28
- ## API
29
-
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>
120
-
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
- ```
160
-
161
- ```js
162
- const { resolveReference } = require('identifier-js');
163
-
164
- console.log(resolveReference('../images/logo.svg', 'https://example.com/docs/api/page'));
165
- // https://example.com/docs/images/logo.svg
166
-
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.
174
-
175
- </details>
176
-
177
- ### Produce absolute and relative forms
178
-
179
- Remove a base fragment or derive a relative reference that resolves back to a target.
180
-
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
- ```
200
-
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.
202
-
203
- </details>
204
-
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
- ```
245
-
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`.
247
-
248
- </details>
249
-
250
- ## Processing model
251
-
252
- The parser builds its validation logic from declarative RFC grammar fragments:
253
-
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.
261
-
262
- <details>
263
- <summary><strong>Lazy compilation and cache behavior</strong></summary>
264
-
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.
266
-
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.
268
-
269
- </details>
270
-
271
- <details>
272
- <summary><strong>Scheme-specific hostname policy</strong></summary>
273
-
274
- The following schemes trigger DNS-style ASCII or Unicode label rules instead of the fully generic `reg-name` grammar:
275
-
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
- ## Qualification
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
- ## Authoritative references
439
-
440
- - [RFC 3986 — Uniform Resource Identifier: Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986)
441
- - [RFC 3987 Internationalized Resource Identifiers](https://www.rfc-editor.org/rfc/rfc3987)
442
- - [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
443
- - [RFC 6455 The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)
444
- - [RFC 8089 — The `file` URI Scheme](https://www.rfc-editor.org/rfc/rfc8089)
445
- - [RFC 9562 — Universally Unique IDentifiers](https://www.rfc-editor.org/rfc/rfc9562)
446
-
447
- ## Disclaimer
448
-
449
- 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.
1
+ ---
2
+
3
+ title: Identifier JS
4
+
5
+ description: An RFC 3986 and RFC 3987 parser, validator, and reference resolver for Node.js and browser bundles.
6
+
7
+ ---
8
+
9
+ # Identifier JS
10
+
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:
12
+
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
+
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.
21
+
22
+ ## Install
23
+
24
+ ```bash title="console"
25
+ npm install identifier-js
26
+ ```
27
+
28
+ ## API
29
+
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>
120
+
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
+ ```
160
+
161
+ ```js
162
+ const { resolveReference } = require('identifier-js');
163
+
164
+ console.log(resolveReference('../images/logo.svg', 'https://example.com/docs/api/page'));
165
+ // https://example.com/docs/images/logo.svg
166
+
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.
174
+
175
+ </details>
176
+
177
+ ### Produce absolute and relative forms
178
+
179
+ Remove a base fragment or derive a relative reference that resolves back to a target.
180
+
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
+ ```
200
+
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.
202
+
203
+ </details>
204
+
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
+ ```
245
+
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`.
247
+
248
+ </details>
249
+
250
+ ## Processing model
251
+
252
+ The parser builds its validation logic from declarative RFC grammar fragments:
253
+
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.
261
+
262
+ <details>
263
+ <summary><strong>Lazy compilation and cache behavior</strong></summary>
264
+
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.
266
+
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.
268
+
269
+ </details>
270
+
271
+ <details>
272
+ <summary><strong>Scheme-specific hostname policy</strong></summary>
273
+
274
+ The following schemes trigger DNS-style ASCII or Unicode label rules instead of the fully generic `reg-name` grammar:
275
+
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
+ ```
463
+
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.
465
+
466
+ </details>
467
+
468
+ ## Authoritative references
469
+
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)
476
+
477
+ ## Disclaimer
478
+
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.