qs 6.11.2 → 6.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc CHANGED
@@ -14,7 +14,7 @@
14
14
  "id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
15
15
  "indent": [2, 4],
16
16
  "max-lines-per-function": [2, { "max": 150 }],
17
- "max-params": [2, 16],
17
+ "max-params": [2, 18],
18
18
  "max-statements": [2, 100],
19
19
  "multiline-comment-style": 0,
20
20
  "no-continue": 1,
package/CHANGELOG.md CHANGED
@@ -1,3 +1,26 @@
1
+ ## **6.12.0**
2
+
3
+ - [New] `parse`/`stringify`: add `decodeDotInKeys`/`encodeDotKeys` options (#488)
4
+ - [New] `parse`: add `duplicates` option
5
+ - [New] `parse`/`stringify`: add `allowEmptyArrays` option to allow [] in object values (#487)
6
+ - [Refactor] `parse`/`stringify`: move allowDots config logic to its own variable
7
+ - [Refactor] `stringify`: move option-handling code into `normalizeStringifyOptions`
8
+ - [readme] update readme, add logos (#484)
9
+ - [readme] `stringify`: clarify default `arrayFormat` behavior
10
+ - [readme] fix line wrapping
11
+ - [readme] remove dead badges
12
+ - [Deps] update `side-channel`
13
+ - [meta] make the dist build 50% smaller
14
+ - [meta] add `sideEffects` flag
15
+ - [meta] run build in prepack, not prepublish
16
+ - [Tests] `parse`: remove useless tests; add coverage
17
+ - [Tests] `stringify`: increase coverage
18
+ - [Tests] use `mock-property`
19
+ - [Tests] `stringify`: improve coverage
20
+ - [Dev Deps] update `@ljharb/eslint-config `, `aud`, `has-override-mistake`, `has-property-descriptors`, `mock-property`, `npmignore`, `object-inspect`, `tape`
21
+ - [Dev Deps] pin `glob`, since v10.3.8+ requires a broken `jackspeak`
22
+ - [Dev Deps] pin `jackspeak` since 2.1.2+ depends on npm aliases, which kill the install process in npm < 6
23
+
1
24
  ## **6.11.2**
2
25
  - [Fix] `parse`: Fix parsing when the global Object prototype is frozen (#473)
3
26
  - [Tests] add passing test cases with empty keys (#473)
package/README.md CHANGED
@@ -1,9 +1,11 @@
1
+ <p align="center">
2
+ <img alt="qs" src="./logos/banner_default.png" width="800" />
3
+ </p>
4
+
1
5
  # qs <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
2
6
 
3
7
  [![github actions][actions-image]][actions-url]
4
8
  [![coverage][codecov-image]][codecov-url]
5
- [![dependency status][deps-svg]][deps-url]
6
- [![dev dependency status][dev-deps-svg]][dev-deps-url]
7
9
  [![License][license-image]][license-url]
8
10
  [![Downloads][downloads-image]][downloads-url]
9
11
 
@@ -53,7 +55,9 @@ var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
53
55
  assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
54
56
  ```
55
57
 
56
- 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.
58
+ 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.
59
+ *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.
60
+ Always be careful with this option.
57
61
 
58
62
  ```javascript
59
63
  var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
@@ -80,8 +84,8 @@ assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {
80
84
  });
81
85
  ```
82
86
 
83
- By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
84
- `'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
87
+ By default, when nesting objects **qs** will only parse up to 5 children deep.
88
+ This means if you attempt to parse a string like `'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
85
89
 
86
90
  ```javascript
87
91
  var expected = {
@@ -147,32 +151,44 @@ var withDots = qs.parse('a.b=c', { allowDots: true });
147
151
  assert.deepEqual(withDots, { a: { b: 'c' } });
148
152
  ```
149
153
 
150
- If you have to deal with legacy browsers or services, there's
151
- also support for decoding percent-encoded octets as iso-8859-1:
154
+ Option `decodeDotInKeys` can be used to decode dots in keys
155
+ Note: it implies `allowDots`, so `parse` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
156
+
157
+ ```javascript
158
+ var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decodeDotInKeys: true });
159
+ assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
160
+ ```
161
+
162
+ Option `allowEmptyArrays` can be used to allowing empty array values in object
163
+ ```javascript
164
+ var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
165
+ assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
166
+ ```
167
+
168
+ Option `duplicates` can be used to change the behavior when duplicate keys are encountered
169
+ ```javascript
170
+ assert.deepEqual(qs.parse('foo=bar&foo=baz'), { foo: ['bar', 'baz'] });
171
+ assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'combine' }), { foo: ['bar', 'baz'] });
172
+ assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'bar' });
173
+ assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
174
+ ```
175
+
176
+ If you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as iso-8859-1:
152
177
 
153
178
  ```javascript
154
179
  var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
155
180
  assert.deepEqual(oldCharset, { a: '§' });
156
181
  ```
157
182
 
158
- Some services add an initial `utf8=✓` value to forms so that old
159
- Internet Explorer versions are more likely to submit the form as
160
- utf-8. Additionally, the server can check the value against wrong
161
- encodings of the checkmark character and detect that a query string
162
- or `application/x-www-form-urlencoded` body was *not* sent as
163
- utf-8, eg. if the form had an `accept-charset` parameter or the
164
- containing page had a different character set.
183
+ Some services add an initial `utf8=✓` value to forms so that old Internet Explorer versions are more likely to submit the form as utf-8.
184
+ Additionally, the server can check the value against wrong encodings of the checkmark character and detect that a query string or `application/x-www-form-urlencoded` body was *not* sent as utf-8, eg. if the form had an `accept-charset` parameter or the containing page had a different character set.
165
185
 
166
186
  **qs** supports this mechanism via the `charsetSentinel` option.
167
- If specified, the `utf8` parameter will be omitted from the
168
- returned object. It will be used to switch to `iso-8859-1`/`utf-8`
169
- mode depending on how the checkmark is encoded.
187
+ If specified, the `utf8` parameter will be omitted from the returned object.
188
+ It will be used to switch to `iso-8859-1`/`utf-8` mode depending on how the checkmark is encoded.
170
189
 
171
- **Important**: When you specify both the `charset` option and the
172
- `charsetSentinel` option, the `charset` will be overridden when
173
- the request contains a `utf8` parameter from which the actual
174
- charset can be deduced. In that sense the `charset` will behave
175
- as the default charset rather than the authoritative charset.
190
+ **Important**: When you specify both the `charset` option and the `charsetSentinel` option, the `charset` will be overridden when the request contains a `utf8` parameter from which the actual charset can be deduced.
191
+ In that sense the `charset` will behave as the default charset rather than the authoritative charset.
176
192
 
177
193
  ```javascript
178
194
  var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
@@ -189,8 +205,7 @@ var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
189
205
  assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
190
206
  ```
191
207
 
192
- If you want to decode the `&#...;` syntax to the actual character,
193
- you can specify the `interpretNumericEntities` option as well:
208
+ If you want to decode the `&#...;` syntax to the actual character, you can specify the `interpretNumericEntities` option as well:
194
209
 
195
210
  ```javascript
196
211
  var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
@@ -200,8 +215,7 @@ var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
200
215
  assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
201
216
  ```
202
217
 
203
- It also works when the charset has been detected in `charsetSentinel`
204
- mode.
218
+ It also works when the charset has been detected in `charsetSentinel` mode.
205
219
 
206
220
  ### Parsing Arrays
207
221
 
@@ -219,9 +233,8 @@ var withIndexes = qs.parse('a[1]=c&a[0]=b');
219
233
  assert.deepEqual(withIndexes, { a: ['b', 'c'] });
220
234
  ```
221
235
 
222
- Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
223
- to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
224
- their order:
236
+ Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number to create an array.
237
+ When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving their order:
225
238
 
226
239
  ```javascript
227
240
  var noSparse = qs.parse('a[1]=b&a[15]=c');
@@ -245,8 +258,9 @@ var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
245
258
  assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
246
259
  ```
247
260
 
248
- **qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
249
- instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
261
+ **qs** will also limit specifying indices in an array to a maximum index of `20`.
262
+ Any array members with an index of greater than `20` will instead be converted to an object with the index as the key.
263
+ This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
250
264
 
251
265
  ```javascript
252
266
  var withMaxIndex = qs.parse('a[100]=b');
@@ -290,7 +304,8 @@ assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
290
304
 
291
305
  ### Parsing primitive/scalar values (numbers, booleans, null, etc)
292
306
 
293
- By default, all values are parsed as strings. This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
307
+ By default, all values are parsed as strings.
308
+ This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
294
309
 
295
310
  ```javascript
296
311
  var primitiveValues = qs.parse('a=15&b=true&c=null');
@@ -373,16 +388,17 @@ var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset,
373
388
  }})
374
389
  ```
375
390
 
376
- 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.
391
+ Examples beyond this point will be shown as though the output is not URI encoded for clarity.
392
+ Please note that the return values in these cases *will* be URI encoded during real usage.
377
393
 
378
- When arrays are stringified, by default they are given explicit indices:
394
+ When arrays are stringified, they follow the `arrayFormat` option, which defaults to `indices`:
379
395
 
380
396
  ```javascript
381
397
  qs.stringify({ a: ['b', 'c', 'd'] });
382
398
  // 'a[0]=b&a[1]=c&a[2]=d'
383
399
  ```
384
400
 
385
- You may override this by setting the `indices` option to `false`:
401
+ You may override this by setting the `indices` option to `false`, or to be more explicit, the `arrayFormat` option to `repeat`:
386
402
 
387
403
  ```javascript
388
404
  qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
@@ -418,6 +434,20 @@ qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
418
434
  // 'a.b.c=d&a.b.e=f'
419
435
  ```
420
436
 
437
+ You may encode the dot notation in the keys of object with option `encodeDotInKeys` by setting it to `true`:
438
+ Note: it implies `allowDots`, so `stringify` will error if you set `decodeDotInKeys` to `true`, and `allowDots` to `false`.
439
+ Caveat: when `encodeValuesOnly` is `true` as well as `encodeDotInKeys`, only dots in keys and nothing else will be encoded.
440
+ ```javascript
441
+ qs.stringify({ "name.obj": { "first": "John", "last": "Doe" } }, { allowDots: true, encodeDotInKeys: true })
442
+ // 'name%252Eobj.first=John&name%252Eobj.last=Doe'
443
+ ```
444
+
445
+ You may allow empty array values by setting the `allowEmptyArrays` option to `true`:
446
+ ```javascript
447
+ qs.stringify({ foo: [], bar: 'baz' }, { allowEmptyArrays: true });
448
+ // 'foo[]&bar=baz'
449
+ ```
450
+
421
451
  Empty strings and null values will omit the value, but the equals sign (=) remains in place:
422
452
 
423
453
  ```javascript
@@ -473,8 +503,8 @@ assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort
473
503
  ```
474
504
 
475
505
  Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
476
- If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
477
- pass an array, it will be used to select properties and array indices for stringification:
506
+ If you pass a function, it will be called for each key to obtain the replacement value.
507
+ Otherwise, if you pass an array, it will be used to select properties and array indices for stringification:
478
508
 
479
509
  ```javascript
480
510
  function filterFunc(prefix, value) {
@@ -498,8 +528,8 @@ qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] });
498
528
  // 'a[0]=b&a[2]=d'
499
529
  ```
500
530
 
501
- You could also use `filter` to inject custom serialization for user defined types. Consider you're working with
502
- some api that expects query strings of the format for ranges:
531
+ You could also use `filter` to inject custom serialization for user defined types.
532
+ Consider you're working with some api that expects query strings of the format for ranges:
503
533
 
504
534
  ```
505
535
  https://domain.com/endpoint?range=30...70
@@ -545,7 +575,8 @@ var withNull = qs.stringify({ a: null, b: '' });
545
575
  assert.equal(withNull, 'a=&b=');
546
576
  ```
547
577
 
548
- Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
578
+ Parsing does not distinguish between parameters with and without equal signs.
579
+ Both are converted to empty strings.
549
580
 
550
581
  ```javascript
551
582
  var equalsInsensitive = qs.parse('a&b=');
@@ -574,25 +605,21 @@ var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
574
605
  assert.equal(nullsSkipped, 'a=b');
575
606
  ```
576
607
 
577
- If you're communicating with legacy systems, you can switch to `iso-8859-1`
578
- using the `charset` option:
608
+ If you're communicating with legacy systems, you can switch to `iso-8859-1` using the `charset` option:
579
609
 
580
610
  ```javascript
581
611
  var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
582
612
  assert.equal(iso, '%E6=%E6');
583
613
  ```
584
614
 
585
- Characters that don't exist in `iso-8859-1` will be converted to numeric
586
- entities, similar to what browsers do:
615
+ Characters that don't exist in `iso-8859-1` will be converted to numeric entities, similar to what browsers do:
587
616
 
588
617
  ```javascript
589
618
  var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
590
619
  assert.equal(numeric, 'a=%26%239786%3B');
591
620
  ```
592
621
 
593
- You can use the `charsetSentinel` option to announce the character by
594
- including an `utf8=✓` parameter with the proper encoding if the checkmark,
595
- similar to what Ruby on Rails and others do when submitting forms.
622
+ You can use the `charsetSentinel` option to announce the character by including an `utf8=✓` parameter with the proper encoding if the checkmark, similar to what Ruby on Rails and others do when submitting forms.
596
623
 
597
624
  ```javascript
598
625
  var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
@@ -604,8 +631,7 @@ assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
604
631
 
605
632
  ### Dealing with special character sets
606
633
 
607
- By default the encoding and decoding of characters is done in `utf-8`,
608
- and `iso-8859-1` support is also built in via the `charset` parameter.
634
+ By default the encoding and decoding of characters is done in `utf-8`, and `iso-8859-1` support is also built in via the `charset` parameter.
609
635
 
610
636
  If you wish to encode querystrings to a different character set (i.e.
611
637
  [Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
@@ -644,7 +670,9 @@ Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/se
644
670
 
645
671
  Available as part of the Tidelift Subscription
646
672
 
647
- The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
673
+ The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications.
674
+ Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use.
675
+ [Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
648
676
 
649
677
  [package-url]: https://npmjs.org/package/qs
650
678
  [npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
@@ -661,3 +689,9 @@ The maintainers of qs and thousands of other packages are working with Tidelift
661
689
  [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
662
690
  [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
663
691
  [actions-url]: https://github.com/ljharb/qs/actions
692
+
693
+ ## Acknowledgements
694
+
695
+ qs logo by [NUMI](https://github.com/numi-hq/open-design):
696
+
697
+ [<img src="https://raw.githubusercontent.com/numi-hq/open-design/main/assets/numi-lockup.png" alt="NUMI Logo" style="width: 200px;"/>](https://numi.tech/?ref=qs)