@h3ravel/support 0.8.5 → 0.9.0

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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
9
  var __export = (target, all) => {
@@ -16,11 +18,19 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/index.ts
22
- var src_exports = {};
23
- __export(src_exports, {
32
+ var index_exports = {};
33
+ __export(index_exports, {
24
34
  abbreviate: () => abbreviate,
25
35
  after: () => after,
26
36
  afterLast: () => afterLast,
@@ -28,7 +38,9 @@ __export(src_exports, {
28
38
  beforeLast: () => beforeLast,
29
39
  capitalize: () => capitalize,
30
40
  chunk: () => chunk,
41
+ dd: () => dd,
31
42
  dot: () => dot,
43
+ dump: () => dump,
32
44
  extractProperties: () => extractProperties,
33
45
  getValue: () => getValue,
34
46
  humanize: () => humanize,
@@ -46,12 +58,11 @@ __export(src_exports, {
46
58
  toHumanTime: () => toHumanTime,
47
59
  truncate: () => truncate
48
60
  });
49
- module.exports = __toCommonJS(src_exports);
61
+ module.exports = __toCommonJS(index_exports);
50
62
 
51
63
  // src/Helpers/Arr.ts
52
64
  var chunk = /* @__PURE__ */ __name((arr, size = 2) => {
53
- if (size <= 0)
54
- throw new Error("Chunk size must be greater than 0");
65
+ if (size <= 0) throw new Error("Chunk size must be greater than 0");
55
66
  const chunks = [];
56
67
  for (let i = 0; i < arr.length; i += size) {
57
68
  chunks.push(arr.slice(i, i + size));
@@ -59,17 +70,37 @@ var chunk = /* @__PURE__ */ __name((arr, size = 2) => {
59
70
  return chunks;
60
71
  }, "chunk");
61
72
  var range = /* @__PURE__ */ __name((size, startAt = 0) => {
62
- if (size <= 0 || !Number.isFinite(size))
63
- return [];
73
+ if (size <= 0 || !Number.isFinite(size)) return [];
64
74
  return Array.from({
65
75
  length: size
66
76
  }, (_, i) => startAt + i);
67
77
  }, "range");
68
78
 
79
+ // src/Helpers/DumpDie.ts
80
+ var import_process = __toESM(require("process"), 1);
81
+ var import_util = __toESM(require("util"), 1);
82
+ var inspect = /* @__PURE__ */ __name((thing) => {
83
+ return import_util.default.inspect(thing, {
84
+ showHidden: true,
85
+ depth: null,
86
+ colors: true
87
+ });
88
+ }, "inspect");
89
+ var dd = /* @__PURE__ */ __name((...args) => {
90
+ args.forEach((thing) => {
91
+ console.log(inspect(thing));
92
+ });
93
+ import_process.default.exit(1);
94
+ }, "dd");
95
+ var dump = /* @__PURE__ */ __name((...args) => {
96
+ args.forEach((thing) => {
97
+ console.log(inspect(thing));
98
+ });
99
+ }, "dump");
100
+
69
101
  // src/Helpers/Number.ts
70
102
  var abbreviate = /* @__PURE__ */ __name((value, locale = "en-US") => {
71
- if (!value)
72
- return "0";
103
+ if (!value) return "0";
73
104
  if (value < 1e3) {
74
105
  return new Intl.NumberFormat(locale).format(value);
75
106
  }
@@ -100,8 +131,7 @@ var abbreviate = /* @__PURE__ */ __name((value, locale = "en-US") => {
100
131
  }
101
132
  ];
102
133
  const match = si.find((scale) => value >= scale.v);
103
- if (!match)
104
- return new Intl.NumberFormat(locale).format(value);
134
+ if (!match) return new Intl.NumberFormat(locale).format(value);
105
135
  const formatted = value / match.v;
106
136
  return new Intl.NumberFormat(locale, {
107
137
  minimumFractionDigits: 0,
@@ -151,10 +181,8 @@ var humanize = /* @__PURE__ */ __name((num, slugify2) => {
151
181
  "ninety"
152
182
  ];
153
183
  const numString = num.toString();
154
- if (num < 0)
155
- throw new Error("Negative numbers are not supported.");
156
- if (num === 0)
157
- return "zero";
184
+ if (num < 0) throw new Error("Negative numbers are not supported.");
185
+ if (num === 0) return "zero";
158
186
  if (num < 20) {
159
187
  return ones[num] ?? "";
160
188
  }
@@ -162,17 +190,13 @@ var humanize = /* @__PURE__ */ __name((num, slugify2) => {
162
190
  return tens[numString[0]] + " " + ones[numString[1]];
163
191
  }
164
192
  if (numString.length == 3) {
165
- if (numString[1] === "0" && numString[2] === "0")
166
- return ones[numString[0]] + " hundred";
167
- else
168
- return ones[numString[0]] + " hundred and " + humanize(+((numString[1] || "") + numString[2]), slugify2);
193
+ if (numString[1] === "0" && numString[2] === "0") return ones[numString[0]] + " hundred";
194
+ else return ones[numString[0]] + " hundred and " + humanize(+((numString[1] || "") + numString[2]), slugify2);
169
195
  }
170
196
  if (numString.length === 4) {
171
197
  const end = +((numString[1] || "") + numString[2] + numString[3]);
172
- if (end === 0)
173
- return ones[numString[0]] + " thousand";
174
- if (end < 100)
175
- return ones[numString[0]] + " thousand and " + humanize(end, slugify2);
198
+ if (end === 0) return ones[numString[0]] + " thousand";
199
+ if (end < 100) return ones[numString[0]] + " thousand and " + humanize(end, slugify2);
176
200
  return ones[numString[0]] + " thousand " + humanize(end, slugify2);
177
201
  }
178
202
  return num;
@@ -209,19 +233,15 @@ var toBytes = /* @__PURE__ */ __name((bytes, decimals = 2, bits = false) => {
209
233
  return `${value} ${sizes[index]}`;
210
234
  }, "toBytes");
211
235
  var toHumanTime = /* @__PURE__ */ __name((seconds = 0, worded = false) => {
212
- if (isNaN(seconds) || seconds < 0)
213
- seconds = 0;
236
+ if (isNaN(seconds) || seconds < 0) seconds = 0;
214
237
  const hours = Math.floor(seconds / 3600);
215
238
  const minutes = Math.floor(seconds % 3600 / 60);
216
239
  const secs = Math.floor(seconds % 60);
217
240
  if (worded) {
218
241
  const parts = [];
219
- if (hours)
220
- parts.push(`${hours}hr`);
221
- if (minutes)
222
- parts.push(`${minutes}min`);
223
- if (secs || !hours && !minutes)
224
- parts.push(`${secs}sec`);
242
+ if (hours) parts.push(`${hours}hr`);
243
+ if (minutes) parts.push(`${minutes}min`);
244
+ if (secs || !hours && !minutes) parts.push(`${secs}sec`);
225
245
  return parts.join(" ");
226
246
  }
227
247
  const hh = hours > 0 ? `${hours}:` : "";
@@ -269,8 +289,7 @@ var modObj = /* @__PURE__ */ __name((obj, callback) => {
269
289
  ])));
270
290
  }, "modObj");
271
291
  function safeDot(data, key) {
272
- if (!key)
273
- return data;
292
+ if (!key) return data;
274
293
  return key.split(".").reduce((acc, k) => acc?.[k], data);
275
294
  }
276
295
  __name(safeDot, "safeDot");
@@ -307,38 +326,32 @@ var slugifyKeys = /* @__PURE__ */ __name((obj, only = [], separator = "_") => {
307
326
 
308
327
  // src/Helpers/Str.ts
309
328
  var after = /* @__PURE__ */ __name((value, search) => {
310
- if (!search)
311
- return value;
329
+ if (!search) return value;
312
330
  const index = value.indexOf(search);
313
331
  return index !== -1 ? value.slice(index + search.length) : value;
314
332
  }, "after");
315
333
  var afterLast = /* @__PURE__ */ __name((value, search) => {
316
- if (!search)
317
- return value;
334
+ if (!search) return value;
318
335
  const lastIndex = value.lastIndexOf(search);
319
336
  return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value;
320
337
  }, "afterLast");
321
338
  var before = /* @__PURE__ */ __name((value, search) => {
322
- if (!search)
323
- return value;
339
+ if (!search) return value;
324
340
  const index = value.indexOf(search);
325
341
  return index !== -1 ? value.slice(0, index) : value;
326
342
  }, "before");
327
343
  var beforeLast = /* @__PURE__ */ __name((value, search) => {
328
- if (!search)
329
- return value;
344
+ if (!search) return value;
330
345
  const lastIndex = value.lastIndexOf(search);
331
346
  return lastIndex !== -1 ? value.slice(0, lastIndex) : value;
332
347
  }, "beforeLast");
333
348
  function capitalize(str) {
334
- if (!str)
335
- return "";
349
+ if (!str) return "";
336
350
  return str[0].toUpperCase() + str.slice(1);
337
351
  }
338
352
  __name(capitalize, "capitalize");
339
353
  var pluralize = /* @__PURE__ */ __name((word, count) => {
340
- if (count === 1)
341
- return word;
354
+ if (count === 1) return word;
342
355
  const irregularPlurals = {
343
356
  foot: "feet",
344
357
  child: "children",
@@ -375,8 +388,7 @@ var singularize = /* @__PURE__ */ __name((word) => {
375
388
  men: "man",
376
389
  women: "woman"
377
390
  };
378
- if (word in irregulars)
379
- return irregulars[word];
391
+ if (word in irregulars) return irregulars[word];
380
392
  if (/ies$/i.test(word) && word.length > 3) {
381
393
  return word.replace(/ies$/i, "y");
382
394
  }
@@ -392,15 +404,12 @@ var slugify = /* @__PURE__ */ __name((str, joiner = "_") => {
392
404
  return str.replace(/([a-z])([A-Z])/g, `$1${joiner}$2`).replace(/[\s\W]+/g, joiner).replace(new RegExp(`${joiner}{2,}`, "g"), joiner).replace(new RegExp(`^${joiner}|${joiner}$`, "g"), "").toLowerCase();
393
405
  }, "slugify");
394
406
  var subString = /* @__PURE__ */ __name((str, len, ellipsis = "...") => {
395
- if (!str)
396
- return "";
397
- if (len <= ellipsis.length)
398
- return ellipsis;
407
+ if (!str) return "";
408
+ if (len <= ellipsis.length) return ellipsis;
399
409
  return str.length > len ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis : str;
400
410
  }, "subString");
401
411
  var substitute = /* @__PURE__ */ __name((str, data = {}, def) => {
402
- if (!str || !data)
403
- return void 0;
412
+ if (!str || !data) return void 0;
404
413
  const regex = /{\s*([a-zA-Z0-9_.]+)\s*}/g;
405
414
  const flattened = dot(data);
406
415
  const out = str.replace(regex, (_, key) => {
@@ -410,8 +419,7 @@ var substitute = /* @__PURE__ */ __name((str, data = {}, def) => {
410
419
  return out;
411
420
  }, "substitute");
412
421
  var truncate = /* @__PURE__ */ __name((str, len = 20, suffix = "...") => {
413
- if (!str)
414
- return "";
422
+ if (!str) return "";
415
423
  const clean = str.replace(/<[^>]+>/g, "");
416
424
  const truncated = clean.length > len ? clean.substring(0, len - suffix.length) + suffix : clean;
417
425
  return truncated.replace(/\n/g, " ").replace(new RegExp(`\\s+${suffix.replace(/\./g, "\\.")}$`), suffix);
@@ -425,7 +433,9 @@ var truncate = /* @__PURE__ */ __name((str, len = 20, suffix = "...") => {
425
433
  beforeLast,
426
434
  capitalize,
427
435
  chunk,
436
+ dd,
428
437
  dot,
438
+ dump,
429
439
  extractProperties,
430
440
  getValue,
431
441
  humanize,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/Helpers/Arr.ts","../src/Helpers/Number.ts","../src/Helpers/Obj.ts","../src/Helpers/Str.ts"],"sourcesContent":["/**\n * @file Automatically generated by barrelsby.\n */\n\nexport * from './Contracts/ObjContract';\nexport * from './Contracts/StrContract';\nexport * from './Helpers/Arr';\nexport * from './Helpers/Number';\nexport * from './Helpers/Obj';\nexport * from './Helpers/Str';\n","/**\n * Splits an array into chunks of a specified size.\n *\n * @template T - Type of elements in the array\n * @param arr - The input array\n * @param size - Size of each chunk (default: 2)\n * @returns An array of chunks (arrays)\n */\nexport const chunk = <T> (arr: T[], size: number = 2): T[][] => {\n if (size <= 0) throw new Error('Chunk size must be greater than 0')\n\n const chunks: T[][] = []\n\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size))\n }\n\n return chunks\n}\n\n\n/**\n * Generates an array of sequential numbers.\n *\n * @param size - Number of elements in the range\n * @param startAt - Starting number (default: 0)\n * @returns An array of numbers from startAt to startAt + size - 1\n */\nexport const range = (size: number, startAt: number = 0): number[] => {\n if (size <= 0 || !Number.isFinite(size)) return []\n\n return Array.from({ length: size }, (_, i) => startAt + i)\n}\n","/**\n * Abbreviates large numbers using SI symbols (K, M, B...) \n * and formats the output according to the given locale.\n *\n * @param value - The number to abbreviate\n * @param locale - Optional locale string (default: \"en-US\")\n * @returns A localized, abbreviated number string\n */\nexport const abbreviate = (value?: number, locale: string = 'en-US'): string => {\n if (!value) return '0'\n\n // Numbers less than 1000 don't need abbreviation\n if (value < 1000) {\n return new Intl.NumberFormat(locale).format(value)\n }\n\n const si = [\n { v: 1e18, s: 'E' },\n { v: 1e15, s: 'P' },\n { v: 1e12, s: 'T' },\n { v: 1e9, s: 'B' },\n { v: 1e6, s: 'M' },\n { v: 1e3, s: 'K' },\n ]\n\n const match = si.find(scale => value >= scale.v)\n if (!match) return new Intl.NumberFormat(locale).format(value)\n\n const formatted = value / match.v\n\n return (\n new Intl.NumberFormat(locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(formatted) + match.s\n )\n}\n\n/**\n * Concverts a number into human readable string\n *\n * @param num The number to convert\n * @param slugify convert the ouput into a slug using this as a separator\n * @returns\n */\nexport const humanize = (num: number, slugify?: '-' | '_'): string => {\n if (!num) {\n return ''\n }\n\n if (slugify === '-' || slugify === '_') {\n const h = humanize(num)\n return typeof h === 'string' ? h.replace(' ', slugify).toLowerCase() : h\n }\n\n const ones = [\n '',\n 'one',\n 'two',\n 'three',\n 'four',\n 'five',\n 'six',\n 'seven',\n 'eight',\n 'nine',\n 'ten',\n 'eleven',\n 'twelve',\n 'thirteen',\n 'fourteen',\n 'fifteen',\n 'sixteen',\n 'seventeen',\n 'eighteen',\n 'nineteen',\n ]\n const tens = [\n '',\n '',\n 'twenty',\n 'thirty',\n 'forty',\n 'fifty',\n 'sixty',\n 'seventy',\n 'eighty',\n 'ninety',\n ]\n\n const numString: string = num.toString()\n\n if (num < 0) throw new Error('Negative numbers are not supported.')\n\n if (num === 0) return 'zero'\n\n //the case of 1 - 20\n if (num < 20) {\n return ones[num] ?? ''\n }\n\n if (numString.length === 2) {\n return tens[numString[0] as unknown as number] + ' ' + ones[numString[1] as unknown as number]\n }\n\n //100 and more\n if (numString.length == 3) {\n if (numString[1] === '0' && numString[2] === '0')\n return ones[numString[0] as unknown as number] + ' hundred'\n else\n return (\n ones[numString[0] as unknown as number] +\n ' hundred and ' +\n humanize(+((numString[1] || '') + numString[2]), slugify)\n )\n }\n\n if (numString.length === 4) {\n const end = +((numString[1] || '') + numString[2] + numString[3])\n if (end === 0) return ones[numString[0] as unknown as number] + ' thousand'\n if (end < 100)\n return ones[numString[0] as unknown as number] + ' thousand and ' + humanize(end, slugify)\n return ones[numString[0] as unknown as number] + ' thousand ' + humanize(end, slugify)\n }\n\n return num as unknown as string\n}\n\n/**\n * Converts a number of bytes into a human-readable string.\n *\n * @param bytes - The size in bytes to convert\n * @param decimals - Number of decimal places to display (default: 2)\n * @param bits - If true, uses 1000-based (SI) units (B, KB, MB...); \n * otherwise uses 1024-based binary units (Bytes, KiB...)\n * @returns A formatted string with the appropriate unit\n */\nexport const toBytes = (\n bytes?: number,\n decimals: number = 2,\n bits: boolean = false\n): string => {\n if (!bytes || isNaN(bytes)) {\n return bits ? '0 B' : '0 Bytes'\n }\n\n const base = bits ? 1000 : 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = bits\n ? ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] // SI units\n : ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] // Binary units\n\n const index = Math.floor(Math.log(bytes) / Math.log(base))\n\n const value = parseFloat((bytes / Math.pow(base, index)).toFixed(dm))\n return `${value} ${sizes[index]}`\n}\n\n/**\n * Formats a duration (in seconds) into a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @param worded - If true, outputs worded format (e.g., \"1hr 2min 3sec\"),\n * otherwise HH:MM:SS (e.g., \"01:02:03\")\n * @returns A formatted time string\n */\nexport const toHumanTime = (\n seconds: number = 0,\n worded: boolean = false\n): string => {\n // Ensure seconds is a number and not negative\n if (isNaN(seconds) || seconds < 0) seconds = 0\n\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const secs = Math.floor(seconds % 60)\n\n // Worded format → \"1hr 2min 3sec\"\n if (worded) {\n const parts = []\n if (hours) parts.push(`${hours}hr`)\n if (minutes) parts.push(`${minutes}min`)\n if (secs || (!hours && !minutes)) parts.push(`${secs}sec`)\n return parts.join(' ')\n }\n\n // HH:MM:SS format → zero-padded\n const hh = hours > 0 ? `${hours}:` : ''\n const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ':'\n const ss = secs < 10 ? `0${secs}` : secs\n\n return `${hh}${mm}${ss}`\n}\n\n","import { DotFlatten, DotNestedKeys, DotNestedValue, KeysToSnakeCase } from '../Contracts/ObjContract'\n\n/**\n * Flattens a nested object into a single-level object\n * with dot-separated keys.\n *\n * Example:\n * doter({\n * user: { name: \"John\", address: { city: \"NY\" } },\n * active: true\n * })\n * \n * Output:\n * {\n * \"user.name\": \"John\",\n * \"user.address.city\": \"NY\",\n * \"active\": true\n * }\n *\n * @template T - The type of the input object\n * @param obj - The nested object to flatten\n * @returns A flattened object with dotted keys and inferred types\n */\nexport const dot = <T extends Record<string, any>> (obj: T): DotFlatten<T> => {\n const result = {} as Record<string, unknown>\n\n /**\n * Internal recursive function to traverse and flatten the object.\n * \n * @param o - Current object to flatten\n * @param prefix - Key path accumulated so far\n */\n const recurse = (o: Record<string, any>, prefix = ''): void => {\n for (const [key, value] of Object.entries(o)) {\n const newKey = prefix ? `${prefix}.${key}` : key\n\n /**\n * Recurse if the value is a plain object\n */\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n recurse(value, newKey)\n } else {\n /**\n * Otherwise, assign directly\n */\n result[newKey] = value\n }\n }\n }\n\n recurse(obj)\n return result as DotFlatten<T>\n}\n\n/**\n * Extracts a subset of properties from an object.\n *\n * @template T - Type of the source object\n * @template K - Keys of T to extract\n * @param obj - The source object\n * @param keys - Array of keys to extract\n * @returns A new object with only the specified keys\n */\nexport const extractProperties = <T extends object, K extends keyof T> (\n obj: T,\n keys: readonly K[] = []\n): Pick<T, K> => {\n return Object.fromEntries(\n keys.map(key => [key, obj[key]])\n ) as Pick<T, K>\n}\n\n/**\n * Safely retrieves a value from an object by key or nested keys.\n *\n * @template T - Type of the source object\n * @param key - Single key or tuple [parentKey, childKey]\n * @param item - The source object\n * @returns The found value as a string or the key itself if not found\n */\nexport const getValue = <\n T extends Record<string, any> // Allow nested objects\n> (\n key: string | [keyof T, keyof T[string]],\n item: T\n): string => {\n if (Array.isArray(key)) {\n const [parent, child] = key\n\n if (child !== undefined) {\n // Access nested property: item[parent][child]\n return (\n String(item?.[parent]?.[child] ??\n item?.[parent] ??\n `${String(parent)}.${String(child)}`)\n )\n }\n\n // Only top-level key\n return String(item?.[parent] ?? parent)\n }\n\n // Single key access\n return String(item?.[key] ?? key)\n}\n\n/**\n * Maps over an object's entries and returns a new object \n * with transformed keys and/or values.\n *\n * @template T - Type of the input object\n * @template R - Type of the new values\n * @param obj - The object to transform\n * @param callback - Function that receives [key, value] and returns [newKey, newValue]\n * @returns A new object with transformed entries\n */\nexport const modObj = <T extends object, R> (\n obj: T,\n callback: (_entry: [keyof T & string, T[keyof T]]) => [string, R]\n): Record<string, R> => {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) =>\n callback([key as keyof T & string, value as T[keyof T]])\n )\n ) as Record<string, R>\n}\n\n\nexport function safeDot<T extends Record<string, any>> (_data: T): T\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (_data: T, _key?: K): DotNestedValue<T, K>\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (data: T, key?: K): any {\n if (!key) return data\n return key.split('.').reduce((acc: any, k) => acc?.[k], data)\n}\n\n/**\n * Sets a nested property on an object using dot notation.\n * \n * @example\n * const obj = {}\n * setNested(obj, 'app.user.name', 'Legacy')\n * console.log(obj)\n * // Output: { app: { user: { name: 'Legacy' } } }\n * \n * @param obj - The target object to modify.\n * @param key - The dot-separated key (e.g., 'app.user.name').\n * @param value - The value to set at the specified path.\n */\nexport const setNested = (\n obj: Record<string, any>,\n key: string,\n value: any\n): void => {\n if (!key.includes('.')) {\n obj[key] = value\n return\n }\n\n const parts = key.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n\n /**\n * If we're at the last key, assign the value\n */\n if (i === parts.length - 1) {\n current[part] = value\n } else {\n /**\n * If the key doesn't exist or isn't an object, create it\n */\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part]\n }\n }\n}\n\n/**\n * Converts object keys to a slugified format (e.g., snake_case).\n *\n * @template T - Type of the input object\n * @param obj - The object whose keys will be slugified\n * @param only - Optional array of keys to slugify (others remain unchanged)\n * @param separator - Separator for slugified keys (default: \"_\")\n * @returns A new object with slugified keys\n */\nexport const slugifyKeys = <T extends object> (\n obj: T,\n only: string[] = [],\n separator: string = '_'\n): KeysToSnakeCase<T> => {\n const slugify = (key: string): string =>\n key\n .replace(/([a-z])([A-Z])/g, `$1${separator}$2`) // Handle camelCase\n .replace(/[\\s\\W]+/g, separator) // Replace spaces/symbols\n .replace(new RegExp(`${separator}{2,}`, 'g'), separator) // Remove duplicate separators\n .replace(new RegExp(`^${separator}|${separator}$`, 'g'), '') // Trim edges\n .toLowerCase()\n\n let entries = Object.entries(obj)\n\n // Filter if `only` is provided\n if (only.length) {\n entries = entries.filter(([key]) => only.includes(key))\n }\n\n return Object.fromEntries(\n entries.map(([key, value]) => [slugify(key), value])\n ) as KeysToSnakeCase<T>\n}\n","import { dot } from './Obj'\n\n/**\n * Get the portion of the string after the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const after = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(index + search.length) : value\n}\n\n/**\n * Get the portion of the string after the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const afterLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value\n}\n\n/**\n * Get the portion of the string before the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const before = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(0, index) : value\n}\n\n/**\n * Get the portion of the string before the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const beforeLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(0, lastIndex) : value\n}\n\n/**\n * Capitalizes the first character of a string.\n *\n * @param str - The input string\n * @returns The string with the first character capitalized\n */\nexport function capitalize (str: string): string {\n if (!str) return '' // Handle empty or undefined strings safely\n return str[0].toUpperCase() + str.slice(1)\n}\n\n\n/**\n * Returns the pluralized form of a word based on the given number.\n *\n * @param word - The word to pluralize\n * @param count - The number determining pluralization\n * @returns Singular if count === 1, otherwise plural form\n */\nexport const pluralize = (word: string, count: number): string => {\n // If count is exactly 1 → singular\n if (count === 1) return word\n\n // Irregular plurals map\n const irregularPlurals: Record<string, string> = {\n foot: 'feet',\n child: 'children',\n mouse: 'mice',\n goose: 'geese',\n person: 'people',\n man: 'men',\n woman: 'women',\n }\n\n // Handle irregular cases first\n if (word in irregularPlurals) {\n return irregularPlurals[word]\n }\n\n // If word ends with consonant + \"y\" → replace \"y\" with \"ies\"\n if (\n word.endsWith('y') &&\n !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2]?.toLowerCase() ?? '')\n ) {\n return word.slice(0, -1) + 'ies'\n }\n\n // If word ends in \"s\", \"ss\", \"sh\", \"ch\", \"x\", or \"z\" → add \"es\"\n if (/(s|ss|sh|ch|x|z)$/i.test(word)) {\n return word + 'es'\n }\n\n // Default: just add \"s\"\n return word + 's'\n}\n\n/**\n * Converts a plural English word into its singular form.\n *\n * @param word - The word to singularize\n * @returns The singular form of the word\n */\nexport const singularize = (word: string): string => {\n // Irregular plurals map (reverse of pluralize)\n const irregulars: Record<string, string> = {\n feet: 'foot',\n children: 'child',\n mice: 'mouse',\n geese: 'goose',\n people: 'person',\n men: 'man',\n women: 'woman',\n }\n\n // Handle irregular cases\n if (word in irregulars) return irregulars[word]\n\n // Words ending in \"ies\" → change to \"y\" (e.g., \"bodies\" → \"body\")\n if (/ies$/i.test(word) && word.length > 3) {\n return word.replace(/ies$/i, 'y')\n }\n\n // Words ending in \"es\" after certain consonants → remove \"es\"\n if (/(ches|shes|sses|xes|zes)$/i.test(word)) {\n return word.replace(/es$/i, '')\n }\n\n // Generic case: remove trailing \"s\"\n if (/s$/i.test(word) && word.length > 1) {\n return word.replace(/s$/i, '')\n }\n\n return word\n}\n\n/**\n * Converts a string into a slug format.\n * Handles camelCase, spaces, and non-alphanumeric characters.\n *\n * @param str - The input string to slugify\n * @param joiner - The character used to join words (default: \"_\")\n * @returns A slugified string\n */\nexport const slugify = (str: string, joiner = '_'): string => {\n return str\n // Handle camelCase by adding joiner between lowercase → uppercase\n .replace(/([a-z])([A-Z])/g, `$1${joiner}$2`)\n // Replace spaces and non-alphanumeric characters with joiner\n .replace(/[\\s\\W]+/g, joiner)\n // Remove duplicate joiners\n .replace(new RegExp(`${joiner}{2,}`, 'g'), joiner)\n // Trim joiners from start/end\n .replace(new RegExp(`^${joiner}|${joiner}$`, 'g'), '')\n .toLowerCase()\n}\n\n/**\n * Truncates a string to a specified length and appends an ellipsis if needed.\n *\n * @param str - The input string\n * @param len - Maximum length of the result (including ellipsis)\n * @param ellipsis - String to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const subString = (\n str: string,\n len: number,\n ellipsis: string = '...'\n): string => {\n if (!str) return ''\n if (len <= ellipsis.length) return ellipsis // Avoid negative slicing\n\n return str.length > len\n ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis\n : str\n}\n\n/**\n * Replaces placeholders in a string with corresponding values from a data object.\n * \n * Example:\n * substitute(\"Hello { user.name }!\", { user: { name: \"John\" } })\n * // \"Hello John!\"\n *\n * @param str - The string containing placeholders wrapped in { } braces.\n * @param data - Object containing values to substitute. Supports nested keys via dot notation.\n * @param def - Default value to use if a key is missing. (Optional)\n * @returns The substituted string or undefined if the input string or data is invalid.\n */\nexport const substitute = (\n str: string,\n data: Record<string, unknown> = {},\n def?: string\n): string | undefined => {\n if (!str || !data) return undefined\n\n // Matches { key } or { nested.key } placeholders\n const regex = /{\\s*([a-zA-Z0-9_.]+)\\s*}/g\n\n // Flatten the data so we can directly access dot notation keys\n const flattened = dot(data)\n\n // Replace each placeholder with its value or the default\n const out = str.replace(regex, (_, key: string) => {\n const value = flattened[key]\n return value !== undefined ? String(value) : def ?? ''\n })\n\n return out\n}\n\n/**\n * Truncates a string to a specified length, removing HTML tags and \n * appending a suffix if the string exceeds the length.\n *\n * @param str - The string to truncate\n * @param len - Maximum length (default: 20)\n * @param suffix - Suffix to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (\n str: string,\n len: number = 20,\n suffix: string = '...'\n): string => {\n if (!str) return ''\n\n // Remove any HTML tags\n const clean = str.replace(/<[^>]+>/g, '')\n\n // Determine if we need to truncate\n const truncated =\n clean.length > len\n ? clean.substring(0, len - suffix.length) + suffix\n : clean\n\n // Normalize spaces and line breaks\n return truncated\n .replace(/\\n/g, ' ') // Replace all line breaks\n .replace(new RegExp(`\\\\s+${suffix.replace(/\\./g, '\\\\.')}$`), suffix) // Avoid extra space before suffix\n}\n\n"],"mappings":"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","names":["chunk","arr","size","Error","chunks","i","length","push","slice","range","startAt","Number","isFinite","Array","from","_","abbreviate","value","locale","Intl","NumberFormat","format","si","v","s","match","find","scale","formatted","minimumFractionDigits","maximumFractionDigits","humanize","num","slugify","h","replace","toLowerCase","ones","tens","numString","toString","Error","length","end","toBytes","bytes","decimals","bits","isNaN","base","dm","sizes","index","Math","floor","log","parseFloat","pow","toFixed","toHumanTime","seconds","worded","hours","minutes","secs","parts","push","join","hh","mm","ss","dot","obj","result","recurse","o","prefix","key","value","Object","entries","newKey","Array","isArray","extractProperties","keys","fromEntries","map","getValue","item","parent","child","undefined","String","modObj","callback","safeDot","data","split","reduce","acc","k","setNested","includes","parts","current","i","length","part","slugifyKeys","only","separator","slugify","replace","RegExp","toLowerCase","filter","after","value","search","index","indexOf","slice","length","afterLast","lastIndex","lastIndexOf","before","beforeLast","capitalize","str","toUpperCase","pluralize","word","count","irregularPlurals","foot","child","mouse","goose","person","man","woman","endsWith","includes","toLowerCase","test","singularize","irregulars","feet","children","mice","geese","people","men","women","replace","slugify","joiner","RegExp","subString","len","ellipsis","substring","trimEnd","substitute","data","def","undefined","regex","flattened","dot","out","_","key","String","truncate","suffix","clean","truncated"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/Helpers/Arr.ts","../src/Helpers/DumpDie.ts","../src/Helpers/Number.ts","../src/Helpers/Obj.ts","../src/Helpers/Str.ts"],"sourcesContent":["export * from './Contracts/ObjContract'\nexport * from './Contracts/StrContract'\nexport * from './Helpers/Arr'\nexport * from './Helpers/DumpDie'\nexport * from './Helpers/Number'\nexport * from './Helpers/Obj'\nexport * from './Helpers/Str'\n","/**\n * Splits an array into chunks of a specified size.\n *\n * @template T - Type of elements in the array\n * @param arr - The input array\n * @param size - Size of each chunk (default: 2)\n * @returns An array of chunks (arrays)\n */\nexport const chunk = <T> (arr: T[], size: number = 2): T[][] => {\n if (size <= 0) throw new Error('Chunk size must be greater than 0')\n\n const chunks: T[][] = []\n\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size))\n }\n\n return chunks\n}\n\n\n/**\n * Generates an array of sequential numbers.\n *\n * @param size - Number of elements in the range\n * @param startAt - Starting number (default: 0)\n * @returns An array of numbers from startAt to startAt + size - 1\n */\nexport const range = (size: number, startAt: number = 0): number[] => {\n if (size <= 0 || !Number.isFinite(size)) return []\n\n return Array.from({ length: size }, (_, i) => startAt + i)\n}\n","import process from 'process'\nimport util from 'util'\n\nconst inspect = (thing: any) => {\n return util.inspect(thing, {\n showHidden: true,\n depth: null,\n colors: true\n })\n}\n\n/**\n * Dump something and kill the process for quick debugging. Based on Laravel's dd()\n * \n * @param args \n */\nexport const dd = (...args: unknown[]): never => {\n args.forEach((thing) => {\n console.log(inspect(thing))\n })\n\n process.exit(1)\n}\n\n/**\n * Dump something but keep the process for quick debugging. Based on Laravel's dump()\n * \n * @param args \n */\nexport const dump = (...args: unknown[]): void => {\n args.forEach((thing) => {\n console.log(inspect(thing))\n })\n} \n","/**\n * Abbreviates large numbers using SI symbols (K, M, B...) \n * and formats the output according to the given locale.\n *\n * @param value - The number to abbreviate\n * @param locale - Optional locale string (default: \"en-US\")\n * @returns A localized, abbreviated number string\n */\nexport const abbreviate = (value?: number, locale: string = 'en-US'): string => {\n if (!value) return '0'\n\n // Numbers less than 1000 don't need abbreviation\n if (value < 1000) {\n return new Intl.NumberFormat(locale).format(value)\n }\n\n const si = [\n { v: 1e18, s: 'E' },\n { v: 1e15, s: 'P' },\n { v: 1e12, s: 'T' },\n { v: 1e9, s: 'B' },\n { v: 1e6, s: 'M' },\n { v: 1e3, s: 'K' },\n ]\n\n const match = si.find(scale => value >= scale.v)\n if (!match) return new Intl.NumberFormat(locale).format(value)\n\n const formatted = value / match.v\n\n return (\n new Intl.NumberFormat(locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(formatted) + match.s\n )\n}\n\n/**\n * Concverts a number into human readable string\n *\n * @param num The number to convert\n * @param slugify convert the ouput into a slug using this as a separator\n * @returns\n */\nexport const humanize = (num: number, slugify?: '-' | '_'): string => {\n if (!num) {\n return ''\n }\n\n if (slugify === '-' || slugify === '_') {\n const h = humanize(num)\n return typeof h === 'string' ? h.replace(' ', slugify).toLowerCase() : h\n }\n\n const ones = [\n '',\n 'one',\n 'two',\n 'three',\n 'four',\n 'five',\n 'six',\n 'seven',\n 'eight',\n 'nine',\n 'ten',\n 'eleven',\n 'twelve',\n 'thirteen',\n 'fourteen',\n 'fifteen',\n 'sixteen',\n 'seventeen',\n 'eighteen',\n 'nineteen',\n ]\n const tens = [\n '',\n '',\n 'twenty',\n 'thirty',\n 'forty',\n 'fifty',\n 'sixty',\n 'seventy',\n 'eighty',\n 'ninety',\n ]\n\n const numString: string = num.toString()\n\n if (num < 0) throw new Error('Negative numbers are not supported.')\n\n if (num === 0) return 'zero'\n\n //the case of 1 - 20\n if (num < 20) {\n return ones[num] ?? ''\n }\n\n if (numString.length === 2) {\n return tens[numString[0] as unknown as number] + ' ' + ones[numString[1] as unknown as number]\n }\n\n //100 and more\n if (numString.length == 3) {\n if (numString[1] === '0' && numString[2] === '0')\n return ones[numString[0] as unknown as number] + ' hundred'\n else\n return (\n ones[numString[0] as unknown as number] +\n ' hundred and ' +\n humanize(+((numString[1] || '') + numString[2]), slugify)\n )\n }\n\n if (numString.length === 4) {\n const end = +((numString[1] || '') + numString[2] + numString[3])\n if (end === 0) return ones[numString[0] as unknown as number] + ' thousand'\n if (end < 100)\n return ones[numString[0] as unknown as number] + ' thousand and ' + humanize(end, slugify)\n return ones[numString[0] as unknown as number] + ' thousand ' + humanize(end, slugify)\n }\n\n return num as unknown as string\n}\n\n/**\n * Converts a number of bytes into a human-readable string.\n *\n * @param bytes - The size in bytes to convert\n * @param decimals - Number of decimal places to display (default: 2)\n * @param bits - If true, uses 1000-based (SI) units (B, KB, MB...); \n * otherwise uses 1024-based binary units (Bytes, KiB...)\n * @returns A formatted string with the appropriate unit\n */\nexport const toBytes = (\n bytes?: number,\n decimals: number = 2,\n bits: boolean = false\n): string => {\n if (!bytes || isNaN(bytes)) {\n return bits ? '0 B' : '0 Bytes'\n }\n\n const base = bits ? 1000 : 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = bits\n ? ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] // SI units\n : ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] // Binary units\n\n const index = Math.floor(Math.log(bytes) / Math.log(base))\n\n const value = parseFloat((bytes / Math.pow(base, index)).toFixed(dm))\n return `${value} ${sizes[index]}`\n}\n\n/**\n * Formats a duration (in seconds) into a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @param worded - If true, outputs worded format (e.g., \"1hr 2min 3sec\"),\n * otherwise HH:MM:SS (e.g., \"01:02:03\")\n * @returns A formatted time string\n */\nexport const toHumanTime = (\n seconds: number = 0,\n worded: boolean = false\n): string => {\n // Ensure seconds is a number and not negative\n if (isNaN(seconds) || seconds < 0) seconds = 0\n\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const secs = Math.floor(seconds % 60)\n\n // Worded format → \"1hr 2min 3sec\"\n if (worded) {\n const parts = []\n if (hours) parts.push(`${hours}hr`)\n if (minutes) parts.push(`${minutes}min`)\n if (secs || (!hours && !minutes)) parts.push(`${secs}sec`)\n return parts.join(' ')\n }\n\n // HH:MM:SS format → zero-padded\n const hh = hours > 0 ? `${hours}:` : ''\n const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ':'\n const ss = secs < 10 ? `0${secs}` : secs\n\n return `${hh}${mm}${ss}`\n}\n\n","import { DotFlatten, DotNestedKeys, DotNestedValue, KeysToSnakeCase } from '../Contracts/ObjContract'\n\n/**\n * Flattens a nested object into a single-level object\n * with dot-separated keys.\n *\n * Example:\n * doter({\n * user: { name: \"John\", address: { city: \"NY\" } },\n * active: true\n * })\n * \n * Output:\n * {\n * \"user.name\": \"John\",\n * \"user.address.city\": \"NY\",\n * \"active\": true\n * }\n *\n * @template T - The type of the input object\n * @param obj - The nested object to flatten\n * @returns A flattened object with dotted keys and inferred types\n */\nexport const dot = <T extends Record<string, any>> (obj: T): DotFlatten<T> => {\n const result = {} as Record<string, unknown>\n\n /**\n * Internal recursive function to traverse and flatten the object.\n * \n * @param o - Current object to flatten\n * @param prefix - Key path accumulated so far\n */\n const recurse = (o: Record<string, any>, prefix = ''): void => {\n for (const [key, value] of Object.entries(o)) {\n const newKey = prefix ? `${prefix}.${key}` : key\n\n /**\n * Recurse if the value is a plain object\n */\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n recurse(value, newKey)\n } else {\n /**\n * Otherwise, assign directly\n */\n result[newKey] = value\n }\n }\n }\n\n recurse(obj)\n return result as DotFlatten<T>\n}\n\n/**\n * Extracts a subset of properties from an object.\n *\n * @template T - Type of the source object\n * @template K - Keys of T to extract\n * @param obj - The source object\n * @param keys - Array of keys to extract\n * @returns A new object with only the specified keys\n */\nexport const extractProperties = <T extends object, K extends keyof T> (\n obj: T,\n keys: readonly K[] = []\n): Pick<T, K> => {\n return Object.fromEntries(\n keys.map(key => [key, obj[key]])\n ) as Pick<T, K>\n}\n\n/**\n * Safely retrieves a value from an object by key or nested keys.\n *\n * @template T - Type of the source object\n * @param key - Single key or tuple [parentKey, childKey]\n * @param item - The source object\n * @returns The found value as a string or the key itself if not found\n */\nexport const getValue = <\n T extends Record<string, any> // Allow nested objects\n> (\n key: string | [keyof T, keyof T[string]],\n item: T\n): string => {\n if (Array.isArray(key)) {\n const [parent, child] = key\n\n if (child !== undefined) {\n // Access nested property: item[parent][child]\n return (\n String(item?.[parent]?.[child] ??\n item?.[parent] ??\n `${String(parent)}.${String(child)}`)\n )\n }\n\n // Only top-level key\n return String(item?.[parent] ?? parent)\n }\n\n // Single key access\n return String(item?.[key] ?? key)\n}\n\n/**\n * Maps over an object's entries and returns a new object \n * with transformed keys and/or values.\n *\n * @template T - Type of the input object\n * @template R - Type of the new values\n * @param obj - The object to transform\n * @param callback - Function that receives [key, value] and returns [newKey, newValue]\n * @returns A new object with transformed entries\n */\nexport const modObj = <T extends object, R> (\n obj: T,\n callback: (_entry: [keyof T & string, T[keyof T]]) => [string, R]\n): Record<string, R> => {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) =>\n callback([key as keyof T & string, value as T[keyof T]])\n )\n ) as Record<string, R>\n}\n\n\nexport function safeDot<T extends Record<string, any>> (_data: T): T\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (_data: T, _key?: K): DotNestedValue<T, K>\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (data: T, key?: K): any {\n if (!key) return data\n return key.split('.').reduce((acc: any, k) => acc?.[k], data)\n}\n\n/**\n * Sets a nested property on an object using dot notation.\n * \n * @example\n * const obj = {}\n * setNested(obj, 'app.user.name', 'Legacy')\n * console.log(obj)\n * // Output: { app: { user: { name: 'Legacy' } } }\n * \n * @param obj - The target object to modify.\n * @param key - The dot-separated key (e.g., 'app.user.name').\n * @param value - The value to set at the specified path.\n */\nexport const setNested = (\n obj: Record<string, any>,\n key: string,\n value: any\n): void => {\n if (!key.includes('.')) {\n obj[key] = value\n return\n }\n\n const parts = key.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n\n /**\n * If we're at the last key, assign the value\n */\n if (i === parts.length - 1) {\n current[part] = value\n } else {\n /**\n * If the key doesn't exist or isn't an object, create it\n */\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part]\n }\n }\n}\n\n/**\n * Converts object keys to a slugified format (e.g., snake_case).\n *\n * @template T - Type of the input object\n * @param obj - The object whose keys will be slugified\n * @param only - Optional array of keys to slugify (others remain unchanged)\n * @param separator - Separator for slugified keys (default: \"_\")\n * @returns A new object with slugified keys\n */\nexport const slugifyKeys = <T extends object> (\n obj: T,\n only: string[] = [],\n separator: string = '_'\n): KeysToSnakeCase<T> => {\n const slugify = (key: string): string =>\n key\n .replace(/([a-z])([A-Z])/g, `$1${separator}$2`) // Handle camelCase\n .replace(/[\\s\\W]+/g, separator) // Replace spaces/symbols\n .replace(new RegExp(`${separator}{2,}`, 'g'), separator) // Remove duplicate separators\n .replace(new RegExp(`^${separator}|${separator}$`, 'g'), '') // Trim edges\n .toLowerCase()\n\n let entries = Object.entries(obj)\n\n // Filter if `only` is provided\n if (only.length) {\n entries = entries.filter(([key]) => only.includes(key))\n }\n\n return Object.fromEntries(\n entries.map(([key, value]) => [slugify(key), value])\n ) as KeysToSnakeCase<T>\n}\n","import { dot } from './Obj'\n\n/**\n * Get the portion of the string after the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const after = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(index + search.length) : value\n}\n\n/**\n * Get the portion of the string after the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const afterLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value\n}\n\n/**\n * Get the portion of the string before the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const before = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(0, index) : value\n}\n\n/**\n * Get the portion of the string before the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const beforeLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(0, lastIndex) : value\n}\n\n/**\n * Capitalizes the first character of a string.\n *\n * @param str - The input string\n * @returns The string with the first character capitalized\n */\nexport function capitalize (str: string): string {\n if (!str) return '' // Handle empty or undefined strings safely\n return str[0].toUpperCase() + str.slice(1)\n}\n\n\n/**\n * Returns the pluralized form of a word based on the given number.\n *\n * @param word - The word to pluralize\n * @param count - The number determining pluralization\n * @returns Singular if count === 1, otherwise plural form\n */\nexport const pluralize = (word: string, count: number): string => {\n // If count is exactly 1 → singular\n if (count === 1) return word\n\n // Irregular plurals map\n const irregularPlurals: Record<string, string> = {\n foot: 'feet',\n child: 'children',\n mouse: 'mice',\n goose: 'geese',\n person: 'people',\n man: 'men',\n woman: 'women',\n }\n\n // Handle irregular cases first\n if (word in irregularPlurals) {\n return irregularPlurals[word]\n }\n\n // If word ends with consonant + \"y\" → replace \"y\" with \"ies\"\n if (\n word.endsWith('y') &&\n !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2]?.toLowerCase() ?? '')\n ) {\n return word.slice(0, -1) + 'ies'\n }\n\n // If word ends in \"s\", \"ss\", \"sh\", \"ch\", \"x\", or \"z\" → add \"es\"\n if (/(s|ss|sh|ch|x|z)$/i.test(word)) {\n return word + 'es'\n }\n\n // Default: just add \"s\"\n return word + 's'\n}\n\n/**\n * Converts a plural English word into its singular form.\n *\n * @param word - The word to singularize\n * @returns The singular form of the word\n */\nexport const singularize = (word: string): string => {\n // Irregular plurals map (reverse of pluralize)\n const irregulars: Record<string, string> = {\n feet: 'foot',\n children: 'child',\n mice: 'mouse',\n geese: 'goose',\n people: 'person',\n men: 'man',\n women: 'woman',\n }\n\n // Handle irregular cases\n if (word in irregulars) return irregulars[word]\n\n // Words ending in \"ies\" → change to \"y\" (e.g., \"bodies\" → \"body\")\n if (/ies$/i.test(word) && word.length > 3) {\n return word.replace(/ies$/i, 'y')\n }\n\n // Words ending in \"es\" after certain consonants → remove \"es\"\n if (/(ches|shes|sses|xes|zes)$/i.test(word)) {\n return word.replace(/es$/i, '')\n }\n\n // Generic case: remove trailing \"s\"\n if (/s$/i.test(word) && word.length > 1) {\n return word.replace(/s$/i, '')\n }\n\n return word\n}\n\n/**\n * Converts a string into a slug format.\n * Handles camelCase, spaces, and non-alphanumeric characters.\n *\n * @param str - The input string to slugify\n * @param joiner - The character used to join words (default: \"_\")\n * @returns A slugified string\n */\nexport const slugify = (str: string, joiner = '_'): string => {\n return str\n // Handle camelCase by adding joiner between lowercase → uppercase\n .replace(/([a-z])([A-Z])/g, `$1${joiner}$2`)\n // Replace spaces and non-alphanumeric characters with joiner\n .replace(/[\\s\\W]+/g, joiner)\n // Remove duplicate joiners\n .replace(new RegExp(`${joiner}{2,}`, 'g'), joiner)\n // Trim joiners from start/end\n .replace(new RegExp(`^${joiner}|${joiner}$`, 'g'), '')\n .toLowerCase()\n}\n\n/**\n * Truncates a string to a specified length and appends an ellipsis if needed.\n *\n * @param str - The input string\n * @param len - Maximum length of the result (including ellipsis)\n * @param ellipsis - String to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const subString = (\n str: string,\n len: number,\n ellipsis: string = '...'\n): string => {\n if (!str) return ''\n if (len <= ellipsis.length) return ellipsis // Avoid negative slicing\n\n return str.length > len\n ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis\n : str\n}\n\n/**\n * Replaces placeholders in a string with corresponding values from a data object.\n * \n * Example:\n * substitute(\"Hello { user.name }!\", { user: { name: \"John\" } })\n * // \"Hello John!\"\n *\n * @param str - The string containing placeholders wrapped in { } braces.\n * @param data - Object containing values to substitute. Supports nested keys via dot notation.\n * @param def - Default value to use if a key is missing. (Optional)\n * @returns The substituted string or undefined if the input string or data is invalid.\n */\nexport const substitute = (\n str: string,\n data: Record<string, unknown> = {},\n def?: string\n): string | undefined => {\n if (!str || !data) return undefined\n\n // Matches { key } or { nested.key } placeholders\n const regex = /{\\s*([a-zA-Z0-9_.]+)\\s*}/g\n\n // Flatten the data so we can directly access dot notation keys\n const flattened = dot(data)\n\n // Replace each placeholder with its value or the default\n const out = str.replace(regex, (_, key: string) => {\n const value = flattened[key]\n return value !== undefined ? String(value) : def ?? ''\n })\n\n return out\n}\n\n/**\n * Truncates a string to a specified length, removing HTML tags and \n * appending a suffix if the string exceeds the length.\n *\n * @param str - The string to truncate\n * @param len - Maximum length (default: 20)\n * @param suffix - Suffix to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (\n str: string,\n len: number = 20,\n suffix: string = '...'\n): string => {\n if (!str) return ''\n\n // Remove any HTML tags\n const clean = str.replace(/<[^>]+>/g, '')\n\n // Determine if we need to truncate\n const truncated =\n clean.length > len\n ? clean.substring(0, len - suffix.length) + suffix\n : clean\n\n // Normalize spaces and line breaks\n return truncated\n .replace(/\\n/g, ' ') // Replace all line breaks\n .replace(new RegExp(`\\\\s+${suffix.replace(/\\./g, '\\\\.')}$`), suffix) // Avoid extra space before suffix\n}\n\n"],"mappings":"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","names":["chunk","arr","size","Error","chunks","i","length","push","slice","range","startAt","Number","isFinite","Array","from","_","inspect","thing","util","showHidden","depth","colors","dd","args","forEach","console","log","process","exit","dump","abbreviate","value","locale","Intl","NumberFormat","format","si","v","s","match","find","scale","formatted","minimumFractionDigits","maximumFractionDigits","humanize","num","slugify","h","replace","toLowerCase","ones","tens","numString","toString","Error","length","end","toBytes","bytes","decimals","bits","isNaN","base","dm","sizes","index","Math","floor","log","parseFloat","pow","toFixed","toHumanTime","seconds","worded","hours","minutes","secs","parts","push","join","hh","mm","ss","dot","obj","result","recurse","o","prefix","key","value","Object","entries","newKey","Array","isArray","extractProperties","keys","fromEntries","map","getValue","item","parent","child","undefined","String","modObj","callback","safeDot","data","split","reduce","acc","k","setNested","includes","parts","current","i","length","part","slugifyKeys","only","separator","slugify","replace","RegExp","toLowerCase","filter","after","value","search","index","indexOf","slice","length","afterLast","lastIndex","lastIndexOf","before","beforeLast","capitalize","str","toUpperCase","pluralize","word","count","irregularPlurals","foot","child","mouse","goose","person","man","woman","endsWith","includes","toLowerCase","test","singularize","irregulars","feet","children","mice","geese","people","men","women","replace","slugify","joiner","RegExp","subString","len","ellipsis","substring","trimEnd","substitute","data","def","undefined","regex","flattened","dot","out","_","key","String","truncate","suffix","clean","truncated"]}
package/dist/index.d.cts CHANGED
@@ -31,6 +31,19 @@ declare const chunk: <T>(arr: T[], size?: number) => T[][];
31
31
  */
32
32
  declare const range: (size: number, startAt?: number) => number[];
33
33
 
34
+ /**
35
+ * Dump something and kill the process for quick debugging. Based on Laravel's dd()
36
+ *
37
+ * @param args
38
+ */
39
+ declare const dd: (...args: unknown[]) => never;
40
+ /**
41
+ * Dump something but keep the process for quick debugging. Based on Laravel's dump()
42
+ *
43
+ * @param args
44
+ */
45
+ declare const dump: (...args: unknown[]) => void;
46
+
34
47
  /**
35
48
  * Abbreviates large numbers using SI symbols (K, M, B...)
36
49
  * and formats the output according to the given locale.
@@ -243,4 +256,4 @@ declare const substitute: (str: string, data?: Record<string, unknown>, def?: st
243
256
  */
244
257
  declare const truncate: (str: string, len?: number, suffix?: string) => string;
245
258
 
246
- export { type CamelToSnakeCase, type KeysToSnakeCase, abbreviate, after, afterLast, before, beforeLast, capitalize, chunk, dot, extractProperties, getValue, humanize, modObj, pluralize, range, safeDot, setNested, singularize, slugify, slugifyKeys, subString, substitute, toBytes, toHumanTime, truncate };
259
+ export { type CamelToSnakeCase, type KeysToSnakeCase, abbreviate, after, afterLast, before, beforeLast, capitalize, chunk, dd, dot, dump, extractProperties, getValue, humanize, modObj, pluralize, range, safeDot, setNested, singularize, slugify, slugifyKeys, subString, substitute, toBytes, toHumanTime, truncate };
package/dist/index.d.ts CHANGED
@@ -31,6 +31,19 @@ declare const chunk: <T>(arr: T[], size?: number) => T[][];
31
31
  */
32
32
  declare const range: (size: number, startAt?: number) => number[];
33
33
 
34
+ /**
35
+ * Dump something and kill the process for quick debugging. Based on Laravel's dd()
36
+ *
37
+ * @param args
38
+ */
39
+ declare const dd: (...args: unknown[]) => never;
40
+ /**
41
+ * Dump something but keep the process for quick debugging. Based on Laravel's dump()
42
+ *
43
+ * @param args
44
+ */
45
+ declare const dump: (...args: unknown[]) => void;
46
+
34
47
  /**
35
48
  * Abbreviates large numbers using SI symbols (K, M, B...)
36
49
  * and formats the output according to the given locale.
@@ -243,4 +256,4 @@ declare const substitute: (str: string, data?: Record<string, unknown>, def?: st
243
256
  */
244
257
  declare const truncate: (str: string, len?: number, suffix?: string) => string;
245
258
 
246
- export { type CamelToSnakeCase, type KeysToSnakeCase, abbreviate, after, afterLast, before, beforeLast, capitalize, chunk, dot, extractProperties, getValue, humanize, modObj, pluralize, range, safeDot, setNested, singularize, slugify, slugifyKeys, subString, substitute, toBytes, toHumanTime, truncate };
259
+ export { type CamelToSnakeCase, type KeysToSnakeCase, abbreviate, after, afterLast, before, beforeLast, capitalize, chunk, dd, dot, dump, extractProperties, getValue, humanize, modObj, pluralize, range, safeDot, setNested, singularize, slugify, slugifyKeys, subString, substitute, toBytes, toHumanTime, truncate };
package/dist/index.js CHANGED
@@ -3,8 +3,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
3
3
 
4
4
  // src/Helpers/Arr.ts
5
5
  var chunk = /* @__PURE__ */ __name((arr, size = 2) => {
6
- if (size <= 0)
7
- throw new Error("Chunk size must be greater than 0");
6
+ if (size <= 0) throw new Error("Chunk size must be greater than 0");
8
7
  const chunks = [];
9
8
  for (let i = 0; i < arr.length; i += size) {
10
9
  chunks.push(arr.slice(i, i + size));
@@ -12,17 +11,37 @@ var chunk = /* @__PURE__ */ __name((arr, size = 2) => {
12
11
  return chunks;
13
12
  }, "chunk");
14
13
  var range = /* @__PURE__ */ __name((size, startAt = 0) => {
15
- if (size <= 0 || !Number.isFinite(size))
16
- return [];
14
+ if (size <= 0 || !Number.isFinite(size)) return [];
17
15
  return Array.from({
18
16
  length: size
19
17
  }, (_, i) => startAt + i);
20
18
  }, "range");
21
19
 
20
+ // src/Helpers/DumpDie.ts
21
+ import process from "process";
22
+ import util from "util";
23
+ var inspect = /* @__PURE__ */ __name((thing) => {
24
+ return util.inspect(thing, {
25
+ showHidden: true,
26
+ depth: null,
27
+ colors: true
28
+ });
29
+ }, "inspect");
30
+ var dd = /* @__PURE__ */ __name((...args) => {
31
+ args.forEach((thing) => {
32
+ console.log(inspect(thing));
33
+ });
34
+ process.exit(1);
35
+ }, "dd");
36
+ var dump = /* @__PURE__ */ __name((...args) => {
37
+ args.forEach((thing) => {
38
+ console.log(inspect(thing));
39
+ });
40
+ }, "dump");
41
+
22
42
  // src/Helpers/Number.ts
23
43
  var abbreviate = /* @__PURE__ */ __name((value, locale = "en-US") => {
24
- if (!value)
25
- return "0";
44
+ if (!value) return "0";
26
45
  if (value < 1e3) {
27
46
  return new Intl.NumberFormat(locale).format(value);
28
47
  }
@@ -53,8 +72,7 @@ var abbreviate = /* @__PURE__ */ __name((value, locale = "en-US") => {
53
72
  }
54
73
  ];
55
74
  const match = si.find((scale) => value >= scale.v);
56
- if (!match)
57
- return new Intl.NumberFormat(locale).format(value);
75
+ if (!match) return new Intl.NumberFormat(locale).format(value);
58
76
  const formatted = value / match.v;
59
77
  return new Intl.NumberFormat(locale, {
60
78
  minimumFractionDigits: 0,
@@ -104,10 +122,8 @@ var humanize = /* @__PURE__ */ __name((num, slugify2) => {
104
122
  "ninety"
105
123
  ];
106
124
  const numString = num.toString();
107
- if (num < 0)
108
- throw new Error("Negative numbers are not supported.");
109
- if (num === 0)
110
- return "zero";
125
+ if (num < 0) throw new Error("Negative numbers are not supported.");
126
+ if (num === 0) return "zero";
111
127
  if (num < 20) {
112
128
  return ones[num] ?? "";
113
129
  }
@@ -115,17 +131,13 @@ var humanize = /* @__PURE__ */ __name((num, slugify2) => {
115
131
  return tens[numString[0]] + " " + ones[numString[1]];
116
132
  }
117
133
  if (numString.length == 3) {
118
- if (numString[1] === "0" && numString[2] === "0")
119
- return ones[numString[0]] + " hundred";
120
- else
121
- return ones[numString[0]] + " hundred and " + humanize(+((numString[1] || "") + numString[2]), slugify2);
134
+ if (numString[1] === "0" && numString[2] === "0") return ones[numString[0]] + " hundred";
135
+ else return ones[numString[0]] + " hundred and " + humanize(+((numString[1] || "") + numString[2]), slugify2);
122
136
  }
123
137
  if (numString.length === 4) {
124
138
  const end = +((numString[1] || "") + numString[2] + numString[3]);
125
- if (end === 0)
126
- return ones[numString[0]] + " thousand";
127
- if (end < 100)
128
- return ones[numString[0]] + " thousand and " + humanize(end, slugify2);
139
+ if (end === 0) return ones[numString[0]] + " thousand";
140
+ if (end < 100) return ones[numString[0]] + " thousand and " + humanize(end, slugify2);
129
141
  return ones[numString[0]] + " thousand " + humanize(end, slugify2);
130
142
  }
131
143
  return num;
@@ -162,19 +174,15 @@ var toBytes = /* @__PURE__ */ __name((bytes, decimals = 2, bits = false) => {
162
174
  return `${value} ${sizes[index]}`;
163
175
  }, "toBytes");
164
176
  var toHumanTime = /* @__PURE__ */ __name((seconds = 0, worded = false) => {
165
- if (isNaN(seconds) || seconds < 0)
166
- seconds = 0;
177
+ if (isNaN(seconds) || seconds < 0) seconds = 0;
167
178
  const hours = Math.floor(seconds / 3600);
168
179
  const minutes = Math.floor(seconds % 3600 / 60);
169
180
  const secs = Math.floor(seconds % 60);
170
181
  if (worded) {
171
182
  const parts = [];
172
- if (hours)
173
- parts.push(`${hours}hr`);
174
- if (minutes)
175
- parts.push(`${minutes}min`);
176
- if (secs || !hours && !minutes)
177
- parts.push(`${secs}sec`);
183
+ if (hours) parts.push(`${hours}hr`);
184
+ if (minutes) parts.push(`${minutes}min`);
185
+ if (secs || !hours && !minutes) parts.push(`${secs}sec`);
178
186
  return parts.join(" ");
179
187
  }
180
188
  const hh = hours > 0 ? `${hours}:` : "";
@@ -222,8 +230,7 @@ var modObj = /* @__PURE__ */ __name((obj, callback) => {
222
230
  ])));
223
231
  }, "modObj");
224
232
  function safeDot(data, key) {
225
- if (!key)
226
- return data;
233
+ if (!key) return data;
227
234
  return key.split(".").reduce((acc, k) => acc?.[k], data);
228
235
  }
229
236
  __name(safeDot, "safeDot");
@@ -260,38 +267,32 @@ var slugifyKeys = /* @__PURE__ */ __name((obj, only = [], separator = "_") => {
260
267
 
261
268
  // src/Helpers/Str.ts
262
269
  var after = /* @__PURE__ */ __name((value, search) => {
263
- if (!search)
264
- return value;
270
+ if (!search) return value;
265
271
  const index = value.indexOf(search);
266
272
  return index !== -1 ? value.slice(index + search.length) : value;
267
273
  }, "after");
268
274
  var afterLast = /* @__PURE__ */ __name((value, search) => {
269
- if (!search)
270
- return value;
275
+ if (!search) return value;
271
276
  const lastIndex = value.lastIndexOf(search);
272
277
  return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value;
273
278
  }, "afterLast");
274
279
  var before = /* @__PURE__ */ __name((value, search) => {
275
- if (!search)
276
- return value;
280
+ if (!search) return value;
277
281
  const index = value.indexOf(search);
278
282
  return index !== -1 ? value.slice(0, index) : value;
279
283
  }, "before");
280
284
  var beforeLast = /* @__PURE__ */ __name((value, search) => {
281
- if (!search)
282
- return value;
285
+ if (!search) return value;
283
286
  const lastIndex = value.lastIndexOf(search);
284
287
  return lastIndex !== -1 ? value.slice(0, lastIndex) : value;
285
288
  }, "beforeLast");
286
289
  function capitalize(str) {
287
- if (!str)
288
- return "";
290
+ if (!str) return "";
289
291
  return str[0].toUpperCase() + str.slice(1);
290
292
  }
291
293
  __name(capitalize, "capitalize");
292
294
  var pluralize = /* @__PURE__ */ __name((word, count) => {
293
- if (count === 1)
294
- return word;
295
+ if (count === 1) return word;
295
296
  const irregularPlurals = {
296
297
  foot: "feet",
297
298
  child: "children",
@@ -328,8 +329,7 @@ var singularize = /* @__PURE__ */ __name((word) => {
328
329
  men: "man",
329
330
  women: "woman"
330
331
  };
331
- if (word in irregulars)
332
- return irregulars[word];
332
+ if (word in irregulars) return irregulars[word];
333
333
  if (/ies$/i.test(word) && word.length > 3) {
334
334
  return word.replace(/ies$/i, "y");
335
335
  }
@@ -345,15 +345,12 @@ var slugify = /* @__PURE__ */ __name((str, joiner = "_") => {
345
345
  return str.replace(/([a-z])([A-Z])/g, `$1${joiner}$2`).replace(/[\s\W]+/g, joiner).replace(new RegExp(`${joiner}{2,}`, "g"), joiner).replace(new RegExp(`^${joiner}|${joiner}$`, "g"), "").toLowerCase();
346
346
  }, "slugify");
347
347
  var subString = /* @__PURE__ */ __name((str, len, ellipsis = "...") => {
348
- if (!str)
349
- return "";
350
- if (len <= ellipsis.length)
351
- return ellipsis;
348
+ if (!str) return "";
349
+ if (len <= ellipsis.length) return ellipsis;
352
350
  return str.length > len ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis : str;
353
351
  }, "subString");
354
352
  var substitute = /* @__PURE__ */ __name((str, data = {}, def) => {
355
- if (!str || !data)
356
- return void 0;
353
+ if (!str || !data) return void 0;
357
354
  const regex = /{\s*([a-zA-Z0-9_.]+)\s*}/g;
358
355
  const flattened = dot(data);
359
356
  const out = str.replace(regex, (_, key) => {
@@ -363,8 +360,7 @@ var substitute = /* @__PURE__ */ __name((str, data = {}, def) => {
363
360
  return out;
364
361
  }, "substitute");
365
362
  var truncate = /* @__PURE__ */ __name((str, len = 20, suffix = "...") => {
366
- if (!str)
367
- return "";
363
+ if (!str) return "";
368
364
  const clean = str.replace(/<[^>]+>/g, "");
369
365
  const truncated = clean.length > len ? clean.substring(0, len - suffix.length) + suffix : clean;
370
366
  return truncated.replace(/\n/g, " ").replace(new RegExp(`\\s+${suffix.replace(/\./g, "\\.")}$`), suffix);
@@ -377,7 +373,9 @@ export {
377
373
  beforeLast,
378
374
  capitalize,
379
375
  chunk,
376
+ dd,
380
377
  dot,
378
+ dump,
381
379
  extractProperties,
382
380
  getValue,
383
381
  humanize,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/Helpers/Arr.ts","../src/Helpers/Number.ts","../src/Helpers/Obj.ts","../src/Helpers/Str.ts"],"sourcesContent":["/**\n * Splits an array into chunks of a specified size.\n *\n * @template T - Type of elements in the array\n * @param arr - The input array\n * @param size - Size of each chunk (default: 2)\n * @returns An array of chunks (arrays)\n */\nexport const chunk = <T> (arr: T[], size: number = 2): T[][] => {\n if (size <= 0) throw new Error('Chunk size must be greater than 0')\n\n const chunks: T[][] = []\n\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size))\n }\n\n return chunks\n}\n\n\n/**\n * Generates an array of sequential numbers.\n *\n * @param size - Number of elements in the range\n * @param startAt - Starting number (default: 0)\n * @returns An array of numbers from startAt to startAt + size - 1\n */\nexport const range = (size: number, startAt: number = 0): number[] => {\n if (size <= 0 || !Number.isFinite(size)) return []\n\n return Array.from({ length: size }, (_, i) => startAt + i)\n}\n","/**\n * Abbreviates large numbers using SI symbols (K, M, B...) \n * and formats the output according to the given locale.\n *\n * @param value - The number to abbreviate\n * @param locale - Optional locale string (default: \"en-US\")\n * @returns A localized, abbreviated number string\n */\nexport const abbreviate = (value?: number, locale: string = 'en-US'): string => {\n if (!value) return '0'\n\n // Numbers less than 1000 don't need abbreviation\n if (value < 1000) {\n return new Intl.NumberFormat(locale).format(value)\n }\n\n const si = [\n { v: 1e18, s: 'E' },\n { v: 1e15, s: 'P' },\n { v: 1e12, s: 'T' },\n { v: 1e9, s: 'B' },\n { v: 1e6, s: 'M' },\n { v: 1e3, s: 'K' },\n ]\n\n const match = si.find(scale => value >= scale.v)\n if (!match) return new Intl.NumberFormat(locale).format(value)\n\n const formatted = value / match.v\n\n return (\n new Intl.NumberFormat(locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(formatted) + match.s\n )\n}\n\n/**\n * Concverts a number into human readable string\n *\n * @param num The number to convert\n * @param slugify convert the ouput into a slug using this as a separator\n * @returns\n */\nexport const humanize = (num: number, slugify?: '-' | '_'): string => {\n if (!num) {\n return ''\n }\n\n if (slugify === '-' || slugify === '_') {\n const h = humanize(num)\n return typeof h === 'string' ? h.replace(' ', slugify).toLowerCase() : h\n }\n\n const ones = [\n '',\n 'one',\n 'two',\n 'three',\n 'four',\n 'five',\n 'six',\n 'seven',\n 'eight',\n 'nine',\n 'ten',\n 'eleven',\n 'twelve',\n 'thirteen',\n 'fourteen',\n 'fifteen',\n 'sixteen',\n 'seventeen',\n 'eighteen',\n 'nineteen',\n ]\n const tens = [\n '',\n '',\n 'twenty',\n 'thirty',\n 'forty',\n 'fifty',\n 'sixty',\n 'seventy',\n 'eighty',\n 'ninety',\n ]\n\n const numString: string = num.toString()\n\n if (num < 0) throw new Error('Negative numbers are not supported.')\n\n if (num === 0) return 'zero'\n\n //the case of 1 - 20\n if (num < 20) {\n return ones[num] ?? ''\n }\n\n if (numString.length === 2) {\n return tens[numString[0] as unknown as number] + ' ' + ones[numString[1] as unknown as number]\n }\n\n //100 and more\n if (numString.length == 3) {\n if (numString[1] === '0' && numString[2] === '0')\n return ones[numString[0] as unknown as number] + ' hundred'\n else\n return (\n ones[numString[0] as unknown as number] +\n ' hundred and ' +\n humanize(+((numString[1] || '') + numString[2]), slugify)\n )\n }\n\n if (numString.length === 4) {\n const end = +((numString[1] || '') + numString[2] + numString[3])\n if (end === 0) return ones[numString[0] as unknown as number] + ' thousand'\n if (end < 100)\n return ones[numString[0] as unknown as number] + ' thousand and ' + humanize(end, slugify)\n return ones[numString[0] as unknown as number] + ' thousand ' + humanize(end, slugify)\n }\n\n return num as unknown as string\n}\n\n/**\n * Converts a number of bytes into a human-readable string.\n *\n * @param bytes - The size in bytes to convert\n * @param decimals - Number of decimal places to display (default: 2)\n * @param bits - If true, uses 1000-based (SI) units (B, KB, MB...); \n * otherwise uses 1024-based binary units (Bytes, KiB...)\n * @returns A formatted string with the appropriate unit\n */\nexport const toBytes = (\n bytes?: number,\n decimals: number = 2,\n bits: boolean = false\n): string => {\n if (!bytes || isNaN(bytes)) {\n return bits ? '0 B' : '0 Bytes'\n }\n\n const base = bits ? 1000 : 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = bits\n ? ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] // SI units\n : ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] // Binary units\n\n const index = Math.floor(Math.log(bytes) / Math.log(base))\n\n const value = parseFloat((bytes / Math.pow(base, index)).toFixed(dm))\n return `${value} ${sizes[index]}`\n}\n\n/**\n * Formats a duration (in seconds) into a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @param worded - If true, outputs worded format (e.g., \"1hr 2min 3sec\"),\n * otherwise HH:MM:SS (e.g., \"01:02:03\")\n * @returns A formatted time string\n */\nexport const toHumanTime = (\n seconds: number = 0,\n worded: boolean = false\n): string => {\n // Ensure seconds is a number and not negative\n if (isNaN(seconds) || seconds < 0) seconds = 0\n\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const secs = Math.floor(seconds % 60)\n\n // Worded format → \"1hr 2min 3sec\"\n if (worded) {\n const parts = []\n if (hours) parts.push(`${hours}hr`)\n if (minutes) parts.push(`${minutes}min`)\n if (secs || (!hours && !minutes)) parts.push(`${secs}sec`)\n return parts.join(' ')\n }\n\n // HH:MM:SS format → zero-padded\n const hh = hours > 0 ? `${hours}:` : ''\n const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ':'\n const ss = secs < 10 ? `0${secs}` : secs\n\n return `${hh}${mm}${ss}`\n}\n\n","import { DotFlatten, DotNestedKeys, DotNestedValue, KeysToSnakeCase } from '../Contracts/ObjContract'\n\n/**\n * Flattens a nested object into a single-level object\n * with dot-separated keys.\n *\n * Example:\n * doter({\n * user: { name: \"John\", address: { city: \"NY\" } },\n * active: true\n * })\n * \n * Output:\n * {\n * \"user.name\": \"John\",\n * \"user.address.city\": \"NY\",\n * \"active\": true\n * }\n *\n * @template T - The type of the input object\n * @param obj - The nested object to flatten\n * @returns A flattened object with dotted keys and inferred types\n */\nexport const dot = <T extends Record<string, any>> (obj: T): DotFlatten<T> => {\n const result = {} as Record<string, unknown>\n\n /**\n * Internal recursive function to traverse and flatten the object.\n * \n * @param o - Current object to flatten\n * @param prefix - Key path accumulated so far\n */\n const recurse = (o: Record<string, any>, prefix = ''): void => {\n for (const [key, value] of Object.entries(o)) {\n const newKey = prefix ? `${prefix}.${key}` : key\n\n /**\n * Recurse if the value is a plain object\n */\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n recurse(value, newKey)\n } else {\n /**\n * Otherwise, assign directly\n */\n result[newKey] = value\n }\n }\n }\n\n recurse(obj)\n return result as DotFlatten<T>\n}\n\n/**\n * Extracts a subset of properties from an object.\n *\n * @template T - Type of the source object\n * @template K - Keys of T to extract\n * @param obj - The source object\n * @param keys - Array of keys to extract\n * @returns A new object with only the specified keys\n */\nexport const extractProperties = <T extends object, K extends keyof T> (\n obj: T,\n keys: readonly K[] = []\n): Pick<T, K> => {\n return Object.fromEntries(\n keys.map(key => [key, obj[key]])\n ) as Pick<T, K>\n}\n\n/**\n * Safely retrieves a value from an object by key or nested keys.\n *\n * @template T - Type of the source object\n * @param key - Single key or tuple [parentKey, childKey]\n * @param item - The source object\n * @returns The found value as a string or the key itself if not found\n */\nexport const getValue = <\n T extends Record<string, any> // Allow nested objects\n> (\n key: string | [keyof T, keyof T[string]],\n item: T\n): string => {\n if (Array.isArray(key)) {\n const [parent, child] = key\n\n if (child !== undefined) {\n // Access nested property: item[parent][child]\n return (\n String(item?.[parent]?.[child] ??\n item?.[parent] ??\n `${String(parent)}.${String(child)}`)\n )\n }\n\n // Only top-level key\n return String(item?.[parent] ?? parent)\n }\n\n // Single key access\n return String(item?.[key] ?? key)\n}\n\n/**\n * Maps over an object's entries and returns a new object \n * with transformed keys and/or values.\n *\n * @template T - Type of the input object\n * @template R - Type of the new values\n * @param obj - The object to transform\n * @param callback - Function that receives [key, value] and returns [newKey, newValue]\n * @returns A new object with transformed entries\n */\nexport const modObj = <T extends object, R> (\n obj: T,\n callback: (_entry: [keyof T & string, T[keyof T]]) => [string, R]\n): Record<string, R> => {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) =>\n callback([key as keyof T & string, value as T[keyof T]])\n )\n ) as Record<string, R>\n}\n\n\nexport function safeDot<T extends Record<string, any>> (_data: T): T\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (_data: T, _key?: K): DotNestedValue<T, K>\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (data: T, key?: K): any {\n if (!key) return data\n return key.split('.').reduce((acc: any, k) => acc?.[k], data)\n}\n\n/**\n * Sets a nested property on an object using dot notation.\n * \n * @example\n * const obj = {}\n * setNested(obj, 'app.user.name', 'Legacy')\n * console.log(obj)\n * // Output: { app: { user: { name: 'Legacy' } } }\n * \n * @param obj - The target object to modify.\n * @param key - The dot-separated key (e.g., 'app.user.name').\n * @param value - The value to set at the specified path.\n */\nexport const setNested = (\n obj: Record<string, any>,\n key: string,\n value: any\n): void => {\n if (!key.includes('.')) {\n obj[key] = value\n return\n }\n\n const parts = key.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n\n /**\n * If we're at the last key, assign the value\n */\n if (i === parts.length - 1) {\n current[part] = value\n } else {\n /**\n * If the key doesn't exist or isn't an object, create it\n */\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part]\n }\n }\n}\n\n/**\n * Converts object keys to a slugified format (e.g., snake_case).\n *\n * @template T - Type of the input object\n * @param obj - The object whose keys will be slugified\n * @param only - Optional array of keys to slugify (others remain unchanged)\n * @param separator - Separator for slugified keys (default: \"_\")\n * @returns A new object with slugified keys\n */\nexport const slugifyKeys = <T extends object> (\n obj: T,\n only: string[] = [],\n separator: string = '_'\n): KeysToSnakeCase<T> => {\n const slugify = (key: string): string =>\n key\n .replace(/([a-z])([A-Z])/g, `$1${separator}$2`) // Handle camelCase\n .replace(/[\\s\\W]+/g, separator) // Replace spaces/symbols\n .replace(new RegExp(`${separator}{2,}`, 'g'), separator) // Remove duplicate separators\n .replace(new RegExp(`^${separator}|${separator}$`, 'g'), '') // Trim edges\n .toLowerCase()\n\n let entries = Object.entries(obj)\n\n // Filter if `only` is provided\n if (only.length) {\n entries = entries.filter(([key]) => only.includes(key))\n }\n\n return Object.fromEntries(\n entries.map(([key, value]) => [slugify(key), value])\n ) as KeysToSnakeCase<T>\n}\n","import { dot } from './Obj'\n\n/**\n * Get the portion of the string after the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const after = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(index + search.length) : value\n}\n\n/**\n * Get the portion of the string after the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const afterLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value\n}\n\n/**\n * Get the portion of the string before the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const before = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(0, index) : value\n}\n\n/**\n * Get the portion of the string before the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const beforeLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(0, lastIndex) : value\n}\n\n/**\n * Capitalizes the first character of a string.\n *\n * @param str - The input string\n * @returns The string with the first character capitalized\n */\nexport function capitalize (str: string): string {\n if (!str) return '' // Handle empty or undefined strings safely\n return str[0].toUpperCase() + str.slice(1)\n}\n\n\n/**\n * Returns the pluralized form of a word based on the given number.\n *\n * @param word - The word to pluralize\n * @param count - The number determining pluralization\n * @returns Singular if count === 1, otherwise plural form\n */\nexport const pluralize = (word: string, count: number): string => {\n // If count is exactly 1 → singular\n if (count === 1) return word\n\n // Irregular plurals map\n const irregularPlurals: Record<string, string> = {\n foot: 'feet',\n child: 'children',\n mouse: 'mice',\n goose: 'geese',\n person: 'people',\n man: 'men',\n woman: 'women',\n }\n\n // Handle irregular cases first\n if (word in irregularPlurals) {\n return irregularPlurals[word]\n }\n\n // If word ends with consonant + \"y\" → replace \"y\" with \"ies\"\n if (\n word.endsWith('y') &&\n !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2]?.toLowerCase() ?? '')\n ) {\n return word.slice(0, -1) + 'ies'\n }\n\n // If word ends in \"s\", \"ss\", \"sh\", \"ch\", \"x\", or \"z\" → add \"es\"\n if (/(s|ss|sh|ch|x|z)$/i.test(word)) {\n return word + 'es'\n }\n\n // Default: just add \"s\"\n return word + 's'\n}\n\n/**\n * Converts a plural English word into its singular form.\n *\n * @param word - The word to singularize\n * @returns The singular form of the word\n */\nexport const singularize = (word: string): string => {\n // Irregular plurals map (reverse of pluralize)\n const irregulars: Record<string, string> = {\n feet: 'foot',\n children: 'child',\n mice: 'mouse',\n geese: 'goose',\n people: 'person',\n men: 'man',\n women: 'woman',\n }\n\n // Handle irregular cases\n if (word in irregulars) return irregulars[word]\n\n // Words ending in \"ies\" → change to \"y\" (e.g., \"bodies\" → \"body\")\n if (/ies$/i.test(word) && word.length > 3) {\n return word.replace(/ies$/i, 'y')\n }\n\n // Words ending in \"es\" after certain consonants → remove \"es\"\n if (/(ches|shes|sses|xes|zes)$/i.test(word)) {\n return word.replace(/es$/i, '')\n }\n\n // Generic case: remove trailing \"s\"\n if (/s$/i.test(word) && word.length > 1) {\n return word.replace(/s$/i, '')\n }\n\n return word\n}\n\n/**\n * Converts a string into a slug format.\n * Handles camelCase, spaces, and non-alphanumeric characters.\n *\n * @param str - The input string to slugify\n * @param joiner - The character used to join words (default: \"_\")\n * @returns A slugified string\n */\nexport const slugify = (str: string, joiner = '_'): string => {\n return str\n // Handle camelCase by adding joiner between lowercase → uppercase\n .replace(/([a-z])([A-Z])/g, `$1${joiner}$2`)\n // Replace spaces and non-alphanumeric characters with joiner\n .replace(/[\\s\\W]+/g, joiner)\n // Remove duplicate joiners\n .replace(new RegExp(`${joiner}{2,}`, 'g'), joiner)\n // Trim joiners from start/end\n .replace(new RegExp(`^${joiner}|${joiner}$`, 'g'), '')\n .toLowerCase()\n}\n\n/**\n * Truncates a string to a specified length and appends an ellipsis if needed.\n *\n * @param str - The input string\n * @param len - Maximum length of the result (including ellipsis)\n * @param ellipsis - String to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const subString = (\n str: string,\n len: number,\n ellipsis: string = '...'\n): string => {\n if (!str) return ''\n if (len <= ellipsis.length) return ellipsis // Avoid negative slicing\n\n return str.length > len\n ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis\n : str\n}\n\n/**\n * Replaces placeholders in a string with corresponding values from a data object.\n * \n * Example:\n * substitute(\"Hello { user.name }!\", { user: { name: \"John\" } })\n * // \"Hello John!\"\n *\n * @param str - The string containing placeholders wrapped in { } braces.\n * @param data - Object containing values to substitute. Supports nested keys via dot notation.\n * @param def - Default value to use if a key is missing. (Optional)\n * @returns The substituted string or undefined if the input string or data is invalid.\n */\nexport const substitute = (\n str: string,\n data: Record<string, unknown> = {},\n def?: string\n): string | undefined => {\n if (!str || !data) return undefined\n\n // Matches { key } or { nested.key } placeholders\n const regex = /{\\s*([a-zA-Z0-9_.]+)\\s*}/g\n\n // Flatten the data so we can directly access dot notation keys\n const flattened = dot(data)\n\n // Replace each placeholder with its value or the default\n const out = str.replace(regex, (_, key: string) => {\n const value = flattened[key]\n return value !== undefined ? String(value) : def ?? ''\n })\n\n return out\n}\n\n/**\n * Truncates a string to a specified length, removing HTML tags and \n * appending a suffix if the string exceeds the length.\n *\n * @param str - The string to truncate\n * @param len - Maximum length (default: 20)\n * @param suffix - Suffix to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (\n str: string,\n len: number = 20,\n suffix: string = '...'\n): string => {\n if (!str) return ''\n\n // Remove any HTML tags\n const clean = str.replace(/<[^>]+>/g, '')\n\n // Determine if we need to truncate\n const truncated =\n clean.length > len\n ? clean.substring(0, len - suffix.length) + suffix\n : clean\n\n // Normalize spaces and line breaks\n return truncated\n .replace(/\\n/g, ' ') // Replace all line breaks\n .replace(new RegExp(`\\\\s+${suffix.replace(/\\./g, '\\\\.')}$`), suffix) // Avoid extra space before suffix\n}\n\n"],"mappings":"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","names":["chunk","arr","size","Error","chunks","i","length","push","slice","range","startAt","Number","isFinite","Array","from","_","abbreviate","value","locale","Intl","NumberFormat","format","si","v","s","match","find","scale","formatted","minimumFractionDigits","maximumFractionDigits","humanize","num","slugify","h","replace","toLowerCase","ones","tens","numString","toString","Error","length","end","toBytes","bytes","decimals","bits","isNaN","base","dm","sizes","index","Math","floor","log","parseFloat","pow","toFixed","toHumanTime","seconds","worded","hours","minutes","secs","parts","push","join","hh","mm","ss","dot","obj","result","recurse","o","prefix","key","value","Object","entries","newKey","Array","isArray","extractProperties","keys","fromEntries","map","getValue","item","parent","child","undefined","String","modObj","callback","safeDot","data","split","reduce","acc","k","setNested","includes","parts","current","i","length","part","slugifyKeys","only","separator","slugify","replace","RegExp","toLowerCase","filter","after","value","search","index","indexOf","slice","length","afterLast","lastIndex","lastIndexOf","before","beforeLast","capitalize","str","toUpperCase","pluralize","word","count","irregularPlurals","foot","child","mouse","goose","person","man","woman","endsWith","includes","toLowerCase","test","singularize","irregulars","feet","children","mice","geese","people","men","women","replace","slugify","joiner","RegExp","subString","len","ellipsis","substring","trimEnd","substitute","data","def","undefined","regex","flattened","dot","out","_","key","String","truncate","suffix","clean","truncated"]}
1
+ {"version":3,"sources":["../src/Helpers/Arr.ts","../src/Helpers/DumpDie.ts","../src/Helpers/Number.ts","../src/Helpers/Obj.ts","../src/Helpers/Str.ts"],"sourcesContent":["/**\n * Splits an array into chunks of a specified size.\n *\n * @template T - Type of elements in the array\n * @param arr - The input array\n * @param size - Size of each chunk (default: 2)\n * @returns An array of chunks (arrays)\n */\nexport const chunk = <T> (arr: T[], size: number = 2): T[][] => {\n if (size <= 0) throw new Error('Chunk size must be greater than 0')\n\n const chunks: T[][] = []\n\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size))\n }\n\n return chunks\n}\n\n\n/**\n * Generates an array of sequential numbers.\n *\n * @param size - Number of elements in the range\n * @param startAt - Starting number (default: 0)\n * @returns An array of numbers from startAt to startAt + size - 1\n */\nexport const range = (size: number, startAt: number = 0): number[] => {\n if (size <= 0 || !Number.isFinite(size)) return []\n\n return Array.from({ length: size }, (_, i) => startAt + i)\n}\n","import process from 'process'\nimport util from 'util'\n\nconst inspect = (thing: any) => {\n return util.inspect(thing, {\n showHidden: true,\n depth: null,\n colors: true\n })\n}\n\n/**\n * Dump something and kill the process for quick debugging. Based on Laravel's dd()\n * \n * @param args \n */\nexport const dd = (...args: unknown[]): never => {\n args.forEach((thing) => {\n console.log(inspect(thing))\n })\n\n process.exit(1)\n}\n\n/**\n * Dump something but keep the process for quick debugging. Based on Laravel's dump()\n * \n * @param args \n */\nexport const dump = (...args: unknown[]): void => {\n args.forEach((thing) => {\n console.log(inspect(thing))\n })\n} \n","/**\n * Abbreviates large numbers using SI symbols (K, M, B...) \n * and formats the output according to the given locale.\n *\n * @param value - The number to abbreviate\n * @param locale - Optional locale string (default: \"en-US\")\n * @returns A localized, abbreviated number string\n */\nexport const abbreviate = (value?: number, locale: string = 'en-US'): string => {\n if (!value) return '0'\n\n // Numbers less than 1000 don't need abbreviation\n if (value < 1000) {\n return new Intl.NumberFormat(locale).format(value)\n }\n\n const si = [\n { v: 1e18, s: 'E' },\n { v: 1e15, s: 'P' },\n { v: 1e12, s: 'T' },\n { v: 1e9, s: 'B' },\n { v: 1e6, s: 'M' },\n { v: 1e3, s: 'K' },\n ]\n\n const match = si.find(scale => value >= scale.v)\n if (!match) return new Intl.NumberFormat(locale).format(value)\n\n const formatted = value / match.v\n\n return (\n new Intl.NumberFormat(locale, {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(formatted) + match.s\n )\n}\n\n/**\n * Concverts a number into human readable string\n *\n * @param num The number to convert\n * @param slugify convert the ouput into a slug using this as a separator\n * @returns\n */\nexport const humanize = (num: number, slugify?: '-' | '_'): string => {\n if (!num) {\n return ''\n }\n\n if (slugify === '-' || slugify === '_') {\n const h = humanize(num)\n return typeof h === 'string' ? h.replace(' ', slugify).toLowerCase() : h\n }\n\n const ones = [\n '',\n 'one',\n 'two',\n 'three',\n 'four',\n 'five',\n 'six',\n 'seven',\n 'eight',\n 'nine',\n 'ten',\n 'eleven',\n 'twelve',\n 'thirteen',\n 'fourteen',\n 'fifteen',\n 'sixteen',\n 'seventeen',\n 'eighteen',\n 'nineteen',\n ]\n const tens = [\n '',\n '',\n 'twenty',\n 'thirty',\n 'forty',\n 'fifty',\n 'sixty',\n 'seventy',\n 'eighty',\n 'ninety',\n ]\n\n const numString: string = num.toString()\n\n if (num < 0) throw new Error('Negative numbers are not supported.')\n\n if (num === 0) return 'zero'\n\n //the case of 1 - 20\n if (num < 20) {\n return ones[num] ?? ''\n }\n\n if (numString.length === 2) {\n return tens[numString[0] as unknown as number] + ' ' + ones[numString[1] as unknown as number]\n }\n\n //100 and more\n if (numString.length == 3) {\n if (numString[1] === '0' && numString[2] === '0')\n return ones[numString[0] as unknown as number] + ' hundred'\n else\n return (\n ones[numString[0] as unknown as number] +\n ' hundred and ' +\n humanize(+((numString[1] || '') + numString[2]), slugify)\n )\n }\n\n if (numString.length === 4) {\n const end = +((numString[1] || '') + numString[2] + numString[3])\n if (end === 0) return ones[numString[0] as unknown as number] + ' thousand'\n if (end < 100)\n return ones[numString[0] as unknown as number] + ' thousand and ' + humanize(end, slugify)\n return ones[numString[0] as unknown as number] + ' thousand ' + humanize(end, slugify)\n }\n\n return num as unknown as string\n}\n\n/**\n * Converts a number of bytes into a human-readable string.\n *\n * @param bytes - The size in bytes to convert\n * @param decimals - Number of decimal places to display (default: 2)\n * @param bits - If true, uses 1000-based (SI) units (B, KB, MB...); \n * otherwise uses 1024-based binary units (Bytes, KiB...)\n * @returns A formatted string with the appropriate unit\n */\nexport const toBytes = (\n bytes?: number,\n decimals: number = 2,\n bits: boolean = false\n): string => {\n if (!bytes || isNaN(bytes)) {\n return bits ? '0 B' : '0 Bytes'\n }\n\n const base = bits ? 1000 : 1024\n const dm = decimals < 0 ? 0 : decimals\n const sizes = bits\n ? ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] // SI units\n : ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] // Binary units\n\n const index = Math.floor(Math.log(bytes) / Math.log(base))\n\n const value = parseFloat((bytes / Math.pow(base, index)).toFixed(dm))\n return `${value} ${sizes[index]}`\n}\n\n/**\n * Formats a duration (in seconds) into a human-readable string.\n *\n * @param seconds - Duration in seconds\n * @param worded - If true, outputs worded format (e.g., \"1hr 2min 3sec\"),\n * otherwise HH:MM:SS (e.g., \"01:02:03\")\n * @returns A formatted time string\n */\nexport const toHumanTime = (\n seconds: number = 0,\n worded: boolean = false\n): string => {\n // Ensure seconds is a number and not negative\n if (isNaN(seconds) || seconds < 0) seconds = 0\n\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const secs = Math.floor(seconds % 60)\n\n // Worded format → \"1hr 2min 3sec\"\n if (worded) {\n const parts = []\n if (hours) parts.push(`${hours}hr`)\n if (minutes) parts.push(`${minutes}min`)\n if (secs || (!hours && !minutes)) parts.push(`${secs}sec`)\n return parts.join(' ')\n }\n\n // HH:MM:SS format → zero-padded\n const hh = hours > 0 ? `${hours}:` : ''\n const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ':'\n const ss = secs < 10 ? `0${secs}` : secs\n\n return `${hh}${mm}${ss}`\n}\n\n","import { DotFlatten, DotNestedKeys, DotNestedValue, KeysToSnakeCase } from '../Contracts/ObjContract'\n\n/**\n * Flattens a nested object into a single-level object\n * with dot-separated keys.\n *\n * Example:\n * doter({\n * user: { name: \"John\", address: { city: \"NY\" } },\n * active: true\n * })\n * \n * Output:\n * {\n * \"user.name\": \"John\",\n * \"user.address.city\": \"NY\",\n * \"active\": true\n * }\n *\n * @template T - The type of the input object\n * @param obj - The nested object to flatten\n * @returns A flattened object with dotted keys and inferred types\n */\nexport const dot = <T extends Record<string, any>> (obj: T): DotFlatten<T> => {\n const result = {} as Record<string, unknown>\n\n /**\n * Internal recursive function to traverse and flatten the object.\n * \n * @param o - Current object to flatten\n * @param prefix - Key path accumulated so far\n */\n const recurse = (o: Record<string, any>, prefix = ''): void => {\n for (const [key, value] of Object.entries(o)) {\n const newKey = prefix ? `${prefix}.${key}` : key\n\n /**\n * Recurse if the value is a plain object\n */\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n recurse(value, newKey)\n } else {\n /**\n * Otherwise, assign directly\n */\n result[newKey] = value\n }\n }\n }\n\n recurse(obj)\n return result as DotFlatten<T>\n}\n\n/**\n * Extracts a subset of properties from an object.\n *\n * @template T - Type of the source object\n * @template K - Keys of T to extract\n * @param obj - The source object\n * @param keys - Array of keys to extract\n * @returns A new object with only the specified keys\n */\nexport const extractProperties = <T extends object, K extends keyof T> (\n obj: T,\n keys: readonly K[] = []\n): Pick<T, K> => {\n return Object.fromEntries(\n keys.map(key => [key, obj[key]])\n ) as Pick<T, K>\n}\n\n/**\n * Safely retrieves a value from an object by key or nested keys.\n *\n * @template T - Type of the source object\n * @param key - Single key or tuple [parentKey, childKey]\n * @param item - The source object\n * @returns The found value as a string or the key itself if not found\n */\nexport const getValue = <\n T extends Record<string, any> // Allow nested objects\n> (\n key: string | [keyof T, keyof T[string]],\n item: T\n): string => {\n if (Array.isArray(key)) {\n const [parent, child] = key\n\n if (child !== undefined) {\n // Access nested property: item[parent][child]\n return (\n String(item?.[parent]?.[child] ??\n item?.[parent] ??\n `${String(parent)}.${String(child)}`)\n )\n }\n\n // Only top-level key\n return String(item?.[parent] ?? parent)\n }\n\n // Single key access\n return String(item?.[key] ?? key)\n}\n\n/**\n * Maps over an object's entries and returns a new object \n * with transformed keys and/or values.\n *\n * @template T - Type of the input object\n * @template R - Type of the new values\n * @param obj - The object to transform\n * @param callback - Function that receives [key, value] and returns [newKey, newValue]\n * @returns A new object with transformed entries\n */\nexport const modObj = <T extends object, R> (\n obj: T,\n callback: (_entry: [keyof T & string, T[keyof T]]) => [string, R]\n): Record<string, R> => {\n return Object.fromEntries(\n Object.entries(obj).map(([key, value]) =>\n callback([key as keyof T & string, value as T[keyof T]])\n )\n ) as Record<string, R>\n}\n\n\nexport function safeDot<T extends Record<string, any>> (_data: T): T\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (_data: T, _key?: K): DotNestedValue<T, K>\nexport function safeDot<\n T extends Record<string, any>,\n K extends DotNestedKeys<T>\n> (data: T, key?: K): any {\n if (!key) return data\n return key.split('.').reduce((acc: any, k) => acc?.[k], data)\n}\n\n/**\n * Sets a nested property on an object using dot notation.\n * \n * @example\n * const obj = {}\n * setNested(obj, 'app.user.name', 'Legacy')\n * console.log(obj)\n * // Output: { app: { user: { name: 'Legacy' } } }\n * \n * @param obj - The target object to modify.\n * @param key - The dot-separated key (e.g., 'app.user.name').\n * @param value - The value to set at the specified path.\n */\nexport const setNested = (\n obj: Record<string, any>,\n key: string,\n value: any\n): void => {\n if (!key.includes('.')) {\n obj[key] = value\n return\n }\n\n const parts = key.split('.')\n let current = obj\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i]\n\n /**\n * If we're at the last key, assign the value\n */\n if (i === parts.length - 1) {\n current[part] = value\n } else {\n /**\n * If the key doesn't exist or isn't an object, create it\n */\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {}\n }\n current = current[part]\n }\n }\n}\n\n/**\n * Converts object keys to a slugified format (e.g., snake_case).\n *\n * @template T - Type of the input object\n * @param obj - The object whose keys will be slugified\n * @param only - Optional array of keys to slugify (others remain unchanged)\n * @param separator - Separator for slugified keys (default: \"_\")\n * @returns A new object with slugified keys\n */\nexport const slugifyKeys = <T extends object> (\n obj: T,\n only: string[] = [],\n separator: string = '_'\n): KeysToSnakeCase<T> => {\n const slugify = (key: string): string =>\n key\n .replace(/([a-z])([A-Z])/g, `$1${separator}$2`) // Handle camelCase\n .replace(/[\\s\\W]+/g, separator) // Replace spaces/symbols\n .replace(new RegExp(`${separator}{2,}`, 'g'), separator) // Remove duplicate separators\n .replace(new RegExp(`^${separator}|${separator}$`, 'g'), '') // Trim edges\n .toLowerCase()\n\n let entries = Object.entries(obj)\n\n // Filter if `only` is provided\n if (only.length) {\n entries = entries.filter(([key]) => only.includes(key))\n }\n\n return Object.fromEntries(\n entries.map(([key, value]) => [slugify(key), value])\n ) as KeysToSnakeCase<T>\n}\n","import { dot } from './Obj'\n\n/**\n * Get the portion of the string after the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const after = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(index + search.length) : value\n}\n\n/**\n * Get the portion of the string after the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const afterLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value\n}\n\n/**\n * Get the portion of the string before the first occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const before = (value: string, search: string): string => {\n if (!search) return value\n const index = value.indexOf(search)\n return index !== -1 ? value.slice(0, index) : value\n}\n\n/**\n * Get the portion of the string before the last occurrence of the given value.\n * \n * @param value \n * @param search \n * @returns \n */\nexport const beforeLast = (value: string, search: string): string => {\n if (!search) return value\n const lastIndex = value.lastIndexOf(search)\n return lastIndex !== -1 ? value.slice(0, lastIndex) : value\n}\n\n/**\n * Capitalizes the first character of a string.\n *\n * @param str - The input string\n * @returns The string with the first character capitalized\n */\nexport function capitalize (str: string): string {\n if (!str) return '' // Handle empty or undefined strings safely\n return str[0].toUpperCase() + str.slice(1)\n}\n\n\n/**\n * Returns the pluralized form of a word based on the given number.\n *\n * @param word - The word to pluralize\n * @param count - The number determining pluralization\n * @returns Singular if count === 1, otherwise plural form\n */\nexport const pluralize = (word: string, count: number): string => {\n // If count is exactly 1 → singular\n if (count === 1) return word\n\n // Irregular plurals map\n const irregularPlurals: Record<string, string> = {\n foot: 'feet',\n child: 'children',\n mouse: 'mice',\n goose: 'geese',\n person: 'people',\n man: 'men',\n woman: 'women',\n }\n\n // Handle irregular cases first\n if (word in irregularPlurals) {\n return irregularPlurals[word]\n }\n\n // If word ends with consonant + \"y\" → replace \"y\" with \"ies\"\n if (\n word.endsWith('y') &&\n !['a', 'e', 'i', 'o', 'u'].includes(word[word.length - 2]?.toLowerCase() ?? '')\n ) {\n return word.slice(0, -1) + 'ies'\n }\n\n // If word ends in \"s\", \"ss\", \"sh\", \"ch\", \"x\", or \"z\" → add \"es\"\n if (/(s|ss|sh|ch|x|z)$/i.test(word)) {\n return word + 'es'\n }\n\n // Default: just add \"s\"\n return word + 's'\n}\n\n/**\n * Converts a plural English word into its singular form.\n *\n * @param word - The word to singularize\n * @returns The singular form of the word\n */\nexport const singularize = (word: string): string => {\n // Irregular plurals map (reverse of pluralize)\n const irregulars: Record<string, string> = {\n feet: 'foot',\n children: 'child',\n mice: 'mouse',\n geese: 'goose',\n people: 'person',\n men: 'man',\n women: 'woman',\n }\n\n // Handle irregular cases\n if (word in irregulars) return irregulars[word]\n\n // Words ending in \"ies\" → change to \"y\" (e.g., \"bodies\" → \"body\")\n if (/ies$/i.test(word) && word.length > 3) {\n return word.replace(/ies$/i, 'y')\n }\n\n // Words ending in \"es\" after certain consonants → remove \"es\"\n if (/(ches|shes|sses|xes|zes)$/i.test(word)) {\n return word.replace(/es$/i, '')\n }\n\n // Generic case: remove trailing \"s\"\n if (/s$/i.test(word) && word.length > 1) {\n return word.replace(/s$/i, '')\n }\n\n return word\n}\n\n/**\n * Converts a string into a slug format.\n * Handles camelCase, spaces, and non-alphanumeric characters.\n *\n * @param str - The input string to slugify\n * @param joiner - The character used to join words (default: \"_\")\n * @returns A slugified string\n */\nexport const slugify = (str: string, joiner = '_'): string => {\n return str\n // Handle camelCase by adding joiner between lowercase → uppercase\n .replace(/([a-z])([A-Z])/g, `$1${joiner}$2`)\n // Replace spaces and non-alphanumeric characters with joiner\n .replace(/[\\s\\W]+/g, joiner)\n // Remove duplicate joiners\n .replace(new RegExp(`${joiner}{2,}`, 'g'), joiner)\n // Trim joiners from start/end\n .replace(new RegExp(`^${joiner}|${joiner}$`, 'g'), '')\n .toLowerCase()\n}\n\n/**\n * Truncates a string to a specified length and appends an ellipsis if needed.\n *\n * @param str - The input string\n * @param len - Maximum length of the result (including ellipsis)\n * @param ellipsis - String to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const subString = (\n str: string,\n len: number,\n ellipsis: string = '...'\n): string => {\n if (!str) return ''\n if (len <= ellipsis.length) return ellipsis // Avoid negative slicing\n\n return str.length > len\n ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis\n : str\n}\n\n/**\n * Replaces placeholders in a string with corresponding values from a data object.\n * \n * Example:\n * substitute(\"Hello { user.name }!\", { user: { name: \"John\" } })\n * // \"Hello John!\"\n *\n * @param str - The string containing placeholders wrapped in { } braces.\n * @param data - Object containing values to substitute. Supports nested keys via dot notation.\n * @param def - Default value to use if a key is missing. (Optional)\n * @returns The substituted string or undefined if the input string or data is invalid.\n */\nexport const substitute = (\n str: string,\n data: Record<string, unknown> = {},\n def?: string\n): string | undefined => {\n if (!str || !data) return undefined\n\n // Matches { key } or { nested.key } placeholders\n const regex = /{\\s*([a-zA-Z0-9_.]+)\\s*}/g\n\n // Flatten the data so we can directly access dot notation keys\n const flattened = dot(data)\n\n // Replace each placeholder with its value or the default\n const out = str.replace(regex, (_, key: string) => {\n const value = flattened[key]\n return value !== undefined ? String(value) : def ?? ''\n })\n\n return out\n}\n\n/**\n * Truncates a string to a specified length, removing HTML tags and \n * appending a suffix if the string exceeds the length.\n *\n * @param str - The string to truncate\n * @param len - Maximum length (default: 20)\n * @param suffix - Suffix to append if truncated (default: \"...\")\n * @returns The truncated string\n */\nexport const truncate = (\n str: string,\n len: number = 20,\n suffix: string = '...'\n): string => {\n if (!str) return ''\n\n // Remove any HTML tags\n const clean = str.replace(/<[^>]+>/g, '')\n\n // Determine if we need to truncate\n const truncated =\n clean.length > len\n ? clean.substring(0, len - suffix.length) + suffix\n : clean\n\n // Normalize spaces and line breaks\n return truncated\n .replace(/\\n/g, ' ') // Replace all line breaks\n .replace(new RegExp(`\\\\s+${suffix.replace(/\\./g, '\\\\.')}$`), suffix) // Avoid extra space before suffix\n}\n\n"],"mappings":"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","names":["chunk","arr","size","Error","chunks","i","length","push","slice","range","startAt","Number","isFinite","Array","from","_","process","util","inspect","thing","util","showHidden","depth","colors","dd","args","forEach","console","log","process","exit","dump","abbreviate","value","locale","Intl","NumberFormat","format","si","v","s","match","find","scale","formatted","minimumFractionDigits","maximumFractionDigits","humanize","num","slugify","h","replace","toLowerCase","ones","tens","numString","toString","Error","length","end","toBytes","bytes","decimals","bits","isNaN","base","dm","sizes","index","Math","floor","log","parseFloat","pow","toFixed","toHumanTime","seconds","worded","hours","minutes","secs","parts","push","join","hh","mm","ss","dot","obj","result","recurse","o","prefix","key","value","Object","entries","newKey","Array","isArray","extractProperties","keys","fromEntries","map","getValue","item","parent","child","undefined","String","modObj","callback","safeDot","data","split","reduce","acc","k","setNested","includes","parts","current","i","length","part","slugifyKeys","only","separator","slugify","replace","RegExp","toLowerCase","filter","after","value","search","index","indexOf","slice","length","afterLast","lastIndex","lastIndexOf","before","beforeLast","capitalize","str","toUpperCase","pluralize","word","count","irregularPlurals","foot","child","mouse","goose","person","man","woman","endsWith","includes","toLowerCase","test","singularize","irregulars","feet","children","mice","geese","people","men","women","replace","slugify","joiner","RegExp","subString","len","ellipsis","substring","trimEnd","substitute","data","def","undefined","regex","flattened","dot","out","_","key","String","truncate","suffix","clean","truncated"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h3ravel/support",
3
- "version": "0.8.5",
3
+ "version": "0.9.0",
4
4
  "description": "Shared helpers, facades and utilities for H3ravel.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -8,9 +8,9 @@
8
8
  "module": "./dist/index.js",
9
9
  "exports": {
10
10
  ".": {
11
+ "types": "./dist/index.d.ts",
11
12
  "import": "./dist/index.js",
12
- "require": "./dist/index.cjs",
13
- "types": "./dist/index.d.ts"
13
+ "require": "./dist/index.cjs"
14
14
  }
15
15
  },
16
16
  "files": [
@@ -37,7 +37,7 @@
37
37
  ],
38
38
  "devDependencies": {
39
39
  "typescript": "^5.4.0",
40
- "@h3ravel/shared": "^0.16.7"
40
+ "@h3ravel/shared": "^0.17.2"
41
41
  },
42
42
  "scripts": {
43
43
  "barrel": "barrelsby --directory src --delete --singleQuotes",