qs 6.4.0 → 6.4.2

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/parse.js CHANGED
@@ -18,7 +18,7 @@ var defaults = {
18
18
 
19
19
  var parseValues = function parseQueryStringValues(str, options) {
20
20
  var obj = {};
21
- var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
21
+ var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? void undefined : options.parameterLimit);
22
22
 
23
23
  for (var i = 0; i < parts.length; ++i) {
24
24
  var part = parts[i];
@@ -50,23 +50,25 @@ var parseObject = function parseObjectRecursive(chain, val, options) {
50
50
  var root = chain.shift();
51
51
 
52
52
  var obj;
53
- if (root === '[]') {
53
+ if (root === '[]' && options.parseArrays) {
54
54
  obj = [];
55
55
  obj = obj.concat(parseObject(chain, val, options));
56
56
  } else {
57
57
  obj = options.plainObjects ? Object.create(null) : {};
58
58
  var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
59
59
  var index = parseInt(cleanRoot, 10);
60
- if (
61
- !isNaN(index) &&
62
- root !== cleanRoot &&
63
- String(index) === cleanRoot &&
64
- index >= 0 &&
65
- (options.parseArrays && index <= options.arrayLimit)
60
+ if (!options.parseArrays && cleanRoot === '') {
61
+ obj = { 0: val };
62
+ } else if (
63
+ !isNaN(index)
64
+ && root !== cleanRoot
65
+ && String(index) === cleanRoot
66
+ && index >= 0
67
+ && (options.parseArrays && index <= options.arrayLimit)
66
68
  ) {
67
69
  obj = [];
68
70
  obj[index] = parseObject(chain, val, options);
69
- } else {
71
+ } else if (cleanRoot !== '__proto__') {
70
72
  obj[cleanRoot] = parseObject(chain, val, options);
71
73
  }
72
74
  }
@@ -96,15 +98,14 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
96
98
 
97
99
  var keys = [];
98
100
  if (parent) {
99
- // If we aren't using plain objects, optionally prefix keys
100
- // that would overwrite object prototype properties
101
+ // If we aren't using plain objects, optionally prefix keys that would overwrite object prototype properties
101
102
  if (!options.plainObjects && has.call(Object.prototype, parent)) {
102
103
  if (!options.allowPrototypes) {
103
104
  return;
104
105
  }
105
106
  }
106
107
 
107
- keys.push(parent);
108
+ keys[keys.length] = parent;
108
109
  }
109
110
 
110
111
  // Loop through children appending to the array until we hit depth
@@ -117,13 +118,13 @@ var parseKeys = function parseQueryStringKeys(givenKey, val, options) {
117
118
  return;
118
119
  }
119
120
  }
120
- keys.push(segment[1]);
121
+ keys[keys.length] = segment[1];
121
122
  }
122
123
 
123
124
  // If there's a remainder, just add whatever is left
124
125
 
125
126
  if (segment) {
126
- keys.push('[' + key.slice(segment.index) + ']');
127
+ keys[keys.length] = '[' + key.slice(segment.index + ']');
127
128
  }
128
129
 
129
130
  return parseObject(keys, val, options);
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) : 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 || {};
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
 
@@ -139,7 +147,7 @@ module.exports = function (object, opts) {
139
147
  var serializeDate = typeof options.serializeDate === 'function' ? options.serializeDate : defaults.serializeDate;
140
148
  var encodeValuesOnly = typeof options.encodeValuesOnly === 'boolean' ? options.encodeValuesOnly : defaults.encodeValuesOnly;
141
149
  if (typeof options.format === 'undefined') {
142
- options.format = formats.default;
150
+ options.format = formats['default'];
143
151
  } else if (!Object.prototype.hasOwnProperty.call(formats.formatters, options.format)) {
144
152
  throw new TypeError('Unknown format option provided.');
145
153
  }
@@ -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
@@ -5,7 +5,7 @@ var has = Object.prototype.hasOwnProperty;
5
5
  var hexTable = (function () {
6
6
  var array = [];
7
7
  for (var i = 0; i < 256; ++i) {
8
- array.push('%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase());
8
+ array[array.length] = '%' + ((i < 16 ? '0' : '' + i.toString(16)).toUpperCase());
9
9
  }
10
10
 
11
11
  return array;
@@ -29,9 +29,9 @@ exports.merge = function (target, source, options) {
29
29
 
30
30
  if (typeof source !== 'object') {
31
31
  if (Array.isArray(target)) {
32
- target.push(source);
33
- } else if (typeof target === 'object') {
34
- if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
32
+ target[target.length] = source;
33
+ } else if (target && typeof target === 'object') {
34
+ if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
35
35
  target[source] = true;
36
36
  }
37
37
  } else {
@@ -41,7 +41,7 @@ exports.merge = function (target, source, options) {
41
41
  return target;
42
42
  }
43
43
 
44
- if (typeof target !== 'object') {
44
+ if (!target || typeof target !== 'object') {
45
45
  return [target].concat(source);
46
46
  }
47
47
 
@@ -56,7 +56,7 @@ exports.merge = function (target, source, options) {
56
56
  if (target[i] && typeof target[i] === 'object') {
57
57
  target[i] = exports.merge(target[i], item, options);
58
58
  } else {
59
- target.push(item);
59
+ target[target.length] = item;
60
60
  }
61
61
  } else {
62
62
  target[i] = item;
@@ -99,13 +99,13 @@ exports.encode = function (str) {
99
99
  var c = string.charCodeAt(i);
100
100
 
101
101
  if (
102
- c === 0x2D || // -
103
- c === 0x2E || // .
104
- c === 0x5F || // _
105
- c === 0x7E || // ~
106
- (c >= 0x30 && c <= 0x39) || // 0-9
107
- (c >= 0x41 && c <= 0x5A) || // a-z
108
- (c >= 0x61 && c <= 0x7A) // A-Z
102
+ c === 0x2D // -
103
+ || c === 0x2E // .
104
+ || c === 0x5F // _
105
+ || c === 0x7E // ~
106
+ || (c >= 0x30 && c <= 0x39) // 0-9
107
+ || (c >= 0x41 && c <= 0x5A) // a-z
108
+ || (c >= 0x61 && c <= 0x7A) // A-Z
109
109
  ) {
110
110
  out += string.charAt(i);
111
111
  continue;
@@ -128,7 +128,11 @@ exports.encode = function (str) {
128
128
 
129
129
  i += 1;
130
130
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
131
- out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]; // eslint-disable-line max-len
131
+ /* eslint operator-linebreak: [2, "before"] */
132
+ out += hexTable[0xF0 | (c >> 18)]
133
+ + hexTable[0x80 | ((c >> 12) & 0x3F)]
134
+ + hexTable[0x80 | ((c >> 6) & 0x3F)]
135
+ + hexTable[0x80 | (c & 0x3F)];
132
136
  }
133
137
 
134
138
  return out;
@@ -145,16 +149,16 @@ exports.compact = function (obj, references) {
145
149
  return refs[lookup];
146
150
  }
147
151
 
148
- refs.push(obj);
152
+ refs[refs.length] = obj;
149
153
 
150
154
  if (Array.isArray(obj)) {
151
155
  var compacted = [];
152
156
 
153
157
  for (var i = 0; i < obj.length; ++i) {
154
158
  if (obj[i] && typeof obj[i] === 'object') {
155
- compacted.push(exports.compact(obj[i], refs));
159
+ compacted[compacted.length] = exports.compact(obj[i], refs);
156
160
  } else if (typeof obj[i] !== 'undefined') {
157
- compacted.push(obj[i]);
161
+ compacted[compacted.length] = obj[i];
158
162
  }
159
163
  }
160
164
 
package/package.json CHANGED
@@ -1,50 +1,54 @@
1
1
  {
2
- "name": "qs",
3
- "description": "A querystring parser that supports nesting and arrays, with a depth limit",
4
- "homepage": "https://github.com/ljharb/qs",
5
- "version": "6.4.0",
6
- "repository": {
7
- "type": "git",
8
- "url": "https://github.com/ljharb/qs.git"
9
- },
10
- "main": "lib/index.js",
11
- "contributors": [
12
- {
13
- "name": "Jordan Harband",
14
- "email": "ljharb@gmail.com",
15
- "url": "http://ljharb.codes"
16
- }
17
- ],
18
- "keywords": [
19
- "querystring",
20
- "qs"
21
- ],
22
- "engines": {
23
- "node": ">=0.6"
24
- },
25
- "dependencies": {},
26
- "devDependencies": {
27
- "@ljharb/eslint-config": "^11.0.0",
28
- "browserify": "^14.1.0",
29
- "covert": "^1.1.0",
30
- "eslint": "^3.17.0",
31
- "evalmd": "^0.0.17",
32
- "iconv-lite": "^0.4.15",
33
- "mkdirp": "^0.5.1",
34
- "parallelshell": "^2.0.0",
35
- "qs-iconv": "^1.0.4",
36
- "safe-publish-latest": "^1.1.1",
37
- "tape": "^4.6.3"
38
- },
39
- "scripts": {
40
- "prepublish": "safe-publish-latest && npm run dist",
41
- "pretest": "npm run --silent readme && npm run --silent lint",
42
- "test": "npm run --silent coverage",
43
- "tests-only": "node test",
44
- "readme": "evalmd README.md",
45
- "lint": "eslint lib/*.js test/*.js",
46
- "coverage": "covert test",
47
- "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
48
- },
49
- "license": "BSD-3-Clause"
2
+ "name": "qs",
3
+ "description": "A querystring parser that supports nesting and arrays, with a depth limit",
4
+ "homepage": "https://github.com/ljharb/qs",
5
+ "version": "6.4.2",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/ljharb/qs.git"
9
+ },
10
+ "main": "lib/index.js",
11
+ "contributors": [
12
+ {
13
+ "name": "Jordan Harband",
14
+ "email": "ljharb@gmail.com",
15
+ "url": "http://ljharb.codes"
16
+ }
17
+ ],
18
+ "keywords": [
19
+ "querystring",
20
+ "qs"
21
+ ],
22
+ "engines": {
23
+ "node": ">=0.6"
24
+ },
25
+ "devDependencies": {
26
+ "@ljharb/eslint-config": "^20.1.0",
27
+ "aud": "^1.1.5",
28
+ "browserify": "^16.5.2",
29
+ "eclint": "^2.8.1",
30
+ "eslint": "^8.6.0",
31
+ "evalmd": "^0.0.17",
32
+ "iconv-lite": "^0.4.24",
33
+ "in-publish": "^2.0.1",
34
+ "mkdirp": "^0.5.1",
35
+ "nyc": "^10.3.2",
36
+ "qs-iconv": "^1.0.4",
37
+ "safe-publish-latest": "^2.0.0",
38
+ "safer-buffer": "^2.1.2",
39
+ "tape": "^5.4.0"
40
+ },
41
+ "scripts": {
42
+ "prepublishOnly": "safe-publish-latest && npm run dist",
43
+ "prepublish": "not-in-publish || npm run prepublishOnly",
44
+ "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",
48
+ "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 .",
51
+ "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
52
+ },
53
+ "license": "BSD-3-Clause"
50
54
  }
package/test/parse.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
5
  var iconv = require('iconv-lite');
6
+ var SaferBuffer = require('safer-buffer').Buffer;
6
7
 
7
8
  test('parse()', function (t) {
8
9
  t.test('parses a simple string', function (st) {
@@ -230,7 +231,7 @@ test('parse()', function (t) {
230
231
  });
231
232
 
232
233
  t.test('parses buffers correctly', function (st) {
233
- var b = new Buffer('test');
234
+ var b = SaferBuffer.from('test');
234
235
  st.deepEqual(qs.parse({ a: b }), { a: b });
235
236
  st.end();
236
237
  });
@@ -255,7 +256,7 @@ test('parse()', function (t) {
255
256
  st.end();
256
257
  });
257
258
 
258
- t.test('should not throw when a native prototype has an enumerable property', { parallel: false }, function (st) {
259
+ t.test('should not throw when a native prototype has an enumerable property', function (st) {
259
260
  Object.prototype.crash = '';
260
261
  Array.prototype.crash = '';
261
262
  st.doesNotThrow(qs.parse.bind(null, 'a=b'));
@@ -300,7 +301,14 @@ test('parse()', function (t) {
300
301
  });
301
302
 
302
303
  t.test('allows disabling array parsing', function (st) {
303
- st.deepEqual(qs.parse('a[0]=b&a[1]=c', { parseArrays: false }), { a: { 0: 'b', 1: 'c' } });
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');
311
+
304
312
  st.end();
305
313
  });
306
314
 
@@ -472,13 +480,73 @@ test('parse()', function (t) {
472
480
 
473
481
  st.deepEqual(
474
482
  qs.parse('a[b]=c&a=toString', { plainObjects: true }),
475
- { a: { b: 'c', toString: true } },
483
+ { __proto__: null, a: { __proto__: null, b: 'c', toString: true } },
476
484
  'can overwrite prototype with plainObjects true'
477
485
  );
478
486
 
479
487
  st.end();
480
488
  });
481
489
 
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
+
482
550
  t.test('can return null objects', { skip: !Object.create }, function (st) {
483
551
  var expected = Object.create(null);
484
552
  expected.a = Object.create(null);
@@ -504,14 +572,14 @@ test('parse()', function (t) {
504
572
  result.push(parseInt(parts[1], 16));
505
573
  parts = reg.exec(str);
506
574
  }
507
- return iconv.decode(new Buffer(result), 'shift_jis').toString();
575
+ return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString();
508
576
  }
509
577
  }), { 県: '大阪府' });
510
578
  st.end();
511
579
  });
512
580
 
513
581
  t.test('throws error with wrong decoder', function (st) {
514
- st.throws(function () {
582
+ st['throws'](function () {
515
583
  qs.parse({}, { decoder: 'string' });
516
584
  }, new TypeError('Decoder has to be a function.'));
517
585
  st.end();
package/test/stringify.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
5
  var iconv = require('iconv-lite');
6
+ var SaferBuffer = require('safer-buffer').Buffer;
6
7
 
7
8
  test('stringify()', function (t) {
8
9
  t.test('stringifies a querystring object', function (st) {
@@ -325,8 +326,8 @@ test('stringify()', function (t) {
325
326
  });
326
327
 
327
328
  t.test('stringifies buffer values', function (st) {
328
- st.equal(qs.stringify({ a: new Buffer('test') }), 'a=test');
329
- st.equal(qs.stringify({ a: { b: new Buffer('test') } }), 'a%5Bb%5D=test');
329
+ st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test');
330
+ st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test');
330
331
  st.end();
331
332
  });
332
333
 
@@ -453,14 +454,14 @@ test('stringify()', function (t) {
453
454
  });
454
455
 
455
456
  t.test('throws error with wrong encoder', function (st) {
456
- st.throws(function () {
457
+ st['throws'](function () {
457
458
  qs.stringify({}, { encoder: 'string' });
458
459
  }, new TypeError('Encoder has to be a function.'));
459
460
  st.end();
460
461
  });
461
462
 
462
463
  t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) {
463
- st.equal(qs.stringify({ a: new Buffer([1]) }, {
464
+ st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, {
464
465
  encoder: function (buffer) {
465
466
  if (typeof buffer === 'string') {
466
467
  return buffer;
@@ -468,6 +469,12 @@ test('stringify()', function (t) {
468
469
  return String.fromCharCode(buffer.readUInt8(0) + 97);
469
470
  }
470
471
  }), 'a=b');
472
+
473
+ st.equal(qs.stringify({ a: SaferBuffer.from('a b') }, {
474
+ encoder: function (buffer) {
475
+ return buffer;
476
+ }
477
+ }), 'a=a b');
471
478
  st.end();
472
479
  });
473
480
 
@@ -483,7 +490,7 @@ test('stringify()', function (t) {
483
490
  mutatedDate.toISOString = function () {
484
491
  throw new SyntaxError();
485
492
  };
486
- st.throws(function () {
493
+ st['throws'](function () {
487
494
  mutatedDate.toISOString();
488
495
  }, SyntaxError);
489
496
  st.equal(
@@ -508,24 +515,27 @@ test('stringify()', function (t) {
508
515
  t.test('RFC 1738 spaces serialization', function (st) {
509
516
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC1738 }), 'a=b+c');
510
517
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC1738 }), 'a+b=c+d');
518
+ st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC1738 }), 'a+b=a+b');
511
519
  st.end();
512
520
  });
513
521
 
514
522
  t.test('RFC 3986 spaces serialization', function (st) {
515
523
  st.equal(qs.stringify({ a: 'b c' }, { format: qs.formats.RFC3986 }), 'a=b%20c');
516
524
  st.equal(qs.stringify({ 'a b': 'c d' }, { format: qs.formats.RFC3986 }), 'a%20b=c%20d');
525
+ st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }, { format: qs.formats.RFC3986 }), 'a%20b=a%20b');
517
526
  st.end();
518
527
  });
519
528
 
520
529
  t.test('Backward compatibility to RFC 3986', function (st) {
521
530
  st.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
531
+ st.equal(qs.stringify({ 'a b': SaferBuffer.from('a b') }), 'a%20b=a%20b');
522
532
  st.end();
523
533
  });
524
534
 
525
535
  t.test('Edge cases and unknown formats', function (st) {
526
536
  ['UFO1234', false, 1234, null, {}, []].forEach(
527
537
  function (format) {
528
- st.throws(
538
+ st['throws'](
529
539
  function () {
530
540
  qs.stringify({ a: 'b c' }, { format: format });
531
541
  },
@@ -564,4 +574,25 @@ test('stringify()', function (t) {
564
574
  st.end();
565
575
  });
566
576
 
577
+ t.test('strictNullHandling works with custom filter', function (st) {
578
+ var filter = function (prefix, value) {
579
+ return value;
580
+ };
581
+
582
+ var options = { strictNullHandling: true, filter: filter };
583
+ st.equal(qs.stringify({ key: null }, options), 'key');
584
+ st.end();
585
+ });
586
+
587
+ t.test('strictNullHandling works with null serializeDate', function (st) {
588
+ var serializeDate = function () {
589
+ return null;
590
+ };
591
+ var options = { strictNullHandling: true, serializeDate: serializeDate };
592
+ var date = new Date();
593
+ st.equal(qs.stringify({ key: date }, options), 'key');
594
+ st.end();
595
+ });
596
+
597
+ t.end();
567
598
  });
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,5 +22,8 @@ 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
+
21
28
  t.end();
22
29
  });
package/.eslintignore DELETED
@@ -1 +0,0 @@
1
- dist