query-string 9.5.0 → 10.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.
Files changed (4) hide show
  1. package/base.d.ts +156 -83
  2. package/base.js +436 -245
  3. package/package.json +11 -22
  4. package/readme.md +89 -45
package/package.json CHANGED
@@ -1,15 +1,11 @@
1
1
  {
2
2
  "name": "query-string",
3
- "version": "9.5.0",
3
+ "version": "10.0.0",
4
4
  "description": "Parse and stringify URL query strings",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/query-string",
7
7
  "funding": "https://github.com/sponsors/sindresorhus",
8
- "author": {
9
- "name": "Sindre Sorhus",
10
- "email": "sindresorhus@gmail.com",
11
- "url": "https://sindresorhus.com"
12
- },
8
+ "author": "Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)",
13
9
  "type": "module",
14
10
  "exports": {
15
11
  "types": "./index.d.ts",
@@ -17,11 +13,11 @@
17
13
  },
18
14
  "sideEffects": false,
19
15
  "engines": {
20
- "node": ">=18"
16
+ "node": ">=22"
21
17
  },
22
18
  "scripts": {
23
- "benchmark": "node benchmark.js",
24
- "test": "xo && ava && tsd"
19
+ "benchmark": "node --experimental-bench --bench benchmark.js",
20
+ "test": "xo && node --test && nz"
25
21
  },
26
22
  "files": [
27
23
  "index.js",
@@ -46,21 +42,14 @@
46
42
  "filter"
47
43
  ],
48
44
  "dependencies": {
49
- "decode-uri-component": "^0.5.0",
50
- "filter-obj": "^5.1.0",
51
- "split-on-first": "^3.0.0"
45
+ "filter-obj": "^6.1.0",
46
+ "split-on-first": "^4.0.0"
52
47
  },
53
48
  "devDependencies": {
54
- "ava": "^6.1.1",
55
- "benchmark": "^2.1.4",
49
+ "@typescript/native": "npm:typescript@^7.0.2",
56
50
  "deep-equal": "^2.2.3",
57
- "fast-check": "^3.15.1",
58
- "tsd": "^0.30.7",
59
- "xo": "^0.57.0"
60
- },
61
- "tsd": {
62
- "compilerOptions": {
63
- "module": "node16"
64
- }
51
+ "fast-check": "^4.10.2",
52
+ "nz": "^0.1.0",
53
+ "xo": "^5.0.1"
65
54
  }
66
55
  }
package/readme.md CHANGED
@@ -59,8 +59,8 @@ The returned object is created with [`Object.create(null)`](https://developer.mo
59
59
  queryString.parse('?foo=bar');
60
60
  //=> {foo: 'bar'}
61
61
 
62
- queryString.parse('#token=secret&name=jhon');
63
- //=> {token: 'secret', name: 'jhon'}
62
+ queryString.parse('#token=secret&name=john');
63
+ //=> {name: 'john', token: 'secret'}
64
64
  ```
65
65
 
66
66
  #### options
@@ -72,7 +72,7 @@ Type: `object`
72
72
  Type: `boolean`\
73
73
  Default: `true`
74
74
 
75
- Decode the keys and values. URL components are decoded with [`decode-uri-component`](https://github.com/SamVerschueren/decode-uri-component).
75
+ Decode the keys and values. A byte that is not valid UTF-8 becomes the replacement character `U+FFFD`, and the bytes around it are still decoded, like the [`URL` parser](https://developer.mozilla.org/en-US/docs/Web/API/URL) and [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) do. A `%` that is not followed by two hexadecimal digits is not an escape and is kept as it is.
76
76
 
77
77
  ##### arrayFormat
78
78
 
@@ -97,6 +97,8 @@ queryString.parse('foo[0]=1&foo[1]=2&foo[3]=3', {arrayFormat: 'index'});
97
97
  //=> {foo: ['1', '2', '3']}
98
98
  ```
99
99
 
100
+ Entries without an index, such as `foo[]`, are appended after the highest index seen so far. Appending after an index greater than or equal to `Number.MAX_SAFE_INTEGER` is unsupported.
101
+
100
102
  - `'comma'`: Parse arrays with elements separated by comma:
101
103
 
102
104
  ```js
@@ -104,6 +106,21 @@ import queryString from 'query-string';
104
106
 
105
107
  queryString.parse('foo=1,2,3', {arrayFormat: 'comma'});
106
108
  //=> {foo: ['1', '2', '3']}
109
+
110
+ queryString.parse('foo=1,2&foo=3', {arrayFormat: 'comma'});
111
+ //=> {foo: ['1', '2', '3']}
112
+ ```
113
+
114
+ A single value has no separator, so it is a plain value, not an array with one element. Use the [`types`](#types) option to always get an array, or use `'bracket-separator'`, which marks arrays explicitly.
115
+
116
+ ```js
117
+ import queryString from 'query-string';
118
+
119
+ queryString.parse('foo=1', {arrayFormat: 'comma'});
120
+ //=> {foo: '1'}
121
+
122
+ queryString.parse('foo=1', {arrayFormat: 'comma', types: {foo: 'string[]'}});
123
+ //=> {foo: ['1']}
107
124
  ```
108
125
 
109
126
  - `'separator'`: Parse arrays with elements separated by a custom character:
@@ -133,10 +150,10 @@ queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatS
133
150
  //=> {foo: ['1', '2', '3']}
134
151
 
135
152
  queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
136
- //=> {foo: ['1', '', 3, '', '', '6']}
153
+ //=> {foo: ['1', '', '3', '', '', '6']}
137
154
 
138
155
  queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
139
- //=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
156
+ //=> {bar: 'fluffy', baz: ['4'], foo: ['1', '2', '3']}
140
157
  ```
141
158
 
142
159
  - `'colon-list-separator'`: Parse arrays with parameter names that are explicitly marked with `:list`:
@@ -164,6 +181,18 @@ Default: `','`
164
181
 
165
182
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
166
183
 
184
+ Only a literal separator delimits array elements. A percent-encoded separator (for example, `%2C`) is part of the element, which is how [`.stringify()`](#stringifyobject-options) encodes elements that contain the separator. Because of this, `%` and a hexadecimal digit are unsupported, since those also appear inside percent-encoded elements, and so are `&`, `+`, and `#`, since those already have a meaning in a URL: `&` separates parameters, `+` is decoded as a space, and `#` starts the fragment identifier. Those are handled on a best-effort basis, so the result is undefined. Whitespace cannot work at all, since leading and trailing whitespace is ignored when parsing, and a lone surrogate cannot be percent-encoded, so both throw a `TypeError`.
185
+
186
+ ```js
187
+ import queryString from 'query-string';
188
+
189
+ queryString.parse('foo=a%2Cb,c', {arrayFormat: 'comma'});
190
+ //=> {foo: ['a,b', 'c']}
191
+
192
+ queryString.parse('foo[]=a%2Cb,c', {arrayFormat: 'bracket-separator'});
193
+ //=> {foo: ['a,b', 'c']}
194
+ ```
195
+
167
196
  ##### sort
168
197
 
169
198
  Type: `Function | boolean`\
@@ -171,6 +200,8 @@ Default: `true`
171
200
 
172
201
  Supports both `Function` as a custom sorting function or `false` to disable sorting.
173
202
 
203
+ If omitted, keys are sorted using `Array#sort()`.
204
+
174
205
  ##### parseNumbers
175
206
 
176
207
  Type: `boolean`\
@@ -212,18 +243,19 @@ You can also provide a custom function to transform the value. The function will
212
243
 
213
244
  Supported Types:
214
245
 
215
-
216
246
  - `'boolean'`: Parse `flagged` as a boolean (overriding the `parseBooleans` option):
217
247
 
218
248
  ```js
249
+ import queryString from 'query-string';
250
+
219
251
  queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
220
- parseBooleans: false,
221
- types: {
222
- flagged: 'boolean',
223
- isOkay: 'boolean',
224
- },
252
+ parseBooleans: false,
253
+ types: {
254
+ flagged: 'boolean',
255
+ isOkay: 'boolean',
256
+ },
225
257
  });
226
- //=> {isAdmin: 'true', flagged: true, isOkay: false}
258
+ //=> {flagged: true, isAdmin: 'true', isOkay: false}
227
259
  ```
228
260
 
229
261
  Note: The `'boolean'` type also converts `'0'` and `'1'` to booleans, and treats valueless keys (e.g. `?flag`) as `true`.
@@ -239,7 +271,7 @@ queryString.parse('?phoneNumber=%2B380951234567&id=1', {
239
271
  phoneNumber: 'string',
240
272
  }
241
273
  });
242
- //=> {phoneNumber: '+380951234567', id: 1}
274
+ //=> {id: 1, phoneNumber: '+380951234567'}
243
275
  ```
244
276
 
245
277
  - `'number'`: Parse `age` as a number (even when `parseNumbers` is false):
@@ -260,7 +292,8 @@ queryString.parse('?age=20&id=01234&zipcode=90210', {
260
292
  ```js
261
293
  import queryString from 'query-string';
262
294
 
263
- queryString.parse('?age=20&items=1%2C2%2C3', {
295
+ queryString.parse('?age=20&items=1,2,3', {
296
+ arrayFormat: 'comma',
264
297
  parseNumbers: true,
265
298
  types: {
266
299
  items: 'string[]',
@@ -274,7 +307,8 @@ queryString.parse('?age=20&items=1%2C2%2C3', {
274
307
  ```js
275
308
  import queryString from 'query-string';
276
309
 
277
- queryString.parse('?age=20&items=1%2C2%2C3', {
310
+ queryString.parse('?age=20&items=1,2,3', {
311
+ arrayFormat: 'comma',
278
312
  types: {
279
313
  items: 'number[]',
280
314
  }
@@ -304,34 +338,19 @@ queryString.parse('?scores=10,20,30', {
304
338
  //=> {scores: [20, 40, 60]}
305
339
  ```
306
340
 
307
- NOTE: Array types (`string[]`, `number[]`) are ignored if `arrayFormat` is set to `'none'`.
341
+ NOTE: With `arrayFormat: 'none'`, array types (`string[]`, `number[]`) do not turn a single value into an array. The value stays a string, and `parseNumbers` and `parseBooleans` do not apply to it. A repeated key is still an array, and its items are parsed with the element type.
308
342
 
309
343
  ```js
310
- queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
344
+ queryString.parse('ids=001,002,003&foods=apple,orange,mango', {
311
345
  arrayFormat: 'none',
312
346
  types: {
313
347
  ids: 'number[]',
314
348
  foods: 'string[]',
315
349
  },
316
- }
317
- //=> {ids:'001,002,003', foods:'apple,orange,mango'}
318
- ```
319
-
320
- ###### Function
321
-
322
- ```js
323
- import queryString from 'query-string';
324
-
325
- queryString.parse('?age=20&id=01234&zipcode=90210', {
326
- types: {
327
- age: value => value * 2,
328
- }
329
350
  });
330
- //=> {age: 40, id: '01234', zipcode: '90210'}
351
+ //=> {foods: 'apple,orange,mango', ids: '001,002,003'}
331
352
  ```
332
353
 
333
- Parse the value as a boolean type instead of string type if it's a boolean.
334
-
335
354
  ### .stringify(object, options?)
336
355
 
337
356
  Stringify an object into a query string and sorting the keys.
@@ -356,6 +375,8 @@ Default: `true`
356
375
 
357
376
  [URL encode](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) the keys and values.
358
377
 
378
+ A lone surrogate, which cannot be percent-encoded, is replaced with the replacement character U+FFFD, like [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) does.
379
+
359
380
  ##### arrayFormat
360
381
 
361
382
  Type: `string`\
@@ -390,7 +411,7 @@ queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
390
411
  queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
391
412
  //=> 'foo=1,,'
392
413
  // Note that typing information for null values is lost
393
- // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
414
+ // and `.parse('foo=1,,', {arrayFormat: 'comma'})` would return `{foo: ['1', '', '']}`.
394
415
  ```
395
416
 
396
417
  - `'separator'`: Serialize arrays by separating elements with a custom character:
@@ -426,7 +447,7 @@ queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-s
426
447
  //=> 'foo[]=1||3|6'
427
448
 
428
449
  queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
429
- //=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
450
+ //=> 'bar=fluffy&baz[]=4&foo[]=1|2|3'
430
451
  ```
431
452
 
432
453
  - `'colon-list-separator'`: Serialize arrays with parameter names that are explicitly marked with `:list`:
@@ -454,9 +475,12 @@ Default: `','`
454
475
 
455
476
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
456
477
 
478
+ A separator inside an element is percent-encoded, so [`.parse()`](#parsestring-options) can tell it apart from the separator between elements. With `comma` and `separator`, a plain value that contains the separator is percent-encoded the same way, so it does not turn into an array. Because of this, `%` and a hexadecimal digit are unsupported, since those also appear inside percent-encoded elements, and so are `&`, `+`, and `#`, since those already have a meaning in a URL: `&` separates parameters, `+` is decoded as a space, and `#` starts the fragment identifier. Those are handled on a best-effort basis, so the result is undefined. Whitespace cannot work at all, since leading and trailing whitespace is ignored when parsing, and a lone surrogate cannot be percent-encoded, so both throw a `TypeError`.
479
+
457
480
  ##### sort
458
481
 
459
- Type: `Function | boolean`
482
+ Type: `Function | boolean`\
483
+ Default: `true`
460
484
 
461
485
  Supports both `Function` as a custom sorting function or `false` to disable sorting.
462
486
 
@@ -478,7 +502,7 @@ queryString.stringify({b: 1, c: 2, a: 3}, {sort: false});
478
502
  //=> 'b=1&c=2&a=3'
479
503
  ```
480
504
 
481
- If omitted, keys are sorted using `Array#sort()`, which means, converting them to strings and comparing strings in Unicode code point order.
505
+ If omitted, keys are sorted using `Array#sort()`.
482
506
 
483
507
  ##### skipNull
484
508
 
@@ -539,7 +563,7 @@ A function that transforms key-value pairs before stringification.
539
563
  Type: `function`\
540
564
  Default: `undefined`
541
565
 
542
- Similar to the `replacer` parameter of `JSON.stringify()`, this function is called for each key-value pair and can be used to transform values before they are stringified. The function receives the key and value, and should return the transformed value. Returning `undefined` will omit the key-value pair from the resulting query string.
566
+ Similar to the `replacer` parameter of `JSON.stringify()`, this function is called for each key-value pair and can be used to transform values before they are stringified. The function receives the key and value, and should return the transformed value. Returning `undefined` will omit the key-value pair from the resulting query string. The `skipNull` and `skipEmptyString` options apply to the returned value.
543
567
 
544
568
  This is useful for custom serialization of non-primitive types like `Date`:
545
569
 
@@ -587,7 +611,7 @@ queryString.extract('https://foo.bar?foo=bar');
587
611
 
588
612
  ### .parseUrl(string, options?)
589
613
 
590
- Extract the URL and the query string as an object.
614
+ Extract the URL and the query string as an object. Leading and trailing whitespace is ignored.
591
615
 
592
616
  Returns an object with a `url` and `query` property.
593
617
 
@@ -633,7 +657,9 @@ The `options` are the same as for `.stringify()`.
633
657
 
634
658
  Returns a string with the URL and a query string.
635
659
 
636
- Query items in the `query` property overrides queries in the `url` property.
660
+ Query items in the `query` property overrides queries in the `url` property. The query in the `url` property is written back from its plain string values, so `parseNumbers`, `parseBooleans`, and `types` do not rewrite it when the options are shared with [`.parseUrl()`](#parseurlstring-options).
661
+
662
+ With `encode: false`, an array marker in an existing parameter name is only treated as a marker when it is literal, such as `a[]`, so a percent-encoded marker such as `a%5B%5D` is not overridden by the plain key. The existing parameter is kept as-is, so overriding `a` alongside an existing `a%5B%5D` in the URL writes both `a=2&a%5B%5D=1`.
637
663
 
638
664
  The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.
639
665
 
@@ -662,7 +688,7 @@ Type: `object`
662
688
 
663
689
  Type: `string`
664
690
 
665
- The URL to stringify.
691
+ The URL to stringify. Leading and trailing whitespace is ignored.
666
692
 
667
693
  ##### query
668
694
 
@@ -670,12 +696,30 @@ Type: `object`
670
696
 
671
697
  Query items to add to the URL.
672
698
 
699
+ ##### fragmentIdentifier
700
+
701
+ Type: `string`
702
+
703
+ The fragment identifier to add to the URL, without the leading `#`. An empty string removes the fragment identifier from the URL.
704
+
705
+ Like the query values, it is a decoded value and is percent-encoded when `encode` is `true`, so [`.parseUrl()`](#parseurlstring-options) reads it back unchanged. `/`, `?`, and `#` are kept as-is, so hash routes like `#/users?id=1` work. When `encode` is `false`, it is written as-is.
706
+
707
+ ```js
708
+ import queryString from 'query-string';
709
+
710
+ queryString.stringifyUrl({url: 'https://foo.bar', fragmentIdentifier: '100%'});
711
+ //=> 'https://foo.bar#100%25'
712
+
713
+ queryString.stringifyUrl({url: 'https://foo.bar', fragmentIdentifier: '/users?id=1'});
714
+ //=> 'https://foo.bar#/users?id=1'
715
+ ```
716
+
673
717
  ### .pick(url, keys, options?)
674
718
  ### .pick(url, filter, options?)
675
719
 
676
720
  Pick query parameters from a URL.
677
721
 
678
- Returns a string with the new URL.
722
+ Returns a string with the new URL. The query parameters are sorted, like with [`.stringify()`](#stringifyobject-options). Pass `sort: false` in the options to keep their order. With sorting disabled, integer-index keys come first in ascending numeric order, following JavaScript object enumeration. Repeated keys are grouped together.
679
723
 
680
724
  ```js
681
725
  import queryString from 'query-string';
@@ -692,7 +736,7 @@ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value ===
692
736
 
693
737
  Exclude query parameters from a URL.
694
738
 
695
- Returns a string with the new URL.
739
+ Returns a string with the new URL. The query parameters are sorted, like with [`.stringify()`](#stringifyobject-options). Pass `sort: false` in the options to keep their order. With sorting disabled, integer-index keys come first in ascending numeric order, following JavaScript object enumeration. Repeated keys are grouped together.
696
740
 
697
741
  ```js
698
742
  import queryString from 'query-string';
@@ -708,7 +752,7 @@ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value
708
752
 
709
753
  Type: `string`
710
754
 
711
- The URL containing the query parameters to filter.
755
+ The URL containing the query parameters to filter. Leading and trailing whitespace is ignored.
712
756
 
713
757
  #### keys
714
758
 
@@ -720,7 +764,7 @@ The names of the query parameters to filter based on the function used.
720
764
 
721
765
  Type: `(key, value) => boolean`
722
766
 
723
- A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
767
+ A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers`, `parseBooleans`, and `types` options also affect `value`. They only affect the value passed to the filter. The query parameters are written back to the URL from the plain string values, so a value like `007` is not rewritten as `7`. The query is decoded only when `encode` is `true`, so a value like `a%20b` is not percent-encoded a second time. With `encode: false`, the filter receives the key and value as they are in the URL. The `decode` option is ignored. The stringify options apply to the output, so `skipNull` and `skipEmptyString` also drop a parameter that the filter kept.
724
768
 
725
769
  #### options
726
770