update
This commit is contained in:
+10
@@ -8,10 +8,14 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 160
|
||||
quote_type = single
|
||||
|
||||
[test/*]
|
||||
max_line_length = off
|
||||
|
||||
[LICENSE.md]
|
||||
indent_size = off
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
|
||||
@@ -28,3 +32,9 @@ indent_size = 2
|
||||
[LICENSE]
|
||||
indent_size = 2
|
||||
max_line_length = off
|
||||
|
||||
[coverage/**/*]
|
||||
indent_size = off
|
||||
indent_style = off
|
||||
indent = off
|
||||
max_line_length = off
|
||||
|
||||
+21
-4
@@ -3,19 +3,36 @@
|
||||
|
||||
"extends": "@ljharb",
|
||||
|
||||
"ignorePatterns": [
|
||||
"dist/",
|
||||
],
|
||||
|
||||
"rules": {
|
||||
"complexity": 0,
|
||||
"consistent-return": 1,
|
||||
"func-name-matching": 0,
|
||||
"func-name-matching": 0,
|
||||
"id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
|
||||
"indent": [2, 4],
|
||||
"max-lines-per-function": [2, { "max": 150 }],
|
||||
"max-params": [2, 14],
|
||||
"max-params": [2, 15],
|
||||
"max-statements": [2, 52],
|
||||
"multiline-comment-style": 0,
|
||||
"no-continue": 1,
|
||||
"no-magic-numbers": 0,
|
||||
"no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
|
||||
"operator-linebreak": [2, "before"],
|
||||
}
|
||||
},
|
||||
|
||||
"overrides": [
|
||||
{
|
||||
"files": "test/**",
|
||||
"rules": {
|
||||
"function-paren-newline": 0,
|
||||
"max-lines-per-function": 0,
|
||||
"max-statements": 0,
|
||||
"no-buffer-constructor": 0,
|
||||
"no-extend-native": 0,
|
||||
"no-throw-literal": 0,
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [ljharb]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: npm/qs
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with a single custom sponsorship URL
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"all": true,
|
||||
"check-coverage": false,
|
||||
"reporter": ["text-summary", "text", "html", "json"],
|
||||
"lines": 86,
|
||||
"statements": 85.93,
|
||||
"functions": 82.43,
|
||||
"branches": 76.06,
|
||||
"exclude": [
|
||||
"coverage",
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
+132
@@ -1,3 +1,135 @@
|
||||
## **6.10.3**
|
||||
- [Fix] `parse`: ignore `__proto__` keys (#428)
|
||||
- [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
|
||||
- [actions] reuse common workflows
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `tape`
|
||||
|
||||
## **6.10.2**
|
||||
- [Fix] `stringify`: actually fix cyclic references (#426)
|
||||
- [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
|
||||
- [readme] remove travis badge; add github actions/codecov badges; update URLs
|
||||
- [Docs] add note and links for coercing primitive values (#408)
|
||||
- [actions] update codecov uploader
|
||||
- [actions] update workflows
|
||||
- [Tests] clean up stringify tests slightly
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `object-inspect`, `safe-publish-latest`, `tape`
|
||||
|
||||
## **6.10.1**
|
||||
- [Fix] `stringify`: avoid exception on repeated object values (#402)
|
||||
|
||||
## **6.10.0**
|
||||
- [New] `stringify`: throw on cycles, instead of an infinite loop (#395, #394, #393)
|
||||
- [New] `parse`: add `allowSparse` option for collapsing arrays with missing indices (#312)
|
||||
- [meta] fix README.md (#399)
|
||||
- [meta] only run `npm run dist` in publish, not install
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbols`, `tape`
|
||||
- [Tests] fix tests on node v0.6
|
||||
- [Tests] use `ljharb/actions/node/install` instead of `ljharb/actions/node/run`
|
||||
- [Tests] Revert "[meta] ignore eclint transitive audit warning"
|
||||
|
||||
## **6.9.6**
|
||||
- [Fix] restore `dist` dir; mistakenly removed in d4f6c32
|
||||
|
||||
## **6.9.5**
|
||||
- [Fix] `stringify`: do not encode parens for RFC1738
|
||||
- [Fix] `stringify`: fix arrayFormat comma with empty array/objects (#350)
|
||||
- [Refactor] `format`: remove `util.assign` call
|
||||
- [meta] add "Allow Edits" workflow; update rebase workflow
|
||||
- [actions] switch Automatic Rebase workflow to `pull_request_target` event
|
||||
- [Tests] `stringify`: add tests for #378
|
||||
- [Tests] migrate tests to Github Actions
|
||||
- [Tests] run `nyc` on all tests; use `tape` runner
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `mkdirp`, `object-inspect`, `tape`; add `aud`
|
||||
|
||||
## **6.9.4**
|
||||
- [Fix] `stringify`: when `arrayFormat` is `comma`, respect `serializeDate` (#364)
|
||||
- [Refactor] `stringify`: reduce branching (part of #350)
|
||||
- [Refactor] move `maybeMap` to `utils`
|
||||
- [Dev Deps] update `browserify`, `tape`
|
||||
|
||||
## **6.9.3**
|
||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
||||
|
||||
## **6.9.2**
|
||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
||||
- [meta] ignore eclint transitive audit warning
|
||||
- [meta] fix indentation in package.json
|
||||
- [meta] add tidelift marketing copy
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `has-symbols`, `tape`, `mkdirp`, `iconv-lite`
|
||||
- [actions] add automatic rebasing / merge commit blocking
|
||||
|
||||
## **6.9.1**
|
||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
||||
- [Fix] `parse`: with comma true, do not split non-string values (#334)
|
||||
- [meta] add `funding` field
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`
|
||||
- [Tests] use shared travis-ci config
|
||||
|
||||
## **6.9.0**
|
||||
- [New] `parse`/`stringify`: Pass extra key/value argument to `decoder` (#333)
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `evalmd`
|
||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
||||
- [Tests] add `posttest` using `npx aud` to run `npm audit` without a lockfile
|
||||
- [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
|
||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
||||
|
||||
## **6.8.2**
|
||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
||||
|
||||
## **6.8.1**
|
||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
||||
- [fix] `parse`: with comma true, do not split non-string values (#334)
|
||||
- [meta] add tidelift marketing copy
|
||||
- [meta] add `funding` field
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `has-symbols`, `iconv-lite`, `mkdirp`, `object-inspect`
|
||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
||||
- [Tests] use shared travis-ci configs
|
||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
||||
- [actions] add automatic rebasing / merge commit blocking
|
||||
|
||||
## **6.8.0**
|
||||
- [New] add `depth=false` to preserve the original key; [Fix] `depth=0` should preserve the original key (#326)
|
||||
- [New] [Fix] stringify symbols and bigints
|
||||
- [Fix] ensure node 0.12 can stringify Symbols
|
||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
||||
- [Refactor] `formats`: tiny bit of cleanup.
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `safe-publish-latest`, `iconv-lite`, `tape`
|
||||
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended (#326)
|
||||
- [Tests] use `eclint` instead of `editorconfig-tools`
|
||||
- [docs] readme: add security note
|
||||
- [meta] add github sponsorship
|
||||
- [meta] add FUNDING.yml
|
||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
||||
|
||||
## **6.7.2**
|
||||
- [Fix] proper comma parsing of URL-encoded commas (#361)
|
||||
- [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
|
||||
|
||||
## **6.7.1**
|
||||
- [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
|
||||
- [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
|
||||
- [fix] `parse`: with comma true, do not split non-string values (#334)
|
||||
- [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
|
||||
- [Fix] fix for an impossible situation: when the formatter is called with a non-string value
|
||||
- [Refactor] `formats`: tiny bit of cleanup.
|
||||
- readme: add security note
|
||||
- [meta] add tidelift marketing copy
|
||||
- [meta] add `funding` field
|
||||
- [meta] add FUNDING.yml
|
||||
- [meta] Clean up license text so it’s properly detected as BSD-3-Clause
|
||||
- [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `iconv-lite`, `mkdirp`, `object-inspect`, `browserify`
|
||||
- [Tests] `parse`: add passing `arrayFormat` tests
|
||||
- [Tests] use shared travis-ci configs
|
||||
- [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
|
||||
- [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended
|
||||
- [Tests] use `eclint` instead of `editorconfig-tools`
|
||||
- [actions] add automatic rebasing / merge commit blocking
|
||||
|
||||
## **6.7.0**
|
||||
- [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
|
||||
- [Fix] correctly parse nested arrays (#212)
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+71
-18
@@ -1,12 +1,13 @@
|
||||
# qs <sup>[![Version Badge][2]][1]</sup>
|
||||
|
||||
[![Build Status][3]][4]
|
||||
[![dependency status][5]][6]
|
||||
[![dev dependency status][7]][8]
|
||||
[![github actions][actions-image]][actions-url]
|
||||
[![coverage][codecov-image]][codecov-url]
|
||||
[![dependency status][deps-svg]][deps-url]
|
||||
[![dev dependency status][dev-deps-svg]][dev-deps-url]
|
||||
[![License][license-image]][license-url]
|
||||
[![Downloads][downloads-image]][downloads-url]
|
||||
|
||||
[![npm badge][11]][1]
|
||||
[![npm badge][npm-badge-png]][package-url]
|
||||
|
||||
A querystring parsing and stringifying library with some added security.
|
||||
|
||||
@@ -227,6 +228,13 @@ var noSparse = qs.parse('a[1]=b&a[15]=c');
|
||||
assert.deepEqual(noSparse, { a: ['b', 'c'] });
|
||||
```
|
||||
|
||||
You may also use `allowSparse` option to parse sparse arrays:
|
||||
|
||||
```javascript
|
||||
var sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
|
||||
assert.deepEqual(sparseArray, { a: [, '2', , '5'] });
|
||||
```
|
||||
|
||||
Note that an empty string is also a value, and will be preserved:
|
||||
|
||||
```javascript
|
||||
@@ -280,6 +288,17 @@ assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
|
||||
```
|
||||
(_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
|
||||
|
||||
### Parsing primitive/scalar values (numbers, booleans, null, etc)
|
||||
|
||||
By default, all values are parsed as strings. This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
|
||||
|
||||
```javascript
|
||||
var primitiveValues = qs.parse('a=15&b=true&c=null');
|
||||
assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
|
||||
```
|
||||
|
||||
If you wish to auto-convert values which look like numbers, booleans, and other values into their primitive counterparts, you can use the [query-types Express JS middleware](https://github.com/xpepermint/query-types) which will auto-convert all request query parameters.
|
||||
|
||||
### Stringifying
|
||||
|
||||
[](#preventEval)
|
||||
@@ -330,6 +349,30 @@ var decoded = qs.parse('x=z', { decoder: function (str) {
|
||||
}})
|
||||
```
|
||||
|
||||
You can encode keys and values using different logic by using the type argument provided to the encoder:
|
||||
|
||||
```javascript
|
||||
var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
|
||||
if (type === 'key') {
|
||||
return // Encoded key
|
||||
} else if (type === 'value') {
|
||||
return // Encoded value
|
||||
}
|
||||
}})
|
||||
```
|
||||
|
||||
The type argument is also provided to the decoder:
|
||||
|
||||
```javascript
|
||||
var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
|
||||
if (type === 'key') {
|
||||
return // Decoded key
|
||||
} else if (type === 'value') {
|
||||
return // Decoded value
|
||||
}
|
||||
}})
|
||||
```
|
||||
|
||||
Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
|
||||
|
||||
When arrays are stringified, by default they are given explicit indices:
|
||||
@@ -553,18 +596,28 @@ assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
|
||||
assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
|
||||
```
|
||||
|
||||
[1]: https://npmjs.org/package/qs
|
||||
[2]: http://versionbadg.es/ljharb/qs.svg
|
||||
[3]: https://api.travis-ci.org/ljharb/qs.svg
|
||||
[4]: https://travis-ci.org/ljharb/qs
|
||||
[5]: https://david-dm.org/ljharb/qs.svg
|
||||
[6]: https://david-dm.org/ljharb/qs
|
||||
[7]: https://david-dm.org/ljharb/qs/dev-status.svg
|
||||
[8]: https://david-dm.org/ljharb/qs?type=dev
|
||||
[9]: https://ci.testling.com/ljharb/qs.png
|
||||
[10]: https://ci.testling.com/ljharb/qs
|
||||
[11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
|
||||
[license-image]: http://img.shields.io/npm/l/qs.svg
|
||||
## Security
|
||||
|
||||
Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
|
||||
|
||||
## qs for enterprise
|
||||
|
||||
Available as part of the Tidelift Subscription
|
||||
|
||||
The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
||||
|
||||
[package-url]: https://npmjs.org/package/qs
|
||||
[npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
|
||||
[deps-svg]: https://david-dm.org/ljharb/qs.svg
|
||||
[deps-url]: https://david-dm.org/ljharb/qs
|
||||
[dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
|
||||
[dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
|
||||
[npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
|
||||
[license-image]: https://img.shields.io/npm/l/qs.svg
|
||||
[license-url]: LICENSE
|
||||
[downloads-image]: http://img.shields.io/npm/dm/qs.svg
|
||||
[downloads-url]: http://npm-stat.com/charts.html?package=qs
|
||||
[downloads-image]: https://img.shields.io/npm/dm/qs.svg
|
||||
[downloads-url]: https://npm-stat.com/charts.html?package=qs
|
||||
[codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
|
||||
[codecov-url]: https://app.codecov.io/gh/ljharb/qs/
|
||||
[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
|
||||
[actions-url]: https://github.com/ljharb/qs/actions
|
||||
|
||||
+1336
-74
File diff suppressed because it is too large
Load Diff
+9
-4
@@ -3,16 +3,21 @@
|
||||
var replace = String.prototype.replace;
|
||||
var percentTwenties = /%20/g;
|
||||
|
||||
var Format = {
|
||||
RFC1738: 'RFC1738',
|
||||
RFC3986: 'RFC3986'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
'default': 'RFC3986',
|
||||
'default': Format.RFC3986,
|
||||
formatters: {
|
||||
RFC1738: function (value) {
|
||||
return replace.call(value, percentTwenties, '+');
|
||||
},
|
||||
RFC3986: function (value) {
|
||||
return value;
|
||||
return String(value);
|
||||
}
|
||||
},
|
||||
RFC1738: 'RFC1738',
|
||||
RFC3986: 'RFC3986'
|
||||
RFC1738: Format.RFC1738,
|
||||
RFC3986: Format.RFC3986
|
||||
};
|
||||
|
||||
+36
-15
@@ -3,10 +3,12 @@
|
||||
var utils = require('./utils');
|
||||
|
||||
var has = Object.prototype.hasOwnProperty;
|
||||
var isArray = Array.isArray;
|
||||
|
||||
var defaults = {
|
||||
allowDots: false,
|
||||
allowPrototypes: false,
|
||||
allowSparse: false,
|
||||
arrayLimit: 20,
|
||||
charset: 'utf-8',
|
||||
charsetSentinel: false,
|
||||
@@ -28,6 +30,14 @@ var interpretNumericEntities = function (str) {
|
||||
});
|
||||
};
|
||||
|
||||
var parseArrayValue = function (val, options) {
|
||||
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
|
||||
return val.split(',');
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
// This is what browsers will submit when the ✓ character occurs in an
|
||||
// application/x-www-form-urlencoded body and the encoding of the page containing
|
||||
// the form is iso-8859-1, or when the submitted form has an accept-charset
|
||||
@@ -72,19 +82,24 @@ var parseValues = function parseQueryStringValues(str, options) {
|
||||
|
||||
var key, val;
|
||||
if (pos === -1) {
|
||||
key = options.decoder(part, defaults.decoder, charset);
|
||||
key = options.decoder(part, defaults.decoder, charset, 'key');
|
||||
val = options.strictNullHandling ? null : '';
|
||||
} else {
|
||||
key = options.decoder(part.slice(0, pos), defaults.decoder, charset);
|
||||
val = options.decoder(part.slice(pos + 1), defaults.decoder, charset);
|
||||
key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
|
||||
val = utils.maybeMap(
|
||||
parseArrayValue(part.slice(pos + 1), options),
|
||||
function (encodedVal) {
|
||||
return options.decoder(encodedVal, defaults.decoder, charset, 'value');
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
|
||||
val = interpretNumericEntities(val);
|
||||
}
|
||||
|
||||
if (val && options.comma && val.indexOf(',') > -1) {
|
||||
val = val.split(',');
|
||||
if (part.indexOf('[]=') > -1) {
|
||||
val = isArray(val) ? [val] : val;
|
||||
}
|
||||
|
||||
if (has.call(obj, key)) {
|
||||
@@ -97,8 +112,8 @@ var parseValues = function parseQueryStringValues(str, options) {
|
||||
return obj;
|
||||
};
|
||||
|
||||
var parseObject = function (chain, val, options) {
|
||||
var leaf = val;
|
||||
var parseObject = function (chain, val, options, valuesParsed) {
|
||||
var leaf = valuesParsed ? val : parseArrayValue(val, options);
|
||||
|
||||
for (var i = chain.length - 1; i >= 0; --i) {
|
||||
var obj;
|
||||
@@ -121,7 +136,7 @@ var parseObject = function (chain, val, options) {
|
||||
) {
|
||||
obj = [];
|
||||
obj[index] = leaf;
|
||||
} else {
|
||||
} else if (cleanRoot !== '__proto__') {
|
||||
obj[cleanRoot] = leaf;
|
||||
}
|
||||
}
|
||||
@@ -132,7 +147,7 @@ var parseObject = function (chain, val, options) {
|
||||
return leaf;
|
||||
};
|
||||
|
||||
var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
|
||||
var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
|
||||
if (!givenKey) {
|
||||
return;
|
||||
}
|
||||
@@ -147,7 +162,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
|
||||
|
||||
// Get the parent
|
||||
|
||||
var segment = brackets.exec(key);
|
||||
var segment = options.depth > 0 && brackets.exec(key);
|
||||
var parent = segment ? key.slice(0, segment.index) : key;
|
||||
|
||||
// Stash the parent if it exists
|
||||
@@ -167,7 +182,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
|
||||
// Loop through children appending to the array until we hit depth
|
||||
|
||||
var i = 0;
|
||||
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
||||
while (options.depth > 0 && (segment = child.exec(key)) !== null && i < options.depth) {
|
||||
i += 1;
|
||||
if (!options.plainObjects && has.call(Object.prototype, segment[1].slice(1, -1))) {
|
||||
if (!options.allowPrototypes) {
|
||||
@@ -183,7 +198,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
|
||||
keys.push('[' + key.slice(segment.index) + ']');
|
||||
}
|
||||
|
||||
return parseObject(keys, val, options);
|
||||
return parseObject(keys, val, options, valuesParsed);
|
||||
};
|
||||
|
||||
var normalizeParseOptions = function normalizeParseOptions(opts) {
|
||||
@@ -196,20 +211,22 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
|
||||
}
|
||||
|
||||
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
|
||||
throw new Error('The charset option must be either utf-8, iso-8859-1, or undefined');
|
||||
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
|
||||
}
|
||||
var charset = typeof opts.charset === 'undefined' ? defaults.charset : opts.charset;
|
||||
|
||||
return {
|
||||
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
|
||||
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
|
||||
allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
|
||||
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
|
||||
charset: charset,
|
||||
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
|
||||
comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
|
||||
decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
|
||||
delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
|
||||
depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth,
|
||||
// eslint-disable-next-line no-implicit-coercion, no-extra-parens
|
||||
depth: (typeof opts.depth === 'number' || opts.depth === false) ? +opts.depth : defaults.depth,
|
||||
ignoreQueryPrefix: opts.ignoreQueryPrefix === true,
|
||||
interpretNumericEntities: typeof opts.interpretNumericEntities === 'boolean' ? opts.interpretNumericEntities : defaults.interpretNumericEntities,
|
||||
parameterLimit: typeof opts.parameterLimit === 'number' ? opts.parameterLimit : defaults.parameterLimit,
|
||||
@@ -234,9 +251,13 @@ module.exports = function (str, opts) {
|
||||
var keys = Object.keys(tempObj);
|
||||
for (var i = 0; i < keys.length; ++i) {
|
||||
var key = keys[i];
|
||||
var newObj = parseKeys(key, tempObj[key], options);
|
||||
var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
|
||||
obj = utils.merge(obj, newObj, options);
|
||||
}
|
||||
|
||||
if (options.allowSparse === true) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
return utils.compact(obj);
|
||||
};
|
||||
|
||||
+99
-51
@@ -1,23 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
var getSideChannel = require('side-channel');
|
||||
var utils = require('./utils');
|
||||
var formats = require('./formats');
|
||||
var has = Object.prototype.hasOwnProperty;
|
||||
|
||||
var arrayPrefixGenerators = {
|
||||
brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
|
||||
brackets: function brackets(prefix) {
|
||||
return prefix + '[]';
|
||||
},
|
||||
comma: 'comma',
|
||||
indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
|
||||
indices: function indices(prefix, key) {
|
||||
return prefix + '[' + key + ']';
|
||||
},
|
||||
repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
|
||||
repeat: function repeat(prefix) {
|
||||
return prefix;
|
||||
}
|
||||
};
|
||||
|
||||
var isArray = Array.isArray;
|
||||
var split = String.prototype.split;
|
||||
var push = Array.prototype.push;
|
||||
var pushToArray = function (arr, valueOrArray) {
|
||||
push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
|
||||
@@ -25,6 +27,7 @@ var pushToArray = function (arr, valueOrArray) {
|
||||
|
||||
var toISO = Date.prototype.toISOString;
|
||||
|
||||
var defaultFormat = formats['default'];
|
||||
var defaults = {
|
||||
addQueryPrefix: false,
|
||||
allowDots: false,
|
||||
@@ -34,17 +37,28 @@ var defaults = {
|
||||
encode: true,
|
||||
encoder: utils.encode,
|
||||
encodeValuesOnly: false,
|
||||
formatter: formats.formatters[formats['default']],
|
||||
format: defaultFormat,
|
||||
formatter: formats.formatters[defaultFormat],
|
||||
// deprecated
|
||||
indices: false,
|
||||
serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
|
||||
serializeDate: function serializeDate(date) {
|
||||
return toISO.call(date);
|
||||
},
|
||||
skipNulls: false,
|
||||
strictNullHandling: false
|
||||
};
|
||||
|
||||
var stringify = function stringify( // eslint-disable-line func-name-matching
|
||||
var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
|
||||
return typeof v === 'string'
|
||||
|| typeof v === 'number'
|
||||
|| typeof v === 'boolean'
|
||||
|| typeof v === 'symbol'
|
||||
|| typeof v === 'bigint';
|
||||
};
|
||||
|
||||
var sentinel = {};
|
||||
|
||||
var stringify = function stringify(
|
||||
object,
|
||||
prefix,
|
||||
generateArrayPrefix,
|
||||
@@ -55,31 +69,66 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
|
||||
sort,
|
||||
allowDots,
|
||||
serializeDate,
|
||||
format,
|
||||
formatter,
|
||||
encodeValuesOnly,
|
||||
charset
|
||||
charset,
|
||||
sideChannel
|
||||
) {
|
||||
var obj = object;
|
||||
|
||||
var tmpSc = sideChannel;
|
||||
var step = 0;
|
||||
var findFlag = false;
|
||||
while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
|
||||
// Where object last appeared in the ref tree
|
||||
var pos = tmpSc.get(object);
|
||||
step += 1;
|
||||
if (typeof pos !== 'undefined') {
|
||||
if (pos === step) {
|
||||
throw new RangeError('Cyclic object value');
|
||||
} else {
|
||||
findFlag = true; // Break while
|
||||
}
|
||||
}
|
||||
if (typeof tmpSc.get(sentinel) === 'undefined') {
|
||||
step = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof filter === 'function') {
|
||||
obj = filter(prefix, obj);
|
||||
} else if (obj instanceof Date) {
|
||||
obj = serializeDate(obj);
|
||||
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
||||
obj = obj.join(',');
|
||||
obj = utils.maybeMap(obj, function (value) {
|
||||
if (value instanceof Date) {
|
||||
return serializeDate(value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
if (obj === null) {
|
||||
if (strictNullHandling) {
|
||||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix;
|
||||
return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
|
||||
}
|
||||
|
||||
obj = '';
|
||||
}
|
||||
|
||||
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) {
|
||||
if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
|
||||
if (encoder) {
|
||||
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset);
|
||||
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))];
|
||||
var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
|
||||
if (generateArrayPrefix === 'comma' && encodeValuesOnly) {
|
||||
var valuesArray = split.call(String(obj), ',');
|
||||
var valuesJoined = '';
|
||||
for (var i = 0; i < valuesArray.length; ++i) {
|
||||
valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset, 'value', format));
|
||||
}
|
||||
return [formatter(keyValue) + '=' + valuesJoined];
|
||||
}
|
||||
return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
|
||||
}
|
||||
return [formatter(prefix) + '=' + formatter(String(obj))];
|
||||
}
|
||||
@@ -91,53 +140,48 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
|
||||
}
|
||||
|
||||
var objKeys;
|
||||
if (isArray(filter)) {
|
||||
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
||||
// we need to join elements in
|
||||
objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
||||
} else if (isArray(filter)) {
|
||||
objKeys = filter;
|
||||
} else {
|
||||
var keys = Object.keys(obj);
|
||||
objKeys = sort ? keys.sort(sort) : keys;
|
||||
}
|
||||
|
||||
for (var i = 0; i < objKeys.length; ++i) {
|
||||
var key = objKeys[i];
|
||||
for (var j = 0; j < objKeys.length; ++j) {
|
||||
var key = objKeys[j];
|
||||
var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
|
||||
|
||||
if (skipNulls && obj[key] === null) {
|
||||
if (skipNulls && value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isArray(obj)) {
|
||||
pushToArray(values, stringify(
|
||||
obj[key],
|
||||
typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
|
||||
generateArrayPrefix,
|
||||
strictNullHandling,
|
||||
skipNulls,
|
||||
encoder,
|
||||
filter,
|
||||
sort,
|
||||
allowDots,
|
||||
serializeDate,
|
||||
formatter,
|
||||
encodeValuesOnly,
|
||||
charset
|
||||
));
|
||||
} else {
|
||||
pushToArray(values, stringify(
|
||||
obj[key],
|
||||
prefix + (allowDots ? '.' + key : '[' + key + ']'),
|
||||
generateArrayPrefix,
|
||||
strictNullHandling,
|
||||
skipNulls,
|
||||
encoder,
|
||||
filter,
|
||||
sort,
|
||||
allowDots,
|
||||
serializeDate,
|
||||
formatter,
|
||||
encodeValuesOnly,
|
||||
charset
|
||||
));
|
||||
}
|
||||
var keyPrefix = isArray(obj)
|
||||
? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
|
||||
: prefix + (allowDots ? '.' + key : '[' + key + ']');
|
||||
|
||||
sideChannel.set(object, step);
|
||||
var valueSideChannel = getSideChannel();
|
||||
valueSideChannel.set(sentinel, sideChannel);
|
||||
pushToArray(values, stringify(
|
||||
value,
|
||||
keyPrefix,
|
||||
generateArrayPrefix,
|
||||
strictNullHandling,
|
||||
skipNulls,
|
||||
encoder,
|
||||
filter,
|
||||
sort,
|
||||
allowDots,
|
||||
serializeDate,
|
||||
format,
|
||||
formatter,
|
||||
encodeValuesOnly,
|
||||
charset,
|
||||
valueSideChannel
|
||||
));
|
||||
}
|
||||
|
||||
return values;
|
||||
@@ -148,7 +192,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
|
||||
if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
|
||||
throw new TypeError('Encoder has to be a function.');
|
||||
}
|
||||
|
||||
@@ -181,6 +225,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
|
||||
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
|
||||
encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
|
||||
filter: filter,
|
||||
format: format,
|
||||
formatter: formatter,
|
||||
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
|
||||
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
|
||||
@@ -229,6 +274,7 @@ module.exports = function (object, opts) {
|
||||
objKeys.sort(options.sort);
|
||||
}
|
||||
|
||||
var sideChannel = getSideChannel();
|
||||
for (var i = 0; i < objKeys.length; ++i) {
|
||||
var key = objKeys[i];
|
||||
|
||||
@@ -246,9 +292,11 @@ module.exports = function (object, opts) {
|
||||
options.sort,
|
||||
options.allowDots,
|
||||
options.serializeDate,
|
||||
options.format,
|
||||
options.formatter,
|
||||
options.encodeValuesOnly,
|
||||
options.charset
|
||||
options.charset,
|
||||
sideChannel
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
+24
-2
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var formats = require('./formats');
|
||||
|
||||
var has = Object.prototype.hasOwnProperty;
|
||||
var isArray = Array.isArray;
|
||||
|
||||
@@ -43,6 +45,7 @@ var arrayToObject = function arrayToObject(source, options) {
|
||||
};
|
||||
|
||||
var merge = function merge(target, source, options) {
|
||||
/* eslint no-param-reassign: 0 */
|
||||
if (!source) {
|
||||
return target;
|
||||
}
|
||||
@@ -119,14 +122,19 @@ var decode = function (str, decoder, charset) {
|
||||
}
|
||||
};
|
||||
|
||||
var encode = function encode(str, defaultEncoder, charset) {
|
||||
var encode = function encode(str, defaultEncoder, charset, kind, format) {
|
||||
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
|
||||
// It has been adapted here for stricter adherence to RFC 3986
|
||||
if (str.length === 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
var string = typeof str === 'string' ? str : String(str);
|
||||
var string = str;
|
||||
if (typeof str === 'symbol') {
|
||||
string = Symbol.prototype.toString.call(str);
|
||||
} else if (typeof str !== 'string') {
|
||||
string = String(str);
|
||||
}
|
||||
|
||||
if (charset === 'iso-8859-1') {
|
||||
return escape(string).replace(/%u[0-9a-f]{4}/gi, function ($0) {
|
||||
@@ -146,6 +154,7 @@ var encode = function encode(str, defaultEncoder, charset) {
|
||||
|| (c >= 0x30 && c <= 0x39) // 0-9
|
||||
|| (c >= 0x41 && c <= 0x5A) // a-z
|
||||
|| (c >= 0x61 && c <= 0x7A) // A-Z
|
||||
|| (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )
|
||||
) {
|
||||
out += string.charAt(i);
|
||||
continue;
|
||||
@@ -168,6 +177,7 @@ var encode = function encode(str, defaultEncoder, charset) {
|
||||
|
||||
i += 1;
|
||||
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
|
||||
/* eslint operator-linebreak: [2, "before"] */
|
||||
out += hexTable[0xF0 | (c >> 18)]
|
||||
+ hexTable[0x80 | ((c >> 12) & 0x3F)]
|
||||
+ hexTable[0x80 | ((c >> 6) & 0x3F)]
|
||||
@@ -217,6 +227,17 @@ var combine = function combine(a, b) {
|
||||
return [].concat(a, b);
|
||||
};
|
||||
|
||||
var maybeMap = function maybeMap(val, fn) {
|
||||
if (isArray(val)) {
|
||||
var mapped = [];
|
||||
for (var i = 0; i < val.length; i += 1) {
|
||||
mapped.push(fn(val[i]));
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
return fn(val);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
arrayToObject: arrayToObject,
|
||||
assign: assign,
|
||||
@@ -226,5 +247,6 @@ module.exports = {
|
||||
encode: encode,
|
||||
isBuffer: isBuffer,
|
||||
isRegExp: isRegExp,
|
||||
maybeMap: maybeMap,
|
||||
merge: merge
|
||||
};
|
||||
|
||||
+46
-31
@@ -1,33 +1,33 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"qs@6.7.0",
|
||||
"/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series"
|
||||
"qs@6.10.3",
|
||||
"/home/node/nuxt"
|
||||
]
|
||||
],
|
||||
"_from": "qs@6.7.0",
|
||||
"_id": "qs@6.7.0",
|
||||
"_from": "qs@6.10.3",
|
||||
"_id": "qs@6.10.3",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
|
||||
"_integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
|
||||
"_location": "/qs",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "qs@6.7.0",
|
||||
"raw": "qs@6.10.3",
|
||||
"name": "qs",
|
||||
"escapedName": "qs",
|
||||
"rawSpec": "6.7.0",
|
||||
"rawSpec": "6.10.3",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "6.7.0"
|
||||
"fetchSpec": "6.10.3"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/body-parser",
|
||||
"/express"
|
||||
"/parse-path"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
|
||||
"_spec": "6.7.0",
|
||||
"_where": "/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series",
|
||||
"_resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
|
||||
"_spec": "6.10.3",
|
||||
"_where": "/home/node/nuxt",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ljharb/qs/issues"
|
||||
},
|
||||
@@ -38,27 +38,41 @@
|
||||
"url": "http://ljharb.codes"
|
||||
}
|
||||
],
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
||||
"devDependencies": {
|
||||
"@ljharb/eslint-config": "^13.1.1",
|
||||
"browserify": "^16.2.3",
|
||||
"covert": "^1.1.1",
|
||||
"editorconfig-tools": "^0.1.1",
|
||||
"eslint": "^5.15.3",
|
||||
"evalmd": "^0.0.17",
|
||||
"@ljharb/eslint-config": "^20.1.0",
|
||||
"aud": "^1.1.5",
|
||||
"browserify": "^16.5.2",
|
||||
"eclint": "^2.8.1",
|
||||
"eslint": "^8.6.0",
|
||||
"evalmd": "^0.0.19",
|
||||
"for-each": "^0.3.3",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"mkdirp": "^0.5.1",
|
||||
"object-inspect": "^1.6.0",
|
||||
"has-symbols": "^1.0.2",
|
||||
"iconv-lite": "^0.5.1",
|
||||
"in-publish": "^2.0.1",
|
||||
"mkdirp": "^0.5.5",
|
||||
"nyc": "^10.3.2",
|
||||
"object-inspect": "^1.12.0",
|
||||
"qs-iconv": "^1.0.4",
|
||||
"safe-publish-latest": "^1.1.2",
|
||||
"safe-publish-latest": "^2.0.0",
|
||||
"safer-buffer": "^2.1.2",
|
||||
"tape": "^4.10.1"
|
||||
"tape": "^5.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
},
|
||||
"greenkeeper": {
|
||||
"ignore": [
|
||||
"iconv-lite",
|
||||
"mkdirp"
|
||||
]
|
||||
},
|
||||
"homepage": "https://github.com/ljharb/qs",
|
||||
"keywords": [
|
||||
"querystring",
|
||||
@@ -76,15 +90,16 @@
|
||||
"url": "git+https://github.com/ljharb/qs.git"
|
||||
},
|
||||
"scripts": {
|
||||
"coverage": "covert test",
|
||||
"dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js",
|
||||
"lint": "eslint lib/*.js test/*.js",
|
||||
"postlint": "editorconfig-tools check * lib/* test/*",
|
||||
"prepublish": "safe-publish-latest && npm run dist",
|
||||
"lint": "eslint .",
|
||||
"postlint": "eclint check * lib/* test/* !dist/*",
|
||||
"posttest": "aud --production",
|
||||
"prepublish": "not-in-publish || npm run prepublishOnly",
|
||||
"prepublishOnly": "safe-publish-latest && npm run dist",
|
||||
"pretest": "npm run --silent readme && npm run --silent lint",
|
||||
"readme": "evalmd README.md",
|
||||
"test": "npm run --silent coverage",
|
||||
"tests-only": "node test"
|
||||
"test": "npm run tests-only",
|
||||
"tests-only": "nyc tape 'test/**/*.js'"
|
||||
},
|
||||
"version": "6.7.0"
|
||||
"version": "6.10.3"
|
||||
}
|
||||
|
||||
+166
-1
@@ -32,6 +32,38 @@ test('parse()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('arrayFormat: brackets allows only explicit arrays', function (st) {
|
||||
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'brackets' }), { a: 'b,c' });
|
||||
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'brackets' }), { a: ['b', 'c'] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('arrayFormat: indices allows only indexed arrays', function (st) {
|
||||
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'indices' }), { a: 'b,c' });
|
||||
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'indices' }), { a: ['b', 'c'] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('arrayFormat: comma allows only comma-separated arrays', function (st) {
|
||||
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'comma' }), { a: 'b,c' });
|
||||
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'comma' }), { a: ['b', 'c'] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('arrayFormat: repeat allows only repeated values', function (st) {
|
||||
st.deepEqual(qs.parse('a[]=b&a[]=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
|
||||
st.deepEqual(qs.parse('a=b,c', { arrayFormat: 'repeat' }), { a: 'b,c' });
|
||||
st.deepEqual(qs.parse('a=b&a=c', { arrayFormat: 'repeat' }), { a: ['b', 'c'] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('allows enabling dot notation', function (st) {
|
||||
st.deepEqual(qs.parse('a.b=c'), { 'a.b': 'c' });
|
||||
st.deepEqual(qs.parse('a.b=c', { allowDots: true }), { a: { b: 'c' } });
|
||||
@@ -52,6 +84,18 @@ test('parse()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
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][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.end();
|
||||
});
|
||||
|
||||
t.test('uses original key when depth = false', function (st) {
|
||||
st.deepEqual(qs.parse('a[0]=b&a[1]=c', { depth: false }), { 'a[0]': 'b', 'a[1]': 'c' });
|
||||
st.deepEqual(qs.parse('a[0][0]=b&a[0][1]=c&a[1]=d&e=2', { depth: false }), { 'a[0][0]': 'b', 'a[0][1]': 'c', 'a[1]': 'd', e: '2' });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.deepEqual(qs.parse('a=b&a=c'), { a: ['b', 'c'] }, 'parses a simple array');
|
||||
|
||||
t.test('parses an explicit array', function (st) {
|
||||
@@ -225,6 +269,15 @@ test('parse()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses sparse arrays', function (st) {
|
||||
/* eslint no-sparse-arrays: 0 */
|
||||
st.deepEqual(qs.parse('a[4]=1&a[1]=2', { allowSparse: true }), { a: [, '2', , , '1'] });
|
||||
st.deepEqual(qs.parse('a[1][b][2][c]=1', { allowSparse: true }), { a: [, { b: [, , { c: '1' }] }] });
|
||||
st.deepEqual(qs.parse('a[1][2][3][c]=1', { allowSparse: true }), { a: [, [, , [, , , { c: '1' }]]] });
|
||||
st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { allowSparse: true }), { a: [, [, , [, , , { c: [, '1'] }]]] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses semi-parsed strings', function (st) {
|
||||
st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
|
||||
st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
|
||||
@@ -356,6 +409,43 @@ test('parse()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses values with comma as array divider', function (st) {
|
||||
st.deepEqual(qs.parse({ foo: 'bar,tee' }, { comma: false }), { foo: 'bar,tee' });
|
||||
st.deepEqual(qs.parse({ foo: 'bar,tee' }, { comma: true }), { foo: ['bar', 'tee'] });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('use number decoder, parses string that has one number with comma option enabled', function (st) {
|
||||
var decoder = function (str, defaultDecoder, charset, type) {
|
||||
if (!isNaN(Number(str))) {
|
||||
return parseFloat(str);
|
||||
}
|
||||
return defaultDecoder(str, defaultDecoder, charset, type);
|
||||
};
|
||||
|
||||
st.deepEqual(qs.parse('foo=1', { comma: true, decoder: decoder }), { foo: 1 });
|
||||
st.deepEqual(qs.parse('foo=0', { comma: true, decoder: decoder }), { foo: 0 });
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses brackets holds array of arrays when having two parts of strings with comma as array divider', function (st) {
|
||||
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=4,5,6', { comma: true }), { foo: [['1', '2', '3'], ['4', '5', '6']] });
|
||||
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=', { comma: true }), { foo: [['1', '2', '3'], ''] });
|
||||
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=,', { comma: true }), { foo: [['1', '2', '3'], ['', '']] });
|
||||
st.deepEqual(qs.parse('foo[]=1,2,3&foo[]=a', { comma: true }), { foo: [['1', '2', '3'], 'a'] });
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses comma delimited array while having percent-encoded comma treated as normal text', function (st) {
|
||||
st.deepEqual(qs.parse('foo=a%2Cb', { comma: true }), { foo: 'a,b' });
|
||||
st.deepEqual(qs.parse('foo=a%2C%20b,d', { comma: true }), { foo: ['a, b', 'd'] });
|
||||
st.deepEqual(qs.parse('foo=a%2C%20b,c%2C%20d', { comma: true }), { foo: ['a, b', 'c, d'] });
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('parses an object in dot notation', function (st) {
|
||||
var input = {
|
||||
'user.name': { 'pop[bob]': 3 },
|
||||
@@ -532,13 +622,73 @@ test('parse()', function (t) {
|
||||
|
||||
st.deepEqual(
|
||||
qs.parse('a[b]=c&a=toString', { plainObjects: true }),
|
||||
{ a: { b: 'c', toString: true } },
|
||||
{ __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
|
||||
'can overwrite prototype with plainObjects true'
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('dunder proto is ignored', function (st) {
|
||||
var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
|
||||
var result = qs.parse(payload, { allowPrototypes: true });
|
||||
|
||||
st.deepEqual(
|
||||
result,
|
||||
{
|
||||
categories: {
|
||||
length: '42'
|
||||
}
|
||||
},
|
||||
'silent [[Prototype]] payload'
|
||||
);
|
||||
|
||||
var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
|
||||
|
||||
st.deepEqual(
|
||||
plainResult,
|
||||
{
|
||||
__proto__: null,
|
||||
categories: {
|
||||
__proto__: null,
|
||||
length: '42'
|
||||
}
|
||||
},
|
||||
'silent [[Prototype]] payload: plain objects'
|
||||
);
|
||||
|
||||
var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
|
||||
|
||||
st.notOk(Array.isArray(query.categories), 'is not an array');
|
||||
st.notOk(query.categories instanceof Array, 'is not instanceof an array');
|
||||
st.deepEqual(query.categories, { some: { json: 'toInject' } });
|
||||
st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
|
||||
|
||||
st.deepEqual(
|
||||
qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
|
||||
{
|
||||
foo: {
|
||||
bar: 'stuffs'
|
||||
}
|
||||
},
|
||||
'hidden values'
|
||||
);
|
||||
|
||||
st.deepEqual(
|
||||
qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
|
||||
{
|
||||
__proto__: null,
|
||||
foo: {
|
||||
__proto__: null,
|
||||
bar: 'stuffs'
|
||||
}
|
||||
},
|
||||
'hidden values: plain objects'
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('can return null objects', { skip: !Object.create }, function (st) {
|
||||
var expected = Object.create(null);
|
||||
expected.a = Object.create(null);
|
||||
@@ -672,5 +822,20 @@ test('parse()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('allows for decoding keys and values differently', function (st) {
|
||||
var decoder = function (str, defaultDecoder, charset, type) {
|
||||
if (type === 'key') {
|
||||
return defaultDecoder(str, defaultDecoder, charset, type).toLowerCase();
|
||||
}
|
||||
if (type === 'value') {
|
||||
return defaultDecoder(str, defaultDecoder, charset, type).toUpperCase();
|
||||
}
|
||||
throw 'this should never happen! type: ' + type;
|
||||
};
|
||||
|
||||
st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
+199
-13
@@ -5,6 +5,8 @@ var qs = require('../');
|
||||
var utils = require('../lib/utils');
|
||||
var iconv = require('iconv-lite');
|
||||
var SaferBuffer = require('safer-buffer').Buffer;
|
||||
var hasSymbols = require('has-symbols');
|
||||
var hasBigInt = typeof BigInt === 'function';
|
||||
|
||||
test('stringify()', function (t) {
|
||||
t.test('stringifies a querystring object', function (st) {
|
||||
@@ -28,6 +30,39 @@ test('stringify()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('stringifies symbols', { skip: !hasSymbols() }, function (st) {
|
||||
st.equal(qs.stringify(Symbol.iterator), '');
|
||||
st.equal(qs.stringify([Symbol.iterator]), '0=Symbol%28Symbol.iterator%29');
|
||||
st.equal(qs.stringify({ a: Symbol.iterator }), 'a=Symbol%28Symbol.iterator%29');
|
||||
st.equal(
|
||||
qs.stringify({ a: [Symbol.iterator] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
|
||||
'a[]=Symbol%28Symbol.iterator%29'
|
||||
);
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('stringifies bigints', { skip: !hasBigInt }, function (st) {
|
||||
var three = BigInt(3);
|
||||
var encodeWithN = function (value, defaultEncoder, charset) {
|
||||
var result = defaultEncoder(value, defaultEncoder, charset);
|
||||
return typeof value === 'bigint' ? result + 'n' : result;
|
||||
};
|
||||
st.equal(qs.stringify(three), '');
|
||||
st.equal(qs.stringify([three]), '0=3');
|
||||
st.equal(qs.stringify([three], { encoder: encodeWithN }), '0=3n');
|
||||
st.equal(qs.stringify({ a: three }), 'a=3');
|
||||
st.equal(qs.stringify({ a: three }, { encoder: encodeWithN }), 'a=3n');
|
||||
st.equal(
|
||||
qs.stringify({ a: [three] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
|
||||
'a[]=3'
|
||||
);
|
||||
st.equal(
|
||||
qs.stringify({ a: [three] }, { encodeValuesOnly: true, encoder: encodeWithN, arrayFormat: 'brackets' }),
|
||||
'a[]=3n'
|
||||
);
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('adds query prefix', function (st) {
|
||||
st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b');
|
||||
st.end();
|
||||
@@ -97,10 +132,10 @@ test('stringify()', function (t) {
|
||||
});
|
||||
|
||||
t.test('stringifies a nested array value', function (st) {
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[b][0]=c&a[b][1]=d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[b][]=c&a[b][]=d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a[b]=c,d');
|
||||
st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true }), 'a[b][0]=c&a[b][1]=d');
|
||||
st.end();
|
||||
});
|
||||
|
||||
@@ -108,7 +143,7 @@ test('stringify()', function (t) {
|
||||
st.equal(
|
||||
qs.stringify(
|
||||
{ a: { b: ['c', 'd'] } },
|
||||
{ allowDots: true, encode: false, arrayFormat: 'indices' }
|
||||
{ allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }
|
||||
),
|
||||
'a.b[0]=c&a.b[1]=d',
|
||||
'indices: stringifies with dots + indices'
|
||||
@@ -116,7 +151,7 @@ test('stringify()', function (t) {
|
||||
st.equal(
|
||||
qs.stringify(
|
||||
{ a: { b: ['c', 'd'] } },
|
||||
{ allowDots: true, encode: false, arrayFormat: 'brackets' }
|
||||
{ allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }
|
||||
),
|
||||
'a.b[]=c&a.b[]=d',
|
||||
'brackets: stringifies with dots + brackets'
|
||||
@@ -124,7 +159,7 @@ test('stringify()', function (t) {
|
||||
st.equal(
|
||||
qs.stringify(
|
||||
{ a: { b: ['c', 'd'] } },
|
||||
{ allowDots: true, encode: false, arrayFormat: 'comma' }
|
||||
{ allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }
|
||||
),
|
||||
'a.b=c,d',
|
||||
'comma: stringifies with dots + comma'
|
||||
@@ -132,7 +167,7 @@ test('stringify()', function (t) {
|
||||
st.equal(
|
||||
qs.stringify(
|
||||
{ a: { b: ['c', 'd'] } },
|
||||
{ allowDots: true, encode: false }
|
||||
{ allowDots: true, encodeValuesOnly: true }
|
||||
),
|
||||
'a.b[0]=c&a.b[1]=d',
|
||||
'default: stringifies with dots + indices'
|
||||
@@ -180,17 +215,23 @@ test('stringify()', function (t) {
|
||||
|
||||
t.test('stringifies an array with mixed objects and primitives', function (st) {
|
||||
st.equal(
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' }),
|
||||
'a[0][b]=1&a[1]=2&a[2]=3',
|
||||
'indices => indices'
|
||||
);
|
||||
st.equal(
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
|
||||
'a[][b]=1&a[]=2&a[]=3',
|
||||
'brackets => brackets'
|
||||
);
|
||||
st.equal(
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }),
|
||||
'???',
|
||||
'brackets => brackets',
|
||||
{ skip: 'TODO: figure out what this should do' }
|
||||
);
|
||||
st.equal(
|
||||
qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true }),
|
||||
'a[0][b]=1&a[1]=2&a[2]=3',
|
||||
'default => indices'
|
||||
);
|
||||
@@ -301,6 +342,27 @@ test('stringify()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('stringifies an empty array in different arrayFormat', function (st) {
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false }), 'b[0]=&c=c');
|
||||
// arrayFormat default
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' }), 'b[0]=&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' }), 'b[]=&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' }), 'b=&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' }), 'b=&c=c');
|
||||
// with strictNullHandling
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', strictNullHandling: true }), 'b[0]&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', strictNullHandling: true }), 'b[]&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', strictNullHandling: true }), 'b&c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', strictNullHandling: true }), 'b&c=c');
|
||||
// with skipNulls
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', skipNulls: true }), 'c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', skipNulls: true }), 'c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', skipNulls: true }), 'c=c');
|
||||
st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', skipNulls: true }), 'c=c');
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('stringifies a null object', { skip: !Object.create }, function (st) {
|
||||
var obj = Object.create(null);
|
||||
obj.a = 'b';
|
||||
@@ -377,7 +439,7 @@ test('stringify()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('doesn\'t blow up when Buffer global is missing', function (st) {
|
||||
t.test('does not blow up when Buffer global is missing', function (st) {
|
||||
var tempBuffer = global.Buffer;
|
||||
delete global.Buffer;
|
||||
var result = qs.stringify({ a: 'b', c: 'd' });
|
||||
@@ -386,6 +448,57 @@ test('stringify()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('does not crash when parsing circular references', function (st) {
|
||||
var a = {};
|
||||
a.b = a;
|
||||
|
||||
st['throws'](
|
||||
function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
|
||||
/RangeError: Cyclic object value/,
|
||||
'cyclic values throw'
|
||||
);
|
||||
|
||||
var circular = {
|
||||
a: 'value'
|
||||
};
|
||||
circular.a = circular;
|
||||
st['throws'](
|
||||
function () { qs.stringify(circular); },
|
||||
/RangeError: Cyclic object value/,
|
||||
'cyclic values throw'
|
||||
);
|
||||
|
||||
var arr = ['a'];
|
||||
st.doesNotThrow(
|
||||
function () { qs.stringify({ x: arr, y: arr }); },
|
||||
'non-cyclic values do not throw'
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('non-circular duplicated references can still work', function (st) {
|
||||
var hourOfDay = {
|
||||
'function': 'hour_of_day'
|
||||
};
|
||||
|
||||
var p1 = {
|
||||
'function': 'gte',
|
||||
arguments: [hourOfDay, 0]
|
||||
};
|
||||
var p2 = {
|
||||
'function': 'lte',
|
||||
arguments: [hourOfDay, 23]
|
||||
};
|
||||
|
||||
st.equal(
|
||||
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true }),
|
||||
'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23'
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('selects properties when filter=array', function (st) {
|
||||
st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
|
||||
st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
|
||||
@@ -520,6 +633,12 @@ test('stringify()', function (t) {
|
||||
return String.fromCharCode(buffer.readUInt8(0) + 97);
|
||||
}
|
||||
}), 'a=b');
|
||||
|
||||
st.equal(qs.stringify({ a: SaferBuffer.from('a b') }, {
|
||||
encoder: function (buffer) {
|
||||
return buffer;
|
||||
}
|
||||
}), 'a=a b');
|
||||
st.end();
|
||||
});
|
||||
|
||||
@@ -554,23 +673,43 @@ test('stringify()', function (t) {
|
||||
'custom serializeDate function called'
|
||||
);
|
||||
|
||||
st.equal(
|
||||
qs.stringify(
|
||||
{ a: [date] },
|
||||
{
|
||||
serializeDate: function (d) { return d.getTime(); },
|
||||
arrayFormat: 'comma'
|
||||
}
|
||||
),
|
||||
'a=' + date.getTime(),
|
||||
'works with arrayFormat comma'
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('RFC 1738 spaces serialization', function (st) {
|
||||
t.test('RFC 1738 serialization', function (st) {
|
||||
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
|
||||
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
|
||||
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b');
|
||||
|
||||
st.equal(qs.stringify({ 'foo(ref)': 'bar' }, { format: qs.formats.RFC1738 }), 'foo(ref)=bar');
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('RFC 3986 spaces serialization', function (st) {
|
||||
st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
|
||||
st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
|
||||
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b');
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('Backward compatibility to RFC 3986', function (st) {
|
||||
st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
|
||||
st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b');
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
@@ -675,5 +814,52 @@ test('stringify()', function (t) {
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('allows for encoding keys and values differently', function (st) {
|
||||
var encoder = function (str, defaultEncoder, charset, type) {
|
||||
if (type === 'key') {
|
||||
return defaultEncoder(str, defaultEncoder, charset, type).toLowerCase();
|
||||
}
|
||||
if (type === 'value') {
|
||||
return defaultEncoder(str, defaultEncoder, charset, type).toUpperCase();
|
||||
}
|
||||
throw 'this should never happen! type: ' + type;
|
||||
};
|
||||
|
||||
st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('objects inside arrays', function (st) {
|
||||
var obj = { a: { b: { c: 'd', e: 'f' } } };
|
||||
var withArray = { a: { b: [{ c: 'd', e: 'f' }] } };
|
||||
|
||||
st.equal(qs.stringify(obj, { encode: false }), 'a[b][c]=d&a[b][e]=f', 'no array, no arrayFormat');
|
||||
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'bracket' }), 'a[b][c]=d&a[b][e]=f', 'no array, bracket');
|
||||
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'indices' }), 'a[b][c]=d&a[b][e]=f', 'no array, indices');
|
||||
st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'comma' }), 'a[b][c]=d&a[b][e]=f', 'no array, comma');
|
||||
|
||||
st.equal(qs.stringify(withArray, { encode: false }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, no arrayFormat');
|
||||
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'bracket' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, bracket');
|
||||
st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'indices' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, indices');
|
||||
st.equal(
|
||||
qs.stringify(withArray, { encode: false, arrayFormat: 'comma' }),
|
||||
'???',
|
||||
'array, comma',
|
||||
{ skip: 'TODO: figure out what this should do' }
|
||||
);
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.test('stringifies sparse arrays', function (st) {
|
||||
/* eslint no-sparse-arrays: 0 */
|
||||
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true }), 'a[1]=2&a[4]=1');
|
||||
st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true }), 'a[1][b][2][c]=1');
|
||||
st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c]=1');
|
||||
st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c][1]=1');
|
||||
|
||||
st.end();
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
+1
-1
@@ -130,7 +130,7 @@ test('isBuffer()', function (t) {
|
||||
var saferBuffer = SaferBuffer.from('abc');
|
||||
t.equal(utils.isBuffer(saferBuffer), true, 'SaferBuffer instance is a buffer');
|
||||
|
||||
var buffer = Buffer.from ? Buffer.from('abc') : new Buffer('abc');
|
||||
var buffer = Buffer.from && Buffer.alloc ? Buffer.from('abc') : new Buffer('abc');
|
||||
t.equal(utils.isBuffer(buffer), true, 'real Buffer instance is a buffer');
|
||||
t.end();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user