qs 6.6.1 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.editorconfig CHANGED
@@ -8,14 +8,10 @@ charset = utf-8
8
8
  trim_trailing_whitespace = true
9
9
  insert_final_newline = true
10
10
  max_line_length = 160
11
- quote_type = single
12
11
 
13
12
  [test/*]
14
13
  max_line_length = off
15
14
 
16
- [LICENSE.md]
17
- indent_size = off
18
-
19
15
  [*.md]
20
16
  max_line_length = off
21
17
 
@@ -32,12 +28,3 @@ indent_size = 2
32
28
  [LICENSE]
33
29
  indent_size = 2
34
30
  max_line_length = off
35
-
36
- [coverage/**/*]
37
- indent_size = off
38
- indent_style = off
39
- indent = off
40
- max_line_length = off
41
-
42
- [.nycrc]
43
- indent_style = tab
package/.eslintignore ADDED
@@ -0,0 +1 @@
1
+ dist
package/.eslintrc CHANGED
@@ -3,14 +3,10 @@
3
3
 
4
4
  "extends": "@ljharb",
5
5
 
6
- "ignorePatterns": [
7
- "dist/",
8
- ],
9
-
10
6
  "rules": {
11
7
  "complexity": 0,
12
8
  "consistent-return": 1,
13
- "func-name-matching": 0,
9
+ "func-name-matching": 0,
14
10
  "id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
15
11
  "indent": [2, 4],
16
12
  "max-lines-per-function": [2, { "max": 150 }],
@@ -19,20 +15,7 @@
19
15
  "multiline-comment-style": 0,
20
16
  "no-continue": 1,
21
17
  "no-magic-numbers": 0,
22
- "no-param-reassign": 1,
23
18
  "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
24
19
  "operator-linebreak": [2, "before"],
25
- },
26
-
27
- "overrides": [
28
- {
29
- "files": "test/**",
30
- "rules": {
31
- "max-lines-per-function": 0,
32
- "max-statements": 0,
33
- "no-extend-native": 0,
34
- "function-paren-newline": 0,
35
- },
36
- },
37
- ],
20
+ }
38
21
  }
package/CHANGELOG.md CHANGED
@@ -1,28 +1,16 @@
1
- ## **6.6.1**
2
- - [Fix] `parse`: ignore `__proto__` keys (#428)
3
- - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
4
- - [Fix] `utils.merge`: avoid a crash with a null target and an array source
5
- - [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
6
- - [Fix] correctly parse nested arrays
7
- - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
1
+ ## **6.7.0**
2
+ - [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
3
+ - [Fix] correctly parse nested arrays (#212)
4
+ - [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source, also with an array source
8
5
  - [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
9
- - [Refactor] `formats`: tiny bit of cleanup.
10
6
  - [Refactor] `utils`: `isBuffer`: small tweak; add tests
11
- - [Refactor]: `stringify`/`utils`: cache `Array.isArray`
12
- - [Refactor] `utils`: reduce observable [[Get]]s
13
7
  - [Refactor] use cached `Array.isArray`
14
8
  - [Refactor] `parse`/`stringify`: make a function to normalize the options
15
- - [readme] remove travis badge; add github actions/codecov badges; update URLs
16
- - [Docs] Clarify the need for "arrayLimit" option
17
- - [meta] fix README.md (#399)
18
- - [meta] do not publish workflow files
19
- - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
20
- - [meta] add FUNDING.yml
21
- - [meta] Fixes typo in CHANGELOG.md
22
- - [actions] backport actions from main
23
- - [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
9
+ - [Refactor] `utils`: reduce observable [[Get]]s
10
+ - [Refactor] `stringify`/`utils`: cache `Array.isArray`
24
11
  - [Tests] always use `String(x)` over `x.toString()`
25
- - [Dev Deps] backport from main
12
+ - [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
13
+ - [Tests] temporarily allow coverage to fail
26
14
 
27
15
  ## **6.6.0**
28
16
  - [New] Add support for iso-8859-1, utf8 "sentinel" and numeric entities (#268)
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2014 Nathan LaFreniere and other contributors.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * The names of any contributors may not be used to endorse or promote
12
+ products derived from this software without specific prior written
13
+ permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+
26
+ * * *
27
+
28
+ The complete list of contributors can be found at: https://github.com/hapijs/qs/graphs/contributors
package/README.md CHANGED
@@ -1,13 +1,12 @@
1
1
  # qs <sup>[![Version Badge][2]][1]</sup>
2
2
 
3
- [![github actions][actions-image]][actions-url]
4
- [![coverage][codecov-image]][codecov-url]
5
- [![dependency status][deps-svg]][deps-url]
6
- [![dev dependency status][dev-deps-svg]][dev-deps-url]
3
+ [![Build Status][3]][4]
4
+ [![dependency status][5]][6]
5
+ [![dev dependency status][7]][8]
7
6
  [![License][license-image]][license-url]
8
7
  [![Downloads][downloads-image]][downloads-url]
9
8
 
10
- [![npm badge][npm-badge-png]][package-url]
9
+ [![npm badge][11]][1]
11
10
 
12
11
  A querystring parsing and stringifying library with some added security.
13
12
 
@@ -274,6 +273,13 @@ var arraysOfObjects = qs.parse('a[][b]=c');
274
273
  assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
275
274
  ```
276
275
 
276
+ Some people use comma to join array, **qs** can parse it:
277
+ ```javascript
278
+ var arraysOfObjects = qs.parse('a=b,c', { comma: true })
279
+ assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
280
+ ```
281
+ (_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
282
+
277
283
  ### Stringifying
278
284
 
279
285
  [](#preventEval)
@@ -324,30 +330,6 @@ var decoded = qs.parse('x=z', { decoder: function (str) {
324
330
  }})
325
331
  ```
326
332
 
327
- You can encode keys and values using different logic by using the type argument provided to the encoder:
328
-
329
- ```javascript
330
- var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
331
- if (type === 'key') {
332
- return // Encoded key
333
- } else if (type === 'value') {
334
- return // Encoded value
335
- }
336
- }})
337
- ```
338
-
339
- The type argument is also provided to the decoder:
340
-
341
- ```javascript
342
- var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
343
- if (type === 'key') {
344
- return // Decoded key
345
- } else if (type === 'value') {
346
- return // Decoded value
347
- }
348
- }})
349
- ```
350
-
351
333
  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.
352
334
 
353
335
  When arrays are stringified, by default they are given explicit indices:
@@ -373,6 +355,8 @@ qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
373
355
  // 'a[]=b&a[]=c'
374
356
  qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
375
357
  // 'a=b&a=c'
358
+ qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
359
+ // 'a=b,c'
376
360
  ```
377
361
 
378
362
  When objects are stringified, by default they use bracket notation:
@@ -569,28 +553,18 @@ assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
569
553
  assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
570
554
  ```
571
555
 
572
- ## Security
573
-
574
- Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
575
-
576
- ## qs for enterprise
577
-
578
- Available as part of the Tidelift Subscription
579
-
580
- 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)
581
-
582
- [package-url]: https://npmjs.org/package/qs
583
- [npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
584
- [deps-svg]: https://david-dm.org/ljharb/qs.svg
585
- [deps-url]: https://david-dm.org/ljharb/qs
586
- [dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
587
- [dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
588
- [npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
589
- [license-image]: https://img.shields.io/npm/l/qs.svg
556
+ [1]: https://npmjs.org/package/qs
557
+ [2]: http://versionbadg.es/ljharb/qs.svg
558
+ [3]: https://api.travis-ci.org/ljharb/qs.svg
559
+ [4]: https://travis-ci.org/ljharb/qs
560
+ [5]: https://david-dm.org/ljharb/qs.svg
561
+ [6]: https://david-dm.org/ljharb/qs
562
+ [7]: https://david-dm.org/ljharb/qs/dev-status.svg
563
+ [8]: https://david-dm.org/ljharb/qs?type=dev
564
+ [9]: https://ci.testling.com/ljharb/qs.png
565
+ [10]: https://ci.testling.com/ljharb/qs
566
+ [11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
567
+ [license-image]: http://img.shields.io/npm/l/qs.svg
590
568
  [license-url]: LICENSE
591
- [downloads-image]: https://img.shields.io/npm/dm/qs.svg
592
- [downloads-url]: https://npm-stat.com/charts.html?package=qs
593
- [codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
594
- [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
595
- [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
596
- [actions-url]: https://github.com/ljharb/qs/actions
569
+ [downloads-image]: http://img.shields.io/npm/dm/qs.svg
570
+ [downloads-url]: http://npm-stat.com/charts.html?package=qs
package/dist/qs.js CHANGED
@@ -4,29 +4,21 @@
4
4
  var replace = String.prototype.replace;
5
5
  var percentTwenties = /%20/g;
6
6
 
7
- var util = require('./utils');
8
-
9
- var Format = {
7
+ module.exports = {
8
+ 'default': 'RFC3986',
9
+ formatters: {
10
+ RFC1738: function (value) {
11
+ return replace.call(value, percentTwenties, '+');
12
+ },
13
+ RFC3986: function (value) {
14
+ return value;
15
+ }
16
+ },
10
17
  RFC1738: 'RFC1738',
11
18
  RFC3986: 'RFC3986'
12
19
  };
13
20
 
14
- module.exports = util.assign(
15
- {
16
- 'default': Format.RFC3986,
17
- formatters: {
18
- RFC1738: function (value) {
19
- return replace.call(value, percentTwenties, '+');
20
- },
21
- RFC3986: function (value) {
22
- return String(value);
23
- }
24
- }
25
- },
26
- Format
27
- );
28
-
29
- },{"./utils":5}],2:[function(require,module,exports){
21
+ },{}],2:[function(require,module,exports){
30
22
  'use strict';
31
23
 
32
24
  var stringify = require('./stringify');
@@ -52,6 +44,7 @@ var defaults = {
52
44
  arrayLimit: 20,
53
45
  charset: 'utf-8',
54
46
  charsetSentinel: false,
47
+ comma: false,
55
48
  decoder: utils.decode,
56
49
  delimiter: '&',
57
50
  depth: 5,
@@ -123,6 +116,11 @@ var parseValues = function parseQueryStringValues(str, options) {
123
116
  if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
124
117
  val = interpretNumericEntities(val);
125
118
  }
119
+
120
+ if (val && options.comma && val.indexOf(',') > -1) {
121
+ val = val.split(',');
122
+ }
123
+
126
124
  if (has.call(obj, key)) {
127
125
  obj[key] = utils.combine(obj[key], val);
128
126
  } else {
@@ -157,7 +155,7 @@ var parseObject = function (chain, val, options) {
157
155
  ) {
158
156
  obj = [];
159
157
  obj[index] = leaf;
160
- } else if (cleanRoot !== '__proto__') {
158
+ } else {
161
159
  obj[cleanRoot] = leaf;
162
160
  }
163
161
  }
@@ -242,6 +240,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
242
240
  arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
243
241
  charset: charset,
244
242
  charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
243
+ comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
245
244
  decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
246
245
  delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
247
246
  depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth,
@@ -284,13 +283,14 @@ var formats = require('./formats');
284
283
  var has = Object.prototype.hasOwnProperty;
285
284
 
286
285
  var arrayPrefixGenerators = {
287
- brackets: function brackets(prefix) {
286
+ brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
288
287
  return prefix + '[]';
289
288
  },
290
- indices: function indices(prefix, key) {
289
+ comma: 'comma',
290
+ indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
291
291
  return prefix + '[' + key + ']';
292
292
  },
293
- repeat: function repeat(prefix) {
293
+ repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
294
294
  return prefix;
295
295
  }
296
296
  };
@@ -303,7 +303,6 @@ var pushToArray = function (arr, valueOrArray) {
303
303
 
304
304
  var toISO = Date.prototype.toISOString;
305
305
 
306
- var defaultFormat = formats['default'];
307
306
  var defaults = {
308
307
  addQueryPrefix: false,
309
308
  allowDots: false,
@@ -313,18 +312,17 @@ var defaults = {
313
312
  encode: true,
314
313
  encoder: utils.encode,
315
314
  encodeValuesOnly: false,
316
- format: defaultFormat,
317
- formatter: formats.formatters[defaultFormat],
315
+ formatter: formats.formatters[formats['default']],
318
316
  // deprecated
319
317
  indices: false,
320
- serializeDate: function serializeDate(date) {
318
+ serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
321
319
  return toISO.call(date);
322
320
  },
323
321
  skipNulls: false,
324
322
  strictNullHandling: false
325
323
  };
326
324
 
327
- var stringify = function stringify(
325
+ var stringify = function stringify( // eslint-disable-line func-name-matching
328
326
  object,
329
327
  prefix,
330
328
  generateArrayPrefix,
@@ -344,6 +342,8 @@ var stringify = function stringify(
344
342
  obj = filter(prefix, obj);
345
343
  } else if (obj instanceof Date) {
346
344
  obj = serializeDate(obj);
345
+ } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
346
+ obj = obj.join(',');
347
347
  }
348
348
 
349
349
  if (obj === null) {
@@ -386,7 +386,7 @@ var stringify = function stringify(
386
386
  if (isArray(obj)) {
387
387
  pushToArray(values, stringify(
388
388
  obj[key],
389
- generateArrayPrefix(prefix, key),
389
+ typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
390
390
  generateArrayPrefix,
391
391
  strictNullHandling,
392
392
  skipNulls,
@@ -426,7 +426,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
426
426
  return defaults;
427
427
  }
428
428
 
429
- if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
429
+ if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
430
430
  throw new TypeError('Encoder has to be a function.');
431
431
  }
432
432
 
package/lib/formats.js CHANGED
@@ -3,24 +3,16 @@
3
3
  var replace = String.prototype.replace;
4
4
  var percentTwenties = /%20/g;
5
5
 
6
- var util = require('./utils');
7
-
8
- var Format = {
6
+ module.exports = {
7
+ 'default': 'RFC3986',
8
+ formatters: {
9
+ RFC1738: function (value) {
10
+ return replace.call(value, percentTwenties, '+');
11
+ },
12
+ RFC3986: function (value) {
13
+ return value;
14
+ }
15
+ },
9
16
  RFC1738: 'RFC1738',
10
17
  RFC3986: 'RFC3986'
11
18
  };
12
-
13
- module.exports = util.assign(
14
- {
15
- 'default': Format.RFC3986,
16
- formatters: {
17
- RFC1738: function (value) {
18
- return replace.call(value, percentTwenties, '+');
19
- },
20
- RFC3986: function (value) {
21
- return String(value);
22
- }
23
- }
24
- },
25
- Format
26
- );
package/lib/parse.js CHANGED
@@ -10,6 +10,7 @@ var defaults = {
10
10
  arrayLimit: 20,
11
11
  charset: 'utf-8',
12
12
  charsetSentinel: false,
13
+ comma: false,
13
14
  decoder: utils.decode,
14
15
  delimiter: '&',
15
16
  depth: 5,
@@ -81,6 +82,11 @@ var parseValues = function parseQueryStringValues(str, options) {
81
82
  if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
82
83
  val = interpretNumericEntities(val);
83
84
  }
85
+
86
+ if (val && options.comma && val.indexOf(',') > -1) {
87
+ val = val.split(',');
88
+ }
89
+
84
90
  if (has.call(obj, key)) {
85
91
  obj[key] = utils.combine(obj[key], val);
86
92
  } else {
@@ -115,7 +121,7 @@ var parseObject = function (chain, val, options) {
115
121
  ) {
116
122
  obj = [];
117
123
  obj[index] = leaf;
118
- } else if (cleanRoot !== '__proto__') {
124
+ } else {
119
125
  obj[cleanRoot] = leaf;
120
126
  }
121
127
  }
@@ -200,6 +206,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
200
206
  arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
201
207
  charset: charset,
202
208
  charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
209
+ comma: typeof opts.comma === 'boolean' ? opts.comma : defaults.comma,
203
210
  decoder: typeof opts.decoder === 'function' ? opts.decoder : defaults.decoder,
204
211
  delimiter: typeof opts.delimiter === 'string' || utils.isRegExp(opts.delimiter) ? opts.delimiter : defaults.delimiter,
205
212
  depth: typeof opts.depth === 'number' ? opts.depth : defaults.depth,
package/lib/stringify.js CHANGED
@@ -5,13 +5,14 @@ var formats = require('./formats');
5
5
  var has = Object.prototype.hasOwnProperty;
6
6
 
7
7
  var arrayPrefixGenerators = {
8
- brackets: function brackets(prefix) {
8
+ brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
9
9
  return prefix + '[]';
10
10
  },
11
- indices: function indices(prefix, key) {
11
+ comma: 'comma',
12
+ indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
12
13
  return prefix + '[' + key + ']';
13
14
  },
14
- repeat: function repeat(prefix) {
15
+ repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
15
16
  return prefix;
16
17
  }
17
18
  };
@@ -24,7 +25,6 @@ var pushToArray = function (arr, valueOrArray) {
24
25
 
25
26
  var toISO = Date.prototype.toISOString;
26
27
 
27
- var defaultFormat = formats['default'];
28
28
  var defaults = {
29
29
  addQueryPrefix: false,
30
30
  allowDots: false,
@@ -34,18 +34,17 @@ var defaults = {
34
34
  encode: true,
35
35
  encoder: utils.encode,
36
36
  encodeValuesOnly: false,
37
- format: defaultFormat,
38
- formatter: formats.formatters[defaultFormat],
37
+ formatter: formats.formatters[formats['default']],
39
38
  // deprecated
40
39
  indices: false,
41
- serializeDate: function serializeDate(date) {
40
+ serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
42
41
  return toISO.call(date);
43
42
  },
44
43
  skipNulls: false,
45
44
  strictNullHandling: false
46
45
  };
47
46
 
48
- var stringify = function stringify(
47
+ var stringify = function stringify( // eslint-disable-line func-name-matching
49
48
  object,
50
49
  prefix,
51
50
  generateArrayPrefix,
@@ -65,6 +64,8 @@ var stringify = function stringify(
65
64
  obj = filter(prefix, obj);
66
65
  } else if (obj instanceof Date) {
67
66
  obj = serializeDate(obj);
67
+ } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
68
+ obj = obj.join(',');
68
69
  }
69
70
 
70
71
  if (obj === null) {
@@ -107,7 +108,7 @@ var stringify = function stringify(
107
108
  if (isArray(obj)) {
108
109
  pushToArray(values, stringify(
109
110
  obj[key],
110
- generateArrayPrefix(prefix, key),
111
+ typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
111
112
  generateArrayPrefix,
112
113
  strictNullHandling,
113
114
  skipNulls,
@@ -147,7 +148,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
147
148
  return defaults;
148
149
  }
149
150
 
150
- if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
151
+ if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
151
152
  throw new TypeError('Encoder has to be a function.');
152
153
  }
153
154
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "qs",
3
3
  "description": "A querystring parser that supports nesting and arrays, with a depth limit",
4
4
  "homepage": "https://github.com/ljharb/qs",
5
- "version": "6.6.1",
5
+ "version": "6.7.0",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -17,39 +17,41 @@
17
17
  ],
18
18
  "keywords": [
19
19
  "querystring",
20
- "qs"
20
+ "qs",
21
+ "query",
22
+ "url",
23
+ "parse",
24
+ "stringify"
21
25
  ],
22
26
  "engines": {
23
27
  "node": ">=0.6"
24
28
  },
29
+ "dependencies": {},
25
30
  "devDependencies": {
26
- "@ljharb/eslint-config": "^20.1.0",
27
- "aud": "^1.1.5",
28
- "browserify": "^16.5.2",
29
- "eclint": "^2.8.1",
30
- "eslint": "^8.6.0",
31
+ "@ljharb/eslint-config": "^13.1.1",
32
+ "browserify": "^16.2.3",
33
+ "covert": "^1.1.1",
34
+ "editorconfig-tools": "^0.1.1",
35
+ "eslint": "^5.15.3",
31
36
  "evalmd": "^0.0.17",
32
37
  "for-each": "^0.3.3",
33
38
  "iconv-lite": "^0.4.24",
34
- "in-publish": "^2.0.1",
35
39
  "mkdirp": "^0.5.1",
36
- "nyc": "^10.3.2",
37
- "object-inspect": "^1.12.0",
40
+ "object-inspect": "^1.6.0",
38
41
  "qs-iconv": "^1.0.4",
39
- "safe-publish-latest": "^2.0.0",
42
+ "safe-publish-latest": "^1.1.2",
40
43
  "safer-buffer": "^2.1.2",
41
- "tape": "^4.14.0"
44
+ "tape": "^4.10.1"
42
45
  },
43
46
  "scripts": {
44
- "prepublishOnly": "safe-publish-latest && npm run dist",
45
- "prepublish": "not-in-publish || npm run prepublishOnly",
47
+ "prepublish": "safe-publish-latest && npm run dist",
46
48
  "pretest": "npm run --silent readme && npm run --silent lint",
47
- "test": "npm run --silent tests-only",
48
- "tests-only": "nyc tape 'test/**/*.js'",
49
- "posttest": "aud --production",
49
+ "test": "npm run --silent coverage",
50
+ "tests-only": "node test",
50
51
  "readme": "evalmd README.md",
51
- "postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
52
- "lint": "eslint --ext=js,mjs .",
52
+ "postlint": "editorconfig-tools check * lib/* test/*",
53
+ "lint": "eslint lib/*.js test/*.js",
54
+ "coverage": "covert test",
53
55
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
54
56
  },
55
57
  "license": "BSD-3-Clause"
package/test/.eslintrc ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "rules": {
3
+ "array-bracket-newline": 0,
4
+ "array-element-newline": 0,
5
+ "consistent-return": 2,
6
+ "function-paren-newline": 0,
7
+ "max-lines": 0,
8
+ "max-lines-per-function": 0,
9
+ "max-nested-callbacks": [2, 3],
10
+ "max-statements": 0,
11
+ "no-buffer-constructor": 0,
12
+ "no-extend-native": 0,
13
+ "no-magic-numbers": 0,
14
+ "object-curly-newline": 0,
15
+ "sort-keys": 0
16
+ }
17
+ }
package/test/parse.js CHANGED
@@ -347,6 +347,15 @@ test('parse()', function (t) {
347
347
  st.end();
348
348
  });
349
349
 
350
+ t.test('parses string with comma as array divider', function (st) {
351
+ st.deepEqual(qs.parse('foo=bar,tee', { comma: true }), { foo: ['bar', 'tee'] });
352
+ st.deepEqual(qs.parse('foo[bar]=coffee,tee', { comma: true }), { foo: { bar: ['coffee', 'tee'] } });
353
+ st.deepEqual(qs.parse('foo=', { comma: true }), { foo: '' });
354
+ st.deepEqual(qs.parse('foo', { comma: true }), { foo: '' });
355
+ st.deepEqual(qs.parse('foo', { comma: true, strictNullHandling: true }), { foo: null });
356
+ st.end();
357
+ });
358
+
350
359
  t.test('parses an object in dot notation', function (st) {
351
360
  var input = {
352
361
  'user.name': { 'pop[bob]': 3 },
@@ -530,66 +539,6 @@ test('parse()', function (t) {
530
539
  st.end();
531
540
  });
532
541
 
533
- t.test('dunder proto is ignored', function (st) {
534
- var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
535
- var result = qs.parse(payload, { allowPrototypes: true });
536
-
537
- st.deepEqual(
538
- result,
539
- {
540
- categories: {
541
- length: '42'
542
- }
543
- },
544
- 'silent [[Prototype]] payload'
545
- );
546
-
547
- var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
548
-
549
- st.deepEqual(
550
- plainResult,
551
- {
552
- __proto__: null,
553
- categories: {
554
- __proto__: null,
555
- length: '42'
556
- }
557
- },
558
- 'silent [[Prototype]] payload: plain objects'
559
- );
560
-
561
- var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
562
-
563
- st.notOk(Array.isArray(query.categories), 'is not an array');
564
- st.notOk(query.categories instanceof Array, 'is not instanceof an array');
565
- st.deepEqual(query.categories, { some: { json: 'toInject' } });
566
- st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
567
-
568
- st.deepEqual(
569
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
570
- {
571
- foo: {
572
- bar: 'stuffs'
573
- }
574
- },
575
- 'hidden values'
576
- );
577
-
578
- st.deepEqual(
579
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
580
- {
581
- __proto__: null,
582
- foo: {
583
- __proto__: null,
584
- bar: 'stuffs'
585
- }
586
- },
587
- 'hidden values: plain objects'
588
- );
589
-
590
- st.end();
591
- });
592
-
593
542
  t.test('can return null objects', { skip: !Object.create }, function (st) {
594
543
  var expected = Object.create(null);
595
544
  expected.a = Object.create(null);
package/test/stringify.js CHANGED
@@ -68,6 +68,11 @@ test('stringify()', function (t) {
68
68
  'a%5B%5D=b&a%5B%5D=c&a%5B%5D=d',
69
69
  'brackets => brackets'
70
70
  );
71
+ st.equal(
72
+ qs.stringify({ a: ['b', 'c', 'd'] }, { arrayFormat: 'comma' }),
73
+ 'a=b%2Cc%2Cd',
74
+ 'comma => comma'
75
+ );
71
76
  st.equal(
72
77
  qs.stringify({ a: ['b', 'c', 'd'] }),
73
78
  'a%5B0%5D=b&a%5B1%5D=c&a%5B2%5D=d',
@@ -94,6 +99,7 @@ test('stringify()', function (t) {
94
99
  t.test('stringifies a nested array value', function (st) {
95
100
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
96
101
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
102
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
97
103
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
98
104
  st.end();
99
105
  });
@@ -115,6 +121,14 @@ test('stringify()', function (t) {
115
121
  'a.b[]=c&a.b[]=d',
116
122
  'brackets: stringifies with dots + brackets'
117
123
  );
124
+ st.equal(
125
+ qs.stringify(
126
+ { a: { b: ['c', 'd'] } },
127
+ { allowDots: true, encode: false, arrayFormat: 'comma' }
128
+ ),
129
+ 'a.b=c,d',
130
+ 'comma: stringifies with dots + comma'
131
+ );
118
132
  st.equal(
119
133
  qs.stringify(
120
134
  { a: { b: ['c', 'd'] } },
@@ -129,12 +143,12 @@ test('stringify()', function (t) {
129
143
  t.test('stringifies an object inside an array', function (st) {
130
144
  st.equal(
131
145
  qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'indices' }),
132
- 'a%5B0%5D%5Bb%5D=c',
146
+ 'a%5B0%5D%5Bb%5D=c', // a[0][b]=c
133
147
  'indices => brackets'
134
148
  );
135
149
  st.equal(
136
150
  qs.stringify({ a: [{ b: 'c' }] }, { arrayFormat: 'brackets' }),
137
- 'a%5B%5D%5Bb%5D=c',
151
+ 'a%5B%5D%5Bb%5D=c', // a[][b]=c
138
152
  'brackets => brackets'
139
153
  );
140
154
  st.equal(
@@ -506,12 +520,6 @@ test('stringify()', function (t) {
506
520
  return String.fromCharCode(buffer.readUInt8(0) + 97);
507
521
  }
508
522
  }), 'a=b');
509
-
510
- st.equal(qs.stringify({ a: SaferBuffer.from('a b') }, {
511
- encoder: function (buffer) {
512
- return buffer;
513
- }
514
- }), 'a=a b');
515
523
  st.end();
516
524
  });
517
525
 
@@ -552,20 +560,17 @@ test('stringify()', function (t) {
552
560
  t.test('RFC 1738 spaces serialization', function (st) {
553
561
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
554
562
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
555
- st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b');
556
563
  st.end();
557
564
  });
558
565
 
559
566
  t.test('RFC 3986 spaces serialization', function (st) {
560
567
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
561
568
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
562
- st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b');
563
569
  st.end();
564
570
  });
565
571
 
566
572
  t.test('Backward compatibility to RFC 3986', function (st) {
567
573
  st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
568
- st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b');
569
574
  st.end();
570
575
  });
571
576
 
@@ -1,12 +0,0 @@
1
- # These are supported funding model platforms
2
-
3
- github: [ljharb]
4
- patreon: # Replace with a single Patreon username
5
- open_collective: # Replace with a single Open Collective username
6
- ko_fi: # Replace with a single Ko-fi username
7
- tidelift: npm/qs
8
- community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9
- liberapay: # Replace with a single Liberapay username
10
- issuehunt: # Replace with a single IssueHunt username
11
- otechie: # Replace with a single Otechie username
12
- custom: # Replace with a single custom sponsorship URL
package/.nycrc DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "all": true,
3
- "check-coverage": false,
4
- "reporter": ["text-summary", "text", "html", "json"],
5
- "lines": 86,
6
- "statements": 85.93,
7
- "functions": 82.43,
8
- "branches": 76.06,
9
- "exclude": [
10
- "coverage",
11
- "dist"
12
- ]
13
- }
package/LICENSE.md DELETED
@@ -1,29 +0,0 @@
1
- BSD 3-Clause License
2
-
3
- Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors)
4
- All rights reserved.
5
-
6
- Redistribution and use in source and binary forms, with or without
7
- modification, are permitted provided that the following conditions are met:
8
-
9
- 1. Redistributions of source code must retain the above copyright notice, this
10
- list of conditions and the following disclaimer.
11
-
12
- 2. Redistributions in binary form must reproduce the above copyright notice,
13
- this list of conditions and the following disclaimer in the documentation
14
- and/or other materials provided with the distribution.
15
-
16
- 3. Neither the name of the copyright holder nor the names of its
17
- contributors may be used to endorse or promote products derived from
18
- this software without specific prior written permission.
19
-
20
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.