qs 6.9.2 → 6.9.6

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
@@ -31,3 +31,9 @@ indent_size = 2
31
31
  [LICENSE]
32
32
  indent_size = 2
33
33
  max_line_length = off
34
+
35
+ [coverage/**/*]
36
+ indent_size = off
37
+ indent_style = off
38
+ indent = off
39
+ max_line_length = off
package/.eslintignore CHANGED
@@ -1 +1,2 @@
1
- dist
1
+ dist/
2
+ coverage/
package/.eslintrc CHANGED
@@ -17,5 +17,19 @@
17
17
  "no-magic-numbers": 0,
18
18
  "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
19
19
  "operator-linebreak": [2, "before"],
20
- }
20
+ },
21
+
22
+ "overrides": [
23
+ {
24
+ "files": "test/**",
25
+ "rules": {
26
+ "function-paren-newline": 0,
27
+ "max-lines-per-function": 0,
28
+ "max-statements": 0,
29
+ "no-buffer-constructor": 0,
30
+ "no-extend-native": 0,
31
+ "no-throw-literal": 0,
32
+ }
33
+ }
34
+ ]
21
35
  }
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.9.6**
2
+ - [Fix] restore `dist` dir; mistakenly removed in d4f6c32
3
+
4
+ ## **6.9.5**
5
+ - [Fix] `stringify`: do not encode parens for RFC1738
6
+ - [Fix] `stringify`: fix arrayFormat comma with empty array/objects (#350)
7
+ - [Refactor] `format`: remove `util.assign` call
8
+ - [meta] add "Allow Edits" workflow; update rebase workflow
9
+ - [actions] switch Automatic Rebase workflow to `pull_request_target` event
10
+ - [Tests] `stringify`: add tests for #378
11
+ - [Tests] migrate tests to Github Actions
12
+ - [Tests] run `nyc` on all tests; use `tape` runner
13
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `browserify`, `mkdirp`, `object-inspect`, `tape`; add `aud`
14
+
15
+ ## **6.9.4**
16
+ - [Fix] `stringify`: when `arrayFormat` is `comma`, respect `serializeDate` (#364)
17
+ - [Refactor] `stringify`: reduce branching (part of #350)
18
+ - [Refactor] move `maybeMap` to `utils`
19
+ - [Dev Deps] update `browserify`, `tape`
20
+
21
+ ## **6.9.3**
22
+ - [Fix] proper comma parsing of URL-encoded commas (#361)
23
+ - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
24
+
1
25
  ## **6.9.2**
2
26
  - [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
3
27
  - [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
@@ -22,6 +46,23 @@
22
46
  - [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
23
47
  - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
24
48
 
49
+ ## **6.8.2**
50
+ - [Fix] proper comma parsing of URL-encoded commas (#361)
51
+ - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
52
+
53
+ ## **6.8.1**
54
+ - [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
55
+ - [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
56
+ - [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
57
+ - [fix] `parse`: with comma true, do not split non-string values (#334)
58
+ - [meta] add tidelift marketing copy
59
+ - [meta] add `funding` field
60
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `has-symbols`, `iconv-lite`, `mkdirp`, `object-inspect`
61
+ - [Tests] `parse`: add passing `arrayFormat` tests
62
+ - [Tests] use shared travis-ci configs
63
+ - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
64
+ - [actions] add automatic rebasing / merge commit blocking
65
+
25
66
  ## **6.8.0**
26
67
  - [New] add `depth=false` to preserve the original key; [Fix] `depth=0` should preserve the original key (#326)
27
68
  - [New] [Fix] stringify symbols and bigints
@@ -36,6 +77,30 @@
36
77
  - [meta] add FUNDING.yml
37
78
  - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
38
79
 
80
+ ## **6.7.2**
81
+ - [Fix] proper comma parsing of URL-encoded commas (#361)
82
+ - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
83
+
84
+ ## **6.7.1**
85
+ - [Fix] `parse`: Fix parsing array from object with `comma` true (#359)
86
+ - [Fix] `parse`: with comma true, handle field that holds an array of arrays (#335)
87
+ - [fix] `parse`: with comma true, do not split non-string values (#334)
88
+ - [Fix] `parse`: throw a TypeError instead of an Error for bad charset (#349)
89
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
90
+ - [Refactor] `formats`: tiny bit of cleanup.
91
+ - readme: add security note
92
+ - [meta] add tidelift marketing copy
93
+ - [meta] add `funding` field
94
+ - [meta] add FUNDING.yml
95
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
96
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `tape`, `safe-publish-latest`, `evalmd`, `iconv-lite`, `mkdirp`, `object-inspect`, `browserify`
97
+ - [Tests] `parse`: add passing `arrayFormat` tests
98
+ - [Tests] use shared travis-ci configs
99
+ - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
100
+ - [Tests] add tests for `depth=0` and `depth=false` behavior, both current and intuitive/intended
101
+ - [Tests] use `eclint` instead of `editorconfig-tools`
102
+ - [actions] add automatic rebasing / merge commit blocking
103
+
39
104
  ## **6.7.0**
40
105
  - [New] `stringify`/`parse`: add `comma` as an `arrayFormat` option (#276, #219)
41
106
  - [Fix] correctly parse nested arrays (#212)
package/dist/qs.js CHANGED
@@ -4,29 +4,26 @@
4
4
  var replace = String.prototype.replace;
5
5
  var percentTwenties = /%20/g;
6
6
 
7
- var util = require('./utils');
8
-
9
7
  var Format = {
10
8
  RFC1738: 'RFC1738',
11
9
  RFC3986: 'RFC3986'
12
10
  };
13
11
 
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
- }
12
+ module.exports = {
13
+ 'default': Format.RFC3986,
14
+ formatters: {
15
+ RFC1738: function (value) {
16
+ return replace.call(value, percentTwenties, '+');
17
+ },
18
+ RFC3986: function (value) {
19
+ return String(value);
24
20
  }
25
21
  },
26
- Format
27
- );
22
+ RFC1738: Format.RFC1738,
23
+ RFC3986: Format.RFC3986
24
+ };
28
25
 
29
- },{"./utils":5}],2:[function(require,module,exports){
26
+ },{}],2:[function(require,module,exports){
30
27
  'use strict';
31
28
 
32
29
  var stringify = require('./stringify');
@@ -127,15 +124,18 @@ var parseValues = function parseQueryStringValues(str, options) {
127
124
  val = options.strictNullHandling ? null : '';
128
125
  } else {
129
126
  key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
130
- val = options.decoder(part.slice(pos + 1), defaults.decoder, charset, 'value');
127
+ val = utils.maybeMap(
128
+ parseArrayValue(part.slice(pos + 1), options),
129
+ function (encodedVal) {
130
+ return options.decoder(encodedVal, defaults.decoder, charset, 'value');
131
+ }
132
+ );
131
133
  }
132
134
 
133
135
  if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
134
136
  val = interpretNumericEntities(val);
135
137
  }
136
138
 
137
- val = parseArrayValue(val, options);
138
-
139
139
  if (part.indexOf('[]=') > -1) {
140
140
  val = isArray(val) ? [val] : val;
141
141
  }
@@ -150,8 +150,8 @@ var parseValues = function parseQueryStringValues(str, options) {
150
150
  return obj;
151
151
  };
152
152
 
153
- var parseObject = function (chain, val, options) {
154
- var leaf = parseArrayValue(val, options);
153
+ var parseObject = function (chain, val, options, valuesParsed) {
154
+ var leaf = valuesParsed ? val : parseArrayValue(val, options);
155
155
 
156
156
  for (var i = chain.length - 1; i >= 0; --i) {
157
157
  var obj;
@@ -185,7 +185,7 @@ var parseObject = function (chain, val, options) {
185
185
  return leaf;
186
186
  };
187
187
 
188
- var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
188
+ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
189
189
  if (!givenKey) {
190
190
  return;
191
191
  }
@@ -236,7 +236,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
236
236
  keys.push('[' + key.slice(segment.index) + ']');
237
237
  }
238
238
 
239
- return parseObject(keys, val, options);
239
+ return parseObject(keys, val, options, valuesParsed);
240
240
  };
241
241
 
242
242
  var normalizeParseOptions = function normalizeParseOptions(opts) {
@@ -288,7 +288,7 @@ module.exports = function (str, opts) {
288
288
  var keys = Object.keys(tempObj);
289
289
  for (var i = 0; i < keys.length; ++i) {
290
290
  var key = keys[i];
291
- var newObj = parseKeys(key, tempObj[key], options);
291
+ var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
292
292
  obj = utils.merge(obj, newObj, options);
293
293
  }
294
294
 
@@ -363,6 +363,7 @@ var stringify = function stringify(
363
363
  sort,
364
364
  allowDots,
365
365
  serializeDate,
366
+ format,
366
367
  formatter,
367
368
  encodeValuesOnly,
368
369
  charset
@@ -373,12 +374,17 @@ var stringify = function stringify(
373
374
  } else if (obj instanceof Date) {
374
375
  obj = serializeDate(obj);
375
376
  } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
376
- obj = obj.join(',');
377
+ obj = utils.maybeMap(obj, function (value) {
378
+ if (value instanceof Date) {
379
+ return serializeDate(value);
380
+ }
381
+ return value;
382
+ });
377
383
  }
378
384
 
379
385
  if (obj === null) {
380
386
  if (strictNullHandling) {
381
- return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
387
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
382
388
  }
383
389
 
384
390
  obj = '';
@@ -386,8 +392,8 @@ var stringify = function stringify(
386
392
 
387
393
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
388
394
  if (encoder) {
389
- var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
390
- return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
395
+ var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
396
+ return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
391
397
  }
392
398
  return [formatter(prefix) + '=' + formatter(String(obj))];
393
399
  }
@@ -399,7 +405,10 @@ var stringify = function stringify(
399
405
  }
400
406
 
401
407
  var objKeys;
402
- if (isArray(filter)) {
408
+ if (generateArrayPrefix === 'comma' && isArray(obj)) {
409
+ // we need to join elements in
410
+ objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : undefined }];
411
+ } else if (isArray(filter)) {
403
412
  objKeys = filter;
404
413
  } else {
405
414
  var keys = Object.keys(obj);
@@ -408,44 +417,32 @@ var stringify = function stringify(
408
417
 
409
418
  for (var i = 0; i < objKeys.length; ++i) {
410
419
  var key = objKeys[i];
420
+ var value = typeof key === 'object' && key.value !== undefined ? key.value : obj[key];
411
421
 
412
- if (skipNulls && obj[key] === null) {
422
+ if (skipNulls && value === null) {
413
423
  continue;
414
424
  }
415
425
 
416
- if (isArray(obj)) {
417
- pushToArray(values, stringify(
418
- obj[key],
419
- typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix,
420
- generateArrayPrefix,
421
- strictNullHandling,
422
- skipNulls,
423
- encoder,
424
- filter,
425
- sort,
426
- allowDots,
427
- serializeDate,
428
- formatter,
429
- encodeValuesOnly,
430
- charset
431
- ));
432
- } else {
433
- pushToArray(values, stringify(
434
- obj[key],
435
- prefix + (allowDots ? '.' + key : '[' + key + ']'),
436
- generateArrayPrefix,
437
- strictNullHandling,
438
- skipNulls,
439
- encoder,
440
- filter,
441
- sort,
442
- allowDots,
443
- serializeDate,
444
- formatter,
445
- encodeValuesOnly,
446
- charset
447
- ));
448
- }
426
+ var keyPrefix = isArray(obj)
427
+ ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
428
+ : prefix + (allowDots ? '.' + key : '[' + key + ']');
429
+
430
+ pushToArray(values, stringify(
431
+ value,
432
+ keyPrefix,
433
+ generateArrayPrefix,
434
+ strictNullHandling,
435
+ skipNulls,
436
+ encoder,
437
+ filter,
438
+ sort,
439
+ allowDots,
440
+ serializeDate,
441
+ format,
442
+ formatter,
443
+ encodeValuesOnly,
444
+ charset
445
+ ));
449
446
  }
450
447
 
451
448
  return values;
@@ -489,6 +486,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
489
486
  encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
490
487
  encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
491
488
  filter: filter,
489
+ format: format,
492
490
  formatter: formatter,
493
491
  serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
494
492
  skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
@@ -554,6 +552,7 @@ module.exports = function (object, opts) {
554
552
  options.sort,
555
553
  options.allowDots,
556
554
  options.serializeDate,
555
+ options.format,
557
556
  options.formatter,
558
557
  options.encodeValuesOnly,
559
558
  options.charset
@@ -579,6 +578,8 @@ module.exports = function (object, opts) {
579
578
  },{"./formats":1,"./utils":5}],5:[function(require,module,exports){
580
579
  'use strict';
581
580
 
581
+ var formats = require('./formats');
582
+
582
583
  var has = Object.prototype.hasOwnProperty;
583
584
  var isArray = Array.isArray;
584
585
 
@@ -699,7 +700,7 @@ var decode = function (str, decoder, charset) {
699
700
  }
700
701
  };
701
702
 
702
- var encode = function encode(str, defaultEncoder, charset) {
703
+ var encode = function encode(str, defaultEncoder, charset, kind, format) {
703
704
  // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
704
705
  // It has been adapted here for stricter adherence to RFC 3986
705
706
  if (str.length === 0) {
@@ -731,6 +732,7 @@ var encode = function encode(str, defaultEncoder, charset) {
731
732
  || (c >= 0x30 && c <= 0x39) // 0-9
732
733
  || (c >= 0x41 && c <= 0x5A) // a-z
733
734
  || (c >= 0x61 && c <= 0x7A) // A-Z
735
+ || (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )
734
736
  ) {
735
737
  out += string.charAt(i);
736
738
  continue;
@@ -802,6 +804,17 @@ var combine = function combine(a, b) {
802
804
  return [].concat(a, b);
803
805
  };
804
806
 
807
+ var maybeMap = function maybeMap(val, fn) {
808
+ if (isArray(val)) {
809
+ var mapped = [];
810
+ for (var i = 0; i < val.length; i += 1) {
811
+ mapped.push(fn(val[i]));
812
+ }
813
+ return mapped;
814
+ }
815
+ return fn(val);
816
+ };
817
+
805
818
  module.exports = {
806
819
  arrayToObject: arrayToObject,
807
820
  assign: assign,
@@ -811,8 +824,9 @@ module.exports = {
811
824
  encode: encode,
812
825
  isBuffer: isBuffer,
813
826
  isRegExp: isRegExp,
827
+ maybeMap: maybeMap,
814
828
  merge: merge
815
829
  };
816
830
 
817
- },{}]},{},[2])(2)
831
+ },{"./formats":1}]},{},[2])(2)
818
832
  });
package/lib/formats.js CHANGED
@@ -3,24 +3,21 @@
3
3
  var replace = String.prototype.replace;
4
4
  var percentTwenties = /%20/g;
5
5
 
6
- var util = require('./utils');
7
-
8
6
  var Format = {
9
7
  RFC1738: 'RFC1738',
10
8
  RFC3986: 'RFC3986'
11
9
  };
12
10
 
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
- }
11
+ module.exports = {
12
+ 'default': Format.RFC3986,
13
+ formatters: {
14
+ RFC1738: function (value) {
15
+ return replace.call(value, percentTwenties, '+');
16
+ },
17
+ RFC3986: function (value) {
18
+ return String(value);
23
19
  }
24
20
  },
25
- Format
26
- );
21
+ RFC1738: Format.RFC1738,
22
+ RFC3986: Format.RFC3986
23
+ };
package/lib/parse.js CHANGED
@@ -85,15 +85,18 @@ var parseValues = function parseQueryStringValues(str, options) {
85
85
  val = options.strictNullHandling ? null : '';
86
86
  } else {
87
87
  key = options.decoder(part.slice(0, pos), defaults.decoder, charset, 'key');
88
- val = options.decoder(part.slice(pos + 1), defaults.decoder, charset, 'value');
88
+ val = utils.maybeMap(
89
+ parseArrayValue(part.slice(pos + 1), options),
90
+ function (encodedVal) {
91
+ return options.decoder(encodedVal, defaults.decoder, charset, 'value');
92
+ }
93
+ );
89
94
  }
90
95
 
91
96
  if (val && options.interpretNumericEntities && charset === 'iso-8859-1') {
92
97
  val = interpretNumericEntities(val);
93
98
  }
94
99
 
95
- val = parseArrayValue(val, options);
96
-
97
100
  if (part.indexOf('[]=') > -1) {
98
101
  val = isArray(val) ? [val] : val;
99
102
  }
@@ -108,8 +111,8 @@ var parseValues = function parseQueryStringValues(str, options) {
108
111
  return obj;
109
112
  };
110
113
 
111
- var parseObject = function (chain, val, options) {
112
- var leaf = parseArrayValue(val, options);
114
+ var parseObject = function (chain, val, options, valuesParsed) {
115
+ var leaf = valuesParsed ? val : parseArrayValue(val, options);
113
116
 
114
117
  for (var i = chain.length - 1; i >= 0; --i) {
115
118
  var obj;
@@ -143,7 +146,7 @@ var parseObject = function (chain, val, options) {
143
146
  return leaf;
144
147
  };
145
148
 
146
- var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
149
+ var parseKeys = function parseQueryStringKeys(givenKey, val, options, valuesParsed) {
147
150
  if (!givenKey) {
148
151
  return;
149
152
  }
@@ -194,7 +197,7 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
194
197
  keys.push('[' + key.slice(segment.index) + ']');
195
198
  }
196
199
 
197
- return parseObject(keys, val, options);
200
+ return parseObject(keys, val, options, valuesParsed);
198
201
  };
199
202
 
200
203
  var normalizeParseOptions = function normalizeParseOptions(opts) {
@@ -246,7 +249,7 @@ module.exports = function (str, opts) {
246
249
  var keys = Object.keys(tempObj);
247
250
  for (var i = 0; i < keys.length; ++i) {
248
251
  var key = keys[i];
249
- var newObj = parseKeys(key, tempObj[key], options);
252
+ var newObj = parseKeys(key, tempObj[key], options, typeof str === 'string');
250
253
  obj = utils.merge(obj, newObj, options);
251
254
  }
252
255
 
package/lib/stringify.js CHANGED
@@ -65,6 +65,7 @@ var stringify = function stringify(
65
65
  sort,
66
66
  allowDots,
67
67
  serializeDate,
68
+ format,
68
69
  formatter,
69
70
  encodeValuesOnly,
70
71
  charset
@@ -75,12 +76,17 @@ var stringify = function stringify(
75
76
  } else if (obj instanceof Date) {
76
77
  obj = serializeDate(obj);
77
78
  } else if (generateArrayPrefix === 'comma' && isArray(obj)) {
78
- obj = obj.join(',');
79
+ obj = utils.maybeMap(obj, function (value) {
80
+ if (value instanceof Date) {
81
+ return serializeDate(value);
82
+ }
83
+ return value;
84
+ });
79
85
  }
80
86
 
81
87
  if (obj === null) {
82
88
  if (strictNullHandling) {
83
- return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key') : prefix;
89
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder, charset, 'key', format) : prefix;
84
90
  }
85
91
 
86
92
  obj = '';
@@ -88,8 +94,8 @@ var stringify = function stringify(
88
94
 
89
95
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
90
96
  if (encoder) {
91
- var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key');
92
- return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value'))];
97
+ var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
98
+ return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
93
99
  }
94
100
  return [formatter(prefix) + '=' + formatter(String(obj))];
95
101
  }
@@ -101,7 +107,10 @@ var stringify = function stringify(
101
107
  }
102
108
 
103
109
  var objKeys;
104
- if (isArray(filter)) {
110
+ if (generateArrayPrefix === 'comma' && isArray(obj)) {
111
+ // we need to join elements in
112
+ objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : undefined }];
113
+ } else if (isArray(filter)) {
105
114
  objKeys = filter;
106
115
  } else {
107
116
  var keys = Object.keys(obj);
@@ -110,44 +119,32 @@ var stringify = function stringify(
110
119
 
111
120
  for (var i = 0; i < objKeys.length; ++i) {
112
121
  var key = objKeys[i];
122
+ var value = typeof key === 'object' && key.value !== undefined ? key.value : obj[key];
113
123
 
114
- if (skipNulls && obj[key] === null) {
124
+ if (skipNulls && value === null) {
115
125
  continue;
116
126
  }
117
127
 
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
- }
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
+ format,
144
+ formatter,
145
+ encodeValuesOnly,
146
+ charset
147
+ ));
151
148
  }
152
149
 
153
150
  return values;
@@ -191,6 +188,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
191
188
  encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
192
189
  encodeValuesOnly: typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
193
190
  filter: filter,
191
+ format: format,
194
192
  formatter: formatter,
195
193
  serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
196
194
  skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
@@ -256,6 +254,7 @@ module.exports = function (object, opts) {
256
254
  options.sort,
257
255
  options.allowDots,
258
256
  options.serializeDate,
257
+ options.format,
259
258
  options.formatter,
260
259
  options.encodeValuesOnly,
261
260
  options.charset
package/lib/utils.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ var formats = require('./formats');
4
+
3
5
  var has = Object.prototype.hasOwnProperty;
4
6
  var isArray = Array.isArray;
5
7
 
@@ -120,7 +122,7 @@ var decode = function (str, decoder, charset) {
120
122
  }
121
123
  };
122
124
 
123
- var encode = function encode(str, defaultEncoder, charset) {
125
+ var encode = function encode(str, defaultEncoder, charset, kind, format) {
124
126
  // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
125
127
  // It has been adapted here for stricter adherence to RFC 3986
126
128
  if (str.length === 0) {
@@ -152,6 +154,7 @@ var encode = function encode(str, defaultEncoder, charset) {
152
154
  || (c >= 0x30 && c <= 0x39) // 0-9
153
155
  || (c >= 0x41 && c <= 0x5A) // a-z
154
156
  || (c >= 0x61 && c <= 0x7A) // A-Z
157
+ || (format === formats.RFC1738 && (c === 0x28 || c === 0x29)) // ( )
155
158
  ) {
156
159
  out += string.charAt(i);
157
160
  continue;
@@ -223,6 +226,17 @@ var combine = function combine(a, b) {
223
226
  return [].concat(a, b);
224
227
  };
225
228
 
229
+ var maybeMap = function maybeMap(val, fn) {
230
+ if (isArray(val)) {
231
+ var mapped = [];
232
+ for (var i = 0; i < val.length; i += 1) {
233
+ mapped.push(fn(val[i]));
234
+ }
235
+ return mapped;
236
+ }
237
+ return fn(val);
238
+ };
239
+
226
240
  module.exports = {
227
241
  arrayToObject: arrayToObject,
228
242
  assign: assign,
@@ -232,5 +246,6 @@ module.exports = {
232
246
  encode: encode,
233
247
  isBuffer: isBuffer,
234
248
  isRegExp: isRegExp,
249
+ maybeMap: maybeMap,
235
250
  merge: merge
236
251
  };
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.9.2",
5
+ "version": "6.9.6",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -31,32 +31,32 @@
31
31
  },
32
32
  "dependencies": {},
33
33
  "devDependencies": {
34
- "@ljharb/eslint-config": "^16.0.0",
35
- "browserify": "^16.5.0",
36
- "covert": "^1.1.1",
34
+ "@ljharb/eslint-config": "^17.3.0",
35
+ "aud": "^1.1.3",
36
+ "browserify": "^16.5.2",
37
37
  "eclint": "^2.8.1",
38
- "eslint": "^6.8.0",
38
+ "eslint": "^7.17.0",
39
39
  "evalmd": "^0.0.19",
40
40
  "for-each": "^0.3.3",
41
41
  "has-symbols": "^1.0.1",
42
42
  "iconv-lite": "^0.5.1",
43
- "mkdirp": "^0.5.3",
44
- "object-inspect": "^1.7.0",
43
+ "mkdirp": "^0.5.5",
44
+ "nyc": "^10.3.2",
45
+ "object-inspect": "^1.9.0",
45
46
  "qs-iconv": "^1.0.4",
46
47
  "safe-publish-latest": "^1.1.4",
47
48
  "safer-buffer": "^2.1.2",
48
- "tape": "^5.0.0-next.5"
49
+ "tape": "^5.1.1"
49
50
  },
50
51
  "scripts": {
51
52
  "prepublish": "safe-publish-latest && npm run dist",
52
53
  "pretest": "npm run --silent readme && npm run --silent lint",
53
- "test": "npm run --silent coverage",
54
- "tests-only": "node test",
55
- "posttest": "npx aud",
54
+ "test": "npm run tests-only",
55
+ "tests-only": "nyc tape 'test/**/*.js'",
56
+ "posttest": "aud --production",
56
57
  "readme": "evalmd README.md",
57
58
  "postlint": "eclint check * lib/* test/*",
58
59
  "lint": "eslint lib/*.js test/*.js",
59
- "coverage": "covert test",
60
60
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
61
61
  },
62
62
  "license": "BSD-3-Clause",
package/test/parse.js CHANGED
@@ -429,6 +429,14 @@ test('parse()', function (t) {
429
429
  st.end();
430
430
  });
431
431
 
432
+ t.test('parses comma delimited array while having percent-encoded comma treated as normal text', function (st) {
433
+ st.deepEqual(qs.parse('foo=a%2Cb', { comma: true }), { foo: 'a,b' });
434
+ st.deepEqual(qs.parse('foo=a%2C%20b,d', { comma: true }), { foo: ['a, b', 'd'] });
435
+ st.deepEqual(qs.parse('foo=a%2C%20b,c%2C%20d', { comma: true }), { foo: ['a, b', 'c, d'] });
436
+
437
+ st.end();
438
+ });
439
+
432
440
  t.test('parses an object in dot notation', function (st) {
433
441
  var input = {
434
442
  'user.name': { 'pop[bob]': 3 },
package/test/stringify.js CHANGED
@@ -336,6 +336,27 @@ test('stringify()', function (t) {
336
336
  st.end();
337
337
  });
338
338
 
339
+ t.test('stringifies an empty array in different arrayFormat', function (st) {
340
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false }), 'b[0]=&c=c');
341
+ // arrayFormat default
342
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices' }), 'b[0]=&c=c');
343
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets' }), 'b[]=&c=c');
344
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat' }), 'b=&c=c');
345
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma' }), 'b=&c=c');
346
+ // with strictNullHandling
347
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', strictNullHandling: true }), 'b[0]&c=c');
348
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', strictNullHandling: true }), 'b[]&c=c');
349
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', strictNullHandling: true }), 'b&c=c');
350
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', strictNullHandling: true }), 'b&c=c');
351
+ // with skipNulls
352
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'indices', skipNulls: true }), 'c=c');
353
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'brackets', skipNulls: true }), 'c=c');
354
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'repeat', skipNulls: true }), 'c=c');
355
+ st.equal(qs.stringify({ a: [], b: [null], c: 'c' }, { encode: false, arrayFormat: 'comma', skipNulls: true }), 'c=c');
356
+
357
+ st.end();
358
+ });
359
+
339
360
  t.test('stringifies a null object', { skip: !Object.create }, function (st) {
340
361
  var obj = Object.create(null);
341
362
  obj.a = 'b';
@@ -595,13 +616,28 @@ test('stringify()', function (t) {
595
616
  'custom serializeDate function called'
596
617
  );
597
618
 
619
+ st.equal(
620
+ qs.stringify(
621
+ { a: [date] },
622
+ {
623
+ serializeDate: function (d) { return d.getTime(); },
624
+ arrayFormat: 'comma'
625
+ }
626
+ ),
627
+ 'a=' + date.getTime(),
628
+ 'works with arrayFormat comma'
629
+ );
630
+
598
631
  st.end();
599
632
  });
600
633
 
601
- t.test('RFC 1738 spaces serialization', function (st) {
634
+ t.test('RFC 1738 serialization', function (st) {
602
635
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
603
636
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
604
637
  st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b');
638
+
639
+ st.equal(qs.stringify({ 'foo(ref)': 'bar' }, { format: qs.formats.RFC1738 }), 'foo(ref)=bar');
640
+
605
641
  st.end();
606
642
  });
607
643
 
@@ -609,12 +645,14 @@ test('stringify()', function (t) {
609
645
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
610
646
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
611
647
  st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b');
648
+
612
649
  st.end();
613
650
  });
614
651
 
615
652
  t.test('Backward compatibility to RFC 3986', function (st) {
616
653
  st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
617
654
  st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b');
655
+
618
656
  st.end();
619
657
  });
620
658
 
@@ -734,5 +772,22 @@ test('stringify()', function (t) {
734
772
  st.end();
735
773
  });
736
774
 
775
+ t.test('objects inside arrays', function (st) {
776
+ var obj = { a: { b: { c: 'd', e: 'f' } } };
777
+ var withArray = { a: { b: [{ c: 'd', e: 'f' }] } };
778
+
779
+ st.equal(qs.stringify(obj, { encode: false }), 'a[b][c]=d&a[b][e]=f', 'no array, no arrayFormat');
780
+ st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'bracket' }), 'a[b][c]=d&a[b][e]=f', 'no array, bracket');
781
+ st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'indices' }), 'a[b][c]=d&a[b][e]=f', 'no array, indices');
782
+ st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'comma' }), 'a[b][c]=d&a[b][e]=f', 'no array, comma');
783
+
784
+ st.equal(qs.stringify(withArray, { encode: false }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, no arrayFormat');
785
+ st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'bracket' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, bracket');
786
+ st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'indices' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, indices');
787
+ st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'comma' }), '???', 'array, comma (pending issue #378)', { skip: true });
788
+
789
+ st.end();
790
+ });
791
+
737
792
  t.end();
738
793
  });
@@ -1,15 +0,0 @@
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/test/.eslintrc DELETED
@@ -1,18 +0,0 @@
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/index.js DELETED
@@ -1,7 +0,0 @@
1
- 'use strict';
2
-
3
- require('./parse');
4
-
5
- require('./stringify');
6
-
7
- require('./utils');