katanakit-js 6.2.0 → 6.6.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.
Files changed (116) hide show
  1. package/README.md +11 -3
  2. package/dist/adapters/better-auth/better-auth.service.d.ts +83 -0
  3. package/dist/adapters/better-auth/better-auth.service.d.ts.map +1 -0
  4. package/dist/adapters/better-auth/better-auth.service.js +110 -0
  5. package/dist/adapters/better-auth/better-auth.service.js.map +1 -0
  6. package/dist/adapters/better-auth/client.d.ts +51 -0
  7. package/dist/adapters/better-auth/client.d.ts.map +1 -0
  8. package/dist/adapters/better-auth/client.js +67 -0
  9. package/dist/adapters/better-auth/client.js.map +1 -0
  10. package/dist/adapters/better-auth/index.d.ts +3 -0
  11. package/dist/adapters/better-auth/index.d.ts.map +1 -0
  12. package/dist/adapters/better-auth/index.js +3 -0
  13. package/dist/adapters/better-auth/index.js.map +1 -0
  14. package/dist/adapters/express/server.d.ts +133 -1
  15. package/dist/adapters/express/server.d.ts.map +1 -1
  16. package/dist/adapters/express/server.js +199 -97
  17. package/dist/adapters/express/server.js.map +1 -1
  18. package/dist/adapters/index.d.ts +2 -0
  19. package/dist/adapters/index.d.ts.map +1 -1
  20. package/dist/adapters/index.js +2 -0
  21. package/dist/adapters/index.js.map +1 -1
  22. package/dist/adapters/insforge/insforge.service.d.ts +139 -4
  23. package/dist/adapters/insforge/insforge.service.d.ts.map +1 -1
  24. package/dist/adapters/insforge/insforge.service.js +383 -213
  25. package/dist/adapters/insforge/insforge.service.js.map +1 -1
  26. package/dist/adapters/notion/notion.service.d.ts +130 -144
  27. package/dist/adapters/notion/notion.service.d.ts.map +1 -1
  28. package/dist/adapters/notion/notion.service.js +330 -341
  29. package/dist/adapters/notion/notion.service.js.map +1 -1
  30. package/dist/adapters/photoswipe/index.d.ts +2 -0
  31. package/dist/adapters/photoswipe/index.d.ts.map +1 -0
  32. package/dist/adapters/photoswipe/index.js +2 -0
  33. package/dist/adapters/photoswipe/index.js.map +1 -0
  34. package/dist/adapters/photoswipe/photoswipe.service.d.ts +99 -0
  35. package/dist/adapters/photoswipe/photoswipe.service.d.ts.map +1 -0
  36. package/dist/adapters/photoswipe/photoswipe.service.js +150 -0
  37. package/dist/adapters/photoswipe/photoswipe.service.js.map +1 -0
  38. package/dist/adapters/telegram/telegram.service.d.ts +82 -13
  39. package/dist/adapters/telegram/telegram.service.d.ts.map +1 -1
  40. package/dist/adapters/telegram/telegram.service.js +133 -55
  41. package/dist/adapters/telegram/telegram.service.js.map +1 -1
  42. package/dist/adapters/wordpress/wordpress.service.d.ts +181 -110
  43. package/dist/adapters/wordpress/wordpress.service.d.ts.map +1 -1
  44. package/dist/adapters/wordpress/wordpress.service.js +556 -465
  45. package/dist/adapters/wordpress/wordpress.service.js.map +1 -1
  46. package/dist/config/seo.service.d.ts +1 -1
  47. package/dist/config/seo.service.d.ts.map +1 -1
  48. package/dist/config/seo.service.js +1 -1
  49. package/dist/config/seo.service.js.map +1 -1
  50. package/dist/core/services/access.service.d.ts +167 -4
  51. package/dist/core/services/access.service.d.ts.map +1 -1
  52. package/dist/core/services/access.service.js +241 -31
  53. package/dist/core/services/access.service.js.map +1 -1
  54. package/dist/core/services/agent.service.d.ts +182 -27
  55. package/dist/core/services/agent.service.d.ts.map +1 -1
  56. package/dist/core/services/agent.service.js +296 -164
  57. package/dist/core/services/agent.service.js.map +1 -1
  58. package/dist/core/services/assistant.service.d.ts +96 -5
  59. package/dist/core/services/assistant.service.d.ts.map +1 -1
  60. package/dist/core/services/assistant.service.js +171 -66
  61. package/dist/core/services/assistant.service.js.map +1 -1
  62. package/dist/core/services/dates.service.d.ts +183 -11
  63. package/dist/core/services/dates.service.d.ts.map +1 -1
  64. package/dist/core/services/dates.service.js +256 -57
  65. package/dist/core/services/dates.service.js.map +1 -1
  66. package/dist/core/services/http.service.d.ts +172 -133
  67. package/dist/core/services/http.service.d.ts.map +1 -1
  68. package/dist/core/services/http.service.js +367 -310
  69. package/dist/core/services/http.service.js.map +1 -1
  70. package/dist/core/services/logger.service.d.ts +96 -34
  71. package/dist/core/services/logger.service.d.ts.map +1 -1
  72. package/dist/core/services/logger.service.js +128 -41
  73. package/dist/core/services/logger.service.js.map +1 -1
  74. package/dist/core/services/query.service.d.ts +101 -0
  75. package/dist/core/services/query.service.d.ts.map +1 -1
  76. package/dist/core/services/query.service.js +131 -14
  77. package/dist/core/services/query.service.js.map +1 -1
  78. package/dist/core/services/utils.service.d.ts +286 -131
  79. package/dist/core/services/utils.service.d.ts.map +1 -1
  80. package/dist/core/services/utils.service.js +417 -227
  81. package/dist/core/services/utils.service.js.map +1 -1
  82. package/dist/core/services/validation.service.d.ts +48 -3
  83. package/dist/core/services/validation.service.d.ts.map +1 -1
  84. package/dist/core/services/validation.service.js +75 -23
  85. package/dist/core/services/validation.service.js.map +1 -1
  86. package/dist/infrastructure/decorators/cache.decorator.d.ts +53 -0
  87. package/dist/infrastructure/decorators/cache.decorator.d.ts.map +1 -0
  88. package/dist/infrastructure/decorators/cache.decorator.js +47 -0
  89. package/dist/infrastructure/decorators/cache.decorator.js.map +1 -0
  90. package/dist/infrastructure/decorators/index.d.ts +4 -0
  91. package/dist/infrastructure/decorators/index.d.ts.map +1 -0
  92. package/dist/infrastructure/decorators/index.js +4 -0
  93. package/dist/infrastructure/decorators/index.js.map +1 -0
  94. package/dist/infrastructure/decorators/logger.decorator.d.ts +38 -0
  95. package/dist/infrastructure/decorators/logger.decorator.d.ts.map +1 -0
  96. package/dist/infrastructure/decorators/logger.decorator.js +66 -0
  97. package/dist/infrastructure/decorators/logger.decorator.js.map +1 -0
  98. package/dist/infrastructure/decorators/retry.decorator.d.ts +37 -0
  99. package/dist/infrastructure/decorators/retry.decorator.d.ts.map +1 -0
  100. package/dist/infrastructure/decorators/retry.decorator.js +54 -0
  101. package/dist/infrastructure/decorators/retry.decorator.js.map +1 -0
  102. package/dist/infrastructure/index.d.ts +1 -0
  103. package/dist/infrastructure/index.d.ts.map +1 -1
  104. package/dist/infrastructure/index.js +1 -0
  105. package/dist/infrastructure/index.js.map +1 -1
  106. package/dist/styles.css +2 -0
  107. package/package.json +49 -14
  108. package/scss/_flags.scss +179 -0
  109. package/scss/_functions.scss +168 -0
  110. package/scss/_mixins.scss +658 -0
  111. package/scss/_reset.scss +275 -0
  112. package/scss/_utilities.scss +449 -0
  113. package/scss/_variables.scss +539 -0
  114. package/scss/components/_index.scss +50 -0
  115. package/scss/demo.scss +12 -0
  116. package/scss/main.scss +56 -0
@@ -1,363 +1,553 @@
1
1
  /**
2
- * Returns a new array with duplicate values removed, preserving first-seen order.
2
+ * UtilsService - Singleton para utilidades genéricas.
3
3
  *
4
- * @typeParam T - Element type.
5
- * @param array - The input array.
6
- * @returns A new array with unique values.
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 [...new Set(array)];
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
- if (size <= 0)
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 array.reduce((acc, item) => {
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 }); // true
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 typeof item === "object" && item !== null && !Array.isArray(item);
430
+ return UtilsService.getInstance().useIsObject(item);
86
431
  }
87
432
  /**
88
- * Deep clones a value using `structuredClone` with a JSON fallback for
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
- if (typeof structuredClone === "function") {
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`. Nested plain
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
- if (!target || !source)
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
- * const user = { id: 1, name: "Alice", email: "a@b.com" };
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 keys.reduce((acc, key) => {
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
- * const user = { id: 1, name: "Alice", password: "secret" };
182
- * useOmit(user, ["password"]); // { id: 1, name: "Alice" }
470
+ * useOmit(user, ["password"]);
183
471
  * ```
184
472
  */
185
473
  export function useOmit(obj, keys) {
186
- const omit = new Set(keys);
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); // waits 1 second
204
- * console.log("done");
481
+ * await useSleep(1000);
205
482
  * ```
206
483
  */
207
484
  export function useSleep(ms) {
208
- return new Promise((resolve) => setTimeout(resolve, ms));
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").then(r => r.json()), 3, 500);
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
- try {
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). Returns `false` on failure
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
- if (typeof navigator === "undefined" || !navigator.clipboard) {
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
- try {
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); // 3.14
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
- const num = typeof value === "string" ? Number.parseFloat(value) : value;
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. Returns `0` for an
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
- if (numbers.length === 0)
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 (props and booleans)
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
- const config = {};
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