qs 6.3.3 → 6.5.1

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/lib/stringify.js CHANGED
@@ -4,37 +4,32 @@ var utils = require('./utils');
4
4
  var formats = require('./formats');
5
5
 
6
6
  var arrayPrefixGenerators = {
7
- brackets: function brackets(prefix) {
7
+ brackets: function brackets(prefix) { // eslint-disable-line func-name-matching
8
8
  return prefix + '[]';
9
9
  },
10
- indices: function indices(prefix, key) {
10
+ indices: function indices(prefix, key) { // eslint-disable-line func-name-matching
11
11
  return prefix + '[' + key + ']';
12
12
  },
13
- repeat: function repeat(prefix) {
13
+ repeat: function repeat(prefix) { // eslint-disable-line func-name-matching
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
-
24
18
  var toISO = Date.prototype.toISOString;
25
19
 
26
20
  var defaults = {
27
21
  delimiter: '&',
28
22
  encode: true,
29
23
  encoder: utils.encode,
30
- serializeDate: function serializeDate(date) {
24
+ encodeValuesOnly: false,
25
+ serializeDate: function serializeDate(date) { // eslint-disable-line func-name-matching
31
26
  return toISO.call(date);
32
27
  },
33
28
  skipNulls: false,
34
29
  strictNullHandling: false
35
30
  };
36
31
 
37
- var stringify = function stringify(
32
+ var stringify = function stringify( // eslint-disable-line func-name-matching
38
33
  object,
39
34
  prefix,
40
35
  generateArrayPrefix,
@@ -45,18 +40,17 @@ var stringify = function stringify(
45
40
  sort,
46
41
  allowDots,
47
42
  serializeDate,
48
- formatter
43
+ formatter,
44
+ encodeValuesOnly
49
45
  ) {
50
46
  var obj = object;
51
47
  if (typeof filter === 'function') {
52
48
  obj = filter(prefix, obj);
53
49
  } else if (obj instanceof Date) {
54
50
  obj = serializeDate(obj);
55
- }
56
-
57
- if (obj === null) {
51
+ } else if (obj === null) {
58
52
  if (strictNullHandling) {
59
- return encoder ? encoder(prefix) : prefix;
53
+ return encoder && !encodeValuesOnly ? encoder(prefix, defaults.encoder) : prefix;
60
54
  }
61
55
 
62
56
  obj = '';
@@ -64,7 +58,8 @@ var stringify = function stringify(
64
58
 
65
59
  if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || utils.isBuffer(obj)) {
66
60
  if (encoder) {
67
- return [formatter(encoder(prefix)) + '=' + formatter(encoder(obj))];
61
+ var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder);
62
+ return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder))];
68
63
  }
69
64
  return [formatter(prefix) + '=' + formatter(String(obj))];
70
65
  }
@@ -76,7 +71,7 @@ var stringify = function stringify(
76
71
  }
77
72
 
78
73
  var objKeys;
79
- if (isArray(filter)) {
74
+ if (Array.isArray(filter)) {
80
75
  objKeys = filter;
81
76
  } else {
82
77
  var keys = Object.keys(obj);
@@ -90,8 +85,8 @@ var stringify = function stringify(
90
85
  continue;
91
86
  }
92
87
 
93
- if (isArray(obj)) {
94
- pushToArray(values, stringify(
88
+ if (Array.isArray(obj)) {
89
+ values = values.concat(stringify(
95
90
  obj[key],
96
91
  generateArrayPrefix(prefix, key),
97
92
  generateArrayPrefix,
@@ -102,10 +97,11 @@ var stringify = function stringify(
102
97
  sort,
103
98
  allowDots,
104
99
  serializeDate,
105
- formatter
100
+ formatter,
101
+ encodeValuesOnly
106
102
  ));
107
103
  } else {
108
- pushToArray(values, stringify(
104
+ values = values.concat(stringify(
109
105
  obj[key],
110
106
  prefix + (allowDots ? '.' + key : '[' + key + ']'),
111
107
  generateArrayPrefix,
@@ -116,7 +112,8 @@ var stringify = function stringify(
116
112
  sort,
117
113
  allowDots,
118
114
  serializeDate,
119
- formatter
115
+ formatter,
116
+ encodeValuesOnly
120
117
  ));
121
118
  }
122
119
  }
@@ -126,9 +123,9 @@ var stringify = function stringify(
126
123
 
127
124
  module.exports = function (object, opts) {
128
125
  var obj = object;
129
- var options = opts || {};
126
+ var options = opts ? utils.assign({}, opts) : {};
130
127
 
131
- if (options.encoder !== null && typeof options.encoder !== 'undefined' && typeof options.encoder !== 'function') {
128
+ if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
132
129
  throw new TypeError('Encoder has to be a function.');
133
130
  }
134
131
 
@@ -136,10 +133,11 @@ module.exports = function (object, opts) {
136
133
  var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
137
134
  var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
138
135
  var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
139
- var encoder = encode ? typeof options.encoder === 'function' ? options.encoder : defaults.encoder : null;
136
+ var encoder = typeof options.encoder === 'function' ? options.encoder : defaults.encoder;
140
137
  var sort = typeof options.sort === 'function' ? options.sort : null;
141
138
  var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
142
139
  var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate;
140
+ var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly;
143
141
  if (typeof options.format === 'undefined') {
144
142
  options.format = formats['default'];
145
143
  } else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) {
@@ -152,7 +150,7 @@ module.exports = function (object, opts) {
152
150
  if (typeof options.filter === 'function') {
153
151
  filter = options.filter;
154
152
  obj = filter('', obj);
155
- } else if (isArray(options.filter)) {
153
+ } else if (Array.isArray(options.filter)) {
156
154
  filter = options.filter;
157
155
  objKeys = filter;
158
156
  }
@@ -188,20 +186,25 @@ module.exports = function (object, opts) {
188
186
  if (skipNulls && obj[key] === null) {
189
187
  continue;
190
188
  }
191
- pushToArray(keys, stringify(
189
+
190
+ keys = keys.concat(stringify(
192
191
  obj[key],
193
192
  key,
194
193
  generateArrayPrefix,
195
194
  strictNullHandling,
196
195
  skipNulls,
197
- encoder,
196
+ encode ? encoder : null,
198
197
  filter,
199
198
  sort,
200
199
  allowDots,
201
200
  serializeDate,
202
- formatter
201
+ formatter,
202
+ encodeValuesOnly
203
203
  ));
204
204
  }
205
205
 
206
- return keys.join(delimiter);
206
+ var joined = keys.join(delimiter);
207
+ var prefix = options.addQueryPrefix === true ? '?' : '';
208
+
209
+ return joined.length > 0 ? prefix + joined : '';
207
210
  };
package/lib/utils.js CHANGED
@@ -11,7 +11,30 @@ var hexTable = (function () {
11
11
  return array;
12
12
  }());
13
13
 
14
- exports.arrayToObject = function (source, options) {
14
+ var compactQueue = function compactQueue(queue) {
15
+ var obj;
16
+
17
+ while (queue.length) {
18
+ var item = queue.pop();
19
+ obj = item.obj[item.prop];
20
+
21
+ if (Array.isArray(obj)) {
22
+ var compacted = [];
23
+
24
+ for (var j = 0; j < obj.length; ++j) {
25
+ if (typeof obj[j] !== 'undefined') {
26
+ compacted.push(obj[j]);
27
+ }
28
+ }
29
+
30
+ item.obj[item.prop] = compacted;
31
+ }
32
+ }
33
+
34
+ return obj;
35
+ };
36
+
37
+ exports.arrayToObject = function arrayToObject(source, options) {
15
38
  var obj = options && options.plainObjects ? Object.create(null) : {};
16
39
  for (var i = 0; i < source.length; ++i) {
17
40
  if (typeof source[i] !== 'undefined') {
@@ -22,7 +45,7 @@ exports.arrayToObject = function (source, options) {
22
45
  return obj;
23
46
  };
24
47
 
25
- exports.merge = function (target, source, options) {
48
+ exports.merge = function merge(target, source, options) {
26
49
  if (!source) {
27
50
  return target;
28
51
  }
@@ -30,8 +53,8 @@ exports.merge = function (target, source, options) {
30
53
  if (typeof source !== 'object') {
31
54
  if (Array.isArray(target)) {
32
55
  target.push(source);
33
- } else if (target && typeof target === 'object') {
34
- if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
56
+ } else if (typeof target === 'object') {
57
+ if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
35
58
  target[source] = true;
36
59
  }
37
60
  } else {
@@ -41,7 +64,7 @@ exports.merge = function (target, source, options) {
41
64
  return target;
42
65
  }
43
66
 
44
- if (!target || typeof target !== 'object') {
67
+ if (typeof target !== 'object') {
45
68
  return [target].concat(source);
46
69
  }
47
70
 
@@ -68,7 +91,7 @@ exports.merge = function (target, source, options) {
68
91
  return Object.keys(source).reduce(function (acc, key) {
69
92
  var value = source[key];
70
93
 
71
- if (Object.prototype.hasOwnProperty.call(acc, key)) {
94
+ if (has.call(acc, key)) {
72
95
  acc[key] = exports.merge(acc[key], value, options);
73
96
  } else {
74
97
  acc[key] = value;
@@ -77,6 +100,13 @@ exports.merge = function (target, source, options) {
77
100
  }, mergeTarget);
78
101
  };
79
102
 
103
+ exports.assign = function assignSingleSource(target, source) {
104
+ return Object.keys(source).reduce(function (acc, key) {
105
+ acc[key] = source[key];
106
+ return acc;
107
+ }, target);
108
+ };
109
+
80
110
  exports.decode = function (str) {
81
111
  try {
82
112
  return decodeURIComponent(str.replace(/\+/g, ' '));
@@ -85,7 +115,7 @@ exports.decode = function (str) {
85
115
  }
86
116
  };
87
117
 
88
- exports.encode = function (str) {
118
+ exports.encode = function encode(str) {
89
119
  // This code was originally written by Brian White (mscdex) for the io.js core querystring library.
90
120
  // It has been adapted here for stricter adherence to RFC 3986
91
121
  if (str.length === 0) {
@@ -128,7 +158,6 @@ exports.encode = function (str) {
128
158
 
129
159
  i += 1;
130
160
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
131
- /* eslint operator-linebreak: [2, "before"] */
132
161
  out += hexTable[0xF0 | (c >> 18)]
133
162
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
134
163
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
@@ -138,46 +167,33 @@ exports.encode = function (str) {
138
167
  return out;
139
168
  };
140
169
 
141
- exports.compact = function (obj, references) {
142
- if (typeof obj !== 'object' || obj === null) {
143
- return obj;
144
- }
145
-
146
- var refs = references || [];
147
- var lookup = refs.indexOf(obj);
148
- if (lookup !== -1) {
149
- return refs[lookup];
150
- }
151
-
152
- refs.push(obj);
153
-
154
- if (Array.isArray(obj)) {
155
- var compacted = [];
156
-
157
- for (var i = 0; i < obj.length; ++i) {
158
- if (obj[i] && typeof obj[i] === 'object') {
159
- compacted.push(exports.compact(obj[i], refs));
160
- } else if (typeof obj[i] !== 'undefined') {
161
- compacted.push(obj[i]);
170
+ exports.compact = function compact(value) {
171
+ var queue = [{ obj: { o: value }, prop: 'o' }];
172
+ var refs = [];
173
+
174
+ for (var i = 0; i < queue.length; ++i) {
175
+ var item = queue[i];
176
+ var obj = item.obj[item.prop];
177
+
178
+ var keys = Object.keys(obj);
179
+ for (var j = 0; j < keys.length; ++j) {
180
+ var key = keys[j];
181
+ var val = obj[key];
182
+ if (typeof val === 'object' && val !== null && refs.indexOf(val) === -1) {
183
+ queue.push({ obj: obj, prop: key });
184
+ refs.push(val);
162
185
  }
163
186
  }
164
-
165
- return compacted;
166
187
  }
167
188
 
168
- var keys = Object.keys(obj);
169
- keys.forEach(function (key) {
170
- obj[key] = exports.compact(obj[key], refs);
171
- });
172
-
173
- return obj;
189
+ return compactQueue(queue);
174
190
  };
175
191
 
176
- exports.isRegExp = function (obj) {
192
+ exports.isRegExp = function isRegExp(obj) {
177
193
  return Object.prototype.toString.call(obj) === '[object RegExp]';
178
194
  };
179
195
 
180
- exports.isBuffer = function (obj) {
196
+ exports.isBuffer = function isBuffer(obj) {
181
197
  if (obj === null || typeof obj === 'undefined') {
182
198
  return false;
183
199
  }
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.3.3",
5
+ "version": "6.5.1",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -22,32 +22,29 @@
22
22
  "engines": {
23
23
  "node": ">=0.6"
24
24
  },
25
+ "dependencies": {},
25
26
  "devDependencies": {
26
- "@ljharb/eslint-config": "^20.1.0",
27
- "aud": "^1.1.5",
28
- "browserify": "^16.5.2",
29
- "eclint": "^2.8.1",
30
- "eslint": "^8.6.0",
27
+ "@ljharb/eslint-config": "^12.2.1",
28
+ "browserify": "^14.4.0",
29
+ "covert": "^1.1.0",
30
+ "editorconfig-tools": "^0.1.1",
31
+ "eslint": "^4.6.1",
31
32
  "evalmd": "^0.0.17",
32
- "iconv-lite": "^0.4.24",
33
- "in-publish": "^2.0.1",
33
+ "iconv-lite": "^0.4.18",
34
34
  "mkdirp": "^0.5.1",
35
- "nyc": "^10.3.2",
36
35
  "qs-iconv": "^1.0.4",
37
- "safe-publish-latest": "^2.0.0",
38
- "safer-buffer": "^2.1.2",
39
- "tape": "^5.4.0"
36
+ "safe-publish-latest": "^1.1.1",
37
+ "tape": "^4.8.0"
40
38
  },
41
39
  "scripts": {
42
- "prepublishOnly": "safe-publish-latest && npm run dist",
43
- "prepublish": "not-in-publish || npm run prepublishOnly",
40
+ "prepublish": "safe-publish-latest && npm run dist",
44
41
  "pretest": "npm run --silent readme && npm run --silent lint",
45
- "test": "npm run --silent tests-only",
46
- "tests-only": "nyc tape 'test/**/*.js'",
47
- "posttest": "aud --production",
42
+ "test": "npm run --silent coverage",
43
+ "tests-only": "node test",
48
44
  "readme": "evalmd README.md",
49
- "postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')",
50
- "lint": "eslint --ext=js,mjs .",
45
+ "prelint": "editorconfig-tools check * lib/* test/*",
46
+ "lint": "eslint lib/*.js test/*.js",
47
+ "coverage": "covert test",
51
48
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
52
49
  },
53
50
  "license": "BSD-3-Clause"
package/test/.eslintrc ADDED
@@ -0,0 +1,15 @@
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
+ }
package/test/parse.js CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
+ var utils = require('../lib/utils');
5
6
  var iconv = require('iconv-lite');
6
- var SaferBuffer = require('safer-buffer').Buffer;
7
7
 
8
8
  test('parse()', function (t) {
9
9
  t.test('parses a simple string', function (st) {
@@ -231,7 +231,7 @@ test('parse()', function (t) {
231
231
  });
232
232
 
233
233
  t.test('parses buffers correctly', function (st) {
234
- var b = SaferBuffer.from('test');
234
+ var b = new Buffer('test');
235
235
  st.deepEqual(qs.parse({ a: b }), { a: b });
236
236
  st.end();
237
237
  });
@@ -256,7 +256,7 @@ test('parse()', function (t) {
256
256
  st.end();
257
257
  });
258
258
 
259
- t.test('should not throw when a native prototype has an enumerable property', function (st) {
259
+ t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) {
260
260
  Object.prototype.crash = '';
261
261
  Array.prototype.crash = '';
262
262
  st.doesNotThrow(qs.parse.bind(null, 'a=b'));
@@ -301,14 +301,14 @@ test('parse()', function (t) {
301
301
  });
302
302
 
303
303
  t.test('allows disabling array parsing', function (st) {
304
- var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
305
- st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
306
- st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
307
-
308
- var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
309
- st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
310
- st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
304
+ st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { 0: 'b', 1: 'c' } });
305
+ st.end();
306
+ });
311
307
 
308
+ t.test('allows for query string prefix', function (st) {
309
+ st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
310
+ st.deepEqual(qs.parse('foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
311
+ st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: false }), { '?foo': 'bar' });
312
312
  st.end();
313
313
  });
314
314
 
@@ -396,6 +396,33 @@ test('parse()', function (t) {
396
396
  st.end();
397
397
  });
398
398
 
399
+ t.test('does not crash when parsing deep objects', function (st) {
400
+ var parsed;
401
+ var str = 'foo';
402
+
403
+ for (var i = 0; i < 5000; i++) {
404
+ str += '[p]';
405
+ }
406
+
407
+ str += '=bar';
408
+
409
+ st.doesNotThrow(function () {
410
+ parsed = qs.parse(str, { depth: 5000 });
411
+ });
412
+
413
+ st.equal('foo' in parsed, true, 'parsed has "foo" property');
414
+
415
+ var depth = 0;
416
+ var ref = parsed.foo;
417
+ while ((ref = ref.p)) {
418
+ depth += 1;
419
+ }
420
+
421
+ st.equal(depth, 5000, 'parsed is 5000 properties deep');
422
+
423
+ st.end();
424
+ });
425
+
399
426
  t.test('parses null objects correctly', { skip: !Object.create }, function (st) {
400
427
  var a = Object.create(null);
401
428
  a.b = 'c';
@@ -480,73 +507,13 @@ test('parse()', function (t) {
480
507
 
481
508
  st.deepEqual(
482
509
  qs.parse('a[b]=c&a=toString', { plainObjects: true }),
483
- { __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
510
+ { a: { b: 'c', toString: true } },
484
511
  'can overwrite prototype with plainObjects true'
485
512
  );
486
513
 
487
514
  st.end();
488
515
  });
489
516
 
490
- t.test('dunder proto is ignored', function (st) {
491
- var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
492
- var result = qs.parse(payload, { allowPrototypes: true });
493
-
494
- st.deepEqual(
495
- result,
496
- {
497
- categories: {
498
- length: '42'
499
- }
500
- },
501
- 'silent [[Prototype]] payload'
502
- );
503
-
504
- var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
505
-
506
- st.deepEqual(
507
- plainResult,
508
- {
509
- __proto__: null,
510
- categories: {
511
- __proto__: null,
512
- length: '42'
513
- }
514
- },
515
- 'silent [[Prototype]] payload: plain objects'
516
- );
517
-
518
- var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
519
-
520
- st.notOk(Array.isArray(query.categories), 'is not an array');
521
- st.notOk(query.categories instanceof Array, 'is not instanceof an array');
522
- st.deepEqual(query.categories, { some: { json: 'toInject' } });
523
- st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
524
-
525
- st.deepEqual(
526
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
527
- {
528
- foo: {
529
- bar: 'stuffs'
530
- }
531
- },
532
- 'hidden values'
533
- );
534
-
535
- st.deepEqual(
536
- qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
537
- {
538
- __proto__: null,
539
- foo: {
540
- __proto__: null,
541
- bar: 'stuffs'
542
- }
543
- },
544
- 'hidden values: plain objects'
545
- );
546
-
547
- st.end();
548
- });
549
-
550
517
  t.test('can return null objects', { skip: !Object.create }, function (st) {
551
518
  var expected = Object.create(null);
552
519
  expected.a = Object.create(null);
@@ -572,16 +539,35 @@ test('parse()', function (t) {
572
539
  result.push(parseInt(parts[1], 16));
573
540
  parts = reg.exec(str);
574
541
  }
575
- return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString();
542
+ return iconv.decode(new Buffer(result), 'shift_jis').toString();
576
543
  }
577
544
  }), { 県: '大阪府' });
578
545
  st.end();
579
546
  });
580
547
 
548
+ t.test('receives the default decoder as a second argument', function (st) {
549
+ st.plan(1);
550
+ qs.parse('a', {
551
+ decoder: function (str, defaultDecoder) {
552
+ st.equal(defaultDecoder, utils.decode);
553
+ }
554
+ });
555
+ st.end();
556
+ });
557
+
581
558
  t.test('throws error with wrong decoder', function (st) {
582
559
  st['throws'](function () {
583
560
  qs.parse({}, { decoder: 'string' });
584
561
  }, new TypeError('Decoder has to be a function.'));
585
562
  st.end();
586
563
  });
564
+
565
+ t.test('does not mutate the options argument', function (st) {
566
+ var options = {};
567
+ qs.parse('a[b]=true', options);
568
+ st.deepEqual(options, {});
569
+ st.end();
570
+ });
571
+
572
+ t.end();
587
573
  });