@salespark/toolkit 2.1.8 → 2.1.10

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
@@ -26,9 +26,9 @@ npm i @salespark/toolkit
26
26
 
27
27
  - **Array utilities**: chunk, uniqBy, deep equality, flatten, groupBy, etc.
28
28
  - **Object utilities**: pick, omit, clean objects, deep merge, etc.
29
- - **String utilities**: slugify, template fill, deburr, sanitize, etc.
29
+ - **String utilities**: slugify, template fill, deburr, sanitize, capitalize words/sentences.
30
30
  - **Number utilities**: clamp, round, safe arithmetic/comparisons, safe parse (locale-aware), random digits, etc.
31
- - **Function utilities**: debounce, throttle, formatCurrency, parseName, currency conversions, etc.
31
+ - **Function utilities**: debounce, throttle, safeJSONParse, 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
34
34
  - **Security utilities**: Markdown XSS protection, content sanitization, risk assessment
@@ -317,6 +317,30 @@ deburr("café résumé naïve");
317
317
  // Result: "cafe resume naive"
318
318
  ```
319
319
 
320
+ **`capitalizeFirst(input: string, options?: { lowerRest?: boolean; locale?: string | string[] }): string`** — Capitalizes only the first character; optionally lowercases the rest.
321
+
322
+ ```javascript
323
+ capitalizeFirst("hELLO world");
324
+ // Result: "Hello world"
325
+ ```
326
+
327
+ **`capitalizeWords(input: string, options?: { lowerRest?: boolean; locale?: string | string[]; treatHyphenAsSeparator?: boolean }): string`** — Capitalizes each word; can treat hyphen as separator.
328
+
329
+ ```javascript
330
+ capitalizeWords("e-mail marketing");
331
+ // Result: "E-mail Marketing"
332
+
333
+ capitalizeWords("e-mail marketing", { treatHyphenAsSeparator: true });
334
+ // Result: "E-Mail Marketing"
335
+ ```
336
+
337
+ **`sentenceCase(input: string, options?: { lowerRest?: boolean; locale?: string | string[] }): string`** — Capitalizes the start of each sentence (. ! ?); optionally lowercases the rest.
338
+
339
+ ```javascript
340
+ sentenceCase("hello world. this is fine!");
341
+ // Result: "Hello world. This is fine!"
342
+ ```
343
+
320
344
  **`sanitize(input: unknown, maxLength?: number): string`** — Sanitizes input by removing dangerous content.
321
345
 
322
346
  ```javascript
@@ -583,6 +607,19 @@ addThousandsSpace(1234567);
583
607
  // Result: "1 234 567"
584
608
  ```
585
609
 
610
+ **`safeJSONParse<T>(input: unknown, defaultValue: T): T`** — Safely parses a JSON string or returns the object if already parsed. Falls back to default value on failure.
611
+
612
+ ```javascript
613
+ safeJSONParse('{"key": "value"}', {});
614
+ // Result: { key: "value" }
615
+
616
+ safeJSONParse({ already: "object" }, {});
617
+ // Result: { already: "object" }
618
+
619
+ safeJSONParse("{invalid}", { fallback: true });
620
+ // Result: { fallback: true }
621
+ ```
622
+
586
623
  **`formatCurrency(value: number | string | null | undefined, withoutCurrencySymbol?: boolean, currency?: string, locale?: string): string`** — Formats currency values with configurable currency and locale. Uses modern Intl.NumberFormat with automatic thousands separators, proper decimal handling, and graceful fallback for errors.
587
624
 
588
625
  ```javascript
@@ -871,5 +908,5 @@ MIT © [SalesPark](https://salespark.io)
871
908
 
872
909
  ---
873
910
 
874
- _Document version: 10_
875
- _Last update: 11-12-2025_
911
+ _Document version: 12_
912
+ _Last update: 21-12-2025_
package/dist/index.cjs CHANGED
@@ -211,6 +211,12 @@ function cleanObject(obj, removeEmptyString = false) {
211
211
  }
212
212
 
213
213
  // src/utils/string.ts
214
+ function upper(value, locale) {
215
+ return locale ? value.toLocaleUpperCase(locale) : value.toUpperCase();
216
+ }
217
+ function lower(value, locale) {
218
+ return locale ? value.toLocaleLowerCase(locale) : value.toLowerCase();
219
+ }
214
220
  function slugify(input) {
215
221
  return input.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
216
222
  }
@@ -224,6 +230,38 @@ function deburr(str) {
224
230
  return str;
225
231
  }
226
232
  }
233
+ function capitalizeFirst(input, options) {
234
+ if (typeof input !== "string" || input.length === 0) return "";
235
+ const { lowerRest = true, locale } = options ?? {};
236
+ const first = upper(input[0], locale);
237
+ const rest = lowerRest ? lower(input.slice(1), locale) : input.slice(1);
238
+ return first + rest;
239
+ }
240
+ function capitalizeWords(input, options) {
241
+ if (typeof input !== "string" || input.length === 0) return "";
242
+ const {
243
+ lowerRest = true,
244
+ locale,
245
+ treatHyphenAsSeparator = false
246
+ } = options ?? {};
247
+ const wordPattern = treatHyphenAsSeparator ? /[\p{L}\p{N}]+(?:['’][\p{L}\p{N}]+)*/gu : /[\p{L}\p{N}]+(?:['’\-][\p{L}\p{N}]+)*/gu;
248
+ return input.replace(wordPattern, (word) => {
249
+ const first = upper(word[0], locale);
250
+ const rest = lowerRest ? lower(word.slice(1), locale) : word.slice(1);
251
+ return first + rest;
252
+ });
253
+ }
254
+ function sentenceCase(input, options) {
255
+ if (typeof input !== "string" || input.length === 0) return "";
256
+ const { lowerRest = true, locale } = options ?? {};
257
+ const base = lowerRest ? lower(input, locale) : input;
258
+ const sentencePattern = /(^\s*[\p{L}])|([.!?]\s*[\p{L}])/gu;
259
+ return base.replace(sentencePattern, (match) => {
260
+ const lastChar = match[match.length - 1];
261
+ const upperChar = upper(lastChar, locale);
262
+ return match.slice(0, -1) + upperChar;
263
+ });
264
+ }
227
265
  var removeDiacritics = deburr;
228
266
  function sanitize(input, maxLength) {
229
267
  if (typeof input !== "string") return "";
@@ -412,6 +450,20 @@ var formatDecimalNumber = (value, decimals = 2) => {
412
450
  };
413
451
 
414
452
  // src/utils/func.ts
453
+ function safeJSONParse(input, defaultValue) {
454
+ if (typeof input === "object" && input !== null) {
455
+ return input;
456
+ }
457
+ if (typeof input === "string") {
458
+ try {
459
+ const parsed = JSON.parse(input);
460
+ return parsed;
461
+ } catch {
462
+ return defaultValue;
463
+ }
464
+ }
465
+ return defaultValue;
466
+ }
415
467
  function debounce(fn, wait = 250) {
416
468
  let t;
417
469
  return function(...args) {
@@ -1702,6 +1754,8 @@ exports.areArraysDeepEqualUnordered = areArraysDeepEqualUnordered;
1702
1754
  exports.areArraysEqual = areArraysEqual;
1703
1755
  exports.assessSecurityRisks = assessSecurityRisks;
1704
1756
  exports.basicSanitize = basicSanitize;
1757
+ exports.capitalizeFirst = capitalizeFirst;
1758
+ exports.capitalizeWords = capitalizeWords;
1705
1759
  exports.checkMarkdownSecurity = checkMarkdownSecurity;
1706
1760
  exports.chunk = chunk;
1707
1761
  exports.clamp = clamp;
@@ -1763,12 +1817,14 @@ exports.removeDiacritics = removeDiacritics;
1763
1817
  exports.round = round;
1764
1818
  exports.safeAdd = safeAdd;
1765
1819
  exports.safeDivide = safeDivide;
1820
+ exports.safeJSONParse = safeJSONParse;
1766
1821
  exports.safeMultiply = safeMultiply;
1767
1822
  exports.safeParseFloat = safeParseFloat;
1768
1823
  exports.safeParseInt = safeParseInt;
1769
1824
  exports.safeSubtract = safeSubtract;
1770
1825
  exports.sanitize = sanitize;
1771
1826
  exports.sanitizeMarkdown = sanitizeMarkdown;
1827
+ exports.sentenceCase = sentenceCase;
1772
1828
  exports.shuffle = shuffle;
1773
1829
  exports.slugify = slugify;
1774
1830
  exports.sortBy = sortBy;