qs 6.1.2 → 6.2.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/.eslintrc CHANGED
@@ -4,10 +4,12 @@
4
4
  "extends": "@ljharb",
5
5
 
6
6
  "rules": {
7
- "complexity": [2, 19],
7
+ "complexity": [2, 22],
8
8
  "consistent-return": [1],
9
+ "id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
10
+ "indent": [2, 4],
9
11
  "max-params": [2, 9],
10
- "max-statements": [2, 33],
12
+ "max-statements": [2, 36],
11
13
  "no-extra-parens": [1],
12
14
  "no-continue": [1],
13
15
  "no-magic-numbers": 0,
package/CHANGELOG.md CHANGED
@@ -1,13 +1,25 @@
1
- ## **6.1.2**
1
+ ## **6.2.3**
2
2
  - [Fix] follow `allowPrototypes` option during merge (#201, #200)
3
3
  - [Fix] chmod a-x
4
4
  - [Fix] support keys starting with brackets (#202, #200)
5
5
  - [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
6
6
 
7
- ## **6.1.1**
7
+ ## **6.2.2**
8
8
  - [Fix] ensure that `allowPrototypes: false` does not ever shadow Object.prototype properties
9
9
 
10
- ## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=34&state=closed)
10
+ ## **6.2.1**
11
+ - [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
12
+ - [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call`
13
+ - [Tests] remove `parallelshell` since it does not reliably report failures
14
+ - [Tests] up to `node` `v6.3`, `v5.12`
15
+ - [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv`
16
+
17
+ ## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed)
18
+ - [New] pass Buffers to the encoder/decoder directly (#161)
19
+ - [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
20
+ - [Fix] fix compacting of nested sparse arrays (#150)
21
+
22
+ ## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed)
11
23
  - [New] allowDots option for `stringify` (#151)
12
24
  - [Fix] "sort" option should work at a depth of 3 or more (#151)
13
25
  - [Fix] Restore `dist` directory; will be removed in v7 (#148)
@@ -21,6 +33,9 @@
21
33
  ## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed)
22
34
  - [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4
23
35
 
36
+ ## **5.2.1**
37
+ - [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
38
+
24
39
  ## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
25
40
  - [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
26
41
 
package/README.md CHANGED
@@ -225,6 +225,26 @@ var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
225
225
  assert.equal(unencoded, 'a[b]=c');
226
226
  ```
227
227
 
228
+ This encoding can also be replaced by a custom encoding method set as `encoder` option:
229
+
230
+ ```javascript
231
+ var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
232
+ // Passed in values `a`, `b`, `c`
233
+ return // Return encoded string
234
+ }})
235
+ ```
236
+
237
+ _(Note: the `encoder` option does not apply if `encode` is `false`)_
238
+
239
+ Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:
240
+
241
+ ```javascript
242
+ var decoded = qs.parse('x=z', { decoder: function (str) {
243
+ // Passed in values `x`, `z`
244
+ return // Return decoded string
245
+ }})
246
+ ```
247
+
228
248
  Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
229
249
 
230
250
  When arrays are stringified, by default they are given explicit indices:
@@ -333,3 +353,24 @@ To completely skip rendering keys with `null` values, use the `skipNulls` flag:
333
353
  var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
334
354
  assert.equal(nullsSkipped, 'a=b');
335
355
  ```
356
+
357
+ ### Dealing with special character sets
358
+
359
+ By default the encoding and decoding of characters is done in `utf-8`. If you
360
+ wish to encode querystrings to a different character set (i.e.
361
+ [Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
362
+ [`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:
363
+
364
+ ```javascript
365
+ var encoder = require('qs-iconv/encoder')('shift_jis');
366
+ var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
367
+ assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
368
+ ```
369
+
370
+ This also works for decoding of query strings:
371
+
372
+ ```javascript
373
+ var decoder = require('qs-iconv/decoder')('shift_jis');
374
+ var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
375
+ assert.deepEqual(obj, { a: 'こんにちは!' });
376
+ ```
package/dist/qs.js CHANGED
@@ -14,7 +14,9 @@ module.exports = {
14
14
 
15
15
  var Utils = require('./utils');
16
16
 
17
- var internals = {
17
+ var has = Object.prototype.hasOwnProperty;
18
+
19
+ var defaults = {
18
20
  delimiter: '&',
19
21
  depth: 5,
20
22
  arrayLimit: 20,
@@ -22,12 +24,11 @@ var internals = {
22
24
  strictNullHandling: false,
23
25
  plainObjects: false,
24
26
  allowPrototypes: false,
25
- allowDots: false
27
+ allowDots: false,
28
+ decoder: Utils.decode
26
29
  };
27
30
 
28
- var has = Object.prototype.hasOwnProperty;
29
-
30
- internals.parseValues = function (str, options) {
31
+ var parseValues = function parseValues(str, options) {
31
32
  var obj = {};
32
33
  var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
33
34
 
@@ -35,28 +36,25 @@ internals.parseValues = function (str, options) {
35
36
  var part = parts[i];
36
37
  var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
37
38
 
39
+ var key, val;
38
40
  if (pos === -1) {
39
- obj[Utils.decode(part)] = '';
40
-
41
- if (options.strictNullHandling) {
42
- obj[Utils.decode(part)] = null;
43
- }
41
+ key = options.decoder(part);
42
+ val = options.strictNullHandling ? null : '';
44
43
  } else {
45
- var key = Utils.decode(part.slice(0, pos));
46
- var val = Utils.decode(part.slice(pos + 1));
47
-
48
- if (has.call(obj, key)) {
49
- obj[key] = [].concat(obj[key]).concat(val);
50
- } else {
51
- obj[key] = val;
52
- }
44
+ key = options.decoder(part.slice(0, pos));
45
+ val = options.decoder(part.slice(pos + 1));
46
+ }
47
+ if (has.call(obj, key)) {
48
+ obj[key] = [].concat(obj[key]).concat(val);
49
+ } else {
50
+ obj[key] = val;
53
51
  }
54
52
  }
55
53
 
56
54
  return obj;
57
55
  };
58
56
 
59
- internals.parseObject = function (chain, val, options) {
57
+ var parseObject = function parseObject(chain, val, options) {
60
58
  if (!chain.length) {
61
59
  return val;
62
60
  }
@@ -66,7 +64,7 @@ internals.parseObject = function (chain, val, options) {
66
64
  var obj;
67
65
  if (root === '[]') {
68
66
  obj = [];
69
- obj = obj.concat(internals.parseObject(chain, val, options));
67
+ obj = obj.concat(parseObject(chain, val, options));
70
68
  } else {
71
69
  obj = options.plainObjects ? Object.create(null) : {};
72
70
  var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
@@ -79,22 +77,22 @@ internals.parseObject = function (chain, val, options) {
79
77
  (options.parseArrays && index <= options.arrayLimit)
80
78
  ) {
81
79
  obj = [];
82
- obj[index] = internals.parseObject(chain, val, options);
80
+ obj[index] = parseObject(chain, val, options);
83
81
  } else {
84
- obj[cleanRoot] = internals.parseObject(chain, val, options);
82
+ obj[cleanRoot] = parseObject(chain, val, options);
85
83
  }
86
84
  }
87
85
 
88
86
  return obj;
89
87
  };
90
88
 
91
- internals.parseKeys = function (givenKey, val, options) {
89
+ var parseKeys = function parseKeys(givenKey, val, options) {
92
90
  if (!givenKey) {
93
91
  return;
94
92
  }
95
93
 
96
94
  // Transform dot notation to bracket notation
97
- var key = options.allowDots ? givenKey.replace(/\.([^\.\[]+)/g, '[$1]') : givenKey;
95
+ var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
98
96
 
99
97
  // The regex chunks
100
98
 
@@ -140,30 +138,32 @@ internals.parseKeys = function (givenKey, val, options) {
140
138
  keys.push('[' + key.slice(segment.index) + ']');
141
139
  }
142
140
 
143
- return internals.parseObject(keys, val, options);
141
+ return parseObject(keys, val, options);
144
142
  };
145
143
 
146
144
  module.exports = function (str, opts) {
147
145
  var options = opts || {};
148
- options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
149
- options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
150
- options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
146
+
147
+ if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
148
+ throw new TypeError('Decoder has to be a function.');
149
+ }
150
+
151
+ options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
152
+ options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
153
+ options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
151
154
  options.parseArrays = options.parseArrays !== false;
152
- options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
153
- options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
154
- options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
155
- options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
156
- options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
157
-
158
- if (
159
- str === '' ||
160
- str === null ||
161
- typeof str === 'undefined'
162
- ) {
155
+ options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
156
+ options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
157
+ options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
158
+ options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
159
+ options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
160
+ options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
161
+
162
+ if (str === '' || str === null || typeof str === 'undefined') {
163
163
  return options.plainObjects ? Object.create(null) : {};
164
164
  }
165
165
 
166
- var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
166
+ var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
167
167
  var obj = options.plainObjects ? Object.create(null) : {};
168
168
 
169
169
  // Iterate over the keys and setup the new object
@@ -171,7 +171,7 @@ module.exports = function (str, opts) {
171
171
  var keys = Object.keys(tempObj);
172
172
  for (var i = 0; i < keys.length; ++i) {
173
173
  var key = keys[i];
174
- var newObj = internals.parseKeys(key, tempObj[key], options);
174
+ var newObj = parseKeys(key, tempObj[key], options);
175
175
  obj = Utils.merge(obj, newObj, options);
176
176
  }
177
177
 
@@ -183,45 +183,45 @@ module.exports = function (str, opts) {
183
183
 
184
184
  var Utils = require('./utils');
185
185
 
186
- var internals = {
187
- delimiter: '&',
188
- arrayPrefixGenerators: {
189
- brackets: function (prefix) {
190
- return prefix + '[]';
191
- },
192
- indices: function (prefix, key) {
193
- return prefix + '[' + key + ']';
194
- },
195
- repeat: function (prefix) {
196
- return prefix;
197
- }
186
+ var arrayPrefixGenerators = {
187
+ brackets: function brackets(prefix) {
188
+ return prefix + '[]';
198
189
  },
190
+ indices: function indices(prefix, key) {
191
+ return prefix + '[' + key + ']';
192
+ },
193
+ repeat: function repeat(prefix) {
194
+ return prefix;
195
+ }
196
+ };
197
+
198
+ var defaults = {
199
+ delimiter: '&',
199
200
  strictNullHandling: false,
200
201
  skipNulls: false,
201
- encode: true
202
+ encode: true,
203
+ encoder: Utils.encode
202
204
  };
203
205
 
204
- internals.stringify = function (object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots) {
206
+ var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
205
207
  var obj = object;
206
208
  if (typeof filter === 'function') {
207
209
  obj = filter(prefix, obj);
208
- } else if (Utils.isBuffer(obj)) {
209
- obj = String(obj);
210
210
  } else if (obj instanceof Date) {
211
211
  obj = obj.toISOString();
212
212
  } else if (obj === null) {
213
213
  if (strictNullHandling) {
214
- return encode ? Utils.encode(prefix) : prefix;
214
+ return encoder ? encoder(prefix) : prefix;
215
215
  }
216
216
 
217
217
  obj = '';
218
218
  }
219
219
 
220
- if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
221
- if (encode) {
222
- return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
220
+ if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
221
+ if (encoder) {
222
+ return [encoder(prefix) + '=' + encoder(obj)];
223
223
  }
224
- return [prefix + '=' + obj];
224
+ return [prefix + '=' + String(obj)];
225
225
  }
226
226
 
227
227
  var values = [];
@@ -246,9 +246,9 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
246
246
  }
247
247
 
248
248
  if (Array.isArray(obj)) {
249
- values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
249
+ values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
250
250
  } else {
251
- values = values.concat(internals.stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
251
+ values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
252
252
  }
253
253
  }
254
254
 
@@ -258,14 +258,20 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
258
258
  module.exports = function (object, opts) {
259
259
  var obj = object;
260
260
  var options = opts || {};
261
- var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
262
- var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
263
- var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
264
- var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
261
+ var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
262
+ var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
263
+ var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
264
+ var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
265
+ var encoder = encode ? (typeof options.encoder === 'function' ? options.encoder : defaults.encoder) : null;
265
266
  var sort = typeof options.sort === 'function' ? options.sort : null;
266
267
  var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
267
268
  var objKeys;
268
269
  var filter;
270
+
271
+ if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
272
+ throw new TypeError('Encoder has to be a function.');
273
+ }
274
+
269
275
  if (typeof options.filter === 'function') {
270
276
  filter = options.filter;
271
277
  obj = filter('', obj);
@@ -280,7 +286,7 @@ module.exports = function (object, opts) {
280
286
  }
281
287
 
282
288
  var arrayFormat;
283
- if (options.arrayFormat in internals.arrayPrefixGenerators) {
289
+ if (options.arrayFormat in arrayPrefixGenerators) {
284
290
  arrayFormat = options.arrayFormat;
285
291
  } else if ('indices' in options) {
286
292
  arrayFormat = options.indices ? 'indices' : 'repeat';
@@ -288,7 +294,7 @@ module.exports = function (object, opts) {
288
294
  arrayFormat = 'indices';
289
295
  }
290
296
 
291
- var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
297
+ var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
292
298
 
293
299
  if (!objKeys) {
294
300
  objKeys = Object.keys(obj);
@@ -305,7 +311,7 @@ module.exports = function (object, opts) {
305
311
  continue;
306
312
  }
307
313
 
308
- keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
314
+ keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
309
315
  }
310
316
 
311
317
  return keys.join(delimiter);
@@ -364,7 +370,7 @@ exports.merge = function (target, source, options) {
364
370
  mergeTarget = exports.arrayToObject(target, options);
365
371
  }
366
372
 
367
- return Object.keys(source).reduce(function (acc, key) {
373
+ return Object.keys(source).reduce(function (acc, key) {
368
374
  var value = source[key];
369
375
 
370
376
  if (has.call(acc, key)) {
@@ -372,7 +378,7 @@ exports.merge = function (target, source, options) {
372
378
  } else {
373
379
  acc[key] = value;
374
380
  }
375
- return acc;
381
+ return acc;
376
382
  }, mergeTarget);
377
383
  };
378
384
 
@@ -427,7 +433,7 @@ exports.encode = function (str) {
427
433
 
428
434
  i += 1;
429
435
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
430
- out += (hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
436
+ out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)];
431
437
  }
432
438
 
433
439
  return out;
@@ -450,7 +456,9 @@ exports.compact = function (obj, references) {
450
456
  var compacted = [];
451
457
 
452
458
  for (var i = 0; i < obj.length; ++i) {
453
- if (typeof obj[i] !== 'undefined') {
459
+ if (obj[i] && typeof obj[i] === 'object') {
460
+ compacted.push(exports.compact(obj[i], refs));
461
+ } else if (typeof obj[i] !== 'undefined') {
454
462
  compacted.push(obj[i]);
455
463
  }
456
464
  }
package/lib/parse.js CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  var Utils = require('./utils');
4
4
 
5
- var internals = {
5
+ var has = Object.prototype.hasOwnProperty;
6
+
7
+ var defaults = {
6
8
  delimiter: '&',
7
9
  depth: 5,
8
10
  arrayLimit: 20,
@@ -10,12 +12,11 @@ var internals = {
10
12
  strictNullHandling: false,
11
13
  plainObjects: false,
12
14
  allowPrototypes: false,
13
- allowDots: false
15
+ allowDots: false,
16
+ decoder: Utils.decode
14
17
  };
15
18
 
16
- var has = Object.prototype.hasOwnProperty;
17
-
18
- internals.parseValues = function (str, options) {
19
+ var parseValues = function parseValues(str, options) {
19
20
  var obj = {};
20
21
  var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
21
22
 
@@ -23,28 +24,25 @@ internals.parseValues = function (str, options) {
23
24
  var part = parts[i];
24
25
  var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
25
26
 
27
+ var key, val;
26
28
  if (pos === -1) {
27
- obj[Utils.decode(part)] = '';
28
-
29
- if (options.strictNullHandling) {
30
- obj[Utils.decode(part)] = null;
31
- }
29
+ key = options.decoder(part);
30
+ val = options.strictNullHandling ? null : '';
32
31
  } else {
33
- var key = Utils.decode(part.slice(0, pos));
34
- var val = Utils.decode(part.slice(pos + 1));
35
-
36
- if (has.call(obj, key)) {
37
- obj[key] = [].concat(obj[key]).concat(val);
38
- } else {
39
- obj[key] = val;
40
- }
32
+ key = options.decoder(part.slice(0, pos));
33
+ val = options.decoder(part.slice(pos + 1));
34
+ }
35
+ if (has.call(obj, key)) {
36
+ obj[key] = [].concat(obj[key]).concat(val);
37
+ } else {
38
+ obj[key] = val;
41
39
  }
42
40
  }
43
41
 
44
42
  return obj;
45
43
  };
46
44
 
47
- internals.parseObject = function (chain, val, options) {
45
+ var parseObject = function parseObject(chain, val, options) {
48
46
  if (!chain.length) {
49
47
  return val;
50
48
  }
@@ -54,7 +52,7 @@ internals.parseObject = function (chain, val, options) {
54
52
  var obj;
55
53
  if (root === '[]') {
56
54
  obj = [];
57
- obj = obj.concat(internals.parseObject(chain, val, options));
55
+ obj = obj.concat(parseObject(chain, val, options));
58
56
  } else {
59
57
  obj = options.plainObjects ? Object.create(null) : {};
60
58
  var cleanRoot = root.charAt(0) === '[' && root.charAt(root.length - 1) === ']' ? root.slice(1, -1) : root;
@@ -67,22 +65,22 @@ internals.parseObject = function (chain, val, options) {
67
65
  (options.parseArrays && index <= options.arrayLimit)
68
66
  ) {
69
67
  obj = [];
70
- obj[index] = internals.parseObject(chain, val, options);
68
+ obj[index] = parseObject(chain, val, options);
71
69
  } else {
72
- obj[cleanRoot] = internals.parseObject(chain, val, options);
70
+ obj[cleanRoot] = parseObject(chain, val, options);
73
71
  }
74
72
  }
75
73
 
76
74
  return obj;
77
75
  };
78
76
 
79
- internals.parseKeys = function (givenKey, val, options) {
77
+ var parseKeys = function parseKeys(givenKey, val, options) {
80
78
  if (!givenKey) {
81
79
  return;
82
80
  }
83
81
 
84
82
  // Transform dot notation to bracket notation
85
- var key = options.allowDots ? givenKey.replace(/\.([^\.\[]+)/g, '[$1]') : givenKey;
83
+ var key = options.allowDots ? givenKey.replace(/\.([^.[]+)/g, '[$1]') : givenKey;
86
84
 
87
85
  // The regex chunks
88
86
 
@@ -128,30 +126,32 @@ internals.parseKeys = function (givenKey, val, options) {
128
126
  keys.push('[' + key.slice(segment.index) + ']');
129
127
  }
130
128
 
131
- return internals.parseObject(keys, val, options);
129
+ return parseObject(keys, val, options);
132
130
  };
133
131
 
134
132
  module.exports = function (str, opts) {
135
133
  var options = opts || {};
136
- options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
137
- options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
138
- options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
134
+
135
+ if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
136
+ throw new TypeError('Decoder has to be a function.');
137
+ }
138
+
139
+ options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
140
+ options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
141
+ options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
139
142
  options.parseArrays = options.parseArrays !== false;
140
- options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : internals.allowDots;
141
- options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
142
- options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
143
- options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
144
- options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
145
-
146
- if (
147
- str === '' ||
148
- str === null ||
149
- typeof str === 'undefined'
150
- ) {
143
+ options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
144
+ options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
145
+ options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
146
+ options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
147
+ options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
148
+ options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
149
+
150
+ if (str === '' || str === null || typeof str === 'undefined') {
151
151
  return options.plainObjects ? Object.create(null) : {};
152
152
  }
153
153
 
154
- var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
154
+ var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
155
155
  var obj = options.plainObjects ? Object.create(null) : {};
156
156
 
157
157
  // Iterate over the keys and setup the new object
@@ -159,7 +159,7 @@ module.exports = function (str, opts) {
159
159
  var keys = Object.keys(tempObj);
160
160
  for (var i = 0; i < keys.length; ++i) {
161
161
  var key = keys[i];
162
- var newObj = internals.parseKeys(key, tempObj[key], options);
162
+ var newObj = parseKeys(key, tempObj[key], options);
163
163
  obj = Utils.merge(obj, newObj, options);
164
164
  }
165
165
 
package/lib/stringify.js CHANGED
@@ -2,45 +2,45 @@
2
2
 
3
3
  var Utils = require('./utils');
4
4
 
5
- var internals = {
6
- delimiter: '&',
7
- arrayPrefixGenerators: {
8
- brackets: function (prefix) {
9
- return prefix + '[]';
10
- },
11
- indices: function (prefix, key) {
12
- return prefix + '[' + key + ']';
13
- },
14
- repeat: function (prefix) {
15
- return prefix;
16
- }
5
+ var arrayPrefixGenerators = {
6
+ brackets: function brackets(prefix) {
7
+ return prefix + '[]';
8
+ },
9
+ indices: function indices(prefix, key) {
10
+ return prefix + '[' + key + ']';
17
11
  },
12
+ repeat: function repeat(prefix) {
13
+ return prefix;
14
+ }
15
+ };
16
+
17
+ var defaults = {
18
+ delimiter: '&',
18
19
  strictNullHandling: false,
19
20
  skipNulls: false,
20
- encode: true
21
+ encode: true,
22
+ encoder: Utils.encode
21
23
  };
22
24
 
23
- internals.stringify = function (object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots) {
25
+ var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
24
26
  var obj = object;
25
27
  if (typeof filter === 'function') {
26
28
  obj = filter(prefix, obj);
27
- } else if (Utils.isBuffer(obj)) {
28
- obj = String(obj);
29
29
  } else if (obj instanceof Date) {
30
30
  obj = obj.toISOString();
31
31
  } else if (obj === null) {
32
32
  if (strictNullHandling) {
33
- return encode ? Utils.encode(prefix) : prefix;
33
+ return encoder ? encoder(prefix) : prefix;
34
34
  }
35
35
 
36
36
  obj = '';
37
37
  }
38
38
 
39
- if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
40
- if (encode) {
41
- return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
39
+ if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
40
+ if (encoder) {
41
+ return [encoder(prefix) + '=' + encoder(obj)];
42
42
  }
43
- return [prefix + '=' + obj];
43
+ return [prefix + '=' + String(obj)];
44
44
  }
45
45
 
46
46
  var values = [];
@@ -65,9 +65,9 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
65
65
  }
66
66
 
67
67
  if (Array.isArray(obj)) {
68
- values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
68
+ values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
69
69
  } else {
70
- values = values.concat(internals.stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
70
+ values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
71
71
  }
72
72
  }
73
73
 
@@ -77,14 +77,20 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
77
77
  module.exports = function (object, opts) {
78
78
  var obj = object;
79
79
  var options = opts || {};
80
- var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
81
- var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
82
- var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : internals.skipNulls;
83
- var encode = typeof options.encode === 'boolean' ? options.encode : internals.encode;
80
+ var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
81
+ var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
82
+ var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
83
+ var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
84
+ var encoder = encode ? (typeof options.encoder === 'function' ? options.encoder : defaults.encoder) : null;
84
85
  var sort = typeof options.sort === 'function' ? options.sort : null;
85
86
  var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
86
87
  var objKeys;
87
88
  var filter;
89
+
90
+ if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
91
+ throw new TypeError('Encoder has to be a function.');
92
+ }
93
+
88
94
  if (typeof options.filter === 'function') {
89
95
  filter = options.filter;
90
96
  obj = filter('', obj);
@@ -99,7 +105,7 @@ module.exports = function (object, opts) {
99
105
  }
100
106
 
101
107
  var arrayFormat;
102
- if (options.arrayFormat in internals.arrayPrefixGenerators) {
108
+ if (options.arrayFormat in arrayPrefixGenerators) {
103
109
  arrayFormat = options.arrayFormat;
104
110
  } else if ('indices' in options) {
105
111
  arrayFormat = options.indices ? 'indices' : 'repeat';
@@ -107,7 +113,7 @@ module.exports = function (object, opts) {
107
113
  arrayFormat = 'indices';
108
114
  }
109
115
 
110
- var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
116
+ var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
111
117
 
112
118
  if (!objKeys) {
113
119
  objKeys = Object.keys(obj);
@@ -124,7 +130,7 @@ module.exports = function (object, opts) {
124
130
  continue;
125
131
  }
126
132
 
127
- keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encode, filter, sort, allowDots));
133
+ keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
128
134
  }
129
135
 
130
136
  return keys.join(delimiter);
package/lib/utils.js CHANGED
@@ -50,7 +50,7 @@ exports.merge = function (target, source, options) {
50
50
  mergeTarget = exports.arrayToObject(target, options);
51
51
  }
52
52
 
53
- return Object.keys(source).reduce(function (acc, key) {
53
+ return Object.keys(source).reduce(function (acc, key) {
54
54
  var value = source[key];
55
55
 
56
56
  if (has.call(acc, key)) {
@@ -58,7 +58,7 @@ exports.merge = function (target, source, options) {
58
58
  } else {
59
59
  acc[key] = value;
60
60
  }
61
- return acc;
61
+ return acc;
62
62
  }, mergeTarget);
63
63
  };
64
64
 
@@ -113,7 +113,7 @@ exports.encode = function (str) {
113
113
 
114
114
  i += 1;
115
115
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
116
- out += (hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)]);
116
+ out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)];
117
117
  }
118
118
 
119
119
  return out;
@@ -136,7 +136,9 @@ exports.compact = function (obj, references) {
136
136
  var compacted = [];
137
137
 
138
138
  for (var i = 0; i < obj.length; ++i) {
139
- if (typeof obj[i] !== 'undefined') {
139
+ if (obj[i] && typeof obj[i] === 'object') {
140
+ compacted.push(exports.compact(obj[i], refs));
141
+ } else if (typeof obj[i] !== 'undefined') {
140
142
  compacted.push(obj[i]);
141
143
  }
142
144
  }
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.1.2",
5
+ "version": "6.2.3",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -24,18 +24,20 @@
24
24
  },
25
25
  "dependencies": {},
26
26
  "devDependencies": {
27
- "browserify": "^12.0.1",
28
- "tape": "^4.3.0",
27
+ "browserify": "^13.0.1",
28
+ "tape": "^4.6.0",
29
29
  "covert": "^1.1.0",
30
30
  "mkdirp": "^0.5.1",
31
- "eslint": "^1.10.3",
32
- "@ljharb/eslint-config": "^1.6.1",
31
+ "eslint": "^3.1.0",
32
+ "@ljharb/eslint-config": "^6.0.0",
33
33
  "parallelshell": "^2.0.0",
34
- "evalmd": "^0.0.16"
34
+ "iconv-lite": "^0.4.13",
35
+ "qs-iconv": "^1.0.3",
36
+ "evalmd": "^0.0.17"
35
37
  },
36
38
  "scripts": {
37
- "pretest": "npm run lint && npm run readme",
38
- "test": "npm run coverage",
39
+ "pretest": "npm run --silent readme && npm run --silent lint",
40
+ "test": "npm run --silent coverage",
39
41
  "tests-only": "node test",
40
42
  "readme": "evalmd README.md",
41
43
  "lint": "eslint lib/*.js text/*.js",
package/test/parse.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
+ var iconv = require('iconv-lite');
5
6
 
6
7
  test('parse()', function (t) {
7
8
  t.test('parses a simple string', function (st) {
@@ -174,14 +175,42 @@ test('parse()', function (t) {
174
175
 
175
176
  t.test('allows for empty strings in arrays', function (st) {
176
177
  st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] });
177
- st.deepEqual(qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true }), { a: ['b', null, 'c', ''] });
178
- st.deepEqual(qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true }), { a: ['b', '', 'c', null] });
179
- st.deepEqual(qs.parse('a[]=&a[]=b&a[]=c'), { a: ['', 'b', 'c'] });
178
+
179
+ st.deepEqual(
180
+ qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 }),
181
+ { a: ['b', null, 'c', ''] },
182
+ 'with arrayLimit 20 + array indices: null then empty string works'
183
+ );
184
+ st.deepEqual(
185
+ qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }),
186
+ { a: ['b', null, 'c', ''] },
187
+ 'with arrayLimit 0 + array brackets: null then empty string works'
188
+ );
189
+
190
+ st.deepEqual(
191
+ qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 }),
192
+ { a: ['b', '', 'c', null] },
193
+ 'with arrayLimit 20 + array indices: empty string then null works'
194
+ );
195
+ st.deepEqual(
196
+ qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }),
197
+ { a: ['b', '', 'c', null] },
198
+ 'with arrayLimit 0 + array brackets: empty string then null works'
199
+ );
200
+
201
+ st.deepEqual(
202
+ qs.parse('a[]=&a[]=b&a[]=c'),
203
+ { a: ['', 'b', 'c'] },
204
+ 'array brackets: empty strings work'
205
+ );
180
206
  st.end();
181
207
  });
182
208
 
183
209
  t.test('compacts sparse arrays', function (st) {
184
210
  st.deepEqual(qs.parse('a[10]=1&a[2]=2'), { a: ['2', '1'] });
211
+ st.deepEqual(qs.parse('a[1][b][2][c]=1'), { a: [{ b: [{ c: '1' }] }] });
212
+ st.deepEqual(qs.parse('a[1][2][3][c]=1'), { a: [[[{ c: '1' }]]] });
213
+ st.deepEqual(qs.parse('a[1][2][3][c][1]=1'), { a: [[[{ c: ['1'] }]]] });
185
214
  st.end();
186
215
  });
187
216
 
@@ -453,4 +482,30 @@ test('parse()', function (t) {
453
482
  st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
454
483
  st.end();
455
484
  });
485
+
486
+ t.test('can parse with custom encoding', function (st) {
487
+ st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', {
488
+ decoder: function (str) {
489
+ var reg = /\%([0-9A-F]{2})/ig;
490
+ var result = [];
491
+ var parts;
492
+ var last = 0;
493
+ while (parts = reg.exec(str)) {
494
+ result.push(parseInt(parts[1], 16));
495
+ last = parts.index + parts[0].length;
496
+ }
497
+ return iconv.decode(new Buffer(result), 'shift_jis').toString();
498
+ }
499
+ }), { 県: '大阪府' });
500
+ st.end();
501
+ });
502
+
503
+ t.test('throws error with wrong decoder', function (st) {
504
+ st.throws(function () {
505
+ qs.parse({}, {
506
+ decoder: 'string'
507
+ });
508
+ }, new TypeError('Decoder has to be a function.'));
509
+ st.end();
510
+ });
456
511
  });
package/test/stringify.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  var test = require('tape');
4
4
  var qs = require('../');
5
+ var iconv = require('iconv-lite');
5
6
 
6
7
  test('stringify()', function (t) {
7
8
  t.test('stringifies a querystring object', function (st) {
@@ -21,7 +22,7 @@ test('stringify()', function (t) {
21
22
  st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }), 'a%5Bb%5D%5Bc%5D%5Bd%5D=e');
22
23
  st.end();
23
24
  });
24
-
25
+
25
26
  t.test('stringifies a nested object with dots notation', function (st) {
26
27
  st.equal(qs.stringify({ a: { b: 'c' } }, { allowDots: true }), 'a.b=c');
27
28
  st.equal(qs.stringify({ a: { b: { c: { d: 'e' } } } }, { allowDots: true }), 'a.b.c.d=e');
@@ -53,7 +54,7 @@ test('stringify()', function (t) {
53
54
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
54
55
  st.end();
55
56
  });
56
-
57
+
57
58
  t.test('stringifies a nested array value with dots notation', function (st) {
58
59
  st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { allowDots: true, encode: false }), 'a.b[0]=c&a.b[1]=d');
59
60
  st.end();
@@ -64,7 +65,12 @@ test('stringify()', function (t) {
64
65
  st.equal(qs.stringify({ a: [{ b: { c: [1] } }] }), 'a%5B0%5D%5Bb%5D%5Bc%5D%5B0%5D=1');
65
66
  st.end();
66
67
  });
67
-
68
+
69
+ t.test('stringifies an array with mixed objects and primitives', function (st) {
70
+ st.equal(qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }), 'a[0][b]=1&a[1]=2&a[2]=3');
71
+ st.end();
72
+ });
73
+
68
74
  t.test('stringifies an object inside an array with dots notation', function (st) {
69
75
  st.equal(qs.stringify({ a: [{ b: 'c' }] }, { allowDots: true, encode: false }), 'a[0].b=c');
70
76
  st.equal(qs.stringify({ a: [{ b: { c: [1] } }] }, { allowDots: true, encode: false }), 'a[0].b.c[0]=1');
@@ -249,11 +255,51 @@ test('stringify()', function (t) {
249
255
  st.equal(qs.stringify({ a: 'c', z: { j: 'a', i: 'b' }, b: 'f' }, { sort: sort }), 'a=c&b=f&z%5Bi%5D=b&z%5Bj%5D=a');
250
256
  st.end();
251
257
  });
252
-
258
+
253
259
  t.test('can sort the keys at depth 3 or more too', function (st) {
254
260
  var sort = function (a, b) { return a.localeCompare(b); };
255
261
  st.equal(qs.stringify({ a: 'a', z: { zj: {zjb: 'zjb', zja: 'zja'}, zi: {zib: 'zib', zia: 'zia'} }, b: 'b' }, { sort: sort, encode: false }), 'a=a&b=b&z[zi][zia]=zia&z[zi][zib]=zib&z[zj][zja]=zja&z[zj][zjb]=zjb');
256
262
  st.equal(qs.stringify({ a: 'a', z: { zj: {zjb: 'zjb', zja: 'zja'}, zi: {zib: 'zib', zia: 'zia'} }, b: 'b' }, { sort: null, encode: false }), 'a=a&z[zj][zjb]=zjb&z[zj][zja]=zja&z[zi][zib]=zib&z[zi][zia]=zia&b=b');
257
263
  st.end();
258
264
  });
265
+
266
+ t.test('can stringify with custom encoding', function (st) {
267
+ st.equal(qs.stringify({ 県: '大阪府', '': ''}, {
268
+ encoder: function (str) {
269
+ if (str.length === 0) {
270
+ return '';
271
+ }
272
+ var buf = iconv.encode(str, 'shiftjis');
273
+ var result = [];
274
+ for (var i=0; i < buf.length; ++i) {
275
+ result.push(buf.readUInt8(i).toString(16));
276
+ }
277
+ return '%' + result.join('%');
278
+ }
279
+ }), '%8c%a7=%91%e5%8d%e3%95%7b&=');
280
+ st.end();
281
+ });
282
+
283
+ t.test('throws error with wrong encoder', function (st) {
284
+ st.throws(function () {
285
+ qs.stringify({}, {
286
+ encoder: 'string'
287
+ });
288
+ }, new TypeError('Encoder has to be a function.'));
289
+ st.end();
290
+ });
291
+
292
+ t.test('can use custom encoder for a buffer object', {
293
+ skip: typeof Buffer === 'undefined'
294
+ }, function (st) {
295
+ st.equal(qs.stringify({ a: new Buffer([1]) }, {
296
+ encoder: function (buffer) {
297
+ if (typeof buffer === 'string') {
298
+ return buffer;
299
+ }
300
+ return String.fromCharCode(buffer.readUInt8(0) + 97);
301
+ }
302
+ }), 'a=b');
303
+ st.end();
304
+ });
259
305
  });
package/.npmignore DELETED
@@ -1,18 +0,0 @@
1
- .idea
2
- *.iml
3
- npm-debug.log
4
- dump.rdb
5
- node_modules
6
- results.tap
7
- results.xml
8
- npm-shrinkwrap.json
9
- config.json
10
- .DS_Store
11
- */.DS_Store
12
- */*/.DS_Store
13
- ._*
14
- */._*
15
- */*/._*
16
- coverage.*
17
- lib-cov
18
- complexity.md
package/.travis.yml DELETED
@@ -1,173 +0,0 @@
1
- language: node_js
2
- os:
3
- - linux
4
- node_js:
5
- - "7.7"
6
- - "6.10"
7
- - "5.12"
8
- - "4.8"
9
- - "iojs-v3.3"
10
- - "iojs-v2.5"
11
- - "iojs-v1.8"
12
- - "0.12"
13
- - "0.10"
14
- - "0.8"
15
- before_install:
16
- - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ]; then npm install -g npm@1.3 ; elif [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then case "$(npm --version)" in 1.*) npm install -g npm@1.4.28 ;; 2.*) npm install -g npm@2 ;; esac ; fi'
17
- - 'if [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then npm install -g npm; fi'
18
- script:
19
- - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi'
20
- - 'if [ -n "${POSTTEST-}" ]; then npm run posttest ; fi'
21
- - 'if [ -n "${COVERAGE-}" ]; then npm run coverage ; fi'
22
- - 'if [ -n "${TEST-}" ]; then npm run tests-only ; fi'
23
- sudo: false
24
- env:
25
- - TEST=true
26
- matrix:
27
- fast_finish: true
28
- include:
29
- - node_js: "node"
30
- env: PRETEST=true
31
- - node_js: "4"
32
- env: COVERAGE=true
33
- - node_js: "7.6"
34
- env: TEST=true ALLOW_FAILURE=true
35
- - node_js: "7.5"
36
- env: TEST=true ALLOW_FAILURE=true
37
- - node_js: "7.4"
38
- env: TEST=true ALLOW_FAILURE=true
39
- - node_js: "7.3"
40
- env: TEST=true ALLOW_FAILURE=true
41
- - node_js: "7.2"
42
- env: TEST=true ALLOW_FAILURE=true
43
- - node_js: "7.1"
44
- env: TEST=true ALLOW_FAILURE=true
45
- - node_js: "7.0"
46
- env: TEST=true ALLOW_FAILURE=true
47
- - node_js: "6.9"
48
- env: TEST=true ALLOW_FAILURE=true
49
- - node_js: "6.8"
50
- env: TEST=true ALLOW_FAILURE=true
51
- - node_js: "6.7"
52
- env: TEST=true ALLOW_FAILURE=true
53
- - node_js: "6.6"
54
- env: TEST=true ALLOW_FAILURE=true
55
- - node_js: "6.5"
56
- env: TEST=true ALLOW_FAILURE=true
57
- - node_js: "6.4"
58
- env: TEST=true ALLOW_FAILURE=true
59
- - node_js: "6.3"
60
- env: TEST=true ALLOW_FAILURE=true
61
- - node_js: "6.2"
62
- env: TEST=true ALLOW_FAILURE=true
63
- - node_js: "6.1"
64
- env: TEST=true ALLOW_FAILURE=true
65
- - node_js: "6.0"
66
- env: TEST=true ALLOW_FAILURE=true
67
- - node_js: "5.11"
68
- env: TEST=true ALLOW_FAILURE=true
69
- - node_js: "5.10"
70
- env: TEST=true ALLOW_FAILURE=true
71
- - node_js: "5.9"
72
- env: TEST=true ALLOW_FAILURE=true
73
- - node_js: "5.8"
74
- env: TEST=true ALLOW_FAILURE=true
75
- - node_js: "5.7"
76
- env: TEST=true ALLOW_FAILURE=true
77
- - node_js: "5.6"
78
- env: TEST=true ALLOW_FAILURE=true
79
- - node_js: "5.5"
80
- env: TEST=true ALLOW_FAILURE=true
81
- - node_js: "5.4"
82
- env: TEST=true ALLOW_FAILURE=true
83
- - node_js: "5.3"
84
- env: TEST=true ALLOW_FAILURE=true
85
- - node_js: "5.2"
86
- env: TEST=true ALLOW_FAILURE=true
87
- - node_js: "5.1"
88
- env: TEST=true ALLOW_FAILURE=true
89
- - node_js: "5.0"
90
- env: TEST=true ALLOW_FAILURE=true
91
- - node_js: "4.7"
92
- env: TEST=true ALLOW_FAILURE=true
93
- - node_js: "4.6"
94
- env: TEST=true ALLOW_FAILURE=true
95
- - node_js: "4.5"
96
- env: TEST=true ALLOW_FAILURE=true
97
- - node_js: "4.4"
98
- env: TEST=true ALLOW_FAILURE=true
99
- - node_js: "4.3"
100
- env: TEST=true ALLOW_FAILURE=true
101
- - node_js: "4.2"
102
- env: TEST=true ALLOW_FAILURE=true
103
- - node_js: "4.1"
104
- env: TEST=true ALLOW_FAILURE=true
105
- - node_js: "4.0"
106
- env: TEST=true ALLOW_FAILURE=true
107
- - node_js: "iojs-v3.2"
108
- env: TEST=true ALLOW_FAILURE=true
109
- - node_js: "iojs-v3.1"
110
- env: TEST=true ALLOW_FAILURE=true
111
- - node_js: "iojs-v3.0"
112
- env: TEST=true ALLOW_FAILURE=true
113
- - node_js: "iojs-v2.4"
114
- env: TEST=true ALLOW_FAILURE=true
115
- - node_js: "iojs-v2.3"
116
- env: TEST=true ALLOW_FAILURE=true
117
- - node_js: "iojs-v2.2"
118
- env: TEST=true ALLOW_FAILURE=true
119
- - node_js: "iojs-v2.1"
120
- env: TEST=true ALLOW_FAILURE=true
121
- - node_js: "iojs-v2.0"
122
- env: TEST=true ALLOW_FAILURE=true
123
- - node_js: "iojs-v1.7"
124
- env: TEST=true ALLOW_FAILURE=true
125
- - node_js: "iojs-v1.6"
126
- env: TEST=true ALLOW_FAILURE=true
127
- - node_js: "iojs-v1.5"
128
- env: TEST=true ALLOW_FAILURE=true
129
- - node_js: "iojs-v1.4"
130
- env: TEST=true ALLOW_FAILURE=true
131
- - node_js: "iojs-v1.3"
132
- env: TEST=true ALLOW_FAILURE=true
133
- - node_js: "iojs-v1.2"
134
- env: TEST=true ALLOW_FAILURE=true
135
- - node_js: "iojs-v1.1"
136
- env: TEST=true ALLOW_FAILURE=true
137
- - node_js: "iojs-v1.0"
138
- env: TEST=true ALLOW_FAILURE=true
139
- - node_js: "0.11"
140
- env: TEST=true ALLOW_FAILURE=true
141
- - node_js: "0.9"
142
- env: TEST=true ALLOW_FAILURE=true
143
- - node_js: "0.6"
144
- env: TEST=true ALLOW_FAILURE=true
145
- - node_js: "0.4"
146
- env: TEST=true ALLOW_FAILURE=true
147
- ##- node_js: "7"
148
- #env: TEST=true
149
- #os: osx
150
- #- node_js: "6"
151
- #env: TEST=true
152
- #os: osx
153
- #- node_js: "5"
154
- #env: TEST=true
155
- #os: osx
156
- #- node_js: "4"
157
- #env: TEST=true
158
- #os: osx
159
- #- node_js: "iojs"
160
- #env: TEST=true
161
- #os: osx
162
- #- node_js: "0.12"
163
- #env: TEST=true
164
- #os: osx
165
- #- node_js: "0.10"
166
- #env: TEST=true
167
- #os: osx
168
- #- node_js: "0.8"
169
- #env: TEST=true
170
- #os: osx
171
- allow_failures:
172
- - os: osx
173
- - env: TEST=true ALLOW_FAILURE=true
package/bower.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "name": "qs",
3
- "main": "dist/qs.js",
4
- "homepage": "https://github.com/hapijs/qs",
5
- "authors": [
6
- "Nathan LaFreniere <quitlahok@gmail.com>"
7
- ],
8
- "description": "A querystring parser that supports nesting and arrays, with a depth limit",
9
- "keywords": [
10
- "querystring",
11
- "qs"
12
- ],
13
- "license": "BSD-3-Clause",
14
- "ignore": [
15
- "**/.*",
16
- "node_modules",
17
- "bower_components",
18
- "test",
19
- "tests"
20
- ]
21
- }
package/component.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "name": "qs",
3
- "repository": "hapijs/qs",
4
- "description": "query-string parser / stringifier with nesting support",
5
- "version": "6.1.2",
6
- "keywords": ["querystring", "query", "parser"],
7
- "main": "lib/index.js",
8
- "scripts": [
9
- "lib/index.js",
10
- "lib/parse.js",
11
- "lib/stringify.js",
12
- "lib/utils.js"
13
- ],
14
- "license": "BSD-3-Clause"
15
- }