query-string 6.13.5 → 6.14.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 +85 -2
  2. package/index.js +44 -18
  3. package/package.json +4 -2
  4. package/readme.md +60 -2
package/index.d.ts CHANGED
@@ -343,14 +343,19 @@ export type Stringifiable = string | boolean | number | null | undefined;
343
343
 
344
344
  export type StringifiableRecord = Record<
345
345
  string,
346
- Stringifiable | Stringifiable[]
346
+ Stringifiable | readonly Stringifiable[]
347
347
  >;
348
348
 
349
349
  /**
350
350
  Stringify an object into a query string and sort the keys.
351
351
  */
352
352
  export function stringify(
353
- object: StringifiableRecord,
353
+ // TODO: Use the below instead when the following TS issues are fixed:
354
+ // - https://github.com/microsoft/TypeScript/issues/15300
355
+ // - https://github.com/microsoft/TypeScript/issues/42021
356
+ // Context: https://github.com/sindresorhus/query-string/issues/298
357
+ // object: StringifiableRecord,
358
+ object: Record<string, any>,
354
359
  options?: StringifyOptions
355
360
  ): string;
356
361
 
@@ -404,3 +409,81 @@ export function stringifyUrl(
404
409
  object: UrlObject,
405
410
  options?: StringifyOptions
406
411
  ): string;
412
+
413
+ /**
414
+ Pick query parameters from a URL.
415
+
416
+ @param url - The URL containing the query parameters to pick.
417
+ @param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
418
+ @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`.
419
+
420
+ @returns The URL with the picked query parameters.
421
+
422
+ @example
423
+ ```
424
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
425
+ //=> 'https://foo.bar?foo=1#hello'
426
+
427
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
428
+ //=> 'https://foo.bar?bar=2#hello'
429
+ ```
430
+ */
431
+ export function pick(
432
+ url: string,
433
+ keys: readonly string[],
434
+ options?: ParseOptions & StringifyOptions
435
+ ): string
436
+ export function pick(
437
+ url: string,
438
+ filter: (key: string, value: string | boolean | number) => boolean,
439
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
440
+ ): string
441
+ export function pick(
442
+ url: string,
443
+ filter: (key: string, value: string | boolean) => boolean,
444
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
445
+ ): string
446
+ export function pick(
447
+ url: string,
448
+ filter: (key: string, value: string | number) => boolean,
449
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
450
+ ): string
451
+
452
+ /**
453
+ Exclude query parameters from a URL. Like `.pick()` but reversed.
454
+
455
+ @param url - The URL containing the query parameters to exclude.
456
+ @param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
457
+ @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`.
458
+
459
+ @returns The URL without the excluded the query parameters.
460
+
461
+ @example
462
+ ```
463
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
464
+ //=> 'https://foo.bar?bar=2#hello'
465
+
466
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
467
+ //=> 'https://foo.bar?foo=1#hello'
468
+ ```
469
+ */
470
+ export function exclude(
471
+ url: string,
472
+ keys: readonly string[],
473
+ options?: ParseOptions & StringifyOptions
474
+ ): string
475
+ export function exclude(
476
+ url: string,
477
+ filter: (key: string, value: string | boolean | number) => boolean,
478
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
479
+ ): string
480
+ export function exclude(
481
+ url: string,
482
+ filter: (key: string, value: string | boolean) => boolean,
483
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
484
+ ): string
485
+ export function exclude(
486
+ url: string,
487
+ filter: (key: string, value: string | number) => boolean,
488
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
489
+ ): 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
 
@@ -122,8 +123,10 @@ function parserForArrayFormat(options) {
122
123
  case 'comma':
123
124
  case 'separator':
124
125
  return (key, value, accumulator) => {
125
- const isArray = typeof value === 'string' && value.split('').indexOf(options.arrayFormatSeparator) > -1;
126
- const newValue = isArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options);
126
+ const isArray = typeof value === 'string' && value.includes(options.arrayFormatSeparator);
127
+ const isEncodedArray = (typeof value === 'string' && !isArray && decode(value, options).includes(options.arrayFormatSeparator));
128
+ value = isEncodedArray ? decode(value, options) : value;
129
+ const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options);
127
130
  accumulator[key] = newValue;
128
131
  };
129
132
 
@@ -214,7 +217,7 @@ function parseValue(value, options) {
214
217
  return value;
215
218
  }
216
219
 
217
- function parse(input, options) {
220
+ function parse(query, options) {
218
221
  options = Object.assign({
219
222
  decode: true,
220
223
  sort: true,
@@ -231,17 +234,21 @@ function parse(input, options) {
231
234
  // Create an object with no prototype
232
235
  const ret = Object.create(null);
233
236
 
234
- if (typeof input !== 'string') {
237
+ if (typeof query !== 'string') {
235
238
  return ret;
236
239
  }
237
240
 
238
- input = input.trim().replace(/^[?#&]/, '');
241
+ query = query.trim().replace(/^[?#&]/, '');
239
242
 
240
- if (!input) {
243
+ if (!query) {
241
244
  return ret;
242
245
  }
243
246
 
244
- for (const param of input.split('&')) {
247
+ for (const param of query.split('&')) {
248
+ if (param === '') {
249
+ continue;
250
+ }
251
+
245
252
  let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');
246
253
 
247
254
  // Missing `=` should be `null`:
@@ -337,42 +344,61 @@ exports.stringify = (object, options) => {
337
344
  }).filter(x => x.length > 0).join('&');
338
345
  };
339
346
 
340
- exports.parseUrl = (input, options) => {
347
+ exports.parseUrl = (url, options) => {
341
348
  options = Object.assign({
342
349
  decode: true
343
350
  }, options);
344
351
 
345
- const [url, hash] = splitOnFirst(input, '#');
352
+ const [url_, hash] = splitOnFirst(url, '#');
346
353
 
347
354
  return Object.assign(
348
355
  {
349
- url: url.split('?')[0] || '',
350
- query: parse(extract(input), options)
356
+ url: url_.split('?')[0] || '',
357
+ query: parse(extract(url), options)
351
358
  },
352
359
  options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {}
353
360
  );
354
361
  };
355
362
 
356
- exports.stringifyUrl = (input, options) => {
363
+ exports.stringifyUrl = (object, options) => {
357
364
  options = Object.assign({
358
365
  encode: true,
359
366
  strict: true
360
367
  }, options);
361
368
 
362
- const url = removeHash(input.url).split('?')[0] || '';
363
- const queryFromUrl = exports.extract(input.url);
369
+ const url = removeHash(object.url).split('?')[0] || '';
370
+ const queryFromUrl = exports.extract(object.url);
364
371
  const parsedQueryFromUrl = exports.parse(queryFromUrl, {sort: false});
365
372
 
366
- const query = Object.assign(parsedQueryFromUrl, input.query);
373
+ const query = Object.assign(parsedQueryFromUrl, object.query);
367
374
  let queryString = exports.stringify(query, options);
368
375
  if (queryString) {
369
376
  queryString = `?${queryString}`;
370
377
  }
371
378
 
372
- let hash = getHash(input.url);
373
- if (input.fragmentIdentifier) {
374
- hash = `#${encode(input.fragmentIdentifier, options)}`;
379
+ let hash = getHash(object.url);
380
+ if (object.fragmentIdentifier) {
381
+ hash = `#${encode(object.fragmentIdentifier, options)}`;
375
382
  }
376
383
 
377
384
  return `${url}${queryString}${hash}`;
378
385
  };
386
+
387
+ exports.pick = (input, filter, options) => {
388
+ options = Object.assign({
389
+ parseFragmentIdentifier: true
390
+ }, options);
391
+
392
+ const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
393
+ return exports.stringifyUrl({
394
+ url,
395
+ query: filterObject(query, filter),
396
+ fragmentIdentifier
397
+ }, options);
398
+ };
399
+
400
+ exports.exclude = (input, filter, options) => {
401
+ const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
402
+
403
+ return exports.pick(input, exclusionFilter, options);
404
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "query-string",
3
- "version": "6.13.5",
3
+ "version": "6.14.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,7 +16,7 @@
16
16
  <sup>Special thanks to:</sup>
17
17
  <br>
18
18
  <br>
19
- <a href="https://standardresume.co">
19
+ <a href="https://standardresume.co/tech">
20
20
  <img src="https://sindresorhus.com/assets/thanks/standard-resume-logo.svg" width="200"/>
21
21
  </a>
22
22
  </p>
@@ -415,6 +415,64 @@ Type: `object`
415
415
 
416
416
  Query items to add to the URL.
417
417
 
418
+ ### .pick(url, keys, options?)
419
+ ### .pick(url, filter, options?)
420
+
421
+ Pick query parameters from a URL.
422
+
423
+ Returns a string with the new URL.
424
+
425
+ ```js
426
+ const queryString = require('query-string');
427
+
428
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
429
+ //=> 'https://foo.bar?foo=1#hello'
430
+
431
+ queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
432
+ //=> 'https://foo.bar?bar=2#hello'
433
+ ```
434
+
435
+ ### .exclude(url, keys, options?)
436
+ ### .exclude(url, filter, options?)
437
+
438
+ Exclude query parameters from a URL.
439
+
440
+ Returns a string with the new URL.
441
+
442
+ ```js
443
+ const queryString = require('query-string');
444
+
445
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
446
+ //=> 'https://foo.bar?bar=2#hello'
447
+
448
+ queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
449
+ //=> 'https://foo.bar?foo=1#hello'
450
+ ```
451
+
452
+ #### url
453
+
454
+ Type: `string`
455
+
456
+ The URL containing the query parameters to filter.
457
+
458
+ #### keys
459
+
460
+ Type: `string[]`
461
+
462
+ The names of the query parameters to filter based on the function used.
463
+
464
+ #### filter
465
+
466
+ Type: `(key, value) => boolean`
467
+
468
+ A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
469
+
470
+ #### options
471
+
472
+ Type: `object`
473
+
474
+ [Parse options](#options) and [stringify options](#options-1).
475
+
418
476
  ## Nesting
419
477
 
420
478
  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).