katanakit-js 6.4.3 → 6.7.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/README.md +4 -3
- package/dist/adapters/express/server.d.ts +133 -1
- package/dist/adapters/express/server.d.ts.map +1 -1
- package/dist/adapters/express/server.js +199 -97
- package/dist/adapters/express/server.js.map +1 -1
- package/dist/adapters/insforge/insforge.service.d.ts +139 -4
- package/dist/adapters/insforge/insforge.service.d.ts.map +1 -1
- package/dist/adapters/insforge/insforge.service.js +383 -213
- package/dist/adapters/insforge/insforge.service.js.map +1 -1
- package/dist/adapters/notion/notion.service.d.ts +130 -144
- package/dist/adapters/notion/notion.service.d.ts.map +1 -1
- package/dist/adapters/notion/notion.service.js +330 -341
- package/dist/adapters/notion/notion.service.js.map +1 -1
- package/dist/adapters/telegram/telegram.service.d.ts +82 -13
- package/dist/adapters/telegram/telegram.service.d.ts.map +1 -1
- package/dist/adapters/telegram/telegram.service.js +133 -55
- package/dist/adapters/telegram/telegram.service.js.map +1 -1
- package/dist/adapters/wordpress/wordpress.service.d.ts +181 -110
- package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
- package/dist/adapters/wordpress/wordpress.service.js +556 -465
- package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
- package/dist/core/services/access.service.d.ts +167 -4
- package/dist/core/services/access.service.d.ts.map +1 -1
- package/dist/core/services/access.service.js +241 -31
- package/dist/core/services/access.service.js.map +1 -1
- package/dist/core/services/agent.service.d.ts +182 -27
- package/dist/core/services/agent.service.d.ts.map +1 -1
- package/dist/core/services/agent.service.js +296 -164
- package/dist/core/services/agent.service.js.map +1 -1
- package/dist/core/services/assistant.service.d.ts +96 -5
- package/dist/core/services/assistant.service.d.ts.map +1 -1
- package/dist/core/services/assistant.service.js +171 -66
- package/dist/core/services/assistant.service.js.map +1 -1
- package/dist/core/services/dates.service.d.ts +183 -11
- package/dist/core/services/dates.service.d.ts.map +1 -1
- package/dist/core/services/dates.service.js +256 -57
- package/dist/core/services/dates.service.js.map +1 -1
- package/dist/core/services/http.service.d.ts +172 -133
- package/dist/core/services/http.service.d.ts.map +1 -1
- package/dist/core/services/http.service.js +381 -310
- package/dist/core/services/http.service.js.map +1 -1
- package/dist/core/services/logger.service.d.ts +96 -34
- package/dist/core/services/logger.service.d.ts.map +1 -1
- package/dist/core/services/logger.service.js +128 -41
- package/dist/core/services/logger.service.js.map +1 -1
- package/dist/core/services/query.service.d.ts +101 -0
- package/dist/core/services/query.service.d.ts.map +1 -1
- package/dist/core/services/query.service.js +131 -14
- package/dist/core/services/query.service.js.map +1 -1
- package/dist/core/services/utils.service.d.ts +286 -131
- package/dist/core/services/utils.service.d.ts.map +1 -1
- package/dist/core/services/utils.service.js +417 -227
- package/dist/core/services/utils.service.js.map +1 -1
- package/dist/core/services/validation.service.d.ts +48 -3
- package/dist/core/services/validation.service.d.ts.map +1 -1
- package/dist/core/services/validation.service.js +75 -23
- package/dist/core/services/validation.service.js.map +1 -1
- package/dist/infrastructure/decorators/cache.decorator.d.ts +53 -0
- package/dist/infrastructure/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/infrastructure/decorators/cache.decorator.js +47 -0
- package/dist/infrastructure/decorators/cache.decorator.js.map +1 -0
- package/dist/infrastructure/decorators/index.d.ts +4 -0
- package/dist/infrastructure/decorators/index.d.ts.map +1 -0
- package/dist/infrastructure/decorators/index.js +4 -0
- package/dist/infrastructure/decorators/index.js.map +1 -0
- package/dist/infrastructure/decorators/logger.decorator.d.ts +38 -0
- package/dist/infrastructure/decorators/logger.decorator.d.ts.map +1 -0
- package/dist/infrastructure/decorators/logger.decorator.js +66 -0
- package/dist/infrastructure/decorators/logger.decorator.js.map +1 -0
- package/dist/infrastructure/decorators/retry.decorator.d.ts +37 -0
- package/dist/infrastructure/decorators/retry.decorator.d.ts.map +1 -0
- package/dist/infrastructure/decorators/retry.decorator.js +54 -0
- package/dist/infrastructure/decorators/retry.decorator.js.map +1 -0
- package/dist/infrastructure/dom/dom.service.d.ts +11 -5
- package/dist/infrastructure/dom/dom.service.d.ts.map +1 -1
- package/dist/infrastructure/dom/dom.service.js +20 -6
- package/dist/infrastructure/dom/dom.service.js.map +1 -1
- package/dist/infrastructure/index.d.ts +1 -0
- package/dist/infrastructure/index.d.ts.map +1 -1
- package/dist/infrastructure/index.js +1 -0
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/prisma/schema.json +17 -13
- package/dist/styles.css +2 -0
- package/package.json +32 -38
- package/scss/_flags.scss +179 -0
- package/scss/_functions.scss +168 -0
- package/scss/_mixins.scss +658 -0
- package/scss/_reset.scss +275 -0
- package/scss/_utilities.scss +449 -0
- package/scss/_variables.scss +539 -0
- package/scss/components/_index.scss +50 -0
- package/scss/demo.scss +12 -0
- package/scss/main.scss +56 -0
|
@@ -1,363 +1,553 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* UtilsService - Singleton para utilidades genéricas.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
4
|
+
* Aplica el patrón Singleton: una única instancia que centraliza utilidades
|
|
5
|
+
* de arrays, objetos, tiempo y configuración. Puro, sin I/O, seguro para SSR.
|
|
6
|
+
*/
|
|
7
|
+
export class UtilsService {
|
|
8
|
+
static instance;
|
|
9
|
+
/**
|
|
10
|
+
* Constructor privado - enforce singleton.
|
|
11
|
+
*/
|
|
12
|
+
constructor() { }
|
|
13
|
+
/**
|
|
14
|
+
* Obtiene la instancia única del UtilsService (Singleton).
|
|
15
|
+
*
|
|
16
|
+
* @returns La instancia única de {@link UtilsService}.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const utils = UtilsService.getInstance();
|
|
21
|
+
* utils.useUnique([1, 2, 2, 3, 1]);
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
static getInstance() {
|
|
25
|
+
if (!UtilsService.instance) {
|
|
26
|
+
UtilsService.instance = new UtilsService();
|
|
27
|
+
}
|
|
28
|
+
return UtilsService.instance;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns a new array with duplicate values removed, preserving first-seen order.
|
|
32
|
+
*
|
|
33
|
+
* @typeParam T - Element type.
|
|
34
|
+
* @param array - The input array.
|
|
35
|
+
* @returns A new array with unique values.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* useUnique([1, 2, 2, 3, 1]); // [1, 2, 3]
|
|
40
|
+
* useUnique(["a", "b", "a"]); // ["a", "b"]
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
useUnique(array) {
|
|
44
|
+
return [...new Set(array)];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Splits an array into chunks of the given size.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam T - Element type.
|
|
50
|
+
* @param array - The input array.
|
|
51
|
+
* @param size - Number of elements per chunk (must be > 0).
|
|
52
|
+
* @returns An array of chunks.
|
|
53
|
+
* @throws {Error} If `size` is less than or equal to 0.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* useChunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
|
58
|
+
* useChunk(["a", "b", "c"], 1); // [["a"], ["b"], ["c"]]
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
useChunk(array, size) {
|
|
62
|
+
if (size <= 0)
|
|
63
|
+
throw new Error("Chunk size must be greater than 0");
|
|
64
|
+
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Groups array items by a key property or key selector function.
|
|
68
|
+
*
|
|
69
|
+
* @typeParam T - Element type.
|
|
70
|
+
* @param array - The input array.
|
|
71
|
+
* @param key - A property name or a function that returns the group key.
|
|
72
|
+
* @returns An object mapping group keys to arrays of items.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* const items = [
|
|
77
|
+
* { type: "fruit", name: "apple" },
|
|
78
|
+
* { type: "veggie", name: "carrot" },
|
|
79
|
+
* { type: "fruit", name: "banana" },
|
|
80
|
+
* ];
|
|
81
|
+
* useGroupBy(items, "type");
|
|
82
|
+
* // { fruit: [{ type: "fruit", name: "apple" }, { type: "fruit", name: "banana" }], veggie: [...] }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
useGroupBy(array, key) {
|
|
86
|
+
return array.reduce((acc, item) => {
|
|
87
|
+
const groupKey = typeof key === "function" ? key(item) : String(item[key]);
|
|
88
|
+
if (!acc[groupKey]) {
|
|
89
|
+
acc[groupKey] = [];
|
|
90
|
+
}
|
|
91
|
+
acc[groupKey].push(item);
|
|
92
|
+
return acc;
|
|
93
|
+
}, {});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Type guard that checks whether a value is a plain object (not null, not
|
|
97
|
+
* an array).
|
|
98
|
+
*
|
|
99
|
+
* @param item - The value to test.
|
|
100
|
+
* @returns `true` if the value is a plain `Record<string, unknown>`.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* useIsObject({ a: 1 }); // true
|
|
105
|
+
* useIsObject([1, 2]); // false
|
|
106
|
+
* useIsObject(null); // false
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
useIsObject(item) {
|
|
110
|
+
return typeof item === "object" && item !== null && !Array.isArray(item);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Deep clones a value using `structuredClone` with a JSON fallback for
|
|
114
|
+
* environments that do not support it.
|
|
115
|
+
*
|
|
116
|
+
* @typeParam T - Value type.
|
|
117
|
+
* @param value - The value to clone.
|
|
118
|
+
* @returns A deep copy of the value.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const original = { a: { b: 1 } };
|
|
123
|
+
* const copy = useDeepClone(original);
|
|
124
|
+
* copy.a.b = 2;
|
|
125
|
+
* original.a.b; // 1 (unchanged)
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
useDeepClone(value) {
|
|
129
|
+
if (typeof structuredClone === "function") {
|
|
130
|
+
return structuredClone(value);
|
|
131
|
+
}
|
|
132
|
+
return JSON.parse(JSON.stringify(value));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Deep merges `source` into a shallow copy of `target`. Nested plain
|
|
136
|
+
* objects are merged recursively. Skips prototype-pollution keys
|
|
137
|
+
* (`__proto__`, `constructor`, `prototype`).
|
|
138
|
+
*
|
|
139
|
+
* @typeParam T - Target object type.
|
|
140
|
+
* @param target - The base object (not mutated).
|
|
141
|
+
* @param source - The object to merge in.
|
|
142
|
+
* @returns A new merged object.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const base = { a: 1, b: { c: 2, d: 3 } };
|
|
147
|
+
* const override = { b: { c: 99, e: 4 }, f: 5 };
|
|
148
|
+
* useDeepMerge(base, override);
|
|
149
|
+
* // { a: 1, b: { c: 99, d: 3, e: 4 }, f: 5 }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
useDeepMerge(target, source) {
|
|
153
|
+
if (!target || !source)
|
|
154
|
+
return { ...target };
|
|
155
|
+
const output = { ...target };
|
|
156
|
+
const dangerousKeys = new Set(["__proto__", "constructor", "prototype"]);
|
|
157
|
+
for (const key of Object.keys(source)) {
|
|
158
|
+
if (dangerousKeys.has(key))
|
|
159
|
+
continue;
|
|
160
|
+
const targetVal = target[key];
|
|
161
|
+
const sourceVal = source[key];
|
|
162
|
+
if (this.useIsObject(targetVal) && this.useIsObject(sourceVal)) {
|
|
163
|
+
output[key] = this.useDeepMerge(targetVal, sourceVal);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
output[key] = sourceVal;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return output;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Picks the listed keys from an object into a new object.
|
|
173
|
+
*
|
|
174
|
+
* @typeParam T - Source object type.
|
|
175
|
+
* @typeParam K - Keys to pick.
|
|
176
|
+
* @param obj - The source object.
|
|
177
|
+
* @param keys - Array of keys to include.
|
|
178
|
+
* @returns A new object with only the picked keys.
|
|
179
|
+
*
|
|
180
|
+
* @example
|
|
181
|
+
* ```ts
|
|
182
|
+
* const user = { id: 1, name: "Alice", email: "a@b.com" };
|
|
183
|
+
* usePick(user, ["id", "name"]); // { id: 1, name: "Alice" }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
usePick(obj, keys) {
|
|
187
|
+
return keys.reduce((acc, key) => {
|
|
188
|
+
if (key in obj)
|
|
189
|
+
acc[key] = obj[key];
|
|
190
|
+
return acc;
|
|
191
|
+
}, {});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Omits the listed keys from an object, returning a shallow copy without
|
|
195
|
+
* them. Shallow on purpose to avoid `structuredClone` failures on
|
|
196
|
+
* non-cloneable values.
|
|
197
|
+
*
|
|
198
|
+
* @typeParam T - Source object type.
|
|
199
|
+
* @typeParam K - Keys to omit.
|
|
200
|
+
* @param obj - The source object.
|
|
201
|
+
* @param keys - Array of keys to exclude.
|
|
202
|
+
* @returns A new object without the omitted keys.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* const user = { id: 1, name: "Alice", password: "secret" };
|
|
207
|
+
* useOmit(user, ["password"]); // { id: 1, name: "Alice" }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
useOmit(obj, keys) {
|
|
211
|
+
const omit = new Set(keys);
|
|
212
|
+
const result = {};
|
|
213
|
+
for (const key of Object.keys(obj)) {
|
|
214
|
+
if (!omit.has(key)) {
|
|
215
|
+
result[key] = obj[key];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Returns a promise that resolves after `ms` milliseconds.
|
|
222
|
+
*
|
|
223
|
+
* @param ms - Delay in milliseconds.
|
|
224
|
+
* @returns A promise that resolves after the delay.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```ts
|
|
228
|
+
* await useSleep(1000); // waits 1 second
|
|
229
|
+
* ```
|
|
230
|
+
*/
|
|
231
|
+
useSleep(ms) {
|
|
232
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Retries an async function with a fixed delay between attempts.
|
|
236
|
+
*
|
|
237
|
+
* @typeParam T - The resolved value type.
|
|
238
|
+
* @param fn - The async function to retry.
|
|
239
|
+
* @param retries - Maximum number of retries (defaults to `3`).
|
|
240
|
+
* @param delayMs - Delay between retries in milliseconds (defaults to `1000`).
|
|
241
|
+
* @returns The resolved value of `fn`.
|
|
242
|
+
* @throws The last error if all retries are exhausted.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* const data = await useRetry(() => fetch("/api").then(r => r.json()), 3, 500);
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
async useRetry(fn, retries = 3, delayMs = 1000) {
|
|
250
|
+
try {
|
|
251
|
+
return await fn();
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
if (retries <= 0)
|
|
255
|
+
throw error;
|
|
256
|
+
await this.useSleep(delayMs);
|
|
257
|
+
return this.useRetry(fn, retries - 1, delayMs);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Copies text to the clipboard (browser only). Returns `false` on failure
|
|
262
|
+
* or in non-browser environments (SSR).
|
|
263
|
+
*
|
|
264
|
+
* @param text - The text to copy.
|
|
265
|
+
* @returns `true` if the text was copied, `false` otherwise.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const ok = await useCopyToClipboard("Hello, world!");
|
|
270
|
+
* console.log(ok); // true (in a browser with clipboard API)
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
async useCopyToClipboard(text) {
|
|
274
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
await navigator.clipboard.writeText(text);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Parses query parameters from a URL string into a plain object.
|
|
287
|
+
* Returns an empty object for invalid URLs.
|
|
288
|
+
*
|
|
289
|
+
* @param urlString - The URL to parse.
|
|
290
|
+
* @returns An object mapping parameter names to values.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* useGetUrlParams("https://example.com?q=hello&page=1");
|
|
295
|
+
* // { q: "hello", page: "1" }
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
useGetUrlParams(urlString) {
|
|
299
|
+
try {
|
|
300
|
+
const url = new URL(urlString);
|
|
301
|
+
return Object.fromEntries(url.searchParams.entries());
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return {};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Rounds a number (or numeric string) to the specified number of decimal
|
|
309
|
+
* places. Returns `0` for non-numeric input.
|
|
310
|
+
*
|
|
311
|
+
* @param value - The number or numeric string to round.
|
|
312
|
+
* @param decimals - Number of decimal places (defaults to `2`).
|
|
313
|
+
* @returns The rounded number.
|
|
314
|
+
*
|
|
315
|
+
* @example
|
|
316
|
+
* ```ts
|
|
317
|
+
* useRound(3.14159, 2); // 3.14
|
|
318
|
+
* useRound("5.678", 1); // 5.7
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
useRound(value, decimals = 2) {
|
|
322
|
+
const num = typeof value === "string" ? Number.parseFloat(value) : value;
|
|
323
|
+
if (Number.isNaN(num))
|
|
324
|
+
return 0;
|
|
325
|
+
const factor = 10 ** decimals;
|
|
326
|
+
return Math.round(num * factor) / factor;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Computes the arithmetic mean of an array of numbers. Returns `0` for an
|
|
330
|
+
* empty array.
|
|
331
|
+
*
|
|
332
|
+
* @param numbers - The input numbers.
|
|
333
|
+
* @returns The arithmetic mean.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```ts
|
|
337
|
+
* useAverage([1, 2, 3, 4, 5]); // 3
|
|
338
|
+
* useAverage([]); // 0
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
useAverage(numbers) {
|
|
342
|
+
if (numbers.length === 0)
|
|
343
|
+
return 0;
|
|
344
|
+
const sum = numbers.reduce((acc, n) => acc + n, 0);
|
|
345
|
+
return sum / numbers.length;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Splits a `define*Config` literal into its config half (props and booleans)
|
|
349
|
+
* and its method half (values declared with `function`).
|
|
350
|
+
*
|
|
351
|
+
* Shared by every `define*Config` so they all read the same way: register
|
|
352
|
+
* `config`, then return `{ config, ...methods }` as a `ConfigFacade`.
|
|
353
|
+
*
|
|
354
|
+
* @typeParam T - The literal passed to a `define*Config` call.
|
|
355
|
+
* @param input - Object literal holding props, booleans and methods.
|
|
356
|
+
* @returns The config entries and the method entries, split apart.
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* const { config, methods } = useSplitConfig({
|
|
361
|
+
* token: "abc",
|
|
362
|
+
* debug: true,
|
|
363
|
+
* send: async function () {},
|
|
364
|
+
* });
|
|
365
|
+
* // config → { token: "abc", debug: true }
|
|
366
|
+
* // methods → { send }
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
useSplitConfig(input) {
|
|
370
|
+
const config = {};
|
|
371
|
+
const methods = {};
|
|
372
|
+
for (const [key, value] of Object.entries(input)) {
|
|
373
|
+
if (typeof value === "function")
|
|
374
|
+
methods[key] = value;
|
|
375
|
+
else
|
|
376
|
+
config[key] = value;
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
// The split is structural, so TS cannot relate either half back to `T`.
|
|
380
|
+
config: config,
|
|
381
|
+
methods: methods,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/* -------------------------------------------------------------------------- */
|
|
386
|
+
/* Wrappers (backward compatibility) */
|
|
387
|
+
/* -------------------------------------------------------------------------- */
|
|
388
|
+
/**
|
|
389
|
+
* Returns a new array with duplicate values removed, preserving first-seen order.
|
|
7
390
|
*
|
|
8
391
|
* @example
|
|
9
392
|
* ```ts
|
|
10
393
|
* useUnique([1, 2, 2, 3, 1]); // [1, 2, 3]
|
|
11
|
-
* useUnique(["a", "b", "a"]); // ["a", "b"]
|
|
12
394
|
* ```
|
|
13
395
|
*/
|
|
14
396
|
export function useUnique(array) {
|
|
15
|
-
return
|
|
397
|
+
return UtilsService.getInstance().useUnique(array);
|
|
16
398
|
}
|
|
17
399
|
/**
|
|
18
400
|
* Splits an array into chunks of the given size.
|
|
19
401
|
*
|
|
20
|
-
* @typeParam T - Element type.
|
|
21
|
-
* @param array - The input array.
|
|
22
|
-
* @param size - Number of elements per chunk (must be > 0).
|
|
23
|
-
* @returns An array of chunks.
|
|
24
|
-
* @throws {Error} If `size` is less than or equal to 0.
|
|
25
|
-
*
|
|
26
402
|
* @example
|
|
27
403
|
* ```ts
|
|
28
404
|
* useChunk([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4], [5]]
|
|
29
|
-
* useChunk(["a", "b", "c"], 1); // [["a"], ["b"], ["c"]]
|
|
30
405
|
* ```
|
|
31
406
|
*/
|
|
32
407
|
export function useChunk(array, size) {
|
|
33
|
-
|
|
34
|
-
throw new Error("Chunk size must be greater than 0");
|
|
35
|
-
return Array.from({ length: Math.ceil(array.length / size) }, (_, i) => array.slice(i * size, i * size + size));
|
|
408
|
+
return UtilsService.getInstance().useChunk(array, size);
|
|
36
409
|
}
|
|
37
410
|
/**
|
|
38
411
|
* Groups array items by a key property or key selector function.
|
|
39
412
|
*
|
|
40
|
-
* @typeParam T - Element type.
|
|
41
|
-
* @param array - The input array.
|
|
42
|
-
* @param key - A property name or a function that returns the group key.
|
|
43
|
-
* @returns An object mapping group keys to arrays of items.
|
|
44
|
-
*
|
|
45
413
|
* @example
|
|
46
414
|
* ```ts
|
|
47
|
-
* const items = [
|
|
48
|
-
* { type: "fruit", name: "apple" },
|
|
49
|
-
* { type: "veggie", name: "carrot" },
|
|
50
|
-
* { type: "fruit", name: "banana" },
|
|
51
|
-
* ];
|
|
52
415
|
* useGroupBy(items, "type");
|
|
53
|
-
* // { fruit: [{ type: "fruit", name: "apple" }, { type: "fruit", name: "banana" }], veggie: [...] }
|
|
54
|
-
*
|
|
55
|
-
* useGroupBy(items, (item) => item.type.toUpperCase());
|
|
56
|
-
* // { FRUIT: [...], VEGGIE: [...] }
|
|
57
416
|
* ```
|
|
58
417
|
*/
|
|
59
418
|
export function useGroupBy(array, key) {
|
|
60
|
-
return
|
|
61
|
-
const groupKey = typeof key === "function" ? key(item) : String(item[key]);
|
|
62
|
-
if (!acc[groupKey]) {
|
|
63
|
-
acc[groupKey] = [];
|
|
64
|
-
}
|
|
65
|
-
acc[groupKey].push(item);
|
|
66
|
-
return acc;
|
|
67
|
-
}, {});
|
|
419
|
+
return UtilsService.getInstance().useGroupBy(array, key);
|
|
68
420
|
}
|
|
69
421
|
/**
|
|
70
|
-
* Type guard that checks whether a value is a plain object (not null, not
|
|
71
|
-
* an array).
|
|
72
|
-
*
|
|
73
|
-
* @param item - The value to test.
|
|
74
|
-
* @returns `true` if the value is a plain `Record<string, unknown>`.
|
|
422
|
+
* Type guard that checks whether a value is a plain object (not null, not an array).
|
|
75
423
|
*
|
|
76
424
|
* @example
|
|
77
425
|
* ```ts
|
|
78
|
-
* useIsObject({ a: 1 });
|
|
79
|
-
* useIsObject([1, 2]); // false
|
|
80
|
-
* useIsObject(null); // false
|
|
81
|
-
* useIsObject("hello"); // false
|
|
426
|
+
* useIsObject({ a: 1 }); // true
|
|
82
427
|
* ```
|
|
83
428
|
*/
|
|
84
429
|
export function useIsObject(item) {
|
|
85
|
-
return
|
|
430
|
+
return UtilsService.getInstance().useIsObject(item);
|
|
86
431
|
}
|
|
87
432
|
/**
|
|
88
|
-
* Deep clones a value using `structuredClone` with a JSON fallback
|
|
89
|
-
* environments that do not support it.
|
|
90
|
-
*
|
|
91
|
-
* @typeParam T - Value type.
|
|
92
|
-
* @param value - The value to clone.
|
|
93
|
-
* @returns A deep copy of the value.
|
|
433
|
+
* Deep clones a value using `structuredClone` with a JSON fallback.
|
|
94
434
|
*
|
|
95
435
|
* @example
|
|
96
436
|
* ```ts
|
|
97
|
-
* const original = { a: { b: 1 } };
|
|
98
437
|
* const copy = useDeepClone(original);
|
|
99
|
-
* copy.a.b = 2;
|
|
100
|
-
* original.a.b; // 1 (unchanged)
|
|
101
438
|
* ```
|
|
102
439
|
*/
|
|
103
440
|
export function useDeepClone(value) {
|
|
104
|
-
|
|
105
|
-
return structuredClone(value);
|
|
106
|
-
}
|
|
107
|
-
return JSON.parse(JSON.stringify(value));
|
|
441
|
+
return UtilsService.getInstance().useDeepClone(value);
|
|
108
442
|
}
|
|
109
443
|
/**
|
|
110
|
-
* Deep merges `source` into a shallow copy of `target`.
|
|
111
|
-
* objects are merged recursively. Skips prototype-pollution keys
|
|
112
|
-
* (`__proto__`, `constructor`, `prototype`).
|
|
113
|
-
*
|
|
114
|
-
* @typeParam T - Target object type.
|
|
115
|
-
* @param target - The base object (not mutated).
|
|
116
|
-
* @param source - The object to merge in.
|
|
117
|
-
* @returns A new merged object.
|
|
444
|
+
* Deep merges `source` into a shallow copy of `target`.
|
|
118
445
|
*
|
|
119
446
|
* @example
|
|
120
447
|
* ```ts
|
|
121
|
-
* const base = { a: 1, b: { c: 2, d: 3 } };
|
|
122
|
-
* const override = { b: { c: 99, e: 4 }, f: 5 };
|
|
123
448
|
* useDeepMerge(base, override);
|
|
124
|
-
* // { a: 1, b: { c: 99, d: 3, e: 4 }, f: 5 }
|
|
125
449
|
* ```
|
|
126
450
|
*/
|
|
127
451
|
export function useDeepMerge(target, source) {
|
|
128
|
-
|
|
129
|
-
return { ...target };
|
|
130
|
-
const output = { ...target };
|
|
131
|
-
const dangerousKeys = new Set(["__proto__", "constructor", "prototype"]);
|
|
132
|
-
for (const key of Object.keys(source)) {
|
|
133
|
-
if (dangerousKeys.has(key))
|
|
134
|
-
continue;
|
|
135
|
-
const targetVal = target[key];
|
|
136
|
-
const sourceVal = source[key];
|
|
137
|
-
if (useIsObject(targetVal) && useIsObject(sourceVal)) {
|
|
138
|
-
output[key] = useDeepMerge(targetVal, sourceVal);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
output[key] = sourceVal;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return output;
|
|
452
|
+
return UtilsService.getInstance().useDeepMerge(target, source);
|
|
145
453
|
}
|
|
146
454
|
/**
|
|
147
455
|
* Picks the listed keys from an object into a new object.
|
|
148
456
|
*
|
|
149
|
-
* @typeParam T - Source object type.
|
|
150
|
-
* @typeParam K - Keys to pick.
|
|
151
|
-
* @param obj - The source object.
|
|
152
|
-
* @param keys - Array of keys to include.
|
|
153
|
-
* @returns A new object with only the picked keys.
|
|
154
|
-
*
|
|
155
457
|
* @example
|
|
156
458
|
* ```ts
|
|
157
|
-
*
|
|
158
|
-
* usePick(user, ["id", "name"]); // { id: 1, name: "Alice" }
|
|
459
|
+
* usePick(user, ["id", "name"]);
|
|
159
460
|
* ```
|
|
160
461
|
*/
|
|
161
462
|
export function usePick(obj, keys) {
|
|
162
|
-
return
|
|
163
|
-
if (key in obj)
|
|
164
|
-
acc[key] = obj[key];
|
|
165
|
-
return acc;
|
|
166
|
-
}, {});
|
|
463
|
+
return UtilsService.getInstance().usePick(obj, keys);
|
|
167
464
|
}
|
|
168
465
|
/**
|
|
169
|
-
* Omits the listed keys from an object, returning a shallow copy without
|
|
170
|
-
* them. Shallow on purpose to avoid `structuredClone` failures on
|
|
171
|
-
* non-cloneable values.
|
|
172
|
-
*
|
|
173
|
-
* @typeParam T - Source object type.
|
|
174
|
-
* @typeParam K - Keys to omit.
|
|
175
|
-
* @param obj - The source object.
|
|
176
|
-
* @param keys - Array of keys to exclude.
|
|
177
|
-
* @returns A new object without the omitted keys.
|
|
466
|
+
* Omits the listed keys from an object, returning a shallow copy without them.
|
|
178
467
|
*
|
|
179
468
|
* @example
|
|
180
469
|
* ```ts
|
|
181
|
-
*
|
|
182
|
-
* useOmit(user, ["password"]); // { id: 1, name: "Alice" }
|
|
470
|
+
* useOmit(user, ["password"]);
|
|
183
471
|
* ```
|
|
184
472
|
*/
|
|
185
473
|
export function useOmit(obj, keys) {
|
|
186
|
-
|
|
187
|
-
const result = {};
|
|
188
|
-
for (const key of Object.keys(obj)) {
|
|
189
|
-
if (!omit.has(key)) {
|
|
190
|
-
result[key] = obj[key];
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return result;
|
|
474
|
+
return UtilsService.getInstance().useOmit(obj, keys);
|
|
194
475
|
}
|
|
195
476
|
/**
|
|
196
477
|
* Returns a promise that resolves after `ms` milliseconds.
|
|
197
478
|
*
|
|
198
|
-
* @param ms - Delay in milliseconds.
|
|
199
|
-
* @returns A promise that resolves after the delay.
|
|
200
|
-
*
|
|
201
479
|
* @example
|
|
202
480
|
* ```ts
|
|
203
|
-
* await useSleep(1000);
|
|
204
|
-
* console.log("done");
|
|
481
|
+
* await useSleep(1000);
|
|
205
482
|
* ```
|
|
206
483
|
*/
|
|
207
484
|
export function useSleep(ms) {
|
|
208
|
-
return
|
|
485
|
+
return UtilsService.getInstance().useSleep(ms);
|
|
209
486
|
}
|
|
210
487
|
/**
|
|
211
488
|
* Retries an async function with a fixed delay between attempts.
|
|
212
489
|
*
|
|
213
|
-
* @typeParam T - The resolved value type.
|
|
214
|
-
* @param fn - The async function to retry.
|
|
215
|
-
* @param retries - Maximum number of retries (defaults to `3`).
|
|
216
|
-
* @param delayMs - Delay between retries in milliseconds (defaults to `1000`).
|
|
217
|
-
* @returns The resolved value of `fn`.
|
|
218
|
-
* @throws The last error if all retries are exhausted.
|
|
219
|
-
*
|
|
220
490
|
* @example
|
|
221
491
|
* ```ts
|
|
222
|
-
* const data = await useRetry(() => fetch("/api")
|
|
492
|
+
* const data = await useRetry(() => fetch("/api"), 3, 500);
|
|
223
493
|
* ```
|
|
224
494
|
*/
|
|
225
495
|
export async function useRetry(fn, retries = 3, delayMs = 1000) {
|
|
226
|
-
|
|
227
|
-
return await fn();
|
|
228
|
-
}
|
|
229
|
-
catch (error) {
|
|
230
|
-
if (retries <= 0)
|
|
231
|
-
throw error;
|
|
232
|
-
await useSleep(delayMs);
|
|
233
|
-
return useRetry(fn, retries - 1, delayMs);
|
|
234
|
-
}
|
|
496
|
+
return UtilsService.getInstance().useRetry(fn, retries, delayMs);
|
|
235
497
|
}
|
|
236
498
|
/**
|
|
237
|
-
* Copies text to the clipboard (browser only).
|
|
238
|
-
* or in non-browser environments (SSR).
|
|
239
|
-
*
|
|
240
|
-
* @param text - The text to copy.
|
|
241
|
-
* @returns `true` if the text was copied, `false` otherwise.
|
|
499
|
+
* Copies text to the clipboard (browser only).
|
|
242
500
|
*
|
|
243
501
|
* @example
|
|
244
502
|
* ```ts
|
|
245
503
|
* const ok = await useCopyToClipboard("Hello, world!");
|
|
246
|
-
* console.log(ok); // true (in a browser with clipboard API)
|
|
247
504
|
* ```
|
|
248
505
|
*/
|
|
249
506
|
export async function useCopyToClipboard(text) {
|
|
250
|
-
|
|
251
|
-
return false;
|
|
252
|
-
}
|
|
253
|
-
try {
|
|
254
|
-
await navigator.clipboard.writeText(text);
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
507
|
+
return UtilsService.getInstance().useCopyToClipboard(text);
|
|
260
508
|
}
|
|
261
509
|
/**
|
|
262
510
|
* Parses query parameters from a URL string into a plain object.
|
|
263
|
-
* Returns an empty object for invalid URLs.
|
|
264
|
-
*
|
|
265
|
-
* @param urlString - The URL to parse.
|
|
266
|
-
* @returns An object mapping parameter names to values.
|
|
267
511
|
*
|
|
268
512
|
* @example
|
|
269
513
|
* ```ts
|
|
270
514
|
* useGetUrlParams("https://example.com?q=hello&page=1");
|
|
271
|
-
* // { q: "hello", page: "1" }
|
|
272
|
-
*
|
|
273
|
-
* useGetUrlParams("not-a-url"); // {}
|
|
274
515
|
* ```
|
|
275
516
|
*/
|
|
276
517
|
export function useGetUrlParams(urlString) {
|
|
277
|
-
|
|
278
|
-
const url = new URL(urlString);
|
|
279
|
-
return Object.fromEntries(url.searchParams.entries());
|
|
280
|
-
}
|
|
281
|
-
catch {
|
|
282
|
-
return {};
|
|
283
|
-
}
|
|
518
|
+
return UtilsService.getInstance().useGetUrlParams(urlString);
|
|
284
519
|
}
|
|
285
520
|
/**
|
|
286
|
-
* Rounds a number (or numeric string) to the specified number of decimal
|
|
287
|
-
* places. Returns `0` for non-numeric input.
|
|
288
|
-
*
|
|
289
|
-
* @param value - The number or numeric string to round.
|
|
290
|
-
* @param decimals - Number of decimal places (defaults to `2`).
|
|
291
|
-
* @returns The rounded number.
|
|
521
|
+
* Rounds a number (or numeric string) to the specified number of decimal places.
|
|
292
522
|
*
|
|
293
523
|
* @example
|
|
294
524
|
* ```ts
|
|
295
|
-
* useRound(3.14159, 2);
|
|
296
|
-
* useRound("5.678", 1); // 5.7
|
|
297
|
-
* useRound("not-a-num"); // 0
|
|
525
|
+
* useRound(3.14159, 2); // 3.14
|
|
298
526
|
* ```
|
|
299
527
|
*/
|
|
300
528
|
export function useRound(value, decimals = 2) {
|
|
301
|
-
|
|
302
|
-
if (Number.isNaN(num))
|
|
303
|
-
return 0;
|
|
304
|
-
const factor = 10 ** decimals;
|
|
305
|
-
return Math.round(num * factor) / factor;
|
|
529
|
+
return UtilsService.getInstance().useRound(value, decimals);
|
|
306
530
|
}
|
|
307
531
|
/**
|
|
308
|
-
* Computes the arithmetic mean of an array of numbers.
|
|
309
|
-
* empty array.
|
|
310
|
-
*
|
|
311
|
-
* @param numbers - The input numbers.
|
|
312
|
-
* @returns The arithmetic mean.
|
|
532
|
+
* Computes the arithmetic mean of an array of numbers.
|
|
313
533
|
*
|
|
314
534
|
* @example
|
|
315
535
|
* ```ts
|
|
316
536
|
* useAverage([1, 2, 3, 4, 5]); // 3
|
|
317
|
-
* useAverage([]); // 0
|
|
318
537
|
* ```
|
|
319
538
|
*/
|
|
320
539
|
export function useAverage(numbers) {
|
|
321
|
-
|
|
322
|
-
return 0;
|
|
323
|
-
const sum = numbers.reduce((acc, n) => acc + n, 0);
|
|
324
|
-
return sum / numbers.length;
|
|
540
|
+
return UtilsService.getInstance().useAverage(numbers);
|
|
325
541
|
}
|
|
326
542
|
/**
|
|
327
|
-
* Splits a `define*Config` literal into its config half
|
|
328
|
-
* and its method half (values declared with `function`).
|
|
329
|
-
*
|
|
330
|
-
* Shared by every `define*Config` so they all read the same way: register
|
|
331
|
-
* `config`, then return `{ config, ...methods }` as a {@link ConfigFacade}.
|
|
332
|
-
*
|
|
333
|
-
* @typeParam T - The literal passed to a `define*Config` call.
|
|
334
|
-
* @param input - Object literal holding props, booleans and methods.
|
|
335
|
-
* @returns The config entries and the method entries, split apart.
|
|
543
|
+
* Splits a `define*Config` literal into its config half and its method half.
|
|
336
544
|
*
|
|
337
545
|
* @example
|
|
338
546
|
* ```ts
|
|
339
|
-
* const { config, methods } = useSplitConfig({
|
|
340
|
-
* token: "abc",
|
|
341
|
-
* debug: true,
|
|
342
|
-
* send: async function () {},
|
|
343
|
-
* });
|
|
344
|
-
* // config → { token: "abc", debug: true }
|
|
345
|
-
* // methods → { send }
|
|
547
|
+
* const { config, methods } = useSplitConfig({ token: "abc", send: async function () {} });
|
|
346
548
|
* ```
|
|
347
549
|
*/
|
|
348
550
|
export function useSplitConfig(input) {
|
|
349
|
-
|
|
350
|
-
const methods = {};
|
|
351
|
-
for (const [key, value] of Object.entries(input)) {
|
|
352
|
-
if (typeof value === "function")
|
|
353
|
-
methods[key] = value;
|
|
354
|
-
else
|
|
355
|
-
config[key] = value;
|
|
356
|
-
}
|
|
357
|
-
return {
|
|
358
|
-
// The split is structural, so TS cannot relate either half back to `T`.
|
|
359
|
-
config: config,
|
|
360
|
-
methods: methods,
|
|
361
|
-
};
|
|
551
|
+
return UtilsService.getInstance().useSplitConfig(input);
|
|
362
552
|
}
|
|
363
553
|
//# sourceMappingURL=utils.service.js.map
|