@salespark/toolkit 2.1.7 → 2.1.9

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,7 +26,7 @@ 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
31
  - **Function utilities**: debounce, throttle, formatCurrency, parseName, currency conversions, etc.
32
32
  - **Boolean utilities**: safe boolean conversion with common representations
@@ -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
@@ -302,6 +317,30 @@ deburr("café résumé naïve");
302
317
  // Result: "cafe resume naive"
303
318
  ```
304
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
+
305
344
  **`sanitize(input: unknown, maxLength?: number): string`** — Sanitizes input by removing dangerous content.
306
345
 
307
346
  ```javascript
@@ -856,5 +895,5 @@ MIT © [SalesPark](https://salespark.io)
856
895
 
857
896
  ---
858
897
 
859
- _Document version: 9_
860
- _Last update: 01-12-2025_
898
+ _Document version: 11_
899
+ _Last update: 19-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;
@@ -211,6 +211,12 @@ function cleanObject(obj) {
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 "";
@@ -1702,6 +1740,8 @@ exports.areArraysDeepEqualUnordered = areArraysDeepEqualUnordered;
1702
1740
  exports.areArraysEqual = areArraysEqual;
1703
1741
  exports.assessSecurityRisks = assessSecurityRisks;
1704
1742
  exports.basicSanitize = basicSanitize;
1743
+ exports.capitalizeFirst = capitalizeFirst;
1744
+ exports.capitalizeWords = capitalizeWords;
1705
1745
  exports.checkMarkdownSecurity = checkMarkdownSecurity;
1706
1746
  exports.chunk = chunk;
1707
1747
  exports.clamp = clamp;
@@ -1769,6 +1809,7 @@ exports.safeParseInt = safeParseInt;
1769
1809
  exports.safeSubtract = safeSubtract;
1770
1810
  exports.sanitize = sanitize;
1771
1811
  exports.sanitizeMarkdown = sanitizeMarkdown;
1812
+ exports.sentenceCase = sentenceCase;
1772
1813
  exports.shuffle = shuffle;
1773
1814
  exports.slugify = slugify;
1774
1815
  exports.sortBy = sortBy;