qs 6.5.0 → 6.6.0
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 +6 -3
- package/CHANGELOG.md +30 -0
- package/README.md +88 -2
- package/dist/qs.js +210 -88
- package/lib/parse.js +87 -33
- package/lib/stringify.js +46 -14
- package/lib/utils.js +78 -42
- package/package.json +9 -8
- package/test/.eslintrc +6 -0
- package/test/parse.js +118 -5
- package/test/stringify.js +59 -6
- package/test/utils.js +55 -0
package/test/parse.js
CHANGED
|
@@ -4,6 +4,7 @@ var test = require('tape');
|
|
|
4
4
|
var qs = require('../');
|
|
5
5
|
var utils = require('../lib/utils');
|
|
6
6
|
var iconv = require('iconv-lite');
|
|
7
|
+
var SaferBuffer = require('safer-buffer').Buffer;
|
|
7
8
|
|
|
8
9
|
test('parse()', function (t) {
|
|
9
10
|
t.test('parses a simple string', function (st) {
|
|
@@ -231,7 +232,7 @@ test('parse()', function (t) {
|
|
|
231
232
|
});
|
|
232
233
|
|
|
233
234
|
t.test('parses buffers correctly', function (st) {
|
|
234
|
-
var b =
|
|
235
|
+
var b = SaferBuffer.from('test');
|
|
235
236
|
st.deepEqual(qs.parse({ a: b }), { a: b });
|
|
236
237
|
st.end();
|
|
237
238
|
});
|
|
@@ -256,7 +257,7 @@ test('parse()', function (t) {
|
|
|
256
257
|
st.end();
|
|
257
258
|
});
|
|
258
259
|
|
|
259
|
-
t.test('should not throw when a native prototype has an enumerable property',
|
|
260
|
+
t.test('should not throw when a native prototype has an enumerable property', function (st) {
|
|
260
261
|
Object.prototype.crash = '';
|
|
261
262
|
Array.prototype.crash = '';
|
|
262
263
|
st.doesNotThrow(qs.parse.bind(null, 'a=b'));
|
|
@@ -301,7 +302,14 @@ test('parse()', function (t) {
|
|
|
301
302
|
});
|
|
302
303
|
|
|
303
304
|
t.test('allows disabling array parsing', function (st) {
|
|
304
|
-
|
|
305
|
+
var indices = qs.parse('a[0]=b&a[1]=c', { parseArrays: false });
|
|
306
|
+
st.deepEqual(indices, { a: { 0: 'b', 1: 'c' } });
|
|
307
|
+
st.equal(Array.isArray(indices.a), false, 'parseArrays:false, indices case is not an array');
|
|
308
|
+
|
|
309
|
+
var emptyBrackets = qs.parse('a[]=b', { parseArrays: false });
|
|
310
|
+
st.deepEqual(emptyBrackets, { a: { 0: 'b' } });
|
|
311
|
+
st.equal(Array.isArray(emptyBrackets.a), false, 'parseArrays:false, empty brackets case is not an array');
|
|
312
|
+
|
|
305
313
|
st.end();
|
|
306
314
|
});
|
|
307
315
|
|
|
@@ -396,6 +404,33 @@ test('parse()', function (t) {
|
|
|
396
404
|
st.end();
|
|
397
405
|
});
|
|
398
406
|
|
|
407
|
+
t.test('does not crash when parsing deep objects', function (st) {
|
|
408
|
+
var parsed;
|
|
409
|
+
var str = 'foo';
|
|
410
|
+
|
|
411
|
+
for (var i = 0; i < 5000; i++) {
|
|
412
|
+
str += '[p]';
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
str += '=bar';
|
|
416
|
+
|
|
417
|
+
st.doesNotThrow(function () {
|
|
418
|
+
parsed = qs.parse(str, { depth: 5000 });
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
st.equal('foo' in parsed, true, 'parsed has "foo" property');
|
|
422
|
+
|
|
423
|
+
var depth = 0;
|
|
424
|
+
var ref = parsed.foo;
|
|
425
|
+
while ((ref = ref.p)) {
|
|
426
|
+
depth += 1;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
st.equal(depth, 5000, 'parsed is 5000 properties deep');
|
|
430
|
+
|
|
431
|
+
st.end();
|
|
432
|
+
});
|
|
433
|
+
|
|
399
434
|
t.test('parses null objects correctly', { skip: !Object.create }, function (st) {
|
|
400
435
|
var a = Object.create(null);
|
|
401
436
|
a.b = 'c';
|
|
@@ -512,7 +547,7 @@ test('parse()', function (t) {
|
|
|
512
547
|
result.push(parseInt(parts[1], 16));
|
|
513
548
|
parts = reg.exec(str);
|
|
514
549
|
}
|
|
515
|
-
return iconv.decode(
|
|
550
|
+
return iconv.decode(SaferBuffer.from(result), 'shift_jis').toString();
|
|
516
551
|
}
|
|
517
552
|
}), { 県: '大阪府' });
|
|
518
553
|
st.end();
|
|
@@ -529,7 +564,7 @@ test('parse()', function (t) {
|
|
|
529
564
|
});
|
|
530
565
|
|
|
531
566
|
t.test('throws error with wrong decoder', function (st) {
|
|
532
|
-
st
|
|
567
|
+
st['throws'](function () {
|
|
533
568
|
qs.parse({}, { decoder: 'string' });
|
|
534
569
|
}, new TypeError('Decoder has to be a function.'));
|
|
535
570
|
st.end();
|
|
@@ -542,5 +577,83 @@ test('parse()', function (t) {
|
|
|
542
577
|
st.end();
|
|
543
578
|
});
|
|
544
579
|
|
|
580
|
+
t.test('throws if an invalid charset is specified', function (st) {
|
|
581
|
+
st['throws'](function () {
|
|
582
|
+
qs.parse('a=b', { charset: 'foobar' });
|
|
583
|
+
}, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
|
|
584
|
+
st.end();
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
t.test('parses an iso-8859-1 string if asked to', function (st) {
|
|
588
|
+
st.deepEqual(qs.parse('%A2=%BD', { charset: 'iso-8859-1' }), { '¢': '½' });
|
|
589
|
+
st.end();
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
var urlEncodedCheckmarkInUtf8 = '%E2%9C%93';
|
|
593
|
+
var urlEncodedOSlashInUtf8 = '%C3%B8';
|
|
594
|
+
var urlEncodedNumCheckmark = '%26%2310003%3B';
|
|
595
|
+
var urlEncodedNumSmiley = '%26%239786%3B';
|
|
596
|
+
|
|
597
|
+
t.test('prefers an utf-8 charset specified by the utf8 sentinel to a default charset of iso-8859-1', function (st) {
|
|
598
|
+
st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'iso-8859-1' }), { ø: 'ø' });
|
|
599
|
+
st.end();
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
t.test('prefers an iso-8859-1 charset specified by the utf8 sentinel to a default charset of utf-8', function (st) {
|
|
603
|
+
st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { 'ø': 'ø' });
|
|
604
|
+
st.end();
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
t.test('does not require the utf8 sentinel to be defined before the parameters whose decoding it affects', function (st) {
|
|
608
|
+
st.deepEqual(qs.parse('a=' + urlEncodedOSlashInUtf8 + '&utf8=' + urlEncodedNumCheckmark, { charsetSentinel: true, charset: 'utf-8' }), { a: 'ø' });
|
|
609
|
+
st.end();
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
t.test('should ignore an utf8 sentinel with an unknown value', function (st) {
|
|
613
|
+
st.deepEqual(qs.parse('utf8=foo&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true, charset: 'utf-8' }), { ø: 'ø' });
|
|
614
|
+
st.end();
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
t.test('uses the utf8 sentinel to switch to utf-8 when no default charset is given', function (st) {
|
|
618
|
+
st.deepEqual(qs.parse('utf8=' + urlEncodedCheckmarkInUtf8 + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { ø: 'ø' });
|
|
619
|
+
st.end();
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
t.test('uses the utf8 sentinel to switch to iso-8859-1 when no default charset is given', function (st) {
|
|
623
|
+
st.deepEqual(qs.parse('utf8=' + urlEncodedNumCheckmark + '&' + urlEncodedOSlashInUtf8 + '=' + urlEncodedOSlashInUtf8, { charsetSentinel: true }), { 'ø': 'ø' });
|
|
624
|
+
st.end();
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
t.test('interprets numeric entities in iso-8859-1 when `interpretNumericEntities`', function (st) {
|
|
628
|
+
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1', interpretNumericEntities: true }), { foo: '☺' });
|
|
629
|
+
st.end();
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
t.test('handles a custom decoder returning `null`, in the `iso-8859-1` charset, when `interpretNumericEntities`', function (st) {
|
|
633
|
+
st.deepEqual(qs.parse('foo=&bar=' + urlEncodedNumSmiley, {
|
|
634
|
+
charset: 'iso-8859-1',
|
|
635
|
+
decoder: function (str, defaultDecoder, charset) {
|
|
636
|
+
return str ? defaultDecoder(str, defaultDecoder, charset) : null;
|
|
637
|
+
},
|
|
638
|
+
interpretNumericEntities: true
|
|
639
|
+
}), { foo: null, bar: '☺' });
|
|
640
|
+
st.end();
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
t.test('does not interpret numeric entities in iso-8859-1 when `interpretNumericEntities` is absent', function (st) {
|
|
644
|
+
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'iso-8859-1' }), { foo: '☺' });
|
|
645
|
+
st.end();
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
t.test('does not interpret numeric entities when the charset is utf-8, even when `interpretNumericEntities`', function (st) {
|
|
649
|
+
st.deepEqual(qs.parse('foo=' + urlEncodedNumSmiley, { charset: 'utf-8', interpretNumericEntities: true }), { foo: '☺' });
|
|
650
|
+
st.end();
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
t.test('does not interpret %uXXXX syntax in iso-8859-1 mode', function (st) {
|
|
654
|
+
st.deepEqual(qs.parse('%u263A=%u263A', { charset: 'iso-8859-1' }), { '%u263A': '%u263A' });
|
|
655
|
+
st.end();
|
|
656
|
+
});
|
|
657
|
+
|
|
545
658
|
t.end();
|
|
546
659
|
});
|
package/test/stringify.js
CHANGED
|
@@ -4,6 +4,7 @@ var test = require('tape');
|
|
|
4
4
|
var qs = require('../');
|
|
5
5
|
var utils = require('../lib/utils');
|
|
6
6
|
var iconv = require('iconv-lite');
|
|
7
|
+
var SaferBuffer = require('safer-buffer').Buffer;
|
|
7
8
|
|
|
8
9
|
test('stringify()', function (t) {
|
|
9
10
|
t.test('stringifies a querystring object', function (st) {
|
|
@@ -336,8 +337,8 @@ test('stringify()', function (t) {
|
|
|
336
337
|
});
|
|
337
338
|
|
|
338
339
|
t.test('stringifies buffer values', function (st) {
|
|
339
|
-
st.equal(qs.stringify({ a:
|
|
340
|
-
st.equal(qs.stringify({ a: { b:
|
|
340
|
+
st.equal(qs.stringify({ a: SaferBuffer.from('test') }), 'a=test');
|
|
341
|
+
st.equal(qs.stringify({ a: { b: SaferBuffer.from('test') } }), 'a%5Bb%5D=test');
|
|
341
342
|
st.end();
|
|
342
343
|
});
|
|
343
344
|
|
|
@@ -474,14 +475,14 @@ test('stringify()', function (t) {
|
|
|
474
475
|
});
|
|
475
476
|
|
|
476
477
|
t.test('throws error with wrong encoder', function (st) {
|
|
477
|
-
st
|
|
478
|
+
st['throws'](function () {
|
|
478
479
|
qs.stringify({}, { encoder: 'string' });
|
|
479
480
|
}, new TypeError('Encoder has to be a function.'));
|
|
480
481
|
st.end();
|
|
481
482
|
});
|
|
482
483
|
|
|
483
484
|
t.test('can use custom encoder for a buffer object', { skip: typeof Buffer === 'undefined' }, function (st) {
|
|
484
|
-
st.equal(qs.stringify({ a:
|
|
485
|
+
st.equal(qs.stringify({ a: SaferBuffer.from([1]) }, {
|
|
485
486
|
encoder: function (buffer) {
|
|
486
487
|
if (typeof buffer === 'string') {
|
|
487
488
|
return buffer;
|
|
@@ -504,7 +505,7 @@ test('stringify()', function (t) {
|
|
|
504
505
|
mutatedDate.toISOString = function () {
|
|
505
506
|
throw new SyntaxError();
|
|
506
507
|
};
|
|
507
|
-
st
|
|
508
|
+
st['throws'](function () {
|
|
508
509
|
mutatedDate.toISOString();
|
|
509
510
|
}, SyntaxError);
|
|
510
511
|
st.equal(
|
|
@@ -546,7 +547,7 @@ test('stringify()', function (t) {
|
|
|
546
547
|
t.test('Edge cases and unknown formats', function (st) {
|
|
547
548
|
['UFO1234', false, 1234, null, {}, []].forEach(
|
|
548
549
|
function (format) {
|
|
549
|
-
st
|
|
550
|
+
st['throws'](
|
|
550
551
|
function () {
|
|
551
552
|
qs.stringify({ a: 'b c' }, { format: format });
|
|
552
553
|
},
|
|
@@ -585,6 +586,38 @@ test('stringify()', function (t) {
|
|
|
585
586
|
st.end();
|
|
586
587
|
});
|
|
587
588
|
|
|
589
|
+
t.test('throws if an invalid charset is specified', function (st) {
|
|
590
|
+
st['throws'](function () {
|
|
591
|
+
qs.stringify({ a: 'b' }, { charset: 'foobar' });
|
|
592
|
+
}, new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined'));
|
|
593
|
+
st.end();
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
t.test('respects a charset of iso-8859-1', function (st) {
|
|
597
|
+
st.equal(qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' }), '%E6=%E6');
|
|
598
|
+
st.end();
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
t.test('encodes unrepresentable chars as numeric entities in iso-8859-1 mode', function (st) {
|
|
602
|
+
st.equal(qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' }), 'a=%26%239786%3B');
|
|
603
|
+
st.end();
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
t.test('respects an explicit charset of utf-8 (the default)', function (st) {
|
|
607
|
+
st.equal(qs.stringify({ a: 'æ' }, { charset: 'utf-8' }), 'a=%C3%A6');
|
|
608
|
+
st.end();
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
t.test('adds the right sentinel when instructed to and the charset is utf-8', function (st) {
|
|
612
|
+
st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'utf-8' }), 'utf8=%E2%9C%93&a=%C3%A6');
|
|
613
|
+
st.end();
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
t.test('adds the right sentinel when instructed to and the charset is iso-8859-1', function (st) {
|
|
617
|
+
st.equal(qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' }), 'utf8=%26%2310003%3B&a=%E6');
|
|
618
|
+
st.end();
|
|
619
|
+
});
|
|
620
|
+
|
|
588
621
|
t.test('does not mutate the options argument', function (st) {
|
|
589
622
|
var options = {};
|
|
590
623
|
qs.stringify({}, options);
|
|
@@ -592,5 +625,25 @@ test('stringify()', function (t) {
|
|
|
592
625
|
st.end();
|
|
593
626
|
});
|
|
594
627
|
|
|
628
|
+
t.test('strictNullHandling works with custom filter', function (st) {
|
|
629
|
+
var filter = function (prefix, value) {
|
|
630
|
+
return value;
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
var options = { strictNullHandling: true, filter: filter };
|
|
634
|
+
st.equal(qs.stringify({ key: null }, options), 'key');
|
|
635
|
+
st.end();
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
t.test('strictNullHandling works with null serializeDate', function (st) {
|
|
639
|
+
var serializeDate = function () {
|
|
640
|
+
return null;
|
|
641
|
+
};
|
|
642
|
+
var options = { strictNullHandling: true, serializeDate: serializeDate };
|
|
643
|
+
var date = new Date();
|
|
644
|
+
st.equal(qs.stringify({ key: date }, options), 'key');
|
|
645
|
+
st.end();
|
|
646
|
+
});
|
|
647
|
+
|
|
595
648
|
t.end();
|
|
596
649
|
});
|
package/test/utils.js
CHANGED
|
@@ -18,6 +18,9 @@ test('merge()', function (t) {
|
|
|
18
18
|
var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
|
|
19
19
|
t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });
|
|
20
20
|
|
|
21
|
+
var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
|
|
22
|
+
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });
|
|
23
|
+
|
|
21
24
|
t.end();
|
|
22
25
|
});
|
|
23
26
|
|
|
@@ -32,3 +35,55 @@ test('assign()', function (t) {
|
|
|
32
35
|
|
|
33
36
|
t.end();
|
|
34
37
|
});
|
|
38
|
+
|
|
39
|
+
test('combine()', function (t) {
|
|
40
|
+
t.test('both arrays', function (st) {
|
|
41
|
+
var a = [1];
|
|
42
|
+
var b = [2];
|
|
43
|
+
var combined = utils.combine(a, b);
|
|
44
|
+
|
|
45
|
+
st.deepEqual(a, [1], 'a is not mutated');
|
|
46
|
+
st.deepEqual(b, [2], 'b is not mutated');
|
|
47
|
+
st.notEqual(a, combined, 'a !== combined');
|
|
48
|
+
st.notEqual(b, combined, 'b !== combined');
|
|
49
|
+
st.deepEqual(combined, [1, 2], 'combined is a + b');
|
|
50
|
+
|
|
51
|
+
st.end();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
t.test('one array, one non-array', function (st) {
|
|
55
|
+
var aN = 1;
|
|
56
|
+
var a = [aN];
|
|
57
|
+
var bN = 2;
|
|
58
|
+
var b = [bN];
|
|
59
|
+
|
|
60
|
+
var combinedAnB = utils.combine(aN, b);
|
|
61
|
+
st.deepEqual(b, [bN], 'b is not mutated');
|
|
62
|
+
st.notEqual(aN, combinedAnB, 'aN + b !== aN');
|
|
63
|
+
st.notEqual(a, combinedAnB, 'aN + b !== a');
|
|
64
|
+
st.notEqual(bN, combinedAnB, 'aN + b !== bN');
|
|
65
|
+
st.notEqual(b, combinedAnB, 'aN + b !== b');
|
|
66
|
+
st.deepEqual([1, 2], combinedAnB, 'first argument is array-wrapped when not an array');
|
|
67
|
+
|
|
68
|
+
var combinedABn = utils.combine(a, bN);
|
|
69
|
+
st.deepEqual(a, [aN], 'a is not mutated');
|
|
70
|
+
st.notEqual(aN, combinedABn, 'a + bN !== aN');
|
|
71
|
+
st.notEqual(a, combinedABn, 'a + bN !== a');
|
|
72
|
+
st.notEqual(bN, combinedABn, 'a + bN !== bN');
|
|
73
|
+
st.notEqual(b, combinedABn, 'a + bN !== b');
|
|
74
|
+
st.deepEqual([1, 2], combinedABn, 'second argument is array-wrapped when not an array');
|
|
75
|
+
|
|
76
|
+
st.end();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
t.test('neither is an array', function (st) {
|
|
80
|
+
var combined = utils.combine(1, 2);
|
|
81
|
+
st.notEqual(1, combined, '1 + 2 !== 1');
|
|
82
|
+
st.notEqual(2, combined, '1 + 2 !== 2');
|
|
83
|
+
st.deepEqual([1, 2], combined, 'both arguments are array-wrapped when not an array');
|
|
84
|
+
|
|
85
|
+
st.end();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
t.end();
|
|
89
|
+
});
|