@salespark/toolkit 2.1.6 → 2.1.8

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 (locale-aware), 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
@@ -267,16 +267,31 @@ objectToString({ name: "John", age: 30 });
267
267
  // Result: "name=John_age=30"
268
268
  ```
269
269
 
270
- **`cleanObject<T>(obj: T): any`** — Deep-cleans an object by removing null/undefined values.
270
+ **`cleanObject<T>(obj: T, removeEmptyString?: boolean): any`** — Deep-cleans an object by removing null/undefined-like values, with optional empty-string removal.
271
271
 
272
272
  ```javascript
273
+ // Default behaviour: keep empty strings
273
274
  cleanObject({
274
275
  name: "John",
275
276
  age: null,
276
277
  city: undefined,
278
+ note: "",
277
279
  data: { valid: true, invalid: null },
278
280
  });
279
- // Result: {name: 'John', data: {valid: true}}
281
+ // Result: {name: 'John', note: '', data: {valid: true}}
282
+
283
+ // With removeEmptyString enabled
284
+ cleanObject(
285
+ {
286
+ name: "John",
287
+ age: null,
288
+ city: undefined,
289
+ note: "",
290
+ tags: ["", "ok"],
291
+ },
292
+ true
293
+ );
294
+ // Result: {name: 'John', tags: ['ok']}
280
295
  ```
281
296
 
282
297
  ### 🔤 String Utilities
@@ -325,6 +340,42 @@ round(1.2345, 2); // 1.23
325
340
  round(1.235, 2); // 1.24
326
341
  ```
327
342
 
343
+ **`safeAdd(a: number, b: number, decimals?: number): number`** — Adds two numbers with rounding and guards against invalid operands and precision errors.
344
+
345
+ ```javascript
346
+ safeAdd(0.1, 0.2, 2); // 0.3
347
+ safeAdd(Number.NaN, 5); // 0 (invalid operand)
348
+ ```
349
+
350
+ **`safeMultiply(a: number, b: number, decimals?: number): number`** — Multiplies two numbers safely with rounding and operand validation.
351
+
352
+ ```javascript
353
+ safeMultiply(0.1, 0.2, 4); // 0.02
354
+ safeMultiply(Infinity, 2); // 0 (invalid operand)
355
+ ```
356
+
357
+ **`safeSubtract(a: number, b: number, decimals?: number): number`** — Subtracts numbers safely, clamping decimal precision when needed.
358
+
359
+ ```javascript
360
+ safeSubtract(10, 3.3333, 2); // 6.67
361
+ safeSubtract(5, Number.NaN); // 0 (invalid operand)
362
+ ```
363
+
364
+ **`safeDivide(a: number, b: number, decimals?: number): number`** — Divides numbers with division-by-zero protection and precision normalization.
365
+
366
+ ```javascript
367
+ safeDivide(1, 3, 3); // 0.333
368
+ safeDivide(10, 0); // 0 (division by zero)
369
+ ```
370
+
371
+ **`numbersEqual(a: number, b: number, decimals?: number): boolean`** — Safely compares two numbers using fixed decimal precision and invalid operand guards.
372
+
373
+ ```javascript
374
+ numbersEqual(0.1 + 0.2, 0.3); // true
375
+ numbersEqual(1.2345, 1.2344, 4); // false
376
+ numbersEqual(NaN, 1); // false
377
+ ```
378
+
328
379
  **`safeParseInt(value: unknown, defaultValue?: number): number`** — Safely converts a value to an integer with fallback.
329
380
 
330
381
  ```javascript
@@ -820,5 +871,5 @@ MIT © [SalesPark](https://salespark.io)
820
871
 
821
872
  ---
822
873
 
823
- _Document version: 8_
824
- _Last update: 10-12-2025_
874
+ _Document version: 10_
875
+ _Last update: 11-12-2025_
package/dist/index.cjs CHANGED
@@ -187,18 +187,18 @@ function objectToString(obj) {
187
187
  }
188
188
  }
189
189
  }
190
- var isRemovable = (v) => v === null || v === void 0 || v === "null" || v === "undefined";
191
- function cleanObject(obj) {
190
+ var isRemovable = (v, removeEmptyString) => v === null || v === void 0 || v === "null" || v === "undefined" || removeEmptyString && v === "";
191
+ function cleanObject(obj, removeEmptyString = false) {
192
192
  if (Array.isArray(obj)) {
193
- const cleanedArray = obj.map((item) => cleanObject(item)).filter((item) => !isRemovable(item));
193
+ const cleanedArray = obj.map((item) => cleanObject(item, removeEmptyString)).filter((item) => !isRemovable(item, removeEmptyString));
194
194
  return cleanedArray;
195
195
  }
196
196
  if (obj !== null && typeof obj === "object") {
197
197
  const cleaned = {};
198
198
  for (const [key, value] of Object.entries(obj)) {
199
- if (isRemovable(value)) continue;
200
- const nested = cleanObject(value);
201
- if (isRemovable(nested)) continue;
199
+ if (isRemovable(value, removeEmptyString)) continue;
200
+ const nested = cleanObject(value, removeEmptyString);
201
+ if (isRemovable(nested, removeEmptyString)) continue;
202
202
  const isObj = typeof nested === "object" && nested !== null;
203
203
  const isEmptyObj = isObj && !Array.isArray(nested) && Object.keys(nested).length === 0;
204
204
  const isEmptyArr = Array.isArray(nested) && nested.length === 0;
@@ -261,6 +261,61 @@ 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 {
@@ -292,7 +347,8 @@ function safeParseFloat(value, decimals = 6) {
292
347
  }
293
348
  const num = parseFloat(normalized);
294
349
  if (!isFinite(num)) return 0;
295
- return Number(num.toFixed(decimals));
350
+ const precision = normalizeDecimals(decimals);
351
+ return Number(num.toFixed(precision));
296
352
  } catch {
297
353
  return 0;
298
354
  }
@@ -1692,6 +1748,7 @@ exports.isNullUndefinedOrZero = isNullUndefinedOrZero;
1692
1748
  exports.isPTTaxId = isPTTaxId;
1693
1749
  exports.isValidIBAN = isValidIBAN;
1694
1750
  exports.isValidPTTaxId = isValidPTTaxId;
1751
+ exports.numbersEqual = numbersEqual;
1695
1752
  exports.objectToString = objectToString;
1696
1753
  exports.omit = omit;
1697
1754
  exports.otp = otp;
@@ -1704,8 +1761,12 @@ exports.pushAll = pushAll;
1704
1761
  exports.randomDigits = randomDigits;
1705
1762
  exports.removeDiacritics = removeDiacritics;
1706
1763
  exports.round = round;
1764
+ exports.safeAdd = safeAdd;
1765
+ exports.safeDivide = safeDivide;
1766
+ exports.safeMultiply = safeMultiply;
1707
1767
  exports.safeParseFloat = safeParseFloat;
1708
1768
  exports.safeParseInt = safeParseInt;
1769
+ exports.safeSubtract = safeSubtract;
1709
1770
  exports.sanitize = sanitize;
1710
1771
  exports.sanitizeMarkdown = sanitizeMarkdown;
1711
1772
  exports.shuffle = shuffle;