qs 6.1.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +4 -2
- package/CHANGELOG.md +16 -1
- package/README.md +41 -0
- package/dist/qs.js +84 -74
- package/lib/parse.js +42 -40
- package/lib/stringify.js +36 -30
- package/lib/utils.js +6 -4
- package/package.json +10 -7
- package/test/parse.js +63 -5
- package/test/stringify.js +50 -4
- package/.npmignore +0 -18
- package/.travis.yml +0 -69
- package/bower.json +0 -21
- package/component.json +0 -15
package/.eslintrc
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
"extends": "@ljharb",
|
|
5
5
|
|
|
6
6
|
"rules": {
|
|
7
|
-
"complexity": [2,
|
|
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,
|
|
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,4 +1,16 @@
|
|
|
1
|
-
##
|
|
1
|
+
## **6.2.1**
|
|
2
|
+
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
|
|
3
|
+
- [Refactor] Be explicit and use `Object.prototype.hasOwnProperty.call`
|
|
4
|
+
- [Tests] remove `parallelshell` since it does not reliably report failures
|
|
5
|
+
- [Tests] up to `node` `v6.3`, `v5.12`
|
|
6
|
+
- [Dev Deps] update `tape`, `eslint`, `@ljharb/eslint-config`, `qs-iconv`
|
|
7
|
+
|
|
8
|
+
## [**6.2.0**](https://github.com/ljharb/qs/issues?milestone=36&state=closed)
|
|
9
|
+
- [New] pass Buffers to the encoder/decoder directly (#161)
|
|
10
|
+
- [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
|
|
11
|
+
- [Fix] fix compacting of nested sparse arrays (#150)
|
|
12
|
+
|
|
13
|
+
## [**6.1.0**](https://github.com/ljharb/qs/issues?milestone=35&state=closed)
|
|
2
14
|
- [New] allowDots option for `stringify` (#151)
|
|
3
15
|
- [Fix] "sort" option should work at a depth of 3 or more (#151)
|
|
4
16
|
- [Fix] Restore `dist` directory; will be removed in v7 (#148)
|
|
@@ -12,6 +24,9 @@
|
|
|
12
24
|
## [**6.0.0**](https://github.com/ljharb/qs/issues?milestone=31&state=closed)
|
|
13
25
|
- [**#124**](https://github.com/ljharb/qs/issues/124) Use ES6 and drop support for node < v4
|
|
14
26
|
|
|
27
|
+
## **5.2.1**
|
|
28
|
+
- [Fix] ensure `key[]=x&key[]&key[]=y` results in 3, not 2, values
|
|
29
|
+
|
|
15
30
|
## [**5.2.0**](https://github.com/ljharb/qs/issues?milestone=30&state=closed)
|
|
16
31
|
- [**#64**](https://github.com/ljharb/qs/issues/64) Add option to sort object keys in the query string
|
|
17
32
|
|
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
|
|
17
|
+
var has = Object.prototype.hasOwnProperty;
|
|
18
|
+
|
|
19
|
+
var defaults = {
|
|
18
20
|
delimiter: '&',
|
|
19
21
|
depth: 5,
|
|
20
22
|
arrayLimit: 20,
|
|
@@ -22,10 +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
|
-
|
|
31
|
+
var parseValues = function parseValues(str, options) {
|
|
29
32
|
var obj = {};
|
|
30
33
|
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
|
|
31
34
|
|
|
@@ -33,28 +36,25 @@ internals.parseValues = function (str, options) {
|
|
|
33
36
|
var part = parts[i];
|
|
34
37
|
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
|
|
35
38
|
|
|
39
|
+
var key, val;
|
|
36
40
|
if (pos === -1) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (options.strictNullHandling) {
|
|
40
|
-
obj[Utils.decode(part)] = null;
|
|
41
|
-
}
|
|
41
|
+
key = options.decoder(part);
|
|
42
|
+
val = options.strictNullHandling ? null : '';
|
|
42
43
|
} else {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
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;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
return obj;
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
var parseObject = function parseObject(chain, val, options) {
|
|
58
58
|
if (!chain.length) {
|
|
59
59
|
return val;
|
|
60
60
|
}
|
|
@@ -64,7 +64,7 @@ internals.parseObject = function (chain, val, options) {
|
|
|
64
64
|
var obj;
|
|
65
65
|
if (root === '[]') {
|
|
66
66
|
obj = [];
|
|
67
|
-
obj = obj.concat(
|
|
67
|
+
obj = obj.concat(parseObject(chain, val, options));
|
|
68
68
|
} else {
|
|
69
69
|
obj = options.plainObjects ? Object.create(null) : {};
|
|
70
70
|
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
|
|
@@ -77,16 +77,16 @@ internals.parseObject = function (chain, val, options) {
|
|
|
77
77
|
(options.parseArrays && index <= options.arrayLimit)
|
|
78
78
|
) {
|
|
79
79
|
obj = [];
|
|
80
|
-
obj[index] =
|
|
80
|
+
obj[index] = parseObject(chain, val, options);
|
|
81
81
|
} else {
|
|
82
|
-
obj[cleanRoot] =
|
|
82
|
+
obj[cleanRoot] = parseObject(chain, val, options);
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
return obj;
|
|
87
87
|
};
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
var parseKeys = function parseKeys(givenKey, val, options) {
|
|
90
90
|
if (!givenKey) {
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
@@ -109,7 +109,7 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
109
109
|
if (segment[1]) {
|
|
110
110
|
// If we aren't using plain objects, optionally prefix keys
|
|
111
111
|
// that would overwrite object prototype properties
|
|
112
|
-
if (!options.plainObjects && Object.prototype
|
|
112
|
+
if (!options.plainObjects && has.call(Object.prototype, segment[1])) {
|
|
113
113
|
if (!options.allowPrototypes) {
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
@@ -123,7 +123,7 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
123
123
|
var i = 0;
|
|
124
124
|
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
|
125
125
|
i += 1;
|
|
126
|
-
if (!options.plainObjects && Object.prototype
|
|
126
|
+
if (!options.plainObjects && has.call(Object.prototype, segment[1].replace(/\[|\]/g, ''))) {
|
|
127
127
|
if (!options.allowPrototypes) {
|
|
128
128
|
continue;
|
|
129
129
|
}
|
|
@@ -137,30 +137,32 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
137
137
|
keys.push('[' + key.slice(segment.index) + ']');
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
return
|
|
140
|
+
return parseObject(keys, val, options);
|
|
141
141
|
};
|
|
142
142
|
|
|
143
143
|
module.exports = function (str, opts) {
|
|
144
144
|
var options = opts || {};
|
|
145
|
-
|
|
146
|
-
options.
|
|
147
|
-
|
|
145
|
+
|
|
146
|
+
if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
|
|
147
|
+
throw new TypeError('Decoder has to be a function.');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
|
|
151
|
+
options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
|
|
152
|
+
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
|
|
148
153
|
options.parseArrays = options.parseArrays !== false;
|
|
149
|
-
options.
|
|
150
|
-
options.
|
|
151
|
-
options.
|
|
152
|
-
options.
|
|
153
|
-
options.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
str === null ||
|
|
158
|
-
typeof str === 'undefined'
|
|
159
|
-
) {
|
|
154
|
+
options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
|
|
155
|
+
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
|
|
156
|
+
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
|
|
157
|
+
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
|
|
158
|
+
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
|
|
159
|
+
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
|
|
160
|
+
|
|
161
|
+
if (str === '' || str === null || typeof str === 'undefined') {
|
|
160
162
|
return options.plainObjects ? Object.create(null) : {};
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
var tempObj = typeof str === 'string' ?
|
|
165
|
+
var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
|
|
164
166
|
var obj = options.plainObjects ? Object.create(null) : {};
|
|
165
167
|
|
|
166
168
|
// Iterate over the keys and setup the new object
|
|
@@ -168,7 +170,7 @@ module.exports = function (str, opts) {
|
|
|
168
170
|
var keys = Object.keys(tempObj);
|
|
169
171
|
for (var i = 0; i < keys.length; ++i) {
|
|
170
172
|
var key = keys[i];
|
|
171
|
-
var newObj =
|
|
173
|
+
var newObj = parseKeys(key, tempObj[key], options);
|
|
172
174
|
obj = Utils.merge(obj, newObj, options);
|
|
173
175
|
}
|
|
174
176
|
|
|
@@ -180,45 +182,45 @@ module.exports = function (str, opts) {
|
|
|
180
182
|
|
|
181
183
|
var Utils = require('./utils');
|
|
182
184
|
|
|
183
|
-
var
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
indices: function (prefix, key) {
|
|
190
|
-
return prefix + '[' + key + ']';
|
|
191
|
-
},
|
|
192
|
-
repeat: function (prefix) {
|
|
193
|
-
return prefix;
|
|
194
|
-
}
|
|
185
|
+
var arrayPrefixGenerators = {
|
|
186
|
+
brackets: function brackets(prefix) {
|
|
187
|
+
return prefix + '[]';
|
|
188
|
+
},
|
|
189
|
+
indices: function indices(prefix, key) {
|
|
190
|
+
return prefix + '[' + key + ']';
|
|
195
191
|
},
|
|
192
|
+
repeat: function repeat(prefix) {
|
|
193
|
+
return prefix;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
var defaults = {
|
|
198
|
+
delimiter: '&',
|
|
196
199
|
strictNullHandling: false,
|
|
197
200
|
skipNulls: false,
|
|
198
|
-
encode: true
|
|
201
|
+
encode: true,
|
|
202
|
+
encoder: Utils.encode
|
|
199
203
|
};
|
|
200
204
|
|
|
201
|
-
|
|
205
|
+
var stringify = function stringify(object, prefix, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots) {
|
|
202
206
|
var obj = object;
|
|
203
207
|
if (typeof filter === 'function') {
|
|
204
208
|
obj = filter(prefix, obj);
|
|
205
|
-
} else if (Utils.isBuffer(obj)) {
|
|
206
|
-
obj = String(obj);
|
|
207
209
|
} else if (obj instanceof Date) {
|
|
208
210
|
obj = obj.toISOString();
|
|
209
211
|
} else if (obj === null) {
|
|
210
212
|
if (strictNullHandling) {
|
|
211
|
-
return
|
|
213
|
+
return encoder ? encoder(prefix) : prefix;
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
obj = '';
|
|
215
217
|
}
|
|
216
218
|
|
|
217
|
-
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean') {
|
|
218
|
-
if (
|
|
219
|
-
return [
|
|
219
|
+
if (typeof obj === 'string' || typeof obj === 'number' || typeof obj === 'boolean' || Utils.isBuffer(obj)) {
|
|
220
|
+
if (encoder) {
|
|
221
|
+
return [encoder(prefix) + '=' + encoder(obj)];
|
|
220
222
|
}
|
|
221
|
-
return [prefix + '=' + obj];
|
|
223
|
+
return [prefix + '=' + String(obj)];
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
var values = [];
|
|
@@ -243,9 +245,9 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
|
|
|
243
245
|
}
|
|
244
246
|
|
|
245
247
|
if (Array.isArray(obj)) {
|
|
246
|
-
values = values.concat(
|
|
248
|
+
values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
|
|
247
249
|
} else {
|
|
248
|
-
values = values.concat(
|
|
250
|
+
values = values.concat(stringify(obj[key], prefix + (allowDots ? '.' + key : '[' + key + ']'), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
|
|
249
251
|
}
|
|
250
252
|
}
|
|
251
253
|
|
|
@@ -255,14 +257,20 @@ internals.stringify = function (object, prefix, generateArrayPrefix, strictNullH
|
|
|
255
257
|
module.exports = function (object, opts) {
|
|
256
258
|
var obj = object;
|
|
257
259
|
var options = opts || {};
|
|
258
|
-
var delimiter = typeof options.delimiter === 'undefined' ?
|
|
259
|
-
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling :
|
|
260
|
-
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls :
|
|
261
|
-
var encode = typeof options.encode === 'boolean' ? options.encode :
|
|
260
|
+
var delimiter = typeof options.delimiter === 'undefined' ? defaults.delimiter : options.delimiter;
|
|
261
|
+
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
|
|
262
|
+
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls : defaults.skipNulls;
|
|
263
|
+
var encode = typeof options.encode === 'boolean' ? options.encode : defaults.encode;
|
|
264
|
+
var encoder = encode ? (typeof options.encoder === 'function' ? options.encoder : defaults.encoder) : null;
|
|
262
265
|
var sort = typeof options.sort === 'function' ? options.sort : null;
|
|
263
266
|
var allowDots = typeof options.allowDots === 'undefined' ? false : options.allowDots;
|
|
264
267
|
var objKeys;
|
|
265
268
|
var filter;
|
|
269
|
+
|
|
270
|
+
if (options.encoder !== null && options.encoder !== undefined && typeof options.encoder !== 'function') {
|
|
271
|
+
throw new TypeError('Encoder has to be a function.');
|
|
272
|
+
}
|
|
273
|
+
|
|
266
274
|
if (typeof options.filter === 'function') {
|
|
267
275
|
filter = options.filter;
|
|
268
276
|
obj = filter('', obj);
|
|
@@ -277,7 +285,7 @@ module.exports = function (object, opts) {
|
|
|
277
285
|
}
|
|
278
286
|
|
|
279
287
|
var arrayFormat;
|
|
280
|
-
if (options.arrayFormat in
|
|
288
|
+
if (options.arrayFormat in arrayPrefixGenerators) {
|
|
281
289
|
arrayFormat = options.arrayFormat;
|
|
282
290
|
} else if ('indices' in options) {
|
|
283
291
|
arrayFormat = options.indices ? 'indices' : 'repeat';
|
|
@@ -285,7 +293,7 @@ module.exports = function (object, opts) {
|
|
|
285
293
|
arrayFormat = 'indices';
|
|
286
294
|
}
|
|
287
295
|
|
|
288
|
-
var generateArrayPrefix =
|
|
296
|
+
var generateArrayPrefix = arrayPrefixGenerators[arrayFormat];
|
|
289
297
|
|
|
290
298
|
if (!objKeys) {
|
|
291
299
|
objKeys = Object.keys(obj);
|
|
@@ -302,7 +310,7 @@ module.exports = function (object, opts) {
|
|
|
302
310
|
continue;
|
|
303
311
|
}
|
|
304
312
|
|
|
305
|
-
keys = keys.concat(
|
|
313
|
+
keys = keys.concat(stringify(obj[key], key, generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
|
|
306
314
|
}
|
|
307
315
|
|
|
308
316
|
return keys.join(delimiter);
|
|
@@ -357,7 +365,7 @@ exports.merge = function (target, source, options) {
|
|
|
357
365
|
mergeTarget = exports.arrayToObject(target, options);
|
|
358
366
|
}
|
|
359
367
|
|
|
360
|
-
|
|
368
|
+
return Object.keys(source).reduce(function (acc, key) {
|
|
361
369
|
var value = source[key];
|
|
362
370
|
|
|
363
371
|
if (Object.prototype.hasOwnProperty.call(acc, key)) {
|
|
@@ -365,7 +373,7 @@ exports.merge = function (target, source, options) {
|
|
|
365
373
|
} else {
|
|
366
374
|
acc[key] = value;
|
|
367
375
|
}
|
|
368
|
-
|
|
376
|
+
return acc;
|
|
369
377
|
}, mergeTarget);
|
|
370
378
|
};
|
|
371
379
|
|
|
@@ -420,7 +428,7 @@ exports.encode = function (str) {
|
|
|
420
428
|
|
|
421
429
|
i += 1;
|
|
422
430
|
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
|
|
423
|
-
out +=
|
|
431
|
+
out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)];
|
|
424
432
|
}
|
|
425
433
|
|
|
426
434
|
return out;
|
|
@@ -443,7 +451,9 @@ exports.compact = function (obj, references) {
|
|
|
443
451
|
var compacted = [];
|
|
444
452
|
|
|
445
453
|
for (var i = 0; i < obj.length; ++i) {
|
|
446
|
-
if (typeof obj[i]
|
|
454
|
+
if (obj[i] && typeof obj[i] === 'object') {
|
|
455
|
+
compacted.push(exports.compact(obj[i], refs));
|
|
456
|
+
} else if (typeof obj[i] !== 'undefined') {
|
|
447
457
|
compacted.push(obj[i]);
|
|
448
458
|
}
|
|
449
459
|
}
|
package/lib/parse.js
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var Utils = require('./utils');
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var has = Object.prototype.hasOwnProperty;
|
|
6
|
+
|
|
7
|
+
var defaults = {
|
|
6
8
|
delimiter: '&',
|
|
7
9
|
depth: 5,
|
|
8
10
|
arrayLimit: 20,
|
|
@@ -10,10 +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
|
-
|
|
19
|
+
var parseValues = function parseValues(str, options) {
|
|
17
20
|
var obj = {};
|
|
18
21
|
var parts = str.split(options.delimiter, options.parameterLimit === Infinity ? undefined : options.parameterLimit);
|
|
19
22
|
|
|
@@ -21,28 +24,25 @@ internals.parseValues = function (str, options) {
|
|
|
21
24
|
var part = parts[i];
|
|
22
25
|
var pos = part.indexOf(']=') === -1 ? part.indexOf('=') : part.indexOf(']=') + 1;
|
|
23
26
|
|
|
27
|
+
var key, val;
|
|
24
28
|
if (pos === -1) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (options.strictNullHandling) {
|
|
28
|
-
obj[Utils.decode(part)] = null;
|
|
29
|
-
}
|
|
29
|
+
key = options.decoder(part);
|
|
30
|
+
val = options.strictNullHandling ? null : '';
|
|
30
31
|
} else {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
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;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return obj;
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
var parseObject = function parseObject(chain, val, options) {
|
|
46
46
|
if (!chain.length) {
|
|
47
47
|
return val;
|
|
48
48
|
}
|
|
@@ -52,7 +52,7 @@ internals.parseObject = function (chain, val, options) {
|
|
|
52
52
|
var obj;
|
|
53
53
|
if (root === '[]') {
|
|
54
54
|
obj = [];
|
|
55
|
-
obj = obj.concat(
|
|
55
|
+
obj = obj.concat(parseObject(chain, val, options));
|
|
56
56
|
} else {
|
|
57
57
|
obj = options.plainObjects ? Object.create(null) : {};
|
|
58
58
|
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
|
|
@@ -65,16 +65,16 @@ internals.parseObject = function (chain, val, options) {
|
|
|
65
65
|
(options.parseArrays && index <= options.arrayLimit)
|
|
66
66
|
) {
|
|
67
67
|
obj = [];
|
|
68
|
-
obj[index] =
|
|
68
|
+
obj[index] = parseObject(chain, val, options);
|
|
69
69
|
} else {
|
|
70
|
-
obj[cleanRoot] =
|
|
70
|
+
obj[cleanRoot] = parseObject(chain, val, options);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return obj;
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
var parseKeys = function parseKeys(givenKey, val, options) {
|
|
78
78
|
if (!givenKey) {
|
|
79
79
|
return;
|
|
80
80
|
}
|
|
@@ -97,7 +97,7 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
97
97
|
if (segment[1]) {
|
|
98
98
|
// If we aren't using plain objects, optionally prefix keys
|
|
99
99
|
// that would overwrite object prototype properties
|
|
100
|
-
if (!options.plainObjects && Object.prototype
|
|
100
|
+
if (!options.plainObjects && has.call(Object.prototype, segment[1])) {
|
|
101
101
|
if (!options.allowPrototypes) {
|
|
102
102
|
return;
|
|
103
103
|
}
|
|
@@ -111,7 +111,7 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
111
111
|
var i = 0;
|
|
112
112
|
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
|
113
113
|
i += 1;
|
|
114
|
-
if (!options.plainObjects && Object.prototype
|
|
114
|
+
if (!options.plainObjects && has.call(Object.prototype, segment[1].replace(/\[|\]/g, ''))) {
|
|
115
115
|
if (!options.allowPrototypes) {
|
|
116
116
|
continue;
|
|
117
117
|
}
|
|
@@ -125,30 +125,32 @@ internals.parseKeys = function (givenKey, val, options) {
|
|
|
125
125
|
keys.push('[' + key.slice(segment.index) + ']');
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
return
|
|
128
|
+
return parseObject(keys, val, options);
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
module.exports = function (str, opts) {
|
|
132
132
|
var options = opts || {};
|
|
133
|
-
|
|
134
|
-
options.
|
|
135
|
-
|
|
133
|
+
|
|
134
|
+
if (options.decoder !== null && options.decoder !== undefined && typeof options.decoder !== 'function') {
|
|
135
|
+
throw new TypeError('Decoder has to be a function.');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : defaults.delimiter;
|
|
139
|
+
options.depth = typeof options.depth === 'number' ? options.depth : defaults.depth;
|
|
140
|
+
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : defaults.arrayLimit;
|
|
136
141
|
options.parseArrays = options.parseArrays !== false;
|
|
137
|
-
options.
|
|
138
|
-
options.
|
|
139
|
-
options.
|
|
140
|
-
options.
|
|
141
|
-
options.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
str === null ||
|
|
146
|
-
typeof str === 'undefined'
|
|
147
|
-
) {
|
|
142
|
+
options.decoder = typeof options.decoder === 'function' ? options.decoder : defaults.decoder;
|
|
143
|
+
options.allowDots = typeof options.allowDots === 'boolean' ? options.allowDots : defaults.allowDots;
|
|
144
|
+
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : defaults.plainObjects;
|
|
145
|
+
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : defaults.allowPrototypes;
|
|
146
|
+
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : defaults.parameterLimit;
|
|
147
|
+
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : defaults.strictNullHandling;
|
|
148
|
+
|
|
149
|
+
if (str === '' || str === null || typeof str === 'undefined') {
|
|
148
150
|
return options.plainObjects ? Object.create(null) : {};
|
|
149
151
|
}
|
|
150
152
|
|
|
151
|
-
var tempObj = typeof str === 'string' ?
|
|
153
|
+
var tempObj = typeof str === 'string' ? parseValues(str, options) : str;
|
|
152
154
|
var obj = options.plainObjects ? Object.create(null) : {};
|
|
153
155
|
|
|
154
156
|
// Iterate over the keys and setup the new object
|
|
@@ -156,7 +158,7 @@ module.exports = function (str, opts) {
|
|
|
156
158
|
var keys = Object.keys(tempObj);
|
|
157
159
|
for (var i = 0; i < keys.length; ++i) {
|
|
158
160
|
var key = keys[i];
|
|
159
|
-
var newObj =
|
|
161
|
+
var newObj = parseKeys(key, tempObj[key], options);
|
|
160
162
|
obj = Utils.merge(obj, newObj, options);
|
|
161
163
|
}
|
|
162
164
|
|
package/lib/stringify.js
CHANGED
|
@@ -2,45 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
var Utils = require('./utils');
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
41
|
-
return [
|
|
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(
|
|
68
|
+
values = values.concat(stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, skipNulls, encoder, filter, sort, allowDots));
|
|
69
69
|
} else {
|
|
70
|
-
values = values.concat(
|
|
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' ?
|
|
81
|
-
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling :
|
|
82
|
-
var skipNulls = typeof options.skipNulls === 'boolean' ? options.skipNulls :
|
|
83
|
-
var encode = typeof options.encode === 'boolean' ? options.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
|
|
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 =
|
|
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(
|
|
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
|
@@ -46,7 +46,7 @@ exports.merge = function (target, source, options) {
|
|
|
46
46
|
mergeTarget = exports.arrayToObject(target, options);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
return Object.keys(source).reduce(function (acc, key) {
|
|
50
50
|
var value = source[key];
|
|
51
51
|
|
|
52
52
|
if (Object.prototype.hasOwnProperty.call(acc, key)) {
|
|
@@ -54,7 +54,7 @@ exports.merge = function (target, source, options) {
|
|
|
54
54
|
} else {
|
|
55
55
|
acc[key] = value;
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
return acc;
|
|
58
58
|
}, mergeTarget);
|
|
59
59
|
};
|
|
60
60
|
|
|
@@ -109,7 +109,7 @@ exports.encode = function (str) {
|
|
|
109
109
|
|
|
110
110
|
i += 1;
|
|
111
111
|
c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
|
|
112
|
-
out +=
|
|
112
|
+
out += hexTable[0xF0 | (c >> 18)] + hexTable[0x80 | ((c >> 12) & 0x3F)] + hexTable[0x80 | ((c >> 6) & 0x3F)] + hexTable[0x80 | (c & 0x3F)];
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
return out;
|
|
@@ -132,7 +132,9 @@ exports.compact = function (obj, references) {
|
|
|
132
132
|
var compacted = [];
|
|
133
133
|
|
|
134
134
|
for (var i = 0; i < obj.length; ++i) {
|
|
135
|
-
if (typeof obj[i]
|
|
135
|
+
if (obj[i] && typeof obj[i] === 'object') {
|
|
136
|
+
compacted.push(exports.compact(obj[i], refs));
|
|
137
|
+
} else if (typeof obj[i] !== 'undefined') {
|
|
136
138
|
compacted.push(obj[i]);
|
|
137
139
|
}
|
|
138
140
|
}
|
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
|
|
5
|
+
"version": "6.2.1",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/ljharb/qs.git"
|
|
@@ -24,17 +24,20 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"browserify": "^
|
|
28
|
-
"tape": "^4.
|
|
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.
|
|
32
|
-
"@ljharb/eslint-config": "^
|
|
31
|
+
"eslint": "^3.1.0",
|
|
32
|
+
"@ljharb/eslint-config": "^6.0.0",
|
|
33
33
|
"parallelshell": "^2.0.0",
|
|
34
|
-
"
|
|
34
|
+
"iconv-lite": "^0.4.13",
|
|
35
|
+
"qs-iconv": "^1.0.3",
|
|
36
|
+
"evalmd": "^0.0.17"
|
|
35
37
|
},
|
|
36
38
|
"scripts": {
|
|
37
|
-
"
|
|
39
|
+
"pretest": "npm run --silent readme && npm run --silent lint",
|
|
40
|
+
"test": "npm run --silent coverage",
|
|
38
41
|
"tests-only": "node test",
|
|
39
42
|
"readme": "evalmd README.md",
|
|
40
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) {
|
|
@@ -120,8 +121,11 @@ test('parse()', function (t) {
|
|
|
120
121
|
st.deepEqual(qs.parse('foo[]=bar&foo[bad]=baz'), { foo: { '0': 'bar', bad: 'baz' } });
|
|
121
122
|
st.deepEqual(qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo'), { foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
|
|
122
123
|
st.deepEqual(qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb'), { foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
|
|
123
|
-
|
|
124
|
-
st.deepEqual(qs.parse('a[]=b&a[
|
|
124
|
+
|
|
125
|
+
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: false }), { a: { '0': 'b', c: true, t: 'u' } });
|
|
126
|
+
st.deepEqual(qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c', { allowPrototypes: true }), { a: { '0': 'b', t: 'u', hasOwnProperty: 'c' } });
|
|
127
|
+
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: false }), { a: { '0': 'b', '1': 'c', x: 'y' } });
|
|
128
|
+
st.deepEqual(qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y', { allowPrototypes: true }), { a: { '0': 'b', hasOwnProperty: 'c', x: 'y' } });
|
|
125
129
|
st.end();
|
|
126
130
|
});
|
|
127
131
|
|
|
@@ -173,14 +177,42 @@ test('parse()', function (t) {
|
|
|
173
177
|
|
|
174
178
|
t.test('allows for empty strings in arrays', function (st) {
|
|
175
179
|
st.deepEqual(qs.parse('a[]=b&a[]=&a[]=c'), { a: ['b', '', 'c'] });
|
|
176
|
-
|
|
177
|
-
st.deepEqual(
|
|
178
|
-
|
|
180
|
+
|
|
181
|
+
st.deepEqual(
|
|
182
|
+
qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true, arrayLimit: 20 }),
|
|
183
|
+
{ a: ['b', null, 'c', ''] },
|
|
184
|
+
'with arrayLimit 20 + array indices: null then empty string works'
|
|
185
|
+
);
|
|
186
|
+
st.deepEqual(
|
|
187
|
+
qs.parse('a[]=b&a[]&a[]=c&a[]=', { strictNullHandling: true, arrayLimit: 0 }),
|
|
188
|
+
{ a: ['b', null, 'c', ''] },
|
|
189
|
+
'with arrayLimit 0 + array brackets: null then empty string works'
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
st.deepEqual(
|
|
193
|
+
qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true, arrayLimit: 20 }),
|
|
194
|
+
{ a: ['b', '', 'c', null] },
|
|
195
|
+
'with arrayLimit 20 + array indices: empty string then null works'
|
|
196
|
+
);
|
|
197
|
+
st.deepEqual(
|
|
198
|
+
qs.parse('a[]=b&a[]=&a[]=c&a[]', { strictNullHandling: true, arrayLimit: 0 }),
|
|
199
|
+
{ a: ['b', '', 'c', null] },
|
|
200
|
+
'with arrayLimit 0 + array brackets: empty string then null works'
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
st.deepEqual(
|
|
204
|
+
qs.parse('a[]=&a[]=b&a[]=c'),
|
|
205
|
+
{ a: ['', 'b', 'c'] },
|
|
206
|
+
'array brackets: empty strings work'
|
|
207
|
+
);
|
|
179
208
|
st.end();
|
|
180
209
|
});
|
|
181
210
|
|
|
182
211
|
t.test('compacts sparse arrays', function (st) {
|
|
183
212
|
st.deepEqual(qs.parse('a[10]=1&a[2]=2'), { a: ['2', '1'] });
|
|
213
|
+
st.deepEqual(qs.parse('a[1][b][2][c]=1'), { a: [{ b: [{ c: '1' }] }] });
|
|
214
|
+
st.deepEqual(qs.parse('a[1][2][3][c]=1'), { a: [[[{ c: '1' }]]] });
|
|
215
|
+
st.deepEqual(qs.parse('a[1][2][3][c][1]=1'), { a: [[[{ c: ['1'] }]]] });
|
|
184
216
|
st.end();
|
|
185
217
|
});
|
|
186
218
|
|
|
@@ -390,4 +422,30 @@ test('parse()', function (t) {
|
|
|
390
422
|
st.deepEqual(qs.parse('a[]=b&a[c]=d', { plainObjects: true }), expectedArray);
|
|
391
423
|
st.end();
|
|
392
424
|
});
|
|
425
|
+
|
|
426
|
+
t.test('can parse with custom encoding', function (st) {
|
|
427
|
+
st.deepEqual(qs.parse('%8c%a7=%91%e5%8d%e3%95%7b', {
|
|
428
|
+
decoder: function (str) {
|
|
429
|
+
var reg = /\%([0-9A-F]{2})/ig;
|
|
430
|
+
var result = [];
|
|
431
|
+
var parts;
|
|
432
|
+
var last = 0;
|
|
433
|
+
while (parts = reg.exec(str)) {
|
|
434
|
+
result.push(parseInt(parts[1], 16));
|
|
435
|
+
last = parts.index + parts[0].length;
|
|
436
|
+
}
|
|
437
|
+
return iconv.decode(new Buffer(result), 'shift_jis').toString();
|
|
438
|
+
}
|
|
439
|
+
}), { 県: '大阪府' });
|
|
440
|
+
st.end();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
t.test('throws error with wrong decoder', function (st) {
|
|
444
|
+
st.throws(function () {
|
|
445
|
+
qs.parse({}, {
|
|
446
|
+
decoder: 'string'
|
|
447
|
+
});
|
|
448
|
+
}, new TypeError('Decoder has to be a function.'));
|
|
449
|
+
st.end();
|
|
450
|
+
});
|
|
393
451
|
});
|
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
package/.travis.yml
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
node_js:
|
|
3
|
-
- "5.3"
|
|
4
|
-
- "5.2"
|
|
5
|
-
- "5.1"
|
|
6
|
-
- "5.0"
|
|
7
|
-
- "4.2"
|
|
8
|
-
- "4.1"
|
|
9
|
-
- "4.0"
|
|
10
|
-
- "iojs-v3.3"
|
|
11
|
-
- "iojs-v3.2"
|
|
12
|
-
- "iojs-v3.1"
|
|
13
|
-
- "iojs-v3.0"
|
|
14
|
-
- "iojs-v2.5"
|
|
15
|
-
- "iojs-v2.4"
|
|
16
|
-
- "iojs-v2.3"
|
|
17
|
-
- "iojs-v2.2"
|
|
18
|
-
- "iojs-v2.1"
|
|
19
|
-
- "iojs-v2.0"
|
|
20
|
-
- "iojs-v1.8"
|
|
21
|
-
- "iojs-v1.7"
|
|
22
|
-
- "iojs-v1.6"
|
|
23
|
-
- "iojs-v1.5"
|
|
24
|
-
- "iojs-v1.4"
|
|
25
|
-
- "iojs-v1.3"
|
|
26
|
-
- "iojs-v1.2"
|
|
27
|
-
- "iojs-v1.1"
|
|
28
|
-
- "iojs-v1.0"
|
|
29
|
-
- "0.12"
|
|
30
|
-
- "0.11"
|
|
31
|
-
- "0.10"
|
|
32
|
-
- "0.9"
|
|
33
|
-
- "0.8"
|
|
34
|
-
- "0.6"
|
|
35
|
-
- "0.4"
|
|
36
|
-
before_install:
|
|
37
|
-
- 'if [ "${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'
|
|
38
|
-
- 'if [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then npm install -g npm; fi'
|
|
39
|
-
script:
|
|
40
|
-
- 'if [ "${TRAVIS_NODE_VERSION}" != "4.2" ]; then npm run tests-only ; else npm test ; fi'
|
|
41
|
-
sudo: false
|
|
42
|
-
matrix:
|
|
43
|
-
fast_finish: true
|
|
44
|
-
allow_failures:
|
|
45
|
-
- node_js: "5.2"
|
|
46
|
-
- node_js: "5.1"
|
|
47
|
-
- node_js: "5.0"
|
|
48
|
-
- node_js: "4.1"
|
|
49
|
-
- node_js: "4.0"
|
|
50
|
-
- node_js: "iojs-v3.2"
|
|
51
|
-
- node_js: "iojs-v3.1"
|
|
52
|
-
- node_js: "iojs-v3.0"
|
|
53
|
-
- node_js: "iojs-v2.4"
|
|
54
|
-
- node_js: "iojs-v2.3"
|
|
55
|
-
- node_js: "iojs-v2.2"
|
|
56
|
-
- node_js: "iojs-v2.1"
|
|
57
|
-
- node_js: "iojs-v2.0"
|
|
58
|
-
- node_js: "iojs-v1.7"
|
|
59
|
-
- node_js: "iojs-v1.6"
|
|
60
|
-
- node_js: "iojs-v1.5"
|
|
61
|
-
- node_js: "iojs-v1.4"
|
|
62
|
-
- node_js: "iojs-v1.3"
|
|
63
|
-
- node_js: "iojs-v1.2"
|
|
64
|
-
- node_js: "iojs-v1.1"
|
|
65
|
-
- node_js: "iojs-v1.0"
|
|
66
|
-
- node_js: "0.11"
|
|
67
|
-
- node_js: "0.9"
|
|
68
|
-
- node_js: "0.6"
|
|
69
|
-
- node_js: "0.4"
|
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.0",
|
|
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
|
-
}
|