query-string 9.2.1 → 9.3.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/base.d.ts +81 -6
  2. package/base.js +26 -5
  3. package/package.json +1 -1
  4. package/readme.md +59 -2
package/base.d.ts CHANGED
@@ -184,7 +184,7 @@ export type ParseOptions = {
184
184
 
185
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).
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
 
@@ -286,7 +286,8 @@ export type ParseOptions = {
286
286
  });
287
287
  //=> {isAdmin: 'true', flagged: true, isOkay: false}
288
288
  ```
289
- Note: The `'boolean'` type will also convert `'0'` and `'1'` string values to booleans.
289
+
290
+ Note: The `'boolean'` type also converts `'0'` and `'1'` to booleans, and treats valueless keys (e.g. `?flag`) as `true`.
290
291
  */
291
292
  readonly types?: Record<
292
293
  string,
@@ -324,9 +325,10 @@ export type ParsedUrl = {
324
325
  /**
325
326
  Extract the URL and the query string as an object.
326
327
 
327
- If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
328
-
329
328
  @param url - The URL to parse.
329
+ @returns An object with a `url` and `query` property.
330
+
331
+ If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
330
332
 
331
333
  @example
332
334
  ```
@@ -533,17 +535,80 @@ export type StringifyOptions = {
533
535
  ```
534
536
  */
535
537
  readonly skipEmptyString?: boolean;
538
+
539
+ /**
540
+ A function that transforms key-value pairs before stringification.
541
+
542
+ 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.
543
+
544
+ 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.
545
+
546
+ This is useful for custom serialization of non-primitive types like `Date`:
547
+
548
+ @example
549
+ ```
550
+ import queryString from 'query-string';
551
+
552
+ queryString.stringify({
553
+ date: new Date('2024-01-15T10:30:00Z'),
554
+ name: 'John'
555
+ }, {
556
+ replacer: (key, value) => {
557
+ if (value instanceof Date) {
558
+ return value.toISOString();
559
+ }
560
+
561
+ return value;
562
+ }
563
+ });
564
+ //=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'
565
+ ```
566
+
567
+ @example
568
+ ```
569
+ import queryString from 'query-string';
570
+
571
+ // Omit keys with null values using replacer instead of skipNull
572
+ queryString.stringify({
573
+ a: 1,
574
+ b: null,
575
+ c: 3
576
+ }, {
577
+ replacer: (key, value) => value === null ? undefined : value
578
+ });
579
+ //=> 'a=1&c=3'
580
+ ```
581
+ */
582
+ readonly replacer?: (key: string, value: unknown) => unknown;
536
583
  };
537
584
 
585
+ /**
586
+ Supported value types for query string parameters.
587
+
588
+ Note: `Symbol`, functions, and objects (except arrays) are not supported and will throw an error when stringified.
589
+ */
538
590
  export type Stringifiable = string | boolean | number | bigint | null | undefined; // eslint-disable-line @typescript-eslint/ban-types
539
591
 
592
+ /**
593
+ A record of stringifiable values.
594
+ */
540
595
  export type StringifiableRecord = Record<
541
596
  string,
542
597
  Stringifiable | readonly Stringifiable[]
543
598
  >;
544
599
 
545
600
  /**
546
- Stringify an object into a query string and sort the keys.
601
+ Stringify an object into a query string and sorting the keys.
602
+
603
+ @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.
604
+
605
+ @example
606
+ ```
607
+ import queryString from 'query-string';
608
+
609
+ queryString.stringify({foo: 'bar', baz: 42, qux: true});
610
+ //=> 'baz=42&foo=bar&qux=true'
611
+ ```
547
612
  */
548
613
  export function stringify(
549
614
  // TODO: Use the below instead when the following TS issues are fixed:
@@ -558,7 +623,13 @@ export function stringify(
558
623
  /**
559
624
  Extract a query string from a URL that can be passed into `.parse()`.
560
625
 
561
- Note: This behaviour can be changed with the `skipNull` option.
626
+ @example
627
+ ```
628
+ import queryString from 'query-string';
629
+
630
+ queryString.extract('https://foo.bar?foo=bar');
631
+ //=> 'foo=bar'
632
+ ```
562
633
  */
563
634
  export function extract(url: string): string;
564
635
 
@@ -579,6 +650,10 @@ export type UrlObject = {
579
650
  /**
580
651
  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)
581
652
 
653
+ The `options` are the same as for `.stringify()`.
654
+
655
+ @returns A string with the URL and a query string.
656
+
582
657
  Query items in the `query` property overrides queries in the `url` property.
583
658
 
584
659
  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
  }
@@ -309,6 +307,10 @@ function parseValue(value, options, type) {
309
307
  return type(value);
310
308
  }
311
309
 
310
+ if (type === 'boolean' && value === null) {
311
+ return true;
312
+ }
313
+
312
314
  if (type === 'boolean' && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) {
313
315
  return value.toLowerCase() === 'true';
314
316
  }
@@ -461,7 +463,17 @@ export function stringify(object, options) {
461
463
  }
462
464
 
463
465
  return keys.map(key => {
464
- const value = object[key];
466
+ let value = object[key];
467
+
468
+ // Apply replacer function if provided
469
+ if (options.replacer) {
470
+ value = options.replacer(key, value);
471
+
472
+ // If replacer returns undefined, skip this key
473
+ if (value === undefined) {
474
+ return '';
475
+ }
476
+ }
465
477
 
466
478
  if (value === undefined) {
467
479
  return '';
@@ -476,7 +488,16 @@ export function stringify(object, options) {
476
488
  return encode(key, options) + '[]';
477
489
  }
478
490
 
479
- return value
491
+ // Apply replacer to array elements if provided
492
+ // Note: We don't re-apply replacer to the array itself, only to elements
493
+ let processedArray = value;
494
+ if (options.replacer) {
495
+ processedArray = value.map((item, index) =>
496
+ options.replacer(`${key}[${index}]`, item),
497
+ ).filter(item => item !== undefined);
498
+ }
499
+
500
+ return processedArray
480
501
  .reduce(formatter(key), [])
481
502
  .join('&');
482
503
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "query-string",
3
- "version": "9.2.1",
3
+ "version": "9.3.0",
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`
@@ -217,7 +225,7 @@ queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
217
225
  //=> {isAdmin: 'true', flagged: true, isOkay: false}
218
226
  ```
219
227
 
220
- Note: The `'boolean'` type will also convert `'0'` and `'1'` string values to booleans.
228
+ Note: The `'boolean'` type also converts `'0'` and `'1'` to booleans, and treats valueless keys (e.g. `?flag`) as `true`.
221
229
 
222
230
  - `'string'`: Parse `phoneNumber` as a string (overriding the `parseNumbers` option):
223
231
 
@@ -318,6 +326,8 @@ Parse the value as a boolean type instead of string type if it's a boolean.
318
326
 
319
327
  Stringify an object into a query string and sorting the keys.
320
328
 
329
+ **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.
330
+
321
331
  #### options
322
332
 
323
333
  Type: `object`
@@ -512,11 +522,58 @@ queryString.stringify({a: '', b: ''}, {
512
522
  //=> ''
513
523
  ```
514
524
 
525
+ ##### replacer
526
+
527
+ A function that transforms key-value pairs before stringification.
528
+
529
+ Type: `function`\
530
+ Default: `undefined`
531
+
532
+ 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.
533
+
534
+ This is useful for custom serialization of non-primitive types like `Date`:
535
+
536
+ ```js
537
+ import queryString from 'query-string';
538
+
539
+ queryString.stringify({
540
+ date: new Date('2024-01-15T10:30:00Z'),
541
+ name: 'John'
542
+ }, {
543
+ replacer: (key, value) => {
544
+ if (value instanceof Date) {
545
+ return value.toISOString();
546
+ }
547
+
548
+ return value;
549
+ }
550
+ });
551
+ //=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'
552
+ ```
553
+
554
+ You can also use it to filter out keys:
555
+
556
+ ```js
557
+ import queryString from 'query-string';
558
+
559
+ queryString.stringify({
560
+ a: 1,
561
+ b: null,
562
+ c: 3
563
+ }, {
564
+ replacer: (key, value) => value === null ? undefined : value
565
+ });
566
+ //=> 'a=1&c=3'
567
+ ```
568
+
515
569
  ### .extract(string)
516
570
 
517
571
  Extract a query string from a URL that can be passed into `.parse()`.
518
572
 
519
- Note: This behaviour can be changed with the `skipNull` option.
573
+ ```js
574
+ queryString.extract('https://foo.bar?foo=bar');
575
+ //=> 'foo=bar'
576
+ ```
520
577
 
521
578
  ### .parseUrl(string, options?)
522
579