From 94ebcd416fab024f1725268bb0a1303132158293 Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Wed, 30 Dec 2020 14:02:35 +0700
Subject: [PATCH 1/9] Work around TypeScript bug
Fixes #298
---
index.d.ts | 7 ++++++-
index.test-d.ts | 11 +++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/index.d.ts b/index.d.ts
index d828894..026d287 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -350,7 +350,12 @@ export type StringifiableRecord = Record<
Stringify an object into a query string and sort the keys.
*/
export function stringify(
- object: StringifiableRecord,
+ // TODO: Use the below instead when the following TS issues are fixed:
+ // - https://github.com/microsoft/TypeScript/issues/15300
+ // - https://github.com/microsoft/TypeScript/issues/42021
+ // Context: https://github.com/sindresorhus/query-string/issues/298
+ // object: StringifiableRecord,
+ object: Record,
options?: StringifyOptions
): string;
diff --git a/index.test-d.ts b/index.test-d.ts
index 9c46344..2aab3fe 100644
--- a/index.test-d.ts
+++ b/index.test-d.ts
@@ -35,6 +35,17 @@ expectType(
)
);
+// Ensure it accepts an `interface`.
+interface Query {
+ foo: string;
+}
+
+const query: Query = {
+ foo: 'bar'
+};
+
+queryString.stringify(query);
+
// Parse
expectType(queryString.parse('?foo=bar'));
From fbe496cc62974bb039cd7618721b87e7fb0dbcf1 Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Wed, 30 Dec 2020 14:03:52 +0700
Subject: [PATCH 2/9] 6.13.8
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 3d41fcc..df8a1f2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "query-string",
- "version": "6.13.7",
+ "version": "6.13.8",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "sindresorhus/query-string",
From 27453b5e49adaaa5419637732c19ab19b502585f Mon Sep 17 00:00:00 2001
From: Richie Bendall
Date: Sat, 2 Jan 2021 17:22:29 +1300
Subject: [PATCH 3/9] Move to GitHub Actions (#300)
---
.github/workflows/main.yml | 24 ++++++++++++++++++++++++
.travis.yml | 7 -------
readme.md | 2 +-
3 files changed, 25 insertions(+), 8 deletions(-)
create mode 100644 .github/workflows/main.yml
delete mode 100644 .travis.yml
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..b85fc2a
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,24 @@
+name: CI
+on:
+ - push
+ - pull_request
+jobs:
+ test:
+ name: Node.js ${{ matrix.node-version }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ node-version:
+ - 14
+ - 12
+ - 10
+ - 8
+ - 6
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-node@v1
+ with:
+ node-version: ${{ matrix.node-version }}
+ - run: npm install
+ - run: npm test
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 81f3378..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: node_js
-node_js:
- - '14'
- - '12'
- - '10'
- - '8'
- - '6'
diff --git a/readme.md b/readme.md
index 46e58b5..0914d74 100644
--- a/readme.md
+++ b/readme.md
@@ -1,4 +1,4 @@
-# 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)
+# query-string
> Parse and stringify URL [query strings](https://en.wikipedia.org/wiki/Query_string)
From 39aef9164653dd7a35877e67ea731a1bbb7ae736 Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Wed, 20 Jan 2021 14:44:06 +0700
Subject: [PATCH 4/9] Update a link
---
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/readme.md b/readme.md
index 0914d74..85f72ce 100644
--- a/readme.md
+++ b/readme.md
@@ -16,7 +16,7 @@
Special thanks to:
-
+
From 667c9e9d296a7f7197bcc8d6abe4a41c8cf4b912 Mon Sep 17 00:00:00 2001
From: Oleksii Teplenko
Date: Tue, 9 Feb 2021 20:03:27 +0200
Subject: [PATCH 5/9] Ignore ending ampersand when parsing (#306)
---
index.js | 4 ++++
test/parse.js | 5 +++++
2 files changed, 9 insertions(+)
diff --git a/index.js b/index.js
index 559ecd4..39e86e3 100644
--- a/index.js
+++ b/index.js
@@ -244,6 +244,10 @@ function parse(query, options) {
}
for (const param of query.split('&')) {
+ if (param === '') {
+ continue;
+ }
+
let [key, value] = splitOnFirst(options.decode ? param.replace(/\+/g, ' ') : param, '=');
// Missing `=` should be `null`:
diff --git a/test/parse.js b/test/parse.js
index 365eb2f..9bfc034 100644
--- a/test/parse.js
+++ b/test/parse.js
@@ -13,6 +13,11 @@ test('query strings starting with a `&`', t => {
t.deepEqual(queryString.parse('&foo=bar&foo=baz'), {foo: ['bar', 'baz']});
});
+test('query strings ending with a `&`', t => {
+ t.deepEqual(queryString.parse('foo=bar&'), {foo: 'bar'});
+ t.deepEqual(queryString.parse('foo=bar&&&'), {foo: 'bar'});
+});
+
test('parse a query string', t => {
t.deepEqual(queryString.parse('foo=bar'), {foo: 'bar'});
});
From 6ed5cb3d36f3e12eb024293c5d262e2d0efed9ec Mon Sep 17 00:00:00 2001
From: Richie Bendall
Date: Wed, 10 Feb 2021 19:13:18 +1300
Subject: [PATCH 6/9] Add `.pick()` and `.exclude()` (#282)
Co-authored-by: Sindre Sorhus
---
index.d.ts | 78 +++++++++++++++++++++++++++++++++++++++++++++++++
index.js | 20 +++++++++++++
index.test-d.ts | 6 ++++
package.json | 4 ++-
readme.md | 58 ++++++++++++++++++++++++++++++++++++
test/exclude.js | 17 +++++++++++
test/pick.js | 17 +++++++++++
7 files changed, 199 insertions(+), 1 deletion(-)
create mode 100644 test/exclude.js
create mode 100644 test/pick.js
diff --git a/index.d.ts b/index.d.ts
index 026d287..b6d651b 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -409,3 +409,81 @@ export function stringifyUrl(
object: UrlObject,
options?: StringifyOptions
): string;
+
+/**
+Pick query parameters from a URL.
+
+@param url - The URL containing the query parameters to pick.
+@param keys - The names of the query parameters to keep. All other query parameters will be removed from the URL.
+@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`.
+
+@returns The URL with the picked query parameters.
+
+@example
+```
+queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
+//=> 'https://foo.bar?foo=1#hello'
+
+queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
+//=> 'https://foo.bar?bar=2#hello'
+```
+*/
+export function pick(
+ url: string,
+ keys: readonly string[],
+ options?: ParseOptions & StringifyOptions
+): string
+export function pick(
+ url: string,
+ filter: (key: string, value: string | boolean | number) => boolean,
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
+): string
+export function pick(
+ url: string,
+ filter: (key: string, value: string | boolean) => boolean,
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
+): string
+export function pick(
+ url: string,
+ filter: (key: string, value: string | number) => boolean,
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
+): string
+
+/**
+Exclude query parameters from a URL. Like `.pick()` but reversed.
+
+@param url - The URL containing the query parameters to exclude.
+@param keys - The names of the query parameters to remove. All other query parameters will remain in the URL.
+@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`.
+
+@returns The URL without the excluded the query parameters.
+
+@example
+```
+queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
+//=> 'https://foo.bar?bar=2#hello'
+
+queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
+//=> 'https://foo.bar?foo=1#hello'
+```
+*/
+export function exclude(
+ url: string,
+ keys: readonly string[],
+ options?: ParseOptions & StringifyOptions
+): string
+export function exclude(
+ url: string,
+ filter: (key: string, value: string | boolean | number) => boolean,
+ options?: {parseBooleans: true, parseNumbers: true} & ParseOptions & StringifyOptions
+): string
+export function exclude(
+ url: string,
+ filter: (key: string, value: string | boolean) => boolean,
+ options?: {parseBooleans: true} & ParseOptions & StringifyOptions
+): string
+export function exclude(
+ url: string,
+ filter: (key: string, value: string | number) => boolean,
+ options?: {parseNumbers: true} & ParseOptions & StringifyOptions
+): string
diff --git a/index.js b/index.js
index 39e86e3..423b9d6 100644
--- a/index.js
+++ b/index.js
@@ -2,6 +2,7 @@
const strictUriEncode = require('strict-uri-encode');
const decodeComponent = require('decode-uri-component');
const splitOnFirst = require('split-on-first');
+const filterObject = require('filter-obj');
const isNullOrUndefined = value => value === null || value === undefined;
@@ -382,3 +383,22 @@ exports.stringifyUrl = (object, options) => {
return `${url}${queryString}${hash}`;
};
+
+exports.pick = (input, filter, options) => {
+ options = Object.assign({
+ parseFragmentIdentifier: true
+ }, options);
+
+ const {url, query, fragmentIdentifier} = exports.parseUrl(input, options);
+ return exports.stringifyUrl({
+ url,
+ query: filterObject(query, filter),
+ fragmentIdentifier
+ }, options);
+};
+
+exports.exclude = (input, filter, options) => {
+ const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value);
+
+ return exports.pick(input, exclusionFilter, options);
+};
diff --git a/index.test-d.ts b/index.test-d.ts
index 2aab3fe..2032584 100644
--- a/index.test-d.ts
+++ b/index.test-d.ts
@@ -124,3 +124,9 @@ expectType(
},
})
);
+
+// Pick
+expectType(queryString.pick('http://foo.bar/?abc=def&hij=klm', ['abc']))
+
+// Exclude
+expectType(queryString.exclude('http://foo.bar/?abc=def&hij=klm', ['abc']))
diff --git a/package.json b/package.json
index df8a1f2..6f5372b 100644
--- a/package.json
+++ b/package.json
@@ -34,10 +34,12 @@
"stringify",
"encode",
"decode",
- "searchparams"
+ "searchparams",
+ "filter"
],
"dependencies": {
"decode-uri-component": "^0.2.0",
+ "filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
diff --git a/readme.md b/readme.md
index 85f72ce..280972e 100644
--- a/readme.md
+++ b/readme.md
@@ -415,6 +415,64 @@ Type: `object`
Query items to add to the URL.
+### .pick(url, keys, options?)
+### .pick(url, filter, options?)
+
+Pick query parameters from a URL.
+
+Returns a string with the new URL.
+
+```js
+const queryString = require('query-string');
+
+queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
+//=> 'https://foo.bar?foo=1#hello'
+
+queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
+//=> 'https://foo.bar?bar=2#hello'
+```
+
+### .exclude(url, keys, options?)
+### .exclude(url, filter, options?)
+
+Exclude query parameters from a URL.
+
+Returns a string with the new URL.
+
+```js
+const queryString = require('query-string');
+
+queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
+//=> 'https://foo.bar?bar=2#hello'
+
+queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
+//=> 'https://foo.bar?foo=1#hello'
+```
+
+#### url
+
+Type: `string`
+
+The URL containing the query parameters to filter.
+
+#### keys
+
+Type: `string[]`
+
+The names of the query parameters to filter based on the function used.
+
+#### filter
+
+Type: `(key, value) => boolean`
+
+A filter predicate that will be provided the name of each query parameter and its value. The `parseNumbers` and `parseBooleans` options also affect `value`.
+
+#### options
+
+Type: `object`
+
+[Parse options](#options) and [stringify options](#options-1).
+
## Nesting
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).
diff --git a/test/exclude.js b/test/exclude.js
new file mode 100644
index 0000000..91e0d4f
--- /dev/null
+++ b/test/exclude.js
@@ -0,0 +1,17 @@
+import test from 'ava';
+import queryString from '..';
+
+test('excludes elements in a URL with a filter array', t => {
+ t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', ['c']), 'http://example.com/?a=1&b=2#a');
+});
+
+test('excludes elements in a URL with a filter predicate', t => {
+ t.is(queryString.exclude('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
+ t.is(typeof name, 'string');
+ t.is(typeof value, 'number');
+
+ return name === 'a';
+ }, {
+ parseNumbers: true
+ }), 'http://example.com/?b=2&c=3#a');
+});
diff --git a/test/pick.js b/test/pick.js
new file mode 100644
index 0000000..e5e4381
--- /dev/null
+++ b/test/pick.js
@@ -0,0 +1,17 @@
+import test from 'ava';
+import queryString from '..';
+
+test('picks elements in a URL with a filter array', t => {
+ t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', ['a', 'b']), 'http://example.com/?a=1&b=2#a');
+});
+
+test('picks elements in a URL with a filter predicate', t => {
+ t.is(queryString.pick('http://example.com/?a=1&b=2&c=3#a', (name, value) => {
+ t.is(typeof name, 'string');
+ t.is(typeof value, 'number');
+
+ return name === 'a';
+ }, {
+ parseNumbers: true
+ }), 'http://example.com/?a=1#a');
+});
From 2a178815cf9b31ea4eef31efd48d9017a29d9519 Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Wed, 10 Feb 2021 13:15:35 +0700
Subject: [PATCH 7/9] 6.14.0
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 6f5372b..3b90b26 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "query-string",
- "version": "6.13.8",
+ "version": "6.14.0",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "sindresorhus/query-string",
From a6d4a3f480b2810a8cce3c0118a2aacc6c6c7add Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Fri, 26 Feb 2021 18:15:35 +0700
Subject: [PATCH 8/9] Fix TypeScript type for `stringifyUrl()`
Fixes #308
---
index.d.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.d.ts b/index.d.ts
index b6d651b..4a115fb 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -372,7 +372,7 @@ export interface UrlObject {
/**
Overrides queries in the `url` property.
*/
- readonly query: StringifiableRecord;
+ readonly query?: StringifiableRecord;
/**
Overrides the fragment identifier in the `url` property.
From 0090a3418253eea4b2c437ba034dd445361325b2 Mon Sep 17 00:00:00 2001
From: Sindre Sorhus
Date: Fri, 26 Feb 2021 18:16:40 +0700
Subject: [PATCH 9/9] 6.14.1
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 3b90b26..c75f01a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "query-string",
- "version": "6.14.0",
+ "version": "6.14.1",
"description": "Parse and stringify URL query strings",
"license": "MIT",
"repository": "sindresorhus/query-string",