qs 6.10.1 → 6.10.4

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/.editorconfig CHANGED
@@ -8,6 +8,7 @@ charset = utf-8
8
8
  trim_trailing_whitespace = true
9
9
  insert_final_newline = true
10
10
  max_line_length = 160
11
+ quote_type = single
11
12
 
12
13
  [test/*]
13
14
  max_line_length = off
@@ -37,3 +38,6 @@ indent_size = off
37
38
  indent_style = off
38
39
  indent = off
39
40
  max_line_length = off
41
+
42
+ [.nycrc]
43
+ indent_style = tab
package/.eslintrc CHANGED
@@ -3,10 +3,14 @@
3
3
 
4
4
  "extends": "@ljharb",
5
5
 
6
+ "ignorePatterns": [
7
+ "dist/",
8
+ ],
9
+
6
10
  "rules": {
7
11
  "complexity": 0,
8
12
  "consistent-return": 1,
9
- "func-name-matching": 0,
13
+ "func-name-matching": 0,
10
14
  "id-length": [2, { "min": 1, "max": 25, "properties": "never" }],
11
15
  "indent": [2, 4],
12
16
  "max-lines-per-function": [2, { "max": 150 }],
@@ -16,7 +20,6 @@
16
20
  "no-continue": 1,
17
21
  "no-magic-numbers": 0,
18
22
  "no-restricted-syntax": [2, "BreakStatement", "DebuggerStatement", "ForInStatement", "LabeledStatement", "WithStatement"],
19
- "operator-linebreak": [2, "before"],
20
23
  },
21
24
 
22
25
  "overrides": [
@@ -29,7 +32,7 @@
29
32
  "no-buffer-constructor": 0,
30
33
  "no-extend-native": 0,
31
34
  "no-throw-literal": 0,
32
- }
33
- }
34
- ]
35
+ },
36
+ },
37
+ ],
35
38
  }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## **6.10.4**
2
+ - [Fix] `stringify`: with `arrayFormat: comma`, include an explicit `[]` on a single-item array (#441)
3
+ - [meta] use `npmignore` to autogenerate an npmignore file
4
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `has-symbol`, `object-inspect`, `tape`
5
+
6
+ ## **6.10.3**
7
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
8
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
9
+ - [actions] reuse common workflows
10
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `object-inspect`, `tape`
11
+
12
+ ## **6.10.2**
13
+ - [Fix] `stringify`: actually fix cyclic references (#426)
14
+ - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
15
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
16
+ - [Docs] add note and links for coercing primitive values (#408)
17
+ - [actions] update codecov uploader
18
+ - [actions] update workflows
19
+ - [Tests] clean up stringify tests slightly
20
+ - [Dev Deps] update `eslint`, `@ljharb/eslint-config`, `aud`, `object-inspect`, `safe-publish-latest`, `tape`
21
+
1
22
  ## **6.10.1**
2
23
  - [Fix] `stringify`: avoid exception on repeated object values (#402)
3
24
 
@@ -11,6 +32,18 @@
11
32
  - [Tests] use `ljharb/actions/node/install` instead of `ljharb/actions/node/run`
12
33
  - [Tests] Revert "[meta] ignore eclint transitive audit warning"
13
34
 
35
+ ## **6.9.7**
36
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
37
+ - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
38
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
39
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
40
+ - [Docs] add note and links for coercing primitive values (#408)
41
+ - [Tests] clean up stringify tests slightly
42
+ - [meta] fix README.md (#399)
43
+ - Revert "[meta] ignore eclint transitive audit warning"
44
+ - [actions] backport actions from main
45
+ - [Dev Deps] backport updates from main
46
+
14
47
  ## **6.9.6**
15
48
  - [Fix] restore `dist` dir; mistakenly removed in d4f6c32
16
49
 
@@ -59,6 +92,19 @@
59
92
  - [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
60
93
  - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
61
94
 
95
+ ## **6.8.3**
96
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
97
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
98
+ - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
99
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
100
+ - [Tests] clean up stringify tests slightly
101
+ - [Docs] add note and links for coercing primitive values (#408)
102
+ - [meta] fix README.md (#399)
103
+ - [actions] backport actions from main
104
+ - [Dev Deps] backport updates from main
105
+ - [Refactor] `stringify`: reduce branching
106
+ - [meta] do not publish workflow files
107
+
62
108
  ## **6.8.2**
63
109
  - [Fix] proper comma parsing of URL-encoded commas (#361)
64
110
  - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
@@ -90,6 +136,19 @@
90
136
  - [meta] add FUNDING.yml
91
137
  - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
92
138
 
139
+ ## **6.7.3**
140
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
141
+ - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
142
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
143
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
144
+ - [Docs] add note and links for coercing primitive values (#408)
145
+ - [meta] fix README.md (#399)
146
+ - [meta] do not publish workflow files
147
+ - [actions] backport actions from main
148
+ - [Dev Deps] backport updates from main
149
+ - [Tests] use `nyc` for coverage
150
+ - [Tests] clean up stringify tests slightly
151
+
93
152
  ## **6.7.2**
94
153
  - [Fix] proper comma parsing of URL-encoded commas (#361)
95
154
  - [Fix] parses comma delimited array while having percent-encoded comma treated as normal text (#336)
@@ -128,6 +187,32 @@
128
187
  - [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
129
188
  - [Tests] temporarily allow coverage to fail
130
189
 
190
+ ## **6.6.1**
191
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
192
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
193
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
194
+ - [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
195
+ - [Fix] correctly parse nested arrays
196
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
197
+ - [Robustness] `stringify`: cache `Object.prototype.hasOwnProperty`
198
+ - [Refactor] `formats`: tiny bit of cleanup.
199
+ - [Refactor] `utils`: `isBuffer`: small tweak; add tests
200
+ - [Refactor]: `stringify`/`utils`: cache `Array.isArray`
201
+ - [Refactor] `utils`: reduce observable [[Get]]s
202
+ - [Refactor] use cached `Array.isArray`
203
+ - [Refactor] `parse`/`stringify`: make a function to normalize the options
204
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
205
+ - [Docs] Clarify the need for "arrayLimit" option
206
+ - [meta] fix README.md (#399)
207
+ - [meta] do not publish workflow files
208
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
209
+ - [meta] add FUNDING.yml
210
+ - [meta] Fixes typo in CHANGELOG.md
211
+ - [actions] backport actions from main
212
+ - [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
213
+ - [Tests] always use `String(x)` over `x.toString()`
214
+ - [Dev Deps] backport from main
215
+
131
216
  ## **6.6.0**
132
217
  - [New] Add support for iso-8859-1, utf8 "sentinel" and numeric entities (#268)
133
218
  - [New] move two-value combine to a `utils` function (#189)
@@ -144,6 +229,30 @@
144
229
  - [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`, `iconv-lite`, `safe-publish-latest`, `tape`
145
230
  - [Tests] up to `node` `v10.10`, `v9.11`, `v8.12`, `v6.14`, `v4.9`; pin included builds to LTS
146
231
 
232
+ ## **6.5.3**
233
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
234
+ - [Fix]` `utils.merge`: avoid a crash with a null target and a truthy non-array source
235
+ - [Fix] correctly parse nested arrays
236
+ - [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
237
+ - [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
238
+ - [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
239
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
240
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
241
+ - [Refactor] `utils`: reduce observable [[Get]]s
242
+ - [Refactor] use cached `Array.isArray`
243
+ - [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
244
+ - [Refactor] `parse`: only need to reassign the var once
245
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
246
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
247
+ - [Docs] Clean up license text so it’s properly detected as BSD-3-Clause
248
+ - [Docs] Clarify the need for "arrayLimit" option
249
+ - [meta] fix README.md (#399)
250
+ - [meta] add FUNDING.yml
251
+ - [actions] backport actions from main
252
+ - [Tests] always use `String(x)` over `x.toString()`
253
+ - [Tests] remove nonexistent tape option
254
+ - [Dev Deps] backport from main
255
+
147
256
  ## **6.5.2**
148
257
  - [Fix] use `safer-buffer` instead of `Buffer` constructor
149
258
  - [Refactor] utils: `module.exports` one thing, instead of mutating `exports` (#230)
@@ -170,6 +279,27 @@
170
279
  - [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4
171
280
  - [Tests] add `editorconfig-tools`
172
281
 
282
+ ## **6.4.1**
283
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
284
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
285
+ - [Fix] use `safer-buffer` instead of `Buffer` constructor
286
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
287
+ - [Fix]` `utils.merge`: avoid a crash with a null target and a truthy non-array source
288
+ - [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
289
+ - [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
290
+ - [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
291
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
292
+ - [Refactor] use cached `Array.isArray`
293
+ - [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
294
+ - [readme] remove travis badge; add github actions/codecov badges; update URLs
295
+ - [Docs] Clarify the need for "arrayLimit" option
296
+ - [meta] fix README.md (#399)
297
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
298
+ - [meta] add FUNDING.yml
299
+ - [actions] backport actions from main
300
+ - [Tests] remove nonexistent tape option
301
+ - [Dev Deps] backport from main
302
+
173
303
  ## **6.4.0**
174
304
  - [New] `qs.stringify`: add `encodeValuesOnly` option
175
305
  - [Fix] follow `allowPrototypes` option during merge (#201, #201)
@@ -179,6 +309,26 @@
179
309
  - [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
180
310
  - [eslint] reduce warnings
181
311
 
312
+ ## **6.3.3**
313
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
314
+ - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
315
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
316
+ - [Fix]` `utils.merge`: avoid a crash with a null target and a truthy non-array source
317
+ - [Fix] `stringify`: fix a crash with `strictNullHandling` and a custom `filter`/`serializeDate` (#279)
318
+ - [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
319
+ - [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
320
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
321
+ - [Refactor] use cached `Array.isArray`
322
+ - [Refactor] `stringify`: Avoid arr = arr.concat(...), push to the existing instance (#269)
323
+ - [Docs] Clarify the need for "arrayLimit" option
324
+ - [meta] fix README.md (#399)
325
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
326
+ - [meta] add FUNDING.yml
327
+ - [actions] backport actions from main
328
+ - [Tests] use `safer-buffer` instead of `Buffer` constructor
329
+ - [Tests] remove nonexistent tape option
330
+ - [Dev Deps] backport from main
331
+
182
332
  ## **6.3.2**
183
333
  - [Fix] follow `allowPrototypes` option during merge (#201, #200)
184
334
  - [Dev Deps] update `eslint`
@@ -212,6 +362,23 @@
212
362
  - [Tests] skip Object.create tests when null objects are not available
213
363
  - [Tests] Turn on eslint for test files (#175)
214
364
 
365
+ ## **6.2.4**
366
+ - [Fix] `parse`: ignore `__proto__` keys (#428)
367
+ - [Fix] `utils.merge`: avoid a crash with a null target and an array source
368
+ - [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
369
+ - [Fix] `utils`: `merge`: fix crash when `source` is a truthy primitive & no options are provided
370
+ - [Fix] when `parseArrays` is false, properly handle keys ending in `[]`
371
+ - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
372
+ - [Refactor] use cached `Array.isArray`
373
+ - [Docs] Clarify the need for "arrayLimit" option
374
+ - [meta] fix README.md (#399)
375
+ - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
376
+ - [meta] add FUNDING.yml
377
+ - [actions] backport actions from main
378
+ - [Tests] use `safer-buffer` instead of `Buffer` constructor
379
+ - [Tests] remove nonexistent tape option
380
+ - [Dev Deps] backport from main
381
+
215
382
  ## **6.2.3**
216
383
  - [Fix] follow `allowPrototypes` option during merge (#201, #200)
217
384
  - [Fix] chmod a-x
package/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # qs <sup>[![Version Badge][2]][1]</sup>
2
2
 
3
- [![Build Status][3]][4]
4
- [![dependency status][5]][6]
5
- [![dev dependency status][7]][8]
3
+ [![github actions][actions-image]][actions-url]
4
+ [![coverage][codecov-image]][codecov-url]
5
+ [![dependency status][deps-svg]][deps-url]
6
+ [![dev dependency status][dev-deps-svg]][dev-deps-url]
6
7
  [![License][license-image]][license-url]
7
8
  [![Downloads][downloads-image]][downloads-url]
8
9
 
9
- [![npm badge][11]][1]
10
+ [![npm badge][npm-badge-png]][package-url]
10
11
 
11
12
  A querystring parsing and stringifying library with some added security.
12
13
 
@@ -287,6 +288,17 @@ assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
287
288
  ```
288
289
  (_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
289
290
 
291
+ ### Parsing primitive/scalar values (numbers, booleans, null, etc)
292
+
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).
294
+
295
+ ```javascript
296
+ var primitiveValues = qs.parse('a=15&b=true&c=null');
297
+ assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
298
+ ```
299
+
300
+ If you wish to auto-convert values which look like numbers, booleans, and other values into their primitive counterparts, you can use the [query-types Express JS middleware](https://github.com/xpepermint/query-types) which will auto-convert all request query parameters.
301
+
290
302
  ### Stringifying
291
303
 
292
304
  [](#preventEval)
@@ -594,18 +606,18 @@ Available as part of the Tidelift Subscription
594
606
 
595
607
  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)
596
608
 
597
- [1]: https://npmjs.org/package/qs
598
- [2]: http://versionbadg.es/ljharb/qs.svg
599
- [3]: https://api.travis-ci.org/ljharb/qs.svg
600
- [4]: https://travis-ci.org/ljharb/qs
601
- [5]: https://david-dm.org/ljharb/qs.svg
602
- [6]: https://david-dm.org/ljharb/qs
603
- [7]: https://david-dm.org/ljharb/qs/dev-status.svg
604
- [8]: https://david-dm.org/ljharb/qs?type=dev
605
- [9]: https://ci.testling.com/ljharb/qs.png
606
- [10]: https://ci.testling.com/ljharb/qs
607
- [11]: https://nodei.co/npm/qs.png?downloads=true&stars=true
608
- [license-image]: http://img.shields.io/npm/l/qs.svg
609
+ [package-url]: https://npmjs.org/package/qs
610
+ [npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
611
+ [deps-svg]: https://david-dm.org/ljharb/qs.svg
612
+ [deps-url]: https://david-dm.org/ljharb/qs
613
+ [dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
614
+ [dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
615
+ [npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
616
+ [license-image]: https://img.shields.io/npm/l/qs.svg
609
617
  [license-url]: LICENSE
610
- [downloads-image]: http://img.shields.io/npm/dm/qs.svg
611
- [downloads-url]: http://npm-stat.com/charts.html?package=qs
618
+ [downloads-image]: https://img.shields.io/npm/dm/qs.svg
619
+ [downloads-url]: https://npm-stat.com/charts.html?package=qs
620
+ [codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
621
+ [codecov-url]: https://app.codecov.io/gh/ljharb/qs/
622
+ [actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
623
+ [actions-url]: https://github.com/ljharb/qs/actions
package/dist/qs.js CHANGED
@@ -175,7 +175,7 @@ var parseObject = function (chain, val, options, valuesParsed) {
175
175
  ) {
176
176
  obj = [];
177
177
  obj[index] = leaf;
178
- } else {
178
+ } else if (cleanRoot !== '__proto__') {
179
179
  obj[cleanRoot] = leaf;
180
180
  }
181
181
  }
@@ -323,6 +323,7 @@ var arrayPrefixGenerators = {
323
323
  };
324
324
 
325
325
  var isArray = Array.isArray;
326
+ var split = String.prototype.split;
326
327
  var push = Array.prototype.push;
327
328
  var pushToArray = function (arr, valueOrArray) {
328
329
  push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
@@ -359,6 +360,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
359
360
  || typeof v === 'bigint';
360
361
  };
361
362
 
363
+ var sentinel = {};
364
+
362
365
  var stringify = function stringify(
363
366
  object,
364
367
  prefix,
@@ -378,8 +381,23 @@ var stringify = function stringify(
378
381
  ) {
379
382
  var obj = object;
380
383
 
381
- if (sideChannel.has(object)) {
382
- throw new RangeError('Cyclic object value');
384
+ var tmpSc = sideChannel;
385
+ var step = 0;
386
+ var findFlag = false;
387
+ while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
388
+ // Where object last appeared in the ref tree
389
+ var pos = tmpSc.get(object);
390
+ step += 1;
391
+ if (typeof pos !== 'undefined') {
392
+ if (pos === step) {
393
+ throw new RangeError('Cyclic object value');
394
+ } else {
395
+ findFlag = true; // Break while
396
+ }
397
+ }
398
+ if (typeof tmpSc.get(sentinel) === 'undefined') {
399
+ step = 0;
400
+ }
383
401
  }
384
402
 
385
403
  if (typeof filter === 'function') {
@@ -406,6 +424,14 @@ var stringify = function stringify(
406
424
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
407
425
  if (encoder) {
408
426
  var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
427
+ if (generateArrayPrefix === 'comma' && encodeValuesOnly) {
428
+ var valuesArray = split.call(String(obj), ',');
429
+ var valuesJoined = '';
430
+ for (var i = 0; i < valuesArray.length; ++i) {
431
+ valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset, 'value', format));
432
+ }
433
+ return [formatter(keyValue) + (i === 1 ? '[]' : '') + '=' + valuesJoined];
434
+ }
409
435
  return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
410
436
  }
411
437
  return [formatter(prefix) + '=' + formatter(String(obj))];
@@ -420,7 +446,7 @@ var stringify = function stringify(
420
446
  var objKeys;
421
447
  if (generateArrayPrefix === 'comma' && isArray(obj)) {
422
448
  // we need to join elements in
423
- objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : undefined }];
449
+ objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
424
450
  } else if (isArray(filter)) {
425
451
  objKeys = filter;
426
452
  } else {
@@ -428,9 +454,9 @@ var stringify = function stringify(
428
454
  objKeys = sort ? keys.sort(sort) : keys;
429
455
  }
430
456
 
431
- for (var i = 0; i < objKeys.length; ++i) {
432
- var key = objKeys[i];
433
- var value = typeof key === 'object' && key.value !== undefined ? key.value : obj[key];
457
+ for (var j = 0; j < objKeys.length; ++j) {
458
+ var key = objKeys[j];
459
+ var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
434
460
 
435
461
  if (skipNulls && value === null) {
436
462
  continue;
@@ -440,7 +466,9 @@ var stringify = function stringify(
440
466
  ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
441
467
  : prefix + (allowDots ? '.' + key : '[' + key + ']');
442
468
 
443
- sideChannel.set(object, true);
469
+ sideChannel.set(object, step);
470
+ var valueSideChannel = getSideChannel();
471
+ valueSideChannel.set(sentinel, sideChannel);
444
472
  pushToArray(values, stringify(
445
473
  value,
446
474
  keyPrefix,
@@ -456,7 +484,7 @@ var stringify = function stringify(
456
484
  formatter,
457
485
  encodeValuesOnly,
458
486
  charset,
459
- sideChannel
487
+ valueSideChannel
460
488
  ));
461
489
  }
462
490
 
@@ -468,7 +496,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
468
496
  return defaults;
469
497
  }
470
498
 
471
- if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
499
+ if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
472
500
  throw new TypeError('Encoder has to be a function.');
473
501
  }
474
502
 
@@ -772,6 +800,7 @@ var encode = function encode(str, defaultEncoder, charset, kind, format) {
772
800
 
773
801
  i += 1;
774
802
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
803
+ /* eslint operator-linebreak: [2, "before"] */
775
804
  out += hexTable[0xF0 | (c >> 18)]
776
805
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
777
806
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
@@ -1385,17 +1414,64 @@ var hasWeakMap = typeof WeakMap === 'function' && WeakMap.prototype;
1385
1414
  var weakMapHas = hasWeakMap ? WeakMap.prototype.has : null;
1386
1415
  var hasWeakSet = typeof WeakSet === 'function' && WeakSet.prototype;
1387
1416
  var weakSetHas = hasWeakSet ? WeakSet.prototype.has : null;
1417
+ var hasWeakRef = typeof WeakRef === 'function' && WeakRef.prototype;
1418
+ var weakRefDeref = hasWeakRef ? WeakRef.prototype.deref : null;
1388
1419
  var booleanValueOf = Boolean.prototype.valueOf;
1389
1420
  var objectToString = Object.prototype.toString;
1390
1421
  var functionToString = Function.prototype.toString;
1391
- var match = String.prototype.match;
1422
+ var $match = String.prototype.match;
1423
+ var $slice = String.prototype.slice;
1424
+ var $replace = String.prototype.replace;
1425
+ var $toUpperCase = String.prototype.toUpperCase;
1426
+ var $toLowerCase = String.prototype.toLowerCase;
1427
+ var $test = RegExp.prototype.test;
1428
+ var $concat = Array.prototype.concat;
1429
+ var $join = Array.prototype.join;
1430
+ var $arrSlice = Array.prototype.slice;
1431
+ var $floor = Math.floor;
1392
1432
  var bigIntValueOf = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null;
1393
1433
  var gOPS = Object.getOwnPropertySymbols;
1394
- var symToString = typeof Symbol === 'function' ? Symbol.prototype.toString : null;
1434
+ var symToString = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol.prototype.toString : null;
1435
+ var hasShammedSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'object';
1436
+ // ie, `has-tostringtag/shams
1437
+ var toStringTag = typeof Symbol === 'function' && Symbol.toStringTag && (typeof Symbol.toStringTag === hasShammedSymbols ? 'object' : 'symbol')
1438
+ ? Symbol.toStringTag
1439
+ : null;
1395
1440
  var isEnumerable = Object.prototype.propertyIsEnumerable;
1396
1441
 
1397
- var inspectCustom = require('./util.inspect').custom;
1398
- var inspectSymbol = inspectCustom && isSymbol(inspectCustom) ? inspectCustom : null;
1442
+ var gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPrototypeOf) || (
1443
+ [].__proto__ === Array.prototype // eslint-disable-line no-proto
1444
+ ? function (O) {
1445
+ return O.__proto__; // eslint-disable-line no-proto
1446
+ }
1447
+ : null
1448
+ );
1449
+
1450
+ function addNumericSeparator(num, str) {
1451
+ if (
1452
+ num === Infinity
1453
+ || num === -Infinity
1454
+ || num !== num
1455
+ || (num && num > -1000 && num < 1000)
1456
+ || $test.call(/e/, str)
1457
+ ) {
1458
+ return str;
1459
+ }
1460
+ var sepRegex = /[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;
1461
+ if (typeof num === 'number') {
1462
+ var int = num < 0 ? -$floor(-num) : $floor(num); // trunc(num)
1463
+ if (int !== num) {
1464
+ var intStr = String(int);
1465
+ var dec = $slice.call(str, intStr.length + 1);
1466
+ return $replace.call(intStr, sepRegex, '$&_') + '.' + $replace.call($replace.call(dec, /([0-9]{3})/g, '$&_'), /_$/, '');
1467
+ }
1468
+ }
1469
+ return $replace.call(str, sepRegex, '$&_');
1470
+ }
1471
+
1472
+ var utilInspect = require('./util.inspect');
1473
+ var inspectCustom = utilInspect.custom;
1474
+ var inspectSymbol = isSymbol(inspectCustom) ? inspectCustom : null;
1399
1475
 
1400
1476
  module.exports = function inspect_(obj, options, depth, seen) {
1401
1477
  var opts = options || {};
@@ -1412,8 +1488,8 @@ module.exports = function inspect_(obj, options, depth, seen) {
1412
1488
  throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');
1413
1489
  }
1414
1490
  var customInspect = has(opts, 'customInspect') ? opts.customInspect : true;
1415
- if (typeof customInspect !== 'boolean') {
1416
- throw new TypeError('option "customInspect", if provided, must be `true` or `false`');
1491
+ if (typeof customInspect !== 'boolean' && customInspect !== 'symbol') {
1492
+ throw new TypeError('option "customInspect", if provided, must be `true`, `false`, or `\'symbol\'`');
1417
1493
  }
1418
1494
 
1419
1495
  if (
@@ -1422,8 +1498,12 @@ module.exports = function inspect_(obj, options, depth, seen) {
1422
1498
  && opts.indent !== '\t'
1423
1499
  && !(parseInt(opts.indent, 10) === opts.indent && opts.indent > 0)
1424
1500
  ) {
1425
- throw new TypeError('options "indent" must be "\\t", an integer > 0, or `null`');
1501
+ throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');
1426
1502
  }
1503
+ if (has(opts, 'numericSeparator') && typeof opts.numericSeparator !== 'boolean') {
1504
+ throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');
1505
+ }
1506
+ var numericSeparator = opts.numericSeparator;
1427
1507
 
1428
1508
  if (typeof obj === 'undefined') {
1429
1509
  return 'undefined';
@@ -1442,10 +1522,12 @@ module.exports = function inspect_(obj, options, depth, seen) {
1442
1522
  if (obj === 0) {
1443
1523
  return Infinity / obj > 0 ? '0' : '-0';
1444
1524
  }
1445
- return String(obj);
1525
+ var str = String(obj);
1526
+ return numericSeparator ? addNumericSeparator(obj, str) : str;
1446
1527
  }
1447
1528
  if (typeof obj === 'bigint') {
1448
- return String(obj) + 'n';
1529
+ var bigIntStr = String(obj) + 'n';
1530
+ return numericSeparator ? addNumericSeparator(obj, bigIntStr) : bigIntStr;
1449
1531
  }
1450
1532
 
1451
1533
  var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth;
@@ -1464,7 +1546,7 @@ module.exports = function inspect_(obj, options, depth, seen) {
1464
1546
 
1465
1547
  function inspect(value, from, noIndent) {
1466
1548
  if (from) {
1467
- seen = seen.slice();
1549
+ seen = $arrSlice.call(seen);
1468
1550
  seen.push(from);
1469
1551
  }
1470
1552
  if (noIndent) {
@@ -1479,24 +1561,24 @@ module.exports = function inspect_(obj, options, depth, seen) {
1479
1561
  return inspect_(value, opts, depth + 1, seen);
1480
1562
  }
1481
1563
 
1482
- if (typeof obj === 'function') {
1564
+ if (typeof obj === 'function' && !isRegExp(obj)) { // in older engines, regexes are callable
1483
1565
  var name = nameOf(obj);
1484
1566
  var keys = arrObjKeys(obj, inspect);
1485
- return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + keys.join(', ') + ' }' : '');
1567
+ return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + $join.call(keys, ', ') + ' }' : '');
1486
1568
  }
1487
1569
  if (isSymbol(obj)) {
1488
- var symString = symToString.call(obj);
1489
- return typeof obj === 'object' ? markBoxed(symString) : symString;
1570
+ var symString = hasShammedSymbols ? $replace.call(String(obj), /^(Symbol\(.*\))_[^)]*$/, '$1') : symToString.call(obj);
1571
+ return typeof obj === 'object' && !hasShammedSymbols ? markBoxed(symString) : symString;
1490
1572
  }
1491
1573
  if (isElement(obj)) {
1492
- var s = '<' + String(obj.nodeName).toLowerCase();
1574
+ var s = '<' + $toLowerCase.call(String(obj.nodeName));
1493
1575
  var attrs = obj.attributes || [];
1494
1576
  for (var i = 0; i < attrs.length; i++) {
1495
1577
  s += ' ' + attrs[i].name + '=' + wrapQuotes(quote(attrs[i].value), 'double', opts);
1496
1578
  }
1497
1579
  s += '>';
1498
1580
  if (obj.childNodes && obj.childNodes.length) { s += '...'; }
1499
- s += '</' + String(obj.nodeName).toLowerCase() + '>';
1581
+ s += '</' + $toLowerCase.call(String(obj.nodeName)) + '>';
1500
1582
  return s;
1501
1583
  }
1502
1584
  if (isArray(obj)) {
@@ -1505,17 +1587,20 @@ module.exports = function inspect_(obj, options, depth, seen) {
1505
1587
  if (indent && !singleLineValues(xs)) {
1506
1588
  return '[' + indentedJoin(xs, indent) + ']';
1507
1589
  }
1508
- return '[ ' + xs.join(', ') + ' ]';
1590
+ return '[ ' + $join.call(xs, ', ') + ' ]';
1509
1591
  }
1510
1592
  if (isError(obj)) {
1511
1593
  var parts = arrObjKeys(obj, inspect);
1594
+ if (!('cause' in Error.prototype) && 'cause' in obj && !isEnumerable.call(obj, 'cause')) {
1595
+ return '{ [' + String(obj) + '] ' + $join.call($concat.call('[cause]: ' + inspect(obj.cause), parts), ', ') + ' }';
1596
+ }
1512
1597
  if (parts.length === 0) { return '[' + String(obj) + ']'; }
1513
- return '{ [' + String(obj) + '] ' + parts.join(', ') + ' }';
1598
+ return '{ [' + String(obj) + '] ' + $join.call(parts, ', ') + ' }';
1514
1599
  }
1515
1600
  if (typeof obj === 'object' && customInspect) {
1516
- if (inspectSymbol && typeof obj[inspectSymbol] === 'function') {
1517
- return obj[inspectSymbol]();
1518
- } else if (typeof obj.inspect === 'function') {
1601
+ if (inspectSymbol && typeof obj[inspectSymbol] === 'function' && utilInspect) {
1602
+ return utilInspect(obj, { depth: maxDepth - depth });
1603
+ } else if (customInspect !== 'symbol' && typeof obj.inspect === 'function') {
1519
1604
  return obj.inspect();
1520
1605
  }
1521
1606
  }
@@ -1539,6 +1624,9 @@ module.exports = function inspect_(obj, options, depth, seen) {
1539
1624
  if (isWeakSet(obj)) {
1540
1625
  return weakCollectionOf('WeakSet');
1541
1626
  }
1627
+ if (isWeakRef(obj)) {
1628
+ return weakCollectionOf('WeakRef');
1629
+ }
1542
1630
  if (isNumber(obj)) {
1543
1631
  return markBoxed(inspect(Number(obj)));
1544
1632
  }
@@ -1553,11 +1641,16 @@ module.exports = function inspect_(obj, options, depth, seen) {
1553
1641
  }
1554
1642
  if (!isDate(obj) && !isRegExp(obj)) {
1555
1643
  var ys = arrObjKeys(obj, inspect);
1556
- if (ys.length === 0) { return '{}'; }
1644
+ var isPlainObject = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object;
1645
+ var protoTag = obj instanceof Object ? '' : 'null prototype';
1646
+ var stringTag = !isPlainObject && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? 'Object' : '';
1647
+ var constructorTag = isPlainObject || typeof obj.constructor !== 'function' ? '' : obj.constructor.name ? obj.constructor.name + ' ' : '';
1648
+ var tag = constructorTag + (stringTag || protoTag ? '[' + $join.call($concat.call([], stringTag || [], protoTag || []), ': ') + '] ' : '');
1649
+ if (ys.length === 0) { return tag + '{}'; }
1557
1650
  if (indent) {
1558
- return '{' + indentedJoin(ys, indent) + '}';
1651
+ return tag + '{' + indentedJoin(ys, indent) + '}';
1559
1652
  }
1560
- return '{ ' + ys.join(', ') + ' }';
1653
+ return tag + '{ ' + $join.call(ys, ', ') + ' }';
1561
1654
  }
1562
1655
  return String(obj);
1563
1656
  };
@@ -1568,18 +1661,45 @@ function wrapQuotes(s, defaultStyle, opts) {
1568
1661
  }
1569
1662
 
1570
1663
  function quote(s) {
1571
- return String(s).replace(/"/g, '&quot;');
1664
+ return $replace.call(String(s), /"/g, '&quot;');
1665
+ }
1666
+
1667
+ function isArray(obj) { return toStr(obj) === '[object Array]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1668
+ function isDate(obj) { return toStr(obj) === '[object Date]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1669
+ function isRegExp(obj) { return toStr(obj) === '[object RegExp]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1670
+ function isError(obj) { return toStr(obj) === '[object Error]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1671
+ function isString(obj) { return toStr(obj) === '[object String]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1672
+ function isNumber(obj) { return toStr(obj) === '[object Number]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1673
+ function isBoolean(obj) { return toStr(obj) === '[object Boolean]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); }
1674
+
1675
+ // Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives
1676
+ function isSymbol(obj) {
1677
+ if (hasShammedSymbols) {
1678
+ return obj && typeof obj === 'object' && obj instanceof Symbol;
1679
+ }
1680
+ if (typeof obj === 'symbol') {
1681
+ return true;
1682
+ }
1683
+ if (!obj || typeof obj !== 'object' || !symToString) {
1684
+ return false;
1685
+ }
1686
+ try {
1687
+ symToString.call(obj);
1688
+ return true;
1689
+ } catch (e) {}
1690
+ return false;
1572
1691
  }
1573
1692
 
1574
- function isArray(obj) { return toStr(obj) === '[object Array]'; }
1575
- function isDate(obj) { return toStr(obj) === '[object Date]'; }
1576
- function isRegExp(obj) { return toStr(obj) === '[object RegExp]'; }
1577
- function isError(obj) { return toStr(obj) === '[object Error]'; }
1578
- function isSymbol(obj) { return toStr(obj) === '[object Symbol]'; }
1579
- function isString(obj) { return toStr(obj) === '[object String]'; }
1580
- function isNumber(obj) { return toStr(obj) === '[object Number]'; }
1581
- function isBigInt(obj) { return toStr(obj) === '[object BigInt]'; }
1582
- function isBoolean(obj) { return toStr(obj) === '[object Boolean]'; }
1693
+ function isBigInt(obj) {
1694
+ if (!obj || typeof obj !== 'object' || !bigIntValueOf) {
1695
+ return false;
1696
+ }
1697
+ try {
1698
+ bigIntValueOf.call(obj);
1699
+ return true;
1700
+ } catch (e) {}
1701
+ return false;
1702
+ }
1583
1703
 
1584
1704
  var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; };
1585
1705
  function has(obj, key) {
@@ -1592,7 +1712,7 @@ function toStr(obj) {
1592
1712
 
1593
1713
  function nameOf(f) {
1594
1714
  if (f.name) { return f.name; }
1595
- var m = match.call(functionToString.call(f), /^function\s*([\w$]+)/);
1715
+ var m = $match.call(functionToString.call(f), /^function\s*([\w$]+)/);
1596
1716
  if (m) { return m[1]; }
1597
1717
  return null;
1598
1718
  }
@@ -1637,6 +1757,17 @@ function isWeakMap(x) {
1637
1757
  return false;
1638
1758
  }
1639
1759
 
1760
+ function isWeakRef(x) {
1761
+ if (!weakRefDeref || !x || typeof x !== 'object') {
1762
+ return false;
1763
+ }
1764
+ try {
1765
+ weakRefDeref.call(x);
1766
+ return true;
1767
+ } catch (e) {}
1768
+ return false;
1769
+ }
1770
+
1640
1771
  function isSet(x) {
1641
1772
  if (!setSize || !x || typeof x !== 'object') {
1642
1773
  return false;
@@ -1681,10 +1812,10 @@ function inspectString(str, opts) {
1681
1812
  if (str.length > opts.maxStringLength) {
1682
1813
  var remaining = str.length - opts.maxStringLength;
1683
1814
  var trailer = '... ' + remaining + ' more character' + (remaining > 1 ? 's' : '');
1684
- return inspectString(str.slice(0, opts.maxStringLength), opts) + trailer;
1815
+ return inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer;
1685
1816
  }
1686
1817
  // eslint-disable-next-line no-control-regex
1687
- var s = str.replace(/(['\\])/g, '\\$1').replace(/[\x00-\x1f]/g, lowbyte);
1818
+ var s = $replace.call($replace.call(str, /(['\\])/g, '\\$1'), /[\x00-\x1f]/g, lowbyte);
1688
1819
  return wrapQuotes(s, 'single', opts);
1689
1820
  }
1690
1821
 
@@ -1698,7 +1829,7 @@ function lowbyte(c) {
1698
1829
  13: 'r'
1699
1830
  }[n];
1700
1831
  if (x) { return '\\' + x; }
1701
- return '\\x' + (n < 0x10 ? '0' : '') + n.toString(16).toUpperCase();
1832
+ return '\\x' + (n < 0x10 ? '0' : '') + $toUpperCase.call(n.toString(16));
1702
1833
  }
1703
1834
 
1704
1835
  function markBoxed(str) {
@@ -1710,7 +1841,7 @@ function weakCollectionOf(type) {
1710
1841
  }
1711
1842
 
1712
1843
  function collectionOf(type, size, entries, indent) {
1713
- var joinedEntries = indent ? indentedJoin(entries, indent) : entries.join(', ');
1844
+ var joinedEntries = indent ? indentedJoin(entries, indent) : $join.call(entries, ', ');
1714
1845
  return type + ' (' + size + ') {' + joinedEntries + '}';
1715
1846
  }
1716
1847
 
@@ -1728,20 +1859,20 @@ function getIndent(opts, depth) {
1728
1859
  if (opts.indent === '\t') {
1729
1860
  baseIndent = '\t';
1730
1861
  } else if (typeof opts.indent === 'number' && opts.indent > 0) {
1731
- baseIndent = Array(opts.indent + 1).join(' ');
1862
+ baseIndent = $join.call(Array(opts.indent + 1), ' ');
1732
1863
  } else {
1733
1864
  return null;
1734
1865
  }
1735
1866
  return {
1736
1867
  base: baseIndent,
1737
- prev: Array(depth + 1).join(baseIndent)
1868
+ prev: $join.call(Array(depth + 1), baseIndent)
1738
1869
  };
1739
1870
  }
1740
1871
 
1741
1872
  function indentedJoin(xs, indent) {
1742
1873
  if (xs.length === 0) { return ''; }
1743
1874
  var lineJoiner = '\n' + indent.prev + indent.base;
1744
- return lineJoiner + xs.join(',' + lineJoiner) + '\n' + indent.prev;
1875
+ return lineJoiner + $join.call(xs, ',' + lineJoiner) + '\n' + indent.prev;
1745
1876
  }
1746
1877
 
1747
1878
  function arrObjKeys(obj, inspect) {
@@ -1753,17 +1884,28 @@ function arrObjKeys(obj, inspect) {
1753
1884
  xs[i] = has(obj, i) ? inspect(obj[i], obj) : '';
1754
1885
  }
1755
1886
  }
1887
+ var syms = typeof gOPS === 'function' ? gOPS(obj) : [];
1888
+ var symMap;
1889
+ if (hasShammedSymbols) {
1890
+ symMap = {};
1891
+ for (var k = 0; k < syms.length; k++) {
1892
+ symMap['$' + syms[k]] = syms[k];
1893
+ }
1894
+ }
1895
+
1756
1896
  for (var key in obj) { // eslint-disable-line no-restricted-syntax
1757
1897
  if (!has(obj, key)) { continue; } // eslint-disable-line no-restricted-syntax, no-continue
1758
1898
  if (isArr && String(Number(key)) === key && key < obj.length) { continue; } // eslint-disable-line no-restricted-syntax, no-continue
1759
- if ((/[^\w$]/).test(key)) {
1899
+ if (hasShammedSymbols && symMap['$' + key] instanceof Symbol) {
1900
+ // this is to prevent shammed Symbols, which are stored as strings, from being included in the string key section
1901
+ continue; // eslint-disable-line no-restricted-syntax, no-continue
1902
+ } else if ($test.call(/[^\w$]/, key)) {
1760
1903
  xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj));
1761
1904
  } else {
1762
1905
  xs.push(key + ': ' + inspect(obj[key], obj));
1763
1906
  }
1764
1907
  }
1765
1908
  if (typeof gOPS === 'function') {
1766
- var syms = gOPS(obj);
1767
1909
  for (var j = 0; j < syms.length; j++) {
1768
1910
  if (isEnumerable.call(obj, syms[j])) {
1769
1911
  xs.push('[' + inspect(syms[j]) + ']: ' + inspect(obj[syms[j]], obj));
package/lib/parse.js CHANGED
@@ -136,7 +136,7 @@ var parseObject = function (chain, val, options, valuesParsed) {
136
136
  ) {
137
137
  obj = [];
138
138
  obj[index] = leaf;
139
- } else {
139
+ } else if (cleanRoot !== '__proto__') {
140
140
  obj[cleanRoot] = leaf;
141
141
  }
142
142
  }
package/lib/stringify.js CHANGED
@@ -19,6 +19,7 @@ var arrayPrefixGenerators = {
19
19
  };
20
20
 
21
21
  var isArray = Array.isArray;
22
+ var split = String.prototype.split;
22
23
  var push = Array.prototype.push;
23
24
  var pushToArray = function (arr, valueOrArray) {
24
25
  push.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
@@ -55,6 +56,8 @@ var isNonNullishPrimitive = function isNonNullishPrimitive(v) {
55
56
  || typeof v === 'bigint';
56
57
  };
57
58
 
59
+ var sentinel = {};
60
+
58
61
  var stringify = function stringify(
59
62
  object,
60
63
  prefix,
@@ -74,8 +77,23 @@ var stringify = function stringify(
74
77
  ) {
75
78
  var obj = object;
76
79
 
77
- if (sideChannel.has(object)) {
78
- throw new RangeError('Cyclic object value');
80
+ var tmpSc = sideChannel;
81
+ var step = 0;
82
+ var findFlag = false;
83
+ while ((tmpSc = tmpSc.get(sentinel)) !== void undefined && !findFlag) {
84
+ // Where object last appeared in the ref tree
85
+ var pos = tmpSc.get(object);
86
+ step += 1;
87
+ if (typeof pos !== 'undefined') {
88
+ if (pos === step) {
89
+ throw new RangeError('Cyclic object value');
90
+ } else {
91
+ findFlag = true; // Break while
92
+ }
93
+ }
94
+ if (typeof tmpSc.get(sentinel) === 'undefined') {
95
+ step = 0;
96
+ }
79
97
  }
80
98
 
81
99
  if (typeof filter === 'function') {
@@ -102,6 +120,14 @@ var stringify = function stringify(
102
120
  if (isNonNullishPrimitive(obj) || utils.isBuffer(obj)) {
103
121
  if (encoder) {
104
122
  var keyValue = encodeValuesOnly ? prefix : encoder(prefix, defaults.encoder, charset, 'key', format);
123
+ if (generateArrayPrefix === 'comma' && encodeValuesOnly) {
124
+ var valuesArray = split.call(String(obj), ',');
125
+ var valuesJoined = '';
126
+ for (var i = 0; i < valuesArray.length; ++i) {
127
+ valuesJoined += (i === 0 ? '' : ',') + formatter(encoder(valuesArray[i], defaults.encoder, charset, 'value', format));
128
+ }
129
+ return [formatter(keyValue) + (i === 1 ? '[]' : '') + '=' + valuesJoined];
130
+ }
105
131
  return [formatter(keyValue) + '=' + formatter(encoder(obj, defaults.encoder, charset, 'value', format))];
106
132
  }
107
133
  return [formatter(prefix) + '=' + formatter(String(obj))];
@@ -116,7 +142,7 @@ var stringify = function stringify(
116
142
  var objKeys;
117
143
  if (generateArrayPrefix === 'comma' && isArray(obj)) {
118
144
  // we need to join elements in
119
- objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : undefined }];
145
+ objKeys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
120
146
  } else if (isArray(filter)) {
121
147
  objKeys = filter;
122
148
  } else {
@@ -124,9 +150,9 @@ var stringify = function stringify(
124
150
  objKeys = sort ? keys.sort(sort) : keys;
125
151
  }
126
152
 
127
- for (var i = 0; i < objKeys.length; ++i) {
128
- var key = objKeys[i];
129
- var value = typeof key === 'object' && key.value !== undefined ? key.value : obj[key];
153
+ for (var j = 0; j < objKeys.length; ++j) {
154
+ var key = objKeys[j];
155
+ var value = typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key];
130
156
 
131
157
  if (skipNulls && value === null) {
132
158
  continue;
@@ -136,8 +162,9 @@ var stringify = function stringify(
136
162
  ? typeof generateArrayPrefix === 'function' ? generateArrayPrefix(prefix, key) : prefix
137
163
  : prefix + (allowDots ? '.' + key : '[' + key + ']');
138
164
 
139
- sideChannel.set(object, true);
165
+ sideChannel.set(object, step);
140
166
  var valueSideChannel = getSideChannel();
167
+ valueSideChannel.set(sentinel, sideChannel);
141
168
  pushToArray(values, stringify(
142
169
  value,
143
170
  keyPrefix,
@@ -165,7 +192,7 @@ var normalizeStringifyOptions = function normalizeStringifyOptions(opts) {
165
192
  return defaults;
166
193
  }
167
194
 
168
- if (opts.encoder !== null && opts.encoder !== undefined && typeof opts.encoder !== 'function') {
195
+ if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
169
196
  throw new TypeError('Encoder has to be a function.');
170
197
  }
171
198
 
package/lib/utils.js CHANGED
@@ -177,6 +177,7 @@ var encode = function encode(str, defaultEncoder, charset, kind, format) {
177
177
 
178
178
  i += 1;
179
179
  c = 0x10000 + (((c & 0x3FF) << 10) | (string.charCodeAt(i) & 0x3FF));
180
+ /* eslint operator-linebreak: [2, "before"] */
180
181
  out += hexTable[0xF0 | (c >> 18)]
181
182
  + hexTable[0x80 | ((c >> 12) & 0x3F)]
182
183
  + hexTable[0x80 | ((c >> 6) & 0x3F)]
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.10.1",
5
+ "version": "6.10.4",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/ljharb/qs.git"
@@ -33,40 +33,45 @@
33
33
  "side-channel": "^1.0.4"
34
34
  },
35
35
  "devDependencies": {
36
- "@ljharb/eslint-config": "^17.5.1",
37
- "aud": "^1.1.4",
36
+ "@ljharb/eslint-config": "^21.0.0",
37
+ "aud": "^2.0.0",
38
38
  "browserify": "^16.5.2",
39
39
  "eclint": "^2.8.1",
40
- "eslint": "^7.22.0",
40
+ "eslint": "=8.8.0",
41
41
  "evalmd": "^0.0.19",
42
42
  "for-each": "^0.3.3",
43
- "has-symbols": "^1.0.2",
43
+ "has-symbols": "^1.0.3",
44
44
  "iconv-lite": "^0.5.1",
45
45
  "in-publish": "^2.0.1",
46
46
  "mkdirp": "^0.5.5",
47
+ "npmignore": "^0.3.0",
47
48
  "nyc": "^10.3.2",
48
- "object-inspect": "^1.9.0",
49
+ "object-inspect": "^1.12.2",
49
50
  "qs-iconv": "^1.0.4",
50
- "safe-publish-latest": "^1.1.4",
51
+ "safe-publish-latest": "^2.0.0",
51
52
  "safer-buffer": "^2.1.2",
52
- "tape": "^5.2.2"
53
+ "tape": "^5.5.3"
53
54
  },
54
55
  "scripts": {
55
- "prepublish": "safe-publish-latest && (not-in-publish || npm run dist)",
56
+ "prepack": "npmignore --auto --commentLines=autogenerated",
57
+ "prepublishOnly": "safe-publish-latest && npm run dist",
58
+ "prepublish": "not-in-publish || npm run prepublishOnly",
56
59
  "pretest": "npm run --silent readme && npm run --silent lint",
57
60
  "test": "npm run tests-only",
58
61
  "tests-only": "nyc tape 'test/**/*.js'",
59
62
  "posttest": "aud --production",
60
63
  "readme": "evalmd README.md",
61
- "postlint": "eclint check * lib/* test/* !dist/*",
62
- "lint": "eslint lib/*.js test/*.js",
64
+ "postlint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git' | grep -v dist/)",
65
+ "lint": "eslint --ext=js,mjs .",
63
66
  "dist": "mkdirp dist && browserify --standalone Qs lib/index.js > dist/qs.js"
64
67
  },
65
68
  "license": "BSD-3-Clause",
66
- "greenkeeper": {
69
+ "publishConfig": {
67
70
  "ignore": [
68
- "iconv-lite",
69
- "mkdirp"
71
+ "!dist/*",
72
+ "bower.json",
73
+ "component.json",
74
+ ".github/workflows"
70
75
  ]
71
76
  }
72
77
  }
package/test/parse.js CHANGED
@@ -140,6 +140,9 @@ test('parse()', function (t) {
140
140
  t.test('limits specific array indices to arrayLimit', function (st) {
141
141
  st.deepEqual(qs.parse('a[20]=a', { arrayLimit: 20 }), { a: ['a'] });
142
142
  st.deepEqual(qs.parse('a[21]=a', { arrayLimit: 20 }), { a: { 21: 'a' } });
143
+
144
+ st.deepEqual(qs.parse('a[20]=a'), { a: ['a'] });
145
+ st.deepEqual(qs.parse('a[21]=a'), { a: { 21: 'a' } });
143
146
  st.end();
144
147
  });
145
148
 
@@ -378,6 +381,7 @@ test('parse()', function (t) {
378
381
  st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
379
382
  st.deepEqual(qs.parse('foo=bar', { ignoreQueryPrefix: true }), { foo: 'bar' });
380
383
  st.deepEqual(qs.parse('?foo=bar', { ignoreQueryPrefix: false }), { '?foo': 'bar' });
384
+
381
385
  st.end();
382
386
  });
383
387
 
@@ -406,6 +410,16 @@ test('parse()', function (t) {
406
410
  st.deepEqual(qs.parse('foo=', { comma: true }), { foo: '' });
407
411
  st.deepEqual(qs.parse('foo', { comma: true }), { foo: '' });
408
412
  st.deepEqual(qs.parse('foo', { comma: true, strictNullHandling: true }), { foo: null });
413
+
414
+ // test cases inversed from from stringify tests
415
+ st.deepEqual(qs.parse('a[0]=c'), { a: ['c'] });
416
+ st.deepEqual(qs.parse('a[]=c'), { a: ['c'] });
417
+ st.deepEqual(qs.parse('a[]=c', { comma: true }), { a: ['c'] });
418
+
419
+ st.deepEqual(qs.parse('a[0]=c&a[1]=d'), { a: ['c', 'd'] });
420
+ st.deepEqual(qs.parse('a[]=c&a[]=d'), { a: ['c', 'd'] });
421
+ st.deepEqual(qs.parse('a=c,d', { comma: true }), { a: ['c', 'd'] });
422
+
409
423
  st.end();
410
424
  });
411
425
 
@@ -629,6 +643,66 @@ test('parse()', function (t) {
629
643
  st.end();
630
644
  });
631
645
 
646
+ t.test('dunder proto is ignored', function (st) {
647
+ var payload = 'categories[__proto__]=login&categories[__proto__]&categories[length]=42';
648
+ var result = qs.parse(payload, { allowPrototypes: true });
649
+
650
+ st.deepEqual(
651
+ result,
652
+ {
653
+ categories: {
654
+ length: '42'
655
+ }
656
+ },
657
+ 'silent [[Prototype]] payload'
658
+ );
659
+
660
+ var plainResult = qs.parse(payload, { allowPrototypes: true, plainObjects: true });
661
+
662
+ st.deepEqual(
663
+ plainResult,
664
+ {
665
+ __proto__: null,
666
+ categories: {
667
+ __proto__: null,
668
+ length: '42'
669
+ }
670
+ },
671
+ 'silent [[Prototype]] payload: plain objects'
672
+ );
673
+
674
+ var query = qs.parse('categories[__proto__]=cats&categories[__proto__]=dogs&categories[some][json]=toInject', { allowPrototypes: true });
675
+
676
+ st.notOk(Array.isArray(query.categories), 'is not an array');
677
+ st.notOk(query.categories instanceof Array, 'is not instanceof an array');
678
+ st.deepEqual(query.categories, { some: { json: 'toInject' } });
679
+ st.equal(JSON.stringify(query.categories), '{"some":{"json":"toInject"}}', 'stringifies as a non-array');
680
+
681
+ st.deepEqual(
682
+ qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true }),
683
+ {
684
+ foo: {
685
+ bar: 'stuffs'
686
+ }
687
+ },
688
+ 'hidden values'
689
+ );
690
+
691
+ st.deepEqual(
692
+ qs.parse('foo[__proto__][hidden]=value&foo[bar]=stuffs', { allowPrototypes: true, plainObjects: true }),
693
+ {
694
+ __proto__: null,
695
+ foo: {
696
+ __proto__: null,
697
+ bar: 'stuffs'
698
+ }
699
+ },
700
+ 'hidden values: plain objects'
701
+ );
702
+
703
+ st.end();
704
+ });
705
+
632
706
  t.test('can return null objects', { skip: !Object.create }, function (st) {
633
707
  var expected = Object.create(null);
634
708
  expected.a = Object.create(null);
package/test/stringify.js CHANGED
@@ -131,11 +131,25 @@ test('stringify()', function (t) {
131
131
  st.end();
132
132
  });
133
133
 
134
+ t.test('stringifies an array value with one item vs multiple items', function (st) {
135
+ st.equal(qs.stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[0]=c');
136
+ st.equal(qs.stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[]=c');
137
+ st.equal(qs.stringify({ a: ['c'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a[]=c'); // so it parses back as an array
138
+ st.equal(qs.stringify({ a: ['c'] }, { encodeValuesOnly: true }), 'a[0]=c');
139
+
140
+ st.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[0]=c&a[1]=d');
141
+ st.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[]=c&a[]=d');
142
+ st.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a=c,d');
143
+ st.equal(qs.stringify({ a: ['c', 'd'] }, { encodeValuesOnly: true }), 'a[0]=c&a[1]=d');
144
+
145
+ st.end();
146
+ });
147
+
134
148
  t.test('stringifies a nested array value', function (st) {
135
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'indices' }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
136
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'brackets' }), 'a%5Bb%5D%5B%5D=c&a%5Bb%5D%5B%5D=d');
137
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { arrayFormat: 'comma' }), 'a%5Bb%5D=c%2Cd'); // a[b]=c,d
138
- st.equal(qs.stringify({ a: { b: ['c', 'd'] } }), 'a%5Bb%5D%5B0%5D=c&a%5Bb%5D%5B1%5D=d');
149
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'indices' }), 'a[b][0]=c&a[b][1]=d');
150
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'brackets' }), 'a[b][]=c&a[b][]=d');
151
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true, arrayFormat: 'comma' }), 'a[b]=c,d');
152
+ st.equal(qs.stringify({ a: { b: ['c', 'd'] } }, { encodeValuesOnly: true }), 'a[b][0]=c&a[b][1]=d');
139
153
  st.end();
140
154
  });
141
155
 
@@ -143,7 +157,7 @@ test('stringify()', function (t) {
143
157
  st.equal(
144
158
  qs.stringify(
145
159
  { a: { b: ['c', 'd'] } },
146
- { allowDots: true, encode: false, arrayFormat: 'indices' }
160
+ { allowDots: true, encodeValuesOnly: true, arrayFormat: 'indices' }
147
161
  ),
148
162
  'a.b[0]=c&a.b[1]=d',
149
163
  'indices: stringifies with dots + indices'
@@ -151,7 +165,7 @@ test('stringify()', function (t) {
151
165
  st.equal(
152
166
  qs.stringify(
153
167
  { a: { b: ['c', 'd'] } },
154
- { allowDots: true, encode: false, arrayFormat: 'brackets' }
168
+ { allowDots: true, encodeValuesOnly: true, arrayFormat: 'brackets' }
155
169
  ),
156
170
  'a.b[]=c&a.b[]=d',
157
171
  'brackets: stringifies with dots + brackets'
@@ -159,7 +173,7 @@ test('stringify()', function (t) {
159
173
  st.equal(
160
174
  qs.stringify(
161
175
  { a: { b: ['c', 'd'] } },
162
- { allowDots: true, encode: false, arrayFormat: 'comma' }
176
+ { allowDots: true, encodeValuesOnly: true, arrayFormat: 'comma' }
163
177
  ),
164
178
  'a.b=c,d',
165
179
  'comma: stringifies with dots + comma'
@@ -167,7 +181,7 @@ test('stringify()', function (t) {
167
181
  st.equal(
168
182
  qs.stringify(
169
183
  { a: { b: ['c', 'd'] } },
170
- { allowDots: true, encode: false }
184
+ { allowDots: true, encodeValuesOnly: true }
171
185
  ),
172
186
  'a.b[0]=c&a.b[1]=d',
173
187
  'default: stringifies with dots + indices'
@@ -215,17 +229,23 @@ test('stringify()', function (t) {
215
229
 
216
230
  t.test('stringifies an array with mixed objects and primitives', function (st) {
217
231
  st.equal(
218
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'indices' }),
232
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'indices' }),
219
233
  'a[0][b]=1&a[1]=2&a[2]=3',
220
234
  'indices => indices'
221
235
  );
222
236
  st.equal(
223
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false, arrayFormat: 'brackets' }),
237
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'brackets' }),
224
238
  'a[][b]=1&a[]=2&a[]=3',
225
239
  'brackets => brackets'
226
240
  );
227
241
  st.equal(
228
- qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encode: false }),
242
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true, arrayFormat: 'comma' }),
243
+ '???',
244
+ 'brackets => brackets',
245
+ { skip: 'TODO: figure out what this should do' }
246
+ );
247
+ st.equal(
248
+ qs.stringify({ a: [{ b: 1 }, 2, 3] }, { encodeValuesOnly: true }),
229
249
  'a[0][b]=1&a[1]=2&a[2]=3',
230
250
  'default => indices'
231
251
  );
@@ -448,7 +468,7 @@ test('stringify()', function (t) {
448
468
 
449
469
  st['throws'](
450
470
  function () { qs.stringify({ 'foo[bar]': 'baz', 'foo[baz]': a }); },
451
- RangeError,
471
+ /RangeError: Cyclic object value/,
452
472
  'cyclic values throw'
453
473
  );
454
474
 
@@ -458,10 +478,16 @@ test('stringify()', function (t) {
458
478
  circular.a = circular;
459
479
  st['throws'](
460
480
  function () { qs.stringify(circular); },
461
- RangeError,
481
+ /RangeError: Cyclic object value/,
462
482
  'cyclic values throw'
463
483
  );
464
484
 
485
+ var arr = ['a'];
486
+ st.doesNotThrow(
487
+ function () { qs.stringify({ x: arr, y: arr }); },
488
+ 'non-cyclic values do not throw'
489
+ );
490
+
465
491
  st.end();
466
492
  });
467
493
 
@@ -702,16 +728,14 @@ test('stringify()', function (t) {
702
728
  });
703
729
 
704
730
  t.test('Edge cases and unknown formats', function (st) {
705
- ['UFO1234', false, 1234, null, {}, []].forEach(
706
- function (format) {
707
- st['throws'](
708
- function () {
709
- qs.stringify({ a: 'b c' }, { format: format });
710
- },
711
- new TypeError('Unknown format option provided.')
712
- );
713
- }
714
- );
731
+ ['UFO1234', false, 1234, null, {}, []].forEach(function (format) {
732
+ st['throws'](
733
+ function () {
734
+ qs.stringify({ a: 'b c' }, { format: format });
735
+ },
736
+ new TypeError('Unknown format option provided.')
737
+ );
738
+ });
715
739
  st.end();
716
740
  });
717
741
 
@@ -829,7 +853,12 @@ test('stringify()', function (t) {
829
853
  st.equal(qs.stringify(withArray, { encode: false }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, no arrayFormat');
830
854
  st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'bracket' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, bracket');
831
855
  st.equal(qs.stringify(withArray, { encode: false, arrayFormat: 'indices' }), 'a[b][0][c]=d&a[b][0][e]=f', 'array, indices');
832
- st.equal(qs.stringify(obj, { encode: false, arrayFormat: 'comma' }), '???', 'array, comma (pending issue #378)', { skip: true });
856
+ st.equal(
857
+ qs.stringify(withArray, { encode: false, arrayFormat: 'comma' }),
858
+ '???',
859
+ 'array, comma',
860
+ { skip: 'TODO: figure out what this should do' }
861
+ );
833
862
 
834
863
  st.end();
835
864
  });
package/.eslintignore DELETED
@@ -1,2 +0,0 @@
1
- dist/
2
- coverage/