identifier-js 0.0.1 → 0.0.2
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 +55 -0
- package/index.js +270 -0
- package/package.json +39 -35
- package/readme.md +82 -0
- package/tests/parse.test.js +472 -0
- package/tests/performance.js +46 -0
- package/tests/resolve.rfc.test.js +62 -0
- package/tests/resolve.test.js +25 -0
- package/tests/to-absolute.test.js +12 -0
- package/tests/to-relative.test.js +50 -0
- package/tests/validation.test.js +278 -0
- package/tests/vitest-setup.js +18 -0
- /package/{rfc3986-rfc3987-abnf.yml → docs/rfc3986-rfc3987-abnf.yml} +0 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export const isUUID: (string: string) => boolean;
|
|
2
|
+
export const isUUIDv4: (string: string) => boolean;
|
|
3
|
+
|
|
4
|
+
export const isUri: (uri: string) => boolean;
|
|
5
|
+
export const isUriReference: (uriReference: string) => boolean;
|
|
6
|
+
export const isAbsoluteUri: (uri: string) => boolean;
|
|
7
|
+
|
|
8
|
+
export const parseUri: (uri: string) => IdentifierComponents;
|
|
9
|
+
export const parseUriReference: (uriReference: string) => RelativeIdentifierComponents;
|
|
10
|
+
export const parseAbsoluteUri: (uri: string) => AbsoluteIdentifierComponents;
|
|
11
|
+
|
|
12
|
+
export const isIri: (iri: string) => boolean;
|
|
13
|
+
export const isIriReference: (iriReference: string) => boolean;
|
|
14
|
+
export const isAbsoluteIri: (iri: string) => boolean;
|
|
15
|
+
|
|
16
|
+
export const parseIri: (iri: string) => IdentifierComponents;
|
|
17
|
+
export const parseIriReference: (iriReference: string) => RelativeIdentifierComponents;
|
|
18
|
+
export const parseAbsoluteIri: (iri: string) => AbsoluteIdentifierComponents;
|
|
19
|
+
|
|
20
|
+
export const normalizeReference: (reference: string) => string;
|
|
21
|
+
export const resolveReference: (reference: string, base: string, strict?: boolean, returnParts?: boolean) => string;
|
|
22
|
+
export const toAbsoluteReference: (reference: string) => string;
|
|
23
|
+
export const toRelativeReference: (target: string, base: string) => string;
|
|
24
|
+
|
|
25
|
+
type IdentifierComponents = {
|
|
26
|
+
scheme: string;
|
|
27
|
+
authority: string;
|
|
28
|
+
userinfo?: string;
|
|
29
|
+
host: string;
|
|
30
|
+
port?: string;
|
|
31
|
+
path: string;
|
|
32
|
+
query?: string;
|
|
33
|
+
fragment?: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type RelativeIdentifierComponents = {
|
|
37
|
+
scheme?: string;
|
|
38
|
+
authority?: string;
|
|
39
|
+
userinfo?: string;
|
|
40
|
+
host?: string;
|
|
41
|
+
port?: string;
|
|
42
|
+
path: string;
|
|
43
|
+
query?: string;
|
|
44
|
+
fragment?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type AbsoluteIdentifierComponents = {
|
|
48
|
+
scheme: string;
|
|
49
|
+
authority: string;
|
|
50
|
+
userinfo?: string;
|
|
51
|
+
host: string;
|
|
52
|
+
port?: string;
|
|
53
|
+
path: string;
|
|
54
|
+
query?: string;
|
|
55
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// a valid URI is always a valid IRI
|
|
3
|
+
const { recursiveCompile } = require('url-templates');
|
|
4
|
+
const patterns = new Map();
|
|
5
|
+
// RFC3986/RFC3987 common rules
|
|
6
|
+
const commonRules = {
|
|
7
|
+
scheme: '[a-zA-Z][a-zA-Z0-9+.-]*',
|
|
8
|
+
port: '[0-9]*',
|
|
9
|
+
IP_literal: '\\[(?:{IPv6address}|{IPvFuture})\\]',
|
|
10
|
+
IPv6address: '(?:(?:{h16}:){6}{ls32}|::(?:{h16}:){5}{ls32}|(?:(?:{h16})?)::(?:{h16}:){4}{ls32}|(?:(?:{h16}:)?{h16})?::(?:{h16}:){3}{ls32}|(?:(?:{h16}:){0,2}{h16})?::(?:{h16}:){2}{ls32}|(?:(?:{h16}:){0,3}{h16})?::(?:{h16}:){1}{ls32}|(?:(?:{h16}:){0,4}{h16})?::{ls32}|(?:(?:{h16}:){0,5}{h16})?::{h16}|(?:(?:{h16}:){0,6}{h16})?::)',
|
|
11
|
+
ls32: '(?:{h16}:{h16}|{IPv4address})',
|
|
12
|
+
h16: '{hexdig}{1,4}',
|
|
13
|
+
IPv4address: '(?:{dec_octet}\\.){3}{dec_octet}',
|
|
14
|
+
dec_octet: '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)',
|
|
15
|
+
IPvFuture: 'v{hexdig}+\\.(?:{unreserved}|{sub_delims}|:)+',
|
|
16
|
+
unreserved: '[a-zA-Z0-9_.~-]',
|
|
17
|
+
reserved: '(?:{gen_delims}|{sub_delims})',
|
|
18
|
+
pct_encoded: '%{hexdig}{2}',
|
|
19
|
+
gen_delims: '[:/?#[\\]@]',
|
|
20
|
+
sub_delims: "[!&'()*+,;=$]",
|
|
21
|
+
hexdig: '[0-9A-Fa-f]',
|
|
22
|
+
uuid: '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}',
|
|
23
|
+
uuid_v4: '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}',
|
|
24
|
+
};
|
|
25
|
+
// RFC3986 rules
|
|
26
|
+
const uriRules = {
|
|
27
|
+
URI_reference: '(?:{URI}|{relative_ref})',
|
|
28
|
+
URI: '{absolute_URI}(?:#{fragment})?',
|
|
29
|
+
absolute_URI: '{scheme}:{hier_part}(?:\\?{query})?',
|
|
30
|
+
relative_ref: '{relative_part}(?:\\?{query})?(?:#{fragment})?',
|
|
31
|
+
hier_part: '(?:\/\/{authority}{path_abempty}|{path_absolute}|{path_rootless}|{path_empty})',
|
|
32
|
+
relative_part: '(?:\/\/{authority}{path_abempty}|{path_absolute}|{path_noscheme}|{path_empty})',
|
|
33
|
+
authority: '(?:{userinfo}@)?{host}(?::{port})?',
|
|
34
|
+
host: '(?:{IP_literal}|{IPv4address}|{reg_name})',
|
|
35
|
+
userinfo: '(?:{unreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
36
|
+
reg_name: '(?:{unreserved}|{pct_encoded}|{sub_delims})*',
|
|
37
|
+
path: '(?:{path_abempty}|{path_absolute}|{path_noscheme}|{path_rootless}|{path_empty})',
|
|
38
|
+
path_abempty: '(?:\/{segment})*',
|
|
39
|
+
path_absolute: '\/(?:{segment_nz}(?:\/{segment})*)?',
|
|
40
|
+
path_noscheme: '{segment_nz_nc}(?:\/{segment})*',
|
|
41
|
+
path_rootless: '{segment_nz}(?:\/{segment})*',
|
|
42
|
+
path_empty: '',
|
|
43
|
+
segment: '{pchar}*',
|
|
44
|
+
segment_nz: '{pchar}+',
|
|
45
|
+
segment_nz_nc: '(?:{unreserved}|{pct_encoded}|{sub_delims}|@)+',
|
|
46
|
+
query: '(?:{pchar}|\/|\\?)*',
|
|
47
|
+
fragment: '(?:{pchar}|\/|\\?)*',
|
|
48
|
+
pchar: '(?:{unreserved}|{pct_encoded}|{sub_delims}|:|@)',
|
|
49
|
+
};
|
|
50
|
+
// RFC3987 rules
|
|
51
|
+
const iriRules = {
|
|
52
|
+
IRI_reference: '(?:{IRI}|{irelative_ref})',
|
|
53
|
+
IRI: '{absolute_IRI}(?:#{ifragment})?',
|
|
54
|
+
absolute_IRI: '{scheme}:{ihier_part}(?:\\?{iquery})?',
|
|
55
|
+
irelative_ref: '(?:{irelative_part}(?:\\?{iquery})?(?:#{ifragment})?)',
|
|
56
|
+
ihier_part: '(?:\/\/{iauthority}{ipath_abempty}|{ipath_absolute}|{ipath_rootless}|{ipath_empty})',
|
|
57
|
+
irelative_part: '(?:\/\/{iauthority}{ipath_abempty}|{ipath_absolute}|{ipath_noscheme}|{ipath_empty})',
|
|
58
|
+
iauthority: '(?:{iuserinfo}@)?{ihost}(?::{port})?',
|
|
59
|
+
iuserinfo: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:)*',
|
|
60
|
+
ihost: '(?:{IP_literal}|{IPv4address}|{ireg_name})',
|
|
61
|
+
ireg_name: '(?:{iunreserved}|{pct_encoded}|{sub_delims})*',
|
|
62
|
+
ipath: '(?:{ipath_abempty}|{ipath_absolute}|{ipath_noscheme}|{ipath_rootless}|{ipath_empty})',
|
|
63
|
+
ipath_empty: '',
|
|
64
|
+
ipath_rootless: '{isegment_nz}(?:\/{isegment})*',
|
|
65
|
+
ipath_noscheme: '{isegment_nz_nc}(?:\/{isegment})*',
|
|
66
|
+
ipath_absolute: '\/(?:{isegment_nz}(?:\/{isegment})*)?',
|
|
67
|
+
ipath_abempty: '(?:\/{isegment})*',
|
|
68
|
+
isegment_nz_nc: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|@)+',
|
|
69
|
+
isegment_nz: '{ipchar}+',
|
|
70
|
+
isegment: '{ipchar}*',
|
|
71
|
+
iquery: '(?:{ipchar}|{iprivate}|\/|\\?)*',
|
|
72
|
+
ifragment: '(?:{ipchar}|\/|\\?)*',
|
|
73
|
+
ipchar: '(?:{iunreserved}|{pct_encoded}|{sub_delims}|:|@)',
|
|
74
|
+
iunreserved: '(?:[a-zA-Z0-9._~-]|{ucschar})',
|
|
75
|
+
iprivate: '[\\uE000-\\uF8FF\\u{F0000}-\\u{FFFFD}\\u{100000}-\\u{10FFFD}]',
|
|
76
|
+
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}]',
|
|
77
|
+
};
|
|
78
|
+
// pattern RFC group names
|
|
79
|
+
const groupNames = {
|
|
80
|
+
scheme: 'scheme',
|
|
81
|
+
port: 'port',
|
|
82
|
+
authority: 'authority',
|
|
83
|
+
host: 'host',
|
|
84
|
+
userinfo: 'userinfo',
|
|
85
|
+
query: 'query',
|
|
86
|
+
fragment: 'fragment',
|
|
87
|
+
iauthority: 'authority',
|
|
88
|
+
ihost: 'host',
|
|
89
|
+
iuserinfo: 'userinfo',
|
|
90
|
+
iquery: 'query',
|
|
91
|
+
ifragment: 'fragment',
|
|
92
|
+
path_abempty: 'path',
|
|
93
|
+
path_absolute: 'path',
|
|
94
|
+
path_noscheme: 'path',
|
|
95
|
+
path_rootless: 'path',
|
|
96
|
+
path_empty: 'path',
|
|
97
|
+
ipath_abempty: 'path',
|
|
98
|
+
ipath_absolute: 'path',
|
|
99
|
+
ipath_noscheme: 'path',
|
|
100
|
+
ipath_rootless: 'path',
|
|
101
|
+
ipath_empty: 'path',
|
|
102
|
+
};
|
|
103
|
+
// all rules into one map
|
|
104
|
+
const rules = Object.assign({}, commonRules, uriRules, iriRules);
|
|
105
|
+
const addNames = (key) => (groupNames[key] ? `(?<${groupNames[key]}>${rules[key]})` : rules[key]);
|
|
106
|
+
// parse (slower, it uses regex.exec and includes named capture groups)
|
|
107
|
+
const parse = (string, rule) => {
|
|
108
|
+
if (!patterns.has('_' + rule)) patterns.set('_' + rule, new RegExp(`^${recursiveCompile(rules, rule, addNames)}$`, 'u'));
|
|
109
|
+
const match = patterns.get('_' + rule).exec(string);
|
|
110
|
+
if (match) return match.groups;
|
|
111
|
+
throw new TypeError(`Invalid ${rule.replace('_', '-')}: ${string}`);
|
|
112
|
+
};
|
|
113
|
+
// validate (faster, it uses regex.test and does not include named capture groups)
|
|
114
|
+
const validate = (string, rule) => {
|
|
115
|
+
if (!patterns.has(rule)) patterns.set(rule, new RegExp(`^${recursiveCompile(rules, rule)}$`, 'u'));
|
|
116
|
+
return patterns.get(rule).test(string);
|
|
117
|
+
};
|
|
118
|
+
// compose as per RFC 3986 Section 5.3 (component recomposition)
|
|
119
|
+
function compose(parts = {}) {
|
|
120
|
+
let result = '';
|
|
121
|
+
if (parts.scheme) result += parts.scheme + ':';
|
|
122
|
+
if (parts.authority) result += '//' + parts.authority;
|
|
123
|
+
result += parts.path || '';
|
|
124
|
+
if (parts.query) result += '?' + parts.query;
|
|
125
|
+
if (parts.fragment) result += '#' + parts.fragment;
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
// remove dot segments algorithm per RFC 3986 Section 5.2.4 (loop and replace)
|
|
129
|
+
function removeDotSegments(path) {
|
|
130
|
+
const output = [];
|
|
131
|
+
let input = path;
|
|
132
|
+
while (input.length > 0) {
|
|
133
|
+
if (input.startsWith('../')) input = input.slice(3);
|
|
134
|
+
else if (input.startsWith('./')) input = input.slice(2);
|
|
135
|
+
else if (input.startsWith('/./')) input = input.replace('/./', '/');
|
|
136
|
+
else if (input === '/.') input = '/';
|
|
137
|
+
else if (input.startsWith('/../')) {
|
|
138
|
+
input = input.replace('/../', '/');
|
|
139
|
+
if (output.length) output.pop();
|
|
140
|
+
} else if (input === '/..') {
|
|
141
|
+
input = '/';
|
|
142
|
+
if (output.length) output.pop();
|
|
143
|
+
} else if (input === '.' || input === '..') {
|
|
144
|
+
input = '';
|
|
145
|
+
} else {
|
|
146
|
+
// move first segment from input to output
|
|
147
|
+
let seg = '';
|
|
148
|
+
if (input[0] === '/') {
|
|
149
|
+
// keep leading '/'
|
|
150
|
+
const idx = input.indexOf('/', 1);
|
|
151
|
+
if (idx === -1) {
|
|
152
|
+
seg = input;
|
|
153
|
+
input = '';
|
|
154
|
+
} else {
|
|
155
|
+
seg = input.slice(0, idx);
|
|
156
|
+
input = input.slice(idx);
|
|
157
|
+
}
|
|
158
|
+
} else {
|
|
159
|
+
const idx = input.indexOf('/');
|
|
160
|
+
if (idx === -1) {
|
|
161
|
+
seg = input;
|
|
162
|
+
input = '';
|
|
163
|
+
} else {
|
|
164
|
+
seg = input.slice(0, idx);
|
|
165
|
+
input = input.slice(idx);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
output.push(seg);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return output.join('');
|
|
172
|
+
}
|
|
173
|
+
// resolve as per RFC https://datatracker.ietf.org/doc/html/rfc3986#section-5.2
|
|
174
|
+
function resolveReference(reference, base, strict = true, parts = false) {
|
|
175
|
+
let B;
|
|
176
|
+
if (typeof base === 'string') {
|
|
177
|
+
B = parse(base, 'IRI');
|
|
178
|
+
} else {
|
|
179
|
+
B = Object.assign({}, base);
|
|
180
|
+
}
|
|
181
|
+
if (!B.scheme) throw new Error('Expected an URI/IRI (with scheme) as base.');
|
|
182
|
+
|
|
183
|
+
let R;
|
|
184
|
+
if (typeof reference === 'string') {
|
|
185
|
+
R = parse(reference, 'IRI_reference');
|
|
186
|
+
} else {
|
|
187
|
+
R = Object.assign({}, reference);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let T;
|
|
191
|
+
if (R.scheme && (strict || R.scheme !== B.scheme)) {
|
|
192
|
+
T = R;
|
|
193
|
+
} else {
|
|
194
|
+
T = {};
|
|
195
|
+
T.scheme = B.scheme;
|
|
196
|
+
if (R.authority !== undefined && R.authority !== null) {
|
|
197
|
+
T.authority = R.authority;
|
|
198
|
+
T.path = R.path;
|
|
199
|
+
T.query = R.query;
|
|
200
|
+
} else {
|
|
201
|
+
T.authority = B.authority;
|
|
202
|
+
if (R.path && R.path.length > 0) {
|
|
203
|
+
if (R.path.startsWith('/')) {
|
|
204
|
+
T.path = R.path;
|
|
205
|
+
} else if (B.authority !== undefined && B.authority !== null && (!B.path || B.path.length === 0)) {
|
|
206
|
+
T.path = '/' + R.path;
|
|
207
|
+
} else {
|
|
208
|
+
// merge base path and ref path
|
|
209
|
+
const idx = B.path ? B.path.lastIndexOf('/') : -1;
|
|
210
|
+
const prefix = idx !== -1 ? B.path.slice(0, idx + 1) : '';
|
|
211
|
+
T.path = prefix + R.path;
|
|
212
|
+
}
|
|
213
|
+
T.query = R.query;
|
|
214
|
+
} else {
|
|
215
|
+
T.path = B.path;
|
|
216
|
+
if (R.query !== undefined && R.query !== null) T.query = R.query;
|
|
217
|
+
else T.query = B.query;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
T.fragment = R.fragment;
|
|
221
|
+
}
|
|
222
|
+
T.path = removeDotSegments(T.path || '');
|
|
223
|
+
if (parts) return T;
|
|
224
|
+
return compose(T);
|
|
225
|
+
}
|
|
226
|
+
// reverse logic for resolve
|
|
227
|
+
const toRelativeReference = (target, base) => {
|
|
228
|
+
const B = parse(base, 'absolute_IRI');
|
|
229
|
+
const T = parse(target, 'IRI');
|
|
230
|
+
if (T.scheme !== B.scheme || T.authority !== B.authority) return target;
|
|
231
|
+
let result;
|
|
232
|
+
if (B.path === T.path) {
|
|
233
|
+
result = '';
|
|
234
|
+
} else {
|
|
235
|
+
const baseSegments = B.path.split('/');
|
|
236
|
+
const targetSegments = T.path.split('/');
|
|
237
|
+
let position = 0;
|
|
238
|
+
while (baseSegments[position] === targetSegments[position] && position < baseSegments.length - 1 && position < targetSegments.length - 1) {
|
|
239
|
+
position++;
|
|
240
|
+
}
|
|
241
|
+
const segments = [];
|
|
242
|
+
for (let index = position + 1; index < baseSegments.length; index++) segments.push('..');
|
|
243
|
+
for (let index = position; index < targetSegments.length; index++) segments.push(targetSegments[index]);
|
|
244
|
+
result = segments.join('/');
|
|
245
|
+
}
|
|
246
|
+
if (T.query !== undefined) result += `?${T.query}`;
|
|
247
|
+
if (T.fragment !== undefined) result += `#${T.fragment}`;
|
|
248
|
+
return result;
|
|
249
|
+
};
|
|
250
|
+
// export
|
|
251
|
+
module.exports = {
|
|
252
|
+
isUUID: (string) => validate(string, 'uuid'),
|
|
253
|
+
isUUIDv4: (string) => validate(string, 'uuid_v4'),
|
|
254
|
+
isUri: (string) => validate(string, 'URI'),
|
|
255
|
+
isUriReference: (string) => validate(string, 'URI_reference'),
|
|
256
|
+
isAbsoluteUri: (string) => validate(string, 'absolute_URI'),
|
|
257
|
+
parseUri: (string) => parse(string, 'URI'),
|
|
258
|
+
parseUriReference: (string) => parse(string, 'URI_reference'),
|
|
259
|
+
parseAbsoluteUri: (string) => parse(string, 'absolute_URI'),
|
|
260
|
+
isIri: (string) => validate(string, 'IRI'),
|
|
261
|
+
isIriReference: (string) => validate(string, 'IRI_reference'),
|
|
262
|
+
isAbsoluteIri: (string) => validate(string, 'absolute_IRI'),
|
|
263
|
+
parseIri: (string) => parse(string, 'IRI'),
|
|
264
|
+
parseIriReference: (string) => parse(string, 'IRI_reference'),
|
|
265
|
+
parseAbsoluteIri: (string) => parse(string, 'absolute_IRI'),
|
|
266
|
+
resolveReference, // not yet decided if the return should be normalized or not, and in what extent
|
|
267
|
+
normalizeReference: (string) => string, // not done yet
|
|
268
|
+
toAbsoluteReference: (string) => resolveReference('', string),
|
|
269
|
+
toRelativeReference,
|
|
270
|
+
};
|
package/package.json
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "identifier-js",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A RFC3986 / RFC3987 compliant fast parser/validator/resolver/composer for NodeJS and browser.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"IRI",
|
|
7
|
-
"URI",
|
|
8
|
-
"IRI-reference",
|
|
9
|
-
"URI-reference",
|
|
10
|
-
"ipv4",
|
|
11
|
-
"ipv6",
|
|
12
|
-
"uuid",
|
|
13
|
-
"parser",
|
|
14
|
-
"validator",
|
|
15
|
-
"RFC",
|
|
16
|
-
"3987",
|
|
17
|
-
"RFC",
|
|
18
|
-
"3986"
|
|
19
|
-
],
|
|
20
|
-
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
21
|
-
"bugs": {
|
|
22
|
-
"url": "https://github.com/SorinGFS/identifier-js/issues"
|
|
23
|
-
},
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "git+https://github.com/SorinGFS/identifier-js.git"
|
|
27
|
-
},
|
|
28
|
-
"license": "MIT",
|
|
29
|
-
"author": "SorinGFS",
|
|
30
|
-
"type": "commonjs",
|
|
31
|
-
"main": "index.js",
|
|
32
|
-
"scripts": {
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "identifier-js",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A RFC3986 / RFC3987 compliant fast parser/validator/resolver/composer for NodeJS and browser.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"IRI",
|
|
7
|
+
"URI",
|
|
8
|
+
"IRI-reference",
|
|
9
|
+
"URI-reference",
|
|
10
|
+
"ipv4",
|
|
11
|
+
"ipv6",
|
|
12
|
+
"uuid",
|
|
13
|
+
"parser",
|
|
14
|
+
"validator",
|
|
15
|
+
"RFC",
|
|
16
|
+
"3987",
|
|
17
|
+
"RFC",
|
|
18
|
+
"3986"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/SorinGFS/identifier-js/issues"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/SorinGFS/identifier-js.git"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "SorinGFS",
|
|
30
|
+
"type": "commonjs",
|
|
31
|
+
"main": "index.js",
|
|
32
|
+
"scripts": {
|
|
33
|
+
"update-deps": "npx npm-check-updates -u && npm install",
|
|
34
|
+
"test": "node tests/vitest-setup && npm test"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"url-templates": "^1.0.4"
|
|
38
|
+
}
|
|
39
|
+
}
|
package/readme.md
CHANGED
|
@@ -15,3 +15,85 @@ A fully RFC [3986](https://datatracker.ietf.org/doc/html/rfc3986.html)/[3897](ht
|
|
|
15
15
|
```bash title="console"
|
|
16
16
|
npm i identifier-js
|
|
17
17
|
```
|
|
18
|
+
|
|
19
|
+
## API
|
|
20
|
+
|
|
21
|
+
Exports are documented below.
|
|
22
|
+
|
|
23
|
+
### URI (RFC 3986)
|
|
24
|
+
|
|
25
|
+
#### Validation
|
|
26
|
+
|
|
27
|
+
Validate a URI:
|
|
28
|
+
- isUri: (value: string) => boolean
|
|
29
|
+
|
|
30
|
+
Validate a URI reference (absolute or relative):
|
|
31
|
+
- isUriReference: (value: string) => boolean
|
|
32
|
+
|
|
33
|
+
Validate an absolute URI (must include scheme):
|
|
34
|
+
- isAbsoluteUri: (value: string) => boolean
|
|
35
|
+
|
|
36
|
+
#### Parsing
|
|
37
|
+
|
|
38
|
+
Parse a URI into structured components:
|
|
39
|
+
- parseUri: (value: string) => IdentifierComponents
|
|
40
|
+
|
|
41
|
+
Parse a URI reference into structured components:
|
|
42
|
+
- parseUriReference: (value: string) => RelativeIdentifierComponents
|
|
43
|
+
|
|
44
|
+
Parse an absolute URI (must include scheme):
|
|
45
|
+
- parseAbsoluteUri: (value: string) => AbsoluteIdentifierComponents
|
|
46
|
+
|
|
47
|
+
### IRI (RFC 3987)
|
|
48
|
+
|
|
49
|
+
#### Validation
|
|
50
|
+
|
|
51
|
+
Validate an IRI:
|
|
52
|
+
- isIri: (value: string) => boolean
|
|
53
|
+
|
|
54
|
+
Validate an IRI reference (absolute or relative):
|
|
55
|
+
- isIriReference: (value: string) => boolean
|
|
56
|
+
|
|
57
|
+
Validate an absolute IRI (must include scheme):
|
|
58
|
+
- isAbsoluteIri: (value: string) => boolean
|
|
59
|
+
|
|
60
|
+
#### Parsing
|
|
61
|
+
|
|
62
|
+
Parse an IRI into structured components:
|
|
63
|
+
- parseIri: (value: string) => IdentifierComponents
|
|
64
|
+
|
|
65
|
+
Parse an IRI reference into structured components:
|
|
66
|
+
- parseIriReference: (value: string) => RelativeIdentifierComponents
|
|
67
|
+
|
|
68
|
+
Parse an absolute IRI (must include scheme):
|
|
69
|
+
- parseAbsoluteIri: (value: string) => AbsoluteIdentifierComponents
|
|
70
|
+
|
|
71
|
+
### Reference Utilities (URI/IRI)
|
|
72
|
+
|
|
73
|
+
Normalize a URI or IRI reference:
|
|
74
|
+
- normalizeReference: (reference: string) => string
|
|
75
|
+
|
|
76
|
+
Resolve a reference against a base identifier:
|
|
77
|
+
- resolveReference: (reference: string, base: string, strict?: boolean, returnParts?: boolean) => string
|
|
78
|
+
|
|
79
|
+
**Note:**
|
|
80
|
+
- strict enables strict resolution behavior.
|
|
81
|
+
- returnParts returns structured components instead of a string.
|
|
82
|
+
|
|
83
|
+
Convert a reference into absolute form:
|
|
84
|
+
- toAbsoluteReference: (reference: string) => string
|
|
85
|
+
|
|
86
|
+
Compute a relative reference from base to target:
|
|
87
|
+
- toRelativeReference: (target: string, base: string) => string
|
|
88
|
+
|
|
89
|
+
### UUID
|
|
90
|
+
|
|
91
|
+
Validate a UUID (any version):
|
|
92
|
+
- isUUID: (value: string) => boolean
|
|
93
|
+
|
|
94
|
+
Validate a UUID version 4:
|
|
95
|
+
- isUUIDv4: (value: string) => boolean
|
|
96
|
+
|
|
97
|
+
## Testing
|
|
98
|
+
|
|
99
|
+
- npm test
|