update
This commit is contained in:
+67
-36
@@ -18,20 +18,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
|
||||||
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
|
"integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "^3.1.2",
|
"bytes": "^3.1.2",
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"http-errors": "^2.0.0",
|
"http-errors": "^2.0.1",
|
||||||
"iconv-lite": "^0.7.0",
|
"iconv-lite": "^0.7.2",
|
||||||
"on-finished": "^2.4.1",
|
"on-finished": "^2.4.1",
|
||||||
"qs": "^6.14.1",
|
"qs": "^6.15.2",
|
||||||
"raw-body": "^3.0.1",
|
"raw-body": "^3.0.2",
|
||||||
"type-is": "^2.0.1"
|
"type-is": "^2.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -41,6 +41,19 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/content-type": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -193,9 +206,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-object-atoms": {
|
"node_modules/es-object-atoms": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@@ -372,9 +385,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -553,9 +566,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "8.3.0",
|
"version": "8.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
||||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
"integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -576,12 +589,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.15.0",
|
"version": "6.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
|
||||||
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.1.0"
|
"es-define-property": "^1.0.1",
|
||||||
|
"side-channel": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
@@ -688,14 +702,14 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
"integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3",
|
"object-inspect": "^1.13.4",
|
||||||
"side-channel-list": "^1.0.0",
|
"side-channel-list": "^1.0.1",
|
||||||
"side-channel-map": "^1.0.1",
|
"side-channel-map": "^1.0.1",
|
||||||
"side-channel-weakmap": "^1.0.2"
|
"side-channel-weakmap": "^1.0.2"
|
||||||
},
|
},
|
||||||
@@ -707,13 +721,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/side-channel-list": {
|
"node_modules/side-channel-list": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3"
|
"object-inspect": "^1.13.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -778,17 +792,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-is": {
|
"node_modules/type-is": {
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
|
||||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
"integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"media-typer": "^1.1.0",
|
"media-typer": "^1.1.0",
|
||||||
"mime-types": "^3.0.0"
|
"mime-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-is/node_modules/content-type": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
|
|||||||
+16
-1
@@ -51,7 +51,14 @@ $ npm install body-parser
|
|||||||
## API
|
## API
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
// Import all parsers
|
||||||
const bodyParser = require('body-parser')
|
const bodyParser = require('body-parser')
|
||||||
|
|
||||||
|
// Or import individual parsers directly
|
||||||
|
const json = require('body-parser/json')
|
||||||
|
const urlencoded = require('body-parser/urlencoded')
|
||||||
|
const raw = require('body-parser/raw')
|
||||||
|
const text = require('body-parser/text')
|
||||||
```
|
```
|
||||||
|
|
||||||
The `bodyParser` object exposes various factories to create middlewares. All
|
The `bodyParser` object exposes various factories to create middlewares. All
|
||||||
@@ -93,11 +100,13 @@ specifies the number of bytes; if it is a string, the value is passed to the
|
|||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
to `'100kb'`.
|
to `'100kb'`.
|
||||||
|
|
||||||
|
> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur.
|
||||||
|
|
||||||
##### reviver
|
##### reviver
|
||||||
|
|
||||||
The `reviver` option is passed directly to `JSON.parse` as the second
|
The `reviver` option is passed directly to `JSON.parse` as the second
|
||||||
argument. You can find more information on this argument
|
argument. You can find more information on this argument
|
||||||
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#Example.3A_Using_the_reviver_parameter).
|
[in the MDN documentation about JSON.parse](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#the_reviver_parameter).
|
||||||
|
|
||||||
##### strict
|
##### strict
|
||||||
|
|
||||||
@@ -149,6 +158,8 @@ specifies the number of bytes; if it is a string, the value is passed to the
|
|||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
to `'100kb'`.
|
to `'100kb'`.
|
||||||
|
|
||||||
|
> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur.
|
||||||
|
|
||||||
##### type
|
##### type
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
The `type` option is used to determine what media type the middleware will
|
||||||
@@ -200,6 +211,8 @@ specifies the number of bytes; if it is a string, the value is passed to the
|
|||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
to `'100kb'`.
|
to `'100kb'`.
|
||||||
|
|
||||||
|
> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur.
|
||||||
|
|
||||||
##### type
|
##### type
|
||||||
|
|
||||||
The `type` option is used to determine what media type the middleware will
|
The `type` option is used to determine what media type the middleware will
|
||||||
@@ -255,6 +268,8 @@ specifies the number of bytes; if it is a string, the value is passed to the
|
|||||||
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
[bytes](https://www.npmjs.com/package/bytes) library for parsing. Defaults
|
||||||
to `'100kb'`.
|
to `'100kb'`.
|
||||||
|
|
||||||
|
> It’s recommended not to configure a very high limit and to use the default value whenever possible. Allowing larger payloads increases memory usage because of the resources required for decoding and transformations, and it can also lead to longer response times as more data is processed. By ‘very high’, we mean values above the default, for example payloads of 5 MB or more can already start to introduce these risks. With the default limits, these issues do not occur.
|
||||||
|
|
||||||
##### parameterLimit
|
##### parameterLimit
|
||||||
|
|
||||||
The `parameterLimit` option controls the maximum number of parameters that
|
The `parameterLimit` option controls the maximum number of parameters that
|
||||||
|
|||||||
+4
-20
@@ -24,41 +24,25 @@ exports = module.exports = bodyParser
|
|||||||
* JSON parser.
|
* JSON parser.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(exports, 'json', {
|
exports.json = require('./lib/types/json')
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => require('./lib/types/json')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raw parser.
|
* Raw parser.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(exports, 'raw', {
|
exports.raw = require('./lib/types/raw')
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => require('./lib/types/raw')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text parser.
|
* Text parser.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(exports, 'text', {
|
exports.text = require('./lib/types/text')
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => require('./lib/types/text')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL-encoded parser.
|
* URL-encoded parser.
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(exports, 'urlencoded', {
|
exports.urlencoded = require('./lib/types/urlencoded')
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
get: () => require('./lib/types/urlencoded')
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a middleware to parse json and urlencoded bodies.
|
* Create a middleware to parse json and urlencoded bodies.
|
||||||
|
|||||||
+17
-17
@@ -11,13 +11,13 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var createError = require('http-errors')
|
const createError = require('http-errors')
|
||||||
var getBody = require('raw-body')
|
const getBody = require('raw-body')
|
||||||
var iconv = require('iconv-lite')
|
const iconv = require('iconv-lite')
|
||||||
var onFinished = require('on-finished')
|
const onFinished = require('on-finished')
|
||||||
var zlib = require('node:zlib')
|
const zlib = require('node:zlib')
|
||||||
var hasBody = require('type-is').hasBody
|
const hasBody = require('type-is').hasBody
|
||||||
var { getCharset } = require('./utils')
|
const { getCharset } = require('./utils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
@@ -63,7 +63,7 @@ function read (req, res, next, parse, debug, options) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var encoding = null
|
let encoding = null
|
||||||
if (options?.skipCharset !== true) {
|
if (options?.skipCharset !== true) {
|
||||||
encoding = getCharset(req) || options.defaultCharset
|
encoding = getCharset(req) || options.defaultCharset
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ function read (req, res, next, parse, debug, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var length
|
let length
|
||||||
var opts = options
|
const opts = options
|
||||||
var stream
|
let stream
|
||||||
|
|
||||||
// read options
|
// read options
|
||||||
var verify = opts.verify
|
const verify = opts.verify
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// get the content stream
|
// get the content stream
|
||||||
@@ -112,7 +112,7 @@ function read (req, res, next, parse, debug, options) {
|
|||||||
debug('read body')
|
debug('read body')
|
||||||
getBody(stream, opts, function (error, body) {
|
getBody(stream, opts, function (error, body) {
|
||||||
if (error) {
|
if (error) {
|
||||||
var _error
|
let _error
|
||||||
|
|
||||||
if (error.type === 'encoding.unsupported') {
|
if (error.type === 'encoding.unsupported') {
|
||||||
// echo back charset
|
// echo back charset
|
||||||
@@ -153,7 +153,7 @@ function read (req, res, next, parse, debug, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse
|
// parse
|
||||||
var str = body
|
let str = body
|
||||||
try {
|
try {
|
||||||
debug('parse body')
|
debug('parse body')
|
||||||
str = typeof body !== 'string' && encoding !== null
|
str = typeof body !== 'string' && encoding !== null
|
||||||
@@ -182,8 +182,8 @@ function read (req, res, next, parse, debug, options) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function contentstream (req, debug, inflate) {
|
function contentstream (req, debug, inflate) {
|
||||||
var encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
|
const encoding = (req.headers['content-encoding'] || 'identity').toLowerCase()
|
||||||
var length = req.headers['content-length']
|
const length = req.headers['content-length']
|
||||||
|
|
||||||
debug('content-encoding "%s"', encoding)
|
debug('content-encoding "%s"', encoding)
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ function contentstream (req, debug, inflate) {
|
|||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = createDecompressionStream(encoding, debug)
|
const stream = createDecompressionStream(encoding, debug)
|
||||||
req.pipe(stream)
|
req.pipe(stream)
|
||||||
return stream
|
return stream
|
||||||
}
|
}
|
||||||
|
|||||||
+69
-41
@@ -12,9 +12,9 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var debug = require('debug')('body-parser:json')
|
const debug = require('debug')('body-parser:json')
|
||||||
var read = require('../read')
|
const read = require('../read')
|
||||||
var { normalizeOptions } = require('../utils')
|
const { normalizeOptions } = require('../utils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
@@ -33,10 +33,10 @@ module.exports = json
|
|||||||
* %x0A / ; Line feed or New line
|
* %x0A / ; Line feed or New line
|
||||||
* %x0D ) ; Carriage return
|
* %x0D ) ; Carriage return
|
||||||
*/
|
*/
|
||||||
var FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
|
const FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
|
||||||
|
|
||||||
var JSON_SYNTAX_CHAR = '#'
|
const JSON_SYNTAX_CHAR = '#'
|
||||||
var JSON_SYNTAX_REGEXP = /#+/g
|
const JSON_SYNTAX_REGEXP = /#+/g
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a middleware to parse JSON bodies.
|
* Create a middleware to parse JSON bodies.
|
||||||
@@ -48,35 +48,7 @@ var JSON_SYNTAX_REGEXP = /#+/g
|
|||||||
function json (options) {
|
function json (options) {
|
||||||
const normalizedOptions = normalizeOptions(options, 'application/json')
|
const normalizedOptions = normalizeOptions(options, 'application/json')
|
||||||
|
|
||||||
var reviver = options?.reviver
|
const parse = createJsonParser(options)
|
||||||
var strict = options?.strict !== false
|
|
||||||
|
|
||||||
function parse (body) {
|
|
||||||
if (body.length === 0) {
|
|
||||||
// special-case empty json body, as it's a common client-side mistake
|
|
||||||
// TODO: maybe make this configurable or part of "strict" option
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strict) {
|
|
||||||
var first = firstchar(body)
|
|
||||||
|
|
||||||
if (first !== '{' && first !== '[') {
|
|
||||||
debug('strict violation')
|
|
||||||
throw createStrictSyntaxError(body, first)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
debug('parse json')
|
|
||||||
return JSON.parse(body, reviver)
|
|
||||||
} catch (e) {
|
|
||||||
throw normalizeJsonSyntaxError(e, {
|
|
||||||
message: e.message,
|
|
||||||
stack: e.stack
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const readOptions = {
|
const readOptions = {
|
||||||
...normalizedOptions,
|
...normalizedOptions,
|
||||||
@@ -89,6 +61,62 @@ function json (options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a JSON parse function
|
||||||
|
*
|
||||||
|
* @param {object} [options]
|
||||||
|
* @return {function}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function createJsonParser (options) {
|
||||||
|
const reviver = options?.reviver
|
||||||
|
const strict = options?.strict !== false
|
||||||
|
|
||||||
|
if (strict) {
|
||||||
|
return function parse (body) {
|
||||||
|
if (body.length === 0) {
|
||||||
|
// special-case empty json body, as it's a common client-side mistake
|
||||||
|
// TODO: maybe make this configurable or part of "strict" option
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const first = firstchar(body)
|
||||||
|
if (first !== '{' && first !== '[') {
|
||||||
|
debug('strict violation')
|
||||||
|
throw createStrictSyntaxError(body, first)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('parse json')
|
||||||
|
return JSON.parse(body, reviver)
|
||||||
|
} catch (e) {
|
||||||
|
throw normalizeJsonSyntaxError(e, {
|
||||||
|
message: e.message,
|
||||||
|
stack: e.stack
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function parse (body) {
|
||||||
|
if (body.length === 0) {
|
||||||
|
// special-case empty json body, as it's a common client-side mistake
|
||||||
|
// TODO: maybe make this configurable or part of "strict" option
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug('parse json')
|
||||||
|
return JSON.parse(body, reviver)
|
||||||
|
} catch (e) {
|
||||||
|
throw normalizeJsonSyntaxError(e, {
|
||||||
|
message: e.message,
|
||||||
|
stack: e.stack
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create strict violation syntax error matching native error.
|
* Create strict violation syntax error matching native error.
|
||||||
*
|
*
|
||||||
@@ -98,8 +126,8 @@ function json (options) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function createStrictSyntaxError (str, char) {
|
function createStrictSyntaxError (str, char) {
|
||||||
var index = str.indexOf(char)
|
const index = str.indexOf(char)
|
||||||
var partial = ''
|
let partial = ''
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
partial = str.substring(0, index) + JSON_SYNTAX_CHAR.repeat(str.length - index)
|
partial = str.substring(0, index) + JSON_SYNTAX_CHAR.repeat(str.length - index)
|
||||||
@@ -125,7 +153,7 @@ function createStrictSyntaxError (str, char) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function firstchar (str) {
|
function firstchar (str) {
|
||||||
var match = FIRST_CHAR_REGEXP.exec(str)
|
const match = FIRST_CHAR_REGEXP.exec(str)
|
||||||
|
|
||||||
return match
|
return match
|
||||||
? match[1]
|
? match[1]
|
||||||
@@ -141,10 +169,10 @@ function firstchar (str) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function normalizeJsonSyntaxError (error, obj) {
|
function normalizeJsonSyntaxError (error, obj) {
|
||||||
var keys = Object.getOwnPropertyNames(error)
|
const keys = Object.getOwnPropertyNames(error)
|
||||||
|
|
||||||
for (var i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
var key = keys[i]
|
const key = keys[i]
|
||||||
if (key !== 'stack' && key !== 'message') {
|
if (key !== 'stack' && key !== 'message') {
|
||||||
delete error[key]
|
delete error[key]
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -10,9 +10,9 @@
|
|||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var debug = require('debug')('body-parser:raw')
|
const debug = require('debug')('body-parser:raw')
|
||||||
var read = require('../read')
|
const read = require('../read')
|
||||||
var { normalizeOptions, passthrough } = require('../utils')
|
const { normalizeOptions, passthrough } = require('../utils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
|
|||||||
+3
-3
@@ -10,9 +10,9 @@
|
|||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var debug = require('debug')('body-parser:text')
|
const debug = require('debug')('body-parser:text')
|
||||||
var read = require('../read')
|
const read = require('../read')
|
||||||
var { normalizeOptions, passthrough } = require('../utils')
|
const { normalizeOptions, passthrough } = require('../utils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
|
|||||||
+16
-20
@@ -12,11 +12,11 @@
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var createError = require('http-errors')
|
const createError = require('http-errors')
|
||||||
var debug = require('debug')('body-parser:urlencoded')
|
const debug = require('debug')('body-parser:urlencoded')
|
||||||
var read = require('../read')
|
const read = require('../read')
|
||||||
var qs = require('qs')
|
const qs = require('qs')
|
||||||
var { normalizeOptions } = require('../utils')
|
const { normalizeOptions } = require('../utils')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
@@ -39,13 +39,7 @@ function urlencoded (options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the appropriate query parser
|
// create the appropriate query parser
|
||||||
var queryparse = createQueryParser(options)
|
const parse = createQueryParser(options)
|
||||||
|
|
||||||
function parse (body, encoding) {
|
|
||||||
return body.length
|
|
||||||
? queryparse(body, encoding)
|
|
||||||
: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
const readOptions = {
|
const readOptions = {
|
||||||
...normalizedOptions,
|
...normalizedOptions,
|
||||||
@@ -66,13 +60,13 @@ function urlencoded (options) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function createQueryParser (options) {
|
function createQueryParser (options) {
|
||||||
var extended = Boolean(options?.extended)
|
const extended = Boolean(options?.extended)
|
||||||
var parameterLimit = options?.parameterLimit !== undefined
|
let parameterLimit = options?.parameterLimit !== undefined
|
||||||
? options?.parameterLimit
|
? options?.parameterLimit
|
||||||
: 1000
|
: 1000
|
||||||
var charsetSentinel = options?.charsetSentinel
|
const charsetSentinel = options?.charsetSentinel
|
||||||
var interpretNumericEntities = options?.interpretNumericEntities
|
const interpretNumericEntities = options?.interpretNumericEntities
|
||||||
var depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
|
const depth = extended ? (options?.depth !== undefined ? options?.depth : 32) : 0
|
||||||
|
|
||||||
if (isNaN(parameterLimit) || parameterLimit < 1) {
|
if (isNaN(parameterLimit) || parameterLimit < 1) {
|
||||||
throw new TypeError('option parameterLimit must be a positive number')
|
throw new TypeError('option parameterLimit must be a positive number')
|
||||||
@@ -86,8 +80,10 @@ function createQueryParser (options) {
|
|||||||
parameterLimit = parameterLimit | 0
|
parameterLimit = parameterLimit | 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return function queryparse (body, encoding) {
|
return function parse (body, encoding) {
|
||||||
var paramCount = parameterCount(body, parameterLimit)
|
if (!body.length) return {}
|
||||||
|
|
||||||
|
const paramCount = parameterCount(body, parameterLimit)
|
||||||
|
|
||||||
if (paramCount === undefined) {
|
if (paramCount === undefined) {
|
||||||
debug('too many parameters')
|
debug('too many parameters')
|
||||||
@@ -96,7 +92,7 @@ function createQueryParser (options) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var arrayLimit = extended ? Math.max(100, paramCount) : paramCount
|
const arrayLimit = extended ? Math.max(100, paramCount) : paramCount
|
||||||
|
|
||||||
debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
|
debug('parse ' + (extended ? 'extended ' : '') + 'urlencoding')
|
||||||
try {
|
try {
|
||||||
|
|||||||
+18
-16
@@ -4,9 +4,9 @@
|
|||||||
* Module dependencies.
|
* Module dependencies.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var bytes = require('bytes')
|
const bytes = require('bytes')
|
||||||
var contentType = require('content-type')
|
const contentType = require('content-type')
|
||||||
var typeis = require('type-is')
|
const typeis = require('type-is')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Module exports.
|
* Module exports.
|
||||||
@@ -25,11 +25,9 @@ module.exports = {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function getCharset (req) {
|
function getCharset (req) {
|
||||||
try {
|
const header = req.headers['content-type']
|
||||||
return (contentType.parse(req).parameters.charset || '').toLowerCase()
|
if (!header) return undefined
|
||||||
} catch {
|
return contentType.parse(header).parameters.charset?.toLowerCase()
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,20 +57,24 @@ function normalizeOptions (options, defaultType) {
|
|||||||
throw new TypeError('defaultType must be provided')
|
throw new TypeError('defaultType must be provided')
|
||||||
}
|
}
|
||||||
|
|
||||||
var inflate = options?.inflate !== false
|
const inflate = options?.inflate !== false
|
||||||
var limit = typeof options?.limit !== 'number'
|
const limit = typeof options?.limit === 'undefined' || options?.limit === null
|
||||||
? bytes.parse(options?.limit || '100kb')
|
? 102400 // 100kb default
|
||||||
: options?.limit
|
: bytes.parse(options.limit)
|
||||||
var type = options?.type || defaultType
|
const type = options?.type || defaultType
|
||||||
var verify = options?.verify || false
|
const verify = options?.verify || false
|
||||||
var defaultCharset = options?.defaultCharset || 'utf-8'
|
const defaultCharset = options?.defaultCharset || 'utf-8'
|
||||||
|
|
||||||
|
if (limit === null) {
|
||||||
|
throw new TypeError(`option limit "${String(options.limit)}" is invalid`)
|
||||||
|
}
|
||||||
|
|
||||||
if (verify !== false && typeof verify !== 'function') {
|
if (verify !== false && typeof verify !== 'function') {
|
||||||
throw new TypeError('option verify must be function')
|
throw new TypeError('option verify must be function')
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the appropriate type checking function
|
// create the appropriate type checking function
|
||||||
var shouldParse = typeof type !== 'function'
|
const shouldParse = typeof type !== 'function'
|
||||||
? typeChecker(type)
|
? typeChecker(type)
|
||||||
: type
|
: type
|
||||||
|
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Douglas Christopher Wilson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
# content-type
|
||||||
|
|
||||||
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[![NPM downloads][downloads-image]][downloads-url]
|
||||||
|
[![Build status][build-image]][build-url]
|
||||||
|
[![Build coverage][coverage-image]][coverage-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
|
||||||
|
Create and parse HTTP `Content-Type` header.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install content-type
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
const contentType = require("content-type");
|
||||||
|
```
|
||||||
|
|
||||||
|
### contentType.parse(string, options?)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const obj = contentType.parse("image/svg+xml; charset=utf-8");
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse a `Content-Type` header. This will return an object with the following properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type. Example: `'image/svg+xml'`.
|
||||||
|
- `parameters`: An object of the parameters in the media type (parameter name is always lower case). Example: `{charset: 'utf-8'}`.
|
||||||
|
|
||||||
|
The parser is lenient and does not error. You should validate `type` and `parameters` before trusting them.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
- `parameters` (default: `true`): Set to `false` to skip parameters.
|
||||||
|
|
||||||
|
### contentType.format(obj)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const str = contentType.format({
|
||||||
|
type: "image/svg+xml",
|
||||||
|
parameters: { charset: "utf-8" },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Format an object into a `Content-Type` header. This will return a string of the content type for the given object with the following properties (examples are shown that produce the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type. Example: `'image/svg+xml'`.
|
||||||
|
- `parameters`: An optional object of the parameters in the media type. Example: `{charset: 'utf-8'}`.
|
||||||
|
|
||||||
|
Throws a `TypeError` if the object contains an invalid type or parameter names.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/content-type
|
||||||
|
[npm-url]: https://npmjs.org/package/content-type
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/content-type
|
||||||
|
[downloads-url]: https://npmjs.org/package/content-type
|
||||||
|
[build-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-type/ci.yml?branch=master
|
||||||
|
[build-url]: https://github.com/jshttp/content-type/actions/workflows/ci.yml?query=branch%3Amaster
|
||||||
|
[coverage-image]: https://img.shields.io/codecov/c/gh/jshttp/content-type
|
||||||
|
[coverage-url]: https://codecov.io/gh/jshttp/content-type
|
||||||
|
[license-image]: http://img.shields.io/npm/l/content-type.svg?style=flat
|
||||||
|
[license-url]: LICENSE
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/*!
|
||||||
|
* content-type
|
||||||
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* The content type object contains a type string and optional parameters.
|
||||||
|
*/
|
||||||
|
export interface ContentType {
|
||||||
|
type: string;
|
||||||
|
parameters: Record<string, string>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Format an object into a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export declare function format(obj: Partial<ContentType>): string;
|
||||||
|
/**
|
||||||
|
* Options for parsing a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export interface ParseOptions {
|
||||||
|
parameters?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parse a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export declare function parse(header: string, options?: ParseOptions): ContentType;
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
"use strict";
|
||||||
|
/*!
|
||||||
|
* content-type
|
||||||
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.format = format;
|
||||||
|
exports.parse = parse;
|
||||||
|
const TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]*$/;
|
||||||
|
const TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
/**
|
||||||
|
* RegExp to match chars that must be quoted-pair in RFC 9110 sec 5.6.4
|
||||||
|
*/
|
||||||
|
const QUOTE_REGEXP = /[\\"]/g;
|
||||||
|
/**
|
||||||
|
* RegExp to match type in RFC 9110 sec 8.3.1
|
||||||
|
*
|
||||||
|
* media-type = type "/" subtype
|
||||||
|
* type = token
|
||||||
|
* subtype = token
|
||||||
|
*/
|
||||||
|
const TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
/**
|
||||||
|
* Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`.
|
||||||
|
*/
|
||||||
|
const NullObject = /* @__PURE__ */ (() => {
|
||||||
|
const C = function () { };
|
||||||
|
C.prototype = Object.create(null);
|
||||||
|
return C;
|
||||||
|
})();
|
||||||
|
/**
|
||||||
|
* Format an object into a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
function format(obj) {
|
||||||
|
const { type, parameters } = obj;
|
||||||
|
if (!type || !TYPE_REGEXP.test(type)) {
|
||||||
|
throw new TypeError(`Invalid type: ${type}`);
|
||||||
|
}
|
||||||
|
let result = type;
|
||||||
|
if (parameters) {
|
||||||
|
for (const param of Object.keys(parameters)) {
|
||||||
|
if (!TOKEN_REGEXP.test(param)) {
|
||||||
|
throw new TypeError(`Invalid parameter name: ${param}`);
|
||||||
|
}
|
||||||
|
result += `; ${param}=${qstring(parameters[param])}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parse a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
function parse(header, options) {
|
||||||
|
const len = header.length;
|
||||||
|
let index = skipOWS(header, 0, len);
|
||||||
|
const valueStart = index;
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
const valueEnd = trailingOWS(header, valueStart, index);
|
||||||
|
const type = header.slice(valueStart, valueEnd).toLowerCase();
|
||||||
|
const parameters = options?.parameters === false
|
||||||
|
? new NullObject()
|
||||||
|
: parseParameters(header, index, len);
|
||||||
|
return { type, parameters };
|
||||||
|
}
|
||||||
|
const SP = 32; // " "
|
||||||
|
const HTAB = 9; // "\t"
|
||||||
|
const SEMI = 59; // ";"
|
||||||
|
const EQ = 61; // "="
|
||||||
|
const DQUOTE = 34; // '"'
|
||||||
|
const BSLASH = 92; // "\\"
|
||||||
|
/**
|
||||||
|
* Parses the parameters of a `Content-Type` header starting at the given index.
|
||||||
|
*/
|
||||||
|
function parseParameters(header, index, len) {
|
||||||
|
const parameters = new NullObject();
|
||||||
|
parameter: while (index < len) {
|
||||||
|
index = skipOWS(header, index + 1 /* Skip over ; */, len);
|
||||||
|
const keyStart = index;
|
||||||
|
while (index < len) {
|
||||||
|
const code = header.charCodeAt(index);
|
||||||
|
if (code === SEMI)
|
||||||
|
continue parameter;
|
||||||
|
if (code === EQ) {
|
||||||
|
const keyEnd = trailingOWS(header, keyStart, index);
|
||||||
|
const key = header.slice(keyStart, keyEnd).toLowerCase();
|
||||||
|
index = skipOWS(header, index + 1, len);
|
||||||
|
if (index < len && header.charCodeAt(index) === DQUOTE) {
|
||||||
|
index++;
|
||||||
|
let value = "";
|
||||||
|
while (index < len) {
|
||||||
|
const code = header.charCodeAt(index++);
|
||||||
|
if (code === DQUOTE) {
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
if (parameters[key] === undefined)
|
||||||
|
parameters[key] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code === BSLASH && index < len) {
|
||||||
|
value += header[index++];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
continue parameter;
|
||||||
|
}
|
||||||
|
const valueStart = index;
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
if (parameters[key] === undefined) {
|
||||||
|
const valueEnd = trailingOWS(header, valueStart, index);
|
||||||
|
parameters[key] = header.slice(valueStart, valueEnd);
|
||||||
|
}
|
||||||
|
continue parameter;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Skip over characters until a semicolon.
|
||||||
|
*/
|
||||||
|
function skipValue(str, index, len) {
|
||||||
|
while (index < len) {
|
||||||
|
const char = str.charCodeAt(index);
|
||||||
|
if (char === SEMI)
|
||||||
|
break;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Skip optional whitespace (OWS) in an HTTP header value.
|
||||||
|
*
|
||||||
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
||||||
|
*/
|
||||||
|
function skipOWS(header, index, len) {
|
||||||
|
while (index < len) {
|
||||||
|
const char = header.charCodeAt(index);
|
||||||
|
if (char !== SP && char !== HTAB)
|
||||||
|
break;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Trim optional whitespace (OWS) from the end of a substring.
|
||||||
|
*
|
||||||
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
||||||
|
*/
|
||||||
|
function trailingOWS(header, start, end) {
|
||||||
|
while (end > start) {
|
||||||
|
const char = header.charCodeAt(end - 1);
|
||||||
|
if (char !== SP && char !== HTAB)
|
||||||
|
break;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Serialize a parameter value.
|
||||||
|
*/
|
||||||
|
function qstring(str) {
|
||||||
|
if (TOKEN_REGEXP.test(str))
|
||||||
|
return str;
|
||||||
|
if (TEXT_REGEXP.test(str))
|
||||||
|
return `"${str.replace(QUOTE_REGEXP, "\\$&")}"`;
|
||||||
|
throw new TypeError(`Invalid parameter value: ${str}`);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+52
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "content-type",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Create and parse HTTP Content-Type header",
|
||||||
|
"keywords": [
|
||||||
|
"content-type",
|
||||||
|
"http",
|
||||||
|
"req",
|
||||||
|
"res",
|
||||||
|
"rfc7231",
|
||||||
|
"rfc9110"
|
||||||
|
],
|
||||||
|
"repository": "jshttp/content-type",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"typings": "./dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"bench": "vitest bench",
|
||||||
|
"build": "ts-scripts build",
|
||||||
|
"format": "ts-scripts format",
|
||||||
|
"prepare": "ts-scripts install && npm run build",
|
||||||
|
"specs": "ts-scripts specs",
|
||||||
|
"test": "ts-scripts test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@borderless/ts-scripts": "^0.15.0",
|
||||||
|
"@vitest/coverage-v8": "^3.0.5",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"ts-scripts": {
|
||||||
|
"dist": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"project": [
|
||||||
|
"tsconfig.build.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-10
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "body-parser",
|
"name": "body-parser",
|
||||||
"description": "Node.js body parsing middleware",
|
"description": "Node.js body parsing middleware",
|
||||||
"version": "2.2.2",
|
"version": "2.3.0",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
||||||
@@ -12,28 +12,41 @@
|
|||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
},
|
},
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": {
|
||||||
|
".": "./index.js",
|
||||||
|
"./package.json": "./package.json",
|
||||||
|
"./json": "./lib/types/json.js",
|
||||||
|
"./raw": "./lib/types/raw.js",
|
||||||
|
"./text": "./lib/types/text.js",
|
||||||
|
"./urlencoded": "./lib/types/urlencoded.js",
|
||||||
|
"./lib/*": "./lib/*.js",
|
||||||
|
"./lib/*.js": "./lib/*.js",
|
||||||
|
"./lib/types/*": "./lib/types/*.js",
|
||||||
|
"./lib/types/*.js": "./lib/types/*.js"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "^3.1.2",
|
"bytes": "^3.1.2",
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"http-errors": "^2.0.0",
|
"http-errors": "^2.0.1",
|
||||||
"iconv-lite": "^0.7.0",
|
"iconv-lite": "^0.7.2",
|
||||||
"on-finished": "^2.4.1",
|
"on-finished": "^2.4.1",
|
||||||
"qs": "^6.14.1",
|
"qs": "^6.15.2",
|
||||||
"raw-body": "^3.0.1",
|
"raw-body": "^3.0.2",
|
||||||
"type-is": "^2.0.1"
|
"type-is": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "^8.57.1",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-standard": "^14.1.1",
|
"eslint-config-standard": "^14.1.1",
|
||||||
"eslint-plugin-import": "^2.31.0",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"eslint-plugin-markdown": "^3.0.1",
|
"eslint-plugin-markdown": "^3.0.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^6.6.0",
|
"eslint-plugin-promise": "^6.6.0",
|
||||||
"eslint-plugin-standard": "^4.1.0",
|
"eslint-plugin-standard": "^4.1.0",
|
||||||
"mocha": "^11.1.0",
|
"mocha": "^11.7.6",
|
||||||
"nyc": "^17.1.0",
|
"nyc": "^17.1.0",
|
||||||
"supertest": "^7.0.0"
|
"supertest": "^7.2.2"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"lib/",
|
"lib/",
|
||||||
|
|||||||
+21
-14
@@ -5,33 +5,40 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [v1.1.1](https://github.com/ljharb/es-object-atoms/compare/v1.1.0...v1.1.1) - 2025-01-14
|
## [v1.1.2](https://github.com/es-shims/es-object-atoms/compare/v1.1.1...v1.1.2) - 2026-05-22
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|
||||||
- [types] `ToObject`: improve types [`cfe8c8a`](https://github.com/ljharb/es-object-atoms/commit/cfe8c8a105c44820cb22e26f62d12ef0ad9715c8)
|
- [Dev Deps] update `@ljharb/eslint-config`, `@ljharb/tsconfig`, `auto-changelog`, `eslint`, `npmignore` [`41e3d94`](https://github.com/es-shims/es-object-atoms/commit/41e3d94f6b49237fa490ec598e068f170c8b161e)
|
||||||
|
- [types] improve `isObject` type [`758edc2`](https://github.com/es-shims/es-object-atoms/commit/758edc2280fa6993a294a55957a43cee5951bf51)
|
||||||
|
|
||||||
## [v1.1.0](https://github.com/ljharb/es-object-atoms/compare/v1.0.1...v1.1.0) - 2025-01-14
|
## [v1.1.1](https://github.com/es-shims/es-object-atoms/compare/v1.1.0...v1.1.1) - 2025-01-14
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|
||||||
- [New] add `isObject` [`51e4042`](https://github.com/ljharb/es-object-atoms/commit/51e4042df722eb3165f40dc5f4bf33d0197ecb07)
|
- [types] `ToObject`: improve types [`cfe8c8a`](https://github.com/es-shims/es-object-atoms/commit/cfe8c8a105c44820cb22e26f62d12ef0ad9715c8)
|
||||||
|
|
||||||
## [v1.0.1](https://github.com/ljharb/es-object-atoms/compare/v1.0.0...v1.0.1) - 2025-01-13
|
## [v1.1.0](https://github.com/es-shims/es-object-atoms/compare/v1.0.1...v1.1.0) - 2025-01-14
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|
||||||
- [Dev Deps] update `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `auto-changelog`, `tape` [`38ab9eb`](https://github.com/ljharb/es-object-atoms/commit/38ab9eb00b62c2f4668644f5e513d9b414ebd595)
|
- [New] add `isObject` [`51e4042`](https://github.com/es-shims/es-object-atoms/commit/51e4042df722eb3165f40dc5f4bf33d0197ecb07)
|
||||||
- [types] improve types [`7d1beb8`](https://github.com/ljharb/es-object-atoms/commit/7d1beb887958b78b6a728a210a1c8370ab7e2aa1)
|
|
||||||
- [Tests] replace `aud` with `npm audit` [`25863ba`](https://github.com/ljharb/es-object-atoms/commit/25863baf99178f1d1ad33d1120498db28631907e)
|
## [v1.0.1](https://github.com/es-shims/es-object-atoms/compare/v1.0.0...v1.0.1) - 2025-01-13
|
||||||
- [Dev Deps] add missing peer dep [`c012309`](https://github.com/ljharb/es-object-atoms/commit/c0123091287e6132d6f4240496340c427433df28)
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `auto-changelog`, `tape` [`38ab9eb`](https://github.com/es-shims/es-object-atoms/commit/38ab9eb00b62c2f4668644f5e513d9b414ebd595)
|
||||||
|
- [types] improve types [`7d1beb8`](https://github.com/es-shims/es-object-atoms/commit/7d1beb887958b78b6a728a210a1c8370ab7e2aa1)
|
||||||
|
- [Tests] replace `aud` with `npm audit` [`25863ba`](https://github.com/es-shims/es-object-atoms/commit/25863baf99178f1d1ad33d1120498db28631907e)
|
||||||
|
- [Dev Deps] add missing peer dep [`c012309`](https://github.com/es-shims/es-object-atoms/commit/c0123091287e6132d6f4240496340c427433df28)
|
||||||
|
|
||||||
## v1.0.0 - 2024-03-16
|
## v1.0.0 - 2024-03-16
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|
||||||
- Initial implementation, tests, readme, types [`f1499db`](https://github.com/ljharb/es-object-atoms/commit/f1499db7d3e1741e64979c61d645ab3137705e82)
|
- Initial implementation, tests, readme, types [`f1499db`](https://github.com/es-shims/es-object-atoms/commit/f1499db7d3e1741e64979c61d645ab3137705e82)
|
||||||
- Initial commit [`99eedc7`](https://github.com/ljharb/es-object-atoms/commit/99eedc7b5fde38a50a28d3c8b724706e3e4c5f6a)
|
- Initial commit [`99eedc7`](https://github.com/es-shims/es-object-atoms/commit/99eedc7b5fde38a50a28d3c8b724706e3e4c5f6a)
|
||||||
- [meta] rename repo [`fc851fa`](https://github.com/ljharb/es-object-atoms/commit/fc851fa70616d2d182aaf0bd02c2ed7084dea8fa)
|
- [meta] rename repo [`fc851fa`](https://github.com/es-shims/es-object-atoms/commit/fc851fa70616d2d182aaf0bd02c2ed7084dea8fa)
|
||||||
- npm init [`b909377`](https://github.com/ljharb/es-object-atoms/commit/b909377c50049bd0ec575562d20b0f9ebae8947f)
|
- npm init [`b909377`](https://github.com/es-shims/es-object-atoms/commit/b909377c50049bd0ec575562d20b0f9ebae8947f)
|
||||||
- Only apps should have lockfiles [`7249edd`](https://github.com/ljharb/es-object-atoms/commit/7249edd2178c1b9ddfc66ffcc6d07fdf0d28efc1)
|
- Only apps should have lockfiles [`7249edd`](https://github.com/es-shims/es-object-atoms/commit/7249edd2178c1b9ddfc66ffcc6d07fdf0d28efc1)
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
declare function isObject(x: unknown): x is object;
|
declare function isObject<T>(x: T): x is T & object & Record<PropertyKey, unknown>;
|
||||||
|
|
||||||
export = isObject;
|
export = isObject;
|
||||||
|
|||||||
+6
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "es-object-atoms",
|
"name": "es-object-atoms",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"description": "ES Object-related atoms: Object, ToObject, RequireObjectCoercible",
|
"description": "ES Object-related atoms: Object, ToObject, RequireObjectCoercible",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -46,16 +46,15 @@
|
|||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ljharb/eslint-config": "^21.1.1",
|
"@ljharb/eslint-config": "^22.2.3",
|
||||||
"@ljharb/tsconfig": "^0.2.3",
|
"@ljharb/tsconfig": "^0.3.2",
|
||||||
"@types/tape": "^5.8.1",
|
"@types/tape": "^5.8.1",
|
||||||
"auto-changelog": "^2.5.0",
|
"auto-changelog": "^2.5.1",
|
||||||
"eclint": "^2.8.1",
|
"eclint": "^2.8.1",
|
||||||
"encoding": "^0.1.13",
|
"eslint": "^8.57.1",
|
||||||
"eslint": "^8.8.0",
|
|
||||||
"evalmd": "^0.0.19",
|
"evalmd": "^0.0.19",
|
||||||
"in-publish": "^2.0.1",
|
"in-publish": "^2.0.1",
|
||||||
"npmignore": "^0.3.1",
|
"npmignore": "^0.3.5",
|
||||||
"nyc": "^10.3.2",
|
"nyc": "^10.3.2",
|
||||||
"safe-publish-latest": "^2.0.0",
|
"safe-publish-latest": "^2.0.0",
|
||||||
"tape": "^5.9.0",
|
"tape": "^5.9.0",
|
||||||
|
|||||||
+1
@@ -2,5 +2,6 @@
|
|||||||
"extends": "@ljharb/tsconfig",
|
"extends": "@ljharb/tsconfig",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
|
"ignoreDeprecations": "6.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"root": true,
|
|
||||||
|
|
||||||
"extends": "@ljharb",
|
|
||||||
}
|
|
||||||
+18
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v2.0.4](https://github.com/inspect-js/hasOwn/compare/v2.0.3...v2.0.4) - 2026-05-28
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [types] drop the dead key-narrowing overload [`fdab00e`](https://github.com/inspect-js/hasOwn/commit/fdab00e2703e65411424e19bf86a7e72a8f10da9)
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `auto-changelog`, `eslint` [`91f6247`](https://github.com/inspect-js/hasOwn/commit/91f624768dd0f7db0d019b89d4d86bd66e20ec30)
|
||||||
|
|
||||||
|
## [v2.0.3](https://github.com/inspect-js/hasOwn/compare/v2.0.2...v2.0.3) - 2026-04-17
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [actions] update workflows [`fb837b8`](https://github.com/inspect-js/hasOwn/commit/fb837b849bcdb8416fdc8fd344edfacd5574696c)
|
||||||
|
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `auto-changelog`, `eslint`, `mock-property`, `npmignore`, `tape` [`f4b279b`](https://github.com/inspect-js/hasOwn/commit/f4b279bd682be34b3f0ede2a58d4e8acb58d6d47)
|
||||||
|
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`; migrate to flat config [`7e415ce`](https://github.com/inspect-js/hasOwn/commit/7e415cee55ebf43b3c34d7fd86db73a9928b05f7)
|
||||||
|
- [Dev Deps] update `eslint` [`ef313da`](https://github.com/inspect-js/hasOwn/commit/ef313da342d33b60e23e738b9f5a86f6065f39ef)
|
||||||
|
- [meta] use `npm audit` instead of `aud` [`d5c6d4d`](https://github.com/inspect-js/hasOwn/commit/d5c6d4d7a19c6ca4f14ac173b30d8bf25abcabee)
|
||||||
|
- [types] add overload that narrows the key [`cc03a09`](https://github.com/inspect-js/hasOwn/commit/cc03a097e9402fb8b86d413050e67f790dd6c8c5)
|
||||||
|
|
||||||
## [v2.0.2](https://github.com/inspect-js/hasOwn/compare/v2.0.1...v2.0.2) - 2024-03-10
|
## [v2.0.2](https://github.com/inspect-js/hasOwn/compare/v2.0.1...v2.0.2) - 2024-03-10
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import ljharbConfig from '@ljharb/eslint-config/flat';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...ljharbConfig,
|
||||||
|
{ rules: { 'no-extra-parens': 'off' } },
|
||||||
|
];
|
||||||
+13
-14
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hasown",
|
"name": "hasown",
|
||||||
"version": "2.0.2",
|
"version": "2.0.4",
|
||||||
"description": "A robust, ES3 compatible, \"has own property\" predicate.",
|
"description": "A robust, ES3 compatible, \"has own property\" predicate.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -14,14 +14,14 @@
|
|||||||
"prepublish": "not-in-publish || npm run prepublishOnly",
|
"prepublish": "not-in-publish || npm run prepublishOnly",
|
||||||
"prepublishOnly": "safe-publish-latest",
|
"prepublishOnly": "safe-publish-latest",
|
||||||
"prelint": "evalmd README.md",
|
"prelint": "evalmd README.md",
|
||||||
"lint": "eslint --ext=js,mjs .",
|
"lint": "eslint .",
|
||||||
"postlint": "npm run tsc",
|
"postlint": "npm run tsc",
|
||||||
"pretest": "npm run lint",
|
"pretest": "npm run lint",
|
||||||
"tsc": "tsc -p .",
|
"tsc": "tsc -p .",
|
||||||
"posttsc": "attw -P",
|
"posttsc": "attw -P",
|
||||||
"tests-only": "nyc tape 'test/**/*.js'",
|
"tests-only": "nyc tape 'test/**/*.js'",
|
||||||
"test": "npm run tests-only",
|
"test": "npm run tests-only",
|
||||||
"posttest": "aud --production",
|
"posttest": "npx npm@\">= 10.2\" audit --production",
|
||||||
"version": "auto-changelog && git add CHANGELOG.md",
|
"version": "auto-changelog && git add CHANGELOG.md",
|
||||||
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
|
"postversion": "auto-changelog && git add CHANGELOG.md && git commit --no-edit --amend && git tag -f \"v$(node -e \"console.log(require('./package.json').version)\")\""
|
||||||
},
|
},
|
||||||
@@ -51,22 +51,21 @@
|
|||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.15.1",
|
"@arethetypeswrong/cli": "^0.18.2",
|
||||||
"@ljharb/eslint-config": "^21.1.0",
|
"@ljharb/eslint-config": "^22.2.3",
|
||||||
"@ljharb/tsconfig": "^0.2.0",
|
"@ljharb/tsconfig": "^0.3.2",
|
||||||
"@types/function-bind": "^1.1.10",
|
"@types/function-bind": "^1.1.10",
|
||||||
"@types/mock-property": "^1.0.2",
|
"@types/tape": "^5.8.1",
|
||||||
"@types/tape": "^5.6.4",
|
"auto-changelog": "^2.5.1",
|
||||||
"aud": "^2.0.4",
|
"eslint": "^10.4.0",
|
||||||
"auto-changelog": "^2.4.0",
|
|
||||||
"eslint": "=8.8.0",
|
|
||||||
"evalmd": "^0.0.19",
|
"evalmd": "^0.0.19",
|
||||||
"in-publish": "^2.0.1",
|
"in-publish": "^2.0.1",
|
||||||
"mock-property": "^1.0.3",
|
"jiti": "^0.0.0",
|
||||||
"npmignore": "^0.3.1",
|
"mock-property": "^1.1.0",
|
||||||
|
"npmignore": "^0.3.5",
|
||||||
"nyc": "^10.3.2",
|
"nyc": "^10.3.2",
|
||||||
"safe-publish-latest": "^2.0.0",
|
"safe-publish-latest": "^2.0.0",
|
||||||
"tape": "^5.7.5",
|
"tape": "^5.9.0",
|
||||||
"typescript": "next"
|
"typescript": "next"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
+3
-3
@@ -153,7 +153,7 @@ The `parse` function accepts a string and returns `TokenData`, which can be used
|
|||||||
|
|
||||||
`TokenData` has two properties:
|
`TokenData` has two properties:
|
||||||
|
|
||||||
- **tokens** A sequence of tokens, currently of types `text`, `parameter`, `wildcard`, or `group`.
|
- **tokens** A sequence of tokens, currently of types `text`, `param`, `wildcard`, or `group`.
|
||||||
- **originalPath** The original path used with `parse`, shown in error messages to assist debugging.
|
- **originalPath** The original path used with `parse`, shown in error messages to assist debugging.
|
||||||
|
|
||||||
### Custom path
|
### Custom path
|
||||||
@@ -165,13 +165,13 @@ import { match } from "path-to-regexp";
|
|||||||
|
|
||||||
const tokens = [
|
const tokens = [
|
||||||
{ type: "text", value: "/" },
|
{ type: "text", value: "/" },
|
||||||
{ type: "parameter", name: "foo" },
|
{ type: "param", name: "foo" },
|
||||||
];
|
];
|
||||||
const originalPath = "/[foo]"; // To help debug error messages.
|
const originalPath = "/[foo]"; // To help debug error messages.
|
||||||
const path = { tokens, originalPath };
|
const path = { tokens, originalPath };
|
||||||
const fn = match(path);
|
const fn = match(path);
|
||||||
|
|
||||||
fn("/test"); //=> { path: '/test', index: 0, params: { foo: 'test' } }
|
fn("/test"); //=> { path: '/test', params: { foo: 'test' } }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
|
|||||||
+3
@@ -134,6 +134,9 @@ export type Path = string | TokenData;
|
|||||||
* Transform a path into a match function.
|
* Transform a path into a match function.
|
||||||
*/
|
*/
|
||||||
export declare function match<P extends ParamData>(path: Path | Path[], options?: MatchOptions & ParseOptions): MatchFunction<P>;
|
export declare function match<P extends ParamData>(path: Path | Path[], options?: MatchOptions & ParseOptions): MatchFunction<P>;
|
||||||
|
/**
|
||||||
|
* Transform a path into a regular expression and capture keys.
|
||||||
|
*/
|
||||||
export declare function pathToRegexp(path: Path | Path[], options?: PathToRegexpOptions & ParseOptions): {
|
export declare function pathToRegexp(path: Path | Path[], options?: PathToRegexpOptions & ParseOptions): {
|
||||||
regexp: RegExp;
|
regexp: RegExp;
|
||||||
keys: Keys;
|
keys: Keys;
|
||||||
|
|||||||
+198
-176
@@ -10,19 +10,7 @@ const DEFAULT_DELIMITER = "/";
|
|||||||
const NOOP_VALUE = (value) => value;
|
const NOOP_VALUE = (value) => value;
|
||||||
const ID_START = /^[$_\p{ID_Start}]$/u;
|
const ID_START = /^[$_\p{ID_Start}]$/u;
|
||||||
const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
|
const ID_CONTINUE = /^[$\u200c\u200d\p{ID_Continue}]$/u;
|
||||||
const SIMPLE_TOKENS = {
|
const ID = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u;
|
||||||
// Groups.
|
|
||||||
"{": "{",
|
|
||||||
"}": "}",
|
|
||||||
// Reserved.
|
|
||||||
"(": "(",
|
|
||||||
")": ")",
|
|
||||||
"[": "[",
|
|
||||||
"]": "]",
|
|
||||||
"+": "+",
|
|
||||||
"?": "?",
|
|
||||||
"!": "!",
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* Escape text for stringify to path.
|
* Escape text for stringify to path.
|
||||||
*/
|
*/
|
||||||
@@ -65,20 +53,44 @@ exports.PathError = PathError;
|
|||||||
function parse(str, options = {}) {
|
function parse(str, options = {}) {
|
||||||
const { encodePath = NOOP_VALUE } = options;
|
const { encodePath = NOOP_VALUE } = options;
|
||||||
const chars = [...str];
|
const chars = [...str];
|
||||||
const tokens = [];
|
|
||||||
let index = 0;
|
let index = 0;
|
||||||
let pos = 0;
|
function consumeUntil(end) {
|
||||||
function name() {
|
const output = [];
|
||||||
let value = "";
|
let path = "";
|
||||||
|
function writePath() {
|
||||||
|
if (!path)
|
||||||
|
return;
|
||||||
|
output.push({
|
||||||
|
type: "text",
|
||||||
|
value: encodePath(path),
|
||||||
|
});
|
||||||
|
path = "";
|
||||||
|
}
|
||||||
|
while (index < chars.length) {
|
||||||
|
const value = chars[index++];
|
||||||
|
if (value === end) {
|
||||||
|
writePath();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
if (value === "\\") {
|
||||||
|
if (index === chars.length) {
|
||||||
|
throw new PathError(`Unexpected end after \\ at index ${index}`, str);
|
||||||
|
}
|
||||||
|
path += chars[index++];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value === ":" || value === "*") {
|
||||||
|
const type = value === ":" ? "param" : "wildcard";
|
||||||
|
let name = "";
|
||||||
if (ID_START.test(chars[index])) {
|
if (ID_START.test(chars[index])) {
|
||||||
do {
|
do {
|
||||||
value += chars[index++];
|
name += chars[index++];
|
||||||
} while (ID_CONTINUE.test(chars[index]));
|
} while (ID_CONTINUE.test(chars[index]));
|
||||||
}
|
}
|
||||||
else if (chars[index] === '"') {
|
else if (chars[index] === '"') {
|
||||||
let quoteStart = index;
|
let quoteStart = index;
|
||||||
while (index++ < chars.length) {
|
while (index < chars.length) {
|
||||||
if (chars[index] === '"') {
|
if (chars[++index] === '"') {
|
||||||
index++;
|
index++;
|
||||||
quoteStart = 0;
|
quoteStart = 0;
|
||||||
break;
|
break;
|
||||||
@@ -86,75 +98,46 @@ function parse(str, options = {}) {
|
|||||||
// Increment over escape characters.
|
// Increment over escape characters.
|
||||||
if (chars[index] === "\\")
|
if (chars[index] === "\\")
|
||||||
index++;
|
index++;
|
||||||
value += chars[index];
|
name += chars[index];
|
||||||
}
|
}
|
||||||
if (quoteStart) {
|
if (quoteStart) {
|
||||||
throw new PathError(`Unterminated quote at index ${quoteStart}`, str);
|
throw new PathError(`Unterminated quote at index ${quoteStart}`, str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!value) {
|
if (!name) {
|
||||||
throw new PathError(`Missing parameter name at index ${index}`, str);
|
throw new PathError(`Missing parameter name at index ${index}`, str);
|
||||||
}
|
}
|
||||||
return value;
|
writePath();
|
||||||
}
|
output.push({ type, name });
|
||||||
while (index < chars.length) {
|
|
||||||
const value = chars[index];
|
|
||||||
const type = SIMPLE_TOKENS[value];
|
|
||||||
if (type) {
|
|
||||||
tokens.push({ type, index: index++, value });
|
|
||||||
}
|
|
||||||
else if (value === "\\") {
|
|
||||||
tokens.push({ type: "escape", index: index++, value: chars[index++] });
|
|
||||||
}
|
|
||||||
else if (value === ":") {
|
|
||||||
tokens.push({ type: "param", index: index++, value: name() });
|
|
||||||
}
|
|
||||||
else if (value === "*") {
|
|
||||||
tokens.push({ type: "wildcard", index: index++, value: name() });
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
tokens.push({ type: "char", index: index++, value });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tokens.push({ type: "end", index, value: "" });
|
|
||||||
function consumeUntil(endType) {
|
|
||||||
const output = [];
|
|
||||||
while (true) {
|
|
||||||
const token = tokens[pos++];
|
|
||||||
if (token.type === endType)
|
|
||||||
break;
|
|
||||||
if (token.type === "char" || token.type === "escape") {
|
|
||||||
let path = token.value;
|
|
||||||
let cur = tokens[pos];
|
|
||||||
while (cur.type === "char" || cur.type === "escape") {
|
|
||||||
path += cur.value;
|
|
||||||
cur = tokens[++pos];
|
|
||||||
}
|
|
||||||
output.push({
|
|
||||||
type: "text",
|
|
||||||
value: encodePath(path),
|
|
||||||
});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.type === "param" || token.type === "wildcard") {
|
if (value === "{") {
|
||||||
output.push({
|
writePath();
|
||||||
type: token.type,
|
|
||||||
name: token.value,
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (token.type === "{") {
|
|
||||||
output.push({
|
output.push({
|
||||||
type: "group",
|
type: "group",
|
||||||
tokens: consumeUntil("}"),
|
tokens: consumeUntil("}"),
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw new PathError(`Unexpected ${token.type} at index ${token.index}, expected ${endType}`, str);
|
if (value === "}" ||
|
||||||
|
value === "(" ||
|
||||||
|
value === ")" ||
|
||||||
|
value === "[" ||
|
||||||
|
value === "]" ||
|
||||||
|
value === "+" ||
|
||||||
|
value === "?" ||
|
||||||
|
value === "!") {
|
||||||
|
throw new PathError(`Unexpected ${value} at index ${index - 1}`, str);
|
||||||
}
|
}
|
||||||
|
path += value;
|
||||||
|
}
|
||||||
|
if (end) {
|
||||||
|
throw new PathError(`Unexpected end at index ${index}, expected ${end}`, str);
|
||||||
|
}
|
||||||
|
writePath();
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
return new TokenData(consumeUntil("end"), str);
|
return new TokenData(consumeUntil(""), str);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Compile a string to a template function for the path.
|
* Compile a string to a template function for the path.
|
||||||
@@ -164,7 +147,8 @@ function compile(path, options = {}) {
|
|||||||
const data = typeof path === "object" ? path : parse(path, options);
|
const data = typeof path === "object" ? path : parse(path, options);
|
||||||
const fn = tokensToFunction(data.tokens, delimiter, encode);
|
const fn = tokensToFunction(data.tokens, delimiter, encode);
|
||||||
return function path(params = {}) {
|
return function path(params = {}) {
|
||||||
const [path, ...missing] = fn(params);
|
const missing = [];
|
||||||
|
const path = fn(params, missing);
|
||||||
if (missing.length) {
|
if (missing.length) {
|
||||||
throw new TypeError(`Missing parameters: ${missing.join(", ")}`);
|
throw new TypeError(`Missing parameters: ${missing.join(", ")}`);
|
||||||
}
|
}
|
||||||
@@ -173,12 +157,10 @@ function compile(path, options = {}) {
|
|||||||
}
|
}
|
||||||
function tokensToFunction(tokens, delimiter, encode) {
|
function tokensToFunction(tokens, delimiter, encode) {
|
||||||
const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode));
|
const encoders = tokens.map((token) => tokenToFunction(token, delimiter, encode));
|
||||||
return (data) => {
|
return (data, missing) => {
|
||||||
const result = [""];
|
let result = "";
|
||||||
for (const encoder of encoders) {
|
for (const encoder of encoders) {
|
||||||
const [value, ...extras] = encoder(data);
|
result += encoder(data, missing);
|
||||||
result[0] += value;
|
|
||||||
result.push(...extras);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@@ -188,45 +170,51 @@ function tokensToFunction(tokens, delimiter, encode) {
|
|||||||
*/
|
*/
|
||||||
function tokenToFunction(token, delimiter, encode) {
|
function tokenToFunction(token, delimiter, encode) {
|
||||||
if (token.type === "text")
|
if (token.type === "text")
|
||||||
return () => [token.value];
|
return () => token.value;
|
||||||
if (token.type === "group") {
|
if (token.type === "group") {
|
||||||
const fn = tokensToFunction(token.tokens, delimiter, encode);
|
const fn = tokensToFunction(token.tokens, delimiter, encode);
|
||||||
return (data) => {
|
return (data, missing) => {
|
||||||
const [value, ...missing] = fn(data);
|
const len = missing.length;
|
||||||
if (!missing.length)
|
const value = fn(data, missing);
|
||||||
return [value];
|
if (missing.length === len)
|
||||||
return [""];
|
return value;
|
||||||
|
missing.length = len; // Reset optional group.
|
||||||
|
return "";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const encodeValue = encode || NOOP_VALUE;
|
const encodeValue = encode || NOOP_VALUE;
|
||||||
if (token.type === "wildcard" && encode !== false) {
|
if (token.type === "wildcard" && encode !== false) {
|
||||||
return (data) => {
|
return (data, missing) => {
|
||||||
const value = data[token.name];
|
const value = data[token.name];
|
||||||
if (value == null)
|
if (value == null) {
|
||||||
return ["", token.name];
|
missing.push(token.name);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
if (!Array.isArray(value) || value.length === 0) {
|
if (!Array.isArray(value) || value.length === 0) {
|
||||||
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
|
throw new TypeError(`Expected "${token.name}" to be a non-empty array`);
|
||||||
}
|
}
|
||||||
return [
|
let result = "";
|
||||||
value
|
for (let i = 0; i < value.length; i++) {
|
||||||
.map((value, index) => {
|
if (typeof value[i] !== "string") {
|
||||||
if (typeof value !== "string") {
|
throw new TypeError(`Expected "${token.name}/${i}" to be a string`);
|
||||||
throw new TypeError(`Expected "${token.name}/${index}" to be a string`);
|
|
||||||
}
|
}
|
||||||
return encodeValue(value);
|
if (i > 0)
|
||||||
})
|
result += delimiter;
|
||||||
.join(delimiter),
|
result += encodeValue(value[i]);
|
||||||
];
|
}
|
||||||
|
return result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return (data) => {
|
return (data, missing) => {
|
||||||
const value = data[token.name];
|
const value = data[token.name];
|
||||||
if (value == null)
|
if (value == null) {
|
||||||
return ["", token.name];
|
missing.push(token.name);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
if (typeof value !== "string") {
|
if (typeof value !== "string") {
|
||||||
throw new TypeError(`Expected "${token.name}" to be a string`);
|
throw new TypeError(`Expected "${token.name}" to be a string`);
|
||||||
}
|
}
|
||||||
return [encodeValue(value)];
|
return encodeValue(value);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@@ -258,54 +246,53 @@ function match(path, options = {}) {
|
|||||||
return { path, params };
|
return { path, params };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Transform a path into a regular expression and capture keys.
|
||||||
|
*/
|
||||||
function pathToRegexp(path, options = {}) {
|
function pathToRegexp(path, options = {}) {
|
||||||
const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true, } = options;
|
const { delimiter = DEFAULT_DELIMITER, end = true, sensitive = false, trailing = true, } = options;
|
||||||
const keys = [];
|
const keys = [];
|
||||||
const flags = sensitive ? "" : "i";
|
let source = "";
|
||||||
const sources = [];
|
let combinations = 0;
|
||||||
for (const input of pathsToArray(path, [])) {
|
function process(path) {
|
||||||
const data = typeof input === "object" ? input : parse(input, options);
|
if (Array.isArray(path)) {
|
||||||
for (const tokens of flatten(data.tokens, 0, [])) {
|
for (const p of path)
|
||||||
sources.push(toRegExpSource(tokens, delimiter, keys, data.originalPath));
|
process(p);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
const data = typeof path === "object" ? path : parse(path, options);
|
||||||
|
flatten(data.tokens, 0, [], (tokens) => {
|
||||||
|
if (combinations >= 256) {
|
||||||
|
throw new PathError("Too many path combinations", data.originalPath);
|
||||||
}
|
}
|
||||||
let pattern = `^(?:${sources.join("|")})`;
|
if (combinations > 0)
|
||||||
|
source += "|";
|
||||||
|
source += toRegExpSource(tokens, delimiter, keys, data.originalPath);
|
||||||
|
combinations++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
process(path);
|
||||||
|
let pattern = `^(?:${source})`;
|
||||||
if (trailing)
|
if (trailing)
|
||||||
pattern += `(?:${escape(delimiter)}$)?`;
|
pattern += "(?:" + escape(delimiter) + "$)?";
|
||||||
pattern += end ? "$" : `(?=${escape(delimiter)}|$)`;
|
pattern += end ? "$" : "(?=" + escape(delimiter) + "|$)";
|
||||||
const regexp = new RegExp(pattern, flags);
|
return { regexp: new RegExp(pattern, sensitive ? "" : "i"), keys };
|
||||||
return { regexp, keys };
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Convert a path or array of paths into a flat array.
|
|
||||||
*/
|
|
||||||
function pathsToArray(paths, init) {
|
|
||||||
if (Array.isArray(paths)) {
|
|
||||||
for (const p of paths)
|
|
||||||
pathsToArray(p, init);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
init.push(paths);
|
|
||||||
}
|
|
||||||
return init;
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Generate a flat list of sequence tokens from the given tokens.
|
* Generate a flat list of sequence tokens from the given tokens.
|
||||||
*/
|
*/
|
||||||
function* flatten(tokens, index, init) {
|
function flatten(tokens, index, result, callback) {
|
||||||
if (index === tokens.length) {
|
while (index < tokens.length) {
|
||||||
return yield init;
|
const token = tokens[index++];
|
||||||
}
|
|
||||||
const token = tokens[index];
|
|
||||||
if (token.type === "group") {
|
if (token.type === "group") {
|
||||||
for (const seq of flatten(token.tokens, 0, init.slice())) {
|
const len = result.length;
|
||||||
yield* flatten(tokens, index + 1, seq);
|
flatten(token.tokens, 0, result, (seq) => flatten(tokens, index, seq, callback));
|
||||||
|
result.length = len;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
result.push(token);
|
||||||
}
|
}
|
||||||
else {
|
callback(result);
|
||||||
init.push(token);
|
|
||||||
}
|
|
||||||
yield* flatten(tokens, index + 1, init);
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Transform a flat sequence of tokens into a regular expression.
|
* Transform a flat sequence of tokens into a regular expression.
|
||||||
@@ -313,72 +300,111 @@ function* flatten(tokens, index, init) {
|
|||||||
function toRegExpSource(tokens, delimiter, keys, originalPath) {
|
function toRegExpSource(tokens, delimiter, keys, originalPath) {
|
||||||
let result = "";
|
let result = "";
|
||||||
let backtrack = "";
|
let backtrack = "";
|
||||||
let isSafeSegmentParam = true;
|
let wildcardBacktrack = "";
|
||||||
for (const token of tokens) {
|
let prevCaptureType = 0;
|
||||||
|
let hasSegmentCapture = 0;
|
||||||
|
let index = 0;
|
||||||
|
function hasInSegment(index, type) {
|
||||||
|
while (index < tokens.length) {
|
||||||
|
const token = tokens[index++];
|
||||||
|
if (token.type === type)
|
||||||
|
return true;
|
||||||
|
if (token.type === "text") {
|
||||||
|
if (token.value.includes(delimiter))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function peekText(index) {
|
||||||
|
let result = "";
|
||||||
|
while (index < tokens.length) {
|
||||||
|
const token = tokens[index++];
|
||||||
|
if (token.type !== "text")
|
||||||
|
break;
|
||||||
|
result += token.value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
while (index < tokens.length) {
|
||||||
|
const token = tokens[index++];
|
||||||
if (token.type === "text") {
|
if (token.type === "text") {
|
||||||
result += escape(token.value);
|
result += escape(token.value);
|
||||||
backtrack += token.value;
|
backtrack += token.value;
|
||||||
isSafeSegmentParam || (isSafeSegmentParam = token.value.includes(delimiter));
|
if (prevCaptureType === 2)
|
||||||
|
wildcardBacktrack += token.value;
|
||||||
|
if (token.value.includes(delimiter))
|
||||||
|
hasSegmentCapture = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.type === "param" || token.type === "wildcard") {
|
if (token.type === "param" || token.type === "wildcard") {
|
||||||
if (!isSafeSegmentParam && !backtrack) {
|
if (prevCaptureType && !backtrack) {
|
||||||
throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath);
|
throw new PathError(`Missing text before "${token.name}" ${token.type}`, originalPath);
|
||||||
}
|
}
|
||||||
if (token.type === "param") {
|
if (token.type === "param") {
|
||||||
result += `(${negate(delimiter, isSafeSegmentParam ? "" : backtrack)}+)`;
|
result +=
|
||||||
|
hasSegmentCapture & 2 // Seen wildcard in segment.
|
||||||
|
? `(${negate(delimiter, backtrack)}+)`
|
||||||
|
: hasInSegment(index, "wildcard") // See wildcard later in segment.
|
||||||
|
? `(${negate(delimiter, peekText(index))}+)`
|
||||||
|
: hasSegmentCapture & 1 // Seen parameter in segment.
|
||||||
|
? `(${negate(delimiter, backtrack)}+|${escape(backtrack)})`
|
||||||
|
: `(${negate(delimiter, "")}+)`;
|
||||||
|
hasSegmentCapture |= prevCaptureType = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result += `([\\s\\S]+)`;
|
result +=
|
||||||
|
hasSegmentCapture & 2 // Seen wildcard in segment.
|
||||||
|
? `(${negate(backtrack, "")}+)`
|
||||||
|
: wildcardBacktrack // No capture in segment, seen wildcard in path.
|
||||||
|
? `(${negate(wildcardBacktrack, "")}+|${negate(delimiter, "")}+)`
|
||||||
|
: `([^]+)`;
|
||||||
|
wildcardBacktrack = "";
|
||||||
|
hasSegmentCapture |= prevCaptureType = 2;
|
||||||
}
|
}
|
||||||
keys.push(token);
|
keys.push(token);
|
||||||
backtrack = "";
|
backtrack = "";
|
||||||
isSafeSegmentParam = false;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
throw new TypeError(`Unknown token type: ${token.type}`);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Block backtracking on previous text and ignore delimiter string.
|
* Block backtracking on previous text/delimiter.
|
||||||
*/
|
*/
|
||||||
function negate(delimiter, backtrack) {
|
function negate(a, b) {
|
||||||
if (backtrack.length < 2) {
|
if (b.length > a.length)
|
||||||
if (delimiter.length < 2)
|
return negate(b, a); // Longest string first.
|
||||||
return `[^${escape(delimiter + backtrack)}]`;
|
if (a === b)
|
||||||
return `(?:(?!${escape(delimiter)})[^${escape(backtrack)}])`;
|
b = ""; // Cleaner regex strings, no duplication.
|
||||||
}
|
if (b.length > 1)
|
||||||
if (delimiter.length < 2) {
|
return `(?:(?!${escape(a)}|${escape(b)})[^])`;
|
||||||
return `(?:(?!${escape(backtrack)})[^${escape(delimiter)}])`;
|
if (a.length > 1)
|
||||||
}
|
return `(?:(?!${escape(a)})[^${escape(b)}])`;
|
||||||
return `(?:(?!${escape(backtrack)}|${escape(delimiter)})[\\s\\S])`;
|
return `[^${escape(a + b)}]`;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Stringify an array of tokens into a path string.
|
* Stringify an array of tokens into a path string.
|
||||||
*/
|
*/
|
||||||
function stringifyTokens(tokens) {
|
function stringifyTokens(tokens, index) {
|
||||||
let value = "";
|
let value = "";
|
||||||
let i = 0;
|
while (index < tokens.length) {
|
||||||
function name(value) {
|
const token = tokens[index++];
|
||||||
const isSafe = isNameSafe(value) && isNextNameSafe(tokens[i]);
|
|
||||||
return isSafe ? value : JSON.stringify(value);
|
|
||||||
}
|
|
||||||
while (i < tokens.length) {
|
|
||||||
const token = tokens[i++];
|
|
||||||
if (token.type === "text") {
|
if (token.type === "text") {
|
||||||
value += escapeText(token.value);
|
value += escapeText(token.value);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.type === "group") {
|
if (token.type === "group") {
|
||||||
value += `{${stringifyTokens(token.tokens)}}`;
|
value += "{" + stringifyTokens(token.tokens, 0) + "}";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.type === "param") {
|
if (token.type === "param") {
|
||||||
value += `:${name(token.name)}`;
|
value += ":" + stringifyName(token.name, tokens[index]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (token.type === "wildcard") {
|
if (token.type === "wildcard") {
|
||||||
value += `*${name(token.name)}`;
|
value += "*" + stringifyName(token.name, tokens[index]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
throw new TypeError(`Unknown token type: ${token.type}`);
|
throw new TypeError(`Unknown token type: ${token.type}`);
|
||||||
@@ -389,21 +415,17 @@ function stringifyTokens(tokens) {
|
|||||||
* Stringify token data into a path string.
|
* Stringify token data into a path string.
|
||||||
*/
|
*/
|
||||||
function stringify(data) {
|
function stringify(data) {
|
||||||
return stringifyTokens(data.tokens);
|
return stringifyTokens(data.tokens, 0);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Validate the parameter name contains valid ID characters.
|
* Stringify a parameter name, escaping when it cannot be emitted directly.
|
||||||
*/
|
*/
|
||||||
function isNameSafe(name) {
|
function stringifyName(name, next) {
|
||||||
const [first, ...rest] = name;
|
if (!ID.test(name))
|
||||||
return ID_START.test(first) && rest.every((char) => ID_CONTINUE.test(char));
|
return JSON.stringify(name);
|
||||||
}
|
if ((next === null || next === void 0 ? void 0 : next.type) === "text" && ID_CONTINUE.test(next.value[0])) {
|
||||||
/**
|
return JSON.stringify(name);
|
||||||
* Validate the next token does not interfere with the current param name.
|
}
|
||||||
*/
|
return name;
|
||||||
function isNextNameSafe(token) {
|
|
||||||
if (token && token.type === "text")
|
|
||||||
return !ID_CONTINUE.test(token.value[0]);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
//# sourceMappingURL=index.js.map
|
//# sourceMappingURL=index.js.map
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "path-to-regexp",
|
"name": "path-to-regexp",
|
||||||
"version": "8.3.0",
|
"version": "8.4.2",
|
||||||
"description": "Express style path to RegExp utility",
|
"description": "Express style path to RegExp utility",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"express",
|
"express",
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
"@types/node": "^22.7.2",
|
"@types/node": "^22.7.2",
|
||||||
"@types/semver": "^7.3.1",
|
"@types/semver": "^7.3.1",
|
||||||
"@vitest/coverage-v8": "^3.0.5",
|
"@vitest/coverage-v8": "^3.0.5",
|
||||||
"recheck": "^4.4.5",
|
"recheck": "^4.5.0",
|
||||||
"size-limit": "^11.1.2",
|
"size-limit": "^11.1.2",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"vitest": "^3.0.5"
|
"vitest": "^3.0.5"
|
||||||
|
|||||||
+27
@@ -1,3 +1,30 @@
|
|||||||
|
## **6.15.3**
|
||||||
|
- [Fix] `parse`: enforce `throwOnLimitExceeded` for cumulative array growth via `combine`/`merge`
|
||||||
|
- [Fix] `utils`: respect encoding of surrogate pairs across chunks (#559)
|
||||||
|
- [Robustness] `parse`: throw the `arrayLimit` error before splitting oversized comma values
|
||||||
|
- [Robustness] `utils.merge` / `utils.assign`: avoid invoking `__proto__` setter when copying own properties
|
||||||
|
- [Robustness] `utils`: enforce `arrayLimit` consistently across `merge`'s array paths
|
||||||
|
- [Perf] `utils`: make `compact` O(n) via a side-channel visited-set instead of `Array.indexOf`
|
||||||
|
- [Deps] update `side-channel`
|
||||||
|
- [Dev Deps] update `eslint`, `mock-property`, `tape`
|
||||||
|
- [Tests] `parse`: characterize current lenient handling of unbalanced bracket keys (#558)
|
||||||
|
|
||||||
|
## **6.15.2**
|
||||||
|
- [Fix] `stringify`: skip null/undefined entries in `arrayFormat: 'comma'` + `encodeValuesOnly` instead of crashing in `encoder`
|
||||||
|
- [Fix] `stringify`: use configured `delimiter` after `charsetSentinel` (#555)
|
||||||
|
- [Fix] `stringify`: apply `formatter` to encoded key under `strictNullHandling` (#554)
|
||||||
|
- [Fix] `stringify`: skip null/undefined filter-array entries instead of crashing in `encoder` (#551)
|
||||||
|
- [Fix] `parse`: handle nested bracket groups and add regression tests (#530)
|
||||||
|
- [readme] fix grammar (#550)
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`
|
||||||
|
- [Tests] add regression tests for keys containing percent-encoded bracket text
|
||||||
|
|
||||||
|
## **6.15.1**
|
||||||
|
- [Fix] `parse`: `parameterLimit: Infinity` with `throwOnLimitExceeded: true` silently drops all parameters
|
||||||
|
- [Deps] update `@ljharb/eslint-config`
|
||||||
|
- [Dev Deps] update `@ljharb/eslint-config`, `iconv-lite`
|
||||||
|
- [Tests] increase coverage
|
||||||
|
|
||||||
## **6.15.0**
|
## **6.15.0**
|
||||||
- [New] `parse`: add `strictMerge` option to wrap object/primitive conflicts in an array (#425, #122)
|
- [New] `parse`: add `strictMerge` option to wrap object/primitive conflicts in an array (#425, #122)
|
||||||
- [Fix] `duplicates` option should not apply to bracket notation keys (#514)
|
- [Fix] `duplicates` option should not apply to bracket notation keys (#514)
|
||||||
|
|||||||
+1
-1
@@ -183,7 +183,7 @@ var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decod
|
|||||||
assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
|
assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
|
||||||
```
|
```
|
||||||
|
|
||||||
Option `allowEmptyArrays` can be used to allowing empty array values in object
|
Option `allowEmptyArrays` can be used to allow empty array values in an object
|
||||||
```javascript
|
```javascript
|
||||||
var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
|
var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
|
||||||
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
|
assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
|
||||||
|
|||||||
+33
-33
File diff suppressed because one or more lines are too long
+1
@@ -11,6 +11,7 @@ export default [
|
|||||||
rules: {
|
rules: {
|
||||||
complexity: 'off',
|
complexity: 'off',
|
||||||
'consistent-return': 'warn',
|
'consistent-return': 'warn',
|
||||||
|
eqeqeq: ['error', 'allow-null'],
|
||||||
'func-name-matching': 'off',
|
'func-name-matching': 'off',
|
||||||
'id-length': [
|
'id-length': [
|
||||||
'error',
|
'error',
|
||||||
|
|||||||
+73
-32
@@ -36,8 +36,19 @@ var interpretNumericEntities = function (str) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseArrayValue = function (val, options, currentArrayLength) {
|
var parseArrayValue = function (val, options, currentArrayLength, isFlatArrayValue) {
|
||||||
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
|
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
|
||||||
|
if (isFlatArrayValue && options.throwOnLimitExceeded) {
|
||||||
|
var commaCount = 0;
|
||||||
|
var commaIndex = val.indexOf(',');
|
||||||
|
while (commaIndex > -1) {
|
||||||
|
commaCount += 1;
|
||||||
|
if (commaCount >= options.arrayLimit) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
|
commaIndex = val.indexOf(',', commaIndex + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
return val.split(',');
|
return val.split(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +78,10 @@ var parseValues = function parseQueryStringValues(str, options) {
|
|||||||
var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
|
var limit = options.parameterLimit === Infinity ? void undefined : options.parameterLimit;
|
||||||
var parts = cleanStr.split(
|
var parts = cleanStr.split(
|
||||||
options.delimiter,
|
options.delimiter,
|
||||||
options.throwOnLimitExceeded ? limit + 1 : limit
|
options.throwOnLimitExceeded && typeof limit !== 'undefined' ? limit + 1 : limit
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.throwOnLimitExceeded && parts.length > limit) {
|
if (options.throwOnLimitExceeded && typeof limit !== 'undefined' && parts.length > limit) {
|
||||||
throw new RangeError('Parameter limit exceeded. Only ' + limit + ' parameter' + (limit === 1 ? '' : 's') + ' allowed.');
|
throw new RangeError('Parameter limit exceeded. Only ' + limit + ' parameter' + (limit === 1 ? '' : 's') + ' allowed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +125,8 @@ var parseValues = function parseQueryStringValues(str, options) {
|
|||||||
parseArrayValue(
|
parseArrayValue(
|
||||||
part.slice(pos + 1),
|
part.slice(pos + 1),
|
||||||
options,
|
options,
|
||||||
isArray(obj[key]) ? obj[key].length : 0
|
isArray(obj[key]) ? obj[key].length : 0,
|
||||||
|
part.indexOf('[]=') === -1
|
||||||
),
|
),
|
||||||
function (encodedVal) {
|
function (encodedVal) {
|
||||||
return options.decoder(encodedVal, defaults.decoder, charset, 'value');
|
return options.decoder(encodedVal, defaults.decoder, charset, 'value');
|
||||||
@@ -132,10 +144,7 @@ var parseValues = function parseQueryStringValues(str, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.comma && isArray(val) && val.length > options.arrayLimit) {
|
if (options.comma && isArray(val) && val.length > options.arrayLimit) {
|
||||||
if (options.throwOnLimitExceeded) {
|
val = utils.combine([], val, options.arrayLimit, options.plainObjects, options.throwOnLimitExceeded);
|
||||||
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
|
||||||
}
|
|
||||||
val = utils.combine([], val, options.arrayLimit, options.plainObjects);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key !== null) {
|
if (key !== null) {
|
||||||
@@ -145,7 +154,8 @@ var parseValues = function parseQueryStringValues(str, options) {
|
|||||||
obj[key],
|
obj[key],
|
||||||
val,
|
val,
|
||||||
options.arrayLimit,
|
options.arrayLimit,
|
||||||
options.plainObjects
|
options.plainObjects,
|
||||||
|
options.throwOnLimitExceeded
|
||||||
);
|
);
|
||||||
} else if (!existing || options.duplicates === 'last') {
|
} else if (!existing || options.duplicates === 'last') {
|
||||||
obj[key] = val;
|
obj[key] = val;
|
||||||
@@ -180,7 +190,8 @@ var parseObject = function (chain, val, options, valuesParsed) {
|
|||||||
[],
|
[],
|
||||||
leaf,
|
leaf,
|
||||||
options.arrayLimit,
|
options.arrayLimit,
|
||||||
options.plainObjects
|
options.plainObjects,
|
||||||
|
options.throwOnLimitExceeded
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -214,9 +225,12 @@ var parseObject = function (chain, val, options, valuesParsed) {
|
|||||||
return leaf;
|
return leaf;
|
||||||
};
|
};
|
||||||
|
|
||||||
var splitKeyIntoSegments = function splitKeyIntoSegments(givenKey, options) {
|
// Split a key like "a[b][c[]]" into ['a', '[b]', '[c[]]'] while preserving
|
||||||
var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
|
// qs parse semantics for depth/prototype guards.
|
||||||
|
var splitKeyIntoSegments = function splitKeyIntoSegments(originalKey, options) {
|
||||||
|
var key = options.allowDots ? originalKey.replace(/\.([^.[]+)/g, '[$1]') : originalKey;
|
||||||
|
|
||||||
|
// depth <= 0 keeps the whole key as one segment
|
||||||
if (options.depth <= 0) {
|
if (options.depth <= 0) {
|
||||||
if (!options.plainObjects && has.call(Object.prototype, key)) {
|
if (!options.plainObjects && has.call(Object.prototype, key)) {
|
||||||
if (!options.allowPrototypes) {
|
if (!options.allowPrototypes) {
|
||||||
@@ -227,14 +241,11 @@ var splitKeyIntoSegments = function splitKeyIntoSegments(givenKey, options) {
|
|||||||
return [key];
|
return [key];
|
||||||
}
|
}
|
||||||
|
|
||||||
var brackets = /(\[[^[\]]*])/;
|
var segments = [];
|
||||||
var child = /(\[[^[\]]*])/g;
|
|
||||||
|
|
||||||
var segment = brackets.exec(key);
|
|
||||||
var parent = segment ? key.slice(0, segment.index) : key;
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
|
|
||||||
|
// parent before the first '[' (may be empty if key starts with '[')
|
||||||
|
var first = key.indexOf('[');
|
||||||
|
var parent = first >= 0 ? key.slice(0, first) : key;
|
||||||
if (parent) {
|
if (parent) {
|
||||||
if (!options.plainObjects && has.call(Object.prototype, parent)) {
|
if (!options.plainObjects && has.call(Object.prototype, parent)) {
|
||||||
if (!options.allowPrototypes) {
|
if (!options.allowPrototypes) {
|
||||||
@@ -242,32 +253,62 @@ var splitKeyIntoSegments = function splitKeyIntoSegments(givenKey, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
keys[keys.length] = parent;
|
segments[segments.length] = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = 0;
|
var n = key.length;
|
||||||
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
var open = first;
|
||||||
i += 1;
|
var collected = 0;
|
||||||
|
|
||||||
var segmentContent = segment[1].slice(1, -1);
|
while (open >= 0 && collected < options.depth) {
|
||||||
if (!options.plainObjects && has.call(Object.prototype, segmentContent)) {
|
var level = 1;
|
||||||
if (!options.allowPrototypes) {
|
var i = open + 1;
|
||||||
|
var close = -1;
|
||||||
|
|
||||||
|
// balance nested '[' and ']' inside this bracket group using a nesting level counter
|
||||||
|
while (i < n && close < 0) {
|
||||||
|
var cu = key.charCodeAt(i);
|
||||||
|
if (cu === 0x5B) { // '['
|
||||||
|
level += 1;
|
||||||
|
} else if (cu === 0x5D) { // ']'
|
||||||
|
level -= 1;
|
||||||
|
if (level === 0) {
|
||||||
|
close = i; // found matching close; loop will exit by condition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (close < 0) {
|
||||||
|
// Unterminated group: wrap the raw remainder in one bracket pair so it stays
|
||||||
|
// a single literal segment (e.g. "[[]b" -> "[[]b]"); we do not infer missing ']'.
|
||||||
|
segments[segments.length] = '[' + key.slice(open) + ']';
|
||||||
|
return segments;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seg = key.slice(open, close + 1);
|
||||||
|
// prototype guard for the content of this group
|
||||||
|
var content = seg.slice(1, -1);
|
||||||
|
if (!options.plainObjects && has.call(Object.prototype, content) && !options.allowPrototypes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
segments[segments.length] = seg;
|
||||||
|
collected += 1;
|
||||||
|
|
||||||
|
// find the next '[' after this balanced group
|
||||||
|
open = key.indexOf('[', close + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
keys[keys.length] = segment[1];
|
if (open >= 0) {
|
||||||
}
|
|
||||||
|
|
||||||
if (segment) {
|
|
||||||
if (options.strictDepth === true) {
|
if (options.strictDepth === true) {
|
||||||
throw new RangeError('Input depth exceeded depth option of ' + options.depth + ' and strictDepth is true');
|
throw new RangeError('Input depth exceeded depth option of ' + options.depth + ' and strictDepth is true');
|
||||||
}
|
}
|
||||||
|
|
||||||
keys[keys.length] = '[' + key.slice(segment.index) + ']';
|
segments[segments.length] = '[' + key.slice(open) + ']';
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys;
|
return segments;
|
||||||
};
|
};
|
||||||
|
|
||||||
var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
|
var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
|
||||||
|
|||||||
+11
-4
@@ -118,7 +118,7 @@ var stringify = function stringify(
|
|||||||
|
|
||||||
if (obj === null) {
|
if (obj === null) {
|
||||||
if (strictNullHandling) {
|
if (strictNullHandling) {
|
||||||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
|
return formatter(encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
obj = '';
|
obj = '';
|
||||||
@@ -142,7 +142,9 @@ var stringify = function stringify(
|
|||||||
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
||||||
// we need to join elements in
|
// we need to join elements in
|
||||||
if (encodeValuesOnly && encoder) {
|
if (encodeValuesOnly && encoder) {
|
||||||
obj = utils.maybeMap(obj, encoder);
|
obj = utils.maybeMap(obj, function (v) {
|
||||||
|
return v == null ? v : encoder(v);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
||||||
} else if (isArray(filter)) {
|
} else if (isArray(filter)) {
|
||||||
@@ -312,6 +314,11 @@ module.exports = function (object, opts) {
|
|||||||
var sideChannel = getSideChannel();
|
var sideChannel = getSideChannel();
|
||||||
for (var i = 0; i < objKeys.length; ++i) {
|
for (var i = 0; i < objKeys.length; ++i) {
|
||||||
var key = objKeys[i];
|
var key = objKeys[i];
|
||||||
|
|
||||||
|
if (typeof key === 'undefined' || key === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
var value = obj[key];
|
var value = obj[key];
|
||||||
|
|
||||||
if (options.skipNulls && value === null) {
|
if (options.skipNulls && value === null) {
|
||||||
@@ -345,10 +352,10 @@ module.exports = function (object, opts) {
|
|||||||
if (options.charsetSentinel) {
|
if (options.charsetSentinel) {
|
||||||
if (options.charset === 'iso-8859-1') {
|
if (options.charset === 'iso-8859-1') {
|
||||||
// encodeURIComponent('✓'), the "numeric entity" representation of a checkmark
|
// encodeURIComponent('✓'), the "numeric entity" representation of a checkmark
|
||||||
prefix += 'utf8=%26%2310003%3B&';
|
prefix += 'utf8=%26%2310003%3B' + options.delimiter;
|
||||||
} else {
|
} else {
|
||||||
// encodeURIComponent('✓')
|
// encodeURIComponent('✓')
|
||||||
prefix += 'utf8=%E2%9C%93&';
|
prefix += 'utf8=%E2%9C%93' + options.delimiter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+47
-8
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
var formats = require('./formats');
|
var formats = require('./formats');
|
||||||
var getSideChannel = require('side-channel');
|
var getSideChannel = require('side-channel');
|
||||||
|
var defineProperty = require('es-define-property');
|
||||||
|
|
||||||
var has = Object.prototype.hasOwnProperty;
|
var has = Object.prototype.hasOwnProperty;
|
||||||
var isArray = Array.isArray;
|
var isArray = Array.isArray;
|
||||||
@@ -66,6 +67,19 @@ var arrayToObject = function arrayToObject(source, options) {
|
|||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var setProperty = function setProperty(obj, key, value) {
|
||||||
|
if (key === '__proto__' && defineProperty) {
|
||||||
|
defineProperty(obj, key, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
value: value,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
obj[key] = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var merge = function merge(target, source, options) {
|
var merge = function merge(target, source, options) {
|
||||||
/* eslint no-param-reassign: 0 */
|
/* eslint no-param-reassign: 0 */
|
||||||
if (!source) {
|
if (!source) {
|
||||||
@@ -75,7 +89,10 @@ var merge = function merge(target, source, options) {
|
|||||||
if (typeof source !== 'object' && typeof source !== 'function') {
|
if (typeof source !== 'object' && typeof source !== 'function') {
|
||||||
if (isArray(target)) {
|
if (isArray(target)) {
|
||||||
var nextIndex = target.length;
|
var nextIndex = target.length;
|
||||||
if (options && typeof options.arrayLimit === 'number' && nextIndex > options.arrayLimit) {
|
if (options && typeof options.arrayLimit === 'number' && nextIndex >= options.arrayLimit) {
|
||||||
|
if (options.throwOnLimitExceeded) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
return markOverflow(arrayToObject(target.concat(source), options), nextIndex);
|
return markOverflow(arrayToObject(target.concat(source), options), nextIndex);
|
||||||
}
|
}
|
||||||
target[nextIndex] = source;
|
target[nextIndex] = source;
|
||||||
@@ -115,6 +132,9 @@ var merge = function merge(target, source, options) {
|
|||||||
}
|
}
|
||||||
var combined = [target].concat(source);
|
var combined = [target].concat(source);
|
||||||
if (options && typeof options.arrayLimit === 'number' && combined.length > options.arrayLimit) {
|
if (options && typeof options.arrayLimit === 'number' && combined.length > options.arrayLimit) {
|
||||||
|
if (options.throwOnLimitExceeded) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
return markOverflow(arrayToObject(combined, options), combined.length - 1);
|
return markOverflow(arrayToObject(combined, options), combined.length - 1);
|
||||||
}
|
}
|
||||||
return combined;
|
return combined;
|
||||||
@@ -138,6 +158,12 @@ var merge = function merge(target, source, options) {
|
|||||||
target[i] = item;
|
target[i] = item;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (options && typeof options.arrayLimit === 'number' && target.length > options.arrayLimit) {
|
||||||
|
if (options.throwOnLimitExceeded) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
|
return markOverflow(arrayToObject(target, options), target.length - 1);
|
||||||
|
}
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,9 +171,9 @@ var merge = function merge(target, source, options) {
|
|||||||
var value = source[key];
|
var value = source[key];
|
||||||
|
|
||||||
if (has.call(acc, key)) {
|
if (has.call(acc, key)) {
|
||||||
acc[key] = merge(acc[key], value, options);
|
setProperty(acc, key, merge(acc[key], value, options));
|
||||||
} else {
|
} else {
|
||||||
acc[key] = value;
|
setProperty(acc, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOverflow(source) && !isOverflow(acc)) {
|
if (isOverflow(source) && !isOverflow(acc)) {
|
||||||
@@ -166,7 +192,7 @@ var merge = function merge(target, source, options) {
|
|||||||
|
|
||||||
var assign = function assignSingleSource(target, source) {
|
var assign = function assignSingleSource(target, source) {
|
||||||
return Object.keys(source).reduce(function (acc, key) {
|
return Object.keys(source).reduce(function (acc, key) {
|
||||||
acc[key] = source[key];
|
setProperty(acc, key, source[key]);
|
||||||
return acc;
|
return acc;
|
||||||
}, target);
|
}, target);
|
||||||
};
|
};
|
||||||
@@ -212,6 +238,13 @@ var encode = function encode(str, defaultEncoder, charset, kind, format) {
|
|||||||
var out = '';
|
var out = '';
|
||||||
for (var j = 0; j < string.length; j += limit) {
|
for (var j = 0; j < string.length; j += limit) {
|
||||||
var segment = string.length >= limit ? string.slice(j, j + limit) : string;
|
var segment = string.length >= limit ? string.slice(j, j + limit) : string;
|
||||||
|
if (j + limit < string.length) {
|
||||||
|
var last = segment.charCodeAt(segment.length - 1);
|
||||||
|
if (last >= 0xD800 && last <= 0xDBFF) {
|
||||||
|
segment = segment.slice(0, -1);
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
var arr = [];
|
var arr = [];
|
||||||
|
|
||||||
for (var i = 0; i < segment.length; ++i) {
|
for (var i = 0; i < segment.length; ++i) {
|
||||||
@@ -265,7 +298,7 @@ var encode = function encode(str, defaultEncoder, charset, kind, format) {
|
|||||||
|
|
||||||
var compact = function compact(value) {
|
var compact = function compact(value) {
|
||||||
var queue = [{ obj: { o: value }, prop: 'o' }];
|
var queue = [{ obj: { o: value }, prop: 'o' }];
|
||||||
var refs = [];
|
var refs = getSideChannel();
|
||||||
|
|
||||||
for (var i = 0; i < queue.length; ++i) {
|
for (var i = 0; i < queue.length; ++i) {
|
||||||
var item = queue[i];
|
var item = queue[i];
|
||||||
@@ -275,9 +308,9 @@ var compact = function compact(value) {
|
|||||||
for (var j = 0; j < keys.length; ++j) {
|
for (var j = 0; j < keys.length; ++j) {
|
||||||
var key = keys[j];
|
var key = keys[j];
|
||||||
var val = obj[key];
|
var val = obj[key];
|
||||||
if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
|
if (typeof val === 'object' && val !== null && !refs.has(val)) {
|
||||||
queue[queue.length] = { obj: obj, prop: key };
|
queue[queue.length] = { obj: obj, prop: key };
|
||||||
refs[refs.length] = val;
|
refs.set(val, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -299,9 +332,12 @@ var isBuffer = function isBuffer(obj) {
|
|||||||
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
|
return !!(obj.constructor && obj.constructor.isBuffer && obj.constructor.isBuffer(obj));
|
||||||
};
|
};
|
||||||
|
|
||||||
var combine = function combine(a, b, arrayLimit, plainObjects) {
|
var combine = function combine(a, b, arrayLimit, plainObjects, throwOnLimitExceeded) {
|
||||||
// If 'a' is already an overflow object, add to it
|
// If 'a' is already an overflow object, add to it
|
||||||
if (isOverflow(a)) {
|
if (isOverflow(a)) {
|
||||||
|
if (throwOnLimitExceeded) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + arrayLimit + ' element' + (arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
var newIndex = getMaxIndex(a) + 1;
|
var newIndex = getMaxIndex(a) + 1;
|
||||||
a[newIndex] = b;
|
a[newIndex] = b;
|
||||||
setMaxIndex(a, newIndex);
|
setMaxIndex(a, newIndex);
|
||||||
@@ -310,6 +346,9 @@ var combine = function combine(a, b, arrayLimit, plainObjects) {
|
|||||||
|
|
||||||
var result = [].concat(a, b);
|
var result = [].concat(a, b);
|
||||||
if (result.length > arrayLimit) {
|
if (result.length > arrayLimit) {
|
||||||
|
if (throwOnLimitExceeded) {
|
||||||
|
throw new RangeError('Array limit exceeded. Only ' + arrayLimit + ' element' + (arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
|
||||||
|
}
|
||||||
return markOverflow(arrayToObject(result, { plainObjects: plainObjects }), result.length - 1);
|
return markOverflow(arrayToObject(result, { plainObjects: plainObjects }), result.length - 1);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+8
-7
@@ -2,7 +2,7 @@
|
|||||||
"name": "qs",
|
"name": "qs",
|
||||||
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
||||||
"homepage": "https://github.com/ljharb/qs",
|
"homepage": "https://github.com/ljharb/qs",
|
||||||
"version": "6.15.0",
|
"version": "6.15.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ljharb/qs.git"
|
"url": "https://github.com/ljharb/qs.git"
|
||||||
@@ -31,18 +31,19 @@
|
|||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.1.0"
|
"es-define-property": "^1.0.1",
|
||||||
|
"side-channel": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@browserify/envify": "^6.0.0",
|
"@browserify/envify": "^6.0.0",
|
||||||
"@browserify/uglifyify": "^6.0.0",
|
"@browserify/uglifyify": "^6.0.0",
|
||||||
"@ljharb/eslint-config": "^22.1.3",
|
"@ljharb/eslint-config": "^22.2.3",
|
||||||
"browserify": "^16.5.2",
|
"browserify": "^16.5.2",
|
||||||
"bundle-collapser": "^1.4.0",
|
"bundle-collapser": "^1.4.0",
|
||||||
"common-shakeify": "~1.0.0",
|
"common-shakeify": "~1.0.0",
|
||||||
"eclint": "^2.8.1",
|
"eclint": "^2.8.1",
|
||||||
"es-value-fixtures": "^1.7.1",
|
"es-value-fixtures": "^1.7.1",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^10.5.0",
|
||||||
"evalmd": "^0.0.19",
|
"evalmd": "^0.0.19",
|
||||||
"for-each": "^0.3.5",
|
"for-each": "^0.3.5",
|
||||||
"glob": "=10.3.7",
|
"glob": "=10.3.7",
|
||||||
@@ -51,12 +52,12 @@
|
|||||||
"has-property-descriptors": "^1.0.2",
|
"has-property-descriptors": "^1.0.2",
|
||||||
"has-proto": "^1.2.0",
|
"has-proto": "^1.2.0",
|
||||||
"has-symbols": "^1.1.0",
|
"has-symbols": "^1.1.0",
|
||||||
"iconv-lite": "^0.5.1",
|
"iconv-lite": "^0.5.2",
|
||||||
"in-publish": "^2.0.1",
|
"in-publish": "^2.0.1",
|
||||||
"jackspeak": "=2.1.1",
|
"jackspeak": "=2.1.1",
|
||||||
"jiti": "^0.0.0",
|
"jiti": "^0.0.0",
|
||||||
"mkdirp": "^0.5.5",
|
"mkdirp": "^0.5.5",
|
||||||
"mock-property": "^1.1.0",
|
"mock-property": "^1.1.2",
|
||||||
"module-deps": "^6.2.3",
|
"module-deps": "^6.2.3",
|
||||||
"npmignore": "^0.3.5",
|
"npmignore": "^0.3.5",
|
||||||
"nyc": "^10.3.2",
|
"nyc": "^10.3.2",
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
"qs-iconv": "^1.0.4",
|
"qs-iconv": "^1.0.4",
|
||||||
"safe-publish-latest": "^2.0.0",
|
"safe-publish-latest": "^2.0.0",
|
||||||
"safer-buffer": "^2.1.2",
|
"safer-buffer": "^2.1.2",
|
||||||
"tape": "^5.9.0",
|
"tape": "^5.10.2",
|
||||||
"unassertify": "^3.0.1"
|
"unassertify": "^3.0.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+365
@@ -14,6 +14,12 @@ var hasProto = require('has-proto')();
|
|||||||
var qs = require('../');
|
var qs = require('../');
|
||||||
var utils = require('../lib/utils');
|
var utils = require('../lib/utils');
|
||||||
|
|
||||||
|
var characterizeParse = function characterizeParse(st, input, opts, expected, label) {
|
||||||
|
var result;
|
||||||
|
st.doesNotThrow(function () { result = qs.parse(input, opts); }, label + ': does not throw');
|
||||||
|
st.deepEqual(result, expected, label + ': parses to the current lenient output');
|
||||||
|
};
|
||||||
|
|
||||||
test('parse()', function (t) {
|
test('parse()', function (t) {
|
||||||
t.test('parses a simple string', function (st) {
|
t.test('parses a simple string', function (st) {
|
||||||
st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
|
st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
|
||||||
@@ -210,6 +216,21 @@ test('parse()', function (t) {
|
|||||||
t.test('uses original key when depth = 0', function (st) {
|
t.test('uses original key when depth = 0', function (st) {
|
||||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: 0 }), { 'a[0]': 'b', 'a[1]': 'c' });
|
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: 0 }), { 'a[0]': 'b', 'a[1]': 'c' });
|
||||||
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: 0 }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
|
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: 0 }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
|
||||||
|
st.deepEqual(qs.parse('a.b=c', { depth: 0, allowDots: true }), { 'a[b]': 'c' }, 'normalizes dots before applying depth-0 behavior');
|
||||||
|
st.deepEqual(qs.parse('toString=foo', { depth: 0 }), {}, 'respects prototype guard at depth 0');
|
||||||
|
st.deepEqual(qs.parse('toString=foo', { depth: 0, allowPrototypes: true }), { toString: 'foo' }, 'allows prototypes at depth 0 when enabled');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('ignores prototype keys when depth = 0 and allowPrototypes is false', function (st) {
|
||||||
|
st.deepEqual(qs.parse('toString=foo', { depth: 0 }), {});
|
||||||
|
st.deepEqual(qs.parse('hasOwnProperty=bar', { depth: 0 }), {});
|
||||||
|
st.deepEqual(qs.parse('toString=foo&a=b', { depth: 0 }), { a: 'b' });
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('allows prototype keys when depth = 0 and allowPrototypes is true', function (st) {
|
||||||
|
st.deepEqual(qs.parse('toString=foo', { depth: 0, allowPrototypes: true }), { toString: 'foo' });
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -251,6 +272,94 @@ test('parse()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('parses keys with literal [] inside a bracket group (#493)', function (st) {
|
||||||
|
// A bracket pair inside a bracket group should be treated literally as part of the key
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('search[withbracket[]]=foobar'),
|
||||||
|
{ search: { 'withbracket[]': 'foobar' } },
|
||||||
|
'treats inner [] literally when inside a bracket group'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Single-level variant
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('a[b[]]=c'),
|
||||||
|
{ a: { 'b[]': 'c' } },
|
||||||
|
'keeps "b[]" as a literal key'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Nested with an array push on the outer level
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('list[][x[]]=y'),
|
||||||
|
{ list: [{ 'x[]': 'y' }] },
|
||||||
|
'preserves inner [] while still treating outer [] as array push'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Multiple nested bracket pairs: inner [] remains literal as part of the key
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('a[b[c[]]]=d'),
|
||||||
|
{ a: { 'b[c[]]': 'd' } },
|
||||||
|
'treats "b[c[]]" as a literal key inside the bracket group'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Depth limits with literal brackets: preserve inner [] while limiting bracket-group parsing
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('a[b[c[]]][d]=e', { depth: 1 }),
|
||||||
|
{ a: { 'b[c[]]': { '[d]': 'e' } } },
|
||||||
|
'respects depth: 1 and preserves literal inner [] in the parsed key'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Unterminated inner bracket group is wrapped as a literal remainder segment
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse('a[[]b=c'),
|
||||||
|
{ a: { '[[]b': 'c' } },
|
||||||
|
'handles unterminated inner bracket groups without throwing'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('currently parses unbalanced bracket keys after a parent leniently to literal segments (issue #558)', function (st) {
|
||||||
|
characterizeParse(st, 'a[bc=v', undefined, { a: { '[bc': 'v' } }, 'unclosed group after a parent');
|
||||||
|
characterizeParse(st, 'a[=v', undefined, { a: { '[': 'v' } }, 'bare unclosed bracket after a parent');
|
||||||
|
characterizeParse(st, 'a[b][c=v', undefined, { a: { b: { '[c': 'v' } } }, 'unclosed group after a valid one');
|
||||||
|
characterizeParse(st, 'a[b]c[d=v', undefined, { a: { b: { '[d': 'v' } } }, 'unclosed group after text following a valid one');
|
||||||
|
characterizeParse(st, 'filters[customtags:Env: Prod=v', undefined, { filters: { '[customtags:Env: Prod': 'v' } }, 'the issue #558 reproduction');
|
||||||
|
characterizeParse(st, '][a=v', undefined, { ']': { '[a': 'v' } }, 'stray close bracket before an unclosed group');
|
||||||
|
characterizeParse(st, 'a][b=v', undefined, { 'a]': { '[b': 'v' } }, 'stray close bracket inside the parent');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('currently parses unbalanced bracket keys containing inner brackets leniently (issue #558)', function (st) {
|
||||||
|
characterizeParse(st, 'a[b[c=v', undefined, { a: { '[b[c': 'v' } }, 'unclosed group containing an inner bracket');
|
||||||
|
characterizeParse(st, 'a[b[c]=v', undefined, { a: { '[b[c]': 'v' } }, 'unbalanced group with an inner bracket and one close');
|
||||||
|
characterizeParse(st, 'a[b][c[d=v', undefined, { a: { b: { '[c[d': 'v' } } }, 'unclosed inner-bracket group after a valid one');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('currently parses bracket-prefixed unbalanced keys leniently (issue #558)', function (st) {
|
||||||
|
characterizeParse(st, '[abc=v', undefined, { '[abc': 'v' }, 'key starting with an unclosed bracket');
|
||||||
|
characterizeParse(st, '[[]b=v', undefined, { '[[]b': 'v' }, 'key starting with an unbalanced bracket group');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('lenient unbalanced-bracket handling currently depends on the depth option (issue #558)', function (st) {
|
||||||
|
characterizeParse(st, 'a[b]c[d]e[f=v', { depth: 5 }, { a: { b: { d: { '[f': 'v' } } } }, 'consumes groups up to the depth budget then keeps the unclosed remainder literal');
|
||||||
|
characterizeParse(st, 'a[b]c[d]e[f=v', { depth: 1 }, { a: { b: { '[d]e[f': 'v' } } }, 'a lower depth keeps more of the unclosed remainder literal');
|
||||||
|
characterizeParse(st, 'a[bc=v', { depth: 0 }, { 'a[bc': 'v' }, 'depth 0 keeps the entire key literal');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('currently parses an allowDots key with a trailing unclosed bracket leniently (issue #558)', function (st) {
|
||||||
|
characterizeParse(st, 'a.b[c=v', { allowDots: true }, { a: { b: { '[c': 'v' } } }, 'allowDots expands the dot then keeps the unclosed bracket literal');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('valid and stray-close bracket keys are unaffected by unbalanced-bracket handling', function (st) {
|
||||||
|
characterizeParse(st, 'a]b=v', undefined, { 'a]b': 'v' }, 'stray close bracket with no open bracket stays a flat key');
|
||||||
|
characterizeParse(st, 'a[b]extra=v', undefined, { a: { b: 'v' } }, 'text after a balanced group is ignored');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('allows to specify array indices', function (st) {
|
t.test('allows to specify array indices', function (st) {
|
||||||
st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
|
st.deepEqual(qs.parse('a[1]=c&a[0]=b&a[2]=d'), { a: ['b', 'c', 'd'] });
|
||||||
st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
|
st.deepEqual(qs.parse('a[1]=c&a[0]=b'), { a: ['b', 'c'] });
|
||||||
@@ -665,6 +774,21 @@ test('parse()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('does not crash on multi-step circular references', function (st) {
|
||||||
|
var a = {};
|
||||||
|
a.b = { c: { d: a } };
|
||||||
|
|
||||||
|
var parsed;
|
||||||
|
|
||||||
|
st.doesNotThrow(function () {
|
||||||
|
parsed = qs.parse({ foo: a });
|
||||||
|
});
|
||||||
|
|
||||||
|
st.equal('foo' in parsed, true, 'parsed has "foo" property');
|
||||||
|
st.equal(parsed.foo.b.c.d, parsed.foo, 'the multi-step cycle is preserved');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('does not crash when parsing deep objects', function (st) {
|
t.test('does not crash when parsing deep objects', function (st) {
|
||||||
var parsed;
|
var parsed;
|
||||||
var str = 'foo';
|
var str = 'foo';
|
||||||
@@ -898,6 +1022,22 @@ test('parse()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('object-valued input with own `__proto__` does not mutate sub-object [[Prototype]]', function (st) {
|
||||||
|
// JSON.parse creates own data `__proto__` properties (via CreateDataProperty),
|
||||||
|
// which would trigger the Object.prototype.__proto__ accessor if merged via `acc[key] = value`.
|
||||||
|
var out = qs.parse({
|
||||||
|
'user[name]': 'alice',
|
||||||
|
user: JSON.parse('{"__proto__":{"isAdmin":true}}')
|
||||||
|
}, { allowPrototypes: false });
|
||||||
|
|
||||||
|
st.equal(out.user.name, 'alice', 'name from bracket key is preserved');
|
||||||
|
st.equal(out.user.isAdmin, undefined, 'attacker-controlled inherited property is not exposed');
|
||||||
|
st.equal(Object.getPrototypeOf(out.user), Object.prototype, 'sub-object [[Prototype]] is unchanged');
|
||||||
|
st.equal(Object.prototype.isAdmin, undefined, 'Object.prototype is not polluted');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('can return null objects', { skip: !hasProto }, function (st) {
|
t.test('can return null objects', { skip: !hasProto }, function (st) {
|
||||||
var expected = {
|
var expected = {
|
||||||
__proto__: null,
|
__proto__: null,
|
||||||
@@ -1074,6 +1214,15 @@ test('parse()', function (t) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
|
st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
|
||||||
|
|
||||||
|
var noopDecoder = function () { return 'x'; };
|
||||||
|
noopDecoder();
|
||||||
|
st['throws'](
|
||||||
|
function () { decoder('x', noopDecoder, 'utf-8', 'unknown'); },
|
||||||
|
'this should never happen! type: unknown',
|
||||||
|
'decoder throws for unexpected type'
|
||||||
|
);
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1103,6 +1252,14 @@ test('parse()', function (t) {
|
|||||||
new RangeError('Parameter limit exceeded. Only 3 parameters allowed.'),
|
new RangeError('Parameter limit exceeded. Only 3 parameters allowed.'),
|
||||||
'throws error when parameter limit is exceeded'
|
'throws error when parameter limit is exceeded'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=1&b=2', { parameterLimit: 1, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Parameter limit exceeded. Only 1 parameter allowed.'),
|
||||||
|
'throws error with singular "parameter" when parameterLimit is 1'
|
||||||
|
);
|
||||||
sst.end();
|
sst.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1124,6 +1281,12 @@ test('parse()', function (t) {
|
|||||||
sst.end();
|
sst.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
st.test('allows unlimited parameters when parameterLimit is Infinity and throwOnLimitExceeded is true', function (sst) {
|
||||||
|
var result = qs.parse('a=1&b=2&c=3&d=4&e=5&f=6', { parameterLimit: Infinity, throwOnLimitExceeded: true });
|
||||||
|
sst.deepEqual(result, { a: '1', b: '2', c: '3', d: '4', e: '5', f: '6' }, 'parses all parameters without truncation or throwing');
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1189,6 +1352,14 @@ test('parse()', function (t) {
|
|||||||
'throws error when a sparse index exceeds arrayLimit'
|
'throws error when a sparse index exceeds arrayLimit'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a[2]=b', { arrayLimit: 1, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws error with singular "element" when arrayLimit is 1'
|
||||||
|
);
|
||||||
|
|
||||||
sst.end();
|
sst.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1206,6 +1377,168 @@ test('parse()', function (t) {
|
|||||||
sst.end();
|
sst.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
st.test('throws when duplicate bracket keys exceed arrayLimit with throwOnLimitExceeded', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a[]=1&a[]=2&a[]=3&a[]=4&a[]=5&a[]=6', { arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
||||||
|
'throws error when duplicate bracket notation exceeds array limit'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws when cumulative comma + duplicate-key combine exceeds arrayLimit', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=1,2,3&a=4,5,6', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
||||||
|
'throws when comma groups within the limit cumulatively exceed it across duplicate keys'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=v,v,v,v,v&a=v,v,v,v,v&a=v,v,v,v,v', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
||||||
|
'throws on a subsequent part once the cumulative array is already over the limit'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws when plain duplicate keys combine past arrayLimit at the boundary', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a=x&a=y', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'duplicate scalar keys'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a[]=x&a[]=y', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'duplicate bracket keys'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws when mixed index and key notation merge past arrayLimit', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a=x&a[0]=y', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'scalar then index that overflows on merge'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a[0]=1&a[1]=2&a=3', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'indexed array then scalar that overflows on merge'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('enforces arrayLimit on merge at the boundary, consistently with combine', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a[0]=x&a=y', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'a trailing scalar merged into an at-limit array throws'
|
||||||
|
);
|
||||||
|
sst.deepEqual(
|
||||||
|
qs.parse('a[0]=x&a=y', { arrayLimit: 1 }),
|
||||||
|
{ a: { 0: 'x', 1: 'y' } },
|
||||||
|
'and converts to an overflow object without throwOnLimitExceeded'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () { qs.parse('a[0]=x&a[]=y', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'mixed index and bracket notation merged past the limit throws'
|
||||||
|
);
|
||||||
|
sst.deepEqual(
|
||||||
|
qs.parse('a[0]=x&a[]=y', { arrayLimit: 1 }),
|
||||||
|
{ a: { 0: 'x', 1: 'y' } },
|
||||||
|
'mixed index and bracket notation converts like duplicate-bracket combine'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('does not throw when cumulative comma combine stays within arrayLimit', function (sst) {
|
||||||
|
var result = qs.parse('a=1,2,3&a=4', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
sst.deepEqual(result, { a: ['1', '2', '3', '4'] }, 'combined array within limit is preserved');
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('silently combines to an overflow object when throwOnLimitExceeded is not set', function (sst) {
|
||||||
|
var result = qs.parse('a=1,2,3&a=4,5,6', { comma: true, arrayLimit: 5 });
|
||||||
|
sst.deepEqual(result, { a: { 0: '1', 1: '2', 2: '3', 3: '4', 4: '5', 5: '6' } }, 'converts to object without throwing');
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('does not throw for comma groups nested under bracket notation, counting each group as one element', function (sst) {
|
||||||
|
var result = qs.parse('a[]=1,2,3&a[]=4,5,6', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
sst.deepEqual(result, { a: [['1', '2', '3'], ['4', '5', '6']] }, 'nested comma groups count as one element each');
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws before splitting when a single comma value exceeds arrayLimit', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=1,2,3,4,5,6', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
||||||
|
'a flat comma value over the limit throws'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=1,2', { comma: true, arrayLimit: 1, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'singular message at arrayLimit 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a[b]=1,2,3,4,5,6', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 5 elements allowed in an array.'),
|
||||||
|
'a non-bracket nested key comma value over the limit throws'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('does not throw for a single comma value within arrayLimit', function (sst) {
|
||||||
|
sst.deepEqual(
|
||||||
|
qs.parse('a=1,2,3', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true }),
|
||||||
|
{ a: ['1', '2', '3'] },
|
||||||
|
'within the limit'
|
||||||
|
);
|
||||||
|
sst.deepEqual(
|
||||||
|
qs.parse('a=1,2,3,4,5', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true }),
|
||||||
|
{ a: ['1', '2', '3', '4', '5'] },
|
||||||
|
'exactly at the limit'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('does not throw for a bracketed comma group within arrayLimit', function (sst) {
|
||||||
|
var result = qs.parse('a[]=1,2,3,4,5,6', { comma: true, arrayLimit: 5, throwOnLimitExceeded: true });
|
||||||
|
sst.deepEqual(result, { a: [['1', '2', '3', '4', '5', '6']] }, 'a bracketed comma group is a single element');
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws for a bracketed comma group when arrayLimit is 0', function (sst) {
|
||||||
|
sst['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a[]=1,2,3', { comma: true, arrayLimit: 0, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 0 elements allowed in an array.'),
|
||||||
|
'a single bracketed element still exceeds arrayLimit 0'
|
||||||
|
);
|
||||||
|
sst.end();
|
||||||
|
});
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1462,6 +1795,14 @@ test('comma + arrayLimit', function (t) {
|
|||||||
new RangeError('Array limit exceeded. Only 3 elements allowed in an array.'),
|
new RangeError('Array limit exceeded. Only 3 elements allowed in an array.'),
|
||||||
'throws error when comma-split exceeds array limit'
|
'throws error when comma-split exceeds array limit'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
st['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse('a=1,2,3', { comma: true, arrayLimit: 1, throwOnLimitExceeded: true });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws error with singular "element" when arrayLimit is 1'
|
||||||
|
);
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1564,5 +1905,29 @@ test('mixed array and object notation', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('uses existing array length for currentArrayLength when parsing object input with bracket keys', function (st) {
|
||||||
|
var input = {};
|
||||||
|
var arr = ['x', 'y'];
|
||||||
|
arr.a = ['z', 'w'];
|
||||||
|
input['a[]'] = arr;
|
||||||
|
st.deepEqual(qs.parse(input), { a: ['x', 'y'] }, 'parses object input with bracket keys using existing array values');
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('throws with singular message when object input bracket key exceeds arrayLimit of 1', function (st) {
|
||||||
|
var input = {};
|
||||||
|
var arr = ['x'];
|
||||||
|
arr.a = ['z', 'w'];
|
||||||
|
input['a[]'] = arr;
|
||||||
|
st['throws'](
|
||||||
|
function () {
|
||||||
|
qs.parse(input, { throwOnLimitExceeded: true, arrayLimit: 1 });
|
||||||
|
},
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws singular error for object input exceeding arrayLimit 1'
|
||||||
|
);
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|||||||
+138
@@ -651,6 +651,49 @@ test('stringify()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('does not crash on null/undefined entries in arrayFormat=comma with encodeValuesOnly', function (st) {
|
||||||
|
st.doesNotThrow(
|
||||||
|
function () { qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
||||||
|
'does not pass a raw null array entry to the encoder'
|
||||||
|
);
|
||||||
|
st.doesNotThrow(
|
||||||
|
function () { qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
||||||
|
'does not pass a raw undefined array entry to the encoder'
|
||||||
|
);
|
||||||
|
st.doesNotThrow(
|
||||||
|
function () { qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }); },
|
||||||
|
'does not crash on a single-null array'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: [null, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
||||||
|
'a=,b',
|
||||||
|
'null entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: [undefined, 'b'] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
||||||
|
'a=,b',
|
||||||
|
'undefined entry joins as empty, comma stays unencoded under encodeValuesOnly'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true }),
|
||||||
|
'a=',
|
||||||
|
'single-null array stringifies as empty value'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, strictNullHandling: true }),
|
||||||
|
'a',
|
||||||
|
'strictNullHandling drops the equals sign for a single-null array'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: [null] }, { arrayFormat: 'comma', encodeValuesOnly: true, skipNulls: true }),
|
||||||
|
'',
|
||||||
|
'skipNulls drops a single-null array entirely'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('stringifies a null object', { skip: !hasProto }, function (st) {
|
t.test('stringifies a null object', { skip: !hasProto }, function (st) {
|
||||||
st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b');
|
st.equal(qs.stringify({ __proto__: null, a: 'b' }), 'a=b');
|
||||||
st.end();
|
st.end();
|
||||||
@@ -825,6 +868,35 @@ test('stringify()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('skips null/undefined entries in filter=array', function (st) {
|
||||||
|
st.doesNotThrow(
|
||||||
|
function () { qs.stringify({ a: 'b', undefined: 'x' }, { filter: ['a', undefined] }); },
|
||||||
|
'does not pass a raw undefined filter entry to the encoder'
|
||||||
|
);
|
||||||
|
st.doesNotThrow(
|
||||||
|
function () { qs.stringify({ a: 'b', 'null': 'x' }, { filter: ['a', null] }); },
|
||||||
|
'does not pass a raw null filter entry to the encoder'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: 'b', undefined: 'x', c: 'd' }, { filter: ['a', undefined, 'c'] }),
|
||||||
|
'a=b&c=d',
|
||||||
|
'undefined filter entry is skipped, remaining keys are kept'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: 'b', 'null': 'x', c: 'd' }, { filter: ['a', null, 'c'] }),
|
||||||
|
'a=b&c=d',
|
||||||
|
'null filter entry is skipped, remaining keys are kept'
|
||||||
|
);
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: 'b', 'null': 'x' }, { filter: [null] }),
|
||||||
|
'',
|
||||||
|
'filter array containing only null yields empty string'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('supports custom representations when filter=function', function (st) {
|
t.test('supports custom representations when filter=function', function (st) {
|
||||||
var calls = 0;
|
var calls = 0;
|
||||||
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
||||||
@@ -1111,6 +1183,28 @@ test('stringify()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('strictNullHandling: applies the formatter to the encoded key (RFC1738)', function (st) {
|
||||||
|
st.equal(
|
||||||
|
qs.stringify(
|
||||||
|
{ 'a b': null, 'c d': 'e f' },
|
||||||
|
{ strictNullHandling: false, format: 'RFC1738' }
|
||||||
|
),
|
||||||
|
'a+b=&c+d=e+f',
|
||||||
|
'without: as expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.equal(
|
||||||
|
qs.stringify(
|
||||||
|
{ 'a b': null, 'c d': 'e f' },
|
||||||
|
{ strictNullHandling: true, format: 'RFC1738' }
|
||||||
|
),
|
||||||
|
'a+b&c+d=e+f',
|
||||||
|
'with: as expected'
|
||||||
|
);
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('throws if an invalid charset is specified', function (st) {
|
t.test('throws if an invalid charset is specified', function (st) {
|
||||||
st['throws'](function () {
|
st['throws'](function () {
|
||||||
qs.stringify({ a: 'b' }, { charset: 'foobar' });
|
qs.stringify({ a: 'b' }, { charset: 'foobar' });
|
||||||
@@ -1146,6 +1240,12 @@ test('stringify()', function (t) {
|
|||||||
'adds the right sentinel when instructed to and the charset is iso-8859-1'
|
'adds the right sentinel when instructed to and the charset is iso-8859-1'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
st.equal(
|
||||||
|
qs.stringify({ a: 1, b: 2 }, { charsetSentinel: true, delimiter: ';' }),
|
||||||
|
'utf8=%E2%9C%93;a=1;b=2',
|
||||||
|
'uses the configured delimiter after the sentinel'
|
||||||
|
);
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1188,6 +1288,15 @@ test('stringify()', function (t) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
|
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
|
||||||
|
|
||||||
|
var noopEncoder = function () { return 'x'; };
|
||||||
|
noopEncoder();
|
||||||
|
st['throws'](
|
||||||
|
function () { encoder('x', noopEncoder, 'utf-8', 'unknown'); },
|
||||||
|
'this should never happen! type: unknown',
|
||||||
|
'encoder throws for unexpected type'
|
||||||
|
);
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1307,4 +1416,33 @@ test('stringifies empty keys', function (t) {
|
|||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('round-trips keys containing percent-encoded bracket text', function (st) {
|
||||||
|
var cases = [
|
||||||
|
{ 'a%5Bb': 'c' },
|
||||||
|
{ 'a%5Db': 'c' },
|
||||||
|
{ 'a%255Bb': 'c' },
|
||||||
|
{ 'a%255Db': 'c' },
|
||||||
|
{ a: { 'b%5Bc': 'd' } },
|
||||||
|
{ a: { 'b%255Bc': 'd' } },
|
||||||
|
{ 'a%5B%255Bb': 'c' }
|
||||||
|
];
|
||||||
|
for (var i = 0; i < cases.length; i++) {
|
||||||
|
st.deepEqual(
|
||||||
|
qs.parse(qs.stringify(cases[i])),
|
||||||
|
cases[i],
|
||||||
|
'round-trips ' + JSON.stringify(cases[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('parses input containing percent-encoded bracket text without mangling', function (st) {
|
||||||
|
st.deepEqual(qs.parse('a%25255Bb=c'), { 'a%255Bb': 'c' }, 'a%25255Bb decodes to a%255Bb, not a%5Bb');
|
||||||
|
st.deepEqual(qs.parse('a%25255Db=c'), { 'a%255Db': 'c' }, 'a%25255Db decodes to a%255Db, not a%5Db');
|
||||||
|
st.deepEqual(qs.parse('a%5Bb%25255Bc%5D=d'), { a: { 'b%255Bc': 'd' } }, 'nested %25255B decodes to %255B inside segment, not %5B');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+225
-3
@@ -31,6 +31,7 @@ test('merge()', function (t) {
|
|||||||
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
|
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
|
||||||
|
|
||||||
var func = function f() {};
|
var func = function f() {};
|
||||||
|
func();
|
||||||
t.deepEqual(
|
t.deepEqual(
|
||||||
utils.merge(func, { foo: 'bar' }),
|
utils.merge(func, { foo: 'bar' }),
|
||||||
[func, { foo: 'bar' }],
|
[func, { foo: 'bar' }],
|
||||||
@@ -64,6 +65,7 @@ test('merge()', function (t) {
|
|||||||
observed[0] = observed[0]; // eslint-disable-line no-self-assign
|
observed[0] = observed[0]; // eslint-disable-line no-self-assign
|
||||||
st.equal(setCount, 1);
|
st.equal(setCount, 1);
|
||||||
st.equal(getCount, 2);
|
st.equal(getCount, 2);
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -77,6 +79,7 @@ test('merge()', function (t) {
|
|||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
||||||
var merged = utils.merge(overflow, 'd');
|
var merged = utils.merge(overflow, 'd');
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'adds primitive at next numeric index');
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'adds primitive at next numeric index');
|
||||||
|
|
||||||
s2t.end();
|
s2t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -92,6 +95,7 @@ test('merge()', function (t) {
|
|||||||
var obj = { foo: 'bar' };
|
var obj = { foo: 'bar' };
|
||||||
var merged = utils.merge(obj, 'baz');
|
var merged = utils.merge(obj, 'baz');
|
||||||
s2t.deepEqual(merged, { foo: 'bar', baz: true }, 'adds primitive as key with value true');
|
s2t.deepEqual(merged, { foo: 'bar', baz: true }, 'adds primitive as key with value true');
|
||||||
|
|
||||||
s2t.end();
|
s2t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -109,6 +113,17 @@ test('merge()', function (t) {
|
|||||||
var merged = utils.merge('c', overflow);
|
var merged = utils.merge('c', overflow);
|
||||||
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
||||||
s2t.deepEqual(merged, { 0: 'c', 1: 'a', 2: 'b' }, 'creates object with primitive at 0, source values shifted');
|
s2t.deepEqual(merged, { 0: 'c', 1: 'a', 2: 'b' }, 'creates object with primitive at 0, source values shifted');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('merges overflow object into primitive with plainObjects', function (s2t) {
|
||||||
|
var overflow = utils.combine(['a'], 'b', 0, false);
|
||||||
|
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
||||||
|
var merged = utils.merge('c', overflow, { plainObjects: true });
|
||||||
|
s2t.ok(utils.isOverflow(merged), 'result is also marked as overflow');
|
||||||
|
s2t.deepEqual(merged, { __proto__: null, 0: 'c', 1: 'a', 2: 'b' }, 'creates null-proto object with primitive at 0');
|
||||||
|
|
||||||
s2t.end();
|
s2t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,6 +133,7 @@ test('merge()', function (t) {
|
|||||||
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
s2t.ok(utils.isOverflow(overflow), 'overflow object is marked');
|
||||||
var merged = utils.merge('a', overflow);
|
var merged = utils.merge('a', overflow);
|
||||||
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'shifts all source indices by 1');
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'shifts all source indices by 1');
|
||||||
|
|
||||||
s2t.end();
|
s2t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -125,6 +141,95 @@ test('merge()', function (t) {
|
|||||||
var obj = { foo: 'bar' };
|
var obj = { foo: 'bar' };
|
||||||
var merged = utils.merge('a', obj);
|
var merged = utils.merge('a', obj);
|
||||||
s2t.deepEqual(merged, ['a', { foo: 'bar' }], 'creates array with primitive and object');
|
s2t.deepEqual(merged, ['a', { foo: 'bar' }], 'creates array with primitive and object');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('merges primitive into array that exceeds arrayLimit', function (s2t) {
|
||||||
|
var arr = ['a', 'b', 'c'];
|
||||||
|
var merged = utils.merge(arr, 'd', { arrayLimit: 1 });
|
||||||
|
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
||||||
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c', 3: 'd' }, 'converts to overflow object with primitive appended');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('merges array into primitive that exceeds arrayLimit', function (s2t) {
|
||||||
|
var merged = utils.merge('a', ['b', 'c'], { arrayLimit: 1 });
|
||||||
|
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
||||||
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b', 2: 'c' }, 'converts to overflow object');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('merges primitive into array at the arrayLimit boundary, consistently with combine', function (s2t) {
|
||||||
|
var merged = utils.merge(['a'], 'b', { arrayLimit: 1 });
|
||||||
|
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow at the boundary');
|
||||||
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b' }, 'converts to overflow object instead of a length-2 array');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('merges two arrays that exceed arrayLimit into an overflow object', function (s2t) {
|
||||||
|
var merged = utils.merge(['a'], ['b'], { arrayLimit: 1 });
|
||||||
|
s2t.ok(utils.isOverflow(merged), 'result is marked as overflow');
|
||||||
|
s2t.deepEqual(merged, { 0: 'a', 1: 'b' }, 'array-into-array merge enforces arrayLimit like combine');
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws at the arrayLimit boundary when merging a primitive into an array with throwOnLimitExceeded', function (s2t) {
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge(['a'], 'b', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws when the resulting length would exceed arrayLimit'
|
||||||
|
);
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws when merging two arrays past arrayLimit with throwOnLimitExceeded', function (s2t) {
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge(['a'], ['b'], { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'array-into-array merge throws rather than silently exceeding arrayLimit'
|
||||||
|
);
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge(['a', 'b', 'c'], ['d', 'e', 'f'], { arrayLimit: 2, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 2 elements allowed in an array.'),
|
||||||
|
'uses the plural message when arrayLimit is not 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws instead of merging primitive into over-limit array when throwOnLimitExceeded is set', function (s2t) {
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge(['a', 'b', 'c'], 'd', { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws rather than converting to an overflow object'
|
||||||
|
);
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge(['a', 'b', 'c'], 'd', { arrayLimit: 2, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 2 elements allowed in an array.'),
|
||||||
|
'uses the plural message when arrayLimit is not 1'
|
||||||
|
);
|
||||||
|
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws instead of merging array into primitive when throwOnLimitExceeded is set', function (s2t) {
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge('a', ['b', 'c'], { arrayLimit: 1, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'throws rather than converting to an overflow object'
|
||||||
|
);
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.merge('a', ['b', 'c', 'd'], { arrayLimit: 2, throwOnLimitExceeded: true }); },
|
||||||
|
new RangeError('Array limit exceeded. Only 2 elements allowed in an array.'),
|
||||||
|
'uses the plural message when arrayLimit is not 1'
|
||||||
|
);
|
||||||
|
|
||||||
s2t.end();
|
s2t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -249,6 +354,47 @@ test('combine()', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('with throwOnLimitExceeded', function (st) {
|
||||||
|
st.test('throws when concatenation exceeds the limit', function (s2t) {
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.combine(['a', 'b', 'c'], 'd', 3, false, true); },
|
||||||
|
new RangeError('Array limit exceeded. Only 3 elements allowed in an array.'),
|
||||||
|
'throws instead of converting to an overflow object'
|
||||||
|
);
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.combine([], 'a', 0, false, true); },
|
||||||
|
new RangeError('Array limit exceeded. Only 0 elements allowed in an array.'),
|
||||||
|
'throws with the correct count at arrayLimit 0'
|
||||||
|
);
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('throws when adding to an existing overflow object', function (s2t) {
|
||||||
|
var overflow = utils.combine(['a', 'b'], 'c', 0, false);
|
||||||
|
s2t.ok(utils.isOverflow(overflow), 'initial object is marked as overflow');
|
||||||
|
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.combine(overflow, 'd', 0, false, true); },
|
||||||
|
new RangeError('Array limit exceeded. Only 0 elements allowed in an array.'),
|
||||||
|
'throws rather than appending to the overflow object'
|
||||||
|
);
|
||||||
|
s2t['throws'](
|
||||||
|
function () { utils.combine(overflow, 'd', 1, false, true); },
|
||||||
|
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
||||||
|
'uses the singular message at arrayLimit 1'
|
||||||
|
);
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.test('does not throw when within the limit', function (s2t) {
|
||||||
|
var combined = utils.combine(['a'], 'b', 5, false, true);
|
||||||
|
s2t.deepEqual(combined, ['a', 'b'], 'returns the array unchanged when under the limit');
|
||||||
|
s2t.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.test('with existing overflow object', function (st) {
|
t.test('with existing overflow object', function (st) {
|
||||||
st.test('adds to existing overflow object at next index', function (s2t) {
|
st.test('adds to existing overflow object at next index', function (s2t) {
|
||||||
// Create overflow object first via combine: 3 elements (indices 0-2) with limit 0
|
// Create overflow object first via combine: 3 elements (indices 0-2) with limit 0
|
||||||
@@ -347,6 +493,79 @@ test('encode', function (t) {
|
|||||||
'encodes a long string'
|
'encodes a long string'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
var boundary = '';
|
||||||
|
var expected = '';
|
||||||
|
for (var j = 0; j < 1023; j++) {
|
||||||
|
boundary += 'a';
|
||||||
|
expected += 'a';
|
||||||
|
}
|
||||||
|
boundary += '😀';
|
||||||
|
expected += '%F0%9F%98%80';
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
utils.encode(boundary),
|
||||||
|
expected,
|
||||||
|
'encodes a surrogate pair split across long-string chunks'
|
||||||
|
);
|
||||||
|
|
||||||
|
var laterBoundary = '';
|
||||||
|
var laterExpected = '';
|
||||||
|
for (var k = 0; k < 2047; k++) {
|
||||||
|
laterBoundary += 'a';
|
||||||
|
laterExpected += 'a';
|
||||||
|
}
|
||||||
|
laterBoundary += '😀';
|
||||||
|
laterExpected += '%F0%9F%98%80';
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
utils.encode(laterBoundary),
|
||||||
|
laterExpected,
|
||||||
|
'encodes a surrogate pair split across a later chunk boundary'
|
||||||
|
);
|
||||||
|
|
||||||
|
var twoPairs = '';
|
||||||
|
for (k = 0; k < 1023; k++) {
|
||||||
|
twoPairs += 'a';
|
||||||
|
}
|
||||||
|
twoPairs += '😀';
|
||||||
|
for (k = 0; k < 1022; k++) {
|
||||||
|
twoPairs += 'b';
|
||||||
|
}
|
||||||
|
twoPairs += '😀';
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
(utils.encode(twoPairs).match(/%F0%9F%98%80/g) || []).length,
|
||||||
|
2,
|
||||||
|
'encodes two surrogate pairs each split across a chunk boundary'
|
||||||
|
);
|
||||||
|
|
||||||
|
var roundTrip = '';
|
||||||
|
for (k = 0; k < 1023; k++) {
|
||||||
|
roundTrip += 'a';
|
||||||
|
}
|
||||||
|
roundTrip += '😀';
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
decodeURIComponent(utils.encode(roundTrip)),
|
||||||
|
roundTrip,
|
||||||
|
'a boundary-split surrogate pair round-trips through decodeURIComponent'
|
||||||
|
);
|
||||||
|
|
||||||
|
var loneBoundary = '';
|
||||||
|
var loneExpected = '';
|
||||||
|
for (k = 0; k < 1023; k++) {
|
||||||
|
loneBoundary += 'a';
|
||||||
|
loneExpected += 'a';
|
||||||
|
}
|
||||||
|
loneBoundary += '\uD83DX';
|
||||||
|
loneExpected += '%F0%9F%91%98';
|
||||||
|
|
||||||
|
t.equal(
|
||||||
|
utils.encode(loneBoundary),
|
||||||
|
loneExpected,
|
||||||
|
'a lone high surrogate at a chunk boundary encodes the same as mid-chunk'
|
||||||
|
);
|
||||||
|
|
||||||
t.equal(
|
t.equal(
|
||||||
utils.encode('\x28\x29'),
|
utils.encode('\x28\x29'),
|
||||||
'%28%29',
|
'%28%29',
|
||||||
@@ -376,7 +595,9 @@ test('encode', function (t) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('isBuffer()', function (t) {
|
test('isBuffer()', function (t) {
|
||||||
forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], function () {}, /a/g], function (x) {
|
var fn = function () {};
|
||||||
|
fn();
|
||||||
|
forEach([null, undefined, true, false, '', 'abc', 42, 0, NaN, {}, [], fn, /a/g], function (x) {
|
||||||
t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
|
t.equal(utils.isBuffer(x), false, inspect(x) + ' is not a buffer');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -386,8 +607,9 @@ test('isBuffer()', function (t) {
|
|||||||
var saferBuffer = SaferBuffer.from('abc');
|
var saferBuffer = SaferBuffer.from('abc');
|
||||||
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
|
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
|
||||||
|
|
||||||
var buffer = Buffer.from && Buffer.alloc ? Buffer.from('abc') : new Buffer('abc');
|
var buffer = SaferBuffer.from('abc');
|
||||||
t.equal(utils.isBuffer(buffer), true, 'real Buffer instance is a buffer');
|
t.notEqual(saferBuffer, buffer, 'different buffer instances');
|
||||||
|
t.equal(utils.isBuffer(buffer), true, 'another Buffer instance is a buffer');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+25
-4
@@ -5,11 +5,32 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.0.1](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/compare/v1.0.0...v1.0.1) - 2026-04-08
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- [Fix] `delete`: do not reset the list when deleting the head node of a multi-node list [`#3`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/issues/3)
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [actions] update workflows [`9e79e6b`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/commit/9e79e6bf845532fafbf03b547429fe4f737214a3)
|
||||||
|
- [Deps] update `@arethetypeswrong/cli`, `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `eslint`, `npmignore` [`babf3ca`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/commit/babf3ca4849d2fd893a470a6b82c62a80ccc9307)
|
||||||
|
- [Deps] update `object-inspect` [`9f0f4b8`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/commit/9f0f4b88ff2aa8b7b7c9e589ac1513f133d64b9f)
|
||||||
|
|
||||||
## v1.0.0 - 2024-12-10
|
## v1.0.0 - 2024-12-10
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|
||||||
- Initial implementation, tests, readme, types [`5d6baee`](https://github.com/ljharb/side-channel-list/commit/5d6baee5c9054a1238007f5a1dfc109a7a816251)
|
- Initial implementation, tests, readme, types [`5d6baee`](https://github.com/ljharb/side-channel-list.git
|
||||||
- Initial commit [`3ae784c`](https://github.com/ljharb/side-channel-list/commit/3ae784c63a47895fbaeed2a91ab54a8029a7a100)
|
/commit/5d6baee5c9054a1238007f5a1dfc109a7a816251)
|
||||||
- npm init [`07055a4`](https://github.com/ljharb/side-channel-list/commit/07055a4d139895565b199dba5fe2479c1a1b9e28)
|
- Initial commit [`3ae784c`](https://github.com/ljharb/side-channel-list.git
|
||||||
- Only apps should have lockfiles [`9573058`](https://github.com/ljharb/side-channel-list/commit/9573058a47494e2d68f8c6c77b5d7fbe441949c1)
|
/commit/3ae784c63a47895fbaeed2a91ab54a8029a7a100)
|
||||||
|
- npm init [`07055a4`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/commit/07055a4d139895565b199dba5fe2479c1a1b9e28)
|
||||||
|
- Only apps should have lockfiles [`9573058`](https://github.com/ljharb/side-channel-list.git
|
||||||
|
/commit/9573058a47494e2d68f8c6c77b5d7fbe441949c1)
|
||||||
|
|||||||
+1
-3
@@ -84,9 +84,8 @@ module.exports = function getSideChannelList() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
'delete': function (key) {
|
'delete': function (key) {
|
||||||
var root = $o && $o.next;
|
|
||||||
var deletedNode = listDelete($o, key);
|
var deletedNode = listDelete($o, key);
|
||||||
if (deletedNode && root && root === deletedNode) {
|
if (deletedNode && $o && !$o.next) {
|
||||||
$o = void undefined;
|
$o = void undefined;
|
||||||
}
|
}
|
||||||
return !!deletedNode;
|
return !!deletedNode;
|
||||||
@@ -108,6 +107,5 @@ module.exports = function getSideChannelList() {
|
|||||||
listSet(/** @type {NonNullable<typeof $o>} */ ($o), key, value);
|
listSet(/** @type {NonNullable<typeof $o>} */ ($o), key, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// @ts-expect-error TODO: figure out why this is erroring
|
|
||||||
return channel;
|
return channel;
|
||||||
};
|
};
|
||||||
|
|||||||
+8
-8
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "side-channel-list",
|
"name": "side-channel-list",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"description": "Store information about any JS value in a side channel, using a linked list",
|
"description": "Store information about any JS value in a side channel, using a linked list",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -38,21 +38,21 @@
|
|||||||
"homepage": "https://github.com/ljharb/side-channel-list#readme",
|
"homepage": "https://github.com/ljharb/side-channel-list#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3"
|
"object-inspect": "^1.13.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.17.1",
|
"@arethetypeswrong/cli": "^0.18.2",
|
||||||
"@ljharb/eslint-config": "^21.1.1",
|
"@ljharb/eslint-config": "^22.2.2",
|
||||||
"@ljharb/tsconfig": "^0.2.2",
|
"@ljharb/tsconfig": "^0.3.2",
|
||||||
"@types/object-inspect": "^1.13.0",
|
"@types/object-inspect": "^1.13.0",
|
||||||
"@types/tape": "^5.6.5",
|
"@types/tape": "^5.8.1",
|
||||||
"auto-changelog": "^2.5.0",
|
"auto-changelog": "^2.5.0",
|
||||||
"eclint": "^2.8.1",
|
"eclint": "^2.8.1",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"eslint": "=8.8.0",
|
"eslint": "^8.57.1",
|
||||||
"evalmd": "^0.0.19",
|
"evalmd": "^0.0.19",
|
||||||
"in-publish": "^2.0.1",
|
"in-publish": "^2.0.1",
|
||||||
"npmignore": "^0.3.1",
|
"npmignore": "^0.3.5",
|
||||||
"nyc": "^10.3.2",
|
"nyc": "^10.3.2",
|
||||||
"safe-publish-latest": "^2.0.0",
|
"safe-publish-latest": "^2.0.0",
|
||||||
"tape": "^5.9.0",
|
"tape": "^5.9.0",
|
||||||
|
|||||||
+50
@@ -100,5 +100,55 @@ test('getSideChannelList', function (t) {
|
|||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
t.test('delete: first node in a multi-node list', function (st) {
|
||||||
|
var channel = getSideChannelList();
|
||||||
|
|
||||||
|
channel.set('a', 1);
|
||||||
|
channel.set('b', 2);
|
||||||
|
|
||||||
|
st.equal(channel['delete']('b'), true, 'deleting first data node yields true');
|
||||||
|
|
||||||
|
st.equal(channel.has('a'), true, 'second node is still present after deleting first');
|
||||||
|
st.equal(channel.get('a'), 1, 'second node value is intact after deleting first');
|
||||||
|
st.equal(channel.has('b'), false, 'deleted node is gone');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('delete: last remaining node empties the list', function (st) {
|
||||||
|
var channel = getSideChannelList();
|
||||||
|
|
||||||
|
channel.set('a', 1);
|
||||||
|
channel.set('b', 2);
|
||||||
|
|
||||||
|
st.equal(channel['delete']('b'), true, 'delete first node');
|
||||||
|
st.equal(channel['delete']('a'), true, 'delete second (last) node');
|
||||||
|
|
||||||
|
st.equal(channel.has('a'), false, 'a is gone');
|
||||||
|
st.equal(channel.has('b'), false, 'b is gone');
|
||||||
|
st.equal(channel.get('a'), undefined, 'get a yields undefined');
|
||||||
|
|
||||||
|
channel.set('c', 3);
|
||||||
|
st.equal(channel.get('c'), 3, 'can set new values after emptying');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
t.test('delete: middle node in a multi-node list', function (st) {
|
||||||
|
var channel = getSideChannelList();
|
||||||
|
|
||||||
|
channel.set('a', 1);
|
||||||
|
channel.set('b', 2);
|
||||||
|
channel.set('c', 3);
|
||||||
|
|
||||||
|
st.equal(channel['delete']('b'), true, 'deleting middle node yields true');
|
||||||
|
|
||||||
|
st.equal(channel.get('a'), 1, 'first node still intact');
|
||||||
|
st.equal(channel.has('b'), false, 'middle node is gone');
|
||||||
|
st.equal(channel.get('c'), 3, 'last node still intact');
|
||||||
|
|
||||||
|
st.end();
|
||||||
|
});
|
||||||
|
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|||||||
+10
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [v1.1.1](https://github.com/ljharb/side-channel/compare/v1.1.0...v1.1.1) - 2026-06-08
|
||||||
|
|
||||||
|
### Commits
|
||||||
|
|
||||||
|
- [Fix] `assert`: do not observably access object keys when throwing [`fc17361`](https://github.com/ljharb/side-channel/commit/fc173615136e6a6c3194656e6088afbc0bd6f9b8)
|
||||||
|
- [actions] update workflows [`35b18c0`](https://github.com/ljharb/side-channel/commit/35b18c0bced5769d59226cd44a7e4003ad0aee81)
|
||||||
|
- [Dev Deps] update `@arethetypeswrong/cli`, `@ljharb/eslint-config`, `@ljharb/tsconfig`, `@types/tape`, `auto-changelog`, `eslint`, `npmignore` [`b456a01`](https://github.com/ljharb/side-channel/commit/b456a01396cfdd028e5d39ea84885fe9178a72cf)
|
||||||
|
- [Deps] update `object-inspect`, `side-channel-list` [`accf1a1`](https://github.com/ljharb/side-channel/commit/accf1a1a2767a22b1f5eeb5769a1db4c35a78090)
|
||||||
|
- [readme] replace runkit CI badge with shields.io check-runs badge [`7e0c956`](https://github.com/ljharb/side-channel/commit/7e0c956411cc29267197a49a155a3d9bd01bacac)
|
||||||
|
|
||||||
## [v1.1.0](https://github.com/ljharb/side-channel/compare/v1.0.6...v1.1.0) - 2024-12-11
|
## [v1.1.0](https://github.com/ljharb/side-channel/compare/v1.0.6...v1.1.0) - 2024-12-11
|
||||||
|
|
||||||
### Commits
|
### Commits
|
||||||
|
|||||||
+1
-1
@@ -57,5 +57,5 @@ Clone the repo, `npm install`, and run `npm test`
|
|||||||
[downloads-url]: https://npm-stat.com/charts.html?package=side-channel
|
[downloads-url]: https://npm-stat.com/charts.html?package=side-channel
|
||||||
[codecov-image]: https://codecov.io/gh/ljharb/side-channel/branch/main/graphs/badge.svg
|
[codecov-image]: https://codecov.io/gh/ljharb/side-channel/branch/main/graphs/badge.svg
|
||||||
[codecov-url]: https://app.codecov.io/gh/ljharb/side-channel/
|
[codecov-url]: https://app.codecov.io/gh/ljharb/side-channel/
|
||||||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/side-channel
|
[actions-image]: https://img.shields.io/github/check-runs/ljharb/side-channel/main
|
||||||
[actions-url]: https://github.com/ljharb/side-channel/actions
|
[actions-url]: https://github.com/ljharb/side-channel/actions
|
||||||
|
|||||||
+5
-2
@@ -18,7 +18,10 @@ module.exports = function getSideChannel() {
|
|||||||
var channel = {
|
var channel = {
|
||||||
assert: function (key) {
|
assert: function (key) {
|
||||||
if (!channel.has(key)) {
|
if (!channel.has(key)) {
|
||||||
throw new $TypeError('Side channel does not contain ' + inspect(key));
|
var keyDesc = key && Object(key) === key
|
||||||
|
? 'the given object key'
|
||||||
|
: inspect(key);
|
||||||
|
throw new $TypeError('Side channel does not contain ' + keyDesc);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'delete': function (key) {
|
'delete': function (key) {
|
||||||
@@ -38,6 +41,6 @@ module.exports = function getSideChannel() {
|
|||||||
$channelData.set(key, value);
|
$channelData.set(key, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// @ts-expect-error TODO: figure out why this is erroring
|
|
||||||
return channel;
|
return channel;
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-10
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "side-channel",
|
"name": "side-channel",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"description": "Store information about any JS value in a side channel. Uses WeakMap if available.",
|
"description": "Store information about any JS value in a side channel. Uses WeakMap if available.",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -44,23 +44,23 @@
|
|||||||
"homepage": "https://github.com/ljharb/side-channel#readme",
|
"homepage": "https://github.com/ljharb/side-channel#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3",
|
"object-inspect": "^1.13.4",
|
||||||
"side-channel-list": "^1.0.0",
|
"side-channel-list": "^1.0.1",
|
||||||
"side-channel-map": "^1.0.1",
|
"side-channel-map": "^1.0.1",
|
||||||
"side-channel-weakmap": "^1.0.2"
|
"side-channel-weakmap": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@arethetypeswrong/cli": "^0.17.1",
|
"@arethetypeswrong/cli": "^0.18.3",
|
||||||
"@ljharb/eslint-config": "^21.1.1",
|
"@ljharb/eslint-config": "^22.2.3",
|
||||||
"@ljharb/tsconfig": "^0.2.2",
|
"@ljharb/tsconfig": "^0.3.2",
|
||||||
"@types/object-inspect": "^1.13.0",
|
"@types/object-inspect": "^1.13.0",
|
||||||
"@types/tape": "^5.6.5",
|
"@types/tape": "^5.8.1",
|
||||||
"auto-changelog": "^2.5.0",
|
"auto-changelog": "^2.6.0",
|
||||||
"eclint": "^2.8.1",
|
"eclint": "^2.8.1",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"eslint": "=8.8.0",
|
"eslint": "^8.57.1",
|
||||||
"in-publish": "^2.0.1",
|
"in-publish": "^2.0.1",
|
||||||
"npmignore": "^0.3.1",
|
"npmignore": "^0.3.5",
|
||||||
"nyc": "^10.3.2",
|
"nyc": "^10.3.2",
|
||||||
"safe-publish-latest": "^2.0.0",
|
"safe-publish-latest": "^2.0.0",
|
||||||
"tape": "^5.9.0",
|
"tape": "^5.9.0",
|
||||||
|
|||||||
+16
@@ -28,6 +28,22 @@ test('getSideChannel', function (t) {
|
|||||||
channel.set(o, 'data');
|
channel.set(o, 'data');
|
||||||
st.doesNotThrow(function () { channel.assert(o); }, 'existent value noops');
|
st.doesNotThrow(function () { channel.assert(o); }, 'existent value noops');
|
||||||
|
|
||||||
|
var accessed = false;
|
||||||
|
var trap = {};
|
||||||
|
Object.defineProperty(trap, 'foo', {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
accessed = true;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
st['throws'](
|
||||||
|
function () { channel.assert(trap); },
|
||||||
|
TypeError,
|
||||||
|
'missing object key throws'
|
||||||
|
);
|
||||||
|
st.equal(accessed, false, 'a missing object key is not observably accessed');
|
||||||
|
|
||||||
st.end();
|
st.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+8
-18
@@ -42,11 +42,16 @@ module.exports.match = mimeMatch
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function typeis (value, types_) {
|
function typeis (value, types_) {
|
||||||
|
// Backward compatibility. TODO: Remove.
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
value = value.headers['content-type']
|
||||||
|
}
|
||||||
|
|
||||||
var i
|
var i
|
||||||
var types = types_
|
var types = types_
|
||||||
|
|
||||||
// remove parameters and normalize
|
// remove parameters and normalize
|
||||||
var val = tryNormalizeType(value)
|
var val = normalizeType(value)
|
||||||
|
|
||||||
// no type or invalid
|
// no type or invalid
|
||||||
if (!val) {
|
if (!val) {
|
||||||
@@ -228,23 +233,8 @@ function mimeMatch (expected, actual) {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function normalizeType (value) {
|
function normalizeType (value) {
|
||||||
// Parse the type
|
if (!value) return null
|
||||||
var type = contentType.parse(value).type
|
var type = contentType.parse(value, { parameters: false }).type
|
||||||
|
|
||||||
return typer.test(type) ? type : null
|
return typer.test(type) ? type : null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to normalize a type and remove parameters.
|
|
||||||
*
|
|
||||||
* @param {string} value
|
|
||||||
* @return {(string|null)}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
function tryNormalizeType (value) {
|
|
||||||
try {
|
|
||||||
return value ? normalizeType(value) : null
|
|
||||||
} catch (err) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Douglas Christopher Wilson
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
# content-type
|
||||||
|
|
||||||
|
[![NPM version][npm-image]][npm-url]
|
||||||
|
[![NPM downloads][downloads-image]][downloads-url]
|
||||||
|
[![Build status][build-image]][build-url]
|
||||||
|
[![Build coverage][coverage-image]][coverage-url]
|
||||||
|
[![License][license-image]][license-url]
|
||||||
|
|
||||||
|
Create and parse HTTP `Content-Type` header.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install content-type
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
```js
|
||||||
|
const contentType = require("content-type");
|
||||||
|
```
|
||||||
|
|
||||||
|
### contentType.parse(string, options?)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const obj = contentType.parse("image/svg+xml; charset=utf-8");
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse a `Content-Type` header. This will return an object with the following properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type. Example: `'image/svg+xml'`.
|
||||||
|
- `parameters`: An object of the parameters in the media type (parameter name is always lower case). Example: `{charset: 'utf-8'}`.
|
||||||
|
|
||||||
|
The parser is lenient and does not error. You should validate `type` and `parameters` before trusting them.
|
||||||
|
|
||||||
|
#### Options
|
||||||
|
|
||||||
|
- `parameters` (default: `true`): Set to `false` to skip parameters.
|
||||||
|
|
||||||
|
### contentType.format(obj)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const str = contentType.format({
|
||||||
|
type: "image/svg+xml",
|
||||||
|
parameters: { charset: "utf-8" },
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Format an object into a `Content-Type` header. This will return a string of the content type for the given object with the following properties (examples are shown that produce the string `'image/svg+xml; charset=utf-8'`):
|
||||||
|
|
||||||
|
- `type`: The media type. Example: `'image/svg+xml'`.
|
||||||
|
- `parameters`: An optional object of the parameters in the media type. Example: `{charset: 'utf-8'}`.
|
||||||
|
|
||||||
|
Throws a `TypeError` if the object contains an invalid type or parameter names.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/content-type
|
||||||
|
[npm-url]: https://npmjs.org/package/content-type
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/content-type
|
||||||
|
[downloads-url]: https://npmjs.org/package/content-type
|
||||||
|
[build-image]: https://img.shields.io/github/actions/workflow/status/jshttp/content-type/ci.yml?branch=master
|
||||||
|
[build-url]: https://github.com/jshttp/content-type/actions/workflows/ci.yml?query=branch%3Amaster
|
||||||
|
[coverage-image]: https://img.shields.io/codecov/c/gh/jshttp/content-type
|
||||||
|
[coverage-url]: https://codecov.io/gh/jshttp/content-type
|
||||||
|
[license-image]: http://img.shields.io/npm/l/content-type.svg?style=flat
|
||||||
|
[license-url]: LICENSE
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
/*!
|
||||||
|
* content-type
|
||||||
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* The content type object contains a type string and optional parameters.
|
||||||
|
*/
|
||||||
|
export interface ContentType {
|
||||||
|
type: string;
|
||||||
|
parameters: Record<string, string>;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Format an object into a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export declare function format(obj: Partial<ContentType>): string;
|
||||||
|
/**
|
||||||
|
* Options for parsing a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export interface ParseOptions {
|
||||||
|
parameters?: boolean;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parse a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
export declare function parse(header: string, options?: ParseOptions): ContentType;
|
||||||
+170
@@ -0,0 +1,170 @@
|
|||||||
|
"use strict";
|
||||||
|
/*!
|
||||||
|
* content-type
|
||||||
|
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.format = format;
|
||||||
|
exports.parse = parse;
|
||||||
|
const TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]*$/;
|
||||||
|
const TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
/**
|
||||||
|
* RegExp to match chars that must be quoted-pair in RFC 9110 sec 5.6.4
|
||||||
|
*/
|
||||||
|
const QUOTE_REGEXP = /[\\"]/g;
|
||||||
|
/**
|
||||||
|
* RegExp to match type in RFC 9110 sec 8.3.1
|
||||||
|
*
|
||||||
|
* media-type = type "/" subtype
|
||||||
|
* type = token
|
||||||
|
* subtype = token
|
||||||
|
*/
|
||||||
|
const TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
|
||||||
|
/**
|
||||||
|
* Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`.
|
||||||
|
*/
|
||||||
|
const NullObject = /* @__PURE__ */ (() => {
|
||||||
|
const C = function () { };
|
||||||
|
C.prototype = Object.create(null);
|
||||||
|
return C;
|
||||||
|
})();
|
||||||
|
/**
|
||||||
|
* Format an object into a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
function format(obj) {
|
||||||
|
const { type, parameters } = obj;
|
||||||
|
if (!type || !TYPE_REGEXP.test(type)) {
|
||||||
|
throw new TypeError(`Invalid type: ${type}`);
|
||||||
|
}
|
||||||
|
let result = type;
|
||||||
|
if (parameters) {
|
||||||
|
for (const param of Object.keys(parameters)) {
|
||||||
|
if (!TOKEN_REGEXP.test(param)) {
|
||||||
|
throw new TypeError(`Invalid parameter name: ${param}`);
|
||||||
|
}
|
||||||
|
result += `; ${param}=${qstring(parameters[param])}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Parse a `Content-Type` header.
|
||||||
|
*/
|
||||||
|
function parse(header, options) {
|
||||||
|
const len = header.length;
|
||||||
|
let index = skipOWS(header, 0, len);
|
||||||
|
const valueStart = index;
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
const valueEnd = trailingOWS(header, valueStart, index);
|
||||||
|
const type = header.slice(valueStart, valueEnd).toLowerCase();
|
||||||
|
const parameters = options?.parameters === false
|
||||||
|
? new NullObject()
|
||||||
|
: parseParameters(header, index, len);
|
||||||
|
return { type, parameters };
|
||||||
|
}
|
||||||
|
const SP = 32; // " "
|
||||||
|
const HTAB = 9; // "\t"
|
||||||
|
const SEMI = 59; // ";"
|
||||||
|
const EQ = 61; // "="
|
||||||
|
const DQUOTE = 34; // '"'
|
||||||
|
const BSLASH = 92; // "\\"
|
||||||
|
/**
|
||||||
|
* Parses the parameters of a `Content-Type` header starting at the given index.
|
||||||
|
*/
|
||||||
|
function parseParameters(header, index, len) {
|
||||||
|
const parameters = new NullObject();
|
||||||
|
parameter: while (index < len) {
|
||||||
|
index = skipOWS(header, index + 1 /* Skip over ; */, len);
|
||||||
|
const keyStart = index;
|
||||||
|
while (index < len) {
|
||||||
|
const code = header.charCodeAt(index);
|
||||||
|
if (code === SEMI)
|
||||||
|
continue parameter;
|
||||||
|
if (code === EQ) {
|
||||||
|
const keyEnd = trailingOWS(header, keyStart, index);
|
||||||
|
const key = header.slice(keyStart, keyEnd).toLowerCase();
|
||||||
|
index = skipOWS(header, index + 1, len);
|
||||||
|
if (index < len && header.charCodeAt(index) === DQUOTE) {
|
||||||
|
index++;
|
||||||
|
let value = "";
|
||||||
|
while (index < len) {
|
||||||
|
const code = header.charCodeAt(index++);
|
||||||
|
if (code === DQUOTE) {
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
if (parameters[key] === undefined)
|
||||||
|
parameters[key] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (code === BSLASH && index < len) {
|
||||||
|
value += header[index++];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value += String.fromCharCode(code);
|
||||||
|
}
|
||||||
|
continue parameter;
|
||||||
|
}
|
||||||
|
const valueStart = index;
|
||||||
|
index = skipValue(header, index, len);
|
||||||
|
if (parameters[key] === undefined) {
|
||||||
|
const valueEnd = trailingOWS(header, valueStart, index);
|
||||||
|
parameters[key] = header.slice(valueStart, valueEnd);
|
||||||
|
}
|
||||||
|
continue parameter;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Skip over characters until a semicolon.
|
||||||
|
*/
|
||||||
|
function skipValue(str, index, len) {
|
||||||
|
while (index < len) {
|
||||||
|
const char = str.charCodeAt(index);
|
||||||
|
if (char === SEMI)
|
||||||
|
break;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Skip optional whitespace (OWS) in an HTTP header value.
|
||||||
|
*
|
||||||
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
||||||
|
*/
|
||||||
|
function skipOWS(header, index, len) {
|
||||||
|
while (index < len) {
|
||||||
|
const char = header.charCodeAt(index);
|
||||||
|
if (char !== SP && char !== HTAB)
|
||||||
|
break;
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Trim optional whitespace (OWS) from the end of a substring.
|
||||||
|
*
|
||||||
|
* OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t").
|
||||||
|
*/
|
||||||
|
function trailingOWS(header, start, end) {
|
||||||
|
while (end > start) {
|
||||||
|
const char = header.charCodeAt(end - 1);
|
||||||
|
if (char !== SP && char !== HTAB)
|
||||||
|
break;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Serialize a parameter value.
|
||||||
|
*/
|
||||||
|
function qstring(str) {
|
||||||
|
if (TOKEN_REGEXP.test(str))
|
||||||
|
return str;
|
||||||
|
if (TEXT_REGEXP.test(str))
|
||||||
|
return `"${str.replace(QUOTE_REGEXP, "\\$&")}"`;
|
||||||
|
throw new TypeError(`Invalid parameter value: ${str}`);
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+52
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"name": "content-type",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"description": "Create and parse HTTP Content-Type header",
|
||||||
|
"keywords": [
|
||||||
|
"content-type",
|
||||||
|
"http",
|
||||||
|
"req",
|
||||||
|
"res",
|
||||||
|
"rfc7231",
|
||||||
|
"rfc9110"
|
||||||
|
],
|
||||||
|
"repository": "jshttp/content-type",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
|
"type": "commonjs",
|
||||||
|
"exports": "./dist/index.js",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"typings": "./dist/index.d.ts",
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"bench": "vitest bench",
|
||||||
|
"build": "ts-scripts build",
|
||||||
|
"format": "ts-scripts format",
|
||||||
|
"prepare": "ts-scripts install && npm run build",
|
||||||
|
"specs": "ts-scripts specs",
|
||||||
|
"test": "ts-scripts test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@borderless/ts-scripts": "^0.15.0",
|
||||||
|
"@vitest/coverage-v8": "^3.0.5",
|
||||||
|
"typescript": "^5.7.3",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"ts-scripts": {
|
||||||
|
"dist": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"project": [
|
||||||
|
"tsconfig.build.json"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
-5
@@ -1,31 +1,35 @@
|
|||||||
{
|
{
|
||||||
"name": "type-is",
|
"name": "type-is",
|
||||||
"description": "Infer the content-type of a request.",
|
"description": "Infer the content-type of a request.",
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||||
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
"Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "jshttp/type-is",
|
"repository": "jshttp/type-is",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"media-typer": "^1.1.0",
|
"media-typer": "^1.1.0",
|
||||||
"mime-types": "^3.0.0"
|
"mime-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-standard": "14.1.1",
|
"eslint-config-standard": "14.1.1",
|
||||||
"eslint-plugin-import": "2.25.4",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-markdown": "2.2.1",
|
"eslint-plugin-markdown": "2.2.1",
|
||||||
"eslint-plugin-node": "11.1.0",
|
"eslint-plugin-node": "11.1.0",
|
||||||
"eslint-plugin-promise": "5.2.0",
|
"eslint-plugin-promise": "5.2.0",
|
||||||
"eslint-plugin-standard": "4.1.0",
|
"eslint-plugin-standard": "4.1.0",
|
||||||
"mocha": "9.2.1",
|
"mocha": "9.2.2",
|
||||||
"nyc": "15.1.0"
|
"nyc": "15.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 18"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
|
|||||||
Generated
+67
-36
@@ -26,20 +26,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/body-parser": {
|
"node_modules/body-parser": {
|
||||||
"version": "2.2.2",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz",
|
||||||
"integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==",
|
"integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bytes": "^3.1.2",
|
"bytes": "^3.1.2",
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"debug": "^4.4.3",
|
"debug": "^4.4.3",
|
||||||
"http-errors": "^2.0.0",
|
"http-errors": "^2.0.1",
|
||||||
"iconv-lite": "^0.7.0",
|
"iconv-lite": "^0.7.2",
|
||||||
"on-finished": "^2.4.1",
|
"on-finished": "^2.4.1",
|
||||||
"qs": "^6.14.1",
|
"qs": "^6.15.2",
|
||||||
"raw-body": "^3.0.1",
|
"raw-body": "^3.0.2",
|
||||||
"type-is": "^2.0.1"
|
"type-is": "^2.1.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
@@ -49,6 +49,19 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/content-type": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -201,9 +214,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/es-object-atoms": {
|
"node_modules/es-object-atoms": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
||||||
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0"
|
"es-errors": "^1.3.0"
|
||||||
@@ -380,9 +393,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
@@ -561,9 +574,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "8.3.0",
|
"version": "8.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
||||||
"integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
|
"integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -584,12 +597,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.15.0",
|
"version": "6.15.3",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
|
||||||
"integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==",
|
"integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"side-channel": "^1.1.0"
|
"es-define-property": "^1.0.1",
|
||||||
|
"side-channel": "^1.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
@@ -696,14 +710,14 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
"node_modules/side-channel": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
"integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3",
|
"object-inspect": "^1.13.4",
|
||||||
"side-channel-list": "^1.0.0",
|
"side-channel-list": "^1.0.1",
|
||||||
"side-channel-map": "^1.0.1",
|
"side-channel-map": "^1.0.1",
|
||||||
"side-channel-weakmap": "^1.0.2"
|
"side-channel-weakmap": "^1.0.2"
|
||||||
},
|
},
|
||||||
@@ -715,13 +729,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/side-channel-list": {
|
"node_modules/side-channel-list": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"es-errors": "^1.3.0",
|
"es-errors": "^1.3.0",
|
||||||
"object-inspect": "^1.13.3"
|
"object-inspect": "^1.13.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
@@ -786,17 +800,34 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/type-is": {
|
"node_modules/type-is": {
|
||||||
"version": "2.0.1",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz",
|
||||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
"integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"content-type": "^1.0.5",
|
"content-type": "^2.0.0",
|
||||||
"media-typer": "^1.1.0",
|
"media-typer": "^1.1.0",
|
||||||
"mime-types": "^3.0.0"
|
"mime-types": "^3.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.6"
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/type-is/node_modules/content-type": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# RDOV SECRETS
|
||||||
|
|
||||||
|
## Google Gemini (Flash 1.5)
|
||||||
|
API_KEY: AIzaSyA-YHI7CDp7bAZz-2U9IRjzMrmzhAM7zkA
|
||||||
|
|
||||||
|
## Supabase
|
||||||
|
URL: https://h0oggskgs0ws0sco8kc4s8ws.supabase.reifonas.cloud
|
||||||
|
ANON_KEY: (ver src/lib/supabase/client.ts)
|
||||||
|
|
||||||
|
## Proxy Login (Bypass Field)
|
||||||
|
USER: teste
|
||||||
|
PASS: teste -> associa-se a admin@tracksteel.com
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
nohup: ignoring input
|
||||||
|
VaultUI Server rodando na porta 80
|
||||||
Reference in New Issue
Block a user