identifier-js 0.3.0 → 0.4.1
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.d.ts +14 -3
- package/index.js +42 -6
- package/normalization.md +28 -3
- package/package.json +5 -3
- package/readme.md +63 -14
package/index.d.ts
CHANGED
|
@@ -90,6 +90,17 @@ export type AbsoluteIdentifierComponents = {
|
|
|
90
90
|
query?: string;
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
type UrnIdentifierComponents = {
|
|
94
|
+
scheme: string;
|
|
95
|
+
nid: string;
|
|
96
|
+
nss: string;
|
|
97
|
+
rComponent?: string;
|
|
98
|
+
qComponent?: string;
|
|
99
|
+
fComponent?: string;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
type AbsoluteUrnIdentifierComponents = Omit<UrnIdentifierComponents, 'fComponent'>;
|
|
103
|
+
|
|
104
|
+
export type ParsedIdentifierComponents = (IdentifierComponents | UrnIdentifierComponents) & NormalizableReference;
|
|
105
|
+
export type ParsedRelativeIdentifierComponents = (RelativeIdentifierComponents | UrnIdentifierComponents) & NormalizableReference;
|
|
106
|
+
export type ParsedAbsoluteIdentifierComponents = (AbsoluteIdentifierComponents | AbsoluteUrnIdentifierComponents) & NormalizableReference;
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
// Parse, validate, normalize, resolve, and convert RFC 3986 URI
|
|
3
|
-
//
|
|
2
|
+
// Parse, validate, normalize, resolve, and convert RFC 3986 URI, RFC 3987 IRI, and RFC 8141 URN references.
|
|
3
|
+
// A valid URI is always a valid IRI, subject to every implemented scheme's more specific grammar.
|
|
4
4
|
const { recursiveCompile } = require('url-templates');
|
|
5
5
|
const patterns = new Map();
|
|
6
6
|
const implemented_schemes = '(?:[hH][tT][tT][pP][sS]?|[wW][sS][sS]?|[fF][iI][lL][eE])';
|
|
@@ -79,6 +79,23 @@ const iriRules = {
|
|
|
79
79
|
iprivate: '[\\uE000-\\uF8FF\\u{F0000}-\\u{FFFFD}\\u{100000}-\\u{10FFFD}]',
|
|
80
80
|
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}]',
|
|
81
81
|
};
|
|
82
|
+
// Define RFC 8141 productions and URI/IRI root overrides for the conditional URN profile.
|
|
83
|
+
const urnRules = {
|
|
84
|
+
scheme: '[uU][rR][nN]',
|
|
85
|
+
URI: '{namestring}',
|
|
86
|
+
absolute_URI: '{assigned_name}(?:{rq_components})?',
|
|
87
|
+
IRI: '{URI}',
|
|
88
|
+
absolute_IRI: '{absolute_URI}',
|
|
89
|
+
namestring: '{assigned_name}(?:{rq_components})?(?:#{f_component})?',
|
|
90
|
+
assigned_name: '{scheme}:{NID}:{NSS}',
|
|
91
|
+
NID: '{alpha_digit}{ldh}{0,30}{alpha_digit}',
|
|
92
|
+
ldh: '(?:{alpha_digit}|-)',
|
|
93
|
+
NSS: '{pchar}(?:{pchar}|/)*',
|
|
94
|
+
rq_components: '(?:[?][+]{r_component})?(?:[?]={q_component})?',
|
|
95
|
+
r_component: '{pchar}(?:{pchar}|/|[?](?!=))*',
|
|
96
|
+
q_component: '{pchar}(?:{pchar}|/|[?])*',
|
|
97
|
+
f_component: '{fragment}',
|
|
98
|
+
};
|
|
82
99
|
// Reuse the grammar repertoires when selecting URI octets safe for IRI output.
|
|
83
100
|
const uriUnreservedPattern = new RegExp(`^${commonRules.unreserved}$`);
|
|
84
101
|
const iriUcscharPattern = new RegExp(`^${iriRules.ucschar}$`, 'u');
|
|
@@ -125,16 +142,28 @@ const groupNames = {
|
|
|
125
142
|
ipath_noscheme: 'path',
|
|
126
143
|
ipath_rootless: 'path',
|
|
127
144
|
ipath_empty: 'path',
|
|
145
|
+
NID: 'nid',
|
|
146
|
+
NSS: 'nss',
|
|
147
|
+
r_component: 'rComponent',
|
|
148
|
+
q_component: 'qComponent',
|
|
149
|
+
f_component: 'fComponent',
|
|
128
150
|
};
|
|
129
|
-
//
|
|
151
|
+
// Keep URN parse results limited to their RFC 8141 component names.
|
|
152
|
+
const genericUrnGroupNames = new Set(['authority', 'userinfo', 'host', 'port', 'path', 'query', 'fragment']);
|
|
153
|
+
// Detect schemes for which the package implements grammar beyond generic URI/IRI syntax.
|
|
130
154
|
const isSpecificScheme = (string) => new RegExp('^' + implemented_schemes + ':').test(string);
|
|
131
|
-
|
|
132
|
-
const
|
|
155
|
+
// Select and merge generic, DNS-host, empty-file-host, or URN grammar profiles.
|
|
156
|
+
const schemeProfile = (string) => (string.slice(0, 4).toLowerCase() === 'urn:' ? 'u' : string.slice(0, 8).toLowerCase() === 'file:///' ? 'f' : isSpecificScheme(string) ? 's' : '');
|
|
157
|
+
const rules = (profile) => Object.assign({}, commonRules, uriRules, iriRules, profile === 'u' ? urnRules : profile === 'f' ? emptyFileHostRules : profile ? schemeSpecificRules : {});
|
|
133
158
|
// parse (slower, it uses regex.exec and includes named capture groups)
|
|
134
159
|
const parse = (string, rule) => {
|
|
135
160
|
if (typeof string !== 'string') throw new TypeError(`Invalid ${rule.replace('_', '-')} type: must be a string.`);
|
|
136
161
|
const profile = schemeProfile(string);
|
|
137
|
-
|
|
162
|
+
// Select only the component captures exposed by the active grammar.
|
|
163
|
+
const addNames = (key) => {
|
|
164
|
+
const groupName = groupNames[key];
|
|
165
|
+
return groupName && !(profile === 'u' && genericUrnGroupNames.has(groupName)) ? `(?<${groupName}>${rules(profile)[key]})` : rules(profile)[key];
|
|
166
|
+
};
|
|
138
167
|
const ruleId = '_' + profile + rule;
|
|
139
168
|
if (!patterns.has(ruleId)) patterns.set(ruleId, new RegExp(`^${recursiveCompile(rules(profile), rule, addNames)}$`, 'u'));
|
|
140
169
|
const match = patterns.get(ruleId).exec(string);
|
|
@@ -533,6 +562,13 @@ function normalizeParsedReference(parts, options = {}) {
|
|
|
533
562
|
const { transform, mapRegName } = options;
|
|
534
563
|
if (transform !== undefined && transform !== 'URI' && transform !== 'IRI') throw new TypeError('Invalid transform option: must be "URI" or "IRI".');
|
|
535
564
|
if (mapRegName !== undefined && typeof mapRegName !== 'function') throw new TypeError('Invalid registered-name mapper type: must be a function.');
|
|
565
|
+
// Normalize captured URN fields without applying generic path or representation processing.
|
|
566
|
+
if (parts.nid !== undefined) {
|
|
567
|
+
const rComponent = parts.rComponent === undefined ? undefined : normalizePercentEncoding(parts.rComponent, false);
|
|
568
|
+
const qComponent = parts.qComponent === undefined ? undefined : normalizePercentEncoding(parts.qComponent, false);
|
|
569
|
+
const query = rComponent !== undefined ? `+${rComponent}${qComponent === undefined ? '' : `?=${qComponent}`}` : qComponent === undefined ? undefined : `=${qComponent}`;
|
|
570
|
+
return compose({ scheme: parts.scheme.toLowerCase(), path: `${parts.nid.toLowerCase()}:${normalizePercentEncoding(parts.nss, false)}`, query, fragment: parts.fComponent === undefined ? undefined : normalizePercentEncoding(parts.fComponent, false) });
|
|
571
|
+
}
|
|
536
572
|
// Normalize each component independently so encoded delimiters cannot become structure.
|
|
537
573
|
const scheme = parts.scheme === undefined ? undefined : parts.scheme.toLowerCase();
|
|
538
574
|
const normalized = {
|
package/normalization.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# URI and IRI normalization
|
|
2
2
|
|
|
3
|
-
Parsed URI and IRI results expose `normalize()` for syntax-based normalization, the implemented HTTP, HTTPS, WS, and
|
|
3
|
+
Parsed URI and IRI results expose `normalize()` for syntax-based normalization, the implemented HTTP, HTTPS, WS, WSS, and URN scheme rules, and optional RFC 3987 URI/IRI representation transformation. The method returns a string and leaves the parsed components unchanged.
|
|
4
4
|
|
|
5
5
|
## API
|
|
6
6
|
|
|
@@ -49,8 +49,8 @@ parsed.path;
|
|
|
49
49
|
| Behavior | Implementation | Source |
|
|
50
50
|
| --- | --- | --- |
|
|
51
51
|
| Case normalization | Lowercase the scheme and an ASCII-only host. Uppercase hexadecimal letters in percent triplets. | [RFC 3986 §6.2.2.1](https://www.rfc-editor.org/rfc/rfc3986#section-6.2.2.1), [RFC 3987 §5.3.2.1](https://www.rfc-editor.org/rfc/rfc3987#section-5.3.2.1) |
|
|
52
|
-
| Percent-encoded unreserved characters |
|
|
53
|
-
| Path segments | Apply the RFC dot-segment algorithm where
|
|
52
|
+
| Percent-encoded unreserved characters | For generic URI/IRI components, decode percent triplets representing ASCII letters, digits, `-`, `.`, `_`, or `~`. Retain percent encoding for reserved octets. URNs use the non-decoding rules below. | [RFC 3986 §§2.2–2.4 and 6.2.2.2](https://www.rfc-editor.org/rfc/rfc3986#section-6.2.2.2), [RFC 3987 §5.3.2.3](https://www.rfc-editor.org/rfc/rfc3987#section-5.3.2.3) |
|
|
53
|
+
| Path segments | Apply the RFC dot-segment algorithm where a generic parsed reference can be normalized independently. Preserve unresolved rootless-relative path semantics and every URN NSS segment. | [RFC 3986 §§5.2.4 and 6.2.2.3](https://www.rfc-editor.org/rfc/rfc3986#section-5.2.4), [RFC 3987 §5.3.2.4](https://www.rfc-editor.org/rfc/rfc3987#section-5.3.2.4), [RFC 8141 §§2.2 and 3.1](https://www.rfc-editor.org/rfc/rfc8141#section-3.1) |
|
|
54
54
|
| Component recomposition | Emit authority, query, and fragment delimiters from component presence, including present-empty components. | [RFC 3986 §5.3](https://www.rfc-editor.org/rfc/rfc3986#section-5.3) |
|
|
55
55
|
| IPv6 text | Suppress leading zeroes, compress the longest zero run with first-run tie breaking, and use lowercase hexadecimal. Known embedded-IPv4 forms use mixed notation. | [RFC 5952 §§4–5](https://www.rfc-editor.org/rfc/rfc5952#section-4) |
|
|
56
56
|
| IRI-to-URI output | With `transform: 'URI'`, encode non-ASCII authority, path, query, and fragment characters as uppercase UTF-8 percent triplets. | [RFC 3987 §3.1](https://www.rfc-editor.org/rfc/rfc3987#section-3.1) |
|
|
@@ -58,6 +58,30 @@ parsed.path;
|
|
|
58
58
|
|
|
59
59
|
Without a mapper, normalization retains the parser's host classification as an IP literal, IPv4 address, or registered name. IPvFuture literals use generic host case normalization. Existing non-ASCII IRI host text is retained unless the registered-name mapper supplies another value.
|
|
60
60
|
|
|
61
|
+
## URNs
|
|
62
|
+
|
|
63
|
+
A parsed value under the case-insensitive `urn` scheme takes a separate RFC 8141 normalization path using its captured `scheme`, `nid`, `nss`, `rComponent`, `qComponent`, and `fComponent` properties.
|
|
64
|
+
|
|
65
|
+
| Input component | Output | Source |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| Scheme | Convert `urn` to lowercase. | [RFC 8141 §3.1](https://www.rfc-editor.org/rfc/rfc8141#section-3.1) |
|
|
68
|
+
| NID | Convert ASCII letters to lowercase. | [RFC 8141 §§2.1 and 3.1](https://www.rfc-editor.org/rfc/rfc8141#section-3.1) |
|
|
69
|
+
| NSS | Uppercase hexadecimal letters in percent triplets without decoding any octet. Preserve literal case, slash structure, and dot segments. | [RFC 8141 §§2.2 and 3.1](https://www.rfc-editor.org/rfc/rfc8141#section-3.1) |
|
|
70
|
+
| r-, q-, and f-components | Retain the components and their delimiters, uppercasing hexadecimal letters in percent triplets without decoding. | [RFC 8141 §2.3](https://www.rfc-editor.org/rfc/rfc8141#section-2.3), [RFC 3986 §6.2.2.1](https://www.rfc-editor.org/rfc/rfc3986#section-6.2.2.1) |
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
const { parseUri } = require('identifier-js');
|
|
74
|
+
|
|
75
|
+
parseUri('URN:EXAMPLE:a%62/./b/../C?+r%2f?=q%2f#f%2f').normalize();
|
|
76
|
+
// urn:example:a%62/./b/../C?+r%2F?=q%2F#f%2F
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
RFC 8141 URNs remain ASCII, including when parsed through an IRI operation. Consequently, `transform: 'URI'` and `transform: 'IRI'` produce the same URN representation, and `mapRegName` is not called because a URN has no authority or registered-name host.
|
|
80
|
+
|
|
81
|
+
For a parsed URN, the current scheme-specific fields are the normalization input. The NSS and optional-component values stay opaque except for percent-triplet letter case. The method leaves every property unchanged.
|
|
82
|
+
|
|
83
|
+
Normalization is not a URN-equivalence API. RFC 8141 equivalence compares the normalized assigned-name and ignores r-, q-, and f-components; namespace definitions can add further equivalence rules. This method instead retains those optional components in its returned string. The package does not implement generic or namespace-specific URN-equivalence comparison.
|
|
84
|
+
|
|
61
85
|
## HTTP and HTTPS
|
|
62
86
|
|
|
63
87
|
For `http` and `https`, normalization applies the generic rules and these scheme rules:
|
|
@@ -141,6 +165,7 @@ npm test
|
|
|
141
165
|
|
|
142
166
|
- [RFC 3986 — Uniform Resource Identifier: Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986)
|
|
143
167
|
- [RFC 3987 — Internationalized Resource Identifiers](https://www.rfc-editor.org/rfc/rfc3987)
|
|
168
|
+
- [RFC 8141 — Uniform Resource Names](https://www.rfc-editor.org/rfc/rfc8141)
|
|
144
169
|
- [RFC 5952 — A Recommendation for IPv6 Address Text Representation](https://www.rfc-editor.org/rfc/rfc5952)
|
|
145
170
|
- [RFC 6455 — The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)
|
|
146
171
|
- [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
package/package.json
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "identifier-js",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A fast URI/IRI parser, validator, normalizer, resolver, and composer
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "A fast RFC 3986/3987 URI/IRI parser, validator, normalizer, resolver, and composer with RFC 8141 URN syntax support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"IRI",
|
|
7
7
|
"URI",
|
|
8
8
|
"IRI-reference",
|
|
9
9
|
"URI-reference",
|
|
10
|
+
"URN",
|
|
10
11
|
"ipv4",
|
|
11
12
|
"ipv6",
|
|
12
13
|
"uuid",
|
|
13
14
|
"parser",
|
|
14
15
|
"validator",
|
|
15
16
|
"RFC3986",
|
|
16
|
-
"RFC3987"
|
|
17
|
+
"RFC3987",
|
|
18
|
+
"RFC8141"
|
|
17
19
|
],
|
|
18
20
|
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
19
21
|
"bugs": {
|
package/readme.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
title: Identifier JS
|
|
4
4
|
|
|
5
|
-
description:
|
|
5
|
+
description: RFC 3986/3987 URI and IRI tools with scheme-specific RFC 8141 URN syntax and normalization support.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Identifier JS
|
|
10
10
|
|
|
11
|
-
`identifier-js` is a URI/IRI parser, validator, normalizer, resolver, and composer based on RFC [3986](https://www.rfc-editor.org/rfc/rfc3986) and RFC [3987](https://www.rfc-editor.org/rfc/rfc3987). Its recognized HTTP, WebSocket, and `file` schemes retain the documented hostname-policy restrictions below. It provides:
|
|
11
|
+
`identifier-js` is a URI/IRI parser, validator, normalizer, resolver, and composer based on RFC [3986](https://www.rfc-editor.org/rfc/rfc3986) and RFC [3987](https://www.rfc-editor.org/rfc/rfc3987), with scheme-specific RFC [8141](https://www.rfc-editor.org/rfc/rfc8141) URN support. Its recognized HTTP, WebSocket, and `file` schemes retain the documented hostname-policy restrictions below. It provides:
|
|
12
12
|
|
|
13
|
-
- URI and IRI validation;
|
|
14
|
-
- parsed
|
|
15
|
-
- conservative syntax normalization, recognized-scheme
|
|
13
|
+
- URI and IRI validation, including RFC 8141 URN namestring syntax;
|
|
14
|
+
- parsed generic URI/IRI components and scheme-specific URN components;
|
|
15
|
+
- conservative syntax normalization, recognized-scheme forms, and a registered-name extension point;
|
|
16
16
|
- RFC 3986 reference resolution and dot-segment removal;
|
|
17
17
|
- relative-reference generation with resolution round-trip guarantees for supported forms;
|
|
18
18
|
- UUID and UUIDv4 lexical validation;
|
|
@@ -143,6 +143,45 @@ console.log(parseIri('https://usér@例え.テスト:8443/résumé?lang=fr#profi
|
|
|
143
143
|
|
|
144
144
|
</details>
|
|
145
145
|
|
|
146
|
+
### Validate and parse RFC 8141 URNs
|
|
147
|
+
|
|
148
|
+
URNs use the existing URI and IRI operations because a URN is a URI under the `urn` scheme. Values with a case-insensitive `urn:` prefix are validated against RFC 8141 namestring syntax; no separate `isUrn` or `parseUrn` API is exported.
|
|
149
|
+
|
|
150
|
+
```text
|
|
151
|
+
urn:NID:NSS[?+r-component][?=q-component][#f-component]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The NID contains 2–32 ASCII characters, starts and ends with a letter or digit, and permits letters, digits, or hyphens internally. The NSS begins with an RFC 3986 `pchar` and then permits `pchar` or `/`. The ordered r- and q-components also begin with `pchar` and then permit `pchar`, `/`, or `?`, while an f-component can be empty. The first `?=` sequence after an r-component starts the q-component, and any other question mark outside an optional component is rejected.
|
|
155
|
+
|
|
156
|
+
<details>
|
|
157
|
+
<summary><strong>API behavior and examples</strong></summary>
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
const { isUri, isIri, parseUri } = require('identifier-js');
|
|
161
|
+
|
|
162
|
+
const value = 'URN:Example:a%2f/../B?+service?x?=key=value#part';
|
|
163
|
+
console.log(isUri(value)); // true
|
|
164
|
+
console.log(isIri(value)); // true
|
|
165
|
+
|
|
166
|
+
const parsed = parseUri(value);
|
|
167
|
+
console.log(parsed.scheme); // URN
|
|
168
|
+
console.log(parsed.nid); // Example
|
|
169
|
+
console.log(parsed.nss); // a%2f/../B
|
|
170
|
+
console.log(parsed.rComponent); // service?x
|
|
171
|
+
console.log(parsed.qComponent); // key=value
|
|
172
|
+
console.log(parsed.fComponent); // part
|
|
173
|
+
console.log(parsed.normalize());
|
|
174
|
+
// urn:example:a%2F/../B?+service?x?=key=value#part
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
URN parse results expose `nid`, `nss`, `rComponent`, `qComponent`, and `fComponent`. They do not expose generic `path`, `query`, or `fragment` aliases. To require a URN after parsing a value accepted as a general URI, check `parsed.scheme.toLowerCase() === 'urn'`.
|
|
178
|
+
|
|
179
|
+
URNs remain ASCII even through the IRI operations. Callers representing non-ASCII names must first encode them as UTF-8 and then percent-encode the resulting octets; lexical validation does not decode or verify those octet sequences.
|
|
180
|
+
|
|
181
|
+
Validation is deliberately lexical and namespace-independent. Success does not prove that an NID is registered or otherwise legitimate, that an NSS obeys a namespace's additional syntax and canonicalization rules, or that the name was legitimately assigned.
|
|
182
|
+
|
|
183
|
+
</details>
|
|
184
|
+
|
|
146
185
|
### Resolve a reference
|
|
147
186
|
|
|
148
187
|
Resolve a URI or IRI reference against an absolute base using RFC 3986 §5.
|
|
@@ -173,6 +212,8 @@ console.log(resolveReference('?page=2', 'https://example.com/items?page=1#curren
|
|
|
173
212
|
|
|
174
213
|
Empty authorities, queries, and fragments are preserved during recomposition.
|
|
175
214
|
|
|
215
|
+
`resolveReference` does not apply when either input uses the `urn` scheme. URN resolution services are outside this package's scope.
|
|
216
|
+
|
|
176
217
|
</details>
|
|
177
218
|
|
|
178
219
|
### Produce absolute and relative forms
|
|
@@ -201,6 +242,8 @@ console.log(relative); // ../images/logo.svg
|
|
|
201
242
|
|
|
202
243
|
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. Complete dot segments in either path also trigger this fallback because RFC resolution removes them. For those inputs, resolving the result produces the same identifier as resolving the target directly; lexical dot-segment spelling is not preserved.
|
|
203
244
|
|
|
245
|
+
`toAbsoluteReference` and `toRelativeReference` do not apply when an input uses the `urn` scheme. Relative-URN semantics are outside this package's scope.
|
|
246
|
+
|
|
204
247
|
</details>
|
|
205
248
|
|
|
206
249
|
### Normalize parsed URI and IRI references
|
|
@@ -239,6 +282,8 @@ The method is available from `parseUri`, `parseUriReference`, `parseAbsoluteUri`
|
|
|
239
282
|
|
|
240
283
|
Normalization implements RFC 3986 and RFC 3987 syntax normalization for scheme and host case, percent triplets, ASCII unreserved characters, path dot segments, and component recomposition. IPv6 literals use RFC 5952 text. HTTP(S) default ports and empty paths follow RFC 9110; WS(S) defaults and resource-name paths follow RFC 6455.
|
|
241
284
|
|
|
285
|
+
URN normalization lowercases the scheme and NID, uppercases percent-triplet hexadecimal letters without decoding, and preserves NSS case, slashes, and dot segments. The r-, q-, and f-components are retained, so normalized-string equality is not the RFC 8141 URN-equivalence procedure. Namespace-specific equivalence and URN resolution are outside this package's scope. URI/IRI transformation and registered-name mapping options do not alter authority-free, ASCII-only URNs.
|
|
286
|
+
|
|
242
287
|
For a non-empty registered-name host, `mapRegName` receives the current host spelling before built-in normalization. The mapper exclusively owns validation, representation, and host-kind policy for its returned string. Apart from enforcing the declared string return type, this package does not check whether mapper output is non-empty, remains a registered name, introduces delimiters, resembles an IP address, or satisfies a scheme-specific hostname grammar.
|
|
243
288
|
|
|
244
289
|
With `transform: 'URI'`, non-ASCII userinfo, mapper output, path, query, and fragment text becomes uppercase UTF-8 percent triplets under RFC 3987 §3.1. With `transform: 'IRI'`, eligible percent-encoded ASCII unreserved characters and strictly legal UTF-8 sequences become IRI characters under RFC 3987 §3.2; reserved, malformed, disallowed, and non-UTF-8 octets remain encoded. Private-use characters are decoded only in queries, and forbidden bidirectional formatting characters remain encoded. A mapper can supply the desired Unicode or ASCII hostname representation; this package does not enforce that policy or validate the complete normalized result.
|
|
@@ -274,11 +319,11 @@ console.log(isUUIDv4('123e4567-e89b-42d3-9456-426614174000')); // true
|
|
|
274
319
|
|
|
275
320
|
The parser builds its validation logic from declarative RFC grammar fragments:
|
|
276
321
|
|
|
277
|
-
1. Select
|
|
322
|
+
1. Select the applicable generic or scheme-specific URI/IRI syntax.
|
|
278
323
|
2. Recursively expand grammar references through `url-templates`.
|
|
279
|
-
3. Add named
|
|
324
|
+
3. Add named captures for the components exposed by the selected syntax.
|
|
280
325
|
4. Compile the complete expression with Unicode support.
|
|
281
|
-
5. Cache
|
|
326
|
+
5. Cache expressions by operation, grammar rule, and scheme class.
|
|
282
327
|
6. Validate with `RegExp.test()` or parse with `RegExp.exec()`.
|
|
283
328
|
7. Resolve references by component inheritance, path merging, dot-segment removal, and definedness-preserving recomposition.
|
|
284
329
|
|
|
@@ -302,7 +347,7 @@ The following schemes trigger DNS-style ASCII or Unicode label rules instead of
|
|
|
302
347
|
- `wss`
|
|
303
348
|
- `file`
|
|
304
349
|
|
|
305
|
-
Matching is case-insensitive. Other valid schemes use generic RFC 3986/3987 registered-name syntax. RFC 8089's empty `file` authority is accepted when followed by an absolute path, as in `file:///path`; empty hosts remain rejected for HTTP and WebSocket schemes.
|
|
350
|
+
Matching is case-insensitive. Other valid schemes use generic RFC 3986/3987 registered-name syntax; URNs instead follow RFC 8141 namestring syntax. RFC 8089's empty `file` authority is accepted when followed by an absolute path, as in `file:///path`; empty hosts remain rejected for HTTP and WebSocket schemes.
|
|
306
351
|
|
|
307
352
|
Parsing validates DNS-style label shape and the selected RFC 3987 Unicode character classes. A registered-name mapper runs later during optional normalization, and its returned string is not submitted to this hostname policy again.
|
|
308
353
|
|
|
@@ -418,8 +463,10 @@ RFC 9562 lists database keys, filenames, system identifiers, and transaction ide
|
|
|
418
463
|
<details>
|
|
419
464
|
<summary><strong>Validation and parsing</strong></summary>
|
|
420
465
|
|
|
421
|
-
- Generic URI syntax follows RFC 3986 character and component grammar;
|
|
422
|
-
- Generic IRI syntax follows the RFC 3987 Unicode extensions to URI grammar;
|
|
466
|
+
- Generic URI syntax follows RFC 3986 character and component grammar; HTTP, WebSocket, and `file` schemes apply the documented hostname restrictions.
|
|
467
|
+
- Generic IRI syntax follows the RFC 3987 Unicode extensions to URI grammar; HTTP, WebSocket, and `file` schemes apply the documented hostname restrictions.
|
|
468
|
+
- Values with the case-insensitive `urn` scheme follow RFC 8141 namestring syntax and expose `nid`, `nss`, `rComponent`, `qComponent`, and `fComponent` fields through the URI and IRI parsers.
|
|
469
|
+
- URN validation establishes generic lexical syntax only, not namespace registration, namespace-specific syntax, assignment, resolution, or equivalence.
|
|
423
470
|
- Validators return `true` or throw at the first grammar violation.
|
|
424
471
|
- `absolute-URI` and `absolute-IRI` use the fragment-free grammar defined by their RFCs; complete URI and IRI operations accept fragments.
|
|
425
472
|
- Port syntax follows RFC 3986 `port = *DIGIT`, including empty and leading-zero values.
|
|
@@ -435,7 +482,8 @@ RFC 9562 lists database keys, filenames, system identifiers, and transaction ide
|
|
|
435
482
|
- `strict = false` implements RFC 3986 §5.2.2 backward-compatible same-scheme handling.
|
|
436
483
|
- `toAbsoluteReference` removes the fragment from an identifier containing a scheme.
|
|
437
484
|
- `toRelativeReference` generates a reference whose RFC resolution equals the target resolution for supported forms.
|
|
438
|
-
- `normalize()` implements the applicable case, percent-encoding, and path-segment rules from RFC 3986 §§6.2.2.1–6.2.2.3 and RFC 3987 §§5.3.2.1, 5.3.2.3–5.3.2.4, RFC 3987 §§3.1–3.2 URI/IRI representation transformation, RFC 5952 IPv6 text, RFC 9110 HTTP(S) port/path forms,
|
|
485
|
+
- `normalize()` implements the applicable case, percent-encoding, and path-segment rules from RFC 3986 §§6.2.2.1–6.2.2.3 and RFC 3987 §§5.3.2.1, 5.3.2.3–5.3.2.4, RFC 3987 §§3.1–3.2 URI/IRI representation transformation, RFC 5952 IPv6 text, RFC 9110 HTTP(S) port/path forms, RFC 6455 WS(S) port/resource-name forms, and RFC 8141 scheme/NID/percent-triplet normalization without NSS decoding or path reduction.
|
|
486
|
+
- Reference resolution and absolute/relative reference conversion do not apply to `urn` inputs; RFC 8141 URN resolution services and URN-equivalence APIs are not implemented.
|
|
439
487
|
|
|
440
488
|
</details>
|
|
441
489
|
|
|
@@ -475,7 +523,7 @@ Run `gh workspace-data load` again to refresh materialized data after public-dat
|
|
|
475
523
|
|
|
476
524
|
### Tests
|
|
477
525
|
|
|
478
|
-
The active suite contains 3,
|
|
526
|
+
The active suite contains 3,157 tests covering URI/IRI validation and parsing, RFC 8141 URN syntax and normalization, generic normalization, bidirectional URI/IRI representation transformation, scheme-specific hosts, IPv4, IPv6, IPvFuture, ports, UUIDs, RFC 3986 resolution examples, empty components, absolute conversion, and relative-reference round trips, including 2,646 generated combinations of target/base paths, query-presence states, and target-fragment states across equivalent URI and IRI families.
|
|
479
527
|
|
|
480
528
|
<details>
|
|
481
529
|
<summary><strong>Test details</strong></summary>
|
|
@@ -495,7 +543,7 @@ The suite uses the `node:test` module built into Node.js and requires no separat
|
|
|
495
543
|
|
|
496
544
|
### Benchmarks
|
|
497
545
|
|
|
498
|
-
The materialized benchmark suite provides portable, version-aware measurements for every exported function
|
|
546
|
+
The 26-scenario materialized benchmark suite provides portable, version-aware measurements for every exported function, isolated package loading, and RFC 8141 URN validation, parsing, and normalization. It reports initial-call behavior, warmed latency statistics, integer throughput, workload counts, representative inputs, and environment metadata.
|
|
499
547
|
|
|
500
548
|
<details>
|
|
501
549
|
<summary><strong>Benchmark details</strong></summary>
|
|
@@ -526,6 +574,7 @@ The generic coordinator delegates version-layer selection and ordered concern di
|
|
|
526
574
|
|
|
527
575
|
- [RFC 3986 — Uniform Resource Identifier: Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986)
|
|
528
576
|
- [RFC 3987 — Internationalized Resource Identifiers](https://www.rfc-editor.org/rfc/rfc3987)
|
|
577
|
+
- [RFC 8141 — Uniform Resource Names](https://www.rfc-editor.org/rfc/rfc8141)
|
|
529
578
|
- [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
|
530
579
|
- [RFC 6455 — The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)
|
|
531
580
|
- [RFC 8089 — The `file` URI Scheme](https://www.rfc-editor.org/rfc/rfc8089)
|