@salespark/toolkit 2.1.5 → 2.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -27,7 +27,7 @@ npm i @salespark/toolkit
27
27
  - **Array utilities**: chunk, uniqBy, deep equality, flatten, groupBy, etc.
28
28
  - **Object utilities**: pick, omit, clean objects, deep merge, etc.
29
29
  - **String utilities**: slugify, template fill, deburr, sanitize, etc.
30
- - **Number utilities**: clamp, round, safe parse, random digits, etc.
30
+ - **Number utilities**: clamp, round, safe arithmetic/comparisons, safe parse (locale-aware), random digits, etc.
31
31
  - **Function utilities**: debounce, throttle, formatCurrency, parseName, currency conversions, etc.
32
32
  - **Boolean utilities**: safe boolean conversion with common representations
33
33
  - **Validation utilities**: IBAN validator (ISO 13616), Portuguese tax ID validator
@@ -325,6 +325,42 @@ round(1.2345, 2); // 1.23
325
325
  round(1.235, 2); // 1.24
326
326
  ```
327
327
 
328
+ **`safeAdd(a: number, b: number, decimals?: number): number`** — Adds two numbers with rounding and guards against invalid operands and precision errors.
329
+
330
+ ```javascript
331
+ safeAdd(0.1, 0.2, 2); // 0.3
332
+ safeAdd(Number.NaN, 5); // 0 (invalid operand)
333
+ ```
334
+
335
+ **`safeMultiply(a: number, b: number, decimals?: number): number`** — Multiplies two numbers safely with rounding and operand validation.
336
+
337
+ ```javascript
338
+ safeMultiply(0.1, 0.2, 4); // 0.02
339
+ safeMultiply(Infinity, 2); // 0 (invalid operand)
340
+ ```
341
+
342
+ **`safeSubtract(a: number, b: number, decimals?: number): number`** — Subtracts numbers safely, clamping decimal precision when needed.
343
+
344
+ ```javascript
345
+ safeSubtract(10, 3.3333, 2); // 6.67
346
+ safeSubtract(5, Number.NaN); // 0 (invalid operand)
347
+ ```
348
+
349
+ **`safeDivide(a: number, b: number, decimals?: number): number`** — Divides numbers with division-by-zero protection and precision normalization.
350
+
351
+ ```javascript
352
+ safeDivide(1, 3, 3); // 0.333
353
+ safeDivide(10, 0); // 0 (division by zero)
354
+ ```
355
+
356
+ **`numbersEqual(a: number, b: number, decimals?: number): boolean`** — Safely compares two numbers using fixed decimal precision and invalid operand guards.
357
+
358
+ ```javascript
359
+ numbersEqual(0.1 + 0.2, 0.3); // true
360
+ numbersEqual(1.2345, 1.2344, 4); // false
361
+ numbersEqual(NaN, 1); // false
362
+ ```
363
+
328
364
  **`safeParseInt(value: unknown, defaultValue?: number): number`** — Safely converts a value to an integer with fallback.
329
365
 
330
366
  ```javascript
@@ -333,13 +369,16 @@ safeParseInt("abc", 10); // 10
333
369
  safeParseInt(3.9); // 3
334
370
  ```
335
371
 
336
- **`safeParseFloat(value: unknown, decimals?: number): number`** — Safely parses a value into a number with decimal precision and comma/dot normalization.
372
+ **`safeParseFloat(value: unknown, decimals?: number): number`** — Safely parses a value into a number with decimal precision, handling locale separators (commas, dots, spaces) and invalid inputs.
337
373
 
338
374
  ```javascript
339
375
  safeParseFloat("123.45"); // 123.45
340
376
  safeParseFloat("123,45"); // 123.45
341
377
  safeParseFloat("1,234.56"); // 1234.56
378
+ safeParseFloat("1 234,56"); // 1234.56 (spaces as thousands)
379
+ safeParseFloat("1.234,56"); // 1234.56 (dot thousands, comma decimals)
342
380
  safeParseFloat("abc"); // 0
381
+ safeParseFloat(NaN); // 0
343
382
  ```
344
383
 
345
384
  **`randomDigits(length?: number, options?: { charset?: string; noLeadingZero?: boolean }): string`** — Generates a random string of digits with secure randomness when available.
@@ -817,5 +856,5 @@ MIT © [SalesPark](https://salespark.io)
817
856
 
818
857
  ---
819
858
 
820
- _Document version: 7_
821
- _Last update: 08-11-2025_
859
+ _Document version: 9_
860
+ _Last update: 01-12-2025_
package/dist/index.cjs CHANGED
@@ -261,19 +261,94 @@ function safeParseInt(value, defaultValue = 0) {
261
261
  return defaultValue;
262
262
  }
263
263
  }
264
+ function normalizeDecimals(decimals) {
265
+ if (!Number.isFinite(decimals)) return 0;
266
+ const int = Math.floor(decimals);
267
+ if (int < 0) return 0;
268
+ if (int > 100) return 100;
269
+ return int;
270
+ }
271
+ function sanitizeOperands(...values) {
272
+ return values.every((value) => Number.isFinite(value));
273
+ }
274
+ function safeAdd(a, b, decimals = 2) {
275
+ try {
276
+ if (!sanitizeOperands(a, b)) return 0;
277
+ const precision = normalizeDecimals(decimals);
278
+ return Number((a + b).toFixed(precision));
279
+ } catch {
280
+ return 0;
281
+ }
282
+ }
283
+ function safeMultiply(a, b, decimals = 2) {
284
+ try {
285
+ if (!sanitizeOperands(a, b)) return 0;
286
+ const precision = normalizeDecimals(decimals);
287
+ return Number((a * b).toFixed(precision));
288
+ } catch {
289
+ return 0;
290
+ }
291
+ }
292
+ function safeSubtract(a, b, decimals = 2) {
293
+ try {
294
+ if (!sanitizeOperands(a, b)) return 0;
295
+ const precision = normalizeDecimals(decimals);
296
+ return Number((a - b).toFixed(precision));
297
+ } catch {
298
+ return 0;
299
+ }
300
+ }
301
+ function safeDivide(a, b, decimals = 2) {
302
+ try {
303
+ if (!sanitizeOperands(a, b) || b === 0) return 0;
304
+ const precision = normalizeDecimals(decimals);
305
+ return Number((a / b).toFixed(precision));
306
+ } catch {
307
+ return 0;
308
+ }
309
+ }
310
+ function numbersEqual(a, b, decimals = 2) {
311
+ try {
312
+ if (!sanitizeOperands(a, b)) return false;
313
+ const precision = normalizeDecimals(decimals);
314
+ return a.toFixed(precision) === b.toFixed(precision);
315
+ } catch {
316
+ return false;
317
+ }
318
+ }
264
319
  var toInteger = safeParseInt;
265
320
  function safeParseFloat(value, decimals = 6) {
266
321
  try {
322
+ if (typeof value === "number") {
323
+ return isNaN(value) ? 0 : Number(value.toFixed(decimals));
324
+ }
267
325
  if (value === void 0 || value === null || value === "") return 0;
268
- let str = String(value);
326
+ let str = String(value).trim();
327
+ if (!str) return 0;
328
+ str = str.replace(/\s+/g, "");
329
+ let normalized;
269
330
  if (str.includes(",") && str.includes(".")) {
270
- str = str.replace(/,/g, "");
331
+ const lastComma = str.lastIndexOf(",");
332
+ const lastDot = str.lastIndexOf(".");
333
+ if (lastDot > lastComma) {
334
+ normalized = str.replace(/,/g, "");
335
+ } else {
336
+ normalized = str.replace(/\./g, "").replace(",", ".");
337
+ }
271
338
  } else if (str.includes(",")) {
272
- str = str.replace(",", ".");
339
+ const parts = str.split(",");
340
+ if (parts.length === 2 && parts[1].length <= 2) {
341
+ normalized = str.replace(",", ".");
342
+ } else {
343
+ normalized = str.replace(/,/g, "");
344
+ }
345
+ } else {
346
+ normalized = str;
273
347
  }
274
- const num = parseFloat(str);
275
- if (isNaN(num)) return 0;
276
- return parseFloat(num.toFixed(decimals));
348
+ const num = parseFloat(normalized);
349
+ if (!isFinite(num)) return 0;
350
+ const precision = normalizeDecimals(decimals);
351
+ return Number(num.toFixed(precision));
277
352
  } catch {
278
353
  return 0;
279
354
  }
@@ -1673,6 +1748,7 @@ exports.isNullUndefinedOrZero = isNullUndefinedOrZero;
1673
1748
  exports.isPTTaxId = isPTTaxId;
1674
1749
  exports.isValidIBAN = isValidIBAN;
1675
1750
  exports.isValidPTTaxId = isValidPTTaxId;
1751
+ exports.numbersEqual = numbersEqual;
1676
1752
  exports.objectToString = objectToString;
1677
1753
  exports.omit = omit;
1678
1754
  exports.otp = otp;
@@ -1685,8 +1761,12 @@ exports.pushAll = pushAll;
1685
1761
  exports.randomDigits = randomDigits;
1686
1762
  exports.removeDiacritics = removeDiacritics;
1687
1763
  exports.round = round;
1764
+ exports.safeAdd = safeAdd;
1765
+ exports.safeDivide = safeDivide;
1766
+ exports.safeMultiply = safeMultiply;
1688
1767
  exports.safeParseFloat = safeParseFloat;
1689
1768
  exports.safeParseInt = safeParseInt;
1769
+ exports.safeSubtract = safeSubtract;
1690
1770
  exports.sanitize = sanitize;
1691
1771
  exports.sanitizeMarkdown = sanitizeMarkdown;
1692
1772
  exports.shuffle = shuffle;