identifier-js 0.4.2 → 0.4.3

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/normalization.md CHANGED
@@ -110,10 +110,22 @@ A parsed value under the case-insensitive `urn` scheme takes the separate RFC 81
110
110
  ```js
111
111
  const { parseUri } = require('identifier-js');
112
112
 
113
- parseUri('URN:EXAMPLE:a%62/./b/../C?+r%2f?=q%2f#f%2f').normalize();
113
+ const input = 'URN:EXAMPLE:a%62/./b/../C?+r%2f?=q%2f#f%2f';
114
+ const output = parseUri(input).normalize();
115
+
116
+ output;
114
117
  // urn:example:a%62/./b/../C?+r%2F?=q%2F#f%2F
115
118
  ```
116
119
 
120
+ This example demonstrates each URN normalization rule:
121
+
122
+ - `URN` becomes `urn` because the scheme is case-insensitive and normalized to lowercase.
123
+ - `EXAMPLE` becomes `example` because ASCII letters in the NID are normalized to lowercase.
124
+ - `%62` remains encoded in the NSS rather than becoming `b`; URN normalization does not decode percent-encoded NSS octets.
125
+ - `/./b/../C` remains unchanged because the NSS is opaque to generic path processing: dot segments are not removed, and literal NSS case is preserved.
126
+ - The r-, q-, and f-components and their `?+`, `?=`, and `#` delimiters are retained.
127
+ - `%2f` becomes `%2F` in each optional component because retained percent triplets use uppercase hexadecimal letters without decoding the represented `/`.
128
+
117
129
  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.
118
130
 
119
131
  For a parsed URN, the current URN-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.
@@ -137,20 +149,54 @@ mapped;
137
149
 
138
150
  The example deliberately produces text that is not a valid URI or IRI; validating or selecting mapper output belongs to the application.
139
151
 
152
+ ### IRI-to-URI transformation
153
+
140
154
  With `transform: 'URI'`, retained reserved and non-ASCII percent triplets remain encoded, and literal non-ASCII userinfo, mapper output, path, query, and fragment text becomes uppercase UTF-8 percent triplets. A mapper can supply an ASCII hostname when its consuming scheme requires one; this package does not validate mapper output against that scheme.
141
155
 
142
156
  ```js
143
- const { parseIri, parseUri } = require('identifier-js');
157
+ const { parseIri } = require('identifier-js');
144
158
 
145
- parseIri('x:/café?q=資料#résultat').normalize({ transform: 'URI' });
146
- // x:/caf%C3%A9?q=%E8%B3%87%E6%96%99#r%C3%A9sultat
159
+ const input = 'x://usér@exämple/latin-é/emoji-😀?native=資料&private=\uE000&reserved=/&encoded=%c3%a9#résultat';
160
+ const output = parseIri(input).normalize({ transform: 'URI' });
147
161
 
148
- parseUri('x:/caf%C3%A9?q=%E8%B3%87%E6%96%99#r%C3%A9sultat').normalize({ transform: 'IRI' });
149
- // x:/café?q=資料#résultat
162
+ output;
163
+ // x://us%C3%A9r@ex%C3%A4mple/latin-%C3%A9/emoji-%F0%9F%98%80?native=%E8%B3%87%E6%96%99&private=%EE%80%80&reserved=/&encoded=%C3%A9#r%C3%A9sultat
150
164
  ```
151
165
 
166
+ This example demonstrates each relevant output rule:
167
+
168
+ - `usér`, `exämple`, `latin-é`, and `résultat` become UTF-8 percent triplets in userinfo, host, path, and fragment text.
169
+ - `😀` is processed as one Unicode scalar and becomes its four UTF-8 octets `%F0%9F%98%80`.
170
+ - `資料` becomes `%E8%B3%87%E6%96%99` in the query.
171
+ - The query's private-use character `\uE000` becomes `%EE%80%80`.
172
+ - The literal reserved `/` remains literal because it is already valid URI query syntax.
173
+ - The existing encoded sequence `%c3%a9` remains encoded while its hexadecimal letters become uppercase as `%C3%A9`.
174
+
175
+ ### URI-to-IRI transformation
176
+
152
177
  With `transform: 'IRI'`, conversion uses UTF-8 exclusively and decodes as many eligible percent-encoded characters as possible. Encoded reserved characters, `%25`, malformed or incomplete UTF-8, legacy character encodings, Unicode outside the RFC 3987 component repertoire, and forbidden bidirectional formatting characters remain percent encoded. Private-use characters are decoded only in the query component. The hexadecimal letters of retained triplets are uppercase.
153
178
 
179
+ ```js
180
+ const { parseUri } = require('identifier-js');
181
+
182
+ const input = 'x:/ok-%C3%A9/reserved-%2F/percent-%25/malformed-%C3%28/incomplete-%E2%82/latin1-%E9/outside-%EF%B7%90/bidi-%E2%80%8E';
183
+ const output = parseUri(input).normalize({ transform: 'IRI' });
184
+
185
+ output;
186
+ // x:/ok-é/reserved-%2F/percent-%25/malformed-%C3%28/incomplete-%E2%82/latin1-%E9/outside-%EF%B7%90/bidi-%E2%80%8E
187
+ ```
188
+
189
+ This example shows why transformation is not equivalent to applying `decodeURIComponent()` to every triplet:
190
+
191
+ - `%C3%A9` becomes `é` because it is valid UTF-8 for a character permitted in an IRI path.
192
+ - `%2F` remains encoded because `/` is reserved and decoding it could change path structure.
193
+ - `%25` remains encoded because decoding it would introduce a literal percent sign.
194
+ - `%C3%28` remains encoded because it is malformed UTF-8.
195
+ - `%E2%82` remains encoded because it is an incomplete UTF-8 sequence.
196
+ - `%E9` remains encoded because a Latin-1 or Windows-1252 byte is not valid UTF-8 by itself.
197
+ - `%EF%B7%90` remains encoded because it represents U+FDD0, which is outside the RFC 3987 character repertoire.
198
+ - `%E2%80%8E` remains encoded because it represents U+200E, a forbidden bidirectional formatting character.
199
+
154
200
  The IRI transformation decodes percent-encoded ASCII unreserved characters even when this changes a registered name into IPv4-looking text. Without an explicit transformation, normalization preserves that registered-name host classification.
155
201
 
156
202
  ACE-to-Unicode and Unicode-to-ACE registered-name conversion remain application policy. `mapRegName` runs before the selected representation transformation, so applications can provide the appropriate mapping in either direction.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "identifier-js",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
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",
package/readme.md CHANGED
@@ -212,7 +212,7 @@ When no safe rootless relative form can round-trip to the target, `toRelativeRef
212
212
  Every URI and IRI parse result provides an optional, non-enumerable `normalize()` method. Parsing remains usable by itself; normalization runs only when the method is called and returns a string without modifying the parsed components.
213
213
 
214
214
  <details>
215
- <summary><strong>API, behavior, and examples</strong></summary>
215
+ <summary><strong>API and examples</strong></summary>
216
216
 
217
217
  ```ts
218
218
  type RegNameMapper = (regName: string) => string