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