query-string 9.2.2 → 9.3.1

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 +100 -11
  2. package/base.js +24 -6
  3. package/package.json +1 -1
  4. package/readme.md +71 -7
package/base.d.ts CHANGED
@@ -182,14 +182,14 @@ export type ParseOptions = {
182
182
 
183
183
  Use this option to explicitly define the type of a specific parameter—particularly useful in cases where the type might otherwise be ambiguous (e.g., phone numbers or IDs).
184
184
 
185
- You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result (see Example 4).
185
+ You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result. When used with array formats (like `comma`, `separator`, `bracket`, etc.), the function is applied to each array element individually.
186
186
 
187
- NOTE: Array types (`string[]`, `number[]`) are ignored if `arrayFormat` is set to `'none'`. (See Example 5.)
187
+ NOTE: Array types (`string[]`, `number[]`) are ignored if `arrayFormat` is set to `'none'`.
188
188
 
189
189
  @default {}
190
190
 
191
191
  @example
192
- Parse `phoneNumber` as a string, overriding the `parseNumber` option:
192
+ Parse `phoneNumber` as a string, overriding the `parseNumbers` option:
193
193
  ```
194
194
  import queryString from 'query-string';
195
195
 
@@ -203,12 +203,12 @@ export type ParseOptions = {
203
203
  ```
204
204
 
205
205
  @example
206
- Parse `items` as an array of strings, overriding the `parseNumber` option:
206
+ Parse `items` as an array of strings, overriding the `parseNumbers` option:
207
207
  ```
208
208
  import queryString from 'query-string';
209
209
 
210
210
  queryString.parse('?age=20&items=1%2C2%2C3', {
211
- parseNumber: true,
211
+ parseNumbers: true,
212
212
  types: {
213
213
  items: 'string[]',
214
214
  }
@@ -236,12 +236,27 @@ export type ParseOptions = {
236
236
 
237
237
  queryString.parse('?age=20&id=01234&zipcode=90210', {
238
238
  types: {
239
- age: (value) => value * 2,
239
+ age: value => value * 2,
240
240
  }
241
241
  });
242
242
  //=> {age: 40, id: '01234', zipcode: '90210'}
243
243
  ```
244
244
 
245
+ @example
246
+ Custom functions are applied to each array element when using array formats:
247
+ ```
248
+ import queryString from 'query-string';
249
+
250
+ // With arrays, the function is applied to each element
251
+ queryString.parse('?scores=10,20,30', {
252
+ arrayFormat: 'comma',
253
+ types: {
254
+ scores: value => Number(value) * 2,
255
+ }
256
+ });
257
+ //=> {scores: [20, 40, 60]}
258
+ ```
259
+
245
260
  @example
246
261
  Array types are ignored when `arrayFormat` is set to `'none'`:
247
262
  ```
@@ -267,7 +282,7 @@ export type ParseOptions = {
267
282
  items: 'string[]',
268
283
  price: 'string',
269
284
  numbers: 'number[]',
270
- double: (value) => value * 2,
285
+ double: value => value * 2,
271
286
  number: 'number',
272
287
  },
273
288
  });
@@ -325,9 +340,10 @@ export type ParsedUrl = {
325
340
  /**
326
341
  Extract the URL and the query string as an object.
327
342
 
328
- If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
329
-
330
343
  @param url - The URL to parse.
344
+ @returns An object with a `url` and `query` property.
345
+
346
+ If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
331
347
 
332
348
  @example
333
349
  ```
@@ -534,17 +550,80 @@ export type StringifyOptions = {
534
550
  ```
535
551
  */
536
552
  readonly skipEmptyString?: boolean;
553
+
554
+ /**
555
+ A function that transforms key-value pairs before stringification.
556
+
557
+ 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.
558
+
559
+ 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.
560
+
561
+ This is useful for custom serialization of non-primitive types like `Date`:
562
+
563
+ @example
564
+ ```
565
+ import queryString from 'query-string';
566
+
567
+ queryString.stringify({
568
+ date: new Date('2024-01-15T10:30:00Z'),
569
+ name: 'John'
570
+ }, {
571
+ replacer: (key, value) => {
572
+ if (value instanceof Date) {
573
+ return value.toISOString();
574
+ }
575
+
576
+ return value;
577
+ }
578
+ });
579
+ //=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'
580
+ ```
581
+
582
+ @example
583
+ ```
584
+ import queryString from 'query-string';
585
+
586
+ // Omit keys with null values using replacer instead of skipNull
587
+ queryString.stringify({
588
+ a: 1,
589
+ b: null,
590
+ c: 3
591
+ }, {
592
+ replacer: (key, value) => value === null ? undefined : value
593
+ });
594
+ //=> 'a=1&c=3'
595
+ ```
596
+ */
597
+ readonly replacer?: (key: string, value: unknown) => unknown;
537
598
  };
538
599
 
600
+ /**
601
+ Supported value types for query string parameters.
602
+
603
+ Note: `Symbol`, functions, and objects (except arrays) are not supported and will throw an error when stringified.
604
+ */
539
605
  export type Stringifiable = string | boolean | number | bigint | null | undefined; // eslint-disable-line @typescript-eslint/ban-types
540
606
 
607
+ /**
608
+ A record of stringifiable values.
609
+ */
541
610
  export type StringifiableRecord = Record<
542
611
  string,
543
612
  Stringifiable | readonly Stringifiable[]
544
613
  >;
545
614
 
546
615
  /**
547
- Stringify an object into a query string and sort the keys.
616
+ Stringify an object into a query string and sorting the keys.
617
+
618
+ @param object - Object to stringify. Supported value types are: `string`, `number`, `bigint`, `boolean`, `null`, `undefined`, and arrays of these types. Other types like `Symbol`, functions, or objects (except arrays) will throw an error.
619
+
620
+ @example
621
+ ```
622
+ import queryString from 'query-string';
623
+
624
+ queryString.stringify({foo: 'bar', baz: 42, qux: true});
625
+ //=> 'baz=42&foo=bar&qux=true'
626
+ ```
548
627
  */
549
628
  export function stringify(
550
629
  // TODO: Use the below instead when the following TS issues are fixed:
@@ -559,7 +638,13 @@ export function stringify(
559
638
  /**
560
639
  Extract a query string from a URL that can be passed into `.parse()`.
561
640
 
562
- Note: This behaviour can be changed with the `skipNull` option.
641
+ @example
642
+ ```
643
+ import queryString from 'query-string';
644
+
645
+ queryString.extract('https://foo.bar?foo=bar');
646
+ //=> 'foo=bar'
647
+ ```
563
648
  */
564
649
  export function extract(url: string): string;
565
650
 
@@ -580,6 +665,10 @@ export type UrlObject = {
580
665
  /**
581
666
  Stringify an object into a URL with a query string and sorting the keys. The inverse of [`.parseUrl()`](https://github.com/sindresorhus/query-string#parseurlstring-options)
582
667
 
668
+ The `options` are the same as for `.stringify()`.
669
+
670
+ @returns A string with the URL and a query string.
671
+
583
672
  Query items in the `query` property overrides queries in the `url` property.
584
673
 
585
674
  The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.
package/base.js CHANGED
@@ -202,9 +202,7 @@ function parserForArrayFormat(options) {
202
202
  case 'separator': {
203
203
  return (key, value, accumulator) => {
204
204
  const isArray = typeof value === 'string' && value.includes(options.arrayFormatSeparator);
205
- const isEncodedArray = (typeof value === 'string' && !isArray && decode(value, options).includes(options.arrayFormatSeparator));
206
- value = isEncodedArray ? decode(value, options) : value;
207
- const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : (value === null ? value : decode(value, options));
205
+ const newValue = isArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : (value === null ? value : decode(value, options));
208
206
  accumulator[key] = newValue;
209
207
  };
210
208
  }
@@ -405,7 +403,8 @@ export function parse(query, options) {
405
403
  for (const [key, value] of Object.entries(returnValue)) {
406
404
  if (typeof value === 'object' && value !== null && options.types[key] !== 'string') {
407
405
  for (const [key2, value2] of Object.entries(value)) {
408
- const type = options.types[key] ? options.types[key].replace('[]', '') : undefined;
406
+ const typeOption = options.types[key];
407
+ const type = typeof typeOption === 'function' ? typeOption : (typeOption ? typeOption.replace('[]', '') : undefined);
409
408
  value[key2] = parseValue(value2, options, type);
410
409
  }
411
410
  } else if (typeof value === 'object' && value !== null && options.types[key] === 'string') {
@@ -465,7 +464,17 @@ export function stringify(object, options) {
465
464
  }
466
465
 
467
466
  return keys.map(key => {
468
- const value = object[key];
467
+ let value = object[key];
468
+
469
+ // Apply replacer function if provided
470
+ if (options.replacer) {
471
+ value = options.replacer(key, value);
472
+
473
+ // If replacer returns undefined, skip this key
474
+ if (value === undefined) {
475
+ return '';
476
+ }
477
+ }
469
478
 
470
479
  if (value === undefined) {
471
480
  return '';
@@ -480,7 +489,16 @@ export function stringify(object, options) {
480
489
  return encode(key, options) + '[]';
481
490
  }
482
491
 
483
- return value
492
+ // Apply replacer to array elements if provided
493
+ // Note: We don't re-apply replacer to the array itself, only to elements
494
+ let processedArray = value;
495
+ if (options.replacer) {
496
+ processedArray = value.map((item, index) =>
497
+ options.replacer(`${key}[${index}]`, item),
498
+ ).filter(item => item !== undefined);
499
+ }
500
+
501
+ return processedArray
484
502
  .reduce(formatter(key), [])
485
503
  .join('&');
486
504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "query-string",
3
- "version": "9.2.2",
3
+ "version": "9.3.1",
4
4
  "description": "Parse and stringify URL query strings",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/query-string",
package/readme.md CHANGED
@@ -52,6 +52,14 @@ Parse a query string into an object. Leading `?` or `#` are ignored, so you can
52
52
 
53
53
  The returned object is created with [`Object.create(null)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) and thus does not have a `prototype`.
54
54
 
55
+ ```js
56
+ queryString.parse('?foo=bar');
57
+ //=> {foo: 'bar'}
58
+
59
+ queryString.parse('#token=secret&name=jhon');
60
+ //=> {token: 'secret', name: 'jhon'}
61
+ ```
62
+
55
63
  #### options
56
64
 
57
65
  Type: `object`
@@ -193,13 +201,11 @@ Parse the value as a boolean type instead of string type if it's a boolean.
193
201
  Type: `object`\
194
202
  Default: `{}`
195
203
 
196
-
197
204
  Specifies a schema for parsing query values with explicit type declarations. When defined, the types provided here take precedence over general parsing options such as `parseNumbers`, `parseBooleans`, and `arrayFormat`.
198
205
 
199
206
  Use this option to explicitly define the type of a specific parameter—particularly useful in cases where the type might otherwise be ambiguous (e.g., phone numbers or IDs).
200
207
 
201
- You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result.
202
-
208
+ You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result. When used with array formats (like `comma`, `separator`, `bracket`, etc.), the function is applied to each array element individually.
203
209
 
204
210
  Supported Types:
205
211
 
@@ -273,17 +279,26 @@ queryString.parse('?age=20&items=1%2C2%2C3', {
273
279
  //=> {age: '20', items: [1, 2, 3]}
274
280
  ```
275
281
 
276
- - `'Function'`: Provide a custom function as the parameter type. The parameter's value will equal the function's return value.
282
+ - `'Function'`: Provide a custom function as the parameter type. The parameter's value will equal the function's return value. When used with array formats (like `comma`, `separator`, `bracket`, etc.), the function is applied to each array element individually.
277
283
 
278
284
  ```js
279
285
  import queryString from 'query-string';
280
286
 
281
287
  queryString.parse('?age=20&id=01234&zipcode=90210', {
282
288
  types: {
283
- age: (value) => value * 2,
289
+ age: value => value * 2,
284
290
  }
285
291
  });
286
292
  //=> {age: 40, id: '01234', zipcode: '90210'}
293
+
294
+ // With arrays, the function is applied to each element
295
+ queryString.parse('?scores=10,20,30', {
296
+ arrayFormat: 'comma',
297
+ types: {
298
+ scores: value => Number(value) * 2,
299
+ }
300
+ });
301
+ //=> {scores: [20, 40, 60]}
287
302
  ```
288
303
 
289
304
  NOTE: Array types (`string[]`, `number[]`) are ignored if `arrayFormat` is set to `'none'`.
@@ -306,7 +321,7 @@ import queryString from 'query-string';
306
321
 
307
322
  queryString.parse('?age=20&id=01234&zipcode=90210', {
308
323
  types: {
309
- age: (value) => value * 2,
324
+ age: value => value * 2,
310
325
  }
311
326
  });
312
327
  //=> {age: 40, id: '01234', zipcode: '90210'}
@@ -318,6 +333,8 @@ Parse the value as a boolean type instead of string type if it's a boolean.
318
333
 
319
334
  Stringify an object into a query string and sorting the keys.
320
335
 
336
+ **Supported value types:** `string`, `number`, `bigint`, `boolean`, `null`, `undefined`, and arrays of these types. Other types like `Symbol`, functions, or objects (except arrays) will throw an error.
337
+
321
338
  #### options
322
339
 
323
340
  Type: `object`
@@ -512,11 +529,58 @@ queryString.stringify({a: '', b: ''}, {
512
529
  //=> ''
513
530
  ```
514
531
 
532
+ ##### replacer
533
+
534
+ A function that transforms key-value pairs before stringification.
535
+
536
+ Type: `function`\
537
+ Default: `undefined`
538
+
539
+ 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.
540
+
541
+ This is useful for custom serialization of non-primitive types like `Date`:
542
+
543
+ ```js
544
+ import queryString from 'query-string';
545
+
546
+ queryString.stringify({
547
+ date: new Date('2024-01-15T10:30:00Z'),
548
+ name: 'John'
549
+ }, {
550
+ replacer: (key, value) => {
551
+ if (value instanceof Date) {
552
+ return value.toISOString();
553
+ }
554
+
555
+ return value;
556
+ }
557
+ });
558
+ //=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'
559
+ ```
560
+
561
+ You can also use it to filter out keys:
562
+
563
+ ```js
564
+ import queryString from 'query-string';
565
+
566
+ queryString.stringify({
567
+ a: 1,
568
+ b: null,
569
+ c: 3
570
+ }, {
571
+ replacer: (key, value) => value === null ? undefined : value
572
+ });
573
+ //=> 'a=1&c=3'
574
+ ```
575
+
515
576
  ### .extract(string)
516
577
 
517
578
  Extract a query string from a URL that can be passed into `.parse()`.
518
579
 
519
- Note: This behaviour can be changed with the `skipNull` option.
580
+ ```js
581
+ queryString.extract('https://foo.bar?foo=bar');
582
+ //=> 'foo=bar'
583
+ ```
520
584
 
521
585
  ### .parseUrl(string, options?)
522
586