@common.js/query-string 9.1.0 → 9.4.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/README.md +1 -1
  2. package/base.d.ts +124 -20
  3. package/base.js +209 -160
  4. package/package.json +3 -3
package/README.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  The [query-string](https://www.npmjs.com/package/query-string) package exported as CommonJS modules.
4
4
 
5
- Exported from [query-string@9.1.0](https://www.npmjs.com/package/query-string/v/9.1.0) using https://github.com/etienne-martin/common.js.
5
+ Exported from [query-string@9.4.1](https://www.npmjs.com/package/query-string/v/9.4.1) using https://github.com/etienne-martin/common.js.
package/base.d.ts CHANGED
@@ -178,18 +178,18 @@ export type ParseOptions = {
178
178
  readonly parseFragmentIdentifier?: boolean;
179
179
 
180
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`.
181
+ 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`.
182
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).
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
- 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).
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[]` and `number[]`) will have no effect 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
  }
@@ -217,7 +217,7 @@ export type ParseOptions = {
217
217
  ```
218
218
 
219
219
  @example
220
- Parse `age` as a number, even when `parseNumber` is false:
220
+ Force `age` to be parsed as a number even when `parseNumbers` is false:
221
221
  ```
222
222
  import queryString from 'query-string';
223
223
 
@@ -226,24 +226,39 @@ export type ParseOptions = {
226
226
  age: 'number',
227
227
  }
228
228
  });
229
- //=> {age: 20, id: '01234', zipcode: '90210 }
229
+ //=> {age: 20, id: '01234', zipcode: '90210'}
230
230
  ```
231
231
 
232
232
  @example
233
- Parse `age` using a custom value parser:
233
+ Use a custom parser function to transform the value of `age`:
234
234
  ```
235
235
  import queryString from 'query-string';
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
+ }
241
+ });
242
+ //=> {age: 40, id: '01234', zipcode: '90210'}
243
+ ```
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,
240
255
  }
241
256
  });
242
- //=> {age: 40, id: '01234', zipcode: '90210 }
257
+ //=> {scores: [20, 40, 60]}
243
258
  ```
244
259
 
245
260
  @example
246
- Array types will have no effect when `arrayFormat` is set to `none`
261
+ Array types are ignored when `arrayFormat` is set to `'none'`:
247
262
  ```
248
263
  queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
249
264
  arrayFormat: 'none',
@@ -256,7 +271,7 @@ export type ParseOptions = {
256
271
  ```
257
272
 
258
273
  @example
259
- Parse a query utilizing all types:
274
+ Parse a query using multiple type definitions:
260
275
  ```
261
276
  import queryString from 'query-string';
262
277
 
@@ -267,16 +282,31 @@ 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
  });
274
289
  //=> {ids: '001,002,003', items: ['1', '2', '3'], price: '22.00', numbers: [1, 2, 3], double: 10, number: 20}
275
290
  ```
291
+
292
+ @example
293
+ Force `flagged` to be parsed as a boolean even when `parseBooleans` is false:
294
+ ```
295
+ queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
296
+ parseBooleans: false,
297
+ types: {
298
+ flagged: 'boolean',
299
+ isOkay: 'boolean',
300
+ },
301
+ });
302
+ //=> {isAdmin: 'true', flagged: true, isOkay: false}
303
+ ```
304
+
305
+ Note: The `'boolean'` type also converts `'0'` and `'1'` to booleans, and treats valueless keys (e.g. `?flag`) as `true`.
276
306
  */
277
307
  readonly types?: Record<
278
308
  string,
279
- 'number' | 'string' | 'string[]' | 'number[]' | ((value: string) => unknown)
309
+ 'boolean' | 'number' | 'string' | 'string[]' | 'number[]' | ((value: string) => unknown)
280
310
  >;
281
311
  };
282
312
 
@@ -310,9 +340,10 @@ export type ParsedUrl = {
310
340
  /**
311
341
  Extract the URL and the query string as an object.
312
342
 
313
- If the `parseFragmentIdentifier` option is `true`, the object will also contain a `fragmentIdentifier` property.
314
-
315
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.
316
347
 
317
348
  @example
318
349
  ```
@@ -519,17 +550,80 @@ export type StringifyOptions = {
519
550
  ```
520
551
  */
521
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;
522
598
  };
523
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
+ */
524
605
  export type Stringifiable = string | boolean | number | bigint | null | undefined; // eslint-disable-line @typescript-eslint/ban-types
525
606
 
607
+ /**
608
+ A record of stringifiable values.
609
+ */
526
610
  export type StringifiableRecord = Record<
527
611
  string,
528
612
  Stringifiable | readonly Stringifiable[]
529
613
  >;
530
614
 
531
615
  /**
532
- 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
+ ```
533
627
  */
534
628
  export function stringify(
535
629
  // TODO: Use the below instead when the following TS issues are fixed:
@@ -544,7 +638,13 @@ export function stringify(
544
638
  /**
545
639
  Extract a query string from a URL that can be passed into `.parse()`.
546
640
 
547
- 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
+ ```
548
648
  */
549
649
  export function extract(url: string): string;
550
650
 
@@ -565,6 +665,10 @@ export type UrlObject = {
565
665
  /**
566
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)
567
667
 
668
+ The `options` are the same as for `.stringify()`.
669
+
670
+ @returns A string with the URL and a query string.
671
+
568
672
  Query items in the `query` property overrides queries in the `url` property.
569
673
 
570
674
  The `fragmentIdentifier` property overrides the fragment identifier in the `url` property.
package/base.js CHANGED
@@ -42,9 +42,6 @@ function _arrayLikeToArray(arr, len) {
42
42
  function _arrayWithHoles(arr) {
43
43
  if (Array.isArray(arr)) return arr;
44
44
  }
45
- function _arrayWithoutHoles(arr) {
46
- if (Array.isArray(arr)) return _arrayLikeToArray(arr);
47
- }
48
45
  function _defineProperty(obj, key, value) {
49
46
  if (key in obj) {
50
47
  Object.defineProperty(obj, key, {
@@ -63,9 +60,6 @@ function _interopRequireDefault(obj) {
63
60
  default: obj
64
61
  };
65
62
  }
66
- function _iterableToArray(iter) {
67
- if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
68
- }
69
63
  function _iterableToArrayLimit(arr, i) {
70
64
  var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
71
65
  if (_i == null) return;
@@ -93,9 +87,6 @@ function _iterableToArrayLimit(arr, i) {
93
87
  function _nonIterableRest() {
94
88
  throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
95
89
  }
96
- function _nonIterableSpread() {
97
- throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
98
- }
99
90
  function _objectSpread(target) {
100
91
  for(var i = 1; i < arguments.length; i++){
101
92
  var source = arguments[i] != null ? arguments[i] : {};
@@ -114,9 +105,6 @@ function _objectSpread(target) {
114
105
  function _slicedToArray(arr, i) {
115
106
  return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
116
107
  }
117
- function _toConsumableArray(arr) {
118
- return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
119
- }
120
108
  function _unsupportedIterableToArray(o, minLen) {
121
109
  if (!o) return;
122
110
  if (typeof o === "string") return _arrayLikeToArray(o, minLen);
@@ -146,24 +134,22 @@ function encoderForArrayFormat(options) {
146
134
  return result;
147
135
  }
148
136
  if (value === null) {
149
- return _toConsumableArray(result).concat([
150
- [
151
- encode(key, options),
152
- "[",
153
- index,
154
- "]"
155
- ].join(""),
156
- ]);
157
- }
158
- return _toConsumableArray(result).concat([
159
- [
137
+ result.push([
160
138
  encode(key, options),
161
139
  "[",
162
- encode(index, options),
163
- "]=",
164
- encode(value, options)
165
- ].join(""),
166
- ]);
140
+ index,
141
+ "]"
142
+ ].join(""));
143
+ return result;
144
+ }
145
+ result.push([
146
+ encode(key, options),
147
+ "[",
148
+ encode(index, options),
149
+ "]=",
150
+ encode(value, options)
151
+ ].join(""));
152
+ return result;
167
153
  };
168
154
  };
169
155
  }
@@ -175,20 +161,18 @@ function encoderForArrayFormat(options) {
175
161
  return result;
176
162
  }
177
163
  if (value === null) {
178
- return _toConsumableArray(result).concat([
179
- [
180
- encode(key, options),
181
- "[]"
182
- ].join(""),
183
- ]);
184
- }
185
- return _toConsumableArray(result).concat([
186
- [
164
+ result.push([
187
165
  encode(key, options),
188
- "[]=",
189
- encode(value, options)
190
- ].join(""),
191
- ]);
166
+ "[]"
167
+ ].join(""));
168
+ return result;
169
+ }
170
+ result.push([
171
+ encode(key, options),
172
+ "[]=",
173
+ encode(value, options)
174
+ ].join(""));
175
+ return result;
192
176
  };
193
177
  };
194
178
  }
@@ -200,20 +184,18 @@ function encoderForArrayFormat(options) {
200
184
  return result;
201
185
  }
202
186
  if (value === null) {
203
- return _toConsumableArray(result).concat([
204
- [
205
- encode(key, options),
206
- ":list="
207
- ].join(""),
208
- ]);
209
- }
210
- return _toConsumableArray(result).concat([
211
- [
187
+ result.push([
212
188
  encode(key, options),
213
- ":list=",
214
- encode(value, options)
215
- ].join(""),
216
- ]);
189
+ ":list="
190
+ ].join(""));
191
+ return result;
192
+ }
193
+ result.push([
194
+ encode(key, options),
195
+ ":list=",
196
+ encode(value, options)
197
+ ].join(""));
198
+ return result;
217
199
  };
218
200
  };
219
201
  }
@@ -230,20 +212,15 @@ function encoderForArrayFormat(options) {
230
212
  // Translate null to an empty string so that it doesn't serialize as 'null'
231
213
  value = value === null ? "" : value;
232
214
  if (result.length === 0) {
233
- return [
234
- [
235
- encode(key, options),
236
- keyValueSeparator,
237
- encode(value, options)
238
- ].join("")
239
- ];
240
- }
241
- return [
242
- [
243
- result,
215
+ result.push([
216
+ encode(key, options),
217
+ keyValueSeparator,
244
218
  encode(value, options)
245
- ].join(options.arrayFormatSeparator)
246
- ];
219
+ ].join(""));
220
+ return result;
221
+ }
222
+ result.push(encode(value, options));
223
+ return result;
247
224
  };
248
225
  };
249
226
  }
@@ -255,17 +232,15 @@ function encoderForArrayFormat(options) {
255
232
  return result;
256
233
  }
257
234
  if (value === null) {
258
- return _toConsumableArray(result).concat([
259
- encode(key, options),
260
- ]);
235
+ result.push(encode(key, options));
236
+ return result;
261
237
  }
262
- return _toConsumableArray(result).concat([
263
- [
264
- encode(key, options),
265
- "=",
266
- encode(value, options)
267
- ].join(""),
268
- ]);
238
+ result.push([
239
+ encode(key, options),
240
+ "=",
241
+ encode(value, options)
242
+ ].join(""));
243
+ return result;
269
244
  };
270
245
  };
271
246
  }
@@ -304,9 +279,14 @@ function parserForArrayFormat(options) {
304
279
  ];
305
280
  return;
306
281
  }
307
- accumulator[key] = _toConsumableArray(accumulator[key]).concat([
308
- value
309
- ]);
282
+ if (!Array.isArray(accumulator[key])) {
283
+ accumulator[key] = [
284
+ accumulator[key],
285
+ value
286
+ ];
287
+ return;
288
+ }
289
+ accumulator[key].push(value);
310
290
  };
311
291
  }
312
292
  case "colon-list-separator":
@@ -324,9 +304,14 @@ function parserForArrayFormat(options) {
324
304
  ];
325
305
  return;
326
306
  }
327
- accumulator[key] = _toConsumableArray(accumulator[key]).concat([
328
- value
329
- ]);
307
+ if (!Array.isArray(accumulator[key])) {
308
+ accumulator[key] = [
309
+ accumulator[key],
310
+ value
311
+ ];
312
+ return;
313
+ }
314
+ accumulator[key].push(value);
330
315
  };
331
316
  }
332
317
  case "comma":
@@ -334,9 +319,7 @@ function parserForArrayFormat(options) {
334
319
  {
335
320
  return function(key, value, accumulator) {
336
321
  var isArray = typeof value === "string" && value.includes(options.arrayFormatSeparator);
337
- var isEncodedArray = typeof value === "string" && !isArray && decode(value, options).includes(options.arrayFormatSeparator);
338
- value = isEncodedArray ? decode(value, options) : value;
339
- var newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(function(item) {
322
+ var newValue = isArray ? value.split(options.arrayFormatSeparator).map(function(item) {
340
323
  return decode(item, options);
341
324
  }) : value === null ? value : decode(value, options);
342
325
  accumulator[key] = newValue;
@@ -351,14 +334,36 @@ function parserForArrayFormat(options) {
351
334
  accumulator[key] = value ? decode(value, options) : value;
352
335
  return;
353
336
  }
354
- var arrayValue = value === null ? [] : value.split(options.arrayFormatSeparator).map(function(item) {
355
- return decode(item, options);
356
- });
337
+ var arrayValue = value === null ? [] : decode(value, options).split(options.arrayFormatSeparator);
357
338
  if (accumulator[key] === undefined) {
358
339
  accumulator[key] = arrayValue;
359
340
  return;
360
341
  }
361
- accumulator[key] = _toConsumableArray(accumulator[key]).concat(_toConsumableArray(arrayValue));
342
+ if (!Array.isArray(accumulator[key])) {
343
+ accumulator[key] = [
344
+ accumulator[key]
345
+ ];
346
+ }
347
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
348
+ try {
349
+ for(var _iterator = arrayValue[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
350
+ var item = _step.value;
351
+ accumulator[key].push(item);
352
+ }
353
+ } catch (err) {
354
+ _didIteratorError = true;
355
+ _iteratorError = err;
356
+ } finally{
357
+ try {
358
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
359
+ _iterator.return();
360
+ }
361
+ } finally{
362
+ if (_didIteratorError) {
363
+ throw _iteratorError;
364
+ }
365
+ }
366
+ }
362
367
  };
363
368
  }
364
369
  default:
@@ -368,11 +373,14 @@ function parserForArrayFormat(options) {
368
373
  accumulator[key] = value;
369
374
  return;
370
375
  }
371
- accumulator[key] = _toConsumableArray([
372
- accumulator[key]
373
- ].flat()).concat([
376
+ if (Array.isArray(accumulator[key])) {
377
+ accumulator[key].push(value);
378
+ return;
379
+ }
380
+ accumulator[key] = [
381
+ accumulator[key],
374
382
  value
375
- ]);
383
+ ];
376
384
  };
377
385
  }
378
386
  }
@@ -422,6 +430,11 @@ function getHash(url) {
422
430
  }
423
431
  return hash;
424
432
  }
433
+ function getUrlWithoutQuery(url) {
434
+ // Avoid `split('?')` so query-heavy URLs don't allocate large arrays.
435
+ var queryStart = url.indexOf("?");
436
+ return queryStart === -1 ? url : url.slice(0, queryStart);
437
+ }
425
438
  function parseValue(value, options, type) {
426
439
  if (type === "string" && typeof value === "string") {
427
440
  return value;
@@ -429,12 +442,31 @@ function parseValue(value, options, type) {
429
442
  if (typeof type === "function" && typeof value === "string") {
430
443
  return type(value);
431
444
  }
432
- if (options.parseBooleans && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
445
+ if (type === "boolean" && value === null) {
446
+ return true;
447
+ }
448
+ if (type === "boolean" && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
433
449
  return value.toLowerCase() === "true";
434
450
  }
451
+ if (type === "boolean" && value !== null && (value.toLowerCase() === "1" || value.toLowerCase() === "0")) {
452
+ return value.toLowerCase() === "1";
453
+ }
454
+ if (type === "string[]" && options.arrayFormat !== "none" && typeof value === "string") {
455
+ return [
456
+ value
457
+ ];
458
+ }
459
+ if (type === "number[]" && options.arrayFormat !== "none" && !Number.isNaN(Number(value)) && typeof value === "string" && value.trim() !== "") {
460
+ return [
461
+ Number(value)
462
+ ];
463
+ }
435
464
  if (type === "number" && !Number.isNaN(Number(value)) && typeof value === "string" && value.trim() !== "") {
436
465
  return Number(value);
437
466
  }
467
+ if (options.parseBooleans && value !== null && (value.toLowerCase() === "true" || value.toLowerCase() === "false")) {
468
+ return value.toLowerCase() === "true";
469
+ }
438
470
  if (options.parseNumbers && !Number.isNaN(Number(value)) && typeof value === "string" && value.trim() !== "") {
439
471
  return Number(value);
440
472
  }
@@ -469,64 +501,56 @@ function parse(query, options) {
469
501
  if (!query) {
470
502
  return returnValue;
471
503
  }
472
- var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
473
- try {
474
- for(var _iterator = query.split("&")[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
475
- var parameter = _step.value;
476
- if (parameter === "") {
477
- continue;
478
- }
479
- var parameter_ = options.decode ? parameter.replaceAll("+", " ") : parameter;
480
- var ref = _slicedToArray((0, _splitOnFirst.default)(parameter_, "="), 2), key = ref[0], value = ref[1];
481
- if (key === undefined) {
482
- key = parameter_;
483
- }
484
- // Missing `=` should be `null`:
485
- // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
486
- value = value === undefined ? null : [
487
- "comma",
488
- "separator",
489
- "bracket-separator"
490
- ].includes(options.arrayFormat) ? value : decode(value, options);
491
- formatter(decode(key, options), value, returnValue);
504
+ // Avoid `split('&')` so separator-heavy inputs don't allocate large arrays of empty strings.
505
+ var parameterStart = 0;
506
+ for(var index = 0; index <= query.length; index++){
507
+ if (index < query.length && query[index] !== "&") {
508
+ continue;
492
509
  }
493
- } catch (err) {
494
- _didIteratorError = true;
495
- _iteratorError = err;
496
- } finally{
497
- try {
498
- if (!_iteratorNormalCompletion && _iterator.return != null) {
499
- _iterator.return();
500
- }
501
- } finally{
502
- if (_didIteratorError) {
503
- throw _iteratorError;
504
- }
510
+ if (index === parameterStart) {
511
+ parameterStart = index + 1;
512
+ continue;
505
513
  }
514
+ var parameter = query.slice(parameterStart, index);
515
+ parameterStart = index + 1;
516
+ var parameter_ = options.decode ? parameter.replaceAll("+", " ") : parameter;
517
+ var ref = _slicedToArray((0, _splitOnFirst.default)(parameter_, "="), 2), key = ref[0], value = ref[1];
518
+ if (key === undefined) {
519
+ key = parameter_;
520
+ }
521
+ // Missing `=` should be `null`:
522
+ // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
523
+ value = value === undefined ? null : [
524
+ "comma",
525
+ "separator",
526
+ "bracket-separator"
527
+ ].includes(options.arrayFormat) ? value : decode(value, options);
528
+ formatter(decode(key, options), value, returnValue);
506
529
  }
507
- var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
530
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
508
531
  try {
509
- for(var _iterator1 = Object.entries(returnValue)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
510
- var _value = _slicedToArray(_step1.value, 2), key1 = _value[0], value1 = _value[1];
532
+ for(var _iterator = Object.entries(returnValue)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
533
+ var _value = _slicedToArray(_step.value, 2), key1 = _value[0], value1 = _value[1];
511
534
  if (typeof value1 === "object" && value1 !== null && options.types[key1] !== "string") {
512
- var _iteratorNormalCompletion2 = true, _didIteratorError2 = false, _iteratorError2 = undefined;
535
+ var _iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
513
536
  try {
514
- for(var _iterator2 = Object.entries(value1)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true){
515
- var _value1 = _slicedToArray(_step2.value, 2), key2 = _value1[0], value2 = _value1[1];
516
- var type = options.types[key1] ? options.types[key1].replace("[]", "") : undefined;
537
+ for(var _iterator1 = Object.entries(value1)[Symbol.iterator](), _step1; !(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done); _iteratorNormalCompletion1 = true){
538
+ var _value1 = _slicedToArray(_step1.value, 2), key2 = _value1[0], value2 = _value1[1];
539
+ var typeOption = options.types[key1];
540
+ var type = typeof typeOption === "function" ? typeOption : typeOption ? typeOption.replace("[]", "") : undefined;
517
541
  value1[key2] = parseValue(value2, options, type);
518
542
  }
519
543
  } catch (err) {
520
- _didIteratorError2 = true;
521
- _iteratorError2 = err;
544
+ _didIteratorError1 = true;
545
+ _iteratorError1 = err;
522
546
  } finally{
523
547
  try {
524
- if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
525
- _iterator2.return();
548
+ if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
549
+ _iterator1.return();
526
550
  }
527
551
  } finally{
528
- if (_didIteratorError2) {
529
- throw _iteratorError2;
552
+ if (_didIteratorError1) {
553
+ throw _iteratorError1;
530
554
  }
531
555
  }
532
556
  }
@@ -537,16 +561,16 @@ function parse(query, options) {
537
561
  }
538
562
  }
539
563
  } catch (err) {
540
- _didIteratorError1 = true;
541
- _iteratorError1 = err;
564
+ _didIteratorError = true;
565
+ _iteratorError = err;
542
566
  } finally{
543
567
  try {
544
- if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
545
- _iterator1.return();
568
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
569
+ _iterator.return();
546
570
  }
547
571
  } finally{
548
- if (_didIteratorError1) {
549
- throw _iteratorError1;
572
+ if (_didIteratorError) {
573
+ throw _iteratorError;
550
574
  }
551
575
  }
552
576
  }
@@ -605,6 +629,14 @@ function stringify(object, options) {
605
629
  }
606
630
  return keys.map(function(key) {
607
631
  var value = object[key];
632
+ // Apply replacer function if provided
633
+ if (options.replacer) {
634
+ value = options.replacer(key, value);
635
+ // If replacer returns undefined, skip this key
636
+ if (value === undefined) {
637
+ return "";
638
+ }
639
+ }
608
640
  if (value === undefined) {
609
641
  return "";
610
642
  }
@@ -615,7 +647,23 @@ function stringify(object, options) {
615
647
  if (value.length === 0 && options.arrayFormat === "bracket-separator") {
616
648
  return encode(key, options) + "[]";
617
649
  }
618
- return value.reduce(formatter(key), []).join("&");
650
+ // Apply replacer to array elements if provided
651
+ // Note: We don't re-apply replacer to the array itself, only to elements
652
+ var processedArray = value;
653
+ if (options.replacer) {
654
+ processedArray = value.map(function(item, index) {
655
+ return options.replacer("".concat(key, "[").concat(index, "]"), item);
656
+ }).filter(function(item) {
657
+ return item !== undefined;
658
+ });
659
+ }
660
+ var result = processedArray.reduce(formatter(key), []);
661
+ var arrayFormatSeparator = [
662
+ "comma",
663
+ "separator",
664
+ "bracket-separator"
665
+ ].includes(options.arrayFormat) ? options.arrayFormatSeparator : "&";
666
+ return result.join(arrayFormatSeparator);
619
667
  }
620
668
  return encode(key, options) + "=" + encode(value, options);
621
669
  }).filter(function(x) {
@@ -623,17 +671,15 @@ function stringify(object, options) {
623
671
  }).join("&");
624
672
  }
625
673
  function parseUrl(url, options) {
626
- var ref;
627
674
  options = _objectSpread({
628
675
  decode: true
629
676
  }, options);
630
- var ref1 = _slicedToArray((0, _splitOnFirst.default)(url, "#"), 2), url_ = ref1[0], hash = ref1[1];
677
+ var ref = _slicedToArray((0, _splitOnFirst.default)(url, "#"), 2), url_ = ref[0], hash = ref[1];
631
678
  if (url_ === undefined) {
632
679
  url_ = url;
633
680
  }
634
- var ref2;
635
681
  return _objectSpread({
636
- url: (ref2 = (ref = url_ === null || url_ === void 0 ? void 0 : url_.split("?")) === null || ref === void 0 ? void 0 : ref[0]) !== null && ref2 !== void 0 ? ref2 : "",
682
+ url: getUrlWithoutQuery(url_ !== null && url_ !== void 0 ? url_ : ""),
637
683
  query: parse(extract(url), options)
638
684
  }, options && options.parseFragmentIdentifier && hash ? {
639
685
  fragmentIdentifier: decode(hash, options)
@@ -644,16 +690,16 @@ function stringifyUrl(object, options) {
644
690
  encode: true,
645
691
  strict: true
646
692
  }, encodeFragmentIdentifier, true), options);
647
- var url = removeHash(object.url).split("?")[0] || "";
693
+ var url = getUrlWithoutQuery(removeHash(object.url)) || "";
648
694
  var queryFromUrl = extract(object.url);
649
- var query = _objectSpread({}, parse(queryFromUrl, {
695
+ var query = _objectSpread({}, parse(queryFromUrl, _objectSpread({
650
696
  sort: false
651
- }), object.query);
697
+ }, options)), object.query);
652
698
  var queryString = stringify(query, options);
653
699
  queryString && (queryString = "?".concat(queryString));
654
700
  var hash = getHash(object.url);
655
701
  if (typeof object.fragmentIdentifier === "string") {
656
- var urlObjectForFragmentEncode = new URL(url);
702
+ var urlObjectForFragmentEncode = new URL(url, "https://query-string.invalid");
657
703
  urlObjectForFragmentEncode.hash = object.fragmentIdentifier;
658
704
  hash = options[encodeFragmentIdentifier] ? urlObjectForFragmentEncode.hash : "#".concat(object.fragmentIdentifier);
659
705
  }
@@ -671,10 +717,13 @@ function pick(input, filter, options) {
671
717
  }, options);
672
718
  }
673
719
  function exclude(input, filter, options) {
674
- var exclusionFilter = Array.isArray(filter) ? function(key) {
675
- return !filter.includes(key);
676
- } : function(key, value) {
720
+ if (Array.isArray(filter)) {
721
+ var filterSet = new Set(filter);
722
+ return pick(input, function(key) {
723
+ return !filterSet.has(key);
724
+ }, options);
725
+ }
726
+ return pick(input, function(key, value) {
677
727
  return !filter(key, value);
678
- };
679
- return pick(input, exclusionFilter, options);
728
+ }, options);
680
729
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@common.js/query-string",
3
- "version": "9.1.0",
3
+ "version": "9.4.1",
4
4
  "description": "query-string package exported as CommonJS modules",
5
5
  "license": "MIT",
6
6
  "repository": "etienne-martin/common.js",
@@ -39,6 +39,6 @@
39
39
  }
40
40
  },
41
41
  "homepage": "https://github.com/etienne-martin/common.js#readme",
42
- "types": "./index.d.ts",
43
- "main": "./index.js"
42
+ "main": "./index.js",
43
+ "types": "./index.d.ts"
44
44
  }