fast-uri 3.1.3 → 4.0.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/.github/dependabot.yml +42 -2
- package/.github/workflows/ci.yml +51 -3
- package/README.md +9 -9
- package/benchmark/encoder.mjs +54 -0
- package/index.js +1 -1
- package/lib/utils.js +87 -6
- package/package.json +4 -4
- package/test/equal.test.js +51 -0
- package/test/scheme-normalization.test.js +16 -0
- package/test/security-utf8-encoding.test.js +0 -0
- package/test/uri-js.test.js +1 -1
- package/tsconfig.json +8 -7
- package/types/index.d.ts +0 -9
- package/types/index.tst.ts +15 -0
- package/.github/workflows/package-manager-ci.yml +0 -24
- package/types/index.test-d.ts +0 -17
package/.github/dependabot.yml
CHANGED
|
@@ -2,12 +2,52 @@ version: 2
|
|
|
2
2
|
updates:
|
|
3
3
|
- package-ecosystem: "github-actions"
|
|
4
4
|
directory: "/"
|
|
5
|
+
commit-message:
|
|
6
|
+
# Prefix all commit messages with "chore: "
|
|
7
|
+
prefix: "chore"
|
|
5
8
|
schedule:
|
|
6
|
-
interval: "
|
|
9
|
+
interval: "weekly"
|
|
10
|
+
cooldown:
|
|
11
|
+
default-days: 7
|
|
12
|
+
allow:
|
|
13
|
+
- dependency-name: "*"
|
|
14
|
+
update-types:
|
|
15
|
+
- "version-update:semver-major"
|
|
7
16
|
open-pull-requests-limit: 10
|
|
8
17
|
|
|
9
18
|
- package-ecosystem: "npm"
|
|
10
19
|
directory: "/"
|
|
20
|
+
commit-message:
|
|
21
|
+
# Prefix all commit messages with "chore: "
|
|
22
|
+
prefix: "chore"
|
|
11
23
|
schedule:
|
|
12
|
-
interval: "
|
|
24
|
+
interval: "weekly"
|
|
25
|
+
cooldown:
|
|
26
|
+
default-days: 7
|
|
27
|
+
versioning-strategy: "increase-if-necessary"
|
|
28
|
+
allow:
|
|
29
|
+
- dependency-name: "*"
|
|
30
|
+
update-types:
|
|
31
|
+
- "version-update:semver-major"
|
|
32
|
+
ignore:
|
|
33
|
+
# TODO: remove ignore until neostandard support ESLint 10
|
|
34
|
+
- dependency-name: "eslint"
|
|
35
|
+
- dependency-name: "neostandard"
|
|
36
|
+
- dependency-name: "@stylistic/*"
|
|
13
37
|
open-pull-requests-limit: 10
|
|
38
|
+
groups:
|
|
39
|
+
# Production dependencies with breaking changes
|
|
40
|
+
dependencies:
|
|
41
|
+
dependency-type: "production"
|
|
42
|
+
# ESLint related dependencies
|
|
43
|
+
dev-dependencies-eslint:
|
|
44
|
+
patterns:
|
|
45
|
+
- "eslint"
|
|
46
|
+
- "neostandard"
|
|
47
|
+
- "@stylistic/*"
|
|
48
|
+
# TypeScript related dependencies
|
|
49
|
+
dev-dependencies-typescript:
|
|
50
|
+
patterns:
|
|
51
|
+
- "@types/*"
|
|
52
|
+
- "tstyche"
|
|
53
|
+
- "typescript"
|
package/.github/workflows/ci.yml
CHANGED
|
@@ -23,13 +23,61 @@ permissions:
|
|
|
23
23
|
contents: read
|
|
24
24
|
|
|
25
25
|
jobs:
|
|
26
|
+
type-tests:
|
|
27
|
+
name: Type Tests
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
strategy:
|
|
30
|
+
matrix:
|
|
31
|
+
node-version: ['20', '22', '24', '26']
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v7
|
|
34
|
+
with:
|
|
35
|
+
persist-credentials: false
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-node@v6
|
|
38
|
+
with:
|
|
39
|
+
node-version: ${{ matrix.node-version }}
|
|
40
|
+
cache: 'npm'
|
|
41
|
+
cache-dependency-path: package.json
|
|
42
|
+
check-latest: true
|
|
43
|
+
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
run: npm install --ignore-scripts
|
|
46
|
+
|
|
47
|
+
- name: Run type tests
|
|
48
|
+
run: npm run test:typescript
|
|
49
|
+
|
|
50
|
+
unit-tests:
|
|
51
|
+
name: Unit Tests
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
strategy:
|
|
54
|
+
matrix:
|
|
55
|
+
node-version: ['16', '18', '20', '22', '24', '26']
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v7
|
|
58
|
+
with:
|
|
59
|
+
persist-credentials: false
|
|
60
|
+
|
|
61
|
+
- uses: actions/setup-node@v6
|
|
62
|
+
with:
|
|
63
|
+
node-version: ${{ matrix.node-version }}
|
|
64
|
+
cache: 'npm'
|
|
65
|
+
cache-dependency-path: package.json
|
|
66
|
+
check-latest: true
|
|
67
|
+
|
|
68
|
+
- name: Install dependencies
|
|
69
|
+
run: npm install --ignore-scripts
|
|
70
|
+
|
|
71
|
+
- name: Run unit tests
|
|
72
|
+
run: npm test
|
|
73
|
+
|
|
26
74
|
test-regression-check-node10:
|
|
27
75
|
name: Test compatibility with Node.js 10
|
|
28
76
|
runs-on: ubuntu-latest
|
|
29
77
|
permissions:
|
|
30
78
|
contents: read
|
|
31
79
|
steps:
|
|
32
|
-
- uses: actions/checkout@
|
|
80
|
+
- uses: actions/checkout@v7
|
|
33
81
|
with:
|
|
34
82
|
persist-credentials: false
|
|
35
83
|
|
|
@@ -71,7 +119,7 @@ jobs:
|
|
|
71
119
|
permissions:
|
|
72
120
|
contents: read
|
|
73
121
|
steps:
|
|
74
|
-
- uses: actions/checkout@
|
|
122
|
+
- uses: actions/checkout@v7
|
|
75
123
|
with:
|
|
76
124
|
persist-credentials: false
|
|
77
125
|
|
|
@@ -103,4 +151,4 @@ jobs:
|
|
|
103
151
|
with:
|
|
104
152
|
license-check: true
|
|
105
153
|
lint: true
|
|
106
|
-
node-versions: '["16", "18", "20", "22", "24"]'
|
|
154
|
+
node-versions: '["16", "18", "20", "22", "24", "26"]'
|
package/README.md
CHANGED
|
@@ -27,10 +27,10 @@ Malformed authorities and out-of-range ports are reported through the parsed com
|
|
|
27
27
|
If set to `true`, the serializer will not resolve a relative `path` component.
|
|
28
28
|
|
|
29
29
|
* `unicodeSupport` (boolean, false)
|
|
30
|
-
If set to `true`, the parser will unescape non-ASCII characters in the parsed output as per [RFC 3987](
|
|
30
|
+
If set to `true`, the parser will unescape non-ASCII characters in the parsed output as per [RFC 3987](https://www.ietf.org/rfc/rfc3987.txt).
|
|
31
31
|
|
|
32
32
|
* `domainHost` (boolean, false)
|
|
33
|
-
If set to `true`, the library will treat the `host` component as a domain name, and convert IDNs (International Domain Names) as per [RFC 5891](
|
|
33
|
+
If set to `true`, the library will treat the `host` component as a domain name, and convert IDNs (International Domain Names) as per [RFC 5891](https://www.ietf.org/rfc/rfc5891.txt).
|
|
34
34
|
|
|
35
35
|
### Parse
|
|
36
36
|
|
|
@@ -90,14 +90,14 @@ true
|
|
|
90
90
|
|
|
91
91
|
## Scheme supports
|
|
92
92
|
|
|
93
|
-
fast-uri supports inserting custom [scheme](
|
|
93
|
+
fast-uri supports inserting custom [scheme](https://en.wikipedia.org/wiki/URI_scheme)-dependent processing rules. Currently, fast-uri has built-in support for the following schemes:
|
|
94
94
|
|
|
95
|
-
* http \[[RFC 2616](
|
|
96
|
-
* https \[[RFC 2818](
|
|
97
|
-
* ws \[[RFC 6455](
|
|
98
|
-
* wss \[[RFC 6455](
|
|
99
|
-
* urn \[[RFC 2141](
|
|
100
|
-
* urn:uuid \[[RFC 4122](
|
|
95
|
+
* http \[[RFC 2616](https://www.ietf.org/rfc/rfc2616.txt)\]
|
|
96
|
+
* https \[[RFC 2818](https://www.ietf.org/rfc/rfc2818.txt)\]
|
|
97
|
+
* ws \[[RFC 6455](https://www.ietf.org/rfc/rfc6455.txt)\]
|
|
98
|
+
* wss \[[RFC 6455](https://www.ietf.org/rfc/rfc6455.txt)\]
|
|
99
|
+
* urn \[[RFC 2141](https://www.ietf.org/rfc/rfc2141.txt)\]
|
|
100
|
+
* urn:uuid \[[RFC 4122](https://www.ietf.org/rfc/rfc4122.txt)\]
|
|
101
101
|
|
|
102
102
|
|
|
103
103
|
## Benchmarks
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Bench } from 'tinybench'
|
|
2
|
+
import { fastUri } from '../index.js'
|
|
3
|
+
|
|
4
|
+
const { serialize, normalize, parse } = fastUri
|
|
5
|
+
|
|
6
|
+
const asciiPath = '/users/profile/settings/account'
|
|
7
|
+
const asciiPathWithSpaces = '/users/john doe/profile page'
|
|
8
|
+
const cjkPath = '/ユーザー/プロフィール/設定'
|
|
9
|
+
const mixedPath = '/users/日本/profile-📱-emoji/設定'
|
|
10
|
+
const emojiPath = '/🚀/🌍/🎉/end'
|
|
11
|
+
const longAsciiPath = '/' + 'a/b/c/d/e/f/g/h/'.repeat(20)
|
|
12
|
+
const longUtf8Path = '/' + '日本/中文/한국/'.repeat(20)
|
|
13
|
+
|
|
14
|
+
const bench = new Bench({ name: 'encoder bench', time: 1000 })
|
|
15
|
+
|
|
16
|
+
bench.add('serialize ascii path', () => {
|
|
17
|
+
serialize({ scheme: 'http', host: 'example.com', path: asciiPath })
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
bench.add('serialize ascii path with spaces', () => {
|
|
21
|
+
serialize({ scheme: 'http', host: 'example.com', path: asciiPathWithSpaces })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
bench.add('serialize cjk path', () => {
|
|
25
|
+
serialize({ scheme: 'http', host: 'example.com', path: cjkPath })
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
bench.add('serialize mixed path', () => {
|
|
29
|
+
serialize({ scheme: 'http', host: 'example.com', path: mixedPath })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
bench.add('serialize emoji path', () => {
|
|
33
|
+
serialize({ scheme: 'http', host: 'example.com', path: emojiPath })
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
bench.add('serialize long ascii path', () => {
|
|
37
|
+
serialize({ scheme: 'http', host: 'example.com', path: longAsciiPath })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
bench.add('serialize long utf8 path', () => {
|
|
41
|
+
serialize({ scheme: 'http', host: 'example.com', path: longUtf8Path })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
bench.add('normalize utf8 uri', () => {
|
|
45
|
+
normalize('http://example.com/日本/profile')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
bench.add('parse utf8 uri', () => {
|
|
49
|
+
parse('http://example.com/日本/profile')
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
await bench.run()
|
|
53
|
+
console.log(bench.name)
|
|
54
|
+
console.table(bench.table())
|
package/index.js
CHANGED
|
@@ -252,7 +252,7 @@ function parseWithStatus (uri, opts) {
|
|
|
252
252
|
|
|
253
253
|
if (matches) {
|
|
254
254
|
// store each component
|
|
255
|
-
parsed.scheme = matches[1]
|
|
255
|
+
parsed.scheme = matches[1] === undefined ? undefined : matches[1].toLowerCase()
|
|
256
256
|
parsed.userinfo = matches[3]
|
|
257
257
|
parsed.host = matches[4]
|
|
258
258
|
parsed.port = parseInt(matches[5], 10)
|
package/lib/utils.js
CHANGED
|
@@ -331,6 +331,55 @@ function normalizePercentEncoding (input, decodeUnreserved = false) {
|
|
|
331
331
|
return output
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/** Pre-built `%XX` strings for every byte. Avoids per-call hex math/concat. */
|
|
335
|
+
const BYTE_HEX = new Array(256)
|
|
336
|
+
{
|
|
337
|
+
const HEX_DIGITS = '0123456789ABCDEF'
|
|
338
|
+
for (let i = 0; i < 256; i++) {
|
|
339
|
+
BYTE_HEX[i] = '%' + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 0xF]
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Mirrors the pass-through set of the deprecated `escape()` global:
|
|
345
|
+
* `A-Z a-z 0-9 * + - . / @ _`. Keeps drop-in semantics for ASCII input.
|
|
346
|
+
*
|
|
347
|
+
* @param {number} cp
|
|
348
|
+
* @returns {boolean}
|
|
349
|
+
*/
|
|
350
|
+
function isEscapeSafe (cp) {
|
|
351
|
+
return (
|
|
352
|
+
(cp >= 0x30 && cp <= 0x39) ||
|
|
353
|
+
(cp >= 0x41 && cp <= 0x5A) ||
|
|
354
|
+
(cp >= 0x61 && cp <= 0x7A) ||
|
|
355
|
+
cp === 0x2A || cp === 0x2B || cp === 0x2D || cp === 0x2E ||
|
|
356
|
+
cp === 0x2F || cp === 0x40 || cp === 0x5F
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* RFC 3986 percent-encode a non-ASCII Unicode code point as UTF-8 bytes.
|
|
362
|
+
* Caller handles the ASCII branch inline for speed.
|
|
363
|
+
*
|
|
364
|
+
* @param {number} cp - Unicode code point (>= 0x80, <= 0x10FFFF)
|
|
365
|
+
* @returns {string}
|
|
366
|
+
*/
|
|
367
|
+
function percentEncodeNonAscii (cp) {
|
|
368
|
+
if (cp < 0x800) {
|
|
369
|
+
return BYTE_HEX[0xC0 | (cp >> 6)] +
|
|
370
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
371
|
+
}
|
|
372
|
+
if (cp < 0x10000) {
|
|
373
|
+
return BYTE_HEX[0xE0 | (cp >> 12)] +
|
|
374
|
+
BYTE_HEX[0x80 | ((cp >> 6) & 0x3F)] +
|
|
375
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
376
|
+
}
|
|
377
|
+
return BYTE_HEX[0xF0 | (cp >> 18)] +
|
|
378
|
+
BYTE_HEX[0x80 | ((cp >> 12) & 0x3F)] +
|
|
379
|
+
BYTE_HEX[0x80 | ((cp >> 6) & 0x3F)] +
|
|
380
|
+
BYTE_HEX[0x80 | (cp & 0x3F)]
|
|
381
|
+
}
|
|
382
|
+
|
|
334
383
|
/**
|
|
335
384
|
* Normalizes path data without turning reserved escapes into live path syntax.
|
|
336
385
|
* Valid escapes are uppercased, raw unsafe characters are escaped, and only
|
|
@@ -343,7 +392,8 @@ function normalizePathEncoding (input) {
|
|
|
343
392
|
let output = ''
|
|
344
393
|
|
|
345
394
|
for (let i = 0; i < input.length; i++) {
|
|
346
|
-
|
|
395
|
+
const ch = input[i]
|
|
396
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
347
397
|
const hex = input.slice(i + 1, i + 3)
|
|
348
398
|
if (isHexPair(hex)) {
|
|
349
399
|
const normalizedHex = hex.toUpperCase()
|
|
@@ -360,10 +410,25 @@ function normalizePathEncoding (input) {
|
|
|
360
410
|
}
|
|
361
411
|
}
|
|
362
412
|
|
|
363
|
-
if (isPathCharacter(
|
|
364
|
-
output +=
|
|
413
|
+
if (isPathCharacter(ch)) {
|
|
414
|
+
output += ch
|
|
365
415
|
} else {
|
|
366
|
-
|
|
416
|
+
const code = input.charCodeAt(i)
|
|
417
|
+
if (code < 0x80) {
|
|
418
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code]
|
|
419
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
420
|
+
output += percentEncodeNonAscii(code)
|
|
421
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
422
|
+
const low = input.charCodeAt(i + 1)
|
|
423
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
424
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00))
|
|
425
|
+
i++
|
|
426
|
+
} else {
|
|
427
|
+
output += percentEncodeNonAscii(0xFFFD)
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
output += percentEncodeNonAscii(0xFFFD)
|
|
431
|
+
}
|
|
367
432
|
}
|
|
368
433
|
}
|
|
369
434
|
|
|
@@ -380,7 +445,8 @@ function escapePreservingEscapes (input) {
|
|
|
380
445
|
let output = ''
|
|
381
446
|
|
|
382
447
|
for (let i = 0; i < input.length; i++) {
|
|
383
|
-
|
|
448
|
+
const ch = input[i]
|
|
449
|
+
if (ch === '%' && i + 2 < input.length) {
|
|
384
450
|
const hex = input.slice(i + 1, i + 3)
|
|
385
451
|
if (isHexPair(hex)) {
|
|
386
452
|
output += '%' + hex.toUpperCase()
|
|
@@ -389,7 +455,22 @@ function escapePreservingEscapes (input) {
|
|
|
389
455
|
}
|
|
390
456
|
}
|
|
391
457
|
|
|
392
|
-
|
|
458
|
+
const code = input.charCodeAt(i)
|
|
459
|
+
if (code < 0x80) {
|
|
460
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code]
|
|
461
|
+
} else if (code < 0xD800 || code > 0xDFFF) {
|
|
462
|
+
output += percentEncodeNonAscii(code)
|
|
463
|
+
} else if (code <= 0xDBFF && i + 1 < input.length) {
|
|
464
|
+
const low = input.charCodeAt(i + 1)
|
|
465
|
+
if (low >= 0xDC00 && low <= 0xDFFF) {
|
|
466
|
+
output += percentEncodeNonAscii(0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00))
|
|
467
|
+
i++
|
|
468
|
+
} else {
|
|
469
|
+
output += percentEncodeNonAscii(0xFFFD)
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
output += percentEncodeNonAscii(0xFFFD)
|
|
473
|
+
}
|
|
393
474
|
}
|
|
394
475
|
|
|
395
476
|
return output
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-uri",
|
|
3
3
|
"description": "Dependency-free RFC 3986 URI toolbox",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.1",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"types": "types/index.d.ts",
|
|
@@ -48,14 +48,14 @@
|
|
|
48
48
|
"scripts": {
|
|
49
49
|
"lint": "eslint",
|
|
50
50
|
"lint:fix": "eslint --fix",
|
|
51
|
-
"test": "npm run test:unit
|
|
51
|
+
"test": "npm run test:unit",
|
|
52
52
|
"test:browser:chromium": "playwright-test ./test/* --runner tape --browser=chromium",
|
|
53
53
|
"test:browser:firefox": "playwright-test ./test/* --runner tape --browser=firefox",
|
|
54
54
|
"test:browser:webkit": "playwright-test ./test/* --runner tape --browser=webkit",
|
|
55
55
|
"test:browser": "npm run test:browser:chromium && npm run test:browser:firefox && npm run test:browser:webkit",
|
|
56
56
|
"test:unit": "tape test/**/*.js",
|
|
57
57
|
"test:unit:dev": "npm run test:unit -- --coverage-report=html",
|
|
58
|
-
"test:typescript": "
|
|
58
|
+
"test:typescript": "tstyche"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"ajv": "^8.16.0",
|
|
@@ -63,6 +63,6 @@
|
|
|
63
63
|
"neostandard": "^0.13.0",
|
|
64
64
|
"playwright-test": "^14.1.12",
|
|
65
65
|
"tape": "^5.8.1",
|
|
66
|
-
"
|
|
66
|
+
"tstyche": "^7.0.0"
|
|
67
67
|
}
|
|
68
68
|
}
|
package/test/equal.test.js
CHANGED
|
@@ -115,3 +115,54 @@ test('URI Equals tolerates malformed fragments', (t) => {
|
|
|
115
115
|
)
|
|
116
116
|
t.end()
|
|
117
117
|
})
|
|
118
|
+
|
|
119
|
+
test('URI Equals — raw UTF-8 char equals its percent-encoded UTF-8 form', (t) => {
|
|
120
|
+
const suite = [
|
|
121
|
+
// BMP characters
|
|
122
|
+
{ pair: ['http://example.com/日本', 'http://example.com/%E6%97%A5%E6%9C%AC'], result: true },
|
|
123
|
+
{ pair: ['http://example.com/café', 'http://example.com/caf%C3%A9'], result: true },
|
|
124
|
+
{ pair: ['http://example.com/€', 'http://example.com/%E2%82%AC'], result: true },
|
|
125
|
+
{ pair: ['http://example.com/中文', 'http://example.com/%E4%B8%AD%E6%96%87'], result: true },
|
|
126
|
+
// supplementary plane (emoji, surrogate pairs)
|
|
127
|
+
{ pair: ['http://example.com/🚀', 'http://example.com/%F0%9F%9A%80'], result: true },
|
|
128
|
+
{ pair: ['http://example.com/😀', 'http://example.com/%F0%9F%98%80'], result: true },
|
|
129
|
+
// mixed encoded + raw in same path
|
|
130
|
+
{ pair: ['http://example.com/users/日本/profile', 'http://example.com/users/%E6%97%A5%E6%9C%AC/profile'], result: true },
|
|
131
|
+
{ pair: ['http://example.com/a/€/b', 'http://example.com/a/%E2%82%AC/b'], result: true }
|
|
132
|
+
]
|
|
133
|
+
runTest(t, suite)
|
|
134
|
+
t.end()
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('URI Equals — percent-encoded UTF-8 is case-insensitive in hex digits', (t) => {
|
|
138
|
+
const suite = [
|
|
139
|
+
{ pair: ['http://example.com/caf%C3%A9', 'http://example.com/caf%c3%a9'], result: true },
|
|
140
|
+
{ pair: ['http://example.com/%E6%97%A5', 'http://example.com/%e6%97%a5'], result: true },
|
|
141
|
+
{ pair: ['http://example.com/%F0%9F%9A%80', 'http://example.com/%f0%9f%9a%80'], result: true }
|
|
142
|
+
]
|
|
143
|
+
runTest(t, suite)
|
|
144
|
+
t.end()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test('URI Equals — different Unicode code points are not equal', (t) => {
|
|
148
|
+
const suite = [
|
|
149
|
+
{ pair: ['http://example.com/日本', 'http://example.com/中文'], result: false },
|
|
150
|
+
{ pair: ['http://example.com/café', 'http://example.com/cafe'], result: false },
|
|
151
|
+
{ pair: ['http://example.com/🚀', 'http://example.com/🎉'], result: false }
|
|
152
|
+
]
|
|
153
|
+
runTest(t, suite)
|
|
154
|
+
t.end()
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('URI Equals — Latin-1 byte is not equal to UTF-8 multi-byte encoding', (t) => {
|
|
158
|
+
// Parser-differential guard: %C3%A9 (UTF-8 for "é") must NOT equal %E9
|
|
159
|
+
// (raw Latin-1 byte). The old escape() emitted single Latin-1 bytes for
|
|
160
|
+
// non-ASCII, which would have made these incorrectly compare equal in a
|
|
161
|
+
// round-tripped URI.
|
|
162
|
+
const suite = [
|
|
163
|
+
{ pair: ['http://example.com/caf%C3%A9', 'http://example.com/caf%E9'], result: false },
|
|
164
|
+
{ pair: ['http://example.com/%E6%97%A5', 'http://example.com/%E6'], result: false }
|
|
165
|
+
]
|
|
166
|
+
runTest(t, suite)
|
|
167
|
+
t.end()
|
|
168
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const test = require('tape')
|
|
4
|
+
const fastURI = require('..')
|
|
5
|
+
|
|
6
|
+
test('scheme is normalized to lowercase (RFC 3986 §6.2.2.1)', (t) => {
|
|
7
|
+
// parse lowercases the scheme, consistent with how it already lowercases the host
|
|
8
|
+
t.equal(fastURI.parse('HTTP://EXAMPLE.com/Path').scheme, 'http', 'parse lowercases an uppercase scheme')
|
|
9
|
+
t.equal(fastURI.parse('HtTpS://example.com').scheme, 'https', 'parse lowercases a mixed-case scheme')
|
|
10
|
+
|
|
11
|
+
// normalize emits a lowercase scheme (matches the already-lowercased host)
|
|
12
|
+
t.equal(fastURI.normalize('HTTP://EXAMPLE.com/p'), 'http://example.com/p', 'normalize lowercases the scheme')
|
|
13
|
+
t.equal(fastURI.normalize('HtTp://Example.COM/'), 'http://example.com/', 'normalize lowercases a mixed-case scheme')
|
|
14
|
+
|
|
15
|
+
t.end()
|
|
16
|
+
})
|
|
Binary file
|
package/test/uri-js.test.js
CHANGED
|
@@ -8,7 +8,7 @@ const fastURI = require('..')
|
|
|
8
8
|
*
|
|
9
9
|
* @fileoverview An RFC 3986 compliant, scheme extendable URI parsing/normalizing/resolving/serializing library for JavaScript.
|
|
10
10
|
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
|
|
11
|
-
* @see
|
|
11
|
+
* @see https://github.com/garycourt/uri-js
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
package/tsconfig.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowJs": true,
|
|
4
|
+
"checkJs": true,
|
|
5
|
+
"strict": true,
|
|
6
|
+
"noImplicitAny": true,
|
|
7
|
+
"target": "es2022",
|
|
8
|
+
"noEmit": true
|
|
9
|
+
}
|
|
9
10
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -28,15 +28,6 @@ declare namespace fastUri {
|
|
|
28
28
|
nid?: string;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
* @deprecated Use Options instead
|
|
33
|
-
*/
|
|
34
|
-
export type options = Options
|
|
35
|
-
/**
|
|
36
|
-
* @deprecated Use URIComponent instead
|
|
37
|
-
*/
|
|
38
|
-
export type URIComponents = URIComponent
|
|
39
|
-
|
|
40
31
|
export function normalize (uri: string, opts?: Options): string
|
|
41
32
|
export function normalize (uri: URIComponent, opts?: Options): URIComponent
|
|
42
33
|
export function normalize (uri: any, opts?: Options): any
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import uri, {
|
|
2
|
+
type URIComponent,
|
|
3
|
+
} from '..'
|
|
4
|
+
import { expect } from 'tstyche'
|
|
5
|
+
|
|
6
|
+
const parsed = uri.parse('foo')
|
|
7
|
+
expect(parsed).type.toBe<URIComponent>()
|
|
8
|
+
|
|
9
|
+
const parsed2 = uri.parse('foo', {
|
|
10
|
+
domainHost: true,
|
|
11
|
+
scheme: 'https',
|
|
12
|
+
unicodeSupport: false
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
expect(parsed2).type.toBe<URIComponent>()
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: package-manager-ci
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
- next
|
|
8
|
-
- 'v*'
|
|
9
|
-
paths-ignore:
|
|
10
|
-
- 'docs/**'
|
|
11
|
-
- '*.md'
|
|
12
|
-
pull_request:
|
|
13
|
-
paths-ignore:
|
|
14
|
-
- 'docs/**'
|
|
15
|
-
- '*.md'
|
|
16
|
-
|
|
17
|
-
permissions:
|
|
18
|
-
contents: read
|
|
19
|
-
|
|
20
|
-
jobs:
|
|
21
|
-
test:
|
|
22
|
-
permissions:
|
|
23
|
-
contents: read
|
|
24
|
-
uses: fastify/workflows/.github/workflows/plugins-ci-package-manager.yml@v6
|
package/types/index.test-d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import uri, { URIComponents, URIComponent, Options, options } from '..'
|
|
2
|
-
import { expectDeprecated, expectType } from 'tsd'
|
|
3
|
-
|
|
4
|
-
const parsed = uri.parse('foo')
|
|
5
|
-
expectType<URIComponents>(parsed)
|
|
6
|
-
const parsed2 = uri.parse('foo', {
|
|
7
|
-
domainHost: true,
|
|
8
|
-
scheme: 'https',
|
|
9
|
-
unicodeSupport: false
|
|
10
|
-
})
|
|
11
|
-
expectType<URIComponents>(parsed2)
|
|
12
|
-
|
|
13
|
-
expectType<URIComponent>({} as URIComponents)
|
|
14
|
-
expectDeprecated({} as URIComponents)
|
|
15
|
-
|
|
16
|
-
expectType<Options>({} as options)
|
|
17
|
-
expectDeprecated({} as options)
|