@salespark/toolkit 2.1.13 → 2.1.15
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 +56 -9
- package/dist/index.cjs +140 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +86 -3
- package/dist/index.d.ts +86 -3
- package/dist/index.js +136 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ npm i @salespark/toolkit
|
|
|
32
32
|
- **Defer utilities**: post-return microtask scheduling, non-critical timers, after-response hooks.
|
|
33
33
|
- **Boolean utilities**: safe boolean conversion with common representations
|
|
34
34
|
- **Validation utilities**: IBAN validator (ISO 13616), Portuguese tax ID validator
|
|
35
|
-
- **Security utilities**: Markdown XSS protection, content sanitization, risk assessment
|
|
35
|
+
- **Security utilities**: Markdown XSS protection, content sanitization, risk assessment, obfuscation helpers
|
|
36
36
|
- **Environment detection**: `isBrowser`, `isNode` runtime checks
|
|
37
37
|
|
|
38
38
|
---
|
|
@@ -135,7 +135,7 @@ uniqBy(
|
|
|
135
135
|
{ id: 2, name: "Jane" },
|
|
136
136
|
{ id: 1, name: "John" },
|
|
137
137
|
],
|
|
138
|
-
(x) => x.id
|
|
138
|
+
(x) => x.id,
|
|
139
139
|
);
|
|
140
140
|
// Result: [{id: 1, name: 'John'}, {id: 2, name: 'Jane'}]
|
|
141
141
|
```
|
|
@@ -177,7 +177,7 @@ groupBy(
|
|
|
177
177
|
{ type: "fruit", name: "banana" },
|
|
178
178
|
{ type: "veggie", name: "carrot" },
|
|
179
179
|
],
|
|
180
|
-
"type"
|
|
180
|
+
"type",
|
|
181
181
|
);
|
|
182
182
|
// Result: {fruit: [{type: 'fruit', name: 'apple'}, {type: 'fruit', name: 'banana'}], veggie: [{type: 'veggie', name: 'carrot'}]}
|
|
183
183
|
```
|
|
@@ -218,7 +218,7 @@ pluck(
|
|
|
218
218
|
{ name: "John", age: 30 },
|
|
219
219
|
{ name: "Jane", age: 25 },
|
|
220
220
|
],
|
|
221
|
-
"name"
|
|
221
|
+
"name",
|
|
222
222
|
);
|
|
223
223
|
// Result: ['John', 'Jane']
|
|
224
224
|
```
|
|
@@ -290,7 +290,7 @@ cleanObject(
|
|
|
290
290
|
note: "",
|
|
291
291
|
tags: ["", "ok"],
|
|
292
292
|
},
|
|
293
|
-
true
|
|
293
|
+
true,
|
|
294
294
|
);
|
|
295
295
|
// Result: {name: 'John', tags: ['ok']}
|
|
296
296
|
```
|
|
@@ -584,14 +584,17 @@ isNilOrNaN("abc");
|
|
|
584
584
|
// Result: true (coerced to NaN)
|
|
585
585
|
```
|
|
586
586
|
|
|
587
|
-
**`formatBytes(bytes: number, si?: boolean, dp?: number): string`** — Formats bytes as human-readable text.
|
|
587
|
+
**`formatBytes(bytes: number, si?: boolean, dp?: number, noSpace?: boolean): string`** — Formats bytes as human-readable text.
|
|
588
588
|
|
|
589
589
|
```javascript
|
|
590
|
+
formatBytes(999, true);
|
|
591
|
+
// Result: "999 Bytes"
|
|
592
|
+
|
|
590
593
|
formatBytes(1024);
|
|
591
594
|
// Result: "1.0 KiB"
|
|
592
595
|
|
|
593
|
-
formatBytes(1000, true);
|
|
594
|
-
// Result: "1.
|
|
596
|
+
formatBytes(1000, true, 1, true);
|
|
597
|
+
// Result: "1.0kB"
|
|
595
598
|
```
|
|
596
599
|
|
|
597
600
|
**`stringSimilarity(s1: string, s2: string): number`** — Returns the similarity between two strings (0..1).
|
|
@@ -648,6 +651,26 @@ formatCurrency(null);
|
|
|
648
651
|
// Result: "0,00 €"
|
|
649
652
|
```
|
|
650
653
|
|
|
654
|
+
**`formatCurrencyPro(value: number | string | null | undefined, options?: { withoutCurrencySymbol?: boolean; currency?: string; locale?: string; redact?: boolean; redactChar?: string }): string`** — Same as `formatCurrency`, but uses a single options object and supports redaction.
|
|
655
|
+
|
|
656
|
+
```javascript
|
|
657
|
+
// Same defaults as formatCurrency
|
|
658
|
+
formatCurrencyPro(1234.56);
|
|
659
|
+
// Result: "1234,56 €"
|
|
660
|
+
|
|
661
|
+
// Options object
|
|
662
|
+
formatCurrencyPro(1234.56, { currency: "USD", locale: "en-US" });
|
|
663
|
+
// Result: "$1,234.56"
|
|
664
|
+
|
|
665
|
+
// Redacted output (mask digits)
|
|
666
|
+
formatCurrencyPro(1234.56, { redact: true });
|
|
667
|
+
// Result: "****,** €"
|
|
668
|
+
|
|
669
|
+
// Custom redaction character
|
|
670
|
+
formatCurrencyPro(1234.56, { redact: true, redactChar: "•" });
|
|
671
|
+
// Result: "••••,•• €"
|
|
672
|
+
```
|
|
673
|
+
|
|
651
674
|
**`parseName(name: string | null | undefined): {firstName: string, lastName: string}`** — Extracts first and last name from a full name string. Handles single names, empty inputs, and multi-word names intelligently.
|
|
652
675
|
|
|
653
676
|
```javascript
|
|
@@ -764,7 +787,7 @@ checkMarkdownSecurity('<script>alert("xss")</script>');
|
|
|
764
787
|
|
|
765
788
|
// Content with multiple threats
|
|
766
789
|
checkMarkdownSecurity(
|
|
767
|
-
'<iframe src="evil.com"></iframe><div onclick="bad()">test</div>'
|
|
790
|
+
'<iframe src="evil.com"></iframe><div onclick="bad()">test</div>',
|
|
768
791
|
);
|
|
769
792
|
// Result: Multiple risks detected, sorted by severity
|
|
770
793
|
```
|
|
@@ -813,6 +836,30 @@ assessSecurityRisks([]);
|
|
|
813
836
|
// Result: { score: 0, level: "safe", recommendations: ["Content appears safe to use"] }
|
|
814
837
|
```
|
|
815
838
|
|
|
839
|
+
**`scrambleString(value: string, secret: string): string`** — Scrambles a string using a repeating secret (XOR) and Base64. Reversible obfuscation only (not crypto).
|
|
840
|
+
|
|
841
|
+
```javascript
|
|
842
|
+
const scrambled = scrambleString("Hello", "secret");
|
|
843
|
+
// Result: "..." (base64)
|
|
844
|
+
|
|
845
|
+
const original = descrambleString(scrambled, "secret");
|
|
846
|
+
// Result: "Hello"
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
**`descrambleString(value: string, secret: string): string`** — Reverses `scrambleString` using the same secret.
|
|
850
|
+
|
|
851
|
+
**`encodeObject(input: object, secret: string): string`** — JSON-stringifies an object, Base64-encodes it, and scrambles the result (obfuscation only).
|
|
852
|
+
|
|
853
|
+
```javascript
|
|
854
|
+
const encoded = encodeObject({ id: 1, name: "Ana" }, "secret");
|
|
855
|
+
// Result: "..." (base64)
|
|
856
|
+
|
|
857
|
+
const decoded = decodeObject(encoded, "secret");
|
|
858
|
+
// Result: { id: 1, name: "Ana" }
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
**`decodeObject(encoded: string, secret: string): object`** — Reverses `encodeObject` using the same secret.
|
|
862
|
+
|
|
816
863
|
### ✅ Validation Utilities
|
|
817
864
|
|
|
818
865
|
**`isPTTaxId(value: string | number): boolean`** — Validates Portuguese Tax ID (NIF) with MOD-11 algorithm and format checking.
|
package/dist/index.cjs
CHANGED
|
@@ -553,11 +553,11 @@ var isNullOrUndefinedInArray = hasNilOrEmpty;
|
|
|
553
553
|
var isNullOrUndefinedEmptyOrZero = isNilEmptyOrZeroLen;
|
|
554
554
|
var isNullUndefinedOrZero = isNilOrZeroLen;
|
|
555
555
|
var isNullOrUndefinedOrNaN = isNilOrNaN;
|
|
556
|
-
var formatBytes = (bytes, si = false, dp = 1) => {
|
|
556
|
+
var formatBytes = (bytes, si = false, dp = 1, noSpace = false) => {
|
|
557
557
|
if (!Number.isFinite(bytes)) return "NaN";
|
|
558
558
|
const thresh = si ? 1e3 : 1024;
|
|
559
559
|
const abs = Math.abs(bytes);
|
|
560
|
-
if (abs < thresh) return `${bytes}
|
|
560
|
+
if (abs < thresh) return `${bytes} Bytes`;
|
|
561
561
|
const units = si ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
|
562
562
|
let u = -1;
|
|
563
563
|
const r = 10 ** dp;
|
|
@@ -566,7 +566,7 @@ var formatBytes = (bytes, si = false, dp = 1) => {
|
|
|
566
566
|
value /= thresh;
|
|
567
567
|
++u;
|
|
568
568
|
} while (Math.round(Math.abs(value) * r) / r >= thresh && u < units.length - 1);
|
|
569
|
-
return `${value.toFixed(dp)} ${units[u]}`;
|
|
569
|
+
return `${value.toFixed(dp)}${noSpace === true ? "" : " "}${units[u]}`;
|
|
570
570
|
};
|
|
571
571
|
var humanFileSize = formatBytes;
|
|
572
572
|
var levenshtein = (a, b) => {
|
|
@@ -658,6 +658,35 @@ var formatCurrency = (value, withoutCurrencySymbol = false, currency = "EUR", lo
|
|
|
658
658
|
return withoutCurrencySymbol ? formatted : `${formatted} \u20AC`;
|
|
659
659
|
}
|
|
660
660
|
};
|
|
661
|
+
var formatCurrencyPro = (value, options = {}) => {
|
|
662
|
+
const resolvedWithoutSymbol = options.withoutCurrencySymbol === true;
|
|
663
|
+
const resolvedCurrency = options.currency ?? "EUR";
|
|
664
|
+
const resolvedLocale = options.locale ?? "pt-PT";
|
|
665
|
+
const redact = options.redact === true;
|
|
666
|
+
const redactChar = options.redactChar ? options.redactChar : "*";
|
|
667
|
+
try {
|
|
668
|
+
const numValue = value === void 0 || value === null || value === "" ? 0 : Number(value);
|
|
669
|
+
if (isNaN(numValue) || !isFinite(numValue)) {
|
|
670
|
+
const fallback = resolvedWithoutSymbol ? "0,00" : "0,00 \u20AC";
|
|
671
|
+
return redact ? fallback.replace(/\d/g, redactChar) : fallback;
|
|
672
|
+
}
|
|
673
|
+
const intlOptions = {
|
|
674
|
+
style: resolvedWithoutSymbol ? "decimal" : "currency",
|
|
675
|
+
currency: resolvedCurrency,
|
|
676
|
+
minimumFractionDigits: 2,
|
|
677
|
+
maximumFractionDigits: 2
|
|
678
|
+
};
|
|
679
|
+
const formatted = new Intl.NumberFormat(resolvedLocale, intlOptions).format(
|
|
680
|
+
numValue
|
|
681
|
+
);
|
|
682
|
+
return redact ? formatted.replace(/\d/g, redactChar) : formatted;
|
|
683
|
+
} catch (error) {
|
|
684
|
+
const numValue = Number(value) || 0;
|
|
685
|
+
const formatted = numValue.toFixed(2).replace(".", ",");
|
|
686
|
+
const fallback = resolvedWithoutSymbol ? formatted : `${formatted} \u20AC`;
|
|
687
|
+
return redact ? fallback.replace(/\d/g, redactChar) : fallback;
|
|
688
|
+
}
|
|
689
|
+
};
|
|
661
690
|
var parseName = (name) => {
|
|
662
691
|
try {
|
|
663
692
|
if (name === void 0 || name === null || name === "") {
|
|
@@ -1745,6 +1774,109 @@ var assessSecurityRisks = (risks) => {
|
|
|
1745
1774
|
return { score, level, recommendations };
|
|
1746
1775
|
};
|
|
1747
1776
|
|
|
1777
|
+
// src/utils/scramble.ts
|
|
1778
|
+
var hasBuffer = typeof Buffer !== "undefined" && typeof Buffer.from === "function";
|
|
1779
|
+
var base64EncodeBinary = (binary) => {
|
|
1780
|
+
if (hasBuffer) {
|
|
1781
|
+
return Buffer.from(binary, "binary").toString("base64");
|
|
1782
|
+
}
|
|
1783
|
+
if (typeof btoa === "function") {
|
|
1784
|
+
return btoa(binary);
|
|
1785
|
+
}
|
|
1786
|
+
throw new Error("Base64 encoder not available");
|
|
1787
|
+
};
|
|
1788
|
+
var base64DecodeToBinary = (base64) => {
|
|
1789
|
+
if (hasBuffer) {
|
|
1790
|
+
return Buffer.from(base64, "base64").toString("binary");
|
|
1791
|
+
}
|
|
1792
|
+
if (typeof atob === "function") {
|
|
1793
|
+
return atob(base64);
|
|
1794
|
+
}
|
|
1795
|
+
throw new Error("Base64 decoder not available");
|
|
1796
|
+
};
|
|
1797
|
+
var utf8ToBinary = (value) => {
|
|
1798
|
+
if (hasBuffer) {
|
|
1799
|
+
return Buffer.from(value, "utf8").toString("binary");
|
|
1800
|
+
}
|
|
1801
|
+
if (typeof TextEncoder !== "undefined") {
|
|
1802
|
+
const bytes = new TextEncoder().encode(value);
|
|
1803
|
+
let binary = "";
|
|
1804
|
+
for (const b of bytes) {
|
|
1805
|
+
binary += String.fromCharCode(b);
|
|
1806
|
+
}
|
|
1807
|
+
return binary;
|
|
1808
|
+
}
|
|
1809
|
+
return value;
|
|
1810
|
+
};
|
|
1811
|
+
var binaryToUtf8 = (binary) => {
|
|
1812
|
+
if (hasBuffer) {
|
|
1813
|
+
return Buffer.from(binary, "binary").toString("utf8");
|
|
1814
|
+
}
|
|
1815
|
+
if (typeof TextDecoder !== "undefined") {
|
|
1816
|
+
const bytes = new Uint8Array(binary.length);
|
|
1817
|
+
for (let i = 0; i < binary.length; i++) {
|
|
1818
|
+
bytes[i] = binary.charCodeAt(i) & 255;
|
|
1819
|
+
}
|
|
1820
|
+
return new TextDecoder().decode(bytes);
|
|
1821
|
+
}
|
|
1822
|
+
return binary;
|
|
1823
|
+
};
|
|
1824
|
+
var toBase64 = (value) => base64EncodeBinary(utf8ToBinary(value));
|
|
1825
|
+
var fromBase64 = (value) => binaryToUtf8(base64DecodeToBinary(value));
|
|
1826
|
+
var scrambleString = (value, secret) => {
|
|
1827
|
+
if (typeof value !== "string") {
|
|
1828
|
+
throw new TypeError("Value must be a string");
|
|
1829
|
+
}
|
|
1830
|
+
if (!secret || typeof secret !== "string") {
|
|
1831
|
+
throw new TypeError("Secret must be a non-empty string");
|
|
1832
|
+
}
|
|
1833
|
+
let result = "";
|
|
1834
|
+
for (let i = 0; i < value.length; i++) {
|
|
1835
|
+
const charCode = value.charCodeAt(i) & 255;
|
|
1836
|
+
const keyCode = secret.charCodeAt(i % secret.length) & 255;
|
|
1837
|
+
result += String.fromCharCode(charCode ^ keyCode);
|
|
1838
|
+
}
|
|
1839
|
+
return base64EncodeBinary(result);
|
|
1840
|
+
};
|
|
1841
|
+
var descrambleString = (value, secret) => {
|
|
1842
|
+
if (typeof value !== "string") {
|
|
1843
|
+
throw new TypeError("Value must be a string");
|
|
1844
|
+
}
|
|
1845
|
+
if (!secret || typeof secret !== "string") {
|
|
1846
|
+
throw new TypeError("Secret must be a non-empty string");
|
|
1847
|
+
}
|
|
1848
|
+
const decoded = base64DecodeToBinary(value);
|
|
1849
|
+
let result = "";
|
|
1850
|
+
for (let i = 0; i < decoded.length; i++) {
|
|
1851
|
+
const charCode = decoded.charCodeAt(i) & 255;
|
|
1852
|
+
const keyCode = secret.charCodeAt(i % secret.length) & 255;
|
|
1853
|
+
result += String.fromCharCode(charCode ^ keyCode);
|
|
1854
|
+
}
|
|
1855
|
+
return result;
|
|
1856
|
+
};
|
|
1857
|
+
var encodeObject = (input, secret) => {
|
|
1858
|
+
if (!input || typeof input !== "object") {
|
|
1859
|
+
throw new TypeError("Input must be an object");
|
|
1860
|
+
}
|
|
1861
|
+
if (!secret || typeof secret !== "string") {
|
|
1862
|
+
throw new TypeError("Secret must be a non-empty string");
|
|
1863
|
+
}
|
|
1864
|
+
const jsonString = JSON.stringify(input);
|
|
1865
|
+
const base64 = toBase64(jsonString);
|
|
1866
|
+
return scrambleString(base64, secret);
|
|
1867
|
+
};
|
|
1868
|
+
var decodeObject = (encoded, secret) => {
|
|
1869
|
+
if (typeof encoded !== "string") {
|
|
1870
|
+
throw new TypeError("Encoded value must be a string");
|
|
1871
|
+
}
|
|
1872
|
+
if (!secret || typeof secret !== "string") {
|
|
1873
|
+
throw new TypeError("Secret must be a non-empty string");
|
|
1874
|
+
}
|
|
1875
|
+
const descrambled = descrambleString(encoded, secret);
|
|
1876
|
+
const jsonString = fromBase64(descrambled);
|
|
1877
|
+
return JSON.parse(jsonString);
|
|
1878
|
+
};
|
|
1879
|
+
|
|
1748
1880
|
// src/utils/defer.ts
|
|
1749
1881
|
var swallow = (p) => p.catch(() => {
|
|
1750
1882
|
});
|
|
@@ -1849,12 +1981,15 @@ exports.compact = compact;
|
|
|
1849
1981
|
exports.currencyToSymbol = currencyToSymbol;
|
|
1850
1982
|
exports.debounce = debounce;
|
|
1851
1983
|
exports.deburr = deburr;
|
|
1984
|
+
exports.decodeObject = decodeObject;
|
|
1852
1985
|
exports.deferAfterResponse = deferAfterResponse;
|
|
1853
1986
|
exports.deferAfterResponseNonCritical = deferAfterResponseNonCritical;
|
|
1854
1987
|
exports.deferNonCritical = deferNonCritical;
|
|
1855
1988
|
exports.deferPostReturn = deferPostReturn;
|
|
1856
1989
|
exports.delay = delay;
|
|
1990
|
+
exports.descrambleString = descrambleString;
|
|
1857
1991
|
exports.difference = difference;
|
|
1992
|
+
exports.encodeObject = encodeObject;
|
|
1858
1993
|
exports.fill = fill;
|
|
1859
1994
|
exports.flatten = flatten;
|
|
1860
1995
|
exports.flattenDepth = flattenDepth;
|
|
@@ -1862,6 +1997,7 @@ exports.flattenDepthBase = flattenDepthBase;
|
|
|
1862
1997
|
exports.flattenOnce = flattenOnce;
|
|
1863
1998
|
exports.formatBytes = formatBytes;
|
|
1864
1999
|
exports.formatCurrency = formatCurrency;
|
|
2000
|
+
exports.formatCurrencyPro = formatCurrencyPro;
|
|
1865
2001
|
exports.formatDecimalNumber = formatDecimalNumber;
|
|
1866
2002
|
exports.getStringSimilarity = getStringSimilarity;
|
|
1867
2003
|
exports.groupBy = groupBy;
|
|
@@ -1913,6 +2049,7 @@ exports.safeParseInt = safeParseInt;
|
|
|
1913
2049
|
exports.safeSubtract = safeSubtract;
|
|
1914
2050
|
exports.sanitize = sanitize;
|
|
1915
2051
|
exports.sanitizeMarkdown = sanitizeMarkdown;
|
|
2052
|
+
exports.scrambleString = scrambleString;
|
|
1916
2053
|
exports.sentenceCase = sentenceCase;
|
|
1917
2054
|
exports.shuffle = shuffle;
|
|
1918
2055
|
exports.slugify = slugify;
|