qs 6.9.7 → 6.10.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/CHANGELOG.md +22 -5
- package/README.md +7 -0
- package/dist/qs.js +1207 -5
- package/lib/parse.js +6 -0
- package/lib/stringify.js +33 -3
- package/package.json +4 -1
- package/test/parse.js +9 -0
- package/test/stringify.js +62 -1
package/lib/parse.js
CHANGED
|
@@ -8,6 +8,7 @@ var isArray = Array.isArray;
|
|
|
8
8
|
var defaults = {
|
|
9
9
|
allowDots: false,
|
|
10
10
|
allowPrototypes: false,
|
|
11
|
+
allowSparse: false,
|
|
11
12
|
arrayLimit: 20,
|
|
12
13
|
charset: 'utf-8',
|
|
13
14
|
charsetSentinel: false,
|
|
@@ -217,6 +218,7 @@ var normalizeParseOptions = function normalizeParseOptions(opts) {
|
|
|
217
218
|
return {
|
|
218
219
|
allowDots: typeof opts.allowDots === 'undefined' ? defaults.allowDots : !!opts.allowDots,
|
|
219
220
|
allowPrototypes: typeof opts.allowPrototypes === 'boolean' ? opts.allowPrototypes : defaults.allowPrototypes,
|
|
221
|
+
allowSparse: typeof opts.allowSparse === 'boolean' ? opts.allowSparse : defaults.allowSparse,
|
|
220
222
|
arrayLimit: typeof opts.arrayLimit === 'number' ? opts.arrayLimit : defaults.arrayLimit,
|
|
221
223
|
charset: charset,
|
|
222
224
|
charsetSentinel: typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
|
|
@@ -253,5 +255,9 @@ module.exports = function (str, opts) {
|
|
|
253
255
|
obj = utils.merge(obj, newObj, options);
|
|
254
256
|
}
|
|
255
257
|
|
|
258
|
+
if (options.allowSparse === true) {
|
|
259
|
+
return obj;
|
|
260
|
+
}
|
|
261
|
+
|
|
256
262
|
return utils.compact(obj);
|
|
257
263
|
};
|
package/lib/stringify.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var getSideChannel = require('side-channel');
|
|
3
4
|
var utils = require('./utils');
|
|
4
5
|
var formats = require('./formats');
|
|
5
6
|
var has = Object.prototype.hasOwnProperty;
|
|
@@ -55,6 +56,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
|
|
|
55
56
|
|| typeof v === 'bigint';
|
|
56
57
|
};
|
|
57
58
|
|
|
59
|
+
var sentinel = {};
|
|
60
|
+
|
|
58
61
|
var stringify = function stringify(
|
|
59
62
|
object,
|
|
60
63
|
prefix,
|
|
@@ -69,9 +72,30 @@ var stringify = function stringify(
|
|
|
69
72
|
format,
|
|
70
73
|
formatter,
|
|
71
74
|
encodeValuesOnly,
|
|
72
|
-
charset
|
|
75
|
+
charset,
|
|
76
|
+
sideChannel
|
|
73
77
|
) {
|
|
74
78
|
var obj = object;
|
|
79
|
+
|
|
80
|
+
var tmpSc = sideChannel;
|
|
81
|
+
var step = 0;
|
|
82
|
+
var findFlag = false;
|
|
83
|
+
while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
|
|
84
|
+
// Where object last appeared in the ref tree
|
|
85
|
+
var pos = tmpSc.get(object);
|
|
86
|
+
step += 1;
|
|
87
|
+
if (typeof pos !== 'undefined') {
|
|
88
|
+
if (pos === step) {
|
|
89
|
+
throw new RangeError('Cyclic object value');
|
|
90
|
+
} else {
|
|
91
|
+
findFlag = true; // Break while
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (typeof tmpSc.get(sentinel) === 'undefined') {
|
|
95
|
+
step = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
if (typeof filter === 'function') {
|
|
76
100
|
obj = filter(prefix, obj);
|
|
77
101
|
} else if (obj instanceof Date) {
|
|
@@ -138,6 +162,9 @@ var stringify = function stringify(
|
|
|
138
162
|
? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
|
|
139
163
|
: prefix + (allowDots ? '.' + key : '[' + key + ']');
|
|
140
164
|
|
|
165
|
+
sideChannel.set(object, step);
|
|
166
|
+
var valueSideChannel = getSideChannel();
|
|
167
|
+
valueSideChannel.set(sentinel, sideChannel);
|
|
141
168
|
pushToArray(values, stringify(
|
|
142
169
|
value,
|
|
143
170
|
keyPrefix,
|
|
@@ -152,7 +179,8 @@ var stringify = function stringify(
|
|
|
152
179
|
format,
|
|
153
180
|
formatter,
|
|
154
181
|
encodeValuesOnly,
|
|
155
|
-
charset
|
|
182
|
+
charset,
|
|
183
|
+
valueSideChannel
|
|
156
184
|
));
|
|
157
185
|
}
|
|
158
186
|
|
|
@@ -246,6 +274,7 @@ module.exports = function (object, opts) {
|
|
|
246
274
|
objKeys.sort(options.sort);
|
|
247
275
|
}
|
|
248
276
|
|
|
277
|
+
var sideChannel = getSideChannel();
|
|
249
278
|
for (var i = 0; i < objKeys.length; ++i) {
|
|
250
279
|
var key = objKeys[i];
|
|
251
280
|
|
|
@@ -266,7 +295,8 @@ module.exports = function (object, opts) {
|
|
|
266
295
|
options.format,
|
|
267
296
|
options.formatter,
|
|
268
297
|
options.encodeValuesOnly,
|
|
269
|
-
options.charset
|
|
298
|
+
options.charset,
|
|
299
|
+
sideChannel
|
|
270
300
|
));
|
|
271
301
|
}
|
|
272
302
|
|
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.
|
|
5
|
+
"version": "6.10.3",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/ljharb/qs.git"
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=0.6"
|
|
31
31
|
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"side-channel": "^1.0.4"
|
|
34
|
+
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"@ljharb/eslint-config": "^20.1.0",
|
|
34
37
|
"aud": "^1.1.5",
|
package/test/parse.js
CHANGED
|
@@ -269,6 +269,15 @@ test('parse()', function (t) {
|
|
|
269
269
|
st.end();
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
+
t.test('parses sparse arrays', function (st) {
|
|
273
|
+
/* eslint no-sparse-arrays: 0 */
|
|
274
|
+
st.deepEqual(qs.parse('a[4]=1&a[1]=2', { allowSparse: true }), { a: [, '2', , , '1'] });
|
|
275
|
+
st.deepEqual(qs.parse('a[1][b][2][c]=1', { allowSparse: true }), { a: [, { b: [, , { c: '1' }] }] });
|
|
276
|
+
st.deepEqual(qs.parse('a[1][2][3][c]=1', { allowSparse: true }), { a: [, [, , [, , , { c: '1' }]]] });
|
|
277
|
+
st.deepEqual(qs.parse('a[1][2][3][c][1]=1', { allowSparse: true }), { a: [, [, , [, , , { c: [, '1'] }]]] });
|
|
278
|
+
st.end();
|
|
279
|
+
});
|
|
280
|
+
|
|
272
281
|
t.test('parses semi-parsed strings', function (st) {
|
|
273
282
|
st.deepEqual(qs.parse({ 'a[b]': 'c' }), { a: { b: 'c' } });
|
|
274
283
|
st.deepEqual(qs.parse({ 'a[b]': 'c', 'a[d]': 'e' }), { a: { b: 'c', d: 'e' } });
|
package/test/stringify.js
CHANGED
|
@@ -439,7 +439,7 @@ test('stringify()', function (t) {
|
|
|
439
439
|
st.end();
|
|
440
440
|
});
|
|
441
441
|
|
|
442
|
-
t.test('
|
|
442
|
+
t.test('does not blow up when Buffer global is missing', function (st) {
|
|
443
443
|
var tempBuffer = global.Buffer;
|
|
444
444
|
delete global.Buffer;
|
|
445
445
|
var result = qs.stringify({ a: 'b', c: 'd' });
|
|
@@ -448,6 +448,57 @@ test('stringify()', function (t) {
|
|
|
448
448
|
st.end();
|
|
449
449
|
});
|
|
450
450
|
|
|
451
|
+
t.test('does not crash when parsing circular references', function (st) {
|
|
452
|
+
var a = {};
|
|
453
|
+
a.b = a;
|
|
454
|
+
|
|
455
|
+
st['throws'](
|
|
456
|
+
function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
|
|
457
|
+
/RangeError: Cyclic object value/,
|
|
458
|
+
'cyclic values throw'
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
var circular = {
|
|
462
|
+
a: 'value'
|
|
463
|
+
};
|
|
464
|
+
circular.a = circular;
|
|
465
|
+
st['throws'](
|
|
466
|
+
function () { qs.stringify(circular); },
|
|
467
|
+
/RangeError: Cyclic object value/,
|
|
468
|
+
'cyclic values throw'
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
var arr = ['a'];
|
|
472
|
+
st.doesNotThrow(
|
|
473
|
+
function () { qs.stringify({ x: arr, y: arr }); },
|
|
474
|
+
'non-cyclic values do not throw'
|
|
475
|
+
);
|
|
476
|
+
|
|
477
|
+
st.end();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
t.test('non-circular duplicated references can still work', function (st) {
|
|
481
|
+
var hourOfDay = {
|
|
482
|
+
'function': 'hour_of_day'
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
var p1 = {
|
|
486
|
+
'function': 'gte',
|
|
487
|
+
arguments: [hourOfDay, 0]
|
|
488
|
+
};
|
|
489
|
+
var p2 = {
|
|
490
|
+
'function': 'lte',
|
|
491
|
+
arguments: [hourOfDay, 23]
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
st.equal(
|
|
495
|
+
qs.stringify({ filters: { $and: [p1, p2] } }, { encodeValuesOnly: true }),
|
|
496
|
+
'filters[$and][0][function]=gte&filters[$and][0][arguments][0][function]=hour_of_day&filters[$and][0][arguments][1]=0&filters[$and][1][function]=lte&filters[$and][1][arguments][0][function]=hour_of_day&filters[$and][1][arguments][1]=23'
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
st.end();
|
|
500
|
+
});
|
|
501
|
+
|
|
451
502
|
t.test('selects properties when filter=array', function (st) {
|
|
452
503
|
st.equal(qs.stringify({ a: 'b' }, { filter: ['a'] }), 'a=b');
|
|
453
504
|
st.equal(qs.stringify({ a: 1 }, { filter: [] }), '');
|
|
@@ -800,5 +851,15 @@ test('stringify()', function (t) {
|
|
|
800
851
|
st.end();
|
|
801
852
|
});
|
|
802
853
|
|
|
854
|
+
t.test('stringifies sparse arrays', function (st) {
|
|
855
|
+
/* eslint no-sparse-arrays: 0 */
|
|
856
|
+
st.equal(qs.stringify({ a: [, '2', , , '1'] }, { encodeValuesOnly: true }), 'a[1]=2&a[4]=1');
|
|
857
|
+
st.equal(qs.stringify({ a: [, { b: [, , { c: '1' }] }] }, { encodeValuesOnly: true }), 'a[1][b][2][c]=1');
|
|
858
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: '1' }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c]=1');
|
|
859
|
+
st.equal(qs.stringify({ a: [, [, , [, , , { c: [, '1'] }]]] }, { encodeValuesOnly: true }), 'a[1][2][3][c][1]=1');
|
|
860
|
+
|
|
861
|
+
st.end();
|
|
862
|
+
});
|
|
863
|
+
|
|
803
864
|
t.end();
|
|
804
865
|
});
|