query-string 6.13.7 → 7.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/index.d.ts +144 -5
  2. package/index.js +66 -3
  3. package/package.json +4 -2
  4. package/readme.md +139 -3
package/index.d.ts CHANGED
@@ -45,6 +45,30 @@ export interface ParseOptions {
45
45
  //=> {foo: ['1', '2', '3']}
46
46
  ```
47
47
 
48
+ - `bracket-separator`: Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character:
49
+
50
+ ```
51
+ import queryString = require('query-string');
52
+
53
+ queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
54
+ //=> {foo: []}
55
+
56
+ queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
57
+ //=> {foo: ['']}
58
+
59
+ queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
60
+ //=> {foo: ['1']}
61
+
62
+ queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
63
+ //=> {foo: ['1', '2', '3']}
64
+
65
+ queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
66
+ //=> {foo: ['1', '', 3, '', '', '6']}
67
+
68
+ queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
69
+ //=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
70
+ ```
71
+
48
72
  - `none`: Parse arrays with elements using duplicate keys:
49
73
 
50
74
  ```
@@ -54,7 +78,7 @@ export interface ParseOptions {
54
78
  //=> {foo: ['1', '2', '3']}
55
79
  ```
56
80
  */
57
- readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
81
+ readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'bracket-separator' | 'none';
58
82
 
59
83
  /**
60
84
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
@@ -229,9 +253,14 @@ export interface StringifyOptions {
229
253
 
230
254
  queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
231
255
  //=> 'foo=1,2,3'
256
+
257
+ queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
258
+ //=> 'foo=1,,'
259
+ // Note that typing information for null values is lost
260
+ // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
232
261
  ```
233
262
 
234
- - `separator`: Serialize arrays by separating elements with character:
263
+ - `separator`: Serialize arrays by separating elements with character:
235
264
 
236
265
  ```
237
266
  import queryString = require('query-string');
@@ -240,6 +269,33 @@ export interface StringifyOptions {
240
269
  //=> 'foo=1|2|3'
241
270
  ```
242
271
 
272
+ - `bracket-separator`: Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character:
273
+
274
+ ```
275
+ import queryString = require('query-string');
276
+
277
+ queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
278
+ //=> 'foo[]'
279
+
280
+ queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
281
+ //=> 'foo[]='
282
+
283
+ queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
284
+ //=> 'foo[]=1'
285
+
286
+ queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
287
+ //=> 'foo[]=1|2|3'
288
+
289
+ queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
290
+ //=> 'foo[]=1||3|||6'
291
+
292
+ queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true});
293
+ //=> 'foo[]=1||3|6'
294
+
295
+ queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
296
+ //=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
297
+ ```
298
+
243
299
  - `none`: Serialize arrays by using duplicate keys:
244
300
 
245
301
  ```
@@ -249,7 +305,7 @@ export interface StringifyOptions {
249
305
  //=> 'foo=1&foo=2&foo=3'
250
306
  ```
251
307
  */
252
- readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'none';
308
+ readonly arrayFormat?: 'bracket' | 'index' | 'comma' | 'separator' | 'bracket-separator' | 'none';
253
309
 
254
310
  /**
255
311
  The character used to separate array elements when using `{arrayFormat: 'separator'}`.
@@ -350,7 +406,12 @@ export type StringifiableRecord = Record<
350
406
  Stringify an object into a query string and sort the keys.
351
407
  */
352
408
  export function stringify(
353
- object: StringifiableRecord,
409
+ // TODO: Use the below instead when the following TS issues are fixed:
410
+ // - https://github.com/microsoft/TypeScript/issues/15300
411
+ // - https://github.com/microsoft/TypeScript/issues/42021
412
+ // Context: https://github.com/sindresorhus/query-string/issues/298
413
+ // object: StringifiableRecord,
414
+ object: Record<string, any>,
354
415
  options?: StringifyOptions
355
416
  ): string;
356
417
 
@@ -367,7 +428,7 @@ export interface UrlObject {
367
428
  /**
368
429
  Overrides queries in the `url` property.
369
430
  */
370
- readonly query: StringifiableRecord;
431
+ readonly query?: StringifiableRecord;
371
432
 
372
433
  /**
373
434
  Overrides the fragment identifier in the `url` property.
@@ -404,3 +465,81 @@ export function stringifyUrl(
404
465
  object: UrlObject,
405
466
  options?: StringifyOptions
406
467
  ): string;
468
+
469
+ /**
470
+ Pick query parameters from a URL.
471
+
472
+ @param url - The URL containing the query parameters to pick.
473
+ @param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
474
+ @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
475
+
476
+ @returns The URL with the picked query parameters.
477
+
478
+ @example
479
+ ```
480
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
481
+ //=> 'https://foo.bar?foo=1#hello'
482
+
483
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
484
+ //=> 'https://foo.bar?bar=2#hello'
485
+ ```
486
+ */
487
+ export function pick(
488
+ url: string,
489
+ keys: readonly string[],
490
+ options?: ParseOptions & StringifyOptions
491
+ ): string
492
+ export function pick(
493
+ url: string,
494
+ filter: (key: string, value: string | boolean | number) => boolean,
495
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
496
+ ): string
497
+ export function pick(
498
+ url: string,
499
+ filter: (key: string, value: string | boolean) => boolean,
500
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
501
+ ): string
502
+ export function pick(
503
+ url: string,
504
+ filter: (key: string, value: string | number) => boolean,
505
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
506
+ ): string
507
+
508
+ /**
509
+ Exclude query parameters from a URL. Like `.pick()` but reversed.
510
+
511
+ @param url - The URL containing the query parameters to exclude.
512
+ @param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
513
+ @param filter - A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
514
+
515
+ @returns The URL without the excluded the query parameters.
516
+
517
+ @example
518
+ ```
519
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
520
+ //=> 'https://foo.bar?bar=2#hello'
521
+
522
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
523
+ //=> 'https://foo.bar?foo=1#hello'
524
+ ```
525
+ */
526
+ export function exclude(
527
+ url: string,
528
+ keys: readonly string[],
529
+ options?: ParseOptions & StringifyOptions
530
+ ): string
531
+ export function exclude(
532
+ url: string,
533
+ filter: (key: string, value: string | boolean | number) => boolean,
534
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
535
+ ): string
536
+ export function exclude(
537
+ url: string,
538
+ filter: (key: string, value: string | boolean) => boolean,
539
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
540
+ ): string
541
+ export function exclude(
542
+ url: string,
543
+ filter: (key: string, value: string | number) => boolean,
544
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
545
+ ): string
package/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  const strictUriEncode = require('strict-uri-encode');
3
3
  const decodeComponent = require('decode-uri-component');
4
4
  const splitOnFirst = require('split-on-first');
5
+ const filterObject = require('filter-obj');
5
6
 
6
7
  const isNullOrUndefined = value => value === null || value === undefined;
7
8
 
@@ -48,17 +49,30 @@ function encoderForArrayFormat(options) {
48
49
 
49
50
  case 'comma':
50
51
  case 'separator':
52
+ case 'bracket-separator': {
53
+ const keyValueSep = options.arrayFormat === 'bracket-separator' ?
54
+ '[]=' :
55
+ '=';
56
+
51
57
  return key => (result, value) => {
52
- if (value === null || value === undefined || value.length === 0) {
58
+ if (
59
+ value === undefined ||
60
+ (options.skipNull && value === null) ||
61
+ (options.skipEmptyString && value === '')
62
+ ) {
53
63
  return result;
54
64
  }
55
65
 
66
+ // Translate null to an empty string so that it doesn't serialize as 'null'
67
+ value = value === null ? '' : value;
68
+
56
69
  if (result.length === 0) {
57
- return [[encode(key, options), '=', encode(value, options)].join('')];
70
+ return [[encode(key, options), keyValueSep, encode(value, options)].join('')];
58
71
  }
59
72
 
60
73
  return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
61
74
  };
75
+ }
62
76
 
63
77
  default:
64
78
  return key => (result, value) => {
@@ -129,6 +143,28 @@ function parserForArrayFormat(options) {
129
143
  accumulator[key] = newValue;
130
144
  };
131
145
 
146
+ case 'bracket-separator':
147
+ return (key, value, accumulator) => {
148
+ const isArray = /(\[\])$/.test(key);
149
+ key = key.replace(/\[\]$/, '');
150
+
151
+ if (!isArray) {
152
+ accumulator[key] = value ? decode(value, options) : value;
153
+ return;
154
+ }
155
+
156
+ const arrayValue = value === null ?
157
+ [] :
158
+ value.split(options.arrayFormatSeparator).map(item => decode(item, options));
159
+
160
+ if (accumulator[key] === undefined) {
161
+ accumulator[key] = arrayValue;
162
+ return;
163
+ }
164
+
165
+ accumulator[key] = [].concat(accumulator[key], arrayValue);
166
+ };
167
+
132
168
  default:
133
169
  return (key, value, accumulator) => {
134
170
  if (accumulator[key] === undefined) {
@@ -244,11 +280,15 @@ function parse(query, options) {
244
280
  }
245
281
 
246
282
  for (const param of query.split('&')) {
283
+ if (param === '') {
284
+ continue;
285
+ }
286
+
247
287
  let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');
248
288
 
249
289
  // Missing `=` should be `null`:
250
290
  // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
251
- value = value === undefined ? null : ['comma', 'separator'].includes(options.arrayFormat) ? value : decode(value, options);
291
+ value = value === undefined ? null : ['comma', 'separator', 'bracket-separator'].includes(options.arrayFormat) ? value : decode(value, options);
252
292
  formatter(decode(key, options), value, ret);
253
293
  }
254
294
 
@@ -330,6 +370,10 @@ exports.stringify = (object, options) => {
330
370
  }
331
371
 
332
372
  if (Array.isArray(value)) {
373
+ if (value.length === 0 && options.arrayFormat === 'bracket-separator') {
374
+ return encode(key, options) + '[]';
375
+ }
376
+
333
377
  return value
334
378
  .reduce(formatter(key), [])
335
379
  .join('&');
@@ -378,3 +422,22 @@ exports.stringifyUrl = (object, options) => {
378
422
 
379
423
  return `${url}${queryString}${hash}`;
380
424
  };
425
+
426
+ exports.pick = (input, filter, options) => {
427
+ options = Object.assign({
428
+ parseFragmentIdentifier: true
429
+ }, options);
430
+
431
+ const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
432
+ return exports.stringifyUrl({
433
+ url,
434
+ query: filterObject(query, filter),
435
+ fragmentIdentifier
436
+ }, options);
437
+ };
438
+
439
+ exports.exclude = (input, filter, options) => {
440
+ const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
441
+
442
+ return exports.pick(input, exclusionFilter, options);
443
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "query-string",
3
- "version": "6.13.7",
3
+ "version": "7.0.0",
4
4
  "description": "Parse and stringify URL query strings",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/query-string",
@@ -34,10 +34,12 @@
34
34
  "stringify",
35
35
  "encode",
36
36
  "decode",
37
- "searchparams"
37
+ "searchparams",
38
+ "filter"
38
39
  ],
39
40
  "dependencies": {
40
41
  "decode-uri-component": "^0.2.0",
42
+ "filter-obj": "^1.1.0",
41
43
  "split-on-first": "^1.0.0",
42
44
  "strict-uri-encode": "^2.0.0"
43
45
  },
package/readme.md CHANGED
@@ -1,4 +1,4 @@
1
- # query-string [![Build Status](https://travis-ci.com/sindresorhus/query-string.svg?branch=master)](https://travis-ci.com/github/sindresorhus/query-string) [![](https://badgen.net/bundlephobia/minzip/query-string)](https://bundlephobia.com/result?p=query-string)
1
+ # query-string
2
2
 
3
3
  > Parse and stringify URL [query strings](https://en.wikipedia.org/wiki/Query_string)
4
4
 
@@ -16,8 +16,21 @@
16
16
  <sup>Special thanks to:</sup>
17
17
  <br>
18
18
  <br>
19
- <a href="https://standardresume.co">
20
- <img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="200"/>
19
+ <a href="https://standardresume.co/tech">
20
+ <img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="180"/>
21
+ </a>
22
+ <br>
23
+ <br>
24
+ <a href="https://doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=query-string&utm_source=github">
25
+ <div>
26
+ <img src="https://dashboard.doppler.com/imgs/logo-long.svg" width="240" alt="Doppler">
27
+ </div>
28
+ <b>All your environment variables, in one place</b>
29
+ <div>
30
+ <span>Stop struggling with scattered API keys, hacking together home-brewed tools,</span>
31
+ <br>
32
+ <span>and avoiding access controls. Keep your team and servers in sync with Doppler.</span>
33
+ </div>
21
34
  </a>
22
35
  </p>
23
36
  </div>
@@ -125,6 +138,30 @@ queryString.parse('foo=1|2|3', {arrayFormat: 'separator', arrayFormatSeparator:
125
138
  //=> {foo: ['1', '2', '3']}
126
139
  ```
127
140
 
141
+ - `'bracket-separator'`: Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character:
142
+
143
+ ```js
144
+ const queryString = require('query-string');
145
+
146
+ queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
147
+ //=> {foo: []}
148
+
149
+ queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
150
+ //=> {foo: ['']}
151
+
152
+ queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
153
+ //=> {foo: ['1']}
154
+
155
+ queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
156
+ //=> {foo: ['1', '2', '3']}
157
+
158
+ queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
159
+ //=> {foo: ['1', '', 3, '', '', '6']}
160
+
161
+ queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
162
+ //=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
163
+ ```
164
+
128
165
  - `'none'`: Parse arrays with elements using duplicate keys:
129
166
 
130
167
  ```js
@@ -228,6 +265,47 @@ const queryString = require('query-string');
228
265
 
229
266
  queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
230
267
  //=> 'foo=1,2,3'
268
+
269
+ queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
270
+ //=> 'foo=1,,'
271
+ // Note that typing information for null values is lost
272
+ // and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
273
+ ```
274
+
275
+ - `'separator'`: Serialize arrays by separating elements with a custom character:
276
+
277
+ ```js
278
+ const queryString = require('query-string');
279
+
280
+ queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'separator', arrayFormatSeparator: '|'});
281
+ //=> 'foo=1|2|3'
282
+ ```
283
+
284
+ - `'bracket-separator'`: Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character:
285
+
286
+ ```js
287
+ const queryString = require('query-string');
288
+
289
+ queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
290
+ //=> 'foo[]'
291
+
292
+ queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
293
+ //=> 'foo[]='
294
+
295
+ queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
296
+ //=> 'foo[]=1'
297
+
298
+ queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
299
+ //=> 'foo[]=1|2|3'
300
+
301
+ queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
302
+ //=> 'foo[]=1||3|||6'
303
+
304
+ queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true});
305
+ //=> 'foo[]=1||3|6'
306
+
307
+ queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
308
+ //=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
231
309
  ```
232
310
 
233
311
  - `'none'`: Serialize arrays by using duplicate keys:
@@ -415,6 +493,64 @@ Type: `object`
415
493
 
416
494
  Query items to add to the URL.
417
495
 
496
+ ### .pick(url, keys, options?)
497
+ ### .pick(url, filter, options?)
498
+
499
+ Pick query parameters from a URL.
500
+
501
+ Returns a string with the new URL.
502
+
503
+ ```js
504
+ const queryString = require('query-string');
505
+
506
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
507
+ //=> 'https://foo.bar?foo=1#hello'
508
+
509
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
510
+ //=> 'https://foo.bar?bar=2#hello'
511
+ ```
512
+
513
+ ### .exclude(url, keys, options?)
514
+ ### .exclude(url, filter, options?)
515
+
516
+ Exclude query parameters from a URL.
517
+
518
+ Returns a string with the new URL.
519
+
520
+ ```js
521
+ const queryString = require('query-string');
522
+
523
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
524
+ //=> 'https://foo.bar?bar=2#hello'
525
+
526
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
527
+ //=> 'https://foo.bar?foo=1#hello'
528
+ ```
529
+
530
+ #### url
531
+
532
+ Type: `string`
533
+
534
+ The URL containing the query parameters to filter.
535
+
536
+ #### keys
537
+
538
+ Type: `string[]`
539
+
540
+ The names of the query parameters to filter based on the function used.
541
+
542
+ #### filter
543
+
544
+ Type: `(key, value) => boolean`
545
+
546
+ A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
547
+
548
+ #### options
549
+
550
+ Type: `object`
551
+
552
+ [Parse options](#options) and [stringify options](#options-1).
553
+
418
554
  ## Nesting
419
555
 
420
556
  This module intentionally doesn't support nesting as it's not spec'd and varies between implementations, which causes a lot of [edge cases](https://github.com/visionmedia/node-querystring/issues).