@h3ravel/support 0.9.0 → 0.10.1

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,456 +1,600 @@
1
- "use strict";
1
+ //#region rolldown:runtime
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
8
  var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
20
17
  };
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
- ));
29
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
30
22
 
31
- // src/index.ts
32
- var index_exports = {};
33
- __export(index_exports, {
34
- abbreviate: () => abbreviate,
35
- after: () => after,
36
- afterLast: () => afterLast,
37
- before: () => before,
38
- beforeLast: () => beforeLast,
39
- capitalize: () => capitalize,
40
- chunk: () => chunk,
41
- dd: () => dd,
42
- dot: () => dot,
43
- dump: () => dump,
44
- extractProperties: () => extractProperties,
45
- getValue: () => getValue,
46
- humanize: () => humanize,
47
- modObj: () => modObj,
48
- pluralize: () => pluralize,
49
- range: () => range,
50
- safeDot: () => safeDot,
51
- setNested: () => setNested,
52
- singularize: () => singularize,
53
- slugify: () => slugify,
54
- slugifyKeys: () => slugifyKeys,
55
- subString: () => subString,
56
- substitute: () => substitute,
57
- toBytes: () => toBytes,
58
- toHumanTime: () => toHumanTime,
59
- truncate: () => truncate
60
- });
61
- module.exports = __toCommonJS(index_exports);
23
+ //#endregion
24
+ let process = require("process");
25
+ process = __toESM(process);
26
+ let util = require("util");
27
+ util = __toESM(util);
62
28
 
63
- // src/Helpers/Arr.ts
64
- var chunk = /* @__PURE__ */ __name((arr, size = 2) => {
65
- if (size <= 0) throw new Error("Chunk size must be greater than 0");
66
- const chunks = [];
67
- for (let i = 0; i < arr.length; i += size) {
68
- chunks.push(arr.slice(i, i + size));
69
- }
70
- return chunks;
71
- }, "chunk");
72
- var range = /* @__PURE__ */ __name((size, startAt = 0) => {
73
- if (size <= 0 || !Number.isFinite(size)) return [];
74
- return Array.from({
75
- length: size
76
- }, (_, i) => startAt + i);
77
- }, "range");
29
+ //#region src/Helpers/Arr.ts
30
+ /**
31
+ * Splits an array into chunks of a specified size.
32
+ *
33
+ * @template T - Type of elements in the array
34
+ * @param arr - The input array
35
+ * @param size - Size of each chunk (default: 2)
36
+ * @returns An array of chunks (arrays)
37
+ */
38
+ const chunk = (arr, size = 2) => {
39
+ if (size <= 0) throw new Error("Chunk size must be greater than 0");
40
+ const chunks = [];
41
+ for (let i = 0; i < arr.length; i += size) chunks.push(arr.slice(i, i + size));
42
+ return chunks;
43
+ };
44
+ /**
45
+ * Generates an array of sequential numbers.
46
+ *
47
+ * @param size - Number of elements in the range
48
+ * @param startAt - Starting number (default: 0)
49
+ * @returns An array of numbers from startAt to startAt + size - 1
50
+ */
51
+ const range = (size, startAt = 0) => {
52
+ if (size <= 0 || !Number.isFinite(size)) return [];
53
+ return Array.from({ length: size }, (_, i) => startAt + i);
54
+ };
78
55
 
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");
56
+ //#endregion
57
+ //#region src/Helpers/DumpDie.ts
58
+ const inspect = (thing) => {
59
+ return util.default.inspect(thing, {
60
+ showHidden: true,
61
+ depth: null,
62
+ colors: true
63
+ });
64
+ };
65
+ /**
66
+ * Dump something and kill the process for quick debugging. Based on Laravel's dd()
67
+ *
68
+ * @param args
69
+ */
70
+ const dd = (...args) => {
71
+ args.forEach((thing) => {
72
+ console.log(inspect(thing));
73
+ });
74
+ process.default.exit(1);
75
+ };
76
+ /**
77
+ * Dump something but keep the process for quick debugging. Based on Laravel's dump()
78
+ *
79
+ * @param args
80
+ */
81
+ const dump = (...args) => {
82
+ args.forEach((thing) => {
83
+ console.log(inspect(thing));
84
+ });
85
+ };
100
86
 
101
- // src/Helpers/Number.ts
102
- var abbreviate = /* @__PURE__ */ __name((value, locale = "en-US") => {
103
- if (!value) return "0";
104
- if (value < 1e3) {
105
- return new Intl.NumberFormat(locale).format(value);
106
- }
107
- const si = [
108
- {
109
- v: 1e18,
110
- s: "E"
111
- },
112
- {
113
- v: 1e15,
114
- s: "P"
115
- },
116
- {
117
- v: 1e12,
118
- s: "T"
119
- },
120
- {
121
- v: 1e9,
122
- s: "B"
123
- },
124
- {
125
- v: 1e6,
126
- s: "M"
127
- },
128
- {
129
- v: 1e3,
130
- s: "K"
131
- }
132
- ];
133
- const match = si.find((scale) => value >= scale.v);
134
- if (!match) return new Intl.NumberFormat(locale).format(value);
135
- const formatted = value / match.v;
136
- return new Intl.NumberFormat(locale, {
137
- minimumFractionDigits: 0,
138
- maximumFractionDigits: 2
139
- }).format(formatted) + match.s;
140
- }, "abbreviate");
141
- var humanize = /* @__PURE__ */ __name((num, slugify2) => {
142
- if (!num) {
143
- return "";
144
- }
145
- if (slugify2 === "-" || slugify2 === "_") {
146
- const h = humanize(num);
147
- return typeof h === "string" ? h.replace(" ", slugify2).toLowerCase() : h;
148
- }
149
- const ones = [
150
- "",
151
- "one",
152
- "two",
153
- "three",
154
- "four",
155
- "five",
156
- "six",
157
- "seven",
158
- "eight",
159
- "nine",
160
- "ten",
161
- "eleven",
162
- "twelve",
163
- "thirteen",
164
- "fourteen",
165
- "fifteen",
166
- "sixteen",
167
- "seventeen",
168
- "eighteen",
169
- "nineteen"
170
- ];
171
- const tens = [
172
- "",
173
- "",
174
- "twenty",
175
- "thirty",
176
- "forty",
177
- "fifty",
178
- "sixty",
179
- "seventy",
180
- "eighty",
181
- "ninety"
182
- ];
183
- const numString = num.toString();
184
- if (num < 0) throw new Error("Negative numbers are not supported.");
185
- if (num === 0) return "zero";
186
- if (num < 20) {
187
- return ones[num] ?? "";
188
- }
189
- if (numString.length === 2) {
190
- return tens[numString[0]] + " " + ones[numString[1]];
191
- }
192
- if (numString.length == 3) {
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);
195
- }
196
- if (numString.length === 4) {
197
- const end = +((numString[1] || "") + numString[2] + numString[3]);
198
- if (end === 0) return ones[numString[0]] + " thousand";
199
- if (end < 100) return ones[numString[0]] + " thousand and " + humanize(end, slugify2);
200
- return ones[numString[0]] + " thousand " + humanize(end, slugify2);
201
- }
202
- return num;
203
- }, "humanize");
204
- var toBytes = /* @__PURE__ */ __name((bytes, decimals = 2, bits = false) => {
205
- if (!bytes || isNaN(bytes)) {
206
- return bits ? "0 B" : "0 Bytes";
207
- }
208
- const base = bits ? 1e3 : 1024;
209
- const dm = decimals < 0 ? 0 : decimals;
210
- const sizes = bits ? [
211
- "B",
212
- "KB",
213
- "MB",
214
- "GB",
215
- "TB",
216
- "PB",
217
- "EB",
218
- "ZB",
219
- "YB"
220
- ] : [
221
- "Bytes",
222
- "KiB",
223
- "MiB",
224
- "GiB",
225
- "TiB",
226
- "PiB",
227
- "EiB",
228
- "ZiB",
229
- "YiB"
230
- ];
231
- const index = Math.floor(Math.log(bytes) / Math.log(base));
232
- const value = parseFloat((bytes / Math.pow(base, index)).toFixed(dm));
233
- return `${value} ${sizes[index]}`;
234
- }, "toBytes");
235
- var toHumanTime = /* @__PURE__ */ __name((seconds = 0, worded = false) => {
236
- if (isNaN(seconds) || seconds < 0) seconds = 0;
237
- const hours = Math.floor(seconds / 3600);
238
- const minutes = Math.floor(seconds % 3600 / 60);
239
- const secs = Math.floor(seconds % 60);
240
- if (worded) {
241
- const parts = [];
242
- if (hours) parts.push(`${hours}hr`);
243
- if (minutes) parts.push(`${minutes}min`);
244
- if (secs || !hours && !minutes) parts.push(`${secs}sec`);
245
- return parts.join(" ");
246
- }
247
- const hh = hours > 0 ? `${hours}:` : "";
248
- const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ":";
249
- const ss = secs < 10 ? `0${secs}` : secs;
250
- return `${hh}${mm}${ss}`;
251
- }, "toHumanTime");
87
+ //#endregion
88
+ //#region src/Helpers/Number.ts
89
+ /**
90
+ * Abbreviates large numbers using SI symbols (K, M, B...)
91
+ * and formats the output according to the given locale.
92
+ *
93
+ * @param value - The number to abbreviate
94
+ * @param locale - Optional locale string (default: "en-US")
95
+ * @returns A localized, abbreviated number string
96
+ */
97
+ const abbreviate = (value, locale = "en-US") => {
98
+ if (!value) return "0";
99
+ if (value < 1e3) return new Intl.NumberFormat(locale).format(value);
100
+ const match = [
101
+ {
102
+ v: 0xde0b6b3a7640000,
103
+ s: "E"
104
+ },
105
+ {
106
+ v: 0x38d7ea4c68000,
107
+ s: "P"
108
+ },
109
+ {
110
+ v: 0xe8d4a51000,
111
+ s: "T"
112
+ },
113
+ {
114
+ v: 1e9,
115
+ s: "B"
116
+ },
117
+ {
118
+ v: 1e6,
119
+ s: "M"
120
+ },
121
+ {
122
+ v: 1e3,
123
+ s: "K"
124
+ }
125
+ ].find((scale) => value >= scale.v);
126
+ if (!match) return new Intl.NumberFormat(locale).format(value);
127
+ const formatted = value / match.v;
128
+ return new Intl.NumberFormat(locale, {
129
+ minimumFractionDigits: 0,
130
+ maximumFractionDigits: 2
131
+ }).format(formatted) + match.s;
132
+ };
133
+ /**
134
+ * Concverts a number into human readable string
135
+ *
136
+ * @param num The number to convert
137
+ * @param slugify convert the ouput into a slug using this as a separator
138
+ * @returns
139
+ */
140
+ const humanize = (num, slugify$1) => {
141
+ if (!num) return "";
142
+ if (slugify$1 === "-" || slugify$1 === "_") {
143
+ const h = humanize(num);
144
+ return typeof h === "string" ? h.replace(" ", slugify$1).toLowerCase() : h;
145
+ }
146
+ const ones = [
147
+ "",
148
+ "one",
149
+ "two",
150
+ "three",
151
+ "four",
152
+ "five",
153
+ "six",
154
+ "seven",
155
+ "eight",
156
+ "nine",
157
+ "ten",
158
+ "eleven",
159
+ "twelve",
160
+ "thirteen",
161
+ "fourteen",
162
+ "fifteen",
163
+ "sixteen",
164
+ "seventeen",
165
+ "eighteen",
166
+ "nineteen"
167
+ ];
168
+ const tens = [
169
+ "",
170
+ "",
171
+ "twenty",
172
+ "thirty",
173
+ "forty",
174
+ "fifty",
175
+ "sixty",
176
+ "seventy",
177
+ "eighty",
178
+ "ninety"
179
+ ];
180
+ const numString = num.toString();
181
+ if (num < 0) throw new Error("Negative numbers are not supported.");
182
+ if (num === 0) return "zero";
183
+ if (num < 20) return ones[num] ?? "";
184
+ if (numString.length === 2) return tens[numString[0]] + " " + ones[numString[1]];
185
+ if (numString.length == 3) if (numString[1] === "0" && numString[2] === "0") return ones[numString[0]] + " hundred";
186
+ else return ones[numString[0]] + " hundred and " + humanize(+((numString[1] || "") + numString[2]), slugify$1);
187
+ if (numString.length === 4) {
188
+ const end = +((numString[1] || "") + numString[2] + numString[3]);
189
+ if (end === 0) return ones[numString[0]] + " thousand";
190
+ if (end < 100) return ones[numString[0]] + " thousand and " + humanize(end, slugify$1);
191
+ return ones[numString[0]] + " thousand " + humanize(end, slugify$1);
192
+ }
193
+ return num;
194
+ };
195
+ /**
196
+ * Converts a number of bytes into a human-readable string.
197
+ *
198
+ * @param bytes - The size in bytes to convert
199
+ * @param decimals - Number of decimal places to display (default: 2)
200
+ * @param bits - If true, uses 1000-based (SI) units (B, KB, MB...);
201
+ * otherwise uses 1024-based binary units (Bytes, KiB...)
202
+ * @returns A formatted string with the appropriate unit
203
+ */
204
+ const toBytes = (bytes, decimals = 2, bits = false) => {
205
+ if (!bytes || isNaN(bytes)) return bits ? "0 B" : "0 Bytes";
206
+ const base = bits ? 1e3 : 1024;
207
+ const dm = decimals < 0 ? 0 : decimals;
208
+ const sizes = bits ? [
209
+ "B",
210
+ "KB",
211
+ "MB",
212
+ "GB",
213
+ "TB",
214
+ "PB",
215
+ "EB",
216
+ "ZB",
217
+ "YB"
218
+ ] : [
219
+ "Bytes",
220
+ "KiB",
221
+ "MiB",
222
+ "GiB",
223
+ "TiB",
224
+ "PiB",
225
+ "EiB",
226
+ "ZiB",
227
+ "YiB"
228
+ ];
229
+ const index = Math.floor(Math.log(bytes) / Math.log(base));
230
+ return `${parseFloat((bytes / Math.pow(base, index)).toFixed(dm))} ${sizes[index]}`;
231
+ };
232
+ /**
233
+ * Formats a duration (in seconds) into a human-readable string.
234
+ *
235
+ * @param seconds - Duration in seconds
236
+ * @param worded - If true, outputs worded format (e.g., "1hr 2min 3sec"),
237
+ * otherwise HH:MM:SS (e.g., "01:02:03")
238
+ * @returns A formatted time string
239
+ */
240
+ const toHumanTime = (seconds = 0, worded = false) => {
241
+ if (isNaN(seconds) || seconds < 0) seconds = 0;
242
+ const hours = Math.floor(seconds / 3600);
243
+ const minutes = Math.floor(seconds % 3600 / 60);
244
+ const secs = Math.floor(seconds % 60);
245
+ if (worded) {
246
+ const parts = [];
247
+ if (hours) parts.push(`${hours}hr`);
248
+ if (minutes) parts.push(`${minutes}min`);
249
+ if (secs || !hours && !minutes) parts.push(`${secs}sec`);
250
+ return parts.join(" ");
251
+ }
252
+ const hh = hours > 0 ? `${hours}:` : "";
253
+ const mm = (hours > 0 && minutes < 10 ? `0${minutes}` : minutes) + ":";
254
+ const ss = secs < 10 ? `0${secs}` : secs;
255
+ return `${hh}${mm}${ss}`;
256
+ };
252
257
 
253
- // src/Helpers/Obj.ts
254
- var dot = /* @__PURE__ */ __name((obj) => {
255
- const result = {};
256
- const recurse = /* @__PURE__ */ __name((o, prefix = "") => {
257
- for (const [key, value] of Object.entries(o)) {
258
- const newKey = prefix ? `${prefix}.${key}` : key;
259
- if (value && typeof value === "object" && !Array.isArray(value)) {
260
- recurse(value, newKey);
261
- } else {
262
- result[newKey] = value;
263
- }
264
- }
265
- }, "recurse");
266
- recurse(obj);
267
- return result;
268
- }, "dot");
269
- var extractProperties = /* @__PURE__ */ __name((obj, keys = []) => {
270
- return Object.fromEntries(keys.map((key) => [
271
- key,
272
- obj[key]
273
- ]));
274
- }, "extractProperties");
275
- var getValue = /* @__PURE__ */ __name((key, item) => {
276
- if (Array.isArray(key)) {
277
- const [parent, child] = key;
278
- if (child !== void 0) {
279
- return String(item?.[parent]?.[child] ?? item?.[parent] ?? `${String(parent)}.${String(child)}`);
280
- }
281
- return String(item?.[parent] ?? parent);
282
- }
283
- return String(item?.[key] ?? key);
284
- }, "getValue");
285
- var modObj = /* @__PURE__ */ __name((obj, callback) => {
286
- return Object.fromEntries(Object.entries(obj).map(([key, value]) => callback([
287
- key,
288
- value
289
- ])));
290
- }, "modObj");
258
+ //#endregion
259
+ //#region src/Helpers/Obj.ts
260
+ /**
261
+ * Flattens a nested object into a single-level object
262
+ * with dot-separated keys.
263
+ *
264
+ * Example:
265
+ * doter({
266
+ * user: { name: "John", address: { city: "NY" } },
267
+ * active: true
268
+ * })
269
+ *
270
+ * Output:
271
+ * {
272
+ * "user.name": "John",
273
+ * "user.address.city": "NY",
274
+ * "active": true
275
+ * }
276
+ *
277
+ * @template T - The type of the input object
278
+ * @param obj - The nested object to flatten
279
+ * @returns A flattened object with dotted keys and inferred types
280
+ */
281
+ const dot = (obj) => {
282
+ const result = {};
283
+ /**
284
+ * Internal recursive function to traverse and flatten the object.
285
+ *
286
+ * @param o - Current object to flatten
287
+ * @param prefix - Key path accumulated so far
288
+ */
289
+ const recurse = (o, prefix = "") => {
290
+ for (const [key, value] of Object.entries(o)) {
291
+ const newKey = prefix ? `${prefix}.${key}` : key;
292
+ /**
293
+ * Recurse if the value is a plain object
294
+ */
295
+ if (value && typeof value === "object" && !Array.isArray(value)) recurse(value, newKey);
296
+ else
297
+ /**
298
+ * Otherwise, assign directly
299
+ */
300
+ result[newKey] = value;
301
+ }
302
+ };
303
+ recurse(obj);
304
+ return result;
305
+ };
306
+ /**
307
+ * Extracts a subset of properties from an object.
308
+ *
309
+ * @template T - Type of the source object
310
+ * @template K - Keys of T to extract
311
+ * @param obj - The source object
312
+ * @param keys - Array of keys to extract
313
+ * @returns A new object with only the specified keys
314
+ */
315
+ const extractProperties = (obj, keys = []) => {
316
+ return Object.fromEntries(keys.map((key) => [key, obj[key]]));
317
+ };
318
+ /**
319
+ * Safely retrieves a value from an object by key or nested keys.
320
+ *
321
+ * @template T - Type of the source object
322
+ * @param key - Single key or tuple [parentKey, childKey]
323
+ * @param item - The source object
324
+ * @returns The found value as a string or the key itself if not found
325
+ */
326
+ const getValue = (key, item) => {
327
+ if (Array.isArray(key)) {
328
+ const [parent, child] = key;
329
+ if (child !== void 0) return String(item?.[parent]?.[child] ?? item?.[parent] ?? `${String(parent)}.${String(child)}`);
330
+ return String(item?.[parent] ?? parent);
331
+ }
332
+ return String(item?.[key] ?? key);
333
+ };
334
+ /**
335
+ * Maps over an object's entries and returns a new object
336
+ * with transformed keys and/or values.
337
+ *
338
+ * @template T - Type of the input object
339
+ * @template R - Type of the new values
340
+ * @param obj - The object to transform
341
+ * @param callback - Function that receives [key, value] and returns [newKey, newValue]
342
+ * @returns A new object with transformed entries
343
+ */
344
+ const modObj = (obj, callback) => {
345
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => callback([key, value])));
346
+ };
291
347
  function safeDot(data, key) {
292
- if (!key) return data;
293
- return key.split(".").reduce((acc, k) => acc?.[k], data);
348
+ if (!key) return data;
349
+ return key.split(".").reduce((acc, k) => acc?.[k], data);
294
350
  }
295
- __name(safeDot, "safeDot");
296
- var setNested = /* @__PURE__ */ __name((obj, key, value) => {
297
- if (!key.includes(".")) {
298
- obj[key] = value;
299
- return;
300
- }
301
- const parts = key.split(".");
302
- let current = obj;
303
- for (let i = 0; i < parts.length; i++) {
304
- const part = parts[i];
305
- if (i === parts.length - 1) {
306
- current[part] = value;
307
- } else {
308
- if (typeof current[part] !== "object" || current[part] === null) {
309
- current[part] = {};
310
- }
311
- current = current[part];
312
- }
313
- }
314
- }, "setNested");
315
- var slugifyKeys = /* @__PURE__ */ __name((obj, only = [], separator = "_") => {
316
- const slugify2 = /* @__PURE__ */ __name((key) => key.replace(/([a-z])([A-Z])/g, `$1${separator}$2`).replace(/[\s\W]+/g, separator).replace(new RegExp(`${separator}{2,}`, "g"), separator).replace(new RegExp(`^${separator}|${separator}$`, "g"), "").toLowerCase(), "slugify");
317
- let entries = Object.entries(obj);
318
- if (only.length) {
319
- entries = entries.filter(([key]) => only.includes(key));
320
- }
321
- return Object.fromEntries(entries.map(([key, value]) => [
322
- slugify2(key),
323
- value
324
- ]));
325
- }, "slugifyKeys");
351
+ /**
352
+ * Sets a nested property on an object using dot notation.
353
+ *
354
+ * @example
355
+ * const obj = {}
356
+ * setNested(obj, 'app.user.name', 'Legacy')
357
+ * console.log(obj)
358
+ * // Output: { app: { user: { name: 'Legacy' } } }
359
+ *
360
+ * @param obj - The target object to modify.
361
+ * @param key - The dot-separated key (e.g., 'app.user.name').
362
+ * @param value - The value to set at the specified path.
363
+ */
364
+ const setNested = (obj, key, value) => {
365
+ if (!key.includes(".")) {
366
+ obj[key] = value;
367
+ return;
368
+ }
369
+ const parts = key.split(".");
370
+ let current = obj;
371
+ for (let i = 0; i < parts.length; i++) {
372
+ const part = parts[i];
373
+ /**
374
+ * If we're at the last key, assign the value
375
+ */
376
+ if (i === parts.length - 1) current[part] = value;
377
+ else {
378
+ /**
379
+ * If the key doesn't exist or isn't an object, create it
380
+ */
381
+ if (typeof current[part] !== "object" || current[part] === null) current[part] = {};
382
+ current = current[part];
383
+ }
384
+ }
385
+ };
386
+ /**
387
+ * Converts object keys to a slugified format (e.g., snake_case).
388
+ *
389
+ * @template T - Type of the input object
390
+ * @param obj - The object whose keys will be slugified
391
+ * @param only - Optional array of keys to slugify (others remain unchanged)
392
+ * @param separator - Separator for slugified keys (default: "_")
393
+ * @returns A new object with slugified keys
394
+ */
395
+ const slugifyKeys = (obj, only = [], separator = "_") => {
396
+ const slugify$1 = (key) => key.replace(/([a-z])([A-Z])/g, `$1${separator}$2`).replace(/[\s\W]+/g, separator).replace(new RegExp(`${separator}{2,}`, "g"), separator).replace(new RegExp(`^${separator}|${separator}$`, "g"), "").toLowerCase();
397
+ let entries = Object.entries(obj);
398
+ if (only.length) entries = entries.filter(([key]) => only.includes(key));
399
+ return Object.fromEntries(entries.map(([key, value]) => [slugify$1(key), value]));
400
+ };
326
401
 
327
- // src/Helpers/Str.ts
328
- var after = /* @__PURE__ */ __name((value, search) => {
329
- if (!search) return value;
330
- const index = value.indexOf(search);
331
- return index !== -1 ? value.slice(index + search.length) : value;
332
- }, "after");
333
- var afterLast = /* @__PURE__ */ __name((value, search) => {
334
- if (!search) return value;
335
- const lastIndex = value.lastIndexOf(search);
336
- return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value;
337
- }, "afterLast");
338
- var before = /* @__PURE__ */ __name((value, search) => {
339
- if (!search) return value;
340
- const index = value.indexOf(search);
341
- return index !== -1 ? value.slice(0, index) : value;
342
- }, "before");
343
- var beforeLast = /* @__PURE__ */ __name((value, search) => {
344
- if (!search) return value;
345
- const lastIndex = value.lastIndexOf(search);
346
- return lastIndex !== -1 ? value.slice(0, lastIndex) : value;
347
- }, "beforeLast");
402
+ //#endregion
403
+ //#region src/Helpers/Str.ts
404
+ /**
405
+ * Get the portion of the string after the first occurrence of the given value.
406
+ *
407
+ * @param value
408
+ * @param search
409
+ * @returns
410
+ */
411
+ const after = (value, search) => {
412
+ if (!search) return value;
413
+ const index = value.indexOf(search);
414
+ return index !== -1 ? value.slice(index + search.length) : value;
415
+ };
416
+ /**
417
+ * Get the portion of the string after the last occurrence of the given value.
418
+ *
419
+ * @param value
420
+ * @param search
421
+ * @returns
422
+ */
423
+ const afterLast = (value, search) => {
424
+ if (!search) return value;
425
+ const lastIndex = value.lastIndexOf(search);
426
+ return lastIndex !== -1 ? value.slice(lastIndex + search.length) : value;
427
+ };
428
+ /**
429
+ * Get the portion of the string before the first occurrence of the given value.
430
+ *
431
+ * @param value
432
+ * @param search
433
+ * @returns
434
+ */
435
+ const before = (value, search) => {
436
+ if (!search) return value;
437
+ const index = value.indexOf(search);
438
+ return index !== -1 ? value.slice(0, index) : value;
439
+ };
440
+ /**
441
+ * Get the portion of the string before the last occurrence of the given value.
442
+ *
443
+ * @param value
444
+ * @param search
445
+ * @returns
446
+ */
447
+ const beforeLast = (value, search) => {
448
+ if (!search) return value;
449
+ const lastIndex = value.lastIndexOf(search);
450
+ return lastIndex !== -1 ? value.slice(0, lastIndex) : value;
451
+ };
452
+ /**
453
+ * Capitalizes the first character of a string.
454
+ *
455
+ * @param str - The input string
456
+ * @returns The string with the first character capitalized
457
+ */
348
458
  function capitalize(str) {
349
- if (!str) return "";
350
- return str[0].toUpperCase() + str.slice(1);
459
+ if (!str) return "";
460
+ return str[0].toUpperCase() + str.slice(1);
351
461
  }
352
- __name(capitalize, "capitalize");
353
- var pluralize = /* @__PURE__ */ __name((word, count) => {
354
- if (count === 1) return word;
355
- const irregularPlurals = {
356
- foot: "feet",
357
- child: "children",
358
- mouse: "mice",
359
- goose: "geese",
360
- person: "people",
361
- man: "men",
362
- woman: "women"
363
- };
364
- if (word in irregularPlurals) {
365
- return irregularPlurals[word];
366
- }
367
- if (word.endsWith("y") && ![
368
- "a",
369
- "e",
370
- "i",
371
- "o",
372
- "u"
373
- ].includes(word[word.length - 2]?.toLowerCase() ?? "")) {
374
- return word.slice(0, -1) + "ies";
375
- }
376
- if (/(s|ss|sh|ch|x|z)$/i.test(word)) {
377
- return word + "es";
378
- }
379
- return word + "s";
380
- }, "pluralize");
381
- var singularize = /* @__PURE__ */ __name((word) => {
382
- const irregulars = {
383
- feet: "foot",
384
- children: "child",
385
- mice: "mouse",
386
- geese: "goose",
387
- people: "person",
388
- men: "man",
389
- women: "woman"
390
- };
391
- if (word in irregulars) return irregulars[word];
392
- if (/ies$/i.test(word) && word.length > 3) {
393
- return word.replace(/ies$/i, "y");
394
- }
395
- if (/(ches|shes|sses|xes|zes)$/i.test(word)) {
396
- return word.replace(/es$/i, "");
397
- }
398
- if (/s$/i.test(word) && word.length > 1) {
399
- return word.replace(/s$/i, "");
400
- }
401
- return word;
402
- }, "singularize");
403
- var slugify = /* @__PURE__ */ __name((str, joiner = "_") => {
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();
405
- }, "slugify");
406
- var subString = /* @__PURE__ */ __name((str, len, ellipsis = "...") => {
407
- if (!str) return "";
408
- if (len <= ellipsis.length) return ellipsis;
409
- return str.length > len ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis : str;
410
- }, "subString");
411
- var substitute = /* @__PURE__ */ __name((str, data = {}, def) => {
412
- if (!str || !data) return void 0;
413
- const regex = /{\s*([a-zA-Z0-9_.]+)\s*}/g;
414
- const flattened = dot(data);
415
- const out = str.replace(regex, (_, key) => {
416
- const value = flattened[key];
417
- return value !== void 0 ? String(value) : def ?? "";
418
- });
419
- return out;
420
- }, "substitute");
421
- var truncate = /* @__PURE__ */ __name((str, len = 20, suffix = "...") => {
422
- if (!str) return "";
423
- const clean = str.replace(/<[^>]+>/g, "");
424
- const truncated = clean.length > len ? clean.substring(0, len - suffix.length) + suffix : clean;
425
- return truncated.replace(/\n/g, " ").replace(new RegExp(`\\s+${suffix.replace(/\./g, "\\.")}$`), suffix);
426
- }, "truncate");
427
- // Annotate the CommonJS export names for ESM import in node:
428
- 0 && (module.exports = {
429
- abbreviate,
430
- after,
431
- afterLast,
432
- before,
433
- beforeLast,
434
- capitalize,
435
- chunk,
436
- dd,
437
- dot,
438
- dump,
439
- extractProperties,
440
- getValue,
441
- humanize,
442
- modObj,
443
- pluralize,
444
- range,
445
- safeDot,
446
- setNested,
447
- singularize,
448
- slugify,
449
- slugifyKeys,
450
- subString,
451
- substitute,
452
- toBytes,
453
- toHumanTime,
454
- truncate
455
- });
462
+ /**
463
+ * Returns the pluralized form of a word based on the given number.
464
+ *
465
+ * @param word - The word to pluralize
466
+ * @param count - The number determining pluralization
467
+ * @returns Singular if count === 1, otherwise plural form
468
+ */
469
+ const pluralize = (word, count) => {
470
+ if (count === 1) return word;
471
+ const irregularPlurals = {
472
+ foot: "feet",
473
+ child: "children",
474
+ mouse: "mice",
475
+ goose: "geese",
476
+ person: "people",
477
+ man: "men",
478
+ woman: "women"
479
+ };
480
+ if (word in irregularPlurals) return irregularPlurals[word];
481
+ if (word.endsWith("y") && ![
482
+ "a",
483
+ "e",
484
+ "i",
485
+ "o",
486
+ "u"
487
+ ].includes(word[word.length - 2]?.toLowerCase() ?? "")) return word.slice(0, -1) + "ies";
488
+ if (/(s|ss|sh|ch|x|z)$/i.test(word)) return word + "es";
489
+ return word + "s";
490
+ };
491
+ /**
492
+ * Converts a plural English word into its singular form.
493
+ *
494
+ * @param word - The word to singularize
495
+ * @returns The singular form of the word
496
+ */
497
+ const singularize = (word) => {
498
+ const irregulars = {
499
+ feet: "foot",
500
+ children: "child",
501
+ mice: "mouse",
502
+ geese: "goose",
503
+ people: "person",
504
+ men: "man",
505
+ women: "woman"
506
+ };
507
+ if (word in irregulars) return irregulars[word];
508
+ if (/ies$/i.test(word) && word.length > 3) return word.replace(/ies$/i, "y");
509
+ if (/(ches|shes|sses|xes|zes)$/i.test(word)) return word.replace(/es$/i, "");
510
+ if (/s$/i.test(word) && word.length > 1) return word.replace(/s$/i, "");
511
+ return word;
512
+ };
513
+ /**
514
+ * Converts a string into a slug format.
515
+ * Handles camelCase, spaces, and non-alphanumeric characters.
516
+ *
517
+ * @param str - The input string to slugify
518
+ * @param joiner - The character used to join words (default: "_")
519
+ * @returns A slugified string
520
+ */
521
+ const slugify = (str, joiner = "_") => {
522
+ 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();
523
+ };
524
+ /**
525
+ * Truncates a string to a specified length and appends an ellipsis if needed.
526
+ *
527
+ * @param str - The input string
528
+ * @param len - Maximum length of the result (including ellipsis)
529
+ * @param ellipsis - String to append if truncated (default: "...")
530
+ * @returns The truncated string
531
+ */
532
+ const subString = (str, len, ellipsis = "...") => {
533
+ if (!str) return "";
534
+ if (len <= ellipsis.length) return ellipsis;
535
+ return str.length > len ? str.substring(0, len - ellipsis.length).trimEnd() + ellipsis : str;
536
+ };
537
+ /**
538
+ * Replaces placeholders in a string with corresponding values from a data object.
539
+ *
540
+ * Example:
541
+ * substitute("Hello { user.name }!", { user: { name: "John" } })
542
+ * // "Hello John!"
543
+ *
544
+ * @param str - The string containing placeholders wrapped in { } braces.
545
+ * @param data - Object containing values to substitute. Supports nested keys via dot notation.
546
+ * @param def - Default value to use if a key is missing. (Optional)
547
+ * @returns The substituted string or undefined if the input string or data is invalid.
548
+ */
549
+ const substitute = (str, data = {}, def) => {
550
+ if (!str || !data) return void 0;
551
+ const regex = /{\s*([a-zA-Z0-9_.]+)\s*}/g;
552
+ const flattened = dot(data);
553
+ return str.replace(regex, (_, key) => {
554
+ const value = flattened[key];
555
+ return value !== void 0 ? String(value) : def ?? "";
556
+ });
557
+ };
558
+ /**
559
+ * Truncates a string to a specified length, removing HTML tags and
560
+ * appending a suffix if the string exceeds the length.
561
+ *
562
+ * @param str - The string to truncate
563
+ * @param len - Maximum length (default: 20)
564
+ * @param suffix - Suffix to append if truncated (default: "...")
565
+ * @returns The truncated string
566
+ */
567
+ const truncate = (str, len = 20, suffix = "...") => {
568
+ if (!str) return "";
569
+ const clean = str.replace(/<[^>]+>/g, "");
570
+ return (clean.length > len ? clean.substring(0, len - suffix.length) + suffix : clean).replace(/\n/g, " ").replace(/* @__PURE__ */ new RegExp(`\\s+${suffix.replace(/\./g, "\\.")}$`), suffix);
571
+ };
572
+
573
+ //#endregion
574
+ exports.abbreviate = abbreviate;
575
+ exports.after = after;
576
+ exports.afterLast = afterLast;
577
+ exports.before = before;
578
+ exports.beforeLast = beforeLast;
579
+ exports.capitalize = capitalize;
580
+ exports.chunk = chunk;
581
+ exports.dd = dd;
582
+ exports.dot = dot;
583
+ exports.dump = dump;
584
+ exports.extractProperties = extractProperties;
585
+ exports.getValue = getValue;
586
+ exports.humanize = humanize;
587
+ exports.modObj = modObj;
588
+ exports.pluralize = pluralize;
589
+ exports.range = range;
590
+ exports.safeDot = safeDot;
591
+ exports.setNested = setNested;
592
+ exports.singularize = singularize;
593
+ exports.slugify = slugify;
594
+ exports.slugifyKeys = slugifyKeys;
595
+ exports.subString = subString;
596
+ exports.substitute = substitute;
597
+ exports.toBytes = toBytes;
598
+ exports.toHumanTime = toHumanTime;
599
+ exports.truncate = truncate;
456
600
  //# sourceMappingURL=index.cjs.map