identifier-js 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +24 -5
- package/normalization.md +299 -0
- package/package.json +41 -41
- package/readme.md +433 -64
- package/docs/rfc3986-rfc3987-abnf.yaml +0 -295
- package/docs/rfc9110-rfc9112-abnf.yaml +0 -267
- package/tests/normalize.test-off.js +0 -21
- package/tests/parse.test.js +0 -472
- package/tests/performance.js +0 -46
- package/tests/resolve.rfc.test.js +0 -63
- package/tests/resolve.test.js +0 -16
- package/tests/to-absolute.test.js +0 -28
- package/tests/to-relative.test.js +0 -50
- package/tests/validation-ip-port.test.js +0 -278
- package/tests/validation-scheme-specific.test.js +0 -604
- package/tests/validation.test.js +0 -278
- package/tests/vitest-setup.js +0 -19
package/index.js
CHANGED
|
@@ -140,10 +140,10 @@ const validate = (string, rule) => {
|
|
|
140
140
|
function compose(parts = {}) {
|
|
141
141
|
let result = '';
|
|
142
142
|
if (parts.scheme) result += parts.scheme + ':';
|
|
143
|
-
if (parts.authority) result += '//' + parts.authority;
|
|
144
|
-
result += parts.path
|
|
145
|
-
if (parts.query) result += '?' + parts.query;
|
|
146
|
-
if (parts.fragment) result += '#' + parts.fragment;
|
|
143
|
+
if (parts.authority !== undefined && parts.authority !== null) result += '//' + parts.authority;
|
|
144
|
+
result += parts.path ?? '';
|
|
145
|
+
if (parts.query !== undefined && parts.query !== null) result += '?' + parts.query;
|
|
146
|
+
if (parts.fragment !== undefined && parts.fragment !== null) result += '#' + parts.fragment;
|
|
147
147
|
return result;
|
|
148
148
|
}
|
|
149
149
|
// remove dot segments algorithm per RFC 3986 Section 5.2.4 (loop and replace)
|
|
@@ -251,21 +251,40 @@ const toRelativeReference = (target, base) => {
|
|
|
251
251
|
if (T.scheme !== B.scheme || T.authority !== B.authority) return target;
|
|
252
252
|
let result;
|
|
253
253
|
if (B.path === T.path) {
|
|
254
|
-
|
|
254
|
+
if (T.query === undefined && B.query !== undefined) {
|
|
255
|
+
// Use an explicit path to prevent the base query from being inherited.
|
|
256
|
+
if (T.path.startsWith('/')) result = T.path;
|
|
257
|
+
else if (T.path) {
|
|
258
|
+
const segment = T.path.slice(T.path.lastIndexOf('/') + 1);
|
|
259
|
+
result = segment && !segment.includes(':') ? segment : `./${segment}`;
|
|
260
|
+
} else if (T.authority !== undefined) result = `//${T.authority}`;
|
|
261
|
+
else return target;
|
|
262
|
+
} else result = '';
|
|
263
|
+
} else if (!T.path) {
|
|
264
|
+
// A network-path reference is required to represent an empty path without inheritance.
|
|
265
|
+
if (T.authority !== undefined) result = `//${T.authority}`;
|
|
266
|
+
else return target;
|
|
255
267
|
} else {
|
|
256
268
|
const baseSegments = B.path.split('/');
|
|
257
269
|
const targetSegments = T.path.split('/');
|
|
258
270
|
let position = 0;
|
|
271
|
+
// Find the common path prefix before constructing the relative traversal.
|
|
259
272
|
while (baseSegments[position] === targetSegments[position] && position < baseSegments.length - 1 && position < targetSegments.length - 1) {
|
|
260
273
|
position++;
|
|
261
274
|
}
|
|
262
275
|
const segments = [];
|
|
276
|
+
// Backtrack from the base resource to the common path prefix.
|
|
263
277
|
for (let index = position + 1; index < baseSegments.length; index++) segments.push('..');
|
|
278
|
+
// Append the target path after the common prefix.
|
|
264
279
|
for (let index = position; index < targetSegments.length; index++) segments.push(targetSegments[index]);
|
|
265
280
|
result = segments.join('/');
|
|
281
|
+
if (!result) result = T.path.startsWith('/') ? T.path : './';
|
|
282
|
+
else if (/^[^/]*:/.test(result)) result = './' + result;
|
|
266
283
|
}
|
|
267
284
|
if (T.query !== undefined) result += `?${T.query}`;
|
|
268
285
|
if (T.fragment !== undefined) result += `#${T.fragment}`;
|
|
286
|
+
// Parent traversal would convert a rootless path into an absolute path during resolution.
|
|
287
|
+
if (T.authority === undefined && !T.path.startsWith('/') && result.startsWith('..')) return target;
|
|
269
288
|
return result;
|
|
270
289
|
};
|
|
271
290
|
// export
|
package/normalization.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Identifier normalization research notes
|
|
2
|
+
|
|
3
|
+
These notes summarize the standards and design choices that should be resolved before implementing `normalizeReference`. They are research material, not the current API contract.
|
|
4
|
+
|
|
5
|
+
## Central constraint
|
|
6
|
+
|
|
7
|
+
There is no universal canonical form for every URI or IRI. RFC 3986 defines comparison in levels because a transformation that is safe for one scheme or application can merge distinct identifiers in another.
|
|
8
|
+
|
|
9
|
+
A normalizer should minimize false negatives without creating false positives:
|
|
10
|
+
|
|
11
|
+
1. **Simple comparison** — compare characters exactly.
|
|
12
|
+
2. **Syntax-based normalization** — apply transformations licensed by generic URI syntax.
|
|
13
|
+
3. **Scheme-based normalization** — apply additional equivalences defined by a scheme.
|
|
14
|
+
4. **Protocol-based normalization** — use equivalences established by observed protocol behavior.
|
|
15
|
+
|
|
16
|
+
`normalizeReference` should implement only an explicitly selected level. Protocol-derived normalization does not belong in a deterministic identifier library.
|
|
17
|
+
|
|
18
|
+
Reference: [RFC 3986 §6](https://www.rfc-editor.org/rfc/rfc3986#section-6).
|
|
19
|
+
|
|
20
|
+
## Generic syntax-based normalization
|
|
21
|
+
|
|
22
|
+
The following transformations are suitable candidates for a generic URI profile.
|
|
23
|
+
|
|
24
|
+
### Scheme and host case
|
|
25
|
+
|
|
26
|
+
- Lowercase the scheme.
|
|
27
|
+
- Lowercase the host.
|
|
28
|
+
- Do not lowercase userinfo, path, query, or fragment.
|
|
29
|
+
- Preserve Unicode component case unless a scheme or external policy defines equivalence.
|
|
30
|
+
|
|
31
|
+
Example:
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
HTTP://WWW.EXAMPLE.COM/ → http://www.example.com/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Reference: RFC 3986 §6.2.2.1.
|
|
38
|
+
|
|
39
|
+
### Percent-triplet case
|
|
40
|
+
|
|
41
|
+
Uppercase hexadecimal letters in every valid percent triplet:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
%3a → %3A
|
|
45
|
+
%2f → %2F
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This changes presentation, not the represented octet.
|
|
49
|
+
|
|
50
|
+
Reference: RFC 3986 §§2.1 and 6.2.2.1.
|
|
51
|
+
|
|
52
|
+
### Decode percent-encoded unreserved characters
|
|
53
|
+
|
|
54
|
+
Decode a percent triplet only when it represents an ASCII unreserved character:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
ALPHA / DIGIT / "-" / "." / "_" / "~"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Examples:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
%63 → c
|
|
64
|
+
%7E → ~
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Do not generically decode reserved characters. `%2F` and `/`, for example, can have different structural meanings.
|
|
68
|
+
|
|
69
|
+
Parse components before decoding so an encoded delimiter cannot be mistaken for syntax. Never decode the same data twice.
|
|
70
|
+
|
|
71
|
+
References: RFC 3986 §§2.2–2.4 and 6.2.2.2.
|
|
72
|
+
|
|
73
|
+
### Remove dot segments
|
|
74
|
+
|
|
75
|
+
Apply the RFC 3986 §5.2.4 algorithm to the parsed path:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
/a/b/c/./../../g → /a/g
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Only complete `.` and `..` path segments are special. Do not process similar text in a query or fragment:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
g?y/../x
|
|
85
|
+
g#s/../x
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
References: RFC 3986 §§5.2.4 and 6.2.2.3.
|
|
89
|
+
|
|
90
|
+
### Preserve component presence
|
|
91
|
+
|
|
92
|
+
Recomposition must distinguish an absent component from a present but empty component:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
https://example.com/path
|
|
96
|
+
https://example.com/path?
|
|
97
|
+
https://example.com/path#
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The same applies to an empty authority. Component delimiters must be emitted based on definedness, not truthiness.
|
|
101
|
+
|
|
102
|
+
Reference: RFC 3986 §5.3.
|
|
103
|
+
|
|
104
|
+
## Scheme-based profiles
|
|
105
|
+
|
|
106
|
+
Scheme-specific transformations should not run unless the scheme profile is selected or automatic scheme handling is part of the documented contract.
|
|
107
|
+
|
|
108
|
+
### HTTP and HTTPS
|
|
109
|
+
|
|
110
|
+
RFC 9110 permits these normal forms:
|
|
111
|
+
|
|
112
|
+
- lowercase scheme and host;
|
|
113
|
+
- remove port `80` from `http`;
|
|
114
|
+
- remove port `443` from `https`;
|
|
115
|
+
- remove an explicitly empty port;
|
|
116
|
+
- use `/` when authority is present and path is empty;
|
|
117
|
+
- decode percent-encoded unreserved characters;
|
|
118
|
+
- preserve all other component case.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
HTTP://Example.COM:80 → http://example.com/
|
|
124
|
+
https://example.com:443/a → https://example.com/a
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
An HTTP request target does not include a fragment, but identifier normalization should not silently discard a fragment unless the selected operation specifically produces a request target.
|
|
128
|
+
|
|
129
|
+
Userinfo in HTTP and HTTPS targets is deprecated and should be rejected or reported by an HTTP policy rather than silently normalized away.
|
|
130
|
+
|
|
131
|
+
Reference: [RFC 9110 §§4.2.1–4.2.5](https://www.rfc-editor.org/rfc/rfc9110#section-4.2).
|
|
132
|
+
|
|
133
|
+
### WS and WSS
|
|
134
|
+
|
|
135
|
+
Potential scheme-specific rules include:
|
|
136
|
+
|
|
137
|
+
- default port `80` for `ws`;
|
|
138
|
+
- default port `443` for `wss`;
|
|
139
|
+
- `/` as the resource path when the path is empty;
|
|
140
|
+
- no fragment identifiers;
|
|
141
|
+
- IDN-to-ASCII handling under an explicitly selected hostname policy.
|
|
142
|
+
|
|
143
|
+
Fragment rejection is validation, not normalization. A normalizer should not repair a WebSocket URI by silently deleting its fragment.
|
|
144
|
+
|
|
145
|
+
Reference: [RFC 6455 §3](https://www.rfc-editor.org/rfc/rfc6455#section-3).
|
|
146
|
+
|
|
147
|
+
### File
|
|
148
|
+
|
|
149
|
+
A generic `file` normalizer is not recommended. Behavior varies by platform and filesystem:
|
|
150
|
+
|
|
151
|
+
- local empty authority versus `localhost`;
|
|
152
|
+
- POSIX roots;
|
|
153
|
+
- Windows drive-letter case and UNC paths;
|
|
154
|
+
- path case sensitivity;
|
|
155
|
+
- backslash handling;
|
|
156
|
+
- platform-specific Unicode normalization;
|
|
157
|
+
- reserved device names and namespace paths.
|
|
158
|
+
|
|
159
|
+
Require an explicit platform profile before applying these transformations.
|
|
160
|
+
|
|
161
|
+
Reference: [RFC 8089](https://www.rfc-editor.org/rfc/rfc8089).
|
|
162
|
+
|
|
163
|
+
### Other schemes
|
|
164
|
+
|
|
165
|
+
Apply only generic syntax normalization unless the scheme's authoritative specification defines additional equivalences. Do not infer default ports, path case rules, or authority behavior from a similar scheme.
|
|
166
|
+
|
|
167
|
+
## IRI and Unicode decisions
|
|
168
|
+
|
|
169
|
+
RFC 3987 recommends that creators produce IRIs in NFC, but comparison code must not arbitrarily normalize an existing Unicode IRI. Normalizing third-party text can merge identifiers that were intentionally distinct.
|
|
170
|
+
|
|
171
|
+
Recommended policy:
|
|
172
|
+
|
|
173
|
+
- do not apply Unicode normalization by default;
|
|
174
|
+
- offer NFC only as an explicit creation or application-policy option;
|
|
175
|
+
- do not offer NFKC as a generic identifier transformation;
|
|
176
|
+
- retain the original IRI when a normalized form is generated only as a comparison key.
|
|
177
|
+
|
|
178
|
+
### IRI-to-URI mapping is a separate operation
|
|
179
|
+
|
|
180
|
+
Mapping an IRI to a URI is not merely normalization:
|
|
181
|
+
|
|
182
|
+
1. encode non-ASCII `ucschar` and `iprivate` characters as UTF-8;
|
|
183
|
+
2. percent-encode each UTF-8 octet as `%HH`;
|
|
184
|
+
3. preserve existing valid percent triplets and URI-allowed characters;
|
|
185
|
+
4. apply an explicit IDNA policy to internationalized hostnames when required.
|
|
186
|
+
|
|
187
|
+
The reverse operation must decode only valid UTF-8 and must preserve encoded reserved characters. It must not guess legacy encodings.
|
|
188
|
+
|
|
189
|
+
Reference: [RFC 3987 §§3 and 5](https://www.rfc-editor.org/rfc/rfc3987).
|
|
190
|
+
|
|
191
|
+
## Transformations excluded by default
|
|
192
|
+
|
|
193
|
+
A generic normalizer should not:
|
|
194
|
+
|
|
195
|
+
- decode percent-encoded reserved characters;
|
|
196
|
+
- lowercase userinfo, paths, queries, or fragments;
|
|
197
|
+
- sort, deduplicate, or reinterpret query parameters;
|
|
198
|
+
- remove empty query or fragment delimiters;
|
|
199
|
+
- add or remove trailing slashes without scheme authority;
|
|
200
|
+
- apply Unicode NFC or NFKC automatically;
|
|
201
|
+
- convert an internationalized hostname without a defined IDNA version and policy;
|
|
202
|
+
- remove userinfo rather than reporting it;
|
|
203
|
+
- infer filesystem semantics for `file`;
|
|
204
|
+
- infer equivalence from redirects or successful retrievals;
|
|
205
|
+
- convert a relative reference without a supplied base.
|
|
206
|
+
|
|
207
|
+
## Suggested API design
|
|
208
|
+
|
|
209
|
+
Avoid a single aggressive operation. Two viable designs are:
|
|
210
|
+
|
|
211
|
+
### Explicit profiles
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
normalizeReference(reference, {
|
|
215
|
+
profile: 'generic', // generic | http | https | ws | wss
|
|
216
|
+
unicodeNormalization: false,
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
A `file` profile should additionally require a platform policy.
|
|
221
|
+
|
|
222
|
+
### Separate operations
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
normalizeReference(reference); // generic syntax only
|
|
226
|
+
normalizeHttpReference(reference); // HTTP/HTTPS scheme rules
|
|
227
|
+
normalizeWebSocketReference(reference); // WS/WSS scheme rules
|
|
228
|
+
iriToUri(reference, options); // explicit representation mapping
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Separate operations are harder to misuse and make compatibility changes more visible. A profile-based API is easier to extend. The final choice should follow the expected consumers.
|
|
232
|
+
|
|
233
|
+
## Suggested generic processing order
|
|
234
|
+
|
|
235
|
+
1. Parse and retain whether authority, query, and fragment are absent or empty.
|
|
236
|
+
2. Lowercase scheme and host where generic syntax permits it.
|
|
237
|
+
3. Normalize percent-triplet hexadecimal case.
|
|
238
|
+
4. Decode percent-encoded ASCII unreserved characters component by component.
|
|
239
|
+
5. Remove dot segments from the path.
|
|
240
|
+
6. Apply an explicitly selected scheme profile.
|
|
241
|
+
7. Apply Unicode normalization only when explicitly requested.
|
|
242
|
+
8. Recompose while preserving empty components.
|
|
243
|
+
9. Validate the normalized output under the same identifier and scheme policy.
|
|
244
|
+
10. Optionally verify idempotence:
|
|
245
|
+
|
|
246
|
+
```js
|
|
247
|
+
normalizeReference(normalizeReference(value)) === normalizeReference(value)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Acceptance scenarios
|
|
251
|
+
|
|
252
|
+
A generic profile should include at least these cases:
|
|
253
|
+
|
|
254
|
+
```text
|
|
255
|
+
HTTP://Example.COM/%7euser → http://example.com/~user
|
|
256
|
+
http://example.com/a/./b/../c → http://example.com/a/c
|
|
257
|
+
http://example.com/path? → http://example.com/path?
|
|
258
|
+
http://example.com/path# → http://example.com/path#
|
|
259
|
+
http://example.com/%2F → http://example.com/%2F
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
An HTTP profile can additionally include:
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
http://example.com:80 → http://example.com/
|
|
266
|
+
https://example.com:443/a → https://example.com/a
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Cases that must remain distinct under generic normalization include:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
http://example.com/a ≠ http://example.com/a/
|
|
273
|
+
http://example.com/path ≠ http://example.com/path?
|
|
274
|
+
http://example.com/%2F ≠ http://example.com//
|
|
275
|
+
http://example.com/A ≠ http://example.com/a
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Testing should cover URI and IRI forms, empty components, rootless paths, percent triplets, Unicode supplementary characters, idempotence, and normalization followed by parsing/recomposition.
|
|
279
|
+
|
|
280
|
+
## Open decisions
|
|
281
|
+
|
|
282
|
+
Before implementation, decide:
|
|
283
|
+
|
|
284
|
+
1. Whether `normalizeReference` is generic-only or selects profiles automatically by scheme.
|
|
285
|
+
2. Whether output preserves the input category: URI versus IRI.
|
|
286
|
+
3. Whether relative references are normalized in place or require a base and become absolute.
|
|
287
|
+
4. Whether Unicode NFC is offered, and for which creation contexts.
|
|
288
|
+
5. Whether IDNA conversion belongs here or in a dedicated hostname dependency.
|
|
289
|
+
6. Whether comparison keys and display/transport identifiers use separate APIs.
|
|
290
|
+
7. Whether unsupported scheme profiles throw, fall back to generic rules, or require an explicit option.
|
|
291
|
+
8. Whether current callers can rely on `normalizeReference` remaining an identity function until a major release.
|
|
292
|
+
|
|
293
|
+
## Authoritative references
|
|
294
|
+
|
|
295
|
+
- [RFC 3986 — Uniform Resource Identifier: Generic Syntax](https://www.rfc-editor.org/rfc/rfc3986)
|
|
296
|
+
- [RFC 3987 — Internationalized Resource Identifiers](https://www.rfc-editor.org/rfc/rfc3987)
|
|
297
|
+
- [RFC 9110 — HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110)
|
|
298
|
+
- [RFC 6455 — The WebSocket Protocol](https://www.rfc-editor.org/rfc/rfc6455)
|
|
299
|
+
- [RFC 8089 — The `file` URI Scheme](https://www.rfc-editor.org/rfc/rfc8089)
|
package/package.json
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
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
|
-
"RFC3986",
|
|
16
|
-
"RFC3987"
|
|
17
|
-
],
|
|
18
|
-
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
19
|
-
"bugs": {
|
|
20
|
-
"url": "https://github.com/SorinGFS/identifier-js/issues"
|
|
21
|
-
},
|
|
22
|
-
"repository": {
|
|
23
|
-
"type": "git",
|
|
24
|
-
"url": "git+https://github.com/SorinGFS/identifier-js.git"
|
|
25
|
-
},
|
|
26
|
-
"license": "MIT",
|
|
27
|
-
"author": "SorinGFS",
|
|
28
|
-
"type": "commonjs",
|
|
29
|
-
"main": "index.js",
|
|
30
|
-
"scripts": {
|
|
31
|
-
"update-deps": "npx npm-check-updates -u && npm install",
|
|
32
|
-
"test": "node tests/vitest-setup && npm test"
|
|
33
|
-
},
|
|
34
|
-
"dependencies": {
|
|
35
|
-
"url-templates": "^1.0.
|
|
36
|
-
},
|
|
37
|
-
"engines": {
|
|
38
|
-
"node": ">=18.0.0"
|
|
39
|
-
},
|
|
40
|
-
"engineStrict": true
|
|
41
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "identifier-js",
|
|
3
|
+
"version": "0.0.15",
|
|
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
|
+
"RFC3986",
|
|
16
|
+
"RFC3987"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/SorinGFS/identifier-js#readme",
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/SorinGFS/identifier-js/issues"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/SorinGFS/identifier-js.git"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "SorinGFS",
|
|
28
|
+
"type": "commonjs",
|
|
29
|
+
"main": "index.js",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"update-deps": "npx npm-check-updates -u && npm install",
|
|
32
|
+
"test": "node \"#/public/tests/vitest-setup.js\" && npm test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"url-templates": "^1.0.7"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"engineStrict": true
|
|
41
|
+
}
|