qs 2.4.1 → 4.0.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/.eslintignore +1 -0
- package/.npmignore +1 -0
- package/CHANGELOG.md +27 -7
- package/LICENSE +0 -0
- package/README.md +86 -2
- package/bower.json +22 -0
- package/lib/index.js +0 -0
- package/lib/parse.js +50 -25
- package/lib/stringify.js +33 -9
- package/lib/utils.js +67 -9
- package/package.json +7 -9
- package/test/parse.js +89 -24
- package/test/stringify.js +55 -5
- package/test/utils.js +28 -0
- package/.jshintignore +0 -1
- package/.jshintrc +0 -10
- package/Makefile +0 -8
- package/index.js +0 -1
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dist
|
package/.npmignore
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
|
|
2
|
-
## [**
|
|
3
|
-
- [**#
|
|
2
|
+
## [**3.1.0**](https://github.com/hapijs/qs/issues?milestone=24&state=open)
|
|
3
|
+
- [**#89**](https://github.com/hapijs/qs/issues/89) Add option to disable "Transform dot notation to bracket notation"
|
|
4
|
+
|
|
5
|
+
## [**3.0.0**](https://github.com/hapijs/qs/issues?milestone=23&state=closed)
|
|
6
|
+
- [**#77**](https://github.com/hapijs/qs/issues/77) Perf boost
|
|
7
|
+
- [**#60**](https://github.com/hapijs/qs/issues/60) Add explicit option to disable array parsing
|
|
8
|
+
- [**#80**](https://github.com/hapijs/qs/issues/80) qs.parse silently drops properties
|
|
9
|
+
- [**#74**](https://github.com/hapijs/qs/issues/74) Bad parse when turning array into object
|
|
10
|
+
- [**#81**](https://github.com/hapijs/qs/issues/81) Add a `filter` option
|
|
11
|
+
- [**#68**](https://github.com/hapijs/qs/issues/68) Fixed issue with recursion and passing strings into objects.
|
|
12
|
+
- [**#66**](https://github.com/hapijs/qs/issues/66) Add mixed array and object dot notation support Closes: #47
|
|
13
|
+
- [**#76**](https://github.com/hapijs/qs/issues/76) RFC 3986
|
|
14
|
+
- [**#85**](https://github.com/hapijs/qs/issues/85) No equal sign
|
|
15
|
+
- [**#84**](https://github.com/hapijs/qs/issues/84) update license attribute
|
|
16
|
+
|
|
17
|
+
## [**2.4.1**](https://github.com/hapijs/qs/issues?milestone=20&state=closed)
|
|
18
|
+
- [**#73**](https://github.com/hapijs/qs/issues/73) Property 'hasOwnProperty' of object #<Object> is not a function
|
|
19
|
+
|
|
20
|
+
## [**2.4.0**](https://github.com/hapijs/qs/issues?milestone=19&state=closed)
|
|
21
|
+
- [**#70**](https://github.com/hapijs/qs/issues/70) Add arrayFormat option
|
|
22
|
+
|
|
23
|
+
## [**2.3.3**](https://github.com/hapijs/qs/issues?milestone=18&state=closed)
|
|
24
|
+
- [**#59**](https://github.com/hapijs/qs/issues/59) make sure array indexes are >= 0, closes #57
|
|
4
25
|
- [**#58**](https://github.com/hapijs/qs/issues/58) make qs usable for browser loader
|
|
5
26
|
|
|
6
27
|
## [**2.3.2**](https://github.com/hapijs/qs/issues?milestone=17&state=closed)
|
|
7
28
|
- [**#55**](https://github.com/hapijs/qs/issues/55) allow merging a string into an object
|
|
8
29
|
|
|
9
30
|
## [**2.3.1**](https://github.com/hapijs/qs/issues?milestone=16&state=closed)
|
|
10
|
-
- [**#52**](https://github.com/hapijs/qs/issues/52) Return
|
|
31
|
+
- [**#52**](https://github.com/hapijs/qs/issues/52) Return "undefined" and "false" instead of throwing "TypeError".
|
|
11
32
|
|
|
12
33
|
## [**2.3.0**](https://github.com/hapijs/qs/issues?milestone=15&state=closed)
|
|
13
34
|
- [**#50**](https://github.com/hapijs/qs/issues/50) add option to omit array indices, closes #46
|
|
@@ -34,9 +55,9 @@
|
|
|
34
55
|
- [**#31**](https://github.com/hapijs/qs/issues/31) qs.parse stackoverflow on circular objects
|
|
35
56
|
|
|
36
57
|
## [**2.2.0**](https://github.com/hapijs/qs/issues?milestone=9&state=closed)
|
|
37
|
-
- [**#26**](https://github.com/hapijs/qs/issues/26) Don
|
|
58
|
+
- [**#26**](https://github.com/hapijs/qs/issues/26) Don't use Buffer global if it's not present
|
|
38
59
|
- [**#30**](https://github.com/hapijs/qs/issues/30) Bug when merging non-object values into arrays
|
|
39
|
-
- [**#29**](https://github.com/hapijs/qs/issues/29) Don
|
|
60
|
+
- [**#29**](https://github.com/hapijs/qs/issues/29) Don't call Utils.clone at the top of Utils.merge
|
|
40
61
|
- [**#23**](https://github.com/hapijs/qs/issues/23) Ability to not limit parameters?
|
|
41
62
|
|
|
42
63
|
## [**2.1.0**](https://github.com/hapijs/qs/issues?milestone=8&state=closed)
|
|
@@ -48,7 +69,7 @@
|
|
|
48
69
|
- [**#21**](https://github.com/hapijs/qs/issues/21) make all limits optional, for #18, for #20
|
|
49
70
|
|
|
50
71
|
## [**1.2.2**](https://github.com/hapijs/qs/issues?milestone=6&state=closed)
|
|
51
|
-
- [**#19**](https://github.com/hapijs/qs/issues/19) Don
|
|
72
|
+
- [**#19**](https://github.com/hapijs/qs/issues/19) Don't overwrite null values
|
|
52
73
|
|
|
53
74
|
## [**1.2.1**](https://github.com/hapijs/qs/issues?milestone=5&state=closed)
|
|
54
75
|
- [**#16**](https://github.com/hapijs/qs/issues/16) ignore non-string delimiters
|
|
@@ -65,4 +86,3 @@
|
|
|
65
86
|
|
|
66
87
|
## [**1.0.2**](https://github.com/hapijs/qs/issues?milestone=2&state=closed)
|
|
67
88
|
- [**#5**](https://github.com/hapijs/qs/issues/5) array holes incorrectly copied into object on large index
|
|
68
|
-
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ var str = Qs.stringify(obj); // 'a=c'
|
|
|
23
23
|
Qs.parse(string, [options]);
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]
|
|
26
|
+
**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`, or prefixing the sub-key with a dot `.`.
|
|
27
27
|
For example, the string `'foo[bar]=baz'` converts to:
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
@@ -34,6 +34,20 @@ For example, the string `'foo[bar]=baz'` converts to:
|
|
|
34
34
|
}
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
When using the `plainObjects` option the parsed value is returned as a plain object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
Qs.parse('a.hasOwnProperty=b', { plainObjects: true });
|
|
41
|
+
// { a: { hasOwnProperty: 'b' } }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
Qs.parse('a.hasOwnProperty=b', { allowPrototypes: true });
|
|
48
|
+
// { a: { hasOwnProperty: 'b' } }
|
|
49
|
+
```
|
|
50
|
+
|
|
37
51
|
URI encoded strings work too:
|
|
38
52
|
|
|
39
53
|
```javascript
|
|
@@ -104,6 +118,13 @@ Qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
|
|
|
104
118
|
// { a: 'b', c: 'd', e: 'f' }
|
|
105
119
|
```
|
|
106
120
|
|
|
121
|
+
Option `allowDots` can be used to disable dot notation:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
Qs.parse('a.b=c', { allowDots: false });
|
|
125
|
+
// { 'a.b': 'c' } }
|
|
126
|
+
```
|
|
127
|
+
|
|
107
128
|
### Parsing Arrays
|
|
108
129
|
|
|
109
130
|
**qs** can also parse arrays using a similar `[]` notation:
|
|
@@ -153,7 +174,12 @@ Qs.parse('a[1]=b', { arrayLimit: 0 });
|
|
|
153
174
|
// { a: { '1': 'b' } }
|
|
154
175
|
```
|
|
155
176
|
|
|
156
|
-
To disable array parsing entirely, set `
|
|
177
|
+
To disable array parsing entirely, set `parseArrays` to `false`.
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
Qs.parse('a[]=b', { parseArrays: false });
|
|
181
|
+
// { a: { '0': 'b' } }
|
|
182
|
+
```
|
|
157
183
|
|
|
158
184
|
If you mix notations, **qs** will merge the two items into an object:
|
|
159
185
|
|
|
@@ -231,3 +257,61 @@ The delimiter may be overridden with stringify as well:
|
|
|
231
257
|
Qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' });
|
|
232
258
|
// 'a=b;c=d'
|
|
233
259
|
```
|
|
260
|
+
|
|
261
|
+
Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
|
|
262
|
+
If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
|
|
263
|
+
pass an array, it will be used to select properties and array indices for stringification:
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
function filterFunc(prefix, value) {
|
|
267
|
+
if (prefix == 'b') {
|
|
268
|
+
// Return an `undefined` value to omit a property.
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (prefix == 'e[f]') {
|
|
272
|
+
return value.getTime();
|
|
273
|
+
}
|
|
274
|
+
if (prefix == 'e[g][0]') {
|
|
275
|
+
return value * 2;
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
Qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc })
|
|
280
|
+
// 'a=b&c=d&e[f]=123&e[g][0]=4'
|
|
281
|
+
Qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] })
|
|
282
|
+
// 'a=b&e=f'
|
|
283
|
+
Qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] })
|
|
284
|
+
// 'a[0]=b&a[2]=d'
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Handling of `null` values
|
|
288
|
+
|
|
289
|
+
By default, `null` values are treated like empty strings:
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
Qs.stringify({ a: null, b: '' });
|
|
293
|
+
// 'a=&b='
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
Qs.parse('a&b=')
|
|
300
|
+
// { a: '', b: '' }
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
|
|
304
|
+
values have no `=` sign:
|
|
305
|
+
|
|
306
|
+
```javascript
|
|
307
|
+
Qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
|
|
308
|
+
// 'a&b='
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
To parse values without `=` back to `null` use the `strictNullHandling` flag:
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
Qs.parse('a&b=', { strictNullHandling: true });
|
|
315
|
+
// { a: null, b: '' }
|
|
316
|
+
|
|
317
|
+
```
|
package/bower.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "qs",
|
|
3
|
+
"main": "dist/qs.js",
|
|
4
|
+
"version": "3.0.0",
|
|
5
|
+
"homepage": "https://github.com/hapijs/qs",
|
|
6
|
+
"authors": [
|
|
7
|
+
"Nathan LaFreniere <quitlahok@gmail.com>"
|
|
8
|
+
],
|
|
9
|
+
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"querystring",
|
|
12
|
+
"qs"
|
|
13
|
+
],
|
|
14
|
+
"license": "BSD-3-Clause",
|
|
15
|
+
"ignore": [
|
|
16
|
+
"**/.*",
|
|
17
|
+
"node_modules",
|
|
18
|
+
"bower_components",
|
|
19
|
+
"test",
|
|
20
|
+
"tests"
|
|
21
|
+
]
|
|
22
|
+
}
|
package/lib/index.js
CHANGED
|
File without changes
|
package/lib/parse.js
CHANGED
|
@@ -9,7 +9,10 @@ var internals = {
|
|
|
9
9
|
delimiter: '&',
|
|
10
10
|
depth: 5,
|
|
11
11
|
arrayLimit: 20,
|
|
12
|
-
parameterLimit: 1000
|
|
12
|
+
parameterLimit: 1000,
|
|
13
|
+
strictNullHandling: false,
|
|
14
|
+
plainObjects: false,
|
|
15
|
+
allowPrototypes: false
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
|
|
@@ -24,16 +27,16 @@ internals.parseValues = function (str, options) {
|
|
|
24
27
|
|
|
25
28
|
if (pos === -1) {
|
|
26
29
|
obj[Utils.decode(part)] = '';
|
|
30
|
+
|
|
31
|
+
if (options.strictNullHandling) {
|
|
32
|
+
obj[Utils.decode(part)] = null;
|
|
33
|
+
}
|
|
27
34
|
}
|
|
28
35
|
else {
|
|
29
36
|
var key = Utils.decode(part.slice(0, pos));
|
|
30
37
|
var val = Utils.decode(part.slice(pos + 1));
|
|
31
38
|
|
|
32
|
-
if (Object.prototype.hasOwnProperty(key)) {
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (!obj.hasOwnProperty(key)) {
|
|
39
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
37
40
|
obj[key] = val;
|
|
38
41
|
}
|
|
39
42
|
else {
|
|
@@ -54,12 +57,13 @@ internals.parseObject = function (chain, val, options) {
|
|
|
54
57
|
|
|
55
58
|
var root = chain.shift();
|
|
56
59
|
|
|
57
|
-
var obj
|
|
60
|
+
var obj;
|
|
58
61
|
if (root === '[]') {
|
|
59
62
|
obj = [];
|
|
60
63
|
obj = obj.concat(internals.parseObject(chain, val, options));
|
|
61
64
|
}
|
|
62
65
|
else {
|
|
66
|
+
obj = options.plainObjects ? Object.create(null) : {};
|
|
63
67
|
var cleanRoot = root[0] === '[' && root[root.length - 1] === ']' ? root.slice(1, root.length - 1) : root;
|
|
64
68
|
var index = parseInt(cleanRoot, 10);
|
|
65
69
|
var indexString = '' + index;
|
|
@@ -67,7 +71,8 @@ internals.parseObject = function (chain, val, options) {
|
|
|
67
71
|
root !== cleanRoot &&
|
|
68
72
|
indexString === cleanRoot &&
|
|
69
73
|
index >= 0 &&
|
|
70
|
-
|
|
74
|
+
(options.parseArrays &&
|
|
75
|
+
index <= options.arrayLimit)) {
|
|
71
76
|
|
|
72
77
|
obj = [];
|
|
73
78
|
obj[index] = internals.parseObject(chain, val, options);
|
|
@@ -87,6 +92,12 @@ internals.parseKeys = function (key, val, options) {
|
|
|
87
92
|
return;
|
|
88
93
|
}
|
|
89
94
|
|
|
95
|
+
// Transform dot notation to bracket notation
|
|
96
|
+
|
|
97
|
+
if (options.allowDots) {
|
|
98
|
+
key = key.replace(/\.([^\.\[]+)/g, '[$1]');
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
// The regex chunks
|
|
91
102
|
|
|
92
103
|
var parent = /^([^\[\]]*)/;
|
|
@@ -96,16 +107,20 @@ internals.parseKeys = function (key, val, options) {
|
|
|
96
107
|
|
|
97
108
|
var segment = parent.exec(key);
|
|
98
109
|
|
|
99
|
-
// Don't allow them to overwrite object prototype properties
|
|
100
|
-
|
|
101
|
-
if (Object.prototype.hasOwnProperty(segment[1])) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
110
|
// Stash the parent if it exists
|
|
106
111
|
|
|
107
112
|
var keys = [];
|
|
108
113
|
if (segment[1]) {
|
|
114
|
+
// If we aren't using plain objects, optionally prefix keys
|
|
115
|
+
// that would overwrite object prototype properties
|
|
116
|
+
if (!options.plainObjects &&
|
|
117
|
+
Object.prototype.hasOwnProperty(segment[1])) {
|
|
118
|
+
|
|
119
|
+
if (!options.allowPrototypes) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
109
124
|
keys.push(segment[1]);
|
|
110
125
|
}
|
|
111
126
|
|
|
@@ -115,9 +130,14 @@ internals.parseKeys = function (key, val, options) {
|
|
|
115
130
|
while ((segment = child.exec(key)) !== null && i < options.depth) {
|
|
116
131
|
|
|
117
132
|
++i;
|
|
118
|
-
if (!
|
|
119
|
-
|
|
133
|
+
if (!options.plainObjects &&
|
|
134
|
+
Object.prototype.hasOwnProperty(segment[1].replace(/\[|\]/g, ''))) {
|
|
135
|
+
|
|
136
|
+
if (!options.allowPrototypes) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
120
139
|
}
|
|
140
|
+
keys.push(segment[1]);
|
|
121
141
|
}
|
|
122
142
|
|
|
123
143
|
// If there's a remainder, just add whatever is left
|
|
@@ -132,21 +152,26 @@ internals.parseKeys = function (key, val, options) {
|
|
|
132
152
|
|
|
133
153
|
module.exports = function (str, options) {
|
|
134
154
|
|
|
135
|
-
if (str === '' ||
|
|
136
|
-
str === null ||
|
|
137
|
-
typeof str === 'undefined') {
|
|
138
|
-
|
|
139
|
-
return {};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
155
|
options = options || {};
|
|
143
156
|
options.delimiter = typeof options.delimiter === 'string' || Utils.isRegExp(options.delimiter) ? options.delimiter : internals.delimiter;
|
|
144
157
|
options.depth = typeof options.depth === 'number' ? options.depth : internals.depth;
|
|
145
158
|
options.arrayLimit = typeof options.arrayLimit === 'number' ? options.arrayLimit : internals.arrayLimit;
|
|
159
|
+
options.parseArrays = options.parseArrays !== false;
|
|
160
|
+
options.allowDots = options.allowDots !== false;
|
|
161
|
+
options.plainObjects = typeof options.plainObjects === 'boolean' ? options.plainObjects : internals.plainObjects;
|
|
162
|
+
options.allowPrototypes = typeof options.allowPrototypes === 'boolean' ? options.allowPrototypes : internals.allowPrototypes;
|
|
146
163
|
options.parameterLimit = typeof options.parameterLimit === 'number' ? options.parameterLimit : internals.parameterLimit;
|
|
164
|
+
options.strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
|
165
|
+
|
|
166
|
+
if (str === '' ||
|
|
167
|
+
str === null ||
|
|
168
|
+
typeof str === 'undefined') {
|
|
169
|
+
|
|
170
|
+
return options.plainObjects ? Object.create(null) : {};
|
|
171
|
+
}
|
|
147
172
|
|
|
148
173
|
var tempObj = typeof str === 'string' ? internals.parseValues(str, options) : str;
|
|
149
|
-
var obj = {};
|
|
174
|
+
var obj = options.plainObjects ? Object.create(null) : {};
|
|
150
175
|
|
|
151
176
|
// Iterate over the keys and setup the new object
|
|
152
177
|
|
|
@@ -154,7 +179,7 @@ module.exports = function (str, options) {
|
|
|
154
179
|
for (var i = 0, il = keys.length; i < il; ++i) {
|
|
155
180
|
var key = keys[i];
|
|
156
181
|
var newObj = internals.parseKeys(key, tempObj[key], options);
|
|
157
|
-
obj = Utils.merge(obj, newObj);
|
|
182
|
+
obj = Utils.merge(obj, newObj, options);
|
|
158
183
|
}
|
|
159
184
|
|
|
160
185
|
return Utils.compact(obj);
|
package/lib/stringify.js
CHANGED
|
@@ -9,27 +9,38 @@ var internals = {
|
|
|
9
9
|
delimiter: '&',
|
|
10
10
|
arrayPrefixGenerators: {
|
|
11
11
|
brackets: function (prefix, key) {
|
|
12
|
+
|
|
12
13
|
return prefix + '[]';
|
|
13
14
|
},
|
|
14
15
|
indices: function (prefix, key) {
|
|
16
|
+
|
|
15
17
|
return prefix + '[' + key + ']';
|
|
16
18
|
},
|
|
17
19
|
repeat: function (prefix, key) {
|
|
20
|
+
|
|
18
21
|
return prefix;
|
|
19
22
|
}
|
|
20
|
-
}
|
|
23
|
+
},
|
|
24
|
+
strictNullHandling: false
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
|
|
24
|
-
internals.stringify = function (obj, prefix, generateArrayPrefix) {
|
|
28
|
+
internals.stringify = function (obj, prefix, generateArrayPrefix, strictNullHandling, filter) {
|
|
25
29
|
|
|
26
|
-
if (
|
|
30
|
+
if (typeof filter === 'function') {
|
|
31
|
+
obj = filter(prefix, obj);
|
|
32
|
+
}
|
|
33
|
+
else if (Utils.isBuffer(obj)) {
|
|
27
34
|
obj = obj.toString();
|
|
28
35
|
}
|
|
29
36
|
else if (obj instanceof Date) {
|
|
30
37
|
obj = obj.toISOString();
|
|
31
38
|
}
|
|
32
39
|
else if (obj === null) {
|
|
40
|
+
if (strictNullHandling) {
|
|
41
|
+
return Utils.encode(prefix);
|
|
42
|
+
}
|
|
43
|
+
|
|
33
44
|
obj = '';
|
|
34
45
|
}
|
|
35
46
|
|
|
@@ -37,7 +48,7 @@ internals.stringify = function (obj, prefix, generateArrayPrefix) {
|
|
|
37
48
|
typeof obj === 'number' ||
|
|
38
49
|
typeof obj === 'boolean') {
|
|
39
50
|
|
|
40
|
-
return [
|
|
51
|
+
return [Utils.encode(prefix) + '=' + Utils.encode(obj)];
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
var values = [];
|
|
@@ -46,14 +57,15 @@ internals.stringify = function (obj, prefix, generateArrayPrefix) {
|
|
|
46
57
|
return values;
|
|
47
58
|
}
|
|
48
59
|
|
|
49
|
-
var objKeys = Object.keys(obj);
|
|
60
|
+
var objKeys = Array.isArray(filter) ? filter : Object.keys(obj);
|
|
50
61
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
|
51
62
|
var key = objKeys[i];
|
|
63
|
+
|
|
52
64
|
if (Array.isArray(obj)) {
|
|
53
|
-
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix));
|
|
65
|
+
values = values.concat(internals.stringify(obj[key], generateArrayPrefix(prefix, key), generateArrayPrefix, strictNullHandling, filter));
|
|
54
66
|
}
|
|
55
67
|
else {
|
|
56
|
-
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix));
|
|
68
|
+
values = values.concat(internals.stringify(obj[key], prefix + '[' + key + ']', generateArrayPrefix, strictNullHandling, filter));
|
|
57
69
|
}
|
|
58
70
|
}
|
|
59
71
|
|
|
@@ -65,6 +77,16 @@ module.exports = function (obj, options) {
|
|
|
65
77
|
|
|
66
78
|
options = options || {};
|
|
67
79
|
var delimiter = typeof options.delimiter === 'undefined' ? internals.delimiter : options.delimiter;
|
|
80
|
+
var strictNullHandling = typeof options.strictNullHandling === 'boolean' ? options.strictNullHandling : internals.strictNullHandling;
|
|
81
|
+
var objKeys;
|
|
82
|
+
var filter;
|
|
83
|
+
if (typeof options.filter === 'function') {
|
|
84
|
+
filter = options.filter;
|
|
85
|
+
obj = filter('', obj);
|
|
86
|
+
}
|
|
87
|
+
else if (Array.isArray(options.filter)) {
|
|
88
|
+
objKeys = filter = options.filter;
|
|
89
|
+
}
|
|
68
90
|
|
|
69
91
|
var keys = [];
|
|
70
92
|
|
|
@@ -87,10 +109,12 @@ module.exports = function (obj, options) {
|
|
|
87
109
|
|
|
88
110
|
var generateArrayPrefix = internals.arrayPrefixGenerators[arrayFormat];
|
|
89
111
|
|
|
90
|
-
|
|
112
|
+
if (!objKeys) {
|
|
113
|
+
objKeys = Object.keys(obj);
|
|
114
|
+
}
|
|
91
115
|
for (var i = 0, il = objKeys.length; i < il; ++i) {
|
|
92
116
|
var key = objKeys[i];
|
|
93
|
-
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix));
|
|
117
|
+
keys = keys.concat(internals.stringify(obj[key], key, generateArrayPrefix, strictNullHandling, filter));
|
|
94
118
|
}
|
|
95
119
|
|
|
96
120
|
return keys.join(delimiter);
|
package/lib/utils.js
CHANGED
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
// Declare internals
|
|
5
5
|
|
|
6
6
|
var internals = {};
|
|
7
|
+
internals.hexTable = new Array(256);
|
|
8
|
+
for (var h = 0; h < 256; ++h) {
|
|
9
|
+
internals.hexTable[h] = '%' + ((h < 16 ? '0' : '') + h.toString(16)).toUpperCase();
|
|
10
|
+
}
|
|
7
11
|
|
|
8
12
|
|
|
9
|
-
exports.arrayToObject = function (source) {
|
|
13
|
+
exports.arrayToObject = function (source, options) {
|
|
10
14
|
|
|
11
|
-
var obj = {};
|
|
15
|
+
var obj = options.plainObjects ? Object.create(null) : {};
|
|
12
16
|
for (var i = 0, il = source.length; i < il; ++i) {
|
|
13
17
|
if (typeof source[i] !== 'undefined') {
|
|
14
18
|
|
|
@@ -20,7 +24,7 @@ exports.arrayToObject = function (source) {
|
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
|
|
23
|
-
exports.merge = function (target, source) {
|
|
27
|
+
exports.merge = function (target, source, options) {
|
|
24
28
|
|
|
25
29
|
if (!source) {
|
|
26
30
|
return target;
|
|
@@ -30,9 +34,12 @@ exports.merge = function (target, source) {
|
|
|
30
34
|
if (Array.isArray(target)) {
|
|
31
35
|
target.push(source);
|
|
32
36
|
}
|
|
33
|
-
else {
|
|
37
|
+
else if (typeof target === 'object') {
|
|
34
38
|
target[source] = true;
|
|
35
39
|
}
|
|
40
|
+
else {
|
|
41
|
+
target = [target, source];
|
|
42
|
+
}
|
|
36
43
|
|
|
37
44
|
return target;
|
|
38
45
|
}
|
|
@@ -45,7 +52,7 @@ exports.merge = function (target, source) {
|
|
|
45
52
|
if (Array.isArray(target) &&
|
|
46
53
|
!Array.isArray(source)) {
|
|
47
54
|
|
|
48
|
-
target = exports.arrayToObject(target);
|
|
55
|
+
target = exports.arrayToObject(target, options);
|
|
49
56
|
}
|
|
50
57
|
|
|
51
58
|
var keys = Object.keys(source);
|
|
@@ -53,11 +60,11 @@ exports.merge = function (target, source) {
|
|
|
53
60
|
var key = keys[k];
|
|
54
61
|
var value = source[key];
|
|
55
62
|
|
|
56
|
-
if (!target
|
|
63
|
+
if (!Object.prototype.hasOwnProperty.call(target, key)) {
|
|
57
64
|
target[key] = value;
|
|
58
65
|
}
|
|
59
66
|
else {
|
|
60
|
-
target[key] = exports.merge(target[key], value);
|
|
67
|
+
target[key] = exports.merge(target[key], value, options);
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -74,6 +81,56 @@ exports.decode = function (str) {
|
|
|
74
81
|
}
|
|
75
82
|
};
|
|
76
83
|
|
|
84
|
+
exports.encode = function (str) {
|
|
85
|
+
|
|
86
|
+
// This code was originally written by Brian White (mscdex) for the io.js core querystring library.
|
|
87
|
+
// It has been adapted here for stricter adherence to RFC 3986
|
|
88
|
+
if (str.length === 0) {
|
|
89
|
+
return str;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (typeof str !== 'string') {
|
|
93
|
+
str = '' + str;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
var out = '';
|
|
97
|
+
for (var i = 0, il = str.length; i < il; ++i) {
|
|
98
|
+
var c = str.charCodeAt(i);
|
|
99
|
+
|
|
100
|
+
if (c === 0x2D || // -
|
|
101
|
+
c === 0x2E || // .
|
|
102
|
+
c === 0x5F || // _
|
|
103
|
+
c === 0x7E || // ~
|
|
104
|
+
(c >= 0x30 && c <= 0x39) || // 0-9
|
|
105
|
+
(c >= 0x41 && c <= 0x5A) || // a-z
|
|
106
|
+
(c >= 0x61 && c <= 0x7A)) { // A-Z
|
|
107
|
+
|
|
108
|
+
out += str[i];
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (c < 0x80) {
|
|
113
|
+
out += internals.hexTable[c];
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (c < 0x800) {
|
|
118
|
+
out += internals.hexTable[0xC0 | (c >> 6)] + internals.hexTable[0x80 | (c & 0x3F)];
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (c < 0xD800 || c >= 0xE000) {
|
|
123
|
+
out += internals.hexTable[0xE0 | (c >> 12)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
++i;
|
|
128
|
+
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF));
|
|
129
|
+
out += internals.hexTable[0xF0 | (c >> 18)] + internals.hexTable[0x80 | ((c >> 12) & 0x3F)] + internals.hexTable[0x80 | ((c >> 6) & 0x3F)] + internals.hexTable[0x80 | (c & 0x3F)];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
77
134
|
|
|
78
135
|
exports.compact = function (obj, refs) {
|
|
79
136
|
|
|
@@ -114,6 +171,7 @@ exports.compact = function (obj, refs) {
|
|
|
114
171
|
|
|
115
172
|
|
|
116
173
|
exports.isRegExp = function (obj) {
|
|
174
|
+
|
|
117
175
|
return Object.prototype.toString.call(obj) === '[object RegExp]';
|
|
118
176
|
};
|
|
119
177
|
|
|
@@ -127,6 +185,6 @@ exports.isBuffer = function (obj) {
|
|
|
127
185
|
}
|
|
128
186
|
|
|
129
187
|
return !!(obj.constructor &&
|
|
130
|
-
|
|
131
|
-
|
|
188
|
+
obj.constructor.isBuffer &&
|
|
189
|
+
obj.constructor.isBuffer(obj));
|
|
132
190
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "A querystring parser that supports nesting and arrays, with a depth limit",
|
|
5
5
|
"homepage": "https://github.com/hapijs/qs",
|
|
6
|
-
"main": "index.js",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
7
|
"dependencies": {},
|
|
8
8
|
"devDependencies": {
|
|
9
|
+
"browserify": "^10.2.1",
|
|
9
10
|
"code": "1.x.x",
|
|
10
11
|
"lab": "5.x.x"
|
|
11
12
|
},
|
|
12
13
|
"scripts": {
|
|
13
|
-
"test": "
|
|
14
|
+
"test": "lab -a code -t 100 -L",
|
|
15
|
+
"test-cov-html": "lab -a code -r html -o coverage.html",
|
|
16
|
+
"dist": "browserify --standalone Qs lib/index.js > dist/qs.js"
|
|
14
17
|
},
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|
|
@@ -20,10 +23,5 @@
|
|
|
20
23
|
"querystring",
|
|
21
24
|
"qs"
|
|
22
25
|
],
|
|
23
|
-
"
|
|
24
|
-
{
|
|
25
|
-
"type": "BSD",
|
|
26
|
-
"url": "http://github.com/hapijs/qs/raw/master/LICENSE"
|
|
27
|
-
}
|
|
28
|
-
]
|
|
26
|
+
"license": "BSD-3-Clause"
|
|
29
27
|
}
|
package/test/parse.js
CHANGED
|
@@ -28,11 +28,15 @@ describe('parse()', function () {
|
|
|
28
28
|
expect(Qs.parse('a[>=]=23')).to.deep.equal({ a: { '>=': '23' } });
|
|
29
29
|
expect(Qs.parse('a[<=>]==23')).to.deep.equal({ a: { '<=>': '=23' } });
|
|
30
30
|
expect(Qs.parse('a[==]=23')).to.deep.equal({ a: { '==': '23' } });
|
|
31
|
-
expect(Qs.parse('foo')).to.deep.equal({ foo:
|
|
31
|
+
expect(Qs.parse('foo', { strictNullHandling: true })).to.deep.equal({ foo: null });
|
|
32
|
+
expect(Qs.parse('foo' )).to.deep.equal({ foo: '' });
|
|
33
|
+
expect(Qs.parse('foo=')).to.deep.equal({ foo: '' });
|
|
32
34
|
expect(Qs.parse('foo=bar')).to.deep.equal({ foo: 'bar' });
|
|
33
35
|
expect(Qs.parse(' foo = bar = baz ')).to.deep.equal({ ' foo ': ' bar = baz ' });
|
|
34
36
|
expect(Qs.parse('foo=bar=baz')).to.deep.equal({ foo: 'bar=baz' });
|
|
35
37
|
expect(Qs.parse('foo=bar&bar=baz')).to.deep.equal({ foo: 'bar', bar: 'baz' });
|
|
38
|
+
expect(Qs.parse('foo2=bar2&baz2=')).to.deep.equal({ foo2: 'bar2', baz2: '' });
|
|
39
|
+
expect(Qs.parse('foo=bar&baz', { strictNullHandling: true })).to.deep.equal({ foo: 'bar', baz: null });
|
|
36
40
|
expect(Qs.parse('foo=bar&baz')).to.deep.equal({ foo: 'bar', baz: '' });
|
|
37
41
|
expect(Qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World')).to.deep.equal({
|
|
38
42
|
cht: 'p3',
|
|
@@ -43,6 +47,13 @@ describe('parse()', function () {
|
|
|
43
47
|
done();
|
|
44
48
|
});
|
|
45
49
|
|
|
50
|
+
it('allows disabling dot notation', function (done) {
|
|
51
|
+
|
|
52
|
+
expect(Qs.parse('a.b=c')).to.deep.equal({ a: { b: 'c' } });
|
|
53
|
+
expect(Qs.parse('a.b=c', { allowDots: false })).to.deep.equal({ 'a.b': 'c' });
|
|
54
|
+
done();
|
|
55
|
+
});
|
|
56
|
+
|
|
46
57
|
it('parses a single nested string', function (done) {
|
|
47
58
|
|
|
48
59
|
expect(Qs.parse('a[b]=c')).to.deep.equal({ a: { b: 'c' } });
|
|
@@ -156,7 +167,24 @@ describe('parse()', function () {
|
|
|
156
167
|
expect(Qs.parse('foo[bad]=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
|
157
168
|
expect(Qs.parse('foo[]=bar&foo[bad]=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
|
|
158
169
|
expect(Qs.parse('foo[bad]=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
|
|
159
|
-
expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({foo: [
|
|
170
|
+
expect(Qs.parse('foo[0][a]=a&foo[0][b]=b&foo[1][a]=aa&foo[1][b]=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
|
|
171
|
+
expect(Qs.parse('a[]=b&a[t]=u&a[hasOwnProperty]=c')).to.deep.equal({ a: { '0': 'b', t: 'u', c: true } });
|
|
172
|
+
expect(Qs.parse('a[]=b&a[hasOwnProperty]=c&a[x]=y')).to.deep.equal({ a: { '0': 'b', '1': 'c', x: 'y' } });
|
|
173
|
+
done();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('transforms arrays to objects (dot notation)', function (done) {
|
|
177
|
+
|
|
178
|
+
expect(Qs.parse('foo[0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: 'baz' } });
|
|
179
|
+
expect(Qs.parse('foo[0].baz=bar&fool.bad.boo=baz')).to.deep.equal({ foo: [{ baz: 'bar' }], fool: { bad: { boo: 'baz' } } });
|
|
180
|
+
expect(Qs.parse('foo[0][0].baz=bar&fool.bad=baz')).to.deep.equal({ foo: [[{ baz: 'bar' }]], fool: { bad: 'baz' } });
|
|
181
|
+
expect(Qs.parse('foo[0].baz[0]=15&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15'], bar: '2' }] });
|
|
182
|
+
expect(Qs.parse('foo[0].baz[0]=15&foo[0].baz[1]=16&foo[0].bar=2')).to.deep.equal({ foo: [{ baz: ['15', '16'], bar: '2' }] });
|
|
183
|
+
expect(Qs.parse('foo.bad=baz&foo[0]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
|
184
|
+
expect(Qs.parse('foo.bad=baz&foo[]=bar')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar' } });
|
|
185
|
+
expect(Qs.parse('foo[]=bar&foo.bad=baz')).to.deep.equal({ foo: { '0': 'bar', bad: 'baz' } });
|
|
186
|
+
expect(Qs.parse('foo.bad=baz&foo[]=bar&foo[]=foo')).to.deep.equal({ foo: { bad: 'baz', '0': 'bar', '1': 'foo' } });
|
|
187
|
+
expect(Qs.parse('foo[0].a=a&foo[0].b=b&foo[1].a=aa&foo[1].b=bb')).to.deep.equal({ foo: [{ a: 'a', b: 'b' }, { a: 'aa', b: 'bb' }] });
|
|
160
188
|
done();
|
|
161
189
|
});
|
|
162
190
|
|
|
@@ -174,7 +202,8 @@ describe('parse()', function () {
|
|
|
174
202
|
|
|
175
203
|
it('supports malformed uri characters', function (done) {
|
|
176
204
|
|
|
177
|
-
expect(Qs.parse('{%:%}')).to.deep.equal({ '{%:%}':
|
|
205
|
+
expect(Qs.parse('{%:%}', { strictNullHandling: true })).to.deep.equal({ '{%:%}': null });
|
|
206
|
+
expect(Qs.parse('{%:%}=')).to.deep.equal({ '{%:%}': '' });
|
|
178
207
|
expect(Qs.parse('foo=%:%}')).to.deep.equal({ foo: '%:%}' });
|
|
179
208
|
done();
|
|
180
209
|
});
|
|
@@ -185,15 +214,6 @@ describe('parse()', function () {
|
|
|
185
214
|
done();
|
|
186
215
|
});
|
|
187
216
|
|
|
188
|
-
it('cannot override prototypes', function (done) {
|
|
189
|
-
|
|
190
|
-
var obj = Qs.parse('hasOwnProperty=bad&toString=bad&bad[toString]=bad&constructor=bad');
|
|
191
|
-
expect(typeof obj.toString).to.equal('function');
|
|
192
|
-
expect(typeof obj.bad.toString).to.equal('function');
|
|
193
|
-
expect(typeof obj.constructor).to.equal('function');
|
|
194
|
-
done();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
217
|
it('cannot access Object prototype', function (done) {
|
|
198
218
|
|
|
199
219
|
Qs.parse('constructor[prototype][bad]=bad');
|
|
@@ -212,7 +232,8 @@ describe('parse()', function () {
|
|
|
212
232
|
it('allows for empty strings in arrays', function (done) {
|
|
213
233
|
|
|
214
234
|
expect(Qs.parse('a[]=b&a[]=&a[]=c')).to.deep.equal({ a: ['b', '', 'c'] });
|
|
215
|
-
expect(Qs.parse('a[0]=b&a[1]
|
|
235
|
+
expect(Qs.parse('a[0]=b&a[1]&a[2]=c&a[19]=', { strictNullHandling: true })).to.deep.equal({ a: ['b', null, 'c', ''] });
|
|
236
|
+
expect(Qs.parse('a[0]=b&a[1]=&a[2]=c&a[19]', { strictNullHandling: true })).to.deep.equal({ a: ['b', '', 'c', null] });
|
|
216
237
|
expect(Qs.parse('a[]=&a[]=b&a[]=c')).to.deep.equal({ a: ['', 'b', 'c'] });
|
|
217
238
|
done();
|
|
218
239
|
});
|
|
@@ -239,7 +260,8 @@ describe('parse()', function () {
|
|
|
239
260
|
|
|
240
261
|
it('continues parsing when no parent is found', function (done) {
|
|
241
262
|
|
|
242
|
-
expect(Qs.parse('[]
|
|
263
|
+
expect(Qs.parse('[]=&a=b')).to.deep.equal({ '0': '', a: 'b' });
|
|
264
|
+
expect(Qs.parse('[]&a=b', { strictNullHandling: true })).to.deep.equal({ '0': null, a: 'b' });
|
|
243
265
|
expect(Qs.parse('[foo]=bar')).to.deep.equal({ foo: 'bar' });
|
|
244
266
|
done();
|
|
245
267
|
});
|
|
@@ -310,16 +332,42 @@ describe('parse()', function () {
|
|
|
310
332
|
done();
|
|
311
333
|
});
|
|
312
334
|
|
|
335
|
+
it('allows disabling array parsing', function (done) {
|
|
336
|
+
|
|
337
|
+
expect(Qs.parse('a[0]=b&a[1]=c', { parseArrays: false })).to.deep.equal({ a: { '0': 'b', '1': 'c' } });
|
|
338
|
+
done();
|
|
339
|
+
});
|
|
340
|
+
|
|
313
341
|
it('parses an object', function (done) {
|
|
314
342
|
|
|
315
343
|
var input = {
|
|
316
|
-
'user[name]': {'pop[bob]': 3},
|
|
344
|
+
'user[name]': { 'pop[bob]': 3 },
|
|
317
345
|
'user[email]': null
|
|
318
346
|
};
|
|
319
347
|
|
|
320
348
|
var expected = {
|
|
321
349
|
'user': {
|
|
322
|
-
'name': {'pop[bob]': 3},
|
|
350
|
+
'name': { 'pop[bob]': 3 },
|
|
351
|
+
'email': null
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
var result = Qs.parse(input);
|
|
356
|
+
|
|
357
|
+
expect(result).to.deep.equal(expected);
|
|
358
|
+
done();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it('parses an object in dot notation', function (done) {
|
|
362
|
+
|
|
363
|
+
var input = {
|
|
364
|
+
'user.name': { 'pop[bob]': 3 },
|
|
365
|
+
'user.email.': null
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
var expected = {
|
|
369
|
+
'user': {
|
|
370
|
+
'name': { 'pop[bob]': 3 },
|
|
323
371
|
'email': null
|
|
324
372
|
}
|
|
325
373
|
};
|
|
@@ -333,13 +381,13 @@ describe('parse()', function () {
|
|
|
333
381
|
it('parses an object and not child values', function (done) {
|
|
334
382
|
|
|
335
383
|
var input = {
|
|
336
|
-
'user[name]': {'pop[bob]': { 'test': 3 }},
|
|
384
|
+
'user[name]': { 'pop[bob]': { 'test': 3 } },
|
|
337
385
|
'user[email]': null
|
|
338
386
|
};
|
|
339
387
|
|
|
340
388
|
var expected = {
|
|
341
389
|
'user': {
|
|
342
|
-
'name': {'pop[bob]': { 'test': 3 }},
|
|
390
|
+
'name': { 'pop[bob]': { 'test': 3 } },
|
|
343
391
|
'email': null
|
|
344
392
|
}
|
|
345
393
|
};
|
|
@@ -360,12 +408,6 @@ describe('parse()', function () {
|
|
|
360
408
|
done();
|
|
361
409
|
});
|
|
362
410
|
|
|
363
|
-
it('does not crash when using invalid dot notation', function (done) {
|
|
364
|
-
|
|
365
|
-
expect(Qs.parse('roomInfoList[0].childrenAges[0]=15&roomInfoList[0].numberOfAdults=2')).to.deep.equal({ roomInfoList: [['15', '2']] });
|
|
366
|
-
done();
|
|
367
|
-
});
|
|
368
|
-
|
|
369
411
|
it('does not crash when parsing circular references', function (done) {
|
|
370
412
|
|
|
371
413
|
var a = {};
|
|
@@ -410,4 +452,27 @@ describe('parse()', function () {
|
|
|
410
452
|
expect(Qs.parse({ a: re })).to.deep.equal({ a: re });
|
|
411
453
|
done();
|
|
412
454
|
});
|
|
455
|
+
|
|
456
|
+
it('can allow overwriting prototype properties', function (done) {
|
|
457
|
+
|
|
458
|
+
expect(Qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true })).to.deep.equal({ a: { hasOwnProperty: 'b' } }, { prototype: false });
|
|
459
|
+
expect(Qs.parse('hasOwnProperty=b', { allowPrototypes: true })).to.deep.equal({ hasOwnProperty: 'b' }, { prototype: false });
|
|
460
|
+
done();
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it('can return plain objects', function (done) {
|
|
464
|
+
|
|
465
|
+
var expected = Object.create(null);
|
|
466
|
+
expected.a = Object.create(null);
|
|
467
|
+
expected.a.b = 'c';
|
|
468
|
+
expected.a.hasOwnProperty = 'd';
|
|
469
|
+
expect(Qs.parse('a[b]=c&a[hasOwnProperty]=d', { plainObjects: true })).to.deep.equal(expected);
|
|
470
|
+
expect(Qs.parse(null, { plainObjects: true })).to.deep.equal(Object.create(null));
|
|
471
|
+
var expectedArray = Object.create(null);
|
|
472
|
+
expectedArray.a = Object.create(null);
|
|
473
|
+
expectedArray.a['0'] = 'b';
|
|
474
|
+
expectedArray.a.c = 'd';
|
|
475
|
+
expect(Qs.parse('a[]=b&a[c]=d', { plainObjects: true })).to.deep.equal(expectedArray);
|
|
476
|
+
done();
|
|
477
|
+
});
|
|
413
478
|
});
|
package/test/stringify.js
CHANGED
|
@@ -26,6 +26,11 @@ describe('stringify()', function () {
|
|
|
26
26
|
expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
|
|
27
27
|
expect(Qs.stringify({ a: 1 })).to.equal('a=1');
|
|
28
28
|
expect(Qs.stringify({ a: 1, b: 2 })).to.equal('a=1&b=2');
|
|
29
|
+
expect(Qs.stringify({ a: 'A_Z' })).to.equal('a=A_Z');
|
|
30
|
+
expect(Qs.stringify({ a: '€' })).to.equal('a=%E2%82%AC');
|
|
31
|
+
expect(Qs.stringify({ a: '' })).to.equal('a=%EE%80%80');
|
|
32
|
+
expect(Qs.stringify({ a: 'א' })).to.equal('a=%D7%90');
|
|
33
|
+
expect(Qs.stringify({ a: '𐐷' })).to.equal('a=%F0%90%90%B7');
|
|
29
34
|
done();
|
|
30
35
|
});
|
|
31
36
|
|
|
@@ -106,9 +111,15 @@ describe('stringify()', function () {
|
|
|
106
111
|
it('stringifies an empty value', function (done) {
|
|
107
112
|
|
|
108
113
|
expect(Qs.stringify({ a: '' })).to.equal('a=');
|
|
114
|
+
expect(Qs.stringify({ a: null }, { strictNullHandling: true })).to.equal('a');
|
|
115
|
+
|
|
109
116
|
expect(Qs.stringify({ a: '', b: '' })).to.equal('a=&b=');
|
|
110
|
-
expect(Qs.stringify({ a: null })).to.equal('a=');
|
|
111
|
-
|
|
117
|
+
expect(Qs.stringify({ a: null, b: '' }, { strictNullHandling: true })).to.equal('a&b=');
|
|
118
|
+
|
|
119
|
+
expect(Qs.stringify({ a: { b: '' } })).to.equal('a%5Bb%5D=');
|
|
120
|
+
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: true })).to.equal('a%5Bb%5D');
|
|
121
|
+
expect(Qs.stringify({ a: { b: null } }, { strictNullHandling: false })).to.equal('a%5Bb%5D=');
|
|
122
|
+
|
|
112
123
|
done();
|
|
113
124
|
});
|
|
114
125
|
|
|
@@ -143,7 +154,10 @@ describe('stringify()', function () {
|
|
|
143
154
|
it('drops keys with a value of undefined', function (done) {
|
|
144
155
|
|
|
145
156
|
expect(Qs.stringify({ a: undefined })).to.equal('');
|
|
146
|
-
|
|
157
|
+
|
|
158
|
+
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: true })).to.equal('a%5Bc%5D');
|
|
159
|
+
expect(Qs.stringify({ a: { b: undefined, c: null } }, { strictNullHandling: false })).to.equal('a%5Bc%5D=');
|
|
160
|
+
expect(Qs.stringify({ a: { b: undefined, c: '' } })).to.equal('a%5Bc%5D=');
|
|
147
161
|
done();
|
|
148
162
|
});
|
|
149
163
|
|
|
@@ -163,14 +177,14 @@ describe('stringify()', function () {
|
|
|
163
177
|
|
|
164
178
|
it('stringifies the weird object from qs', function (done) {
|
|
165
179
|
|
|
166
|
-
expect(Qs.stringify({ 'my weird field': 'q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field
|
|
180
|
+
expect(Qs.stringify({ 'my weird field': '~q1!2"\'w$5&7/z8)?' })).to.equal('my%20weird%20field=~q1%212%22%27w%245%267%2Fz8%29%3F');
|
|
167
181
|
done();
|
|
168
182
|
});
|
|
169
183
|
|
|
170
184
|
it('skips properties that are part of the object prototype', function (done) {
|
|
171
185
|
|
|
172
186
|
Object.prototype.crash = 'test';
|
|
173
|
-
expect(Qs.stringify({ a: 'b'})).to.equal('a=b');
|
|
187
|
+
expect(Qs.stringify({ a: 'b' })).to.equal('a=b');
|
|
174
188
|
expect(Qs.stringify({ a: { b: 'c' } })).to.equal('a%5Bb%5D=c');
|
|
175
189
|
delete Object.prototype.crash;
|
|
176
190
|
done();
|
|
@@ -206,4 +220,40 @@ describe('stringify()', function () {
|
|
|
206
220
|
global.Buffer = tempBuffer;
|
|
207
221
|
done();
|
|
208
222
|
});
|
|
223
|
+
|
|
224
|
+
it('selects properties when filter=array', function (done) {
|
|
225
|
+
|
|
226
|
+
expect(Qs.stringify({ a: 'b' }, { filter: ['a'] })).to.equal('a=b');
|
|
227
|
+
expect(Qs.stringify({ a: 1 }, { filter: [] })).to.equal('');
|
|
228
|
+
expect(Qs.stringify({ a: { b: [1, 2, 3, 4], c: 'd' }, c: 'f' }, { filter: ['a', 'b', 0, 2] })).to.equal('a%5Bb%5D%5B0%5D=1&a%5Bb%5D%5B2%5D=3');
|
|
229
|
+
done();
|
|
230
|
+
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('supports custom representations when filter=function', function (done) {
|
|
234
|
+
|
|
235
|
+
var calls = 0;
|
|
236
|
+
var obj = { a: 'b', c: 'd', e: { f: new Date(1257894000000) } };
|
|
237
|
+
var filterFunc = function (prefix, value) {
|
|
238
|
+
|
|
239
|
+
calls++;
|
|
240
|
+
if (calls === 1) {
|
|
241
|
+
expect(prefix).to.be.empty();
|
|
242
|
+
expect(value).to.equal(obj);
|
|
243
|
+
}
|
|
244
|
+
else if (prefix === 'c') {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
else if (value instanceof Date) {
|
|
248
|
+
expect(prefix).to.equal('e[f]');
|
|
249
|
+
return value.getTime();
|
|
250
|
+
}
|
|
251
|
+
return value;
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
expect(Qs.stringify(obj, { filter: filterFunc })).to.equal('a=b&e%5Bf%5D=1257894000000');
|
|
255
|
+
expect(calls).to.equal(5);
|
|
256
|
+
done();
|
|
257
|
+
|
|
258
|
+
});
|
|
209
259
|
});
|
package/test/utils.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Load modules
|
|
2
|
+
|
|
3
|
+
var Code = require('code');
|
|
4
|
+
var Lab = require('lab');
|
|
5
|
+
var Utils = require('../lib/utils');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Declare internals
|
|
9
|
+
|
|
10
|
+
var internals = {};
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Test shortcuts
|
|
14
|
+
|
|
15
|
+
var lab = exports.lab = Lab.script();
|
|
16
|
+
var expect = Code.expect;
|
|
17
|
+
var describe = lab.experiment;
|
|
18
|
+
var it = lab.test;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
describe('merge()', function () {
|
|
22
|
+
|
|
23
|
+
it('can merge two objects with the same key', function (done) {
|
|
24
|
+
|
|
25
|
+
expect(Utils.merge({ a: 'b' }, { a: 'c' })).to.deep.equal({ a: ['b', 'c'] });
|
|
26
|
+
done();
|
|
27
|
+
});
|
|
28
|
+
});
|
package/.jshintignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
node_modules
|
package/.jshintrc
DELETED
package/Makefile
DELETED
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
module.exports = require('./lib/');
|