nhb-toolbox 4.29.10 → 4.29.20

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 (38) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/dist/cjs/array/Finder.js +16 -20
  3. package/dist/cjs/array/sort.js +4 -4
  4. package/dist/cjs/colors/initials.js +3 -2
  5. package/dist/cjs/colors/utils.js +3 -1
  6. package/dist/cjs/date/chronos-fn.js +4 -2
  7. package/dist/cjs/date/guards.js +13 -15
  8. package/dist/cjs/date/utils.js +17 -4
  9. package/dist/cjs/dom/query.js +5 -8
  10. package/dist/cjs/form/convert.js +2 -2
  11. package/dist/cjs/form/guards.js +2 -1
  12. package/dist/cjs/form/transform.js +4 -3
  13. package/dist/cjs/number/basics.js +8 -5
  14. package/dist/cjs/number/guards.js +10 -2
  15. package/dist/cjs/pluralizer/Pluralizer.js +3 -3
  16. package/dist/cjs/string/convert.js +2 -1
  17. package/dist/cjs/utils/index.js +1 -1
  18. package/dist/dts/converter/Converter.d.ts +1 -1
  19. package/dist/dts/number/guards.d.ts +5 -4
  20. package/dist/esm/array/Finder.js +16 -20
  21. package/dist/esm/array/sort.js +4 -4
  22. package/dist/esm/colors/initials.js +3 -2
  23. package/dist/esm/colors/utils.js +3 -1
  24. package/dist/esm/converter/Converter.js +1 -1
  25. package/dist/esm/date/chronos-fn.js +4 -2
  26. package/dist/esm/date/guards.js +14 -16
  27. package/dist/esm/date/plugins/timeZonePlugin.js +1 -1
  28. package/dist/esm/date/utils.js +17 -4
  29. package/dist/esm/dom/query.js +5 -8
  30. package/dist/esm/form/convert.js +3 -3
  31. package/dist/esm/form/guards.js +2 -1
  32. package/dist/esm/form/transform.js +4 -3
  33. package/dist/esm/number/basics.js +9 -6
  34. package/dist/esm/number/guards.js +10 -2
  35. package/dist/esm/pluralizer/Pluralizer.js +4 -4
  36. package/dist/esm/string/convert.js +2 -1
  37. package/dist/esm/utils/index.js +1 -1
  38. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -4,6 +4,11 @@
4
4
 
5
5
  All notable changes to the package will be documented here.
6
6
 
7
+ ## [4.29.20] - 2026-04-03
8
+
9
+ - **Updated** `isEven` and `isOdd` utilities to accept *numeric string* and return `false` for `NaN` and *non-integer* values.
10
+ - **Replaced** manual type checks with *type guards* internally for better *type safety* and *code readability*.
11
+
7
12
  ## [4.29.10] - 2026-03-27
8
13
 
9
14
  - **Added** new *color utlities* `applyOpacityToHex` and `percentToHex` for working with *hex colors* and *opacity*.
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Finder = void 0;
4
+ const non_primitives_1 = require("../guards/non-primitives");
5
+ const primitives_1 = require("../guards/primitives");
4
6
  class Finder {
5
7
  static #DEFAULT_TTL = 1000 * 60 * 5;
6
8
  #cachedResult = new Map();
@@ -9,7 +11,7 @@ class Finder {
9
11
  #items;
10
12
  constructor(data, ttl = Finder.#DEFAULT_TTL) {
11
13
  this.#ttl = ttl;
12
- this.#items = typeof data === 'function' ? data() : data;
14
+ this.#items = (0, non_primitives_1.isFunction)(data) ? data() : data;
13
15
  }
14
16
  clearCache(key) {
15
17
  if (key) {
@@ -21,14 +23,14 @@ class Finder {
21
23
  }
22
24
  findAll(matcher, keySelector, options) {
23
25
  const { fuzzy = false, needSorting = true, cacheKey = 'finder-cache', forceBinary = false, caseInsensitive = true, data, } = options ?? {};
24
- const source = typeof data === 'function' ? data() : (data ?? this.#items);
26
+ const source = (0, non_primitives_1.isFunction)(data) ? data() : (data ?? this.#items);
25
27
  if (!source?.length)
26
28
  return [];
27
29
  const rawGetKey = typeof keySelector === 'function'
28
30
  ? keySelector
29
31
  : (item) => item[keySelector];
30
32
  const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
31
- const normalizedMatcher = caseInsensitive && typeof matcher === 'string' ? matcher.toLowerCase() : matcher;
33
+ const normalizedMatcher = caseInsensitive && (0, primitives_1.isString)(matcher) ? matcher.toLowerCase() : matcher;
32
34
  if (cacheKey) {
33
35
  const entry = this.#cachedResult.get(cacheKey);
34
36
  if (entry && Date.now() - entry.timestamp < this.#ttl) {
@@ -42,7 +44,7 @@ class Finder {
42
44
  if (source.length < 100 && !forceBinary) {
43
45
  results = source.filter((item) => {
44
46
  const key = getKey(item);
45
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
47
+ const value = caseInsensitive && (0, primitives_1.isString)(key) ? key.toLowerCase() : key;
46
48
  return value === normalizedMatcher;
47
49
  });
48
50
  }
@@ -51,22 +53,18 @@ class Finder {
51
53
  const firstMatch = this.binarySearch(sorted, normalizedMatcher, getKey, caseInsensitive);
52
54
  if (firstMatch) {
53
55
  const baseKey = getKey(firstMatch);
54
- const base = caseInsensitive && typeof baseKey === 'string'
55
- ? baseKey.toLowerCase()
56
- : baseKey;
56
+ const base = caseInsensitive && (0, primitives_1.isString)(baseKey) ? baseKey.toLowerCase() : baseKey;
57
57
  results = sorted.filter((item) => {
58
58
  const key = getKey(item);
59
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
59
+ const value = caseInsensitive && (0, primitives_1.isString)(key) ? key.toLowerCase() : key;
60
60
  return value === base;
61
61
  });
62
62
  }
63
63
  }
64
- if (!results.length && fuzzy && typeof normalizedMatcher === 'string') {
64
+ if (!results.length && fuzzy && (0, primitives_1.isString)(normalizedMatcher)) {
65
65
  results = source.filter((item) => {
66
66
  const rawKey = getKey(item);
67
- const key = caseInsensitive && typeof rawKey === 'string'
68
- ? rawKey.toLowerCase()
69
- : String(rawKey);
67
+ const key = caseInsensitive && (0, primitives_1.isString)(rawKey) ? rawKey.toLowerCase() : String(rawKey);
70
68
  return this.#match(key, normalizedMatcher);
71
69
  });
72
70
  }
@@ -80,14 +78,14 @@ class Finder {
80
78
  }
81
79
  findOne(matcher, keySelector, options) {
82
80
  const { fuzzy = false, needSorting = true, cacheKey = 'finder-cache', forceBinary = false, caseInsensitive = true, data, } = options ?? {};
83
- const source = typeof data === 'function' ? data() : (data ?? this.#items);
81
+ const source = (0, non_primitives_1.isFunction)(data) ? data() : (data ?? this.#items);
84
82
  if (!source?.length)
85
83
  return undefined;
86
84
  const rawGetKey = typeof keySelector === 'function'
87
85
  ? keySelector
88
86
  : (item) => item[keySelector];
89
87
  const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
90
- const normalizedMatcher = caseInsensitive && typeof matcher === 'string' ? matcher.toLowerCase() : matcher;
88
+ const normalizedMatcher = caseInsensitive && (0, primitives_1.isString)(matcher) ? matcher.toLowerCase() : matcher;
91
89
  if (cacheKey) {
92
90
  const entry = this.#cachedResult.get(cacheKey);
93
91
  if (entry && Date.now() - entry.timestamp < this.#ttl) {
@@ -101,14 +99,14 @@ class Finder {
101
99
  if (source?.length < 100 && !forceBinary) {
102
100
  result = source?.find((item) => {
103
101
  const key = getKey(item);
104
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
102
+ const value = caseInsensitive && (0, primitives_1.isString)(key) ? key.toLowerCase() : key;
105
103
  return value === normalizedMatcher;
106
104
  });
107
105
  }
108
106
  else {
109
107
  result = this.binarySearch(needSorting ? this.#sortAndCache(source, getKey, cacheKey) : source, normalizedMatcher, getKey, caseInsensitive);
110
108
  }
111
- if (!result && fuzzy && typeof normalizedMatcher === 'string') {
109
+ if (!result && fuzzy && (0, primitives_1.isString)(normalizedMatcher)) {
112
110
  return this.fuzzySearch(source, normalizedMatcher, getKey, caseInsensitive);
113
111
  }
114
112
  if (cacheKey && result) {
@@ -132,7 +130,7 @@ class Finder {
132
130
  while (min <= max) {
133
131
  const mid = Math.floor((min + max) / 2);
134
132
  const midKey = keySelector(sorted[mid]);
135
- const key = caseInsensitive && typeof midKey === 'string' ? midKey.toLowerCase() : midKey;
133
+ const key = caseInsensitive && (0, primitives_1.isString)(midKey) ? midKey.toLowerCase() : midKey;
136
134
  if (key === matcher)
137
135
  return sorted[mid];
138
136
  if (key < matcher)
@@ -145,9 +143,7 @@ class Finder {
145
143
  fuzzySearch(array, matcher, keySelector, caseInsensitive) {
146
144
  for (const item of array) {
147
145
  const rawKey = keySelector(item);
148
- const key = caseInsensitive && typeof rawKey === 'string'
149
- ? rawKey.toLowerCase()
150
- : String(rawKey);
146
+ const key = caseInsensitive && (0, primitives_1.isString)(rawKey) ? rawKey.toLowerCase() : String(rawKey);
151
147
  if (this.#match(key, matcher))
152
148
  return item;
153
149
  }
@@ -36,18 +36,18 @@ function naturalSort(a, b, options) {
36
36
  for (let i = 0; i < Math.min(aChunks?.length, bChunks?.length); i++) {
37
37
  let aChunk = aChunks[i];
38
38
  let bChunk = bChunks[i];
39
- if (caseInsensitive && typeof aChunk === 'string' && typeof bChunk === 'string') {
39
+ if (caseInsensitive && (0, primitives_1.isString)(aChunk) && (0, primitives_1.isString)(bChunk)) {
40
40
  aChunk = aChunk?.toLowerCase();
41
41
  bChunk = bChunk?.toLowerCase();
42
42
  }
43
43
  if (typeof aChunk !== typeof bChunk) {
44
- return typeof aChunk === 'string' ? 1 : -1;
44
+ return (0, primitives_1.isString)(aChunk) ? 1 : -1;
45
45
  }
46
46
  if (aChunk !== bChunk) {
47
- if (typeof aChunk === 'number' && typeof bChunk === 'number') {
47
+ if ((0, primitives_1.isNumber)(aChunk) && (0, primitives_1.isNumber)(bChunk)) {
48
48
  return aChunk - bChunk;
49
49
  }
50
- if (typeof aChunk === 'string' && typeof bChunk === 'string') {
50
+ if ((0, primitives_1.isString)(aChunk) && (0, primitives_1.isString)(bChunk)) {
51
51
  if (localeAware) {
52
52
  const cmp = aChunk.localeCompare(bChunk, undefined, {
53
53
  sensitivity: caseInsensitive ? 'accent' : 'variant',
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getColorForInitial = getColorForInitial;
4
+ const primitives_1 = require("../guards/primitives");
4
5
  const constants_1 = require("./constants");
5
6
  const helpers_1 = require("./helpers");
6
7
  const utils_1 = require("./utils");
@@ -11,7 +12,7 @@ function getColorForInitial(input = '', opacity = 100) {
11
12
  const DEFAULT = '#010514';
12
13
  if (!input)
13
14
  return (0, helpers_1._applyOpacity)(DEFAULT, hexOpacity);
14
- if (typeof input === 'string') {
15
+ if ((0, primitives_1.isString)(input)) {
15
16
  initial = input[0];
16
17
  if (NUMBERS.includes(initial)) {
17
18
  return (0, helpers_1._applyOpacity)(constants_1.NUMBER_COLOR_PALETTE[parseInt(initial, 10)], hexOpacity);
@@ -23,7 +24,7 @@ function getColorForInitial(input = '', opacity = 100) {
23
24
  }
24
25
  return (0, helpers_1._applyOpacity)(DEFAULT, hexOpacity);
25
26
  }
26
- else if (typeof input === 'number' && !isNaN(input)) {
27
+ else if ((0, primitives_1.isNumber)(input)) {
27
28
  initial = input.toString()[0];
28
29
  if (NUMBERS.includes(initial)) {
29
30
  return (0, helpers_1._applyOpacity)(constants_1.NUMBER_COLOR_PALETTE[parseInt(initial, 10)], hexOpacity);
@@ -38,6 +38,8 @@ function applyOpacityToHex(color, opacity) {
38
38
  }
39
39
  }
40
40
  else {
41
- throw new TypeError('Invalid color value. Must be a hex color string in the format #RRGGBB or #RRGGBBAA.');
41
+ throw new TypeError('Invalid color value!', {
42
+ cause: 'Value must be a hex color string in the format #RRGGBB or #RRGGBBAA.',
43
+ });
42
44
  }
43
45
  }
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.chronusts = exports.chronusjs = exports.chronus = exports.chronosts = exports.chronosjs = exports.chronos = void 0;
4
+ const non_primitives_1 = require("../guards/non-primitives");
5
+ const primitives_1 = require("../guards/primitives");
4
6
  const Chronos_1 = require("./Chronos");
5
7
  const $chronos = (valueOrYear, month, date, hours, minutes, seconds, ms) => {
6
- if (typeof valueOrYear === 'number' && typeof month === 'number') {
8
+ if ((0, primitives_1.isNumber)(valueOrYear) && (0, primitives_1.isNumber)(month)) {
7
9
  return new Chronos_1.Chronos(valueOrYear, month, date ?? 1, hours ?? 0, minutes ?? 0, seconds ?? 0, ms ?? 0);
8
10
  }
9
11
  else {
@@ -15,7 +17,7 @@ function _isChronosStaticKey(prop) {
15
17
  prop !== 'prototype' &&
16
18
  prop !== 'name' &&
17
19
  prop !== 'length' &&
18
- typeof Chronos_1.Chronos[prop] === 'function');
20
+ (0, non_primitives_1.isFunction)(Chronos_1.Chronos[prop]));
19
21
  }
20
22
  exports.chronos = new Proxy($chronos, {
21
23
  get(target, prop, receiver) {
@@ -7,6 +7,7 @@ exports.isNativeTimeZoneId = isNativeTimeZoneId;
7
7
  exports.isLeapYear = isLeapYear;
8
8
  exports.isDateLike = isDateLike;
9
9
  exports.isTimeWithUnit = isTimeWithUnit;
10
+ const non_primitives_1 = require("../guards/non-primitives");
10
11
  const primitives_1 = require("../guards/primitives");
11
12
  const specials_1 = require("../guards/specials");
12
13
  const utilities_1 = require("../number/utilities");
@@ -37,27 +38,24 @@ function isLeapYear(year) {
37
38
  function isDateLike(value) {
38
39
  if (value instanceof Date)
39
40
  return true;
40
- if (value && typeof value === 'object') {
41
- const v = value;
42
- if (typeof v.format === 'function' &&
43
- typeof v.toJSON === 'function' &&
44
- typeof v.toISOString === 'function') {
41
+ if ((0, non_primitives_1.isObject)(value)) {
42
+ if ((0, non_primitives_1.isFunction)(value.format) &&
43
+ (0, non_primitives_1.isFunction)(value.toJSON) &&
44
+ (0, non_primitives_1.isFunction)(value.toISOString)) {
45
45
  return true;
46
46
  }
47
- if (typeof v.toISO === 'function' &&
48
- typeof v.toFormat === 'function' &&
49
- typeof v.isValid === 'boolean') {
47
+ if ((0, non_primitives_1.isFunction)(value.toISO) && (0, non_primitives_1.isFunction)(value.toFormat) && (0, primitives_1.isBoolean)(value.isValid)) {
50
48
  return true;
51
49
  }
52
- if (typeof v.plus === 'function' &&
53
- typeof v.minus === 'function' &&
54
- typeof v.equals === 'function' &&
55
- typeof v.getClass === 'function') {
50
+ if ((0, non_primitives_1.isFunction)(value.plus) &&
51
+ (0, non_primitives_1.isFunction)(value.minus) &&
52
+ (0, non_primitives_1.isFunction)(value.equals) &&
53
+ (0, non_primitives_1.isFunction)(value.getClass)) {
56
54
  return true;
57
55
  }
58
- if (typeof v.toJSON === 'function' &&
59
- typeof v.toString === 'function' &&
60
- ['PlainDate', 'ZonedDateTime', 'Instant'].includes(v.constructor?.name ?? '')) {
56
+ if ((0, non_primitives_1.isFunction)(value.toJSON) &&
57
+ (0, non_primitives_1.isFunction)(value.toString) &&
58
+ ['PlainDate', 'ZonedDateTime', 'Instant'].includes(value.constructor?.name ?? '')) {
61
59
  return true;
62
60
  }
63
61
  }
@@ -15,6 +15,8 @@ exports.formatTimePart = formatTimePart;
15
15
  exports.formatDateRelative = formatDateRelative;
16
16
  exports.getTimestamp = getTimestamp;
17
17
  const non_primitives_1 = require("../guards/non-primitives");
18
+ const primitives_1 = require("../guards/primitives");
19
+ const utilities_1 = require("../number/utilities");
18
20
  const guards_1 = require("./guards");
19
21
  const helpers_1 = require("./helpers");
20
22
  const timezone_1 = require("./timezone");
@@ -38,13 +40,24 @@ function extractMinutesFromUTC(utc) {
38
40
  return getTotalMinutes(extractTimeFromUTC(utc));
39
41
  }
40
42
  function convertMinutesToTime(minutes) {
41
- const numMIn = Math.abs(typeof minutes === 'number' ? minutes : Number(minutes));
43
+ const parsed = (0, utilities_1.normalizeNumber)(minutes);
44
+ if ((0, primitives_1.isUndefined)(parsed)) {
45
+ throw new TypeError(`Invalid numeric input!`, {
46
+ cause: `${minutes} cannot be converted to a number.`,
47
+ });
48
+ }
49
+ const numMIn = Math.abs(parsed);
42
50
  return `${String(Math.floor(numMIn / 60))}:${String(numMIn % 60).padStart(2, '0')}`;
43
51
  }
44
52
  function formatUTCOffset(minutes) {
45
- const numMIn = typeof minutes === 'number' ? minutes : Number(minutes);
46
- const sign = numMIn < 0 ? '-' : '+';
47
- const abs = Math.abs(numMIn);
53
+ const parsed = (0, utilities_1.normalizeNumber)(minutes);
54
+ if ((0, primitives_1.isUndefined)(parsed)) {
55
+ throw new TypeError(`Invalid numeric input!`, {
56
+ cause: `${minutes} cannot be converted to a number.`,
57
+ });
58
+ }
59
+ const sign = parsed < 0 ? '-' : '+';
60
+ const abs = Math.abs(parsed);
48
61
  const hours = String(Math.floor(abs / 60)).padStart(2, '0');
49
62
  const mins = String(abs % 60).padStart(2, '0');
50
63
  return `UTC${sign}${hours}:${mins}`;
@@ -5,22 +5,19 @@ exports.getQueryParams = getQueryParams;
5
5
  exports.updateQueryParam = updateQueryParam;
6
6
  exports.parseQueryString = parseQueryString;
7
7
  exports.parseQueryStringLiteral = parseQueryStringLiteral;
8
+ const primitives_1 = require("../guards/primitives");
8
9
  const objectify_1 = require("../object/objectify");
9
10
  const sanitize_1 = require("../object/sanitize");
10
11
  const index_1 = require("../utils/index");
11
12
  function generateQueryParams(params = {}) {
12
13
  const flattenedParams = (0, objectify_1.flattenObjectKeyValue)(params);
13
14
  const queryParams = Object.entries(flattenedParams)
14
- ?.filter(([_, value]) => value !== undefined &&
15
- value !== null &&
16
- !(typeof value === 'string' && value?.trim() === ''))
15
+ ?.filter(([_, value]) => value != null && !((0, primitives_1.isString)(value) && value?.trim() === ''))
17
16
  ?.flatMap(([key, value]) => Array.isArray(value)
18
17
  ? value
19
- ?.filter((v) => v !== undefined &&
20
- v !== null &&
21
- !(typeof v === 'string' && v.trim() === ''))
22
- ?.map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(typeof v === 'boolean' ? String(v) : String(v))}`)
23
- : `${encodeURIComponent(key)}=${encodeURIComponent(typeof value === 'boolean' ? String(value) : String(value))}`)
18
+ ?.filter((v) => v != null && !((0, primitives_1.isString)(v) && v.trim() === ''))
19
+ ?.map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(String(v))}`)
20
+ : `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
24
21
  .join('&');
25
22
  return queryParams ? `?${queryParams}` : '';
26
23
  }
@@ -70,7 +70,7 @@ const createControlledFormData = (data, configs) => {
70
70
  }
71
71
  }
72
72
  else {
73
- if (typeof value === 'string') {
73
+ if ((0, primitives_1.isString)(value)) {
74
74
  if ((0, primitives_1.isNonEmptyString)(value)) {
75
75
  let cleanString = value;
76
76
  if (configs?.trimStrings) {
@@ -175,7 +175,7 @@ const createControlledFormData = (data, configs) => {
175
175
  else {
176
176
  const isNotNullish = value != null && value !== '';
177
177
  if (isNotNullish || _isRequiredKey(key)) {
178
- if (typeof value === 'string' && _shouldLowercaseValue(key)) {
178
+ if ((0, primitives_1.isString)(value) && _shouldLowercaseValue(key)) {
179
179
  formData.append(transformedKey, value?.toLowerCase());
180
180
  }
181
181
  else {
@@ -8,6 +8,7 @@ exports.isFileArray = isFileArray;
8
8
  exports.isFileList = isFileList;
9
9
  exports.isFileOrBlob = isFileOrBlob;
10
10
  exports.isFileUpload = isFileUpload;
11
+ const primitives_1 = require("../guards/primitives");
11
12
  function isValidFormData(value) {
12
13
  if (!(value instanceof FormData))
13
14
  return false;
@@ -25,7 +26,7 @@ function isOriginFileObj(value) {
25
26
  return false;
26
27
  }
27
28
  const obj = value;
28
- return typeof obj.uid === 'string';
29
+ return (0, primitives_1.isString)(obj.uid);
29
30
  }
30
31
  function isCustomFile(value) {
31
32
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.serializeForm = serializeForm;
4
4
  exports.parseFormData = parseFormData;
5
5
  const query_1 = require("../dom/query");
6
+ const primitives_1 = require("../guards/primitives");
6
7
  const sanitize_1 = require("../object/sanitize");
7
8
  function serializeForm(form, toQueryString = false) {
8
9
  const formData = new FormData(form);
@@ -25,11 +26,11 @@ function serializeForm(form, toQueryString = false) {
25
26
  }
26
27
  function parseFormData(data, parsePrimitives = true) {
27
28
  const parsed = {};
28
- if (typeof data === 'string') {
29
+ if ((0, primitives_1.isString)(data)) {
29
30
  const params = new URLSearchParams(data);
30
31
  params?.forEach((value, key) => {
31
32
  const existing = parsed[key];
32
- if (typeof existing === 'string') {
33
+ if ((0, primitives_1.isString)(existing)) {
33
34
  parsed[key] = [existing, value];
34
35
  }
35
36
  else if (Array.isArray(existing)) {
@@ -55,7 +56,7 @@ function parseFormData(data, parsePrimitives = true) {
55
56
  }
56
57
  }
57
58
  else {
58
- if (typeof existing === 'string') {
59
+ if ((0, primitives_1.isString)(existing)) {
59
60
  parsed[key] = [existing, value];
60
61
  }
61
62
  else if (Array.isArray(existing)) {
@@ -43,10 +43,13 @@ const getRandomNumber = (options) => {
43
43
  exports.getRandomNumber = getRandomNumber;
44
44
  const convertToDecimal = (input, options) => {
45
45
  const { decimalPlaces = 2, isString = false } = options || {};
46
- const number = typeof input === 'number' ? input : Number(input);
47
- return isString
48
- ? number.toFixed(decimalPlaces)
49
- : Number(number.toFixed(decimalPlaces));
46
+ const parsed = (0, utilities_1.normalizeNumber)(input);
47
+ if ((0, primitives_1.isUndefined)(parsed)) {
48
+ throw new TypeError(`Invalid numeric input!`, {
49
+ cause: `${input} cannot be converted to a number.`,
50
+ });
51
+ }
52
+ return (isString ? parsed.toFixed(decimalPlaces) : Number(parsed.toFixed(decimalPlaces)));
50
53
  };
51
54
  exports.convertToDecimal = convertToDecimal;
52
55
  const calculateHCF = (...numbers) => {
@@ -117,7 +120,7 @@ function getAverage(...numbers) {
117
120
  let count = 0;
118
121
  for (const n of numbers) {
119
122
  const num = Number(n);
120
- if (typeof num === 'number' && !isNaN(num)) {
123
+ if ((0, primitives_1.isNumber)(num)) {
121
124
  sum += num;
122
125
  count++;
123
126
  }
@@ -4,12 +4,20 @@ exports.isMultiple = exports.isOdd = exports.isEven = void 0;
4
4
  exports.isPerfectSquare = isPerfectSquare;
5
5
  exports.isFibonacci = isFibonacci;
6
6
  exports.areInvalidNumbers = areInvalidNumbers;
7
+ const primitives_1 = require("../guards/primitives");
8
+ const utilities_1 = require("./utilities");
7
9
  const isEven = (input) => {
8
- return input % 2 === 0;
10
+ const parsed = (0, utilities_1.normalizeNumber)(input);
11
+ if ((0, primitives_1.isUndefined)(parsed))
12
+ return false;
13
+ return parsed % 2 === 0;
9
14
  };
10
15
  exports.isEven = isEven;
11
16
  const isOdd = (input) => {
12
- return input % 2 !== 0;
17
+ const parsed = (0, utilities_1.normalizeNumber)(input);
18
+ if ((0, primitives_1.isUndefined)(parsed))
19
+ return false;
20
+ return parsed % 2 !== 0;
13
21
  };
14
22
  exports.isOdd = isOdd;
15
23
  const isMultiple = (input, multipleOf) => {
@@ -71,7 +71,7 @@ class Pluralizer {
71
71
  }
72
72
  #isUncountable(word) {
73
73
  for (const entry of this.#uncountables) {
74
- if (typeof entry === 'string') {
74
+ if ((0, primitives_1.isString)(entry)) {
75
75
  if (entry.toLowerCase() === word)
76
76
  return true;
77
77
  }
@@ -89,7 +89,7 @@ class Pluralizer {
89
89
  this.#singularRules.push([rule, replacement]);
90
90
  }
91
91
  addUncountable(word) {
92
- this.#uncountables.add(typeof word === 'string' ? word?.toLowerCase() : word);
92
+ this.#uncountables.add((0, primitives_1.isString)(word) ? word?.toLowerCase() : word);
93
93
  }
94
94
  addIrregular(single, plural) {
95
95
  const singleLower = single?.toLowerCase();
@@ -99,7 +99,7 @@ class Pluralizer {
99
99
  }
100
100
  pluralize(word, options = {}) {
101
101
  const count = (0, utilities_1.normalizeNumber)(options?.count);
102
- if (typeof count === 'number') {
102
+ if (!(0, primitives_1.isUndefined)(count)) {
103
103
  const pluralized = count === 1 ? this.toSingular(word) : this.toPlural(word);
104
104
  return options?.inclusive ? `${count} ${pluralized}` : pluralized;
105
105
  }
@@ -5,10 +5,11 @@ exports.normalizeString = normalizeString;
5
5
  exports.extractEmails = extractEmails;
6
6
  exports.extractURLs = extractURLs;
7
7
  exports.formatUnitWithPlural = formatUnitWithPlural;
8
+ const primitives_1 = require("../guards/primitives");
8
9
  const basics_1 = require("./basics");
9
10
  const replaceAllInString = (input, find, replace) => {
10
11
  const trimmedString = (0, basics_1.trimString)(input);
11
- const regex = typeof find === 'string'
12
+ const regex = (0, primitives_1.isString)(find)
12
13
  ? new RegExp(find, 'g')
13
14
  : new RegExp(find, find?.flags.includes('g') ? find?.flags : find?.flags + 'g');
14
15
  return trimmedString?.replace(regex, replace);
@@ -198,7 +198,7 @@ function deepParsePrimitives(input) {
198
198
  return input;
199
199
  }
200
200
  function definePrototypeMethod(proto, name, impl, options) {
201
- const alreadyExists = Object.prototype.hasOwnProperty.call(proto, name);
201
+ const alreadyExists = Object.hasOwn(proto, name);
202
202
  if (alreadyExists && !options?.overwrite)
203
203
  return;
204
204
  Object.defineProperty(proto, name, {
@@ -14,4 +14,4 @@ import { $Volume } from './volume';
14
14
  * The returned instance exposes only methods relevant to the provided unit type.
15
15
  */
16
16
  export declare function Converter<U extends $Unit>(value: Numeric, unit?: U): Converted<U>;
17
- export { $Area as AreaConverter, Converter as converter, $Data as DataConverter, $Length as LengthConverter, $Mass as MassConverter, $Temperature as TemperatureConverter, $Time as TimeConverter, $Volume as VolumeConverter, };
17
+ export { $Area as AreaConverter, $Data as DataConverter, $Length as LengthConverter, $Mass as MassConverter, $Temperature as TemperatureConverter, $Time as TimeConverter, $Volume as VolumeConverter, Converter as converter, };
@@ -1,17 +1,18 @@
1
+ import type { Numeric } from '../types/index';
1
2
  /**
2
3
  * * Check if a number is even or not.
3
4
  *
4
- * @param input The number to check.
5
+ * @param input The number or numeric string to check.
5
6
  * @returns Boolean: `true` if even and `false` if not even.
6
7
  */
7
- export declare const isEven: (input: number) => boolean;
8
+ export declare const isEven: (input: Numeric) => boolean;
8
9
  /**
9
10
  * * Checks if a number is odd or not.
10
11
  *
11
- * @param input The number to check.
12
+ * @param input The number or numeric string to check.
12
13
  * @returns Boolean: `true` if odd and `false` if not odd.
13
14
  */
14
- export declare const isOdd: (input: number) => boolean;
15
+ export declare const isOdd: (input: Numeric) => boolean;
15
16
  /**
16
17
  * * Checks if a number is a multiple of another number.
17
18
  *
@@ -1,3 +1,5 @@
1
+ import { isFunction } from '../guards/non-primitives.js';
2
+ import { isString } from '../guards/primitives.js';
1
3
  export class Finder {
2
4
  static #DEFAULT_TTL = 1000 * 60 * 5;
3
5
  #cachedResult = new Map();
@@ -6,7 +8,7 @@ export class Finder {
6
8
  #items;
7
9
  constructor(data, ttl = Finder.#DEFAULT_TTL) {
8
10
  this.#ttl = ttl;
9
- this.#items = typeof data === 'function' ? data() : data;
11
+ this.#items = isFunction(data) ? data() : data;
10
12
  }
11
13
  clearCache(key) {
12
14
  if (key) {
@@ -18,14 +20,14 @@ export class Finder {
18
20
  }
19
21
  findAll(matcher, keySelector, options) {
20
22
  const { fuzzy = false, needSorting = true, cacheKey = 'finder-cache', forceBinary = false, caseInsensitive = true, data, } = options ?? {};
21
- const source = typeof data === 'function' ? data() : (data ?? this.#items);
23
+ const source = isFunction(data) ? data() : (data ?? this.#items);
22
24
  if (!source?.length)
23
25
  return [];
24
26
  const rawGetKey = typeof keySelector === 'function'
25
27
  ? keySelector
26
28
  : (item) => item[keySelector];
27
29
  const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
28
- const normalizedMatcher = caseInsensitive && typeof matcher === 'string' ? matcher.toLowerCase() : matcher;
30
+ const normalizedMatcher = caseInsensitive && isString(matcher) ? matcher.toLowerCase() : matcher;
29
31
  if (cacheKey) {
30
32
  const entry = this.#cachedResult.get(cacheKey);
31
33
  if (entry && Date.now() - entry.timestamp < this.#ttl) {
@@ -39,7 +41,7 @@ export class Finder {
39
41
  if (source.length < 100 && !forceBinary) {
40
42
  results = source.filter((item) => {
41
43
  const key = getKey(item);
42
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
44
+ const value = caseInsensitive && isString(key) ? key.toLowerCase() : key;
43
45
  return value === normalizedMatcher;
44
46
  });
45
47
  }
@@ -48,22 +50,18 @@ export class Finder {
48
50
  const firstMatch = this.binarySearch(sorted, normalizedMatcher, getKey, caseInsensitive);
49
51
  if (firstMatch) {
50
52
  const baseKey = getKey(firstMatch);
51
- const base = caseInsensitive && typeof baseKey === 'string'
52
- ? baseKey.toLowerCase()
53
- : baseKey;
53
+ const base = caseInsensitive && isString(baseKey) ? baseKey.toLowerCase() : baseKey;
54
54
  results = sorted.filter((item) => {
55
55
  const key = getKey(item);
56
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
56
+ const value = caseInsensitive && isString(key) ? key.toLowerCase() : key;
57
57
  return value === base;
58
58
  });
59
59
  }
60
60
  }
61
- if (!results.length && fuzzy && typeof normalizedMatcher === 'string') {
61
+ if (!results.length && fuzzy && isString(normalizedMatcher)) {
62
62
  results = source.filter((item) => {
63
63
  const rawKey = getKey(item);
64
- const key = caseInsensitive && typeof rawKey === 'string'
65
- ? rawKey.toLowerCase()
66
- : String(rawKey);
64
+ const key = caseInsensitive && isString(rawKey) ? rawKey.toLowerCase() : String(rawKey);
67
65
  return this.#match(key, normalizedMatcher);
68
66
  });
69
67
  }
@@ -77,14 +75,14 @@ export class Finder {
77
75
  }
78
76
  findOne(matcher, keySelector, options) {
79
77
  const { fuzzy = false, needSorting = true, cacheKey = 'finder-cache', forceBinary = false, caseInsensitive = true, data, } = options ?? {};
80
- const source = typeof data === 'function' ? data() : (data ?? this.#items);
78
+ const source = isFunction(data) ? data() : (data ?? this.#items);
81
79
  if (!source?.length)
82
80
  return undefined;
83
81
  const rawGetKey = typeof keySelector === 'function'
84
82
  ? keySelector
85
83
  : (item) => item[keySelector];
86
84
  const getKey = Finder.#createMemoizedKeyGetter(rawGetKey);
87
- const normalizedMatcher = caseInsensitive && typeof matcher === 'string' ? matcher.toLowerCase() : matcher;
85
+ const normalizedMatcher = caseInsensitive && isString(matcher) ? matcher.toLowerCase() : matcher;
88
86
  if (cacheKey) {
89
87
  const entry = this.#cachedResult.get(cacheKey);
90
88
  if (entry && Date.now() - entry.timestamp < this.#ttl) {
@@ -98,14 +96,14 @@ export class Finder {
98
96
  if (source?.length < 100 && !forceBinary) {
99
97
  result = source?.find((item) => {
100
98
  const key = getKey(item);
101
- const value = caseInsensitive && typeof key === 'string' ? key.toLowerCase() : key;
99
+ const value = caseInsensitive && isString(key) ? key.toLowerCase() : key;
102
100
  return value === normalizedMatcher;
103
101
  });
104
102
  }
105
103
  else {
106
104
  result = this.binarySearch(needSorting ? this.#sortAndCache(source, getKey, cacheKey) : source, normalizedMatcher, getKey, caseInsensitive);
107
105
  }
108
- if (!result && fuzzy && typeof normalizedMatcher === 'string') {
106
+ if (!result && fuzzy && isString(normalizedMatcher)) {
109
107
  return this.fuzzySearch(source, normalizedMatcher, getKey, caseInsensitive);
110
108
  }
111
109
  if (cacheKey && result) {
@@ -129,7 +127,7 @@ export class Finder {
129
127
  while (min <= max) {
130
128
  const mid = Math.floor((min + max) / 2);
131
129
  const midKey = keySelector(sorted[mid]);
132
- const key = caseInsensitive && typeof midKey === 'string' ? midKey.toLowerCase() : midKey;
130
+ const key = caseInsensitive && isString(midKey) ? midKey.toLowerCase() : midKey;
133
131
  if (key === matcher)
134
132
  return sorted[mid];
135
133
  if (key < matcher)
@@ -142,9 +140,7 @@ export class Finder {
142
140
  fuzzySearch(array, matcher, keySelector, caseInsensitive) {
143
141
  for (const item of array) {
144
142
  const rawKey = keySelector(item);
145
- const key = caseInsensitive && typeof rawKey === 'string'
146
- ? rawKey.toLowerCase()
147
- : String(rawKey);
143
+ const key = caseInsensitive && isString(rawKey) ? rawKey.toLowerCase() : String(rawKey);
148
144
  if (this.#match(key, matcher))
149
145
  return item;
150
146
  }
@@ -32,18 +32,18 @@ export function naturalSort(a, b, options) {
32
32
  for (let i = 0; i < Math.min(aChunks?.length, bChunks?.length); i++) {
33
33
  let aChunk = aChunks[i];
34
34
  let bChunk = bChunks[i];
35
- if (caseInsensitive && typeof aChunk === 'string' && typeof bChunk === 'string') {
35
+ if (caseInsensitive && isString(aChunk) && isString(bChunk)) {
36
36
  aChunk = aChunk?.toLowerCase();
37
37
  bChunk = bChunk?.toLowerCase();
38
38
  }
39
39
  if (typeof aChunk !== typeof bChunk) {
40
- return typeof aChunk === 'string' ? 1 : -1;
40
+ return isString(aChunk) ? 1 : -1;
41
41
  }
42
42
  if (aChunk !== bChunk) {
43
- if (typeof aChunk === 'number' && typeof bChunk === 'number') {
43
+ if (isNumber(aChunk) && isNumber(bChunk)) {
44
44
  return aChunk - bChunk;
45
45
  }
46
- if (typeof aChunk === 'string' && typeof bChunk === 'string') {
46
+ if (isString(aChunk) && isString(bChunk)) {
47
47
  if (localeAware) {
48
48
  const cmp = aChunk.localeCompare(bChunk, undefined, {
49
49
  sensitivity: caseInsensitive ? 'accent' : 'variant',
@@ -1,3 +1,4 @@
1
+ import { isNumber, isString } from '../guards/primitives.js';
1
2
  import { ALPHABET_COLOR_PALETTE, NUMBER_COLOR_PALETTE } from './constants.js';
2
3
  import { _applyOpacity } from './helpers.js';
3
4
  import { percentToHex } from './utils.js';
@@ -8,7 +9,7 @@ export function getColorForInitial(input = '', opacity = 100) {
8
9
  const DEFAULT = '#010514';
9
10
  if (!input)
10
11
  return _applyOpacity(DEFAULT, hexOpacity);
11
- if (typeof input === 'string') {
12
+ if (isString(input)) {
12
13
  initial = input[0];
13
14
  if (NUMBERS.includes(initial)) {
14
15
  return _applyOpacity(NUMBER_COLOR_PALETTE[parseInt(initial, 10)], hexOpacity);
@@ -20,7 +21,7 @@ export function getColorForInitial(input = '', opacity = 100) {
20
21
  }
21
22
  return _applyOpacity(DEFAULT, hexOpacity);
22
23
  }
23
- else if (typeof input === 'number' && !isNaN(input)) {
24
+ else if (isNumber(input)) {
24
25
  initial = input.toString()[0];
25
26
  if (NUMBERS.includes(initial)) {
26
27
  return _applyOpacity(NUMBER_COLOR_PALETTE[parseInt(initial, 10)], hexOpacity);
@@ -32,6 +32,8 @@ export function applyOpacityToHex(color, opacity) {
32
32
  }
33
33
  }
34
34
  else {
35
- throw new TypeError('Invalid color value. Must be a hex color string in the format #RRGGBB or #RRGGBBAA.');
35
+ throw new TypeError('Invalid color value!', {
36
+ cause: 'Value must be a hex color string in the format #RRGGBB or #RRGGBBAA.',
37
+ });
36
38
  }
37
39
  }
@@ -36,4 +36,4 @@ export function Converter(value, unit) {
36
36
  return new $BaseConverter(value, unit);
37
37
  }
38
38
  }
39
- export { $Area as AreaConverter, Converter as converter, $Data as DataConverter, $Length as LengthConverter, $Mass as MassConverter, $Temperature as TemperatureConverter, $Time as TimeConverter, $Volume as VolumeConverter, };
39
+ export { $Area as AreaConverter, $Data as DataConverter, $Length as LengthConverter, $Mass as MassConverter, $Temperature as TemperatureConverter, $Time as TimeConverter, $Volume as VolumeConverter, Converter as converter, };
@@ -1,6 +1,8 @@
1
+ import { isFunction } from '../guards/non-primitives.js';
2
+ import { isNumber } from '../guards/primitives.js';
1
3
  import { Chronos } from './Chronos.js';
2
4
  const $chronos = (valueOrYear, month, date, hours, minutes, seconds, ms) => {
3
- if (typeof valueOrYear === 'number' && typeof month === 'number') {
5
+ if (isNumber(valueOrYear) && isNumber(month)) {
4
6
  return new Chronos(valueOrYear, month, date ?? 1, hours ?? 0, minutes ?? 0, seconds ?? 0, ms ?? 0);
5
7
  }
6
8
  else {
@@ -12,7 +14,7 @@ function _isChronosStaticKey(prop) {
12
14
  prop !== 'prototype' &&
13
15
  prop !== 'name' &&
14
16
  prop !== 'length' &&
15
- typeof Chronos[prop] === 'function');
17
+ isFunction(Chronos[prop]));
16
18
  }
17
19
  export const chronos = new Proxy($chronos, {
18
20
  get(target, prop, receiver) {
@@ -1,4 +1,5 @@
1
- import { isNonEmptyString, isString } from '../guards/primitives.js';
1
+ import { isFunction, isObject } from '../guards/non-primitives.js';
2
+ import { isBoolean, isNonEmptyString, isString } from '../guards/primitives.js';
2
3
  import { isNumericString } from '../guards/specials.js';
3
4
  import { normalizeNumber } from '../number/utilities.js';
4
5
  import { IANA_TZ_IDS, NATIVE_TZ_IDS } from './timezone.js';
@@ -28,27 +29,24 @@ export function isLeapYear(year) {
28
29
  export function isDateLike(value) {
29
30
  if (value instanceof Date)
30
31
  return true;
31
- if (value && typeof value === 'object') {
32
- const v = value;
33
- if (typeof v.format === 'function' &&
34
- typeof v.toJSON === 'function' &&
35
- typeof v.toISOString === 'function') {
32
+ if (isObject(value)) {
33
+ if (isFunction(value.format) &&
34
+ isFunction(value.toJSON) &&
35
+ isFunction(value.toISOString)) {
36
36
  return true;
37
37
  }
38
- if (typeof v.toISO === 'function' &&
39
- typeof v.toFormat === 'function' &&
40
- typeof v.isValid === 'boolean') {
38
+ if (isFunction(value.toISO) && isFunction(value.toFormat) && isBoolean(value.isValid)) {
41
39
  return true;
42
40
  }
43
- if (typeof v.plus === 'function' &&
44
- typeof v.minus === 'function' &&
45
- typeof v.equals === 'function' &&
46
- typeof v.getClass === 'function') {
41
+ if (isFunction(value.plus) &&
42
+ isFunction(value.minus) &&
43
+ isFunction(value.equals) &&
44
+ isFunction(value.getClass)) {
47
45
  return true;
48
46
  }
49
- if (typeof v.toJSON === 'function' &&
50
- typeof v.toString === 'function' &&
51
- ['PlainDate', 'ZonedDateTime', 'Instant'].includes(v.constructor?.name ?? '')) {
47
+ if (isFunction(value.toJSON) &&
48
+ isFunction(value.toString) &&
49
+ ['PlainDate', 'ZonedDateTime', 'Instant'].includes(value.constructor?.name ?? '')) {
52
50
  return true;
53
51
  }
54
52
  }
@@ -1,7 +1,7 @@
1
1
  import { INTERNALS } from '../constants.js';
2
2
  import { isValidUTCOffset } from '../guards.js';
3
3
  import { _gmtToUtcOffset, _resolveNativeTzName } from '../helpers.js';
4
- import { NATIVE_TZ_IDS, TIME_ZONES, TIME_ZONE_LABELS } from '../timezone.js';
4
+ import { NATIVE_TZ_IDS, TIME_ZONE_LABELS, TIME_ZONES } from '../timezone.js';
5
5
  import { extractMinutesFromUTC } from '../utils.js';
6
6
  export const timeZonePlugin = ($Chronos) => {
7
7
  const { internalDate: $Date, withOrigin, offset } = $Chronos[INTERNALS];
@@ -1,4 +1,6 @@
1
1
  import { isObject } from '../guards/non-primitives.js';
2
+ import { isUndefined } from '../guards/primitives.js';
3
+ import { normalizeNumber } from '../number/utilities.js';
2
4
  import { isValidUTCOffset } from './guards.js';
3
5
  import { _dateArgsToDate, _formatDate, _gmtToUtcOffset, _normalizeOffset, _resolveNativeTzName, } from './helpers.js';
4
6
  import { NATIVE_TZ_IDS } from './timezone.js';
@@ -22,13 +24,24 @@ export function extractMinutesFromUTC(utc) {
22
24
  return getTotalMinutes(extractTimeFromUTC(utc));
23
25
  }
24
26
  export function convertMinutesToTime(minutes) {
25
- const numMIn = Math.abs(typeof minutes === 'number' ? minutes : Number(minutes));
27
+ const parsed = normalizeNumber(minutes);
28
+ if (isUndefined(parsed)) {
29
+ throw new TypeError(`Invalid numeric input!`, {
30
+ cause: `${minutes} cannot be converted to a number.`,
31
+ });
32
+ }
33
+ const numMIn = Math.abs(parsed);
26
34
  return `${String(Math.floor(numMIn / 60))}:${String(numMIn % 60).padStart(2, '0')}`;
27
35
  }
28
36
  export function formatUTCOffset(minutes) {
29
- const numMIn = typeof minutes === 'number' ? minutes : Number(minutes);
30
- const sign = numMIn < 0 ? '-' : '+';
31
- const abs = Math.abs(numMIn);
37
+ const parsed = normalizeNumber(minutes);
38
+ if (isUndefined(parsed)) {
39
+ throw new TypeError(`Invalid numeric input!`, {
40
+ cause: `${minutes} cannot be converted to a number.`,
41
+ });
42
+ }
43
+ const sign = parsed < 0 ? '-' : '+';
44
+ const abs = Math.abs(parsed);
32
45
  const hours = String(Math.floor(abs / 60)).padStart(2, '0');
33
46
  const mins = String(abs % 60).padStart(2, '0');
34
47
  return `UTC${sign}${hours}:${mins}`;
@@ -1,19 +1,16 @@
1
+ import { isString } from '../guards/primitives.js';
1
2
  import { flattenObjectKeyValue } from '../object/objectify.js';
2
3
  import { parseObjectValues } from '../object/sanitize.js';
3
4
  import { deepParsePrimitives } from '../utils/index.js';
4
5
  export function generateQueryParams(params = {}) {
5
6
  const flattenedParams = flattenObjectKeyValue(params);
6
7
  const queryParams = Object.entries(flattenedParams)
7
- ?.filter(([_, value]) => value !== undefined &&
8
- value !== null &&
9
- !(typeof value === 'string' && value?.trim() === ''))
8
+ ?.filter(([_, value]) => value != null && !(isString(value) && value?.trim() === ''))
10
9
  ?.flatMap(([key, value]) => Array.isArray(value)
11
10
  ? value
12
- ?.filter((v) => v !== undefined &&
13
- v !== null &&
14
- !(typeof v === 'string' && v.trim() === ''))
15
- ?.map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(typeof v === 'boolean' ? String(v) : String(v))}`)
16
- : `${encodeURIComponent(key)}=${encodeURIComponent(typeof value === 'boolean' ? String(value) : String(value))}`)
11
+ ?.filter((v) => v != null && !(isString(v) && v.trim() === ''))
12
+ ?.map((v) => `${encodeURIComponent(key)}=${encodeURIComponent(String(v))}`)
13
+ : `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
17
14
  .join('&');
18
15
  return queryParams ? `?${queryParams}` : '';
19
16
  }
@@ -1,6 +1,6 @@
1
1
  import { isDateLike } from '../date/guards.js';
2
2
  import { isEmptyObject, isNotEmptyObject, isValidArray } from '../guards/non-primitives.js';
3
- import { isNonEmptyString } from '../guards/primitives.js';
3
+ import { isNonEmptyString, isString } from '../guards/primitives.js';
4
4
  import { isCustomFile, isCustomFileArray, isFileArray, isFileList, isFileOrBlob, isFileUpload, } from './guards.js';
5
5
  export const createControlledFormData = (data, configs) => {
6
6
  const formData = new FormData();
@@ -67,7 +67,7 @@ export const createControlledFormData = (data, configs) => {
67
67
  }
68
68
  }
69
69
  else {
70
- if (typeof value === 'string') {
70
+ if (isString(value)) {
71
71
  if (isNonEmptyString(value)) {
72
72
  let cleanString = value;
73
73
  if (configs?.trimStrings) {
@@ -172,7 +172,7 @@ export const createControlledFormData = (data, configs) => {
172
172
  else {
173
173
  const isNotNullish = value != null && value !== '';
174
174
  if (isNotNullish || _isRequiredKey(key)) {
175
- if (typeof value === 'string' && _shouldLowercaseValue(key)) {
175
+ if (isString(value) && _shouldLowercaseValue(key)) {
176
176
  formData.append(transformedKey, value?.toLowerCase());
177
177
  }
178
178
  else {
@@ -1,3 +1,4 @@
1
+ import { isString } from '../guards/primitives.js';
1
2
  export function isValidFormData(value) {
2
3
  if (!(value instanceof FormData))
3
4
  return false;
@@ -15,7 +16,7 @@ export function isOriginFileObj(value) {
15
16
  return false;
16
17
  }
17
18
  const obj = value;
18
- return typeof obj.uid === 'string';
19
+ return isString(obj.uid);
19
20
  }
20
21
  export function isCustomFile(value) {
21
22
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
@@ -1,4 +1,5 @@
1
1
  import { generateQueryParams } from '../dom/query.js';
2
+ import { isString } from '../guards/primitives.js';
2
3
  import { parseObjectValues } from '../object/sanitize.js';
3
4
  export function serializeForm(form, toQueryString = false) {
4
5
  const formData = new FormData(form);
@@ -21,11 +22,11 @@ export function serializeForm(form, toQueryString = false) {
21
22
  }
22
23
  export function parseFormData(data, parsePrimitives = true) {
23
24
  const parsed = {};
24
- if (typeof data === 'string') {
25
+ if (isString(data)) {
25
26
  const params = new URLSearchParams(data);
26
27
  params?.forEach((value, key) => {
27
28
  const existing = parsed[key];
28
- if (typeof existing === 'string') {
29
+ if (isString(existing)) {
29
30
  parsed[key] = [existing, value];
30
31
  }
31
32
  else if (Array.isArray(existing)) {
@@ -51,7 +52,7 @@ export function parseFormData(data, parsePrimitives = true) {
51
52
  }
52
53
  }
53
54
  else {
54
- if (typeof existing === 'string') {
55
+ if (isString(existing)) {
55
56
  parsed[key] = [existing, value];
56
57
  }
57
58
  else if (Array.isArray(existing)) {
@@ -1,4 +1,4 @@
1
- import { isNumber } from '../guards/primitives.js';
1
+ import { isNumber, isUndefined } from '../guards/primitives.js';
2
2
  import { _find2NumbersHCF, _find2NumbersLCM } from './helpers.js';
3
3
  import { normalizeNumber } from './utilities.js';
4
4
  export const getRandomNumber = (options) => {
@@ -32,10 +32,13 @@ export const getRandomNumber = (options) => {
32
32
  };
33
33
  export const convertToDecimal = (input, options) => {
34
34
  const { decimalPlaces = 2, isString = false } = options || {};
35
- const number = typeof input === 'number' ? input : Number(input);
36
- return isString
37
- ? number.toFixed(decimalPlaces)
38
- : Number(number.toFixed(decimalPlaces));
35
+ const parsed = normalizeNumber(input);
36
+ if (isUndefined(parsed)) {
37
+ throw new TypeError(`Invalid numeric input!`, {
38
+ cause: `${input} cannot be converted to a number.`,
39
+ });
40
+ }
41
+ return (isString ? parsed.toFixed(decimalPlaces) : Number(parsed.toFixed(decimalPlaces)));
39
42
  };
40
43
  export const calculateHCF = (...numbers) => {
41
44
  const converted = numbers?.map(Number);
@@ -103,7 +106,7 @@ export function getAverage(...numbers) {
103
106
  let count = 0;
104
107
  for (const n of numbers) {
105
108
  const num = Number(n);
106
- if (typeof num === 'number' && !isNaN(num)) {
109
+ if (isNumber(num)) {
107
110
  sum += num;
108
111
  count++;
109
112
  }
@@ -1,8 +1,16 @@
1
+ import { isUndefined } from '../guards/primitives.js';
2
+ import { normalizeNumber } from './utilities.js';
1
3
  export const isEven = (input) => {
2
- return input % 2 === 0;
4
+ const parsed = normalizeNumber(input);
5
+ if (isUndefined(parsed))
6
+ return false;
7
+ return parsed % 2 === 0;
3
8
  };
4
9
  export const isOdd = (input) => {
5
- return input % 2 !== 0;
10
+ const parsed = normalizeNumber(input);
11
+ if (isUndefined(parsed))
12
+ return false;
13
+ return parsed % 2 !== 0;
6
14
  };
7
15
  export const isMultiple = (input, multipleOf) => {
8
16
  return input % multipleOf === 0;
@@ -1,4 +1,4 @@
1
- import { isNonEmptyString } from '../guards/primitives.js';
1
+ import { isNonEmptyString, isString, isUndefined } from '../guards/primitives.js';
2
2
  import { normalizeNumber } from '../number/utilities.js';
3
3
  import { irregularRules, pluralRules, singularRules, uncountables } from './rules.js';
4
4
  export class Pluralizer {
@@ -68,7 +68,7 @@ export class Pluralizer {
68
68
  }
69
69
  #isUncountable(word) {
70
70
  for (const entry of this.#uncountables) {
71
- if (typeof entry === 'string') {
71
+ if (isString(entry)) {
72
72
  if (entry.toLowerCase() === word)
73
73
  return true;
74
74
  }
@@ -86,7 +86,7 @@ export class Pluralizer {
86
86
  this.#singularRules.push([rule, replacement]);
87
87
  }
88
88
  addUncountable(word) {
89
- this.#uncountables.add(typeof word === 'string' ? word?.toLowerCase() : word);
89
+ this.#uncountables.add(isString(word) ? word?.toLowerCase() : word);
90
90
  }
91
91
  addIrregular(single, plural) {
92
92
  const singleLower = single?.toLowerCase();
@@ -96,7 +96,7 @@ export class Pluralizer {
96
96
  }
97
97
  pluralize(word, options = {}) {
98
98
  const count = normalizeNumber(options?.count);
99
- if (typeof count === 'number') {
99
+ if (!isUndefined(count)) {
100
100
  const pluralized = count === 1 ? this.toSingular(word) : this.toPlural(word);
101
101
  return options?.inclusive ? `${count} ${pluralized}` : pluralized;
102
102
  }
@@ -1,7 +1,8 @@
1
+ import { isString } from '../guards/primitives.js';
1
2
  import { trimString } from './basics.js';
2
3
  export const replaceAllInString = (input, find, replace) => {
3
4
  const trimmedString = trimString(input);
4
- const regex = typeof find === 'string'
5
+ const regex = isString(find)
5
6
  ? new RegExp(find, 'g')
6
7
  : new RegExp(find, find?.flags.includes('g') ? find?.flags : find?.flags + 'g');
7
8
  return trimmedString?.replace(regex, replace);
@@ -179,7 +179,7 @@ export function deepParsePrimitives(input) {
179
179
  return input;
180
180
  }
181
181
  export function definePrototypeMethod(proto, name, impl, options) {
182
- const alreadyExists = Object.prototype.hasOwnProperty.call(proto, name);
182
+ const alreadyExists = Object.hasOwn(proto, name);
183
183
  if (alreadyExists && !options?.overwrite)
184
184
  return;
185
185
  Object.defineProperty(proto, name, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nhb-toolbox",
3
- "version": "4.29.10",
3
+ "version": "4.29.20",
4
4
  "description": "A versatile collection of smart, efficient, and reusable utility functions, classes and types for everyday development needs.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -44,14 +44,14 @@
44
44
  },
45
45
  "license": "Apache-2.0",
46
46
  "devDependencies": {
47
- "@biomejs/biome": "^2.4.9",
47
+ "@biomejs/biome": "^2.4.10",
48
48
  "@types/jest": "^30.0.0",
49
49
  "@types/node": "^25.5.0",
50
50
  "husky": "^9.1.7",
51
51
  "jest": "^30.3.0",
52
52
  "lint-staged": "^16.4.0",
53
53
  "nhb-scripts": "^1.9.2",
54
- "ts-jest": "^29.4.6",
54
+ "ts-jest": "^29.4.9",
55
55
  "typescript": "^6.0.2"
56
56
  },
57
57
  "keywords": [