query-string 8.2.0 → 9.1.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.
- package/base.d.ts +110 -1
- package/base.js +37 -18
- package/package.json +7 -7
- package/readme.md +110 -1
package/base.d.ts
CHANGED
|
@@ -87,7 +87,14 @@ export type ParseOptions = {
|
|
|
87
87
|
//=> {foo: ['1', '2', '3']}
|
|
88
88
|
```
|
|
89
89
|
*/
|
|
90
|
-
readonly arrayFormat?:
|
|
90
|
+
readonly arrayFormat?:
|
|
91
|
+
| 'bracket'
|
|
92
|
+
| 'index'
|
|
93
|
+
| 'comma'
|
|
94
|
+
| 'separator'
|
|
95
|
+
| 'bracket-separator'
|
|
96
|
+
| 'colon-list-separator'
|
|
97
|
+
| 'none';
|
|
91
98
|
|
|
92
99
|
/**
|
|
93
100
|
The character used to separate array elements when using `{arrayFormat: 'separator'}`.
|
|
@@ -169,6 +176,108 @@ export type ParseOptions = {
|
|
|
169
176
|
```
|
|
170
177
|
*/
|
|
171
178
|
readonly parseFragmentIdentifier?: boolean;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
Specify a pre-defined schema to be used when parsing values. The types specified will take precedence over options such as: `parseNumber`, `parseBooleans`, and `arrayFormat`.
|
|
182
|
+
|
|
183
|
+
Use this feature to override the type of a value. This can be useful when the type is ambiguous such as a phone number (see example 1 and 2).
|
|
184
|
+
|
|
185
|
+
It is possible to provide a custom function as the parameter type. The parameter's value will equal the function's return value (see example 4).
|
|
186
|
+
|
|
187
|
+
NOTE: Array types (`string[]` and `number[]`) will have no effect if `arrayFormat` is set to `none` (see example 5).
|
|
188
|
+
|
|
189
|
+
@default {}
|
|
190
|
+
|
|
191
|
+
@example
|
|
192
|
+
Parse `phoneNumber` as a string, overriding the `parseNumber` option:
|
|
193
|
+
```
|
|
194
|
+
import queryString from 'query-string';
|
|
195
|
+
|
|
196
|
+
queryString.parse('?phoneNumber=%2B380951234567&id=1', {
|
|
197
|
+
parseNumbers: true,
|
|
198
|
+
types: {
|
|
199
|
+
phoneNumber: 'string',
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
//=> {phoneNumber: '+380951234567', id: 1}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
@example
|
|
206
|
+
Parse `items` as an array of strings, overriding the `parseNumber` option:
|
|
207
|
+
```
|
|
208
|
+
import queryString from 'query-string';
|
|
209
|
+
|
|
210
|
+
queryString.parse('?age=20&items=1%2C2%2C3', {
|
|
211
|
+
parseNumber: true,
|
|
212
|
+
types: {
|
|
213
|
+
items: 'string[]',
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
//=> {age: 20, items: ['1', '2', '3']}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
@example
|
|
220
|
+
Parse `age` as a number, even when `parseNumber` is false:
|
|
221
|
+
```
|
|
222
|
+
import queryString from 'query-string';
|
|
223
|
+
|
|
224
|
+
queryString.parse('?age=20&id=01234&zipcode=90210', {
|
|
225
|
+
types: {
|
|
226
|
+
age: 'number',
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
//=> {age: 20, id: '01234', zipcode: '90210 }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
@example
|
|
233
|
+
Parse `age` using a custom value parser:
|
|
234
|
+
```
|
|
235
|
+
import queryString from 'query-string';
|
|
236
|
+
|
|
237
|
+
queryString.parse('?age=20&id=01234&zipcode=90210', {
|
|
238
|
+
types: {
|
|
239
|
+
age: (value) => value * 2,
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
//=> {age: 40, id: '01234', zipcode: '90210 }
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
@example
|
|
246
|
+
Array types will have no effect when `arrayFormat` is set to `none`
|
|
247
|
+
```
|
|
248
|
+
queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
|
|
249
|
+
arrayFormat: 'none',
|
|
250
|
+
types: {
|
|
251
|
+
ids: 'number[]',
|
|
252
|
+
foods: 'string[]',
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
//=> {ids:'001,002,003', foods:'apple,orange,mango'}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
@example
|
|
259
|
+
Parse a query utilizing all types:
|
|
260
|
+
```
|
|
261
|
+
import queryString from 'query-string';
|
|
262
|
+
|
|
263
|
+
queryString.parse('?ids=001%2C002%2C003&items=1%2C2%2C3&price=22%2E00&numbers=1%2C2%2C3&double=5&number=20', {
|
|
264
|
+
arrayFormat: 'comma',
|
|
265
|
+
types: {
|
|
266
|
+
ids: 'string',
|
|
267
|
+
items: 'string[]',
|
|
268
|
+
price: 'string',
|
|
269
|
+
numbers: 'number[]',
|
|
270
|
+
double: (value) => value * 2,
|
|
271
|
+
number: 'number',
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
//=> {ids: '001,002,003', items: ['1', '2', '3'], price: '22.00', numbers: [1, 2, 3], double: 10, number: 20}
|
|
275
|
+
```
|
|
276
|
+
*/
|
|
277
|
+
readonly types?: Record<
|
|
278
|
+
string,
|
|
279
|
+
'number' | 'string' | 'string[]' | 'number[]' | ((value: string) => unknown)
|
|
280
|
+
>;
|
|
172
281
|
};
|
|
173
282
|
|
|
174
283
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
package/base.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import decodeComponent from 'decode-uri-component';
|
|
2
|
-
import splitOnFirst from 'split-on-first';
|
|
3
2
|
import {includeKeys} from 'filter-obj';
|
|
3
|
+
import splitOnFirst from 'split-on-first';
|
|
4
4
|
|
|
5
5
|
const isNullOrUndefined = value => value === null || value === undefined;
|
|
6
6
|
|
|
7
7
|
// eslint-disable-next-line unicorn/prefer-code-point
|
|
8
|
-
const strictUriEncode = string => encodeURIComponent(string).
|
|
8
|
+
const strictUriEncode = string => encodeURIComponent(string).replaceAll(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
9
9
|
|
|
10
10
|
const encodeFragmentIdentifier = Symbol('encodeFragmentIdentifier');
|
|
11
11
|
|
|
@@ -87,7 +87,7 @@ function encoderForArrayFormat(options) {
|
|
|
87
87
|
case 'comma':
|
|
88
88
|
case 'separator':
|
|
89
89
|
case 'bracket-separator': {
|
|
90
|
-
const
|
|
90
|
+
const keyValueSeparator = options.arrayFormat === 'bracket-separator'
|
|
91
91
|
? '[]='
|
|
92
92
|
: '=';
|
|
93
93
|
|
|
@@ -104,7 +104,7 @@ function encoderForArrayFormat(options) {
|
|
|
104
104
|
value = value === null ? '' : value;
|
|
105
105
|
|
|
106
106
|
if (result.length === 0) {
|
|
107
|
-
return [[encode(key, options),
|
|
107
|
+
return [[encode(key, options), keyValueSeparator, encode(value, options)].join('')];
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
return [[result, encode(value, options)].join(options.arrayFormatSeparator)];
|
|
@@ -300,11 +300,25 @@ function getHash(url) {
|
|
|
300
300
|
return hash;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
function parseValue(value, options) {
|
|
303
|
+
function parseValue(value, options, type) {
|
|
304
|
+
if (type === 'string' && typeof value === 'string') {
|
|
305
|
+
return value;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (typeof type === 'function' && typeof value === 'string') {
|
|
309
|
+
return type(value);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
|
|
313
|
+
return value.toLowerCase() === 'true';
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (type === 'number' && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
|
|
317
|
+
return Number(value);
|
|
318
|
+
}
|
|
319
|
+
|
|
304
320
|
if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) {
|
|
305
|
-
|
|
306
|
-
} else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
|
|
307
|
-
value = value.toLowerCase() === 'true';
|
|
321
|
+
return Number(value);
|
|
308
322
|
}
|
|
309
323
|
|
|
310
324
|
return value;
|
|
@@ -328,6 +342,7 @@ export function parse(query, options) {
|
|
|
328
342
|
arrayFormatSeparator: ',',
|
|
329
343
|
parseNumbers: false,
|
|
330
344
|
parseBooleans: false,
|
|
345
|
+
types: Object.create(null),
|
|
331
346
|
...options,
|
|
332
347
|
};
|
|
333
348
|
|
|
@@ -353,7 +368,7 @@ export function parse(query, options) {
|
|
|
353
368
|
continue;
|
|
354
369
|
}
|
|
355
370
|
|
|
356
|
-
const parameter_ = options.decode ? parameter.
|
|
371
|
+
const parameter_ = options.decode ? parameter.replaceAll('+', ' ') : parameter;
|
|
357
372
|
|
|
358
373
|
let [key, value] = splitOnFirst(parameter_, '=');
|
|
359
374
|
|
|
@@ -368,12 +383,15 @@ export function parse(query, options) {
|
|
|
368
383
|
}
|
|
369
384
|
|
|
370
385
|
for (const [key, value] of Object.entries(returnValue)) {
|
|
371
|
-
if (typeof value === 'object' && value !== null) {
|
|
386
|
+
if (typeof value === 'object' && value !== null && options.types[key] !== 'string') {
|
|
372
387
|
for (const [key2, value2] of Object.entries(value)) {
|
|
373
|
-
|
|
388
|
+
const type = options.types[key] ? options.types[key].replace('[]', '') : undefined;
|
|
389
|
+
value[key2] = parseValue(value2, options, type);
|
|
374
390
|
}
|
|
391
|
+
} else if (typeof value === 'object' && value !== null && options.types[key] === 'string') {
|
|
392
|
+
returnValue[key] = Object.values(value).join(options.arrayFormatSeparator);
|
|
375
393
|
} else {
|
|
376
|
-
returnValue[key] = parseValue(value, options);
|
|
394
|
+
returnValue[key] = parseValue(value, options, options.types[key]);
|
|
377
395
|
}
|
|
378
396
|
}
|
|
379
397
|
|
|
@@ -395,10 +413,13 @@ export function stringify(object, options) {
|
|
|
395
413
|
return '';
|
|
396
414
|
}
|
|
397
415
|
|
|
398
|
-
options = {
|
|
416
|
+
options = {
|
|
417
|
+
encode: true,
|
|
399
418
|
strict: true,
|
|
400
419
|
arrayFormat: 'none',
|
|
401
|
-
arrayFormatSeparator: ',',
|
|
420
|
+
arrayFormatSeparator: ',',
|
|
421
|
+
...options,
|
|
422
|
+
};
|
|
402
423
|
|
|
403
424
|
validateArrayFormatSeparator(options.arrayFormatSeparator);
|
|
404
425
|
|
|
@@ -484,12 +505,10 @@ export function stringifyUrl(object, options) {
|
|
|
484
505
|
};
|
|
485
506
|
|
|
486
507
|
let queryString = stringify(query, options);
|
|
487
|
-
|
|
488
|
-
queryString = `?${queryString}`;
|
|
489
|
-
}
|
|
508
|
+
queryString &&= `?${queryString}`;
|
|
490
509
|
|
|
491
510
|
let hash = getHash(object.url);
|
|
492
|
-
if (object.fragmentIdentifier) {
|
|
511
|
+
if (typeof object.fragmentIdentifier === 'string') {
|
|
493
512
|
const urlObjectForFragmentEncode = new URL(url);
|
|
494
513
|
urlObjectForFragmentEncode.hash = object.fragmentIdentifier;
|
|
495
514
|
hash = options[encodeFragmentIdentifier] ? urlObjectForFragmentEncode.hash : `#${object.fragmentIdentifier}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "query-string",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.1.0",
|
|
4
4
|
"description": "Parse and stringify URL query strings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/query-string",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"sideEffects": false,
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=
|
|
20
|
+
"node": ">=18"
|
|
21
21
|
},
|
|
22
22
|
"scripts": {
|
|
23
23
|
"benchmark": "node benchmark.js",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"split-on-first": "^3.0.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"ava": "^
|
|
54
|
+
"ava": "^6.1.1",
|
|
55
55
|
"benchmark": "^2.1.4",
|
|
56
|
-
"deep-equal": "^2.
|
|
57
|
-
"fast-check": "^3.
|
|
58
|
-
"tsd": "^0.
|
|
59
|
-
"xo": "^0.
|
|
56
|
+
"deep-equal": "^2.2.3",
|
|
57
|
+
"fast-check": "^3.15.1",
|
|
58
|
+
"tsd": "^0.30.7",
|
|
59
|
+
"xo": "^0.57.0"
|
|
60
60
|
},
|
|
61
61
|
"tsd": {
|
|
62
62
|
"compilerOptions": {
|
package/readme.md
CHANGED
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
npm install query-string
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
> [!WARNING]
|
|
48
|
+
> Remember the hyphen! Do not install the deprecated [`querystring`](https://github.com/Gozala/querystring) package!
|
|
48
49
|
|
|
49
50
|
For browser usage, this package targets the latest version of Chrome, Firefox, and Safari.
|
|
50
51
|
|
|
@@ -223,6 +224,114 @@ queryString.parse('foo=true', {parseBooleans: true});
|
|
|
223
224
|
|
|
224
225
|
Parse the value as a boolean type instead of string type if it's a boolean.
|
|
225
226
|
|
|
227
|
+
##### types
|
|
228
|
+
|
|
229
|
+
Type: `object`\
|
|
230
|
+
Default: `{}`
|
|
231
|
+
|
|
232
|
+
Specify a pre-defined schema to be used when parsing values. The types specified will take precedence over options such as: `parseNumber`, `parseBooleans`, and `arrayFormat`.
|
|
233
|
+
|
|
234
|
+
Use this feature to override the type of a value. This can be useful when the type is ambiguous such as a phone number.
|
|
235
|
+
|
|
236
|
+
It is possible to provide a custom function as the parameter type. The parameter's value will equal the function's return value.
|
|
237
|
+
|
|
238
|
+
Supported Types:
|
|
239
|
+
|
|
240
|
+
- `'string'`: Parse `phoneNumber` as a string (overriding the `parseNumber` option):
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
import queryString from 'query-string';
|
|
244
|
+
|
|
245
|
+
queryString.parse('?phoneNumber=%2B380951234567&id=1', {
|
|
246
|
+
parseNumbers: true,
|
|
247
|
+
types: {
|
|
248
|
+
phoneNumber: 'string',
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
//=> {phoneNumber: '+380951234567', id: 1}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
- `'number'`: Parse `age` as a number (even when `parseNumber` is false):
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
import queryString from 'query-string';
|
|
258
|
+
|
|
259
|
+
queryString.parse('?age=20&id=01234&zipcode=90210', {
|
|
260
|
+
types: {
|
|
261
|
+
age: 'number',
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
//=> {age: 20, id: '01234', zipcode: '90210 }
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
- `'string[]'`: Parse `items` as an array of strings (overriding the `parseNumber` option):
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
import queryString from 'query-string';
|
|
271
|
+
|
|
272
|
+
queryString.parse('?age=20&items=1%2C2%2C3', {
|
|
273
|
+
parseNumber: true,
|
|
274
|
+
types: {
|
|
275
|
+
items: 'string[]',
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
//=> {age: 20, items: ['1', '2', '3']}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- `'number[]'`: Parse `items` as an array of numbers (even when `parseNumber` is false):
|
|
282
|
+
|
|
283
|
+
```js
|
|
284
|
+
import queryString from 'query-string';
|
|
285
|
+
|
|
286
|
+
queryString.parse('?age=20&items=1%2C2%2C3', {
|
|
287
|
+
types: {
|
|
288
|
+
items: 'number[]',
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
//=> {age: '20', items: [1, 2, 3]}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
- `'Function'`: Provide a custom function as the parameter type. The parameter's value will equal the function's return value.
|
|
295
|
+
|
|
296
|
+
```js
|
|
297
|
+
import queryString from 'query-string';
|
|
298
|
+
|
|
299
|
+
queryString.parse('?age=20&id=01234&zipcode=90210', {
|
|
300
|
+
types: {
|
|
301
|
+
age: (value) => value * 2,
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
//=> {age: 40, id: '01234', zipcode: '90210 }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
NOTE: Array types (`string[]` and `number[]`) will have no effect if `arrayFormat` is set to `none`.
|
|
308
|
+
|
|
309
|
+
```js
|
|
310
|
+
queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
|
|
311
|
+
arrayFormat: 'none',
|
|
312
|
+
types: {
|
|
313
|
+
ids: 'number[]',
|
|
314
|
+
foods: 'string[]',
|
|
315
|
+
},
|
|
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
|
+
});
|
|
330
|
+
//=> {age: 40, id: '01234', zipcode: '90210 }
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Parse the value as a boolean type instead of string type if it's a boolean.
|
|
334
|
+
|
|
226
335
|
### .stringify(object, options?)
|
|
227
336
|
|
|
228
337
|
Stringify an object into a query string and sorting the keys.
|