qs 6.5.2 → 6.5.3

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
@@ -7,11 +7,15 @@ end_of_line = lf
7
7
  charset = utf-8
8
8
  trim_trailing_whitespace = true
9
9
  insert_final_newline = true
10
- max_line_length = 140
10
+ max_line_length = 160
11
+ quote_type = single
11
12
 
12
13
  [test/*]
13
14
  max_line_length = off
14
15
 
16
+ [LICENSE.md]
17
+ indent_size = off
18
+
15
19
  [*.md]
16
20
  max_line_length = off
17
21
 
@@ -28,3 +32,12 @@ indent_size = 2
28
32
  [LICENSE]
29
33
  indent_size = 2
30
34
  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/.eslintrc CHANGED
@@ -3,17 +3,35 @@
3
3
 
4
4
  "extends": "@ljharb",
5
5
 
6
+ "ignorePatterns": [
7
+ "dist/",
8
+ ],
9
+
6
10
  "rules": {
7
11
  "complexity": 0,
8
12
  "consistent-return": 1,
9
- "func-name-matching": 0,
13
+ "func-name-matching": 0,
10
14
  "id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
11
15
  "indent": [2, 4],
16
+ "max-lines-per-function": 0,
12
17
  "max-params": [2, 12],
13
18
  "max-statements": [2, 45],
19
+ "multiline-comment-style": 0,
14
20
  "no-continue": 1,
15
21
  "no-magic-numbers": 0,
22
+ "no-param-reassign": 1,
16
23
  "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
17
- "operator-linebreak": [2, "before"],
18
- }
24
+ },
25
+
26
+ "overrides": [
27
+ {
28
+ "files": "test/**",
29
+ "rules": {
30
+ "max-lines-per-function": 0,
31
+ "max-statements": 0,
32
+ "no-extend-native": 0,
33
+ "function-paren-newline": 0,
34
+ },
35
+ },
36
+ ],
19
37
  }
@@ -0,0 +1,12 @@
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 ADDED
@@ -0,0 +1,13 @@
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/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ ## **6.5.3**
2
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
3
+ - [Fix]` `utils.merge`: avoid a crash with a null target and a truthy non-array source
4
+ - [Fix] correctly parse nested arrays
5
+ - [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
6
+ - [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
7
+ - [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
8
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
9
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
10
+ - [Refactor] `utils`: reduce observable [[Get]]s
11
+ - [Refactor] use cached `Array.isArray`
12
+ - [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
13
+ - [Refactor] `parse`: only need to reassign the var once
14
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
15
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
16
+ - [Docs] Clean up license text so it’s properly detected as BSD-3-Clause
17
+ - [Docs] Clarify the need for "arrayLimit" option
18
+ - [meta] fix README.md (#399)
19
+ - [meta] add FUNDING.yml
20
+ - [actions] backport actions from main
21
+ - [Tests] always use `String(x)` over `x.toString()`
22
+ - [Tests] remove nonexistent tape option
23
+ - [Dev Deps] backport from main
24
+
1
25
  ## **6.5.2**
2
26
  - [Fix] use `safer-buffer` instead of `Buffer` constructor
3
27
  - [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230)
package/LICENSE.md ADDED
@@ -0,0 +1,29 @@
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.
package/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # qs <sup>[![Version Badge][2]][1]</sup>
2
2
 
3
- [![Build Status][3]][4]
4
- [![dependency status][5]][6]
5
- [![dev dependency status][7]][8]
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]
6
7
  [![License][license-image]][license-url]
7
8
  [![Downloads][downloads-image]][downloads-url]
8
9
 
9
- [![npm badge][11]][1]
10
+ [![npm badge][npm-badge-png]][package-url]
10
11
 
11
12
  A querystring parsing and stringifying library with some added security.
12
13
 
@@ -182,7 +183,7 @@ assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
182
183
  ```
183
184
 
184
185
  **qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
185
- instead be converted to an object with the index as the key:
186
+ instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
186
187
 
187
188
  ```javascript
188
189
  var withMaxIndex = qs.parse('a[100]=b');
@@ -267,6 +268,30 @@ var decoded = qs.parse('x=z', { decoder: function (str) {
267
268
  }})
268
269
  ```
269
270
 
271
+ You can encode keys and values using different logic by using the type argument provided to the encoder:
272
+
273
+ ```javascript
274
+ var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
275
+ if (type === 'key') {
276
+ return // Encoded key
277
+ } else if (type === 'value') {
278
+ return // Encoded value
279
+ }
280
+ }})
281
+ ```
282
+
283
+ The type argument is also provided to the decoder:
284
+
285
+ ```javascript
286
+ var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
287
+ if (type === 'key') {
288
+ return // Decoded key
289
+ } else if (type === 'value') {
290
+ return // Decoded value
291
+ }
292
+ }})
293
+ ```
294
+
270
295
  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.
271
296
 
272
297
  When arrays are stringified, by default they are given explicit indices:
@@ -458,18 +483,28 @@ assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
458
483
  assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
459
484
  ```
460
485
 
461
- [1]: https://npmjs.org/package/qs
462
- [2]: http://versionbadg.es/ljharb/qs.svg
463
- [3]: https://api.travis-ci.org/ljharb/qs.svg
464
- [4]: https://travis-ci.org/ljharb/qs
465
- [5]: https://david-dm.org/ljharb/qs.svg
466
- [6]: https://david-dm.org/ljharb/qs
467
- [7]: https://david-dm.org/ljharb/qs/dev-status.svg
468
- [8]: https://david-dm.org/ljharb/qs?type=dev
469
- [9]: https://ci.testling.com/ljharb/qs.png
470
- [10]: https://ci.testling.com/ljharb/qs
471
- [11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
472
- [license-image]: http://img.shields.io/npm/l/qs.svg
486
+ ## Security
487
+
488
+ Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
489
+
490
+ ## qs for enterprise
491
+
492
+ Available as part of the Tidelift Subscription
493
+
494
+ 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)
495
+
496
+ [package-url]: https://npmjs.org/package/qs
497
+ [npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
498
+ [deps-svg]: https://david-dm.org/ljharb/qs.svg
499
+ [deps-url]: https://david-dm.org/ljharb/qs
500
+ [dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
501
+ [dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
502
+ [npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
503
+ [license-image]: https://img.shields.io/npm/l/qs.svg
473
504
  [license-url]: LICENSE
474
- [downloads-image]: http://img.shields.io/npm/dm/qs.svg
475
- [downloads-url]: http://npm-stat.com/charts.html?package=qs
505
+ [downloads-image]: https://img.shields.io/npm/dm/qs.svg
506
+ [downloads-url]: https://npm-stat.com/charts.html?package=qs
507
+ [codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
508
+ [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
509
+ [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
510
+ [actions-url]: https://github.com/ljharb/qs/actions
package/bower.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "qs",
3
+ "main": "dist/qs.js",
4
+ "homepage": "https://github.com/hapijs/qs",
5
+ "authors": [
6
+ "Nathan LaFreniere <quitlahok@gmail.com>"
7
+ ],
8
+ "description": "A querystring parser that supports nesting and arrays, with a depth limit",
9
+ "keywords": [
10
+ "querystring",
11
+ "qs"
12
+ ],
13
+ "license": "BSD-3-Clause",
14
+ "ignore": [
15
+ "**/.*",
16
+ "node_modules",
17
+ "bower_components",
18
+ "test",
19
+ "tests"
20
+ ]
21
+ }
package/component.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "qs",
3
+ "repository": "ljharb/qs",
4
+ "description": "query-string parser / stringifier with nesting support",
5
+ "version": "6.5.3",
6
+ "keywords": ["querystring", "query", "parser"],
7
+ "main": "lib/index.js",
8
+ "scripts": [
9
+ "lib/index.js",
10
+ "lib/parse.js",
11
+ "lib/stringify.js",
12
+ "lib/utils.js"
13
+ ],
14
+ "license": "BSD-3-Clause"
15
+ }
package/dist/qs.js CHANGED
@@ -11,7 +11,7 @@ module.exports = {
11
11
  return replace.call(value, percentTwenties, '+');
12
12
  },
13
13
  RFC3986: function (value) {
14
- return value;
14
+ return String(value);
15
15
  }
16
16
  },
17
17
  RFC1738: 'RFC1738',
@@ -87,14 +87,15 @@ var parseObject = function (chain, val, options) {
87
87
  var obj;
88
88
  var root = chain[i];
89
89
 
90
- if (root === '[]') {
91
- obj = [];
92
- obj = obj.concat(leaf);
90
+ if (root === '[]' && options.parseArrays) {
91
+ obj = [].concat(leaf);
93
92
  } else {
94
93
  obj = options.plainObjects ? Object.create(null) : {};
95
94
  var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
96
95
  var index = parseInt(cleanRoot, 10);
97
- if (
96
+ if (!options.parseArrays && cleanRoot === '') {
97
+ obj = { 0: leaf };
98
+ } else if (
98
99
  !isNaN(index)
99
100
  && root !== cleanRoot
100
101
  && String(index) === cleanRoot
@@ -103,7 +104,7 @@ var parseObject = function (chain, val, options) {
103
104
  ) {
104
105
  obj = [];
105
106
  obj[index] = leaf;
106
- } else {
107
+ } else if (cleanRoot !== '__proto__') {
107
108
  obj[cleanRoot] = leaf;
108
109
  }
109
110
  }
@@ -214,17 +215,23 @@ var utils = require('./utils');
214
215
  var formats = require('./formats');
215
216
 
216
217
  var arrayPrefixGenerators = {
217
- brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
218
+ brackets: function brackets(prefix) {
218
219
  return prefix + '[]';
219
220
  },
220
- indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
221
+ indices: function indices(prefix, key) {
221
222
  return prefix + '[' + key + ']';
222
223
  },
223
- repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
224
+ repeat: function repeat(prefix) {
224
225
  return prefix;
225
226
  }
226
227
  };
227
228
 
229
+ var isArray = Array.isArray;
230
+ var push = Array.prototype.push;
231
+ var pushToArray = function (arr, valueOrArray) {
232
+ push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
233
+ };
234
+
228
235
  var toISO = Date.prototype.toISOString;
229
236
 
230
237
  var defaults = {
@@ -232,14 +239,14 @@ var defaults = {
232
239
  encode: true,
233
240
  encoder: utils.encode,
234
241
  encodeValuesOnly: false,
235
- serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
242
+ serializeDate: function serializeDate(date) {
236
243
  return toISO.call(date);
237
244
  },
238
245
  skipNulls: false,
239
246
  strictNullHandling: false
240
247
  };
241
248
 
242
- var stringify = function stringify( // eslint-disable-line func-name-matching
249
+ var stringify = function stringify(
243
250
  object,
244
251
  prefix,
245
252
  generateArrayPrefix,
@@ -258,7 +265,9 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
258
265
  obj = filter(prefix, obj);
259
266
  } else if (obj instanceof Date) {
260
267
  obj = serializeDate(obj);
261
- } else if (obj === null) {
268
+ }
269
+
270
+ if (obj === null) {
262
271
  if (strictNullHandling) {
263
272
  return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix;
264
273
  }
@@ -281,7 +290,7 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
281
290
  }
282
291
 
283
292
  var objKeys;
284
- if (Array.isArray(filter)) {
293
+ if (isArray(filter)) {
285
294
  objKeys = filter;
286
295
  } else {
287
296
  var keys = Object.keys(obj);
@@ -295,8 +304,8 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
295
304
  continue;
296
305
  }
297
306
 
298
- if (Array.isArray(obj)) {
299
- values = values.concat(stringify(
307
+ if (isArray(obj)) {
308
+ pushToArray(values, stringify(
300
309
  obj[key],
301
310
  generateArrayPrefix(prefix, key),
302
311
  generateArrayPrefix,
@@ -311,7 +320,7 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
311
320
  encodeValuesOnly
312
321
  ));
313
322
  } else {
314
- values = values.concat(stringify(
323
+ pushToArray(values, stringify(
315
324
  obj[key],
316
325
  prefix + (allowDots ? '.' + key : '[' + key + ']'),
317
326
  generateArrayPrefix,
@@ -335,7 +344,7 @@ module.exports = function (object, opts) {
335
344
  var obj = object;
336
345
  var options = opts ? utils.assign({}, opts) : {};
337
346
 
338
- if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
347
+ if (options.encoder !== null && typeof options.encoder !== 'undefined' && typeof options.encoder !== 'function') {
339
348
  throw new TypeError('Encoder has to be a function.');
340
349
  }
341
350
 
@@ -360,7 +369,7 @@ module.exports = function (object, opts) {
360
369
  if (typeof options.filter === 'function') {
361
370
  filter = options.filter;
362
371
  obj = filter('', obj);
363
- } else if (Array.isArray(options.filter)) {
372
+ } else if (isArray(options.filter)) {
364
373
  filter = options.filter;
365
374
  objKeys = filter;
366
375
  }
@@ -396,8 +405,7 @@ module.exports = function (object, opts) {
396
405
  if (skipNulls && obj[key] === null) {
397
406
  continue;
398
407
  }
399
-
400
- keys = keys.concat(stringify(
408
+ pushToArray(keys, stringify(
401
409
  obj[key],
402
410
  key,
403
411
  generateArrayPrefix,
@@ -475,8 +483,8 @@ var merge = function merge(target, source, options) {
475
483
  if (typeof source !== 'object') {
476
484
  if (Array.isArray(target)) {
477
485
  target.push(source);
478
- } else if (typeof target === 'object') {
479
- if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
486
+ } else if (target && typeof target === 'object') {
487
+ if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
480
488
  target[source] = true;
481
489
  }
482
490
  } else {
@@ -486,7 +494,7 @@ var merge = function merge(target, source, options) {
486
494
  return target;
487
495
  }
488
496
 
489
- if (typeof target !== 'object') {
497
+ if (!target || typeof target !== 'object') {
490
498
  return [target].concat(source);
491
499
  }
492
500
 
@@ -498,8 +506,9 @@ var merge = function merge(target, source, options) {
498
506
  if (Array.isArray(target) && Array.isArray(source)) {
499
507
  source.forEach(function (item, i) {
500
508
  if (has.call(target, i)) {
501
- if (target[i] && typeof target[i] === 'object') {
502
- target[i] = merge(target[i], item, options);
509
+ var targetItem = target[i];
510
+ if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
511
+ target[i] = merge(targetItem, item, options);
503
512
  } else {
504
513
  target.push(item);
505
514
  }
@@ -580,6 +589,7 @@ var encode = function encode(str) {
580
589
 
581
590
  i += 1;
582
591
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
592
+ /* eslint operator-linebreak: [2, "before"] */
583
593
  out += hexTable[0xF0 | (c >> 18)]
584
594
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
585
595
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
package/lib/formats.js CHANGED
@@ -10,7 +10,7 @@ module.exports = {
10
10
  return replace.call(value, percentTwenties, '+');
11
11
  },
12
12
  RFC3986: function (value) {
13
- return value;
13
+ return String(value);
14
14
  }
15
15
  },
16
16
  RFC1738: 'RFC1738',
package/lib/parse.js CHANGED
@@ -53,14 +53,15 @@ var parseObject = function (chain, val, options) {
53
53
  var obj;
54
54
  var root = chain[i];
55
55
 
56
- if (root === '[]') {
57
- obj = [];
58
- obj = obj.concat(leaf);
56
+ if (root === '[]' && options.parseArrays) {
57
+ obj = [].concat(leaf);
59
58
  } else {
60
59
  obj = options.plainObjects ? Object.create(null) : {};
61
60
  var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
62
61
  var index = parseInt(cleanRoot, 10);
63
- if (
62
+ if (!options.parseArrays && cleanRoot === '') {
63
+ obj = { 0: leaf };
64
+ } else if (
64
65
  !isNaN(index)
65
66
  && root !== cleanRoot
66
67
  && String(index) === cleanRoot
@@ -69,7 +70,7 @@ var parseObject = function (chain, val, options) {
69
70
  ) {
70
71
  obj = [];
71
72
  obj[index] = leaf;
72
- } else {
73
+ } else if (cleanRoot !== '__proto__') {
73
74
  obj[cleanRoot] = leaf;
74
75
  }
75
76
  }
package/lib/stringify.js CHANGED
@@ -4,17 +4,23 @@ var utils = require('./utils');
4
4
  var formats = require('./formats');
5
5
 
6
6
  var arrayPrefixGenerators = {
7
- brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
7
+ brackets: function brackets(prefix) {
8
8
  return prefix + '[]';
9
9
  },
10
- indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
10
+ indices: function indices(prefix, key) {
11
11
  return prefix + '[' + key + ']';
12
12
  },
13
- repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
13
+ repeat: function repeat(prefix) {
14
14
  return prefix;
15
15
  }
16
16
  };
17
17
 
18
+ var isArray = Array.isArray;
19
+ var push = Array.prototype.push;
20
+ var pushToArray = function (arr, valueOrArray) {
21
+ push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
22
+ };
23
+
18
24
  var toISO = Date.prototype.toISOString;
19
25
 
20
26
  var defaults = {
@@ -22,14 +28,14 @@ var defaults = {
22
28
  encode: true,
23
29
  encoder: utils.encode,
24
30
  encodeValuesOnly: false,
25
- serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
31
+ serializeDate: function serializeDate(date) {
26
32
  return toISO.call(date);
27
33
  },
28
34
  skipNulls: false,
29
35
  strictNullHandling: false
30
36
  };
31
37
 
32
- var stringify = function stringify( // eslint-disable-line func-name-matching
38
+ var stringify = function stringify(
33
39
  object,
34
40
  prefix,
35
41
  generateArrayPrefix,
@@ -48,7 +54,9 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
48
54
  obj = filter(prefix, obj);
49
55
  } else if (obj instanceof Date) {
50
56
  obj = serializeDate(obj);
51
- } else if (obj === null) {
57
+ }
58
+
59
+ if (obj === null) {
52
60
  if (strictNullHandling) {
53
61
  return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix;
54
62
  }
@@ -71,7 +79,7 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
71
79
  }
72
80
 
73
81
  var objKeys;
74
- if (Array.isArray(filter)) {
82
+ if (isArray(filter)) {
75
83
  objKeys = filter;
76
84
  } else {
77
85
  var keys = Object.keys(obj);
@@ -85,8 +93,8 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
85
93
  continue;
86
94
  }
87
95
 
88
- if (Array.isArray(obj)) {
89
- values = values.concat(stringify(
96
+ if (isArray(obj)) {
97
+ pushToArray(values, stringify(
90
98
  obj[key],
91
99
  generateArrayPrefix(prefix, key),
92
100
  generateArrayPrefix,
@@ -101,7 +109,7 @@ var stringify = function stringify( // eslint-disable-line func-name-matching
101
109
  encodeValuesOnly
102
110
  ));
103
111
  } else {
104
- values = values.concat(stringify(
112
+ pushToArray(values, stringify(
105
113
  obj[key],
106
114
  prefix + (allowDots ? '.' + key : '[' + key + ']'),
107
115
  generateArrayPrefix,
@@ -125,7 +133,7 @@ module.exports = function (object, opts) {
125
133
  var obj = object;
126
134
  var options = opts ? utils.assign({}, opts) : {};
127
135
 
128
- if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
136
+ if (options.encoder !== null && typeof options.encoder !== 'undefined' && typeof options.encoder !== 'function') {
129
137
  throw new TypeError('Encoder has to be a function.');
130
138
  }
131
139
 
@@ -150,7 +158,7 @@ module.exports = function (object, opts) {
150
158
  if (typeof options.filter === 'function') {
151
159
  filter = options.filter;
152
160
  obj = filter('', obj);
153
- } else if (Array.isArray(options.filter)) {
161
+ } else if (isArray(options.filter)) {
154
162
  filter = options.filter;
155
163
  objKeys = filter;
156
164
  }
@@ -186,8 +194,7 @@ module.exports = function (object, opts) {
186
194
  if (skipNulls && obj[key] === null) {
187
195
  continue;
188
196
  }
189
-
190
- keys = keys.concat(stringify(
197
+ pushToArray(keys, stringify(
191
198
  obj[key],
192
199
  key,
193
200
  generateArrayPrefix,
package/lib/utils.js CHANGED
@@ -53,8 +53,8 @@ var merge = function merge(target, source, options) {
53
53
  if (typeof source !== 'object') {
54
54
  if (Array.isArray(target)) {
55
55
  target.push(source);
56
- } else if (typeof target === 'object') {
57
- if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
56
+ } else if (target && typeof target === 'object') {
57
+ if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
58
58
  target[source] = true;
59
59
  }
60
60
  } else {
@@ -64,7 +64,7 @@ var merge = function merge(target, source, options) {
64
64
  return target;
65
65
  }
66
66
 
67
- if (typeof target !== 'object') {
67
+ if (!target || typeof target !== 'object') {
68
68
  return [target].concat(source);
69
69
  }
70
70
 
@@ -76,8 +76,9 @@ var merge = function merge(target, source, options) {
76
76
  if (Array.isArray(target) && Array.isArray(source)) {
77
77
  source.forEach(function (item, i) {
78
78
  if (has.call(target, i)) {
79
- if (target[i] && typeof target[i] === 'object') {
80
- target[i] = merge(target[i], item, options);
79
+ var targetItem = target[i];
80
+ if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
81
+ target[i] = merge(targetItem, item, options);
81
82
  } else {
82
83
  target.push(item);
83
84
  }
@@ -158,6 +159,7 @@ var encode = function encode(str) {
158
159
 
159
160
  i += 1;
160
161
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
162
+ /* eslint operator-linebreak: [2, "before"] */
161
163
  out += hexTable[0xF0 | (c >> 18)]
162
164
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
163
165
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
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.5.2",
5
+ "version": "6.5.3",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -22,30 +22,32 @@
22
22
  "engines": {
23
23
  "node": ">=0.6"
24
24
  },
25
- "dependencies": {},
26
25
  "devDependencies": {
27
- "@ljharb/eslint-config": "^12.2.1",
28
- "browserify": "^16.2.0",
29
- "covert": "^1.1.0",
30
- "editorconfig-tools": "^0.1.1",
31
- "eslint": "^4.19.1",
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",
32
31
  "evalmd": "^0.0.17",
33
- "iconv-lite": "^0.4.21",
32
+ "iconv-lite": "^0.4.24",
33
+ "in-publish": "^2.0.1",
34
34
  "mkdirp": "^0.5.1",
35
+ "nyc": "^10.3.2",
35
36
  "qs-iconv": "^1.0.4",
36
- "safe-publish-latest": "^1.1.1",
37
+ "safe-publish-latest": "^2.0.0",
37
38
  "safer-buffer": "^2.1.2",
38
- "tape": "^4.9.0"
39
+ "tape": "^5.4.0"
39
40
  },
40
41
  "scripts": {
41
- "prepublish": "safe-publish-latest && npm run dist",
42
+ "prepublishOnly": "safe-publish-latest && npm run dist",
43
+ "prepublish": "not-in-publish || npm run prepublishOnly",
42
44
  "pretest": "npm run --silent readme && npm run --silent lint",
43
- "test": "npm run --silent coverage",
44
- "tests-only": "node test",
45
+ "test": "npm run --silent tests-only",
46
+ "tests-only": "nyc tape 'test/**/*.js'",
47
+ "posttest": "aud --production",
45
48
  "readme": "evalmd README.md",
46
- "prelint": "editorconfig-tools check * lib/* test/*",
47
- "lint": "eslint lib/*.js test/*.js",
48
- "coverage": "covert test",
49
+ "postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
50
+ "lint": "eslint --ext=js,mjs .",
49
51
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
50
52
  },
51
53
  "license": "BSD-3-Clause"
package/test/parse.js CHANGED
@@ -237,6 +237,14 @@ test('parse()', function (t) {
237
237
  st.end();
238
238
  });
239
239
 
240
+ t.test('parses jquery-param strings', function (st) {
241
+ // readable = 'filter[0][]=int1&filter[0][]==&filter[0][]=77&filter[]=and&filter[2][]=int2&filter[2][]==&filter[2][]=8'
242
+ var encoded = 'filter%5B0%5D%5B%5D=int1&filter%5B0%5D%5B%5D=%3D&filter%5B0%5D%5B%5D=77&filter%5B%5D=and&filter%5B2%5D%5B%5D=int2&filter%5B2%5D%5B%5D=%3D&filter%5B2%5D%5B%5D=8';
243
+ var expected = { filter: [['int1', '=', '77'], 'and', ['int2', '=', '8']] };
244
+ st.deepEqual(qs.parse(encoded), expected);
245
+ st.end();
246
+ });
247
+
240
248
  t.test('continues parsing when no parent is found', function (st) {
241
249
  st.deepEqual(qs.parse('[]=&a=b'), { 0: '', a: 'b' });
242
250
  st.deepEqual(qs.parse('[]&a=b', { strictNullHandling: true }), { 0: null, a: 'b' });
@@ -257,7 +265,7 @@ test('parse()', function (t) {
257
265
  st.end();
258
266
  });
259
267
 
260
- t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) {
268
+ t.test('should not throw when a native prototype has an enumerable property', function (st) {
261
269
  Object.prototype.crash = '';
262
270
  Array.prototype.crash = '';
263
271
  st.doesNotThrow(qs.parse.bind(null, 'a=b'));
@@ -302,7 +310,14 @@ test('parse()', function (t) {
302
310
  });
303
311
 
304
312
  t.test('allows disabling array parsing', function (st) {
305
- st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { 0: 'b', 1: 'c' } });
313
+ var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
314
+ st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
315
+ st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
316
+
317
+ var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
318
+ st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
319
+ st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
320
+
306
321
  st.end();
307
322
  });
308
323
 
@@ -508,13 +523,73 @@ test('parse()', function (t) {
508
523
 
509
524
  st.deepEqual(
510
525
  qs.parse('a[b]=c&a=toString', { plainObjects: true }),
511
- { a: { b: 'c', toString: true } },
526
+ { __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
512
527
  'can overwrite prototype with plainObjects true'
513
528
  );
514
529
 
515
530
  st.end();
516
531
  });
517
532
 
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
+
518
593
  t.test('can return null objects', { skip: !Object.create }, function (st) {
519
594
  var expected = Object.create(null);
520
595
  expected.a = Object.create(null);
@@ -540,7 +615,7 @@ test('parse()', function (t) {
540
615
  result.push(parseInt(parts[1], 16));
541
616
  parts = reg.exec(str);
542
617
  }
543
- return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString();
618
+ return String(iconv.decode(SaferBuffer.from(result), 'shift_jis'));
544
619
  }
545
620
  }), { 県: '大阪府' });
546
621
  st.end();
package/test/stringify.js CHANGED
@@ -19,6 +19,15 @@ test('stringify()', function (t) {
19
19
  st.end();
20
20
  });
21
21
 
22
+ t.test('stringifies falsy values', function (st) {
23
+ st.equal(qs.stringify(undefined), '');
24
+ st.equal(qs.stringify(null), '');
25
+ st.equal(qs.stringify(null, { strictNullHandling: true }), '');
26
+ st.equal(qs.stringify(false), '');
27
+ st.equal(qs.stringify(0), '');
28
+ st.end();
29
+ });
30
+
22
31
  t.test('adds query prefix', function (st) {
23
32
  st.equal(qs.stringify({ a: 'b' }, { addQueryPrefix: true }), '?a=b');
24
33
  st.end();
@@ -29,6 +38,13 @@ test('stringify()', function (t) {
29
38
  st.end();
30
39
  });
31
40
 
41
+ t.test('stringifies nested falsy values', function (st) {
42
+ st.equal(qs.stringify({ a: { b: { c: null } } }), 'a%5Bb%5D%5Bc%5D=');
43
+ st.equal(qs.stringify({ a: { b: { c: null } } }, { strictNullHandling: true }), 'a%5Bb%5D%5Bc%5D');
44
+ st.equal(qs.stringify({ a: { b: { c: false } } }), 'a%5Bb%5D%5Bc%5D=false');
45
+ st.end();
46
+ });
47
+
32
48
  t.test('stringifies a nested object', function (st) {
33
49
  st.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
34
50
  st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e');
@@ -490,6 +506,12 @@ test('stringify()', function (t) {
490
506
  return String.fromCharCode(buffer.readUInt8(0) + 97);
491
507
  }
492
508
  }), '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');
493
515
  st.end();
494
516
  });
495
517
 
@@ -530,17 +552,20 @@ test('stringify()', function (t) {
530
552
  t.test('RFC 1738 spaces serialization', function (st) {
531
553
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
532
554
  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');
533
556
  st.end();
534
557
  });
535
558
 
536
559
  t.test('RFC 3986 spaces serialization', function (st) {
537
560
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
538
561
  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');
539
563
  st.end();
540
564
  });
541
565
 
542
566
  t.test('Backward compatibility to RFC 3986', function (st) {
543
567
  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');
544
569
  st.end();
545
570
  });
546
571
 
@@ -593,5 +618,25 @@ test('stringify()', function (t) {
593
618
  st.end();
594
619
  });
595
620
 
621
+ t.test('strictNullHandling works with custom filter', function (st) {
622
+ var filter = function (prefix, value) {
623
+ return value;
624
+ };
625
+
626
+ var options = { strictNullHandling: true, filter: filter };
627
+ st.equal(qs.stringify({ key: null }, options), 'key');
628
+ st.end();
629
+ });
630
+
631
+ t.test('strictNullHandling works with null serializeDate', function (st) {
632
+ var serializeDate = function () {
633
+ return null;
634
+ };
635
+ var options = { strictNullHandling: true, serializeDate: serializeDate };
636
+ var date = new Date();
637
+ st.equal(qs.stringify({ key: date }, options), 'key');
638
+ st.end();
639
+ });
640
+
596
641
  t.end();
597
642
  });
package/test/utils.js CHANGED
@@ -4,6 +4,10 @@ var test = require('tape');
4
4
  var utils = require('../lib/utils');
5
5
 
6
6
  test('merge()', function (t) {
7
+ t.deepEqual(utils.merge(null, true), [null, true], 'merges true into null');
8
+
9
+ t.deepEqual(utils.merge(null, [42]), [null, 42], 'merges null into an array');
10
+
7
11
  t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');
8
12
 
9
13
  var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
@@ -18,6 +22,33 @@ test('merge()', function (t) {
18
22
  var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
19
23
  t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });
20
24
 
25
+ var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
26
+ t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
27
+
28
+ t.test(
29
+ 'avoids invoking array setters unnecessarily',
30
+ { skip: typeof Object.defineProperty !== 'function' },
31
+ function (st) {
32
+ var setCount = 0;
33
+ var getCount = 0;
34
+ var observed = [];
35
+ Object.defineProperty(observed, 0, {
36
+ get: function () {
37
+ getCount += 1;
38
+ return { bar: 'baz' };
39
+ },
40
+ set: function () { setCount += 1; }
41
+ });
42
+ utils.merge(observed, [null]);
43
+ st.equal(setCount, 0);
44
+ st.equal(getCount, 1);
45
+ observed[0] = observed[0]; // eslint-disable-line no-self-assign
46
+ st.equal(setCount, 1);
47
+ st.equal(getCount, 2);
48
+ st.end();
49
+ }
50
+ );
51
+
21
52
  t.end();
22
53
  });
23
54
 
package/.eslintignore DELETED
@@ -1 +0,0 @@
1
- dist
package/LICENSE DELETED
@@ -1,28 +0,0 @@
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/test/.eslintrc DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "rules": {
3
- "array-bracket-newline": 0,
4
- "array-element-newline": 0,
5
- "consistent-return": 2,
6
- "max-lines": 0,
7
- "max-nested-callbacks": [2, 3],
8
- "max-statements": 0,
9
- "no-buffer-constructor": 0,
10
- "no-extend-native": 0,
11
- "no-magic-numbers": 0,
12
- "object-curly-newline": 0,
13
- "sort-keys": 0
14
- }
15
- }