qs 6.8.3 → 6.9.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
@@ -8,7 +8,6 @@ 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
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 }],
@@ -20,17 +16,6 @@
20
16
  "no-continue": 1,
21
17
  "no-magic-numbers": 0,
22
18
  "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
23
- },
24
-
25
- "overrides": [
26
- {
27
- "files": "test/**",
28
- "rules": {
29
- "function-paren-newline": 0,
30
- "max-lines-per-function": 0,
31
- "max-statements": 0,
32
- "no-extend-native": 0,
33
- },
34
- },
35
- ],
19
+ "operator-linebreak": [2, "before"],
20
+ }
36
21
  }
@@ -0,0 +1,15 @@
1
+ name: Automatic Rebase
2
+
3
+ on: [pull_request]
4
+
5
+ jobs:
6
+ _:
7
+ name: "Automatic Rebase"
8
+
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v1
13
+ - uses: ljharb/rebase@master
14
+ env:
15
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/CHANGELOG.md CHANGED
@@ -1,15 +1,30 @@
1
- ## **6.8.3**
2
- - [Fix] `parse`: ignore `__proto__` keys (#428)
3
- - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
4
- - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
5
- - [readme] remove travis badge; add github actions/codecov badges; update URLs
6
- - [Tests] clean up stringify tests slightly
7
- - [Docs] add note and links for coercing primitive values (#408)
8
- - [meta] fix README.md (#399)
9
- - [actions] backport actions from main
10
- - [Dev Deps] backport updates from main
11
- - [Refactor] `stringify`: reduce branching
12
- - [meta] do not publish workflow files
1
+ ## **6.9.3**
2
+ - [Fix] proper comma parsing of URL-encoded commas (#361)
3
+ - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
4
+
5
+ ## **6.9.2**
6
+ - [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
7
+ - [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
8
+ - [meta] ignore eclint transitive audit warning
9
+ - [meta] fix indentation in package.json
10
+ - [meta] add tidelift marketing copy
11
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `has-symbols`, `tape`, `mkdirp`, `iconv-lite`
12
+ - [actions] add automatic rebasing / merge commit blocking
13
+
14
+ ## **6.9.1**
15
+ - [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
16
+ - [Fix] `parse`: with comma true, do not split non-string values (#334)
17
+ - [meta] add `funding` field
18
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`
19
+ - [Tests] use shared travis-ci config
20
+
21
+ ## **6.9.0**
22
+ - [New] `parse`/`stringify`: Pass extra key/value argument to `decoder` (#333)
23
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `evalmd`
24
+ - [Tests] `parse`: add passing `arrayFormat` tests
25
+ - [Tests] add `posttest` using `npx aud` to run `npm audit` without a lockfile
26
+ - [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
27
+ - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
13
28
 
14
29
  ## **6.8.2**
15
30
  - [Fix] proper comma parsing of URL-encoded commas (#361)
@@ -42,6 +57,30 @@
42
57
  - [meta] add FUNDING.yml
43
58
  - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
44
59
 
60
+ ## **6.7.2**
61
+ - [Fix] proper comma parsing of URL-encoded commas (#361)
62
+ - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
63
+
64
+ ## **6.7.1**
65
+ - [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
66
+ - [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
67
+ - [fix] `parse`: with comma true, do not split non-string values (#334)
68
+ - [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
69
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
70
+ - [Refactor] `formats`: tiny bit of cleanup.
71
+ - readme: add security note
72
+ - [meta] add tidelift marketing copy
73
+ - [meta] add `funding` field
74
+ - [meta] add FUNDING.yml
75
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
76
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `iconv-lite`, `mkdirp`, `object-inspect`, `browserify`
77
+ - [Tests] `parse`: add passing `arrayFormat` tests
78
+ - [Tests] use shared travis-ci configs
79
+ - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
80
+ - [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended
81
+ - [Tests] use `eclint` instead of `editorconfig-tools`
82
+ - [actions] add automatic rebasing / merge commit blocking
83
+
45
84
  ## **6.7.0**
46
85
  - [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
47
86
  - [Fix] correctly parse nested arrays (#212)
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
 
@@ -281,17 +280,6 @@ assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
281
280
  ```
282
281
  (_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
283
282
 
284
- ### Parsing primitive/scalar values (numbers, booleans, null, etc)
285
-
286
- 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).
287
-
288
- ```javascript
289
- var primitiveValues = qs.parse('a=15&b=true&c=null');
290
- assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
291
- ```
292
-
293
- 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.
294
-
295
283
  ### Stringifying
296
284
 
297
285
  [](#preventEval)
@@ -357,7 +345,7 @@ var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultE
357
345
  The type argument is also provided to the decoder:
358
346
 
359
347
  ```javascript
360
- var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
348
+ var decoded = qs.parse('x=z', { decoder: function (str, defaultEncoder, charset, type) {
361
349
  if (type === 'key') {
362
350
  return // Decoded key
363
351
  } else if (type === 'value') {
@@ -599,18 +587,18 @@ Available as part of the Tidelift Subscription
599
587
 
600
588
  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)
601
589
 
602
- [package-url]: https://npmjs.org/package/qs
603
- [npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
604
- [deps-svg]: https://david-dm.org/ljharb/qs.svg
605
- [deps-url]: https://david-dm.org/ljharb/qs
606
- [dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
607
- [dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
608
- [npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
609
- [license-image]: https://img.shields.io/npm/l/qs.svg
590
+ [1]: https://npmjs.org/package/qs
591
+ [2]: http://versionbadg.es/ljharb/qs.svg
592
+ [3]: https://api.travis-ci.org/ljharb/qs.svg
593
+ [4]: https://travis-ci.org/ljharb/qs
594
+ [5]: https://david-dm.org/ljharb/qs.svg
595
+ [6]: https://david-dm.org/ljharb/qs
596
+ [7]: https://david-dm.org/ljharb/qs/dev-status.svg
597
+ [8]: https://david-dm.org/ljharb/qs?type=dev
598
+ [9]: https://ci.testling.com/ljharb/qs.png
599
+ [10]: https://ci.testling.com/ljharb/qs
600
+ [11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
601
+ [license-image]: http://img.shields.io/npm/l/qs.svg
610
602
  [license-url]: LICENSE
611
- [downloads-image]: https://img.shields.io/npm/dm/qs.svg
612
- [downloads-url]: https://npm-stat.com/charts.html?package=qs
613
- [codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
614
- [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
615
- [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
616
- [actions-url]: https://github.com/ljharb/qs/actions
603
+ [downloads-image]: http://img.shields.io/npm/dm/qs.svg
604
+ [downloads-url]: http://npm-stat.com/charts.html?package=qs
package/dist/qs.js CHANGED
@@ -134,14 +134,14 @@ var parseValues = function parseQueryStringValues(str, options) {
134
134
 
135
135
  var key, val;
136
136
  if (pos === -1) {
137
- key = options.decoder(part, defaults.decoder, charset);
137
+ key = options.decoder(part, defaults.decoder, charset, 'key');
138
138
  val = options.strictNullHandling ? null : '';
139
139
  } else {
140
- key = options.decoder(part.slice(0, pos), defaults.decoder, charset);
140
+ key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
141
141
  val = maybeMap(
142
142
  parseArrayValue(part.slice(pos + 1), options),
143
143
  function (encodedVal) {
144
- return options.decoder(encodedVal, defaults.decoder, charset);
144
+ return options.decoder(encodedVal, defaults.decoder, charset, 'value');
145
145
  }
146
146
  );
147
147
  }
@@ -188,12 +188,12 @@ var parseObject = function (chain, val, options, valuesParsed) {
188
188
  ) {
189
189
  obj = [];
190
190
  obj[index] = leaf;
191
- } else if (cleanRoot !== '__proto__') {
191
+ } else {
192
192
  obj[cleanRoot] = leaf;
193
193
  }
194
194
  }
195
195
 
196
- leaf = obj;
196
+ leaf = obj; // eslint-disable-line no-param-reassign
197
197
  }
198
198
 
199
199
  return leaf;
@@ -330,7 +330,6 @@ var arrayPrefixGenerators = {
330
330
  };
331
331
 
332
332
  var isArray = Array.isArray;
333
- var split = String.prototype.split;
334
333
  var push = Array.prototype.push;
335
334
  var pushToArray = function (arr, valueOrArray) {
336
335
  push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
@@ -393,7 +392,7 @@ var stringify = function stringify(
393
392
 
394
393
  if (obj === null) {
395
394
  if (strictNullHandling) {
396
- return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix;
395
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
397
396
  }
398
397
 
399
398
  obj = '';
@@ -401,16 +400,8 @@ var stringify = function stringify(
401
400
 
402
401
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
403
402
  if (encoder) {
404
- var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset);
405
- if (generateArrayPrefix === 'comma' && encodeValuesOnly) {
406
- var valuesArray = split.call(String(obj), ',');
407
- var valuesJoined = '';
408
- for (var i = 0; i < valuesArray.length; ++i) {
409
- valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset));
410
- }
411
- return [formatter(keyValue) + '=' + valuesJoined];
412
- }
413
- return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))];
403
+ var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
404
+ return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
414
405
  }
415
406
  return [formatter(prefix) + '=' + formatter(String(obj))];
416
407
  }
@@ -429,33 +420,46 @@ var stringify = function stringify(
429
420
  objKeys = sort ? keys.sort(sort) : keys;
430
421
  }
431
422
 
432
- for (var j = 0; j < objKeys.length; ++j) {
433
- var key = objKeys[j];
434
- var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
423
+ for (var i = 0; i < objKeys.length; ++i) {
424
+ var key = objKeys[i];
435
425
 
436
- if (skipNulls && value === null) {
426
+ if (skipNulls && obj[key] === null) {
437
427
  continue;
438
428
  }
439
429
 
440
- var keyPrefix = isArray(obj)
441
- ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
442
- : prefix + (allowDots ? '.' + key : '[' + key + ']');
443
-
444
- pushToArray(values, stringify(
445
- value,
446
- keyPrefix,
447
- generateArrayPrefix,
448
- strictNullHandling,
449
- skipNulls,
450
- encoder,
451
- filter,
452
- sort,
453
- allowDots,
454
- serializeDate,
455
- formatter,
456
- encodeValuesOnly,
457
- charset
458
- ));
430
+ if (isArray(obj)) {
431
+ pushToArray(values, stringify(
432
+ obj[key],
433
+ typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
434
+ generateArrayPrefix,
435
+ strictNullHandling,
436
+ skipNulls,
437
+ encoder,
438
+ filter,
439
+ sort,
440
+ allowDots,
441
+ serializeDate,
442
+ formatter,
443
+ encodeValuesOnly,
444
+ charset
445
+ ));
446
+ } else {
447
+ pushToArray(values, stringify(
448
+ obj[key],
449
+ prefix + (allowDots ? '.' + key : '[' + key + ']'),
450
+ generateArrayPrefix,
451
+ strictNullHandling,
452
+ skipNulls,
453
+ encoder,
454
+ filter,
455
+ sort,
456
+ allowDots,
457
+ serializeDate,
458
+ formatter,
459
+ encodeValuesOnly,
460
+ charset
461
+ ));
462
+ }
459
463
  }
460
464
 
461
465
  return values;
@@ -466,7 +470,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
466
470
  return defaults;
467
471
  }
468
472
 
469
- if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
473
+ if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
470
474
  throw new TypeError('Encoder has to be a function.');
471
475
  }
472
476
 
@@ -632,6 +636,7 @@ var arrayToObject = function arrayToObject(source, options) {
632
636
  };
633
637
 
634
638
  var merge = function merge(target, source, options) {
639
+ /* eslint no-param-reassign: 0 */
635
640
  if (!source) {
636
641
  return target;
637
642
  }
@@ -641,7 +646,7 @@ var merge = function merge(target, source, options) {
641
646
  target.push(source);
642
647
  } else if (target && typeof target === 'object') {
643
648
  if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
644
- target[source] = true; // eslint-disable-line no-param-reassign
649
+ target[source] = true;
645
650
  }
646
651
  } else {
647
652
  return [target, source];
@@ -664,12 +669,12 @@ var merge = function merge(target, source, options) {
664
669
  if (has.call(target, i)) {
665
670
  var targetItem = target[i];
666
671
  if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
667
- target[i] = merge(targetItem, item, options); // eslint-disable-line no-param-reassign
672
+ target[i] = merge(targetItem, item, options);
668
673
  } else {
669
674
  target.push(item);
670
675
  }
671
676
  } else {
672
- target[i] = item; // eslint-disable-line no-param-reassign
677
+ target[i] = item;
673
678
  }
674
679
  });
675
680
  return target;
@@ -679,9 +684,9 @@ var merge = function merge(target, source, options) {
679
684
  var value = source[key];
680
685
 
681
686
  if (has.call(acc, key)) {
682
- acc[key] = merge(acc[key], value, options); // eslint-disable-line no-param-reassign
687
+ acc[key] = merge(acc[key], value, options);
683
688
  } else {
684
- acc[key] = value; // eslint-disable-line no-param-reassign
689
+ acc[key] = value;
685
690
  }
686
691
  return acc;
687
692
  }, mergeTarget);
@@ -689,7 +694,7 @@ var merge = function merge(target, source, options) {
689
694
 
690
695
  var assign = function assignSingleSource(target, source) {
691
696
  return Object.keys(source).reduce(function (acc, key) {
692
- acc[key] = source[key]; // eslint-disable-line no-param-reassign
697
+ acc[key] = source[key];
693
698
  return acc;
694
699
  }, target);
695
700
  };
@@ -762,7 +767,6 @@ var encode = function encode(str, defaultEncoder, charset) {
762
767
 
763
768
  i += 1;
764
769
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
765
- /* eslint operator-linebreak: [2, "before"] */
766
770
  out += hexTable[0xF0 | (c >> 18)]
767
771
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
768
772
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
package/lib/parse.js CHANGED
@@ -92,14 +92,14 @@ var parseValues = function parseQueryStringValues(str, options) {
92
92
 
93
93
  var key, val;
94
94
  if (pos === -1) {
95
- key = options.decoder(part, defaults.decoder, charset);
95
+ key = options.decoder(part, defaults.decoder, charset, 'key');
96
96
  val = options.strictNullHandling ? null : '';
97
97
  } else {
98
- key = options.decoder(part.slice(0, pos), defaults.decoder, charset);
98
+ key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
99
99
  val = maybeMap(
100
100
  parseArrayValue(part.slice(pos + 1), options),
101
101
  function (encodedVal) {
102
- return options.decoder(encodedVal, defaults.decoder, charset);
102
+ return options.decoder(encodedVal, defaults.decoder, charset, 'value');
103
103
  }
104
104
  );
105
105
  }
@@ -146,12 +146,12 @@ var parseObject = function (chain, val, options, valuesParsed) {
146
146
  ) {
147
147
  obj = [];
148
148
  obj[index] = leaf;
149
- } else if (cleanRoot !== '__proto__') {
149
+ } else {
150
150
  obj[cleanRoot] = leaf;
151
151
  }
152
152
  }
153
153
 
154
- leaf = obj;
154
+ leaf = obj; // eslint-disable-line no-param-reassign
155
155
  }
156
156
 
157
157
  return leaf;
package/lib/stringify.js CHANGED
@@ -18,7 +18,6 @@ var arrayPrefixGenerators = {
18
18
  };
19
19
 
20
20
  var isArray = Array.isArray;
21
- var split = String.prototype.split;
22
21
  var push = Array.prototype.push;
23
22
  var pushToArray = function (arr, valueOrArray) {
24
23
  push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
@@ -81,7 +80,7 @@ var stringify = function stringify(
81
80
 
82
81
  if (obj === null) {
83
82
  if (strictNullHandling) {
84
- return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset) : prefix;
83
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
85
84
  }
86
85
 
87
86
  obj = '';
@@ -89,16 +88,8 @@ var stringify = function stringify(
89
88
 
90
89
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
91
90
  if (encoder) {
92
- var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset);
93
- if (generateArrayPrefix === 'comma' && encodeValuesOnly) {
94
- var valuesArray = split.call(String(obj), ',');
95
- var valuesJoined = '';
96
- for (var i = 0; i < valuesArray.length; ++i) {
97
- valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset));
98
- }
99
- return [formatter(keyValue) + '=' + valuesJoined];
100
- }
101
- return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset))];
91
+ var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
92
+ return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
102
93
  }
103
94
  return [formatter(prefix) + '=' + formatter(String(obj))];
104
95
  }
@@ -117,33 +108,46 @@ var stringify = function stringify(
117
108
  objKeys = sort ? keys.sort(sort) : keys;
118
109
  }
119
110
 
120
- for (var j = 0; j < objKeys.length; ++j) {
121
- var key = objKeys[j];
122
- var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
111
+ for (var i = 0; i < objKeys.length; ++i) {
112
+ var key = objKeys[i];
123
113
 
124
- if (skipNulls && value === null) {
114
+ if (skipNulls && obj[key] === null) {
125
115
  continue;
126
116
  }
127
117
 
128
- var keyPrefix = isArray(obj)
129
- ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
130
- : prefix + (allowDots ? '.' + key : '[' + key + ']');
131
-
132
- pushToArray(values, stringify(
133
- value,
134
- keyPrefix,
135
- generateArrayPrefix,
136
- strictNullHandling,
137
- skipNulls,
138
- encoder,
139
- filter,
140
- sort,
141
- allowDots,
142
- serializeDate,
143
- formatter,
144
- encodeValuesOnly,
145
- charset
146
- ));
118
+ if (isArray(obj)) {
119
+ pushToArray(values, stringify(
120
+ obj[key],
121
+ typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
122
+ generateArrayPrefix,
123
+ strictNullHandling,
124
+ skipNulls,
125
+ encoder,
126
+ filter,
127
+ sort,
128
+ allowDots,
129
+ serializeDate,
130
+ formatter,
131
+ encodeValuesOnly,
132
+ charset
133
+ ));
134
+ } else {
135
+ pushToArray(values, stringify(
136
+ obj[key],
137
+ prefix + (allowDots ? '.' + key : '[' + key + ']'),
138
+ generateArrayPrefix,
139
+ strictNullHandling,
140
+ skipNulls,
141
+ encoder,
142
+ filter,
143
+ sort,
144
+ allowDots,
145
+ serializeDate,
146
+ formatter,
147
+ encodeValuesOnly,
148
+ charset
149
+ ));
150
+ }
147
151
  }
148
152
 
149
153
  return values;
@@ -154,7 +158,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
154
158
  return defaults;
155
159
  }
156
160
 
157
- if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
161
+ if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
158
162
  throw new TypeError('Encoder has to be a function.');
159
163
  }
160
164
 
package/lib/utils.js CHANGED
@@ -43,6 +43,7 @@ var arrayToObject = function arrayToObject(source, options) {
43
43
  };
44
44
 
45
45
  var merge = function merge(target, source, options) {
46
+ /* eslint no-param-reassign: 0 */
46
47
  if (!source) {
47
48
  return target;
48
49
  }
@@ -52,7 +53,7 @@ var merge = function merge(target, source, options) {
52
53
  target.push(source);
53
54
  } else if (target && typeof target === 'object') {
54
55
  if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
55
- target[source] = true; // eslint-disable-line no-param-reassign
56
+ target[source] = true;
56
57
  }
57
58
  } else {
58
59
  return [target, source];
@@ -75,12 +76,12 @@ var merge = function merge(target, source, options) {
75
76
  if (has.call(target, i)) {
76
77
  var targetItem = target[i];
77
78
  if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
78
- target[i] = merge(targetItem, item, options); // eslint-disable-line no-param-reassign
79
+ target[i] = merge(targetItem, item, options);
79
80
  } else {
80
81
  target.push(item);
81
82
  }
82
83
  } else {
83
- target[i] = item; // eslint-disable-line no-param-reassign
84
+ target[i] = item;
84
85
  }
85
86
  });
86
87
  return target;
@@ -90,9 +91,9 @@ var merge = function merge(target, source, options) {
90
91
  var value = source[key];
91
92
 
92
93
  if (has.call(acc, key)) {
93
- acc[key] = merge(acc[key], value, options); // eslint-disable-line no-param-reassign
94
+ acc[key] = merge(acc[key], value, options);
94
95
  } else {
95
- acc[key] = value; // eslint-disable-line no-param-reassign
96
+ acc[key] = value;
96
97
  }
97
98
  return acc;
98
99
  }, mergeTarget);
@@ -100,7 +101,7 @@ var merge = function merge(target, source, options) {
100
101
 
101
102
  var assign = function assignSingleSource(target, source) {
102
103
  return Object.keys(source).reduce(function (acc, key) {
103
- acc[key] = source[key]; // eslint-disable-line no-param-reassign
104
+ acc[key] = source[key];
104
105
  return acc;
105
106
  }, target);
106
107
  };
@@ -173,7 +174,6 @@ var encode = function encode(str, defaultEncoder, charset) {
173
174
 
174
175
  i += 1;
175
176
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
176
- /* eslint operator-linebreak: [2, "before"] */
177
177
  out += hexTable[0xF0 | (c >> 18)]
178
178
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
179
179
  + 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.8.3",
5
+ "version": "6.9.3",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -29,36 +29,41 @@
29
29
  "engines": {
30
30
  "node": ">=0.6"
31
31
  },
32
+ "dependencies": {},
32
33
  "devDependencies": {
33
- "@ljharb/eslint-config": "^20.1.0",
34
- "browserify": "^16.5.2",
34
+ "@ljharb/eslint-config": "^16.0.0",
35
+ "browserify": "^16.5.0",
35
36
  "covert": "^1.1.1",
36
37
  "eclint": "^2.8.1",
37
- "eslint": "^8.6.0",
38
+ "eslint": "^6.8.0",
38
39
  "evalmd": "^0.0.19",
39
40
  "for-each": "^0.3.3",
40
- "has-symbols": "^1.0.2",
41
+ "has-symbols": "^1.0.1",
41
42
  "iconv-lite": "^0.5.1",
42
- "in-publish": "^2.0.1",
43
43
  "mkdirp": "^0.5.4",
44
- "object-inspect": "^1.12.0",
44
+ "object-inspect": "^1.7.0",
45
45
  "qs-iconv": "^1.0.4",
46
- "safe-publish-latest": "^2.0.0",
46
+ "safe-publish-latest": "^1.1.4",
47
47
  "safer-buffer": "^2.1.2",
48
- "tape": "^5.4.0"
48
+ "tape": "^5.0.0-next.5"
49
49
  },
50
50
  "scripts": {
51
- "prepublishOnly": "safe-publish-latest && npm run dist",
52
- "prepublish": "not-in-publish || npm run prepublishOnly",
51
+ "prepublish": "safe-publish-latest && npm run dist",
53
52
  "pretest": "npm run --silent readme && npm run --silent lint",
54
53
  "test": "npm run --silent coverage",
55
54
  "tests-only": "node test",
56
55
  "posttest": "npx aud --production",
57
56
  "readme": "evalmd README.md",
58
- "postlint": "eclint check * lib/* test/* !dist/* '!coverage/**' '.nyc_output/**'",
59
- "lint": "eslint .",
57
+ "postlint": "eclint check * lib/* test/*",
58
+ "lint": "eslint lib/*.js test/*.js",
60
59
  "coverage": "covert test",
61
60
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
62
61
  },
63
- "license": "BSD-3-Clause"
62
+ "license": "BSD-3-Clause",
63
+ "greenkeeper": {
64
+ "ignore": [
65
+ "iconv-lite",
66
+ "mkdirp"
67
+ ]
68
+ }
64
69
  }
package/test/.eslintrc ADDED
@@ -0,0 +1,18 @@
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
+ "no-throw-literal": 0,
15
+ "object-curly-newline": 0,
16
+ "sort-keys": 0,
17
+ }
18
+ }
package/test/parse.js CHANGED
@@ -620,66 +620,6 @@ test('parse()', function (t) {
620
620
  st.end();
621
621
  });
622
622
 
623
- t.test('dunder proto is ignored', function (st) {
624
- var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
625
- var result = qs.parse(payload, { allowPrototypes: true });
626
-
627
- st.deepEqual(
628
- result,
629
- {
630
- categories: {
631
- length: '42'
632
- }
633
- },
634
- 'silent [[Prototype]] payload'
635
- );
636
-
637
- var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
638
-
639
- st.deepEqual(
640
- plainResult,
641
- {
642
- __proto__: null,
643
- categories: {
644
- __proto__: null,
645
- length: '42'
646
- }
647
- },
648
- 'silent [[Prototype]] payload: plain objects'
649
- );
650
-
651
- var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
652
-
653
- st.notOk(Array.isArray(query.categories), 'is not an array');
654
- st.notOk(query.categories instanceof Array, 'is not instanceof an array');
655
- st.deepEqual(query.categories, { some: { json: 'toInject' } });
656
- st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
657
-
658
- st.deepEqual(
659
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
660
- {
661
- foo: {
662
- bar: 'stuffs'
663
- }
664
- },
665
- 'hidden values'
666
- );
667
-
668
- st.deepEqual(
669
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
670
- {
671
- __proto__: null,
672
- foo: {
673
- __proto__: null,
674
- bar: 'stuffs'
675
- }
676
- },
677
- 'hidden values: plain objects'
678
- );
679
-
680
- st.end();
681
- });
682
-
683
623
  t.test('can return null objects', { skip: !Object.create }, function (st) {
684
624
  var expected = Object.create(null);
685
625
  expected.a = Object.create(null);
@@ -813,5 +753,20 @@ test('parse()', function (t) {
813
753
  st.end();
814
754
  });
815
755
 
756
+ t.test('allows for decoding keys and values differently', function (st) {
757
+ var decoder = function (str, defaultDecoder, charset, type) {
758
+ if (type === 'key') {
759
+ return defaultDecoder(str, defaultDecoder, charset, type).toLowerCase();
760
+ }
761
+ if (type === 'value') {
762
+ return defaultDecoder(str, defaultDecoder, charset, type).toUpperCase();
763
+ }
764
+ throw 'this should never happen! type: ' + type;
765
+ };
766
+
767
+ st.deepEqual(qs.parse('KeY=vAlUe', { decoder: decoder }), { key: 'VALUE' });
768
+ st.end();
769
+ });
770
+
816
771
  t.end();
817
772
  });
package/test/stringify.js CHANGED
@@ -132,10 +132,10 @@ test('stringify()', function (t) {
132
132
  });
133
133
 
134
134
  t.test('stringifies a nested array value', function (st) {
135
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[b][0]=c&a[b][1]=d');
136
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[b][]=c&a[b][]=d');
137
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a[b]=c,d');
138
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true }), 'a[b][0]=c&a[b][1]=d');
135
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
136
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
137
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
138
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
139
139
  st.end();
140
140
  });
141
141
 
@@ -143,7 +143,7 @@ test('stringify()', function (t) {
143
143
  st.equal(
144
144
  qs.stringify(
145
145
  { a: { b: ['c', 'd'] } },
146
- { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }
146
+ { allowDots: true, encode: false, arrayFormat: 'indices' }
147
147
  ),
148
148
  'a.b[0]=c&a.b[1]=d',
149
149
  'indices: stringifies with dots + indices'
@@ -151,7 +151,7 @@ test('stringify()', function (t) {
151
151
  st.equal(
152
152
  qs.stringify(
153
153
  { a: { b: ['c', 'd'] } },
154
- { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }
154
+ { allowDots: true, encode: false, arrayFormat: 'brackets' }
155
155
  ),
156
156
  'a.b[]=c&a.b[]=d',
157
157
  'brackets: stringifies with dots + brackets'
@@ -159,7 +159,7 @@ test('stringify()', function (t) {
159
159
  st.equal(
160
160
  qs.stringify(
161
161
  { a: { b: ['c', 'd'] } },
162
- { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }
162
+ { allowDots: true, encode: false, arrayFormat: 'comma' }
163
163
  ),
164
164
  'a.b=c,d',
165
165
  'comma: stringifies with dots + comma'
@@ -167,7 +167,7 @@ test('stringify()', function (t) {
167
167
  st.equal(
168
168
  qs.stringify(
169
169
  { a: { b: ['c', 'd'] } },
170
- { allowDots: true, encodeValuesOnly: true }
170
+ { allowDots: true, encode: false }
171
171
  ),
172
172
  'a.b[0]=c&a.b[1]=d',
173
173
  'default: stringifies with dots + indices'
@@ -215,23 +215,17 @@ test('stringify()', function (t) {
215
215
 
216
216
  t.test('stringifies an array with mixed objects and primitives', function (st) {
217
217
  st.equal(
218
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' }),
218
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
219
219
  'a[0][b]=1&a[1]=2&a[2]=3',
220
220
  'indices => indices'
221
221
  );
222
222
  st.equal(
223
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
223
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
224
224
  'a[][b]=1&a[]=2&a[]=3',
225
225
  'brackets => brackets'
226
226
  );
227
227
  st.equal(
228
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }),
229
- '???',
230
- 'brackets => brackets',
231
- { skip: 'TODO: figure out what this should do' }
232
- );
233
- st.equal(
234
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true }),
228
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
235
229
  'a[0][b]=1&a[1]=2&a[2]=3',
236
230
  'default => indices'
237
231
  );
@@ -725,5 +719,20 @@ test('stringify()', function (t) {
725
719
  st.end();
726
720
  });
727
721
 
722
+ t.test('allows for encoding keys and values differently', function (st) {
723
+ var encoder = function (str, defaultEncoder, charset, type) {
724
+ if (type === 'key') {
725
+ return defaultEncoder(str, defaultEncoder, charset, type).toLowerCase();
726
+ }
727
+ if (type === 'value') {
728
+ return defaultEncoder(str, defaultEncoder, charset, type).toUpperCase();
729
+ }
730
+ throw 'this should never happen! type: ' + type;
731
+ };
732
+
733
+ st.deepEqual(qs.stringify({ KeY: 'vAlUe' }, { encoder: encoder }), 'key=VALUE');
734
+ st.end();
735
+ });
736
+
728
737
  t.end();
729
738
  });
package/.travis.yml DELETED
@@ -1,12 +0,0 @@
1
- version: ~> 1.0
2
- language: node_js
3
- os:
4
- - linux
5
- import:
6
- - ljharb/travis-ci:node/all.yml
7
- - ljharb/travis-ci:node/pretest.yml
8
- - ljharb/travis-ci:node/posttest.yml
9
- - ljharb/travis-ci:node/coverage.yml
10
- matrix:
11
- allow_failures:
12
- - env: COVERAGE=true # temporarily allow this to fail