avancement planning
This commit is contained in:
-21
@@ -1,21 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 15
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "discussion"
|
||||
- "feature request"
|
||||
- "bug"
|
||||
- "help wanted"
|
||||
- "plugin suggestion"
|
||||
- "good first issue"
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
comment: |
|
||||
Hello! Thank you for contributing!
|
||||
It appears that you have changed the code, but the tests that verify your change are missing. Could you please add them?
|
||||
fileExtensions:
|
||||
- '.ts'
|
||||
- '.js'
|
||||
|
||||
testDir: 'test'
|
||||
+12
-7
@@ -14,6 +14,11 @@ on:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
# This allows a subsequently queued workflow run to interrupt previous runs
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -24,11 +29,11 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '10'
|
||||
cache: 'npm'
|
||||
@@ -66,11 +71,11 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: 'npm'
|
||||
@@ -80,21 +85,21 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
npm install --ignore-scripts
|
||||
|
||||
|
||||
- if: ${{ matrix.os == 'windows-latest' }}
|
||||
run: npx playwright install winldd
|
||||
|
||||
- name: Run browser tests
|
||||
run: |
|
||||
npm run test:browser:${{ matrix.browser }}
|
||||
|
||||
|
||||
test:
|
||||
needs:
|
||||
- test-regression-check-node10
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6
|
||||
with:
|
||||
license-check: true
|
||||
lint: true
|
||||
|
||||
+1
-1
@@ -21,4 +21,4 @@ jobs:
|
||||
test:
|
||||
permissions:
|
||||
contents: read
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci-package-manager.yml@v5
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci-package-manager.yml@v6
|
||||
|
||||
+1
-3
@@ -1,9 +1,7 @@
|
||||
Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
|
||||
Copyright (c) 2021-present The Fastify team
|
||||
Copyright (c) 2021-present The Fastify team <https://github.com/fastify/fastify#team>
|
||||
All rights reserved.
|
||||
|
||||
The Fastify team members are listed at https://github.com/fastify/fastify#team.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
|
||||
+13
-4
@@ -1,13 +1,9 @@
|
||||
# fast-uri
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://www.npmjs.com/package/fast-uri)
|
||||
[](https://github.com/fastify/fast-uri/actions/workflows/ci.yml)
|
||||
[](https://github.com/neostandard/neostandard)
|
||||
|
||||
</div>
|
||||
|
||||
Dependency-free RFC 3986 URI toolbox.
|
||||
|
||||
## Usage
|
||||
@@ -16,6 +12,8 @@ Dependency-free RFC 3986 URI toolbox.
|
||||
|
||||
All of the above functions can accept an additional options argument that is an object that can contain one or more of the following properties:
|
||||
|
||||
Malformed authorities and out-of-range ports are reported through the parsed component's `error` field. `normalize()` leaves malformed string inputs unchanged, and `equal()` returns `false` when either string input is malformed.
|
||||
|
||||
* `scheme` (string)
|
||||
Indicates the scheme that the URI should be treated as, overriding the URI's normal scheme parsing behavior.
|
||||
|
||||
@@ -70,6 +68,17 @@ uri.resolve("uri://a/b/c/d?q", "../../g")
|
||||
"uri://a/g"
|
||||
```
|
||||
|
||||
### Normalize
|
||||
|
||||
```js
|
||||
const uri = require('fast-uri')
|
||||
uri.normalize('http://example.com/a%2Fb')
|
||||
// Output
|
||||
"http://example.com/a%2Fb"
|
||||
```
|
||||
|
||||
Reserved path escapes such as `%2F` and `%2E` are preserved as path data during normalization and comparison.
|
||||
|
||||
### Equal
|
||||
|
||||
```js
|
||||
|
||||
+90
-24
@@ -1,6 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require('./lib/utils')
|
||||
const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require('./lib/utils')
|
||||
const { SCHEMES, getSchemeHandler } = require('./lib/schemes')
|
||||
|
||||
/**
|
||||
@@ -11,7 +11,7 @@ const { SCHEMES, getSchemeHandler } = require('./lib/schemes')
|
||||
*/
|
||||
function normalize (uri, options) {
|
||||
if (typeof uri === 'string') {
|
||||
uri = /** @type {T} */ (serialize(parse(uri, options), options))
|
||||
uri = /** @type {T} */ (normalizeString(uri, options))
|
||||
} else if (typeof uri === 'object') {
|
||||
uri = /** @type {T} */ (parse(serialize(uri, options), options))
|
||||
}
|
||||
@@ -106,21 +106,10 @@ function resolveComponent (base, relative, options, skipNormalization) {
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function equal (uriA, uriB, options) {
|
||||
if (typeof uriA === 'string') {
|
||||
uriA = unescape(uriA)
|
||||
uriA = serialize(normalizeComponentEncoding(parse(uriA, options), true), { ...options, skipEscape: true })
|
||||
} else if (typeof uriA === 'object') {
|
||||
uriA = serialize(normalizeComponentEncoding(uriA, true), { ...options, skipEscape: true })
|
||||
}
|
||||
const normalizedA = normalizeComparableURI(uriA, options)
|
||||
const normalizedB = normalizeComparableURI(uriB, options)
|
||||
|
||||
if (typeof uriB === 'string') {
|
||||
uriB = unescape(uriB)
|
||||
uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), { ...options, skipEscape: true })
|
||||
} else if (typeof uriB === 'object') {
|
||||
uriB = serialize(normalizeComponentEncoding(uriB, true), { ...options, skipEscape: true })
|
||||
}
|
||||
|
||||
return uriA.toLowerCase() === uriB.toLowerCase()
|
||||
return normalizedA !== undefined && normalizedB !== undefined && normalizedA.toLowerCase() === normalizedB.toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,13 +145,13 @@ function serialize (cmpts, opts) {
|
||||
|
||||
if (component.path !== undefined) {
|
||||
if (!options.skipEscape) {
|
||||
component.path = escape(component.path)
|
||||
component.path = escapePreservingEscapes(component.path)
|
||||
|
||||
if (component.scheme !== undefined) {
|
||||
component.path = component.path.split('%3A').join(':')
|
||||
}
|
||||
} else {
|
||||
component.path = unescape(component.path)
|
||||
component.path = normalizePercentEncoding(component.path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,12 +202,29 @@ function serialize (cmpts, opts) {
|
||||
|
||||
const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u
|
||||
|
||||
/**
|
||||
* @param {import('./types/index').URIComponent} parsed
|
||||
* @param {RegExpMatchArray} matches
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
function getParseError (parsed, matches) {
|
||||
if (matches[2] !== undefined && parsed.path && parsed.path[0] !== '/') {
|
||||
return 'URI path must start with "/" when authority is present.'
|
||||
}
|
||||
|
||||
if (typeof parsed.port === 'number' && (parsed.port < 0 || parsed.port > 65535)) {
|
||||
return 'URI port is malformed.'
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} uri
|
||||
* @param {import('./types/index').Options} [opts]
|
||||
* @returns
|
||||
* @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean }}
|
||||
*/
|
||||
function parse (uri, opts) {
|
||||
function parseWithStatus (uri, opts) {
|
||||
const options = Object.assign({}, opts)
|
||||
/** @type {import('./types/index').URIComponent} */
|
||||
const parsed = {
|
||||
@@ -231,6 +237,8 @@ function parse (uri, opts) {
|
||||
fragment: undefined
|
||||
}
|
||||
|
||||
let malformedAuthorityOrPort = false
|
||||
|
||||
let isIP = false
|
||||
if (options.reference === 'suffix') {
|
||||
if (options.scheme) {
|
||||
@@ -256,6 +264,13 @@ function parse (uri, opts) {
|
||||
if (isNaN(parsed.port)) {
|
||||
parsed.port = matches[5]
|
||||
}
|
||||
|
||||
const parseError = getParseError(parsed, matches)
|
||||
if (parseError !== undefined) {
|
||||
parsed.error = parsed.error || parseError
|
||||
malformedAuthorityOrPort = true
|
||||
}
|
||||
|
||||
if (parsed.host) {
|
||||
const ipv4result = isIPv4(parsed.host)
|
||||
if (ipv4result === false) {
|
||||
@@ -304,14 +319,18 @@ function parse (uri, opts) {
|
||||
parsed.scheme = unescape(parsed.scheme)
|
||||
}
|
||||
if (parsed.host !== undefined) {
|
||||
parsed.host = unescape(parsed.host)
|
||||
parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP)
|
||||
}
|
||||
}
|
||||
if (parsed.path) {
|
||||
parsed.path = escape(unescape(parsed.path))
|
||||
parsed.path = normalizePathEncoding(parsed.path)
|
||||
}
|
||||
if (parsed.fragment) {
|
||||
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment))
|
||||
try {
|
||||
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment))
|
||||
} catch {
|
||||
parsed.error = parsed.error || 'URI malformed'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,7 +341,54 @@ function parse (uri, opts) {
|
||||
} else {
|
||||
parsed.error = parsed.error || 'URI can not be parsed.'
|
||||
}
|
||||
return parsed
|
||||
return { parsed, malformedAuthorityOrPort }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} uri
|
||||
* @param {import('./types/index').Options} [opts]
|
||||
* @returns
|
||||
*/
|
||||
function parse (uri, opts) {
|
||||
return parseWithStatus(uri, opts).parsed
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} uri
|
||||
* @param {import('./types/index').Options} [opts]
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeString (uri, opts) {
|
||||
return normalizeStringWithStatus(uri, opts).normalized
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} uri
|
||||
* @param {import('./types/index').Options} [opts]
|
||||
* @returns {{ normalized: string, malformedAuthorityOrPort: boolean }}
|
||||
*/
|
||||
function normalizeStringWithStatus (uri, opts) {
|
||||
const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts)
|
||||
return {
|
||||
normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts),
|
||||
malformedAuthorityOrPort
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import ('./types/index').URIComponent|string} uri
|
||||
* @param {import('./types/index').Options} [opts]
|
||||
* @returns {string|undefined}
|
||||
*/
|
||||
function normalizeComparableURI (uri, opts) {
|
||||
if (typeof uri === 'string') {
|
||||
const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts)
|
||||
return malformedAuthorityOrPort ? undefined : normalized
|
||||
}
|
||||
|
||||
if (typeof uri === 'object') {
|
||||
return serialize(uri, opts)
|
||||
}
|
||||
}
|
||||
|
||||
const fastUri = {
|
||||
|
||||
+129
-22
@@ -6,6 +6,15 @@ const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\d
|
||||
/** @type {(value: string) => boolean} */
|
||||
const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u)
|
||||
|
||||
/** @type {(value: string) => boolean} */
|
||||
const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu)
|
||||
|
||||
/** @type {(value: string) => boolean} */
|
||||
const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu)
|
||||
|
||||
/** @type {(value: string) => boolean} */
|
||||
const isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu)
|
||||
|
||||
/**
|
||||
* @param {Array<string>} input
|
||||
* @returns {string}
|
||||
@@ -264,31 +273,126 @@ function removeDotSegments (path) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../types/index').URIComponent} component
|
||||
* @param {boolean} esc
|
||||
* @returns {import('../types/index').URIComponent}
|
||||
* Re-escape RFC 3986 gen-delims that must not appear literally in the host.
|
||||
* After the URI regex parses, these characters cannot be literal in the host
|
||||
* field, so any that appear after decoding came from percent-encoding and
|
||||
* must be restored to prevent authority structure changes.
|
||||
*
|
||||
* @param {string} host
|
||||
* @param {boolean} isIP - true for IPv4/IPv6 hosts (skip colon re-escaping)
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeComponentEncoding (component, esc) {
|
||||
const func = esc !== true ? escape : unescape
|
||||
if (component.scheme !== undefined) {
|
||||
component.scheme = func(component.scheme)
|
||||
const HOST_DELIMS = { '@': '%40', '/': '%2F', '?': '%3F', '#': '%23', ':': '%3A' }
|
||||
const HOST_DELIM_RE = /[@/?#:]/g
|
||||
const HOST_DELIM_NO_COLON_RE = /[@/?#]/g
|
||||
|
||||
function reescapeHostDelimiters (host, isIP) {
|
||||
const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE
|
||||
re.lastIndex = 0
|
||||
return host.replace(re, (ch) => HOST_DELIMS[ch])
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
|
||||
* Reserved delimiters such as `%2F` and `%2E` stay escaped.
|
||||
*
|
||||
* @param {string} input
|
||||
* @param {boolean} [decodeUnreserved=false]
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizePercentEncoding (input, decodeUnreserved = false) {
|
||||
if (input.indexOf('%') === -1) {
|
||||
return input
|
||||
}
|
||||
if (component.userinfo !== undefined) {
|
||||
component.userinfo = func(component.userinfo)
|
||||
|
||||
let output = ''
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (input[i] === '%' && i + 2 < input.length) {
|
||||
const hex = input.slice(i + 1, i + 3)
|
||||
if (isHexPair(hex)) {
|
||||
const normalizedHex = hex.toUpperCase()
|
||||
const decoded = String.fromCharCode(parseInt(normalizedHex, 16))
|
||||
|
||||
if (decodeUnreserved && isUnreserved(decoded)) {
|
||||
output += decoded
|
||||
} else {
|
||||
output += '%' + normalizedHex
|
||||
}
|
||||
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
output += input[i]
|
||||
}
|
||||
if (component.host !== undefined) {
|
||||
component.host = func(component.host)
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes path data without turning reserved escapes into live path syntax.
|
||||
* Valid escapes are uppercased, raw unsafe characters are escaped, and only
|
||||
* unreserved bytes that are not `.` are decoded.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizePathEncoding (input) {
|
||||
let output = ''
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (input[i] === '%' && i + 2 < input.length) {
|
||||
const hex = input.slice(i + 1, i + 3)
|
||||
if (isHexPair(hex)) {
|
||||
const normalizedHex = hex.toUpperCase()
|
||||
const decoded = String.fromCharCode(parseInt(normalizedHex, 16))
|
||||
|
||||
if (decoded !== '.' && isUnreserved(decoded)) {
|
||||
output += decoded
|
||||
} else {
|
||||
output += '%' + normalizedHex
|
||||
}
|
||||
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (isPathCharacter(input[i])) {
|
||||
output += input[i]
|
||||
} else {
|
||||
output += escape(input[i])
|
||||
}
|
||||
}
|
||||
if (component.path !== undefined) {
|
||||
component.path = func(component.path)
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a component while preserving existing valid percent escapes.
|
||||
*
|
||||
* @param {string} input
|
||||
* @returns {string}
|
||||
*/
|
||||
function escapePreservingEscapes (input) {
|
||||
let output = ''
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
if (input[i] === '%' && i + 2 < input.length) {
|
||||
const hex = input.slice(i + 1, i + 3)
|
||||
if (isHexPair(hex)) {
|
||||
output += '%' + hex.toUpperCase()
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
output += escape(input[i])
|
||||
}
|
||||
if (component.query !== undefined) {
|
||||
component.query = func(component.query)
|
||||
}
|
||||
if (component.fragment !== undefined) {
|
||||
component.fragment = func(component.fragment)
|
||||
}
|
||||
return component
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,7 +414,7 @@ function recomposeAuthority (component) {
|
||||
if (ipV6res.isIPV6 === true) {
|
||||
host = `[${ipV6res.escapedHost}]`
|
||||
} else {
|
||||
host = component.host
|
||||
host = reescapeHostDelimiters(host, false)
|
||||
}
|
||||
}
|
||||
uriTokens.push(host)
|
||||
@@ -327,7 +431,10 @@ function recomposeAuthority (component) {
|
||||
module.exports = {
|
||||
nonSimpleDomain,
|
||||
recomposeAuthority,
|
||||
normalizeComponentEncoding,
|
||||
reescapeHostDelimiters,
|
||||
normalizePercentEncoding,
|
||||
normalizePathEncoding,
|
||||
escapePreservingEscapes,
|
||||
removeDotSegments,
|
||||
isIPv4,
|
||||
isUUID,
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "fast-uri",
|
||||
"description": "Dependency-free RFC 3986 URI toolbox",
|
||||
"version": "3.1.0",
|
||||
"version": "3.1.2",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
@@ -58,12 +58,11 @@
|
||||
"test:typescript": "tsd"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fastify/pre-commit": "^2.1.0",
|
||||
"ajv": "^8.16.0",
|
||||
"eslint": "^9.17.0",
|
||||
"neostandard": "^0.12.0",
|
||||
"neostandard": "^0.13.0",
|
||||
"playwright-test": "^14.1.12",
|
||||
"tape": "^5.8.1",
|
||||
"tsd": "^0.32.0"
|
||||
"tsd": "^0.33.0"
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -106,3 +106,12 @@ test('WSS Equal', (t) => {
|
||||
runTest(t, suite)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('URI Equals tolerates malformed fragments', (t) => {
|
||||
t.equal(
|
||||
fastURI.equal('http://example.com/#%E0%A4A', 'http://example.com/#%E0%A4A'),
|
||||
true,
|
||||
'malformed fragment does not throw during equality checks'
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
+5
@@ -150,6 +150,11 @@ test('URI parse', (t) => {
|
||||
t.equal(components.query, undefined, 'query')
|
||||
t.equal(components.fragment, '%0D', 'fragment')
|
||||
|
||||
// malformed percent-encoded fragment must not throw
|
||||
components = fastURI.parse('http://example.com/#%E0%A4A')
|
||||
t.equal(components.error, 'URI malformed', 'malformed fragment errors')
|
||||
t.equal(components.fragment, '%E0%A4A', 'malformed fragment is preserved')
|
||||
|
||||
// all
|
||||
components = fastURI.parse('uri://user:pass@example.com:123/one/two.three?q1=a1&q2=a2#body')
|
||||
t.equal(components.error, undefined, 'all errors')
|
||||
|
||||
+9
@@ -76,3 +76,12 @@ test('URN Resolving', (t) => {
|
||||
t.equal(fastURI.resolve('urn:some:other:prop', 'urn:some:ip:prop'), 'urn:some:ip:prop', 'urn:some:ip:prop')
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('URI Resolving tolerates malformed fragments', (t) => {
|
||||
t.equal(
|
||||
fastURI.resolve('http://base.com/', 'http://example.com/#%E0%A4A'),
|
||||
'http://example.com/#%E0%A4A',
|
||||
'malformed fragment does not throw during resolve'
|
||||
)
|
||||
t.end()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user