@oscarpalmer/atoms 0.187.0 → 0.187.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.
package/dist/index.d.mts CHANGED
@@ -8179,4 +8179,4 @@ declare class SizedSet<Value = unknown> extends Set<Value> {
8179
8179
  get(value: Value, update?: boolean): Value | undefined;
8180
8180
  }
8181
8181
  //#endregion
8182
- export { AnyResult, ArrayComparison, ArrayComparisonSorter, ArrayKeySorter, ArrayOrPlainObject, ArrayValueSorter, AssertProperty, Asserter, AssignOptions, Assigner, AsyncCancelableCallback, AttemptFlow, AttemptFlowPromise, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, Err, EventPosition, type Events, ExtendedErr, ExtendedResult, Flow, FlowPromise, Frozen, FulfilledPromise, FuzzyConfiguration, FuzzyOptions, FuzzyResult, FuzzySearchOptions, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, type Herald, Key$1 as Key, type KeyedQueue, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NormalizeOptions, Normalizer, NumericalKeys, NumericalValues, type Observable, type Observer, Ok, OnceAsyncCallback, OnceCallback, PROMISE_ABORT_EVENT, PROMISE_ABORT_OPTIONS, PROMISE_ERROR_NAME, PROMISE_MESSAGE_EXPECTATION_ATTEMPT, PROMISE_MESSAGE_EXPECTATION_RESULT, PROMISE_MESSAGE_EXPECTATION_TIMED, PROMISE_MESSAGE_TIMEOUT, PROMISE_STRATEGY_ALL, PROMISE_STRATEGY_DEFAULT, PROMISE_TYPE_FULFILLED, PROMISE_TYPE_REJECTED, PlainObject, Primitive, PromiseData, PromiseHandlers, PromiseOptions, PromiseParameters, PromiseStrategy, PromiseTimeoutError, PromisesItems, PromisesOptions, PromisesResult, PromisesUnwrapped, PromisesValue, PromisesValues, type Queue, type QueueError, type QueueOptions, type Queued, type QueuedResult, type RGBAColor, type RGBColor, RejectedPromise, RequiredKeys, Result, ResultMatch, RetryError, RetryOptions, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, Shaken, Simplify, SizedMap, SizedSet, Smushed, SortDirection, Sorter, type Subscription, TemplateOptions, type Timed, ToString, Transformer, TypedArray, UnionToIntersection, Unsmushed, Unsubscriber, UnwrapValue, assert, assertCondition, assertDefined, assertInstanceOf, assertIs, assertProperty, assign, asyncAttempt, asyncDebounce, asyncFlow, asyncMatchResult, asyncOnce, asyncPipe, asyncThrottle, attempt, attemptAsyncFlow, attemptAsyncPipe, attemptFlow, attemptPipe, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, copy, count, debounce, deburr, dedent, delay, deregisterCloner, deregisterComparator, deregisterEqualizer, diff, difference, drop, endsWith, endsWithArray, equal, error, exclude, exists, filter, find, findLast, first, firstOrDefault, flatFreeze, flatten, floor, flow, freeze, fromQuery, toPromise as fromResult, toPromise, fuzzy, fuzzyMatch, getArray, getArrayComparison, getColor, getError, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloatingNumber as getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getSortedIndex, getString, getTimedPromise, getUuid, getValue, groupArraysBy, groupBy, handleResult, hasValue, hasValueResult, herald, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, inMap, inSet, includes, includesArray, indexOf, indexOfArray, initializeAssigner, initializeEqualizer, initializeMerger, initializeNormalizer, initializeSorter, initializeTemplater, initializeTransformer, insert, interpolate, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFrozen, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonArrayOrPlainObject, isNonConstructor, isNonEmpty, isNonInstanceOf, isNonKey, isNonNullable, isNonNullableOrEmpty, isNonNullableOrWhitespace, isNonNumber, isNonNumerical, isNonObject, isNonPlainObject, isNonPrimitive, isNonTypedArray, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isSorted, isTypedArray, join, kebabCase, keyedQueue, last, lastIndexOf, lastOrDefault, logger, lowerCase, matchResult, max, median, memoize, merge, min, move, moveIndices, moveToIndex, noop, normalize, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, registerCloner, registerComparator, registerEqualizer, resultPromises, retry, reverse, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, settlePromise, shake, shuffle, single, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toMapArrays, toQuery, toRecord, toRecordArrays, toResult, toSet, toggle, transform, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
8182
+ export { AnyResult, ArrayComparison, ArrayComparisonSorter, ArrayKeySorter, ArrayOrPlainObject, ArrayValueSorter, AssertProperty, Asserter, AssignOptions, Assigner, AsyncCancelableCallback, AttemptFlow, AttemptFlowPromise, type Beacon, type BeaconOptions, BuiltIns, CancelableCallback, CancelablePromise, type Color, Constructor, DiffOptions, DiffResult, DiffValue, EqualOptions, Err, EventPosition, type Events, ExtendedErr, ExtendedResult, Flow, FlowPromise, Frozen, FulfilledPromise, FuzzyConfiguration, FuzzyOptions, FuzzyResult, FuzzySearchOptions, GenericAsyncCallback, GenericCallback, type HSLAColor, type HSLColor, type Herald, Key$1 as Key, type KeyedQueue, KeyedValue, type Logger, type Memoized, type MemoizedOptions, MergeOptions, Merger, NestedArray, NestedKeys, NestedPartial, NestedValue, NestedValues, NormalizeOptions, Normalizer, NumericalKeys, NumericalValues, type Observable, type Observer, Ok, OnceAsyncCallback, OnceCallback, PROMISE_ABORT_EVENT, PROMISE_ABORT_OPTIONS, PROMISE_ERROR_NAME, PROMISE_MESSAGE_EXPECTATION_ATTEMPT, PROMISE_MESSAGE_EXPECTATION_RESULT, PROMISE_MESSAGE_EXPECTATION_TIMED, PROMISE_MESSAGE_TIMEOUT, PROMISE_STRATEGY_ALL, PROMISE_STRATEGY_DEFAULT, PROMISE_TYPE_FULFILLED, PROMISE_TYPE_REJECTED, PlainObject, Primitive, PromiseData, PromiseHandlers, PromiseOptions, PromiseParameters, PromiseStrategy, PromiseTimeoutError, PromisesItems, PromisesOptions, PromisesResult, PromisesUnwrapped, PromisesValue, PromisesValues, type Queue, type QueueError, type QueueOptions, type Queued, type QueuedResult, type RGBAColor, type RGBColor, RejectedPromise, RequiredKeys, Result, ResultMatch, RetryError, RetryOptions, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, type Shaken, Simplify, SizedMap, SizedSet, type Smushed, SortDirection, Sorter, type Subscription, TemplateOptions, type Timed, ToString, Transformer, TypedArray, UnionToIntersection, type Unsmushed, Unsubscriber, UnwrapValue, assert, assertCondition, assertDefined, assertInstanceOf, assertIs, assertProperty, assign, asyncAttempt, asyncDebounce, asyncFlow, asyncMatchResult, asyncOnce, asyncPipe, asyncThrottle, attempt, attemptAsyncFlow, attemptAsyncPipe, attemptFlow, attemptPipe, attemptPromise, average, beacon, between, camelCase, cancelable, capitalize, ceil, chunk, clamp, clone, compact, compare, copy, count, debounce, deburr, dedent, delay, deregisterCloner, deregisterComparator, deregisterEqualizer, diff, difference, drop, endsWith, endsWithArray, equal, error, exclude, exists, filter, find, findLast, first, firstOrDefault, flatFreeze, flatten, floor, flow, freeze, fromQuery, toPromise as fromResult, toPromise, fuzzy, fuzzyMatch, getArray, getArrayComparison, getColor, getError, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getNormalizedHex, getNumber, getRandomBoolean, getRandomCharacters, getRandomColor, getRandomFloatingNumber as getRandomFloat, getRandomHex, getRandomInteger, getRandomItem, getRandomItems, getRgbColor, getRgbaColor, getSortedIndex, getString, getTimedPromise, getUuid, getValue, groupArraysBy, groupBy, handleResult, hasValue, hasValueResult, herald, hexToHsl, hexToHsla, hexToRgb, hexToRgba, hslToHex, hslToRgb, hslToRgba, ignoreKey, inMap, inSet, includes, includesArray, indexOf, indexOfArray, initializeAssigner, initializeEqualizer, initializeMerger, initializeNormalizer, initializeSorter, initializeTemplater, initializeTransformer, insert, interpolate, intersection, isArrayOrPlainObject, isColor, isConstructor, isEmpty, isError, isFrozen, isFulfilled, isHexColor, isHslColor, isHslLike, isHslaColor, isInstanceOf, isKey, isNonArrayOrPlainObject, isNonConstructor, isNonEmpty, isNonInstanceOf, isNonKey, isNonNullable, isNonNullableOrEmpty, isNonNullableOrWhitespace, isNonNumber, isNonNumerical, isNonObject, isNonPlainObject, isNonPrimitive, isNonTypedArray, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isOk, isPlainObject, isPrimitive, isRejected, isResult, isRgbColor, isRgbLike, isRgbaColor, isSorted, isTypedArray, join, kebabCase, keyedQueue, last, lastIndexOf, lastOrDefault, logger, lowerCase, matchResult, max, median, memoize, merge, min, move, moveIndices, moveToIndex, noop, normalize, ok, omit, once, parse, partition, pascalCase, pick, pipe, promises, push, queue, range, registerCloner, registerComparator, registerEqualizer, resultPromises, retry, reverse, rgbToHex, rgbToHsl, rgbToHsla, round, select, setValue, settlePromise, shake, shuffle, single, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, sum, swap, take, template, throttle, timed, times, titleCase, toMap, toMapArrays, toQuery, toRecord, toRecordArrays, toResult, toSet, toggle, transform, trim, truncate, tryDecode, tryEncode, union, unique, unsmush, unwrap, update, upperCase, words };
package/dist/index.mjs CHANGED
@@ -2535,7 +2535,7 @@ function setValue(data, path, value, ignoreCase) {
2535
2535
  let next = handleValue(target, currentPath, null, true, shouldIgnoreCase).value;
2536
2536
  if (typeof next !== "object" || next === null) {
2537
2537
  const nextPath = paths[index + 1];
2538
- if (EXPRESSION_INDEX.test(nextPath)) next = Array.from({ length: Number.parseInt(nextPath, 10) + 1 }, () => void 0);
2538
+ if (EXPRESSION_INDEX.test(nextPath)) next = Array.from({ length: Number(nextPath) + 1 }, () => void 0);
2539
2539
  else next = {};
2540
2540
  target[currentPath] = next;
2541
2541
  }
@@ -5322,13 +5322,26 @@ async function resultPromises(items, signal) {
5322
5322
  */
5323
5323
  function fromQuery(query) {
5324
5324
  if (typeof query !== "string" || query.trim().length === 0) return {};
5325
- const parts = query.split(AMPERSAND);
5325
+ const parts = query.split(AMPERSAND).map((part) => part.split(EQUAL).map(tryDecode)).sort(([first], [second]) => first.localeCompare(second));
5326
5326
  const { length } = parts;
5327
5327
  const parameters = {};
5328
+ let position = 0;
5329
+ let array;
5328
5330
  for (let index = 0; index < length; index += 1) {
5329
- const decoded = parts[index].split(EQUAL).map(tryDecode);
5330
- const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, "");
5331
- if (!ignoreKey(key)) setQueryValue(parameters, key, decoded[1]);
5331
+ const [key, value] = parts[index];
5332
+ if (EXPRESSION_ARRAY_SUFFIX.test(key)) {
5333
+ const named = key.replace(EXPRESSION_ARRAY_SUFFIX, "");
5334
+ if (named !== array) {
5335
+ array = named;
5336
+ position = 0;
5337
+ }
5338
+ } else {
5339
+ array = void 0;
5340
+ position = 0;
5341
+ }
5342
+ const full = array == null ? key : `${array}.${position}`;
5343
+ if (!ignoreKey(full)) setValue(parameters, full, getQueryValue(value));
5344
+ position += 1;
5332
5345
  }
5333
5346
  return parameters;
5334
5347
  }
@@ -5339,11 +5352,13 @@ function getParts(value, fromArray, prefix) {
5339
5352
  for (let index = 0; index < length; index += 1) {
5340
5353
  const key = keys[index];
5341
5354
  const val = value[key];
5342
- const full = join([prefix, fromArray ? void 0 : key], ".");
5343
- if (Array.isArray(val)) parts.push(...getParts(val, true, full));
5344
- else if (isPlainObject(val)) parts.push(...getParts(val, false, full));
5345
- else if (isDecodable(val)) parts.push(`${getString(tryEncode(full))}=${getString(tryEncode(val))}`);
5346
- else if (val instanceof Date) parts.push(`${getString(tryEncode(full))}=${val.toJSON()}`);
5355
+ const fullKey = join([prefix, fromArray ? void 0 : key], DOT);
5356
+ const encodedKey = getString(tryEncode(fullKey));
5357
+ const prefixedKey = fromArray ? join([encodedKey, index], DOT) : encodedKey;
5358
+ if (Array.isArray(val)) parts.push(...getParts(val, true, fullKey));
5359
+ else if (isPlainObject(val)) parts.push(...getParts(val, false, fullKey));
5360
+ else if (isDecodable(val)) parts.push(`${prefixedKey}=${getString(tryEncode(val))}`);
5361
+ else if (val instanceof Date) parts.push(`${prefixedKey}=${val.toJSON()}`);
5347
5362
  }
5348
5363
  return parts;
5349
5364
  }
@@ -5359,13 +5374,6 @@ function getQueryValue(value) {
5359
5374
  function isDecodable(value) {
5360
5375
  return TYPES.has(typeof value);
5361
5376
  }
5362
- function setQueryValue(parameters, key, value) {
5363
- if (EXPRESSION_DOT.test(key)) setValue(parameters, key, getQueryValue(value));
5364
- else if (key in parameters) {
5365
- if (!Array.isArray(parameters[key])) parameters[key] = [parameters[key]];
5366
- parameters[key].push(getQueryValue(value));
5367
- } else parameters[key] = getQueryValue(value);
5368
- }
5369
5377
  /**
5370
5378
  * Convert a plain _(nested)_ object to a query string
5371
5379
  *
@@ -5376,10 +5384,10 @@ function toQuery(parameters) {
5376
5384
  return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0), AMPERSAND) : "";
5377
5385
  }
5378
5386
  const AMPERSAND = "&";
5387
+ const DOT = ".";
5379
5388
  const EQUAL = "=";
5380
5389
  const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
5381
5390
  const EXPRESSION_BOOLEAN = /^(false|true)$/;
5382
- const EXPRESSION_DOT = /\./g;
5383
5391
  const TRUE = "true";
5384
5392
  const TYPES = new Set([
5385
5393
  "boolean",
@@ -20,7 +20,7 @@ function setValue(data, path, value, ignoreCase) {
20
20
  let next = handleValue(target, currentPath, null, true, shouldIgnoreCase).value;
21
21
  if (typeof next !== "object" || next === null) {
22
22
  const nextPath = paths[index + 1];
23
- if (EXPRESSION_INDEX.test(nextPath)) next = Array.from({ length: Number.parseInt(nextPath, 10) + 1 }, () => void 0);
23
+ if (EXPRESSION_INDEX.test(nextPath)) next = Array.from({ length: Number(nextPath) + 1 }, () => void 0);
24
24
  else next = {};
25
25
  target[currentPath] = next;
26
26
  }
package/dist/query.mjs CHANGED
@@ -11,13 +11,26 @@ import { setValue } from "./internal/value/set.mjs";
11
11
  */
12
12
  function fromQuery(query) {
13
13
  if (typeof query !== "string" || query.trim().length === 0) return {};
14
- const parts = query.split(AMPERSAND);
14
+ const parts = query.split(AMPERSAND).map((part) => part.split(EQUAL).map(tryDecode)).sort(([first], [second]) => first.localeCompare(second));
15
15
  const { length } = parts;
16
16
  const parameters = {};
17
+ let position = 0;
18
+ let array;
17
19
  for (let index = 0; index < length; index += 1) {
18
- const decoded = parts[index].split(EQUAL).map(tryDecode);
19
- const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, "");
20
- if (!ignoreKey(key)) setQueryValue(parameters, key, decoded[1]);
20
+ const [key, value] = parts[index];
21
+ if (EXPRESSION_ARRAY_SUFFIX.test(key)) {
22
+ const named = key.replace(EXPRESSION_ARRAY_SUFFIX, "");
23
+ if (named !== array) {
24
+ array = named;
25
+ position = 0;
26
+ }
27
+ } else {
28
+ array = void 0;
29
+ position = 0;
30
+ }
31
+ const full = array == null ? key : `${array}.${position}`;
32
+ if (!ignoreKey(full)) setValue(parameters, full, getQueryValue(value));
33
+ position += 1;
21
34
  }
22
35
  return parameters;
23
36
  }
@@ -28,11 +41,13 @@ function getParts(value, fromArray, prefix) {
28
41
  for (let index = 0; index < length; index += 1) {
29
42
  const key = keys[index];
30
43
  const val = value[key];
31
- const full = join([prefix, fromArray ? void 0 : key], ".");
32
- if (Array.isArray(val)) parts.push(...getParts(val, true, full));
33
- else if (isPlainObject(val)) parts.push(...getParts(val, false, full));
34
- else if (isDecodable(val)) parts.push(`${getString(tryEncode(full))}=${getString(tryEncode(val))}`);
35
- else if (val instanceof Date) parts.push(`${getString(tryEncode(full))}=${val.toJSON()}`);
44
+ const fullKey = join([prefix, fromArray ? void 0 : key], DOT);
45
+ const encodedKey = getString(tryEncode(fullKey));
46
+ const prefixedKey = fromArray ? join([encodedKey, index], DOT) : encodedKey;
47
+ if (Array.isArray(val)) parts.push(...getParts(val, true, fullKey));
48
+ else if (isPlainObject(val)) parts.push(...getParts(val, false, fullKey));
49
+ else if (isDecodable(val)) parts.push(`${prefixedKey}=${getString(tryEncode(val))}`);
50
+ else if (val instanceof Date) parts.push(`${prefixedKey}=${val.toJSON()}`);
36
51
  }
37
52
  return parts;
38
53
  }
@@ -48,13 +63,6 @@ function getQueryValue(value) {
48
63
  function isDecodable(value) {
49
64
  return TYPES.has(typeof value);
50
65
  }
51
- function setQueryValue(parameters, key, value) {
52
- if (EXPRESSION_DOT.test(key)) setValue(parameters, key, getQueryValue(value));
53
- else if (key in parameters) {
54
- if (!Array.isArray(parameters[key])) parameters[key] = [parameters[key]];
55
- parameters[key].push(getQueryValue(value));
56
- } else parameters[key] = getQueryValue(value);
57
- }
58
66
  /**
59
67
  * Convert a plain _(nested)_ object to a query string
60
68
  *
@@ -65,10 +73,10 @@ function toQuery(parameters) {
65
73
  return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0), AMPERSAND) : "";
66
74
  }
67
75
  const AMPERSAND = "&";
76
+ const DOT = ".";
68
77
  const EQUAL = "=";
69
78
  const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
70
79
  const EXPRESSION_BOOLEAN = /^(false|true)$/;
71
- const EXPRESSION_DOT = /\./g;
72
80
  const TRUE = "true";
73
81
  const TYPES = new Set([
74
82
  "boolean",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/atoms",
3
- "version": "0.187.0",
3
+ "version": "0.187.1",
4
4
  "description": "Atomic utilities for making your JavaScript better.",
5
5
  "keywords": [
6
6
  "helper",
@@ -257,10 +257,10 @@
257
257
  "watch": "npx vite build --watch"
258
258
  },
259
259
  "devDependencies": {
260
- "@oxlint/plugins": "^1.64",
261
- "@types/node": "^25.7",
260
+ "@oxlint/plugins": "^1.66",
261
+ "@types/node": "^25.9",
262
262
  "@vitest/coverage-istanbul": "^4.1",
263
- "eslint": "^10.3",
263
+ "eslint": "^10.4",
264
264
  "jsdom": "^29.1",
265
265
  "tsdown": "^0.22",
266
266
  "typescript": "^6",
@@ -109,7 +109,7 @@ export function setValue(data: object, path: string, value: unknown, ignoreCase?
109
109
  const nextPath = paths[index + 1];
110
110
 
111
111
  if (EXPRESSION_INDEX.test(nextPath)) {
112
- next = Array.from({length: Number.parseInt(nextPath, 10) + 1}, () => undefined);
112
+ next = Array.from({length: Number(nextPath) + 1}, () => undefined);
113
113
  } else {
114
114
  next = {};
115
115
  }
package/src/query.ts CHANGED
@@ -17,18 +17,40 @@ export function fromQuery(query: string): PlainObject {
17
17
  return {};
18
18
  }
19
19
 
20
- const parts = query.split(AMPERSAND);
20
+ const parts = query
21
+ .split(AMPERSAND)
22
+ .map(part => part.split(EQUAL).map(tryDecode))
23
+ .sort(([first], [second]) => first.localeCompare(second));
24
+
21
25
  const {length} = parts;
22
26
 
23
27
  const parameters: PlainObject = {};
24
28
 
29
+ let position = 0;
30
+ let array: string | undefined;
31
+
25
32
  for (let index = 0; index < length; index += 1) {
26
- const decoded = parts[index].split(EQUAL).map(tryDecode);
27
- const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, '');
33
+ const [key, value] = parts[index];
34
+
35
+ if (EXPRESSION_ARRAY_SUFFIX.test(key)) {
36
+ const named = key.replace(EXPRESSION_ARRAY_SUFFIX, '');
37
+
38
+ if (named !== array) {
39
+ array = named;
40
+ position = 0;
41
+ }
42
+ } else {
43
+ array = undefined;
44
+ position = 0;
45
+ }
46
+
47
+ const full = array == null ? key : `${array}.${position}`;
28
48
 
29
- if (!ignoreKey(key)) {
30
- setQueryValue(parameters, key, decoded[1]);
49
+ if (!ignoreKey(full)) {
50
+ setValue(parameters, full, getQueryValue(value));
31
51
  }
52
+
53
+ position += 1;
32
54
  }
33
55
 
34
56
  return parameters;
@@ -44,16 +66,19 @@ function getParts(value: ArrayOrPlainObject, fromArray: boolean, prefix?: string
44
66
  const key = keys[index];
45
67
  const val = value[key as never];
46
68
 
47
- const full = join([prefix, fromArray ? undefined : key], '.');
69
+ const fullKey = join([prefix, fromArray ? undefined : key], DOT);
70
+
71
+ const encodedKey = getString(tryEncode(fullKey));
72
+ const prefixedKey = fromArray ? join([encodedKey, index], DOT) : encodedKey;
48
73
 
49
74
  if (Array.isArray(val)) {
50
- parts.push(...getParts(val, true, full));
75
+ parts.push(...getParts(val, true, fullKey));
51
76
  } else if (isPlainObject(val)) {
52
- parts.push(...getParts(val, false, full));
77
+ parts.push(...getParts(val, false, fullKey));
53
78
  } else if (isDecodable(val)) {
54
- parts.push(`${getString(tryEncode(full))}=${getString(tryEncode(val))}`);
79
+ parts.push(`${prefixedKey}=${getString(tryEncode(val))}`);
55
80
  } else if (val instanceof Date) {
56
- parts.push(`${getString(tryEncode(full))}=${val.toJSON()}`);
81
+ parts.push(`${prefixedKey}=${val.toJSON()}`);
57
82
  }
58
83
  }
59
84
 
@@ -86,22 +111,6 @@ function isDecodable(value: unknown): value is boolean | number | string {
86
111
  return TYPES.has(typeof value);
87
112
  }
88
113
 
89
- function setQueryValue(parameters: PlainObject, key: string, value: string): void {
90
- if (EXPRESSION_DOT.test(key)) {
91
- setValue(parameters, key, getQueryValue(value));
92
- } else {
93
- if (key in parameters) {
94
- if (!Array.isArray(parameters[key])) {
95
- parameters[key] = [parameters[key]];
96
- }
97
-
98
- (parameters[key] as unknown[]).push(getQueryValue(value) as never);
99
- } else {
100
- parameters[key] = getQueryValue(value) as never;
101
- }
102
- }
103
- }
104
-
105
114
  /**
106
115
  * Convert a plain _(nested)_ object to a query string
107
116
  *
@@ -123,14 +132,14 @@ export function toQuery(parameters: PlainObject): string {
123
132
 
124
133
  const AMPERSAND = '&';
125
134
 
135
+ const DOT = '.';
136
+
126
137
  const EQUAL = '=';
127
138
 
128
139
  const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
129
140
 
130
141
  const EXPRESSION_BOOLEAN = /^(false|true)$/;
131
142
 
132
- const EXPRESSION_DOT = /\./g;
133
-
134
143
  const TRUE = 'true';
135
144
 
136
145
  const TYPES = new Set(['boolean', 'number', 'string']);