@oscarpalmer/atoms 0.218.0 → 0.220.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 (54) hide show
  1. package/dist/array/filter.d.mts +38 -4
  2. package/dist/array/find.d.mts +34 -0
  3. package/dist/array/first.d.mts +40 -4
  4. package/dist/array/last.d.mts +46 -10
  5. package/dist/array/partition.d.mts +24 -8
  6. package/dist/array/reverse.d.mts +6 -0
  7. package/dist/array/reverse.mjs +6 -0
  8. package/dist/array/select.d.mts +97 -116
  9. package/dist/array/select.mjs +19 -15
  10. package/dist/array/single.d.mts +22 -0
  11. package/dist/array/single.mjs +1 -1
  12. package/dist/array/sort.d.mts +54 -0
  13. package/dist/color/space/hex.d.mts +8 -0
  14. package/dist/color/space/hex.mjs +8 -0
  15. package/dist/index.d.mts +764 -189
  16. package/dist/index.mjs +508 -207
  17. package/dist/internal/array/callbacks.mjs +1 -1
  18. package/dist/internal/array/find.d.mts +3 -3
  19. package/dist/internal/array/find.mjs +71 -52
  20. package/dist/internal/array/shuffle.d.mts +5 -0
  21. package/dist/internal/array/shuffle.mjs +5 -0
  22. package/dist/internal/math/aggregate.d.mts +69 -21
  23. package/dist/internal/math/misc.d.mts +14 -0
  24. package/dist/internal/math/misc.mjs +14 -0
  25. package/dist/internal/number.d.mts +18 -0
  26. package/dist/internal/number.mjs +18 -0
  27. package/dist/internal/string/case.d.mts +63 -0
  28. package/dist/internal/string/case.mjs +63 -0
  29. package/dist/internal/string/misc.d.mts +20 -0
  30. package/dist/internal/string/misc.mjs +20 -0
  31. package/dist/logger.d.mts +12 -0
  32. package/dist/logger.mjs +12 -0
  33. package/dist/math.d.mts +90 -0
  34. package/dist/math.mjs +7 -0
  35. package/dist/models/array/array.find.model.d.mts +26 -0
  36. package/dist/models/array/array.misc.model.d.mts +2 -4
  37. package/dist/models/index.d.mts +1 -1
  38. package/dist/models/string/string.fuzzy.model.d.mts +46 -18
  39. package/dist/models/string/string.fuzzy.model.mjs +3 -1
  40. package/dist/models/value/value.merge.model.d.mts +3 -3
  41. package/dist/promise/misc.d.mts +6 -0
  42. package/dist/promise/misc.mjs +6 -0
  43. package/dist/promise/timed.d.mts +3 -0
  44. package/dist/promise/timed.mjs +3 -0
  45. package/dist/queue.d.mts +0 -1
  46. package/dist/queue.mjs +1 -1
  47. package/dist/string/fuzzy.d.mts +3 -3
  48. package/dist/string/fuzzy.mjs +171 -97
  49. package/dist/string/index.d.mts +15 -1
  50. package/dist/string/index.mjs +15 -1
  51. package/dist/string/match.d.mts +24 -0
  52. package/dist/string/match.mjs +24 -0
  53. package/dist/string/template.d.mts +4 -0
  54. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -8,7 +8,7 @@ function getArrayCallback(value) {
8
8
  switch (typeof value) {
9
9
  case "function": return value;
10
10
  case "number":
11
- case "string": return typeof value === "string" && value.includes(".") ? void 0 : (obj) => obj[value];
11
+ case "string": return (obj) => obj?.[value];
12
12
  default: return;
13
13
  }
14
14
  }
@@ -21,6 +21,15 @@ function getArrayCallbacks(bool, key, value) {
21
21
  }
22
22
  //#endregion
23
23
  //#region src/internal/array/find.ts
24
+ function addResult(result, mapped, flat) {
25
+ if (!flat) {
26
+ result.matched.push(mapped);
27
+ return;
28
+ }
29
+ const array = Array.isArray(mapped) ? mapped : [mapped];
30
+ const { length } = array;
31
+ for (let index = 0; index < length; index += 1) result.matched.push(array[index]);
32
+ }
24
33
  function createFindParameters(original) {
25
34
  const { length } = original;
26
35
  return {
@@ -29,24 +38,27 @@ function createFindParameters(original) {
29
38
  value: length === 1 && typeof original[0] !== "function" ? original[0] : original[1]
30
39
  };
31
40
  }
41
+ function findAbsoluteValueOrDefault(array, parameters, defaultValue, useDefaultValue, reversed) {
42
+ if (parameters.length === 0) {
43
+ if (Array.isArray(array) && array.length > 0) return reversed ? array.at(-1) : array[0];
44
+ return useDefaultValue ? defaultValue : void 0;
45
+ }
46
+ const index = findValue(ARRAY_FIND_VALUE_INDEX, array, parameters, reversed);
47
+ if (index === -1) return useDefaultValue ? defaultValue : void 0;
48
+ return array[index];
49
+ }
32
50
  function findComplexValues(parameters) {
33
- const { array, keyCallback, length, mapAfter, mapBefore, result, type, value } = parameters;
51
+ const { array, arrayLength, callback, limit, result, type, value } = parameters;
34
52
  const keys = /* @__PURE__ */ new Set();
35
- for (let index = 0; index < length; index += 1) {
36
- const item = array[index];
37
- let keyed;
38
- let transformed;
39
- if (mapBefore == null) {
40
- keyed = keyCallback?.(item, index, array) ?? item;
41
- transformed = item;
42
- } else {
43
- transformed = mapBefore(item, index, array);
44
- keyed = keyCallback?.(transformed, index, array) ?? transformed;
45
- }
53
+ for (let arrayIndex = 0; arrayIndex < arrayLength; arrayIndex += 1) {
54
+ const arrayItem = array[arrayIndex];
55
+ const keyed = callback == null ? arrayItem : callback(arrayItem, arrayIndex, array);
56
+ const mapped = arrayItem;
46
57
  if (type === "all" && Object.is(keyed, value) || type === "unique" && !keys.has(keyed)) {
47
58
  keys.add(keyed);
48
- result.matched.push(mapAfter?.(item, index, array) ?? transformed);
49
- } else result.notMatched.push(item);
59
+ result.matched.push(mapped);
60
+ if (result.matched.length >= limit) return result;
61
+ } else result.notMatched.push(arrayItem);
50
62
  }
51
63
  return result;
52
64
  }
@@ -54,13 +66,28 @@ function findSimpleValue(array, value, findIndex, reversed) {
54
66
  if (findIndex) return reversed ? array.lastIndexOf(value) : array.indexOf(value);
55
67
  return reversed ? array.findLast((item) => Object.is(item, value)) : array.find((item) => Object.is(item, value));
56
68
  }
57
- function findSimpleValues(array, length, value, boolCallback, mapBefore, mapAfter, result) {
58
- const callback = boolCallback ?? ((item) => Object.is(item, value));
59
- for (let index = 0; index < length; index += 1) {
60
- const item = array[index];
61
- const transformed = mapBefore?.(item, index, array) ?? item;
62
- if (callback(transformed, index, array)) result.matched.push(mapAfter?.(item, index, array) ?? transformed);
63
- else result.notMatched.push(item);
69
+ function findSimpleValues(parameters) {
70
+ const { array, arrayLength, callback, flat, limit, map, result, value } = parameters;
71
+ const { after, before } = map ?? {};
72
+ const handler = callback ?? ((item) => Object.is(item, value));
73
+ for (let arrayIndex = 0; arrayIndex < arrayLength; arrayIndex += 1) {
74
+ const arrayItem = array[arrayIndex];
75
+ let mapped = before == null ? arrayItem : before(arrayItem, arrayIndex, array);
76
+ if (!flat || after != null) {
77
+ if (handler(mapped, arrayIndex, array)) {
78
+ mapped = after == null ? mapped : after(arrayItem, arrayIndex, array);
79
+ addResult(result, mapped, flat);
80
+ if (result.matched.length >= limit) return result;
81
+ } else result.notMatched.push(arrayItem);
82
+ continue;
83
+ }
84
+ const flatItems = Array.isArray(mapped) ? mapped : [mapped];
85
+ const flatLength = flatItems.length;
86
+ for (let flatIndex = 0; flatIndex < flatLength; flatIndex += 1) {
87
+ const flatItem = flatItems[flatIndex];
88
+ if (handler(flatItem, arrayIndex, array)) result.matched.push(flatItem);
89
+ else result.notMatched.push(flatItem);
90
+ }
64
91
  }
65
92
  return result;
66
93
  }
@@ -84,50 +111,42 @@ function findValueInArray(array, callback, value, findIndex, reversed) {
84
111
  }
85
112
  return findIndex ? -1 : void 0;
86
113
  }
87
- function findAbsoluteValueOrDefault(array, parameters, defaultValue, useDefaultValue, reversed) {
88
- if (parameters.length === 0) {
89
- if (Array.isArray(array) && array.length > 0) return reversed ? array.at(-1) : array[0];
90
- return useDefaultValue ? defaultValue : void 0;
91
- }
92
- const index = findValue(ARRAY_FIND_VALUE_INDEX, array, parameters, reversed);
93
- if (index === -1) return useDefaultValue ? defaultValue : void 0;
94
- return array[index];
95
- }
96
- function findValues(type, array, parameters, mapper) {
114
+ function findValues(type, array, parameters, options) {
97
115
  const result = {
98
116
  matched: [],
99
117
  notMatched: []
100
118
  };
101
- if (!Array.isArray(array) || array.length === 0) return result;
102
- const { length } = array;
119
+ if (!Array.isArray(array)) return result;
120
+ const arrayLength = array.length;
121
+ if (arrayLength === 0) return result;
103
122
  const { bool, key, value } = createFindParameters(parameters);
104
123
  const callbacks = getArrayCallbacks(bool, key);
105
- if (type === "unique" && callbacks?.keyed == null && length >= 100) {
124
+ if (type === "unique" && callbacks?.keyed == null && arrayLength >= 100) {
106
125
  result.matched = [...new Set(array)];
107
126
  return result;
108
127
  }
109
- const mapCallback = getArrayCallback(mapper?.callback);
110
- const mapReverse = mapper?.reverse ?? false;
111
- let mapAfter;
112
- let mapBefore;
113
- if (mapCallback == null) {
114
- mapAfter = void 0;
115
- mapBefore = void 0;
116
- } else {
117
- mapAfter = mapReverse ? void 0 : mapCallback;
118
- mapBefore = mapReverse ? mapCallback : void 0;
119
- }
120
- if (callbacks?.bool != null || type === "all" && key == null) return findSimpleValues(array, length, value, callbacks?.bool, mapBefore, mapAfter, result);
121
- return findComplexValues({
128
+ const limit = options?.limit ?? Number.POSITIVE_INFINITY;
129
+ const flat = options?.map?.flat ?? false;
130
+ const mapCallback = getArrayCallback(options?.map?.callback);
131
+ const mapReverse = options?.map?.reverse ?? false;
132
+ let map;
133
+ if (mapCallback != null) map = {
134
+ before: mapReverse ? mapCallback : void 0,
135
+ after: mapReverse ? void 0 : mapCallback
136
+ };
137
+ const simple = callbacks?.bool != null || type === "all" && key == null;
138
+ const findParameters = {
122
139
  array,
123
- length,
124
- mapAfter,
125
- mapBefore,
140
+ arrayLength,
141
+ flat,
142
+ limit,
143
+ map,
126
144
  result,
127
145
  type,
128
146
  value,
129
- keyCallback: callbacks?.keyed
130
- });
147
+ callback: simple ? callbacks?.bool : callbacks?.keyed
148
+ };
149
+ return simple ? findSimpleValues(findParameters) : findComplexValues(findParameters);
131
150
  }
132
151
  //#endregion
133
152
  //#region src/array/filter.ts
@@ -506,6 +525,13 @@ function startsWithArray(haystack, needle, key) {
506
525
  * @param value Number to round down
507
526
  * @param decimals Number of decimal places to round to _(defaults to `0`)_
508
527
  * @returns Rounded number, or `Number.NaN` if the value if unable to be rounded
528
+ *
529
+ * @example
530
+ * ```typescript
531
+ * floor(10.567, 2); // => 10.56
532
+ * floor(10.567); // => 10
533
+ * floor('abc'); // => Number.NaN
534
+ * ```
509
535
  */
510
536
  function floor(value, decimals) {
511
537
  return roundNumber(Math.floor, value, decimals);
@@ -516,6 +542,13 @@ function floor(value, decimals) {
516
542
  * @param value Number to round
517
543
  * @param decimals Number of decimal places to round to _(defaults to `0`)_
518
544
  * @returns Rounded number, or `Number.NaN` if the value if unable to be rounded
545
+ *
546
+ * @example
547
+ * ```typescript
548
+ * round(10.567, 2); // => 10.57
549
+ * round(10.567); // => 11
550
+ * round('abc'); // => Number.NaN
551
+ * ```
519
552
  */
520
553
  function round(value, decimals) {
521
554
  return roundNumber(Math.round, value, decimals);
@@ -573,6 +606,11 @@ const RANDOM_DIVISOR = 2 ** 32;
573
606
  *
574
607
  * @param array Original array
575
608
  * @returns Shuffled array
609
+ *
610
+ * @example
611
+ * ```typescript
612
+ * shuffle([1, 2, 3]); // => [3, 1, 2] (maybe? it's random, you know)
613
+ * ```
576
614
  */
577
615
  function shuffle(array) {
578
616
  if (!Array.isArray(array)) return [];
@@ -805,6 +843,12 @@ function push(array, pushed) {
805
843
  *
806
844
  * @param array Array to reverse
807
845
  * @returns Reversed array
846
+ *
847
+ * @example
848
+ * ```typescript
849
+ * reverse([1, 2, 3]); // => [3, 2, 1]
850
+ * reverse(['a', 'b', 'c']); // => ['c', 'b', 'a']
851
+ * ```
808
852
  */
809
853
  function reverse(array) {
810
854
  if (!Array.isArray(array)) return [];
@@ -822,7 +866,7 @@ function reverse(array) {
822
866
  //#endregion
823
867
  //#region src/array/single.ts
824
868
  function single(array, ...parameters) {
825
- const { matched } = findValues("all", array, parameters);
869
+ const { matched } = findValues("all", array, parameters, { limit: 2 });
826
870
  if (matched.length > 1) throw new Error(SINGLE_MESSAGE);
827
871
  return matched[0];
828
872
  }
@@ -1053,22 +1097,28 @@ move.indices = moveIndices;
1053
1097
  move.toIndex = moveToIndex;
1054
1098
  //#endregion
1055
1099
  //#region src/array/select.ts
1056
- function filterAndMap(array, ...parameters) {
1057
- return selectValues(array, parameters);
1100
+ function filterAndFlatMap(array, filter, mapper) {
1101
+ return selectValues(array, filter, mapper, true, false);
1102
+ }
1103
+ function filterAndMap(array, filter, mapper) {
1104
+ return selectValues(array, filter, mapper, false, false);
1058
1105
  }
1059
- function mapAndFilter(array, ...parameters) {
1060
- return findValues("all", array, parameters, {
1061
- callback: parameters.shift(),
1062
- reverse: true
1063
- }).matched;
1106
+ function flatMapAndFilter(array, mapper, filter) {
1107
+ return selectValues(array, filter, mapper, true, true);
1064
1108
  }
1065
- function selectValues(array, parameters) {
1066
- return findValues("all", array, parameters, {
1067
- callback: parameters.pop(),
1068
- reverse: false
1069
- }).matched;
1109
+ function mapAndFilter(array, mapper, filter) {
1110
+ return selectValues(array, filter, mapper, false, true);
1070
1111
  }
1112
+ function selectValues(array, filter, mapper, flat, reverse) {
1113
+ return findValues("all", array, [filter], { map: {
1114
+ flat,
1115
+ reverse,
1116
+ callback: mapper
1117
+ } }).matched;
1118
+ }
1119
+ filterAndMap.flat = filterAndFlatMap;
1071
1120
  filterAndMap.reverse = mapAndFilter;
1121
+ mapAndFilter.flat = flatMapAndFilter;
1072
1122
  //#endregion
1073
1123
  //#region src/models/value/value.misc.model.ts
1074
1124
  const COMPARE_NAME = "compare";
@@ -1194,9 +1244,15 @@ function getString(value) {
1194
1244
  const asString = String(value.valueOf?.() ?? value);
1195
1245
  return asString.startsWith("[object ") ? JSON.stringify(value) : asString;
1196
1246
  }
1247
+ /**
1248
+ * @internal
1249
+ */
1197
1250
  function ignoreKey(key) {
1198
1251
  return STRING_EXPRESSION_IGNORED.test(key);
1199
1252
  }
1253
+ /**
1254
+ * @internal
1255
+ */
1200
1256
  function interpolate(strings, values) {
1201
1257
  const { length } = strings;
1202
1258
  let interpolated = "";
@@ -1214,6 +1270,14 @@ function interpolate(strings, values) {
1214
1270
  * @param array Array of values
1215
1271
  * @param delimiter Delimiter to use between values
1216
1272
  * @returns Joined string
1273
+ * @example
1274
+ * ```typescript
1275
+ * join(['a', 'b', 'c']); // => 'abc'
1276
+ * join(['a', 'b', 'c'], ', '); // => 'a, b, c'
1277
+ * join(['a', '', 'c']); // => 'ac'
1278
+ * join([null, 'b', undefined]); // => 'b'
1279
+ * join([]); // => ''
1280
+ * ```
1217
1281
  */
1218
1282
  function join(array, delimiter) {
1219
1283
  if (!Array.isArray(array)) return "";
@@ -1229,9 +1293,15 @@ function join(array, delimiter) {
1229
1293
  function tryCallback(value, callback) {
1230
1294
  return unwrap(attempt(() => callback(value)), value);
1231
1295
  }
1296
+ /**
1297
+ * @internal
1298
+ */
1232
1299
  function tryDecode(value) {
1233
1300
  return tryCallback(value, decodeURIComponent);
1234
1301
  }
1302
+ /**
1303
+ * @internal
1304
+ */
1235
1305
  function tryEncode(value) {
1236
1306
  return tryCallback(value, encodeURIComponent);
1237
1307
  }
@@ -2219,6 +2289,13 @@ const EXPRESSION_WHITESPACE_ONLY = /^\s*$/;
2219
2289
  * @param minimum Minimum value
2220
2290
  * @param maximum Maximum value _(inclusive)_
2221
2291
  * @returns `true` if the value is between the minimum and maximum, otherwise `false`
2292
+ *
2293
+ * @example
2294
+ * ```typescript
2295
+ * between(5, 1, 10); // => true
2296
+ * between(0, 1, 10); // => false
2297
+ * between(10, 10, 10); // => true
2298
+ * ```
2222
2299
  */
2223
2300
  function between(value, minimum, maximum) {
2224
2301
  if (![
@@ -2261,6 +2338,17 @@ function clamp(value, minimum, maximum, loop) {
2261
2338
  *
2262
2339
  * @param value Original value
2263
2340
  * @returns Original value as a number, or `Number.NaN` if the value is unable to be parsed
2341
+ *
2342
+ * @example
2343
+ * ```typescript
2344
+ * getNumber(null) // => Number.NaN
2345
+ * getNumber('foo') // => Number.NaN
2346
+ * getNumber('123') // => 123
2347
+ * getNumber(true) // => 1
2348
+ * getNumber([1, 2, 3]) // => Number.NaN
2349
+ * getNumber({a: 1}) // => Number.NaN
2350
+ * getNumber(() => 123) // => 123
2351
+ * ```
2264
2352
  */
2265
2353
  function getNumber(value) {
2266
2354
  if (typeof value === "number") return value;
@@ -2493,6 +2581,14 @@ const STRING_REPLACEMENT_CAMEL_CASE = "$1-$2";
2493
2581
  *
2494
2582
  * @param value String to convert
2495
2583
  * @returns Camel-cased string
2584
+ *
2585
+ * @example
2586
+ * ```typescript
2587
+ * camelCase('this is camel case'); // => 'thisIsCamelCase'
2588
+ * camelCase('ThisIsCamelCase'); // => 'thisIsCamelCase'
2589
+ * camelCase('this-is-camel-case'); // => 'thisIsCamelCase'
2590
+ * camelCase('this_is_camel_case'); // => 'thisIsCamelCase'
2591
+ * ```
2496
2592
  */
2497
2593
  function camelCase(value) {
2498
2594
  return toCase(STRING_CASE_CAMEL, value, true, false);
@@ -2502,6 +2598,14 @@ function camelCase(value) {
2502
2598
  *
2503
2599
  * @param value String to capitalize
2504
2600
  * @returns Capitalized string
2601
+ *
2602
+ * @example
2603
+ * ```typescript
2604
+ * capitalize('this is a test'); // => 'This is a test'
2605
+ * capitalize('This Is A Test'); // => 'This is a test'
2606
+ * capitalize('t'); // => 'T'
2607
+ * capitalize(''); // => ''
2608
+ * ```
2505
2609
  */
2506
2610
  function capitalize(value) {
2507
2611
  if (typeof value !== "string" || value.length === 0) return "";
@@ -2513,6 +2617,14 @@ function capitalize(value) {
2513
2617
  *
2514
2618
  * @param value String to convert
2515
2619
  * @returns Kebab-cased string
2620
+ *
2621
+ * @example
2622
+ * ```typescript
2623
+ * kebabCase('this is kebab case'); // => 'this-is-kebab-case'
2624
+ * kebabCase('ThisIsKebabCase'); // => 'this-is-kebab-case'
2625
+ * kebabCase('this-is-kebab-case'); // => 'this-is-kebab-case'
2626
+ * kebabCase('this_is_kebab_case'); // => 'this-is-kebab-case'
2627
+ * ```
2516
2628
  */
2517
2629
  function kebabCase(value) {
2518
2630
  return toCase(STRING_CASE_KEBAB, value, false, false);
@@ -2522,6 +2634,13 @@ function kebabCase(value) {
2522
2634
  *
2523
2635
  * @param value String to convert
2524
2636
  * @returns Lower-cased string
2637
+ *
2638
+ * @example
2639
+ * ```typescript
2640
+ * lowerCase('THIS IS A TEST'); // => 'this is a test'
2641
+ * lowerCase('this is a test'); // => 'this is a test'
2642
+ * lowerCase('t'); // => 't'
2643
+ * ```
2525
2644
  */
2526
2645
  function lowerCase(value) {
2527
2646
  if (typeof value !== "string") return "";
@@ -2533,6 +2652,14 @@ function lowerCase(value) {
2533
2652
  *
2534
2653
  * @param value String to convert
2535
2654
  * @returns Pascal-cased string
2655
+ *
2656
+ * @example
2657
+ * ```typescript
2658
+ * pascalCase('this is pascal case'); // => 'ThisIsPascalCase'
2659
+ * pascalCase('ThisIsPascalCase'); // => 'ThisIsPascalCase'
2660
+ * pascalCase('this-is-pascal-case'); // => 'ThisIsPascalCase'
2661
+ * pascalCase('this_is_pascal_case'); // => 'ThisIsPascalCase'
2662
+ * ```
2536
2663
  */
2537
2664
  function pascalCase(value) {
2538
2665
  return toCase(STRING_CASE_PASCAL, value, true, true);
@@ -2542,6 +2669,14 @@ function pascalCase(value) {
2542
2669
  *
2543
2670
  * @param value String to convert
2544
2671
  * @returns Snake-cased string
2672
+ *
2673
+ * @example
2674
+ * ```typescript
2675
+ * snakeCase('this is snake case'); // => 'this_is_snake_case'
2676
+ * snakeCase('ThisIsSnakeCase'); // => 'this_is_snake_case'
2677
+ * snakeCase('this-is-snake-case'); // => 'this_is_snake_case'
2678
+ * snakeCase('this_is_snake_case'); // => 'this_is_snake_case'
2679
+ * ```
2545
2680
  */
2546
2681
  function snakeCase(value) {
2547
2682
  return toCase(STRING_CASE_SNAKE, value, false, false);
@@ -2551,6 +2686,14 @@ function snakeCase(value) {
2551
2686
  *
2552
2687
  * @param value String to convert
2553
2688
  * @returns Title-cased string
2689
+ *
2690
+ * @example
2691
+ * ```typescript
2692
+ * titleCase('this is a test'); // => 'This Is A Test'
2693
+ * titleCase('This Is A Test'); // => 'This Is A Test'
2694
+ * titleCase('t'); // => 'T'
2695
+ * titleCase(''); // => ''
2696
+ * ```
2554
2697
  */
2555
2698
  function titleCase(value) {
2556
2699
  if (typeof value !== "string" || value.length === 0) return "";
@@ -2593,6 +2736,14 @@ function toCaseCallback(value) {
2593
2736
  *
2594
2737
  * @param value String to convert
2595
2738
  * @returns Upper-cased string
2739
+ *
2740
+ * @example
2741
+ * ```typescript
2742
+ * upperCase('this is a test'); // => 'THIS IS A TEST'
2743
+ * upperCase('This Is A Test'); // => 'THIS IS A TEST'
2744
+ * upperCase('t'); // => 'T'
2745
+ * upperCase(''); // => ''
2746
+ * ```
2596
2747
  */
2597
2748
  function upperCase(value) {
2598
2749
  if (typeof value !== "string" || value.length === 0) return "";
@@ -3337,6 +3488,9 @@ function isRejected(value) {
3337
3488
  function cancelable(executor) {
3338
3489
  return new CancelablePromise(executor);
3339
3490
  }
3491
+ /**
3492
+ * @internal
3493
+ */
3340
3494
  function handleResult(status, parameters) {
3341
3495
  const { aborter, complete, data, handlers, index, key, value } = parameters;
3342
3496
  if (aborter?.signal.aborted ?? false) return;
@@ -3360,6 +3514,9 @@ function handleResult(status, parameters) {
3360
3514
  handlers.resolve(data.result);
3361
3515
  }
3362
3516
  }
3517
+ /**
3518
+ * @internal
3519
+ */
3363
3520
  function settlePromise(settler, value, aborter) {
3364
3521
  aborter?.cancel();
3365
3522
  settler(value);
@@ -3390,6 +3547,9 @@ function delay(options) {
3390
3547
  }
3391
3548
  //#endregion
3392
3549
  //#region src/promise/timed.ts
3550
+ /**
3551
+ * @internal
3552
+ */
3393
3553
  async function getTimedPromise(promise, time, signal) {
3394
3554
  const aborter = createAborter(signal, () => {
3395
3555
  limiter.cancel();
@@ -3568,55 +3728,12 @@ function attemptPipe(initial, first, ...seconds) {
3568
3728
  attemptPipe.async = attemptAsyncPipe;
3569
3729
  //#endregion
3570
3730
  //#region src/models/string/string.fuzzy.model.ts
3731
+ const FUZZY_BOUNDARY_SEPARATOR = /[\s./:\\_-]/;
3571
3732
  const FUZZY_MESSAGE_ARRAY = "Fuzzy requires an array of items";
3572
3733
  const FUZZY_MESSAGE_HANDLER = "Fuzzy requires a key or function to stringify items";
3573
3734
  const FUZZY_PROPERTY = "$fuzzy";
3574
3735
  const FUZZY_SYMBOL = Symbol(FUZZY_PROPERTY);
3575
3736
  //#endregion
3576
- //#region src/string/match.ts
3577
- /**
3578
- * Check if a string ends with a specified substring
3579
- *
3580
- * @param haystack String to look in
3581
- * @param needle String to look for
3582
- * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
3583
- * @returns `true` if the string ends with the given substring, otherwise `false`
3584
- */
3585
- function endsWith(haystack, needle, ignoreCase) {
3586
- return match(STRING_MATCH_ENDS_WITH, haystack, needle, ignoreCase === true);
3587
- }
3588
- /**
3589
- * Check if a string includes a specified substring
3590
- *
3591
- * @param haystack String to look in
3592
- * @param needle String to look for
3593
- * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
3594
- * @returns `true` if the string includes the given substring, otherwise `false`
3595
- */
3596
- function includes(haystack, needle, ignoreCase) {
3597
- return match(STRING_MATCH_INCLUDES, haystack, needle, ignoreCase === true);
3598
- }
3599
- function match(type, haystack, needle, ignoreCase) {
3600
- if (typeof haystack !== "string" || typeof needle !== "string") return false;
3601
- matchMemoizers[type] ??= memoize(matchCallback.bind(type));
3602
- return matchMemoizers[type].run(haystack, needle, ignoreCase);
3603
- }
3604
- function matchCallback(haystack, needle, ignoreCase) {
3605
- return (ignoreCase ? haystack.toLocaleLowerCase() : haystack)[this](ignoreCase ? needle.toLocaleLowerCase() : needle);
3606
- }
3607
- /**
3608
- * Check if a string starts with a specified substring
3609
- *
3610
- * @param haystack String to look in
3611
- * @param needle String to look for
3612
- * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
3613
- * @returns `true` if the string starts with the given substring, otherwise `false`
3614
- */
3615
- function startsWith(haystack, needle, ignoreCase) {
3616
- return match(STRING_MATCH_STARTS_WITH, haystack, needle, ignoreCase === true);
3617
- }
3618
- const matchMemoizers = {};
3619
- //#endregion
3620
3737
  //#region src/string/fuzzy.ts
3621
3738
  function Fuzzy(state) {
3622
3739
  this[FUZZY_SYMBOL] = state;
@@ -3626,39 +3743,76 @@ Fuzzy.prototype.search = search;
3626
3743
  Object.defineProperties(Fuzzy.prototype, {
3627
3744
  items: {
3628
3745
  enumerable: true,
3629
- get: getFuzzyItems,
3630
- set: setFuzzyItems
3746
+ get: getItems,
3747
+ set: setItems
3631
3748
  },
3632
3749
  strings: {
3633
3750
  enumerable: true,
3634
- get: getFuzzyStrings
3751
+ get: getStrings
3635
3752
  }
3636
3753
  });
3637
- function createFuzzyItems(items) {
3638
- return items.toSorted((first, second) => first.haystack.localeCompare(second.haystack)).map(({ item }) => item);
3754
+ function compareItem(sorter) {
3755
+ return (first, second) => sorter(first.string.original, second.string.original);
3639
3756
  }
3640
- function createFuzzyOptions(input, state) {
3641
- const options = isPlainObject(input) ? input : {};
3642
- const limit = typeof input === "number" ? input : options.limit;
3757
+ function compareRanked(sorter) {
3758
+ return (first, second) => first.rank - second.rank || first.position - second.position || sorter(first.string.original, second.string.original);
3759
+ }
3760
+ function createItems(state, items) {
3761
+ state.items.original = items.map((item) => ({
3762
+ data: item,
3763
+ string: createString(state.handler(item))
3764
+ }));
3765
+ state.items.sorted = void 0;
3766
+ }
3767
+ function createOptions(input, state) {
3768
+ const options = {};
3769
+ const original = isPlainObject(input) ? input : {};
3770
+ const limit = typeof input === "number" ? input : original.limit;
3643
3771
  if (typeof limit === "number" && !Number.isNaN(limit) && limit >= 1) options.limit = floor(limit);
3644
- else options.limit = state?.limit;
3645
- options.tolerance = getTolerance(options.tolerance, state);
3772
+ else options.limit = state?.limit ?? Number.POSITIVE_INFINITY;
3773
+ options.tolerance = getTolerance(original.tolerance, state);
3646
3774
  return options;
3647
3775
  }
3648
- function createFuzzyState(items, input) {
3649
- const handler = getHandler(input);
3650
- const options = createFuzzyOptions(input);
3776
+ function createRanked(item, needle, position) {
3777
+ let boundary = position;
3778
+ while (boundary > -1 && !isBoundary(item.string, boundary)) boundary = item.string.lowercase.indexOf(needle, boundary + 1);
3779
+ if (boundary === -1) return {
3780
+ ...item,
3781
+ position,
3782
+ rank: 2
3783
+ };
3651
3784
  return {
3785
+ ...item,
3786
+ position: boundary,
3787
+ rank: boundary === 0 ? 0 : 1
3788
+ };
3789
+ }
3790
+ function createState(items, input) {
3791
+ const handler = getHandler(input);
3792
+ const options = createOptions(input);
3793
+ const sorter = getComparator(input);
3794
+ const state = {
3795
+ compare: {
3796
+ item: compareItem(sorter),
3797
+ ranked: compareRanked(sorter)
3798
+ },
3652
3799
  handler,
3653
- items: items.slice(),
3800
+ items: { original: [] },
3654
3801
  limit: options.limit,
3655
- strings: items.map(handler),
3656
3802
  tolerance: options.tolerance
3657
3803
  };
3804
+ createItems(state, items);
3805
+ return state;
3806
+ }
3807
+ function createString(original) {
3808
+ return {
3809
+ original,
3810
+ lowercase: lowerCase(original)
3811
+ };
3658
3812
  }
3659
3813
  function fuzzy(items, configuration) {
3660
3814
  if (!Array.isArray(items)) throw new TypeError(FUZZY_MESSAGE_ARRAY);
3661
- return new Fuzzy(createFuzzyState(items, configuration));
3815
+ return new Fuzzy(createState(items, configuration));
3662
3816
  }
3663
3817
  /**
3664
3818
  * Does the needle match the haystack in a fuzzy way?
@@ -3672,53 +3826,117 @@ function fuzzy(items, configuration) {
3672
3826
  function fuzzyMatch(haystack, needle) {
3673
3827
  if (typeof haystack !== "string" || typeof needle !== "string") return false;
3674
3828
  const trimmed = needle.trim();
3675
- if (includes(haystack, trimmed, true)) return true;
3676
- return getScore(haystack, trimmed) > -1;
3829
+ const lowercaseHaystack = lowerCase(haystack);
3830
+ const lowercaseNeedle = lowerCase(trimmed);
3831
+ if (lowercaseHaystack.includes(lowercaseNeedle)) return true;
3832
+ return getScore({
3833
+ lowercase: lowercaseHaystack,
3834
+ original: haystack
3835
+ }, lowercaseNeedle) > -1;
3836
+ }
3837
+ function getComparator(input) {
3838
+ if (isPlainObject(input) && typeof input.compare === "function") return input.compare;
3839
+ defaultSorter ??= Intl.Collator().compare;
3840
+ return defaultSorter;
3677
3841
  }
3678
3842
  function getHandler(input) {
3679
3843
  if (input == null || input === getString) return getString;
3680
3844
  switch (typeof input) {
3681
- case "function": return input;
3682
- case "string": return (item) => item[input];
3845
+ case "function": return (item) => getString(input(item));
3846
+ case "number":
3847
+ case "string":
3848
+ case "symbol": return (item) => getString(item?.[input]);
3683
3849
  default:
3684
3850
  if (isPlainObject(input)) return getHandler(input.key ?? input.handler);
3685
3851
  throw new TypeError(FUZZY_MESSAGE_HANDLER);
3686
3852
  }
3687
3853
  }
3688
- function getFuzzyItems() {
3689
- return this[FUZZY_SYMBOL].items.slice();
3854
+ function getItems() {
3855
+ return this[FUZZY_SYMBOL].items.original.map((item) => item.data);
3690
3856
  }
3691
- function getFuzzyStrings() {
3692
- return this[FUZZY_SYMBOL].strings.slice();
3693
- }
3694
- function getScore(haystack, needle) {
3695
- if (!isSubsequence(haystack, needle)) return -1;
3696
- const lowerCaseHaystack = lowerCase(haystack);
3697
- const lowerCaseNeedle = lowerCase(needle);
3698
- const needleLength = lowerCaseNeedle.length;
3857
+ function getMatchEnd(haystack, needle, cursor) {
3858
+ const haystackLength = haystack.length;
3859
+ const needleLength = needle.length;
3699
3860
  let needleIndex = 0;
3700
- let previousMatchIndex = -1;
3701
- let score = 0;
3702
- for (let haystackIndex = 0; haystackIndex < lowerCaseHaystack.length; haystackIndex += 1) {
3703
- if (lowerCaseHaystack[haystackIndex] === lowerCaseNeedle[needleIndex]) {
3704
- score += 1;
3705
- if (haystackIndex === 0) score += 1;
3706
- if (previousMatchIndex !== -1) {
3707
- const gap = haystackIndex - previousMatchIndex - 1;
3708
- score += max([0, 5 - gap]);
3861
+ for (let index = cursor; index < haystackLength; index += 1) if (haystack[index] === needle[needleIndex]) {
3862
+ needleIndex += 1;
3863
+ if (needleIndex === needleLength) return index;
3864
+ }
3865
+ return -1;
3866
+ }
3867
+ function getResult(state, exact, included, scored, options) {
3868
+ const result = {
3869
+ exact: [],
3870
+ included: [],
3871
+ similar: []
3872
+ };
3873
+ exact.sort(state.compare.item);
3874
+ result.exact = exact.slice(0, options.limit).map((item) => item.data);
3875
+ included.sort(state.compare.ranked);
3876
+ result.included = included.slice(0, options.limit - result.exact.length).map((item) => item.data);
3877
+ const remaining = options.limit - result.exact.length - result.included.length;
3878
+ if (remaining === 0) return result;
3879
+ const keys = Object.keys(scored).map(Number).sort((first, second) => second - first);
3880
+ const keysLength = keys.length;
3881
+ if (keysLength === 0) return result;
3882
+ const maxScore = keys[0];
3883
+ outer: for (let index = 0; index < keysLength; index += 1) {
3884
+ const key = keys[index];
3885
+ if (maxScore - key > options.tolerance) break;
3886
+ const keyed = scored[key];
3887
+ const keyedLength = keyed.length;
3888
+ keyed.sort(state.compare.item);
3889
+ for (let keyedIndex = 0; keyedIndex < keyedLength; keyedIndex += 1) {
3890
+ result.similar.push(keyed[keyedIndex].data);
3891
+ if (result.similar.length >= remaining) break outer;
3892
+ }
3893
+ }
3894
+ return result;
3895
+ }
3896
+ function getScore(item, needle) {
3897
+ const haystackLength = item.lowercase.length;
3898
+ const needleLength = needle.length;
3899
+ if (needleLength > haystackLength) return -1;
3900
+ let best = -1;
3901
+ let cursor = 0;
3902
+ while (cursor < haystackLength) {
3903
+ const end = getMatchEnd(item.lowercase, needle, cursor);
3904
+ if (end === -1) break;
3905
+ let index = end;
3906
+ let needleIndex = needleLength - 1;
3907
+ let next = -1;
3908
+ let score = 0;
3909
+ let start = end;
3910
+ while (needleIndex >= 0) {
3911
+ if (item.lowercase[index] === needle[needleIndex]) {
3912
+ score += 1;
3913
+ if (next !== -1) score += max([0, 5 - (next - index - 1)]);
3914
+ if (isBoundary(item, index)) score += 2;
3915
+ next = index;
3916
+ start = index;
3917
+ needleIndex -= 1;
3709
3918
  }
3710
- previousMatchIndex = haystackIndex;
3711
- needleIndex += 1;
3919
+ index -= 1;
3712
3920
  }
3713
- if (needleIndex === needleLength) break;
3921
+ if (start === 0) score += 1;
3922
+ if (score > best) best = score;
3923
+ cursor = start + 1;
3714
3924
  }
3715
- score -= floor(lowerCaseHaystack.length / 3);
3716
- return max([0, score]);
3925
+ if (best === -1) return -1;
3926
+ return max([0, best - floor(haystackLength / 3)]);
3927
+ }
3928
+ function getStrings() {
3929
+ return this[FUZZY_SYMBOL].items.original.map((item) => item.string.original);
3717
3930
  }
3718
3931
  function getTolerance(input, state) {
3719
3932
  if (typeof input === "number" && !Number.isNaN(input)) return input < 0 ? 0 : floor(input);
3720
3933
  return state?.tolerance ?? 5;
3721
3934
  }
3935
+ function isBoundary(string, index) {
3936
+ if (index === 0 || FUZZY_BOUNDARY_SEPARATOR.test(string.lowercase[index - 1])) return true;
3937
+ if (string.original.length !== string.lowercase.length) return false;
3938
+ return string.original[index] !== string.lowercase[index] && string.original[index - 1] === string.lowercase[index - 1];
3939
+ }
3722
3940
  /**
3723
3941
  * Is the value a fuzzy searcher?
3724
3942
  *
@@ -3730,74 +3948,48 @@ function getTolerance(input, state) {
3730
3948
  function isFuzzy(value) {
3731
3949
  return typeof value === "object" && value !== null && "$fuzzy" in value && value["$fuzzy"] === true;
3732
3950
  }
3733
- function isSubsequence(haystack, needle) {
3734
- const lowerCaseHaystack = lowerCase(haystack);
3735
- const lowerCaseNeedle = lowerCase(needle);
3736
- const haystackLength = lowerCaseHaystack.length;
3737
- const needleLength = lowerCaseNeedle.length;
3738
- let needleIndex = 0;
3739
- for (let haystackIndex = 0; haystackIndex < haystackLength; haystackIndex += 1) {
3740
- if (lowerCaseHaystack[haystackIndex] === lowerCaseNeedle[needleIndex]) needleIndex += 1;
3741
- if (needleIndex === needleLength) return true;
3742
- }
3743
- return false;
3744
- }
3745
3951
  function search(input, option) {
3746
3952
  const state = this[FUZZY_SYMBOL];
3747
- const options = option == null ? state : createFuzzyOptions(option, state);
3748
- const result = {
3749
- exact: [],
3750
- similar: []
3751
- };
3953
+ const options = option == null ? state : createOptions(option, state);
3752
3954
  const value = typeof input === "string" ? input.trim() : "";
3955
+ const lowercaseValue = lowerCase(value);
3753
3956
  if (value.length === 0) {
3754
- result.exact = state.items.slice(0, options.limit);
3755
- return result;
3957
+ state.items.sorted ??= state.items.original.toSorted(state.compare.item);
3958
+ return {
3959
+ exact: state.items.sorted.slice(0, options.limit).map((item) => item.data),
3960
+ included: [],
3961
+ similar: []
3962
+ };
3756
3963
  }
3757
- let { length } = state.items;
3758
3964
  const exact = [];
3759
- const similar = [];
3965
+ const included = [];
3760
3966
  const scored = {};
3967
+ const { length } = state.items.original;
3761
3968
  for (let index = 0; index < length; index += 1) {
3762
- const item = state.items[index];
3763
- const haystack = state.strings[index];
3764
- if (includes(haystack, value, true)) {
3765
- exact.push({
3766
- item,
3767
- haystack
3768
- });
3969
+ const item = state.items.original[index];
3970
+ const { string } = item;
3971
+ if (string.lowercase === lowercaseValue) {
3972
+ exact.push(item);
3973
+ continue;
3974
+ }
3975
+ const position = string.lowercase.indexOf(lowercaseValue);
3976
+ if (position > -1) {
3977
+ included.push(createRanked(item, lowercaseValue, position));
3769
3978
  continue;
3770
3979
  }
3771
- const score = getScore(haystack, value);
3980
+ const score = getScore(string, lowercaseValue);
3772
3981
  if (score > -1) {
3773
3982
  scored[score] ??= [];
3774
- scored[score].push({
3775
- item,
3776
- haystack
3777
- });
3778
- }
3779
- }
3780
- const keys = Object.keys(scored).map(Number).sort((first, second) => second - first);
3781
- length = keys.length;
3782
- if (length > 0 && options.tolerance > 0) {
3783
- const maxScore = keys[0];
3784
- for (let index = 0; index < length; index += 1) {
3785
- const key = keys[index];
3786
- if (maxScore - key > options.tolerance) break;
3787
- similar.push(...createFuzzyItems(scored[key]));
3983
+ scored[score].push(item);
3788
3984
  }
3789
3985
  }
3790
- result.exact = createFuzzyItems(options.limit == null ? exact : exact.slice(0, options.limit));
3791
- if (options.limit == null) result.similar = similar;
3792
- else result.similar = similar.slice(0, options.limit - result.exact.length);
3793
- return result;
3986
+ return getResult(state, exact, included, scored, options);
3794
3987
  }
3795
- function setFuzzyItems(value) {
3988
+ function setItems(value) {
3796
3989
  if (!Array.isArray(value)) throw new TypeError(FUZZY_MESSAGE_ARRAY);
3797
- const state = this[FUZZY_SYMBOL];
3798
- state.items = value.slice();
3799
- state.strings = value.map((item) => state.handler(item));
3990
+ createItems(this[FUZZY_SYMBOL], value);
3800
3991
  }
3992
+ let defaultSorter;
3801
3993
  fuzzy.is = isFuzzy;
3802
3994
  fuzzy.match = fuzzyMatch;
3803
3995
  //#endregion
@@ -3855,7 +4047,7 @@ function getUuid(html) {
3855
4047
  *
3856
4048
  * @param value JSON string to parse
3857
4049
  * @param reviver Reviver function to transform the parsed values
3858
- * @returns Parsed value or `undefined` if parsing fails
4050
+ * @returns Parsed value, or `undefined` if parsing fails
3859
4051
  */
3860
4052
  function parse(value, reviver) {
3861
4053
  try {
@@ -3869,6 +4061,13 @@ function parse(value, reviver) {
3869
4061
  *
3870
4062
  * @param value String to trim
3871
4063
  * @returns Trimmed string
4064
+ * @example
4065
+ * ```typescript
4066
+ * trim(' this is a test '); // => 'this is a test'
4067
+ * trim('this is a test'); // => 'this is a test'
4068
+ * trim(' '); // => ''
4069
+ * trim(''); // => ''
4070
+ * ```
3872
4071
  */
3873
4072
  function trim(value) {
3874
4073
  return typeof value === "string" ? value.trim() : "";
@@ -3880,6 +4079,13 @@ function trim(value) {
3880
4079
  * @param length Maximum length of the string after truncation
3881
4080
  * @param suffix Suffix to append to the truncated string
3882
4081
  * @returns Truncated string
4082
+ * @example
4083
+ * ```typescript
4084
+ * truncate('this is a test', 7); // => 'this is'
4085
+ * truncate('this is a test', 7, '...'); // => 'this ...'
4086
+ * truncate('this is a test', 20); // => 'this is a test'
4087
+ * truncate('this is a test', 0); // => ''
4088
+ * ```
3883
4089
  */
3884
4090
  function truncate(value, length, suffix) {
3885
4091
  if (typeof value !== "string" || typeof length !== "number" || length === 0) return "";
@@ -3891,6 +4097,74 @@ function truncate(value, length, suffix) {
3891
4097
  return `${value.slice(0, truncatedLength)}${actualSuffix}`;
3892
4098
  }
3893
4099
  //#endregion
4100
+ //#region src/string/match.ts
4101
+ /**
4102
+ * Check if a string ends with a specified substring
4103
+ *
4104
+ * @param haystack String to look in
4105
+ * @param needle String to look for
4106
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
4107
+ * @returns `true` if the string ends with the given substring, otherwise `false`
4108
+ *
4109
+ * @example
4110
+ * ```typescript
4111
+ * endsWith('this is a test', 'test'); // => true
4112
+ * endsWith('this is a test', 'Test'); // => false
4113
+ * endsWith('this is a test', 'Test', true); // => true
4114
+ * endsWith('this is a test', 'not'); // => false
4115
+ * ```
4116
+ */
4117
+ function endsWith(haystack, needle, ignoreCase) {
4118
+ return match(STRING_MATCH_ENDS_WITH, haystack, needle, ignoreCase === true);
4119
+ }
4120
+ /**
4121
+ * Check if a string includes a specified substring
4122
+ *
4123
+ * @param haystack String to look in
4124
+ * @param needle String to look for
4125
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
4126
+ * @returns `true` if the string includes the given substring, otherwise `false`
4127
+ *
4128
+ * @example
4129
+ * ```typescript
4130
+ * includes('this is a test', 'is'); // => true
4131
+ * includes('this is a test', 'Is'); // => false
4132
+ * includes('this is a test', 'Is', true); // => true
4133
+ * includes('this is a test', 'not'); // => false
4134
+ * ```
4135
+ */
4136
+ function includes(haystack, needle, ignoreCase) {
4137
+ return match(STRING_MATCH_INCLUDES, haystack, needle, ignoreCase === true);
4138
+ }
4139
+ function match(type, haystack, needle, ignoreCase) {
4140
+ if (typeof haystack !== "string" || typeof needle !== "string") return false;
4141
+ matchMemoizers[type] ??= memoize(matchCallback.bind(type));
4142
+ return matchMemoizers[type].run(haystack, needle, ignoreCase);
4143
+ }
4144
+ function matchCallback(haystack, needle, ignoreCase) {
4145
+ return (ignoreCase ? haystack.toLocaleLowerCase() : haystack)[this](ignoreCase ? needle.toLocaleLowerCase() : needle);
4146
+ }
4147
+ /**
4148
+ * Check if a string starts with a specified substring
4149
+ *
4150
+ * @param haystack String to look in
4151
+ * @param needle String to look for
4152
+ * @param ignoreCase Ignore case when matching? _(defaults to `false`)_
4153
+ * @returns `true` if the string starts with the given substring, otherwise `false`
4154
+ *
4155
+ * @example
4156
+ * ```typescript
4157
+ * startsWith('this is a test', 'this'); // => true
4158
+ * startsWith('this is a test', 'This'); // => false
4159
+ * startsWith('this is a test', 'This', true); // => true
4160
+ * startsWith('this is a test', 'not'); // => false
4161
+ * ```
4162
+ */
4163
+ function startsWith(haystack, needle, ignoreCase) {
4164
+ return match(STRING_MATCH_STARTS_WITH, haystack, needle, ignoreCase === true);
4165
+ }
4166
+ const matchMemoizers = {};
4167
+ //#endregion
3894
4168
  //#region src/models/string/string.normalize.model.ts
3895
4169
  const NORMALIZE_DEBURR_CHARACTERS = {
3896
4170
  Æ: "AE",
@@ -5929,6 +6203,14 @@ function getHexaColor$1() {
5929
6203
  * @param value Value to normalize
5930
6204
  * @param alpha Include alpha channel _(opacity)_? _(defaults to `false`)_
5931
6205
  * @returns Normalized hex color
6206
+ *
6207
+ * @example
6208
+ * ```typescript
6209
+ * getNormalizedHex('#abc'); // => '#aabbcc'
6210
+ * getNormalizedHex('#abc', true); // => '#aabbccff'
6211
+ * getNormalizedHex(123); // => '#000000'
6212
+ * getNormalizedHex(123, true); // => '#000000ff'
6213
+ * ```
5932
6214
  */
5933
6215
  function getNormalizedHex(value, alpha) {
5934
6216
  const includeAlpha = alpha === true;
@@ -6703,12 +6985,24 @@ function getTimeLoggerStop() {
6703
6985
  }
6704
6986
  return noop;
6705
6987
  }
6988
+ /**
6989
+ * Is the value a _Logger_?
6990
+ *
6991
+ * @param value Value to check
6992
+ * @returns `true` if the value is a _Logger_, otherwise `false`
6993
+ */
6706
6994
  function isLogger(value) {
6707
6995
  return isLoggerInstance(LOGGER_NAME, value);
6708
6996
  }
6709
6997
  function isLoggerInstance(name, value) {
6710
6998
  return typeof value === "object" && value !== null && "$logger" in value && value["$logger"] === name;
6711
6999
  }
7000
+ /**
7001
+ * Is the value a _TimeLogger_?
7002
+ *
7003
+ * @param value Value to check
7004
+ * @returns `true` if the value is a _TimeLogger_, otherwise `false`
7005
+ */
6712
7006
  function isTimeLogger(value) {
6713
7007
  return isLoggerInstance(LOGGER_NAME_TIMED, value);
6714
7008
  }
@@ -6734,6 +7028,13 @@ function average(array, key) {
6734
7028
  * @param value Number to round up
6735
7029
  * @param decimals Number of decimal places to round to _(defaults to `0`)_
6736
7030
  * @returns Rounded number, or `Number.NaN` if the value if unable to be rounded
7031
+ *
7032
+ * @example
7033
+ * ```typescript
7034
+ * ceil(1.234, 2); // => 1.24
7035
+ * ceil(1.234); // => 2
7036
+ * ceil('abc'); // => Number.NaN
7037
+ * ```
6737
7038
  */
6738
7039
  function ceil(value, decimals) {
6739
7040
  return roundNumber(Math.ceil, value, decimals);
@@ -7353,4 +7654,4 @@ var SizedSet = class extends Set {
7353
7654
  }
7354
7655
  };
7355
7656
  //#endregion
7356
- export { CancelablePromise, Logger, PromiseTimeoutError, RetryError, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SizedMap, SizedSet, push as append, push, assert, assertCondition, assertDefined, assertInstanceOf, assertIs, assertProperty, assign, asyncAttempt, asyncAttemptRun, asyncDebounce, asyncFail, asyncFlow, asyncMatchResult, asyncOnce, asyncPipe, asyncRun, asyncSucceed, asyncThrottle, asyncUse, attempt, attemptAsyncFlow, attemptAsyncPipe, attemptFlow, attemptPipe, attemptPromise, attemptRun, 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, fail, filter, filterAndMap, filterAndMap as select, find, findLast, first, firstOrDefault, flatFreeze, flatten, floor, flow, freeze, fromQuery, toPromise as fromResult, toPromise, fuzzy, fuzzyMatch, getArray, getArrayComparison, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getHwbColor, getHwbaColor, getNormalizedHex, getNumber, getRandom, 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, hexToHwb, hexToHwba, hexToRgb, hexToRgba, hslToHex, hslToHwb, hslToHwba, hslToRgb, hslToRgba, hwbToHex, hwbToHsl, hwbToHsla, hwbToRgb, hwbToRgba, ignoreKey, inMap, inSet, includes, includesArray, indexOf, indexOfArray, initializeAssigner, initializeCloner, initializeEqualizer, initializeMerger, initializeNormalizer, initializeSorter, initializeTemplater, initializeTransformer, insert, interpolate, intersection, isArrayOrPlainObject, isAsyncFailure, isAsyncGenerator, isAsyncGeneratorFunction, isAsyncOnce, isAsyncPlan, isAsyncStep, isAsyncSuccess, isBeacon, isBeaconSubscription, isColor, isConstructor, isEmpty, isError, isFailure, isFrozen, isFulfilled, isFuzzy, isGenerator, isGeneratorFunction, isHerald, isHeraldEvents, isHeraldSubscription, isHexColor, isHslColor, isHslaColor, isHwbColor, isHwbaColor, isInstanceOf, isKey, isKeyedQueue, isLogger, isNonArrayOrPlainObject, isNonConstructor, isNonEmpty, isNonInstanceOf, isNonKey, isNonNullable, isNonNullableOrEmpty, isNonNullableOrWhitespace, isNonNumber, isNonNumerical, isNonObject, isNonPlainObject, isNonPrimitive, isNonTemplateStringsArray, isNonTypedArray, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isObservable, isOk, isOnce, isPlainObject, isPlan, isPrimitive, isQueue, isQueueInstance, isRejected, isResult, isRgbColor, isRgbaColor, isSorted, isStep, isSubscription, isSubscriptions, isSuccess, isTemplateStringsArray, isTimeLogger, isTypedArray, join, kebabCase, keyedQueue, last, lastIndexOf, lastOrDefault, lowerCase, mapAndFilter, mapAndFilter as reverseSelect, matchResult, max, median, memoize, merge, min, move, moveIndices, moveToIndex, noop, normalize, ok, omit, once, parse, partition, pascalCase, pick, pipe, plan, promises, queue, range, registerCloner, registerComparator, registerEqualizer, resultPromises, retry, reverse, rgbToHex, rgbToHsl, rgbToHsla, rgbToHwb, rgbToHwba, round, run, setValue, settlePromise, shake, shuffle, single, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, subscriptions, succeed, 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, use, words };
7657
+ export { CancelablePromise, Logger, PromiseTimeoutError, RetryError, SORT_DIRECTION_ASCENDING, SORT_DIRECTION_DESCENDING, SizedMap, SizedSet, push as append, push, assert, assertCondition, assertDefined, assertInstanceOf, assertIs, assertProperty, assign, asyncAttempt, asyncAttemptRun, asyncDebounce, asyncFail, asyncFlow, asyncMatchResult, asyncOnce, asyncPipe, asyncRun, asyncSucceed, asyncThrottle, asyncUse, attempt, attemptAsyncFlow, attemptAsyncPipe, attemptFlow, attemptPipe, attemptPromise, attemptRun, 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, fail, filter, filterAndFlatMap, filterAndMap, filterAndMap as select, find, findLast, first, firstOrDefault, flatFreeze, flatMapAndFilter, flatten, floor, flow, freeze, fromQuery, toPromise as fromResult, toPromise, fuzzy, fuzzyMatch, getArray, getArrayComparison, getColor, getForegroundColor, getHexColor, getHexaColor, getHslColor, getHslaColor, getHwbColor, getHwbaColor, getNormalizedHex, getNumber, getRandom, 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, hexToHwb, hexToHwba, hexToRgb, hexToRgba, hslToHex, hslToHwb, hslToHwba, hslToRgb, hslToRgba, hwbToHex, hwbToHsl, hwbToHsla, hwbToRgb, hwbToRgba, ignoreKey, inMap, inSet, includes, includesArray, indexOf, indexOfArray, initializeAssigner, initializeCloner, initializeEqualizer, initializeMerger, initializeNormalizer, initializeSorter, initializeTemplater, initializeTransformer, insert, interpolate, intersection, isArrayOrPlainObject, isAsyncFailure, isAsyncGenerator, isAsyncGeneratorFunction, isAsyncOnce, isAsyncPlan, isAsyncStep, isAsyncSuccess, isBeacon, isBeaconSubscription, isColor, isConstructor, isEmpty, isError, isFailure, isFrozen, isFulfilled, isFuzzy, isGenerator, isGeneratorFunction, isHerald, isHeraldEvents, isHeraldSubscription, isHexColor, isHslColor, isHslaColor, isHwbColor, isHwbaColor, isInstanceOf, isKey, isKeyedQueue, isLogger, isNonArrayOrPlainObject, isNonConstructor, isNonEmpty, isNonInstanceOf, isNonKey, isNonNullable, isNonNullableOrEmpty, isNonNullableOrWhitespace, isNonNumber, isNonNumerical, isNonObject, isNonPlainObject, isNonPrimitive, isNonTemplateStringsArray, isNonTypedArray, isNullable, isNullableOrEmpty, isNullableOrWhitespace, isNumber, isNumerical, isObject, isObservable, isOk, isOnce, isPlainObject, isPlan, isPrimitive, isQueue, isRejected, isResult, isRgbColor, isRgbaColor, isSorted, isStep, isSubscription, isSubscriptions, isSuccess, isTemplateStringsArray, isTimeLogger, isTypedArray, join, kebabCase, keyedQueue, last, lastIndexOf, lastOrDefault, lowerCase, mapAndFilter, mapAndFilter as reverseSelect, matchResult, max, median, memoize, merge, min, move, moveIndices, moveToIndex, noop, normalize, ok, omit, once, parse, partition, pascalCase, pick, pipe, plan, promises, queue, range, registerCloner, registerComparator, registerEqualizer, resultPromises, retry, reverse, rgbToHex, rgbToHsl, rgbToHsla, rgbToHwb, rgbToHwba, round, run, setValue, settlePromise, shake, shuffle, single, slice, smush, snakeCase, sort, splice, startsWith, startsWithArray, subscriptions, succeed, 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, use, words };