@xxanderwp/jstoolkit 1.0.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.
@@ -0,0 +1,1459 @@
1
+ import md5 from 'md5';
2
+
3
+ /**
4
+ * Generate a random string of specified length
5
+ * @param length - Length of the string to generate
6
+ * @param characters - Characters to use (default: alphanumeric)
7
+ * @returns Random string
8
+ */
9
+ function randomString(length, characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
10
+ let result = '';
11
+ const charactersLength = characters.length;
12
+ for (let i = 0; i < length; i++) {
13
+ result += characters.charAt(Math.floor(Math.random() * charactersLength));
14
+ }
15
+ return result;
16
+ }
17
+ /**
18
+ * Truncate a string to a specified length and add ellipsis
19
+ * @param str - String to truncate
20
+ * @param maxLength - Maximum length
21
+ * @param ellipsis - Ellipsis string (default: '...')
22
+ * @returns Truncated string
23
+ */
24
+ function truncate(str, maxLength, ellipsis = '...') {
25
+ if (str.length <= maxLength)
26
+ return str;
27
+ return str.slice(0, maxLength - ellipsis.length) + ellipsis;
28
+ }
29
+ /**
30
+ * Capitalize the first letter of a string
31
+ * @param str - String to capitalize
32
+ * @returns Capitalized string
33
+ */
34
+ function capitalize(str) {
35
+ if (!str)
36
+ return str;
37
+ return str.charAt(0).toUpperCase() + str.slice(1);
38
+ }
39
+ /**
40
+ * Convert string to camelCase
41
+ * @param str - String to convert
42
+ * @returns camelCase string
43
+ */
44
+ function toCamelCase(str) {
45
+ return str
46
+ .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
47
+ .replace(/^[A-Z]/, (c) => c.toLowerCase());
48
+ }
49
+ /**
50
+ * Convert string to snake_case
51
+ * @param str - String to convert
52
+ * @returns snake_case string
53
+ */
54
+ function toSnakeCase(str) {
55
+ return str
56
+ .replace(/([A-Z])/g, '_$1')
57
+ .toLowerCase()
58
+ .replace(/^_/, '');
59
+ }
60
+ /**
61
+ * Convert string to kebab-case
62
+ * @param str - String to convert
63
+ * @returns kebab-case string
64
+ */
65
+ function toKebabCase(str) {
66
+ return str
67
+ .replace(/([A-Z])/g, '-$1')
68
+ .toLowerCase()
69
+ .replace(/^-/, '');
70
+ }
71
+ /**
72
+ * Remove all whitespace from a string
73
+ * @param str - String to process
74
+ * @returns String without whitespace
75
+ */
76
+ function removeWhitespace(str) {
77
+ return str.replace(/\s+/g, '');
78
+ }
79
+ /**
80
+ * Count occurrences of a substring in a string
81
+ * @param str - String to search in
82
+ * @param substring - Substring to count
83
+ * @returns Number of occurrences
84
+ */
85
+ function countOccurrences(str, substring) {
86
+ if (!substring)
87
+ return 0;
88
+ return (str.match(new RegExp(substring, 'g')) || []).length;
89
+ }
90
+
91
+ var string = /*#__PURE__*/Object.freeze({
92
+ __proto__: null,
93
+ capitalize: capitalize,
94
+ countOccurrences: countOccurrences,
95
+ randomString: randomString,
96
+ removeWhitespace: removeWhitespace,
97
+ toCamelCase: toCamelCase,
98
+ toKebabCase: toKebabCase,
99
+ toSnakeCase: toSnakeCase,
100
+ truncate: truncate
101
+ });
102
+
103
+ /**
104
+ * Get a random element from an array
105
+ * @param arr - Array to pick from
106
+ * @returns Random element
107
+ */
108
+ function randomElement(arr) {
109
+ return arr[Math.floor(Math.random() * arr.length)];
110
+ }
111
+ /**
112
+ * Get a random index from an array
113
+ * @param arr - Array to pick from
114
+ * @returns Random index
115
+ */
116
+ function randomElementIndex(arr) {
117
+ return Math.floor(Math.random() * arr.length);
118
+ }
119
+ /**
120
+ * Shuffle an array using Fisher-Yates algorithm
121
+ * @param arr - Array to shuffle
122
+ * @returns Shuffled array (new array)
123
+ */
124
+ function shuffle(arr) {
125
+ const result = [...arr];
126
+ for (let i = result.length - 1; i > 0; i--) {
127
+ const j = Math.floor(Math.random() * (i + 1));
128
+ [result[i], result[j]] = [result[j], result[i]];
129
+ }
130
+ return result;
131
+ }
132
+ /**
133
+ * Split an array into chunks of specified size
134
+ * @param array - Array to chunk
135
+ * @param size - Size of each chunk
136
+ * @returns Array of chunks
137
+ */
138
+ function chunk(array, size) {
139
+ const result = [];
140
+ for (let i = 0; i < array.length; i += size) {
141
+ result.push(array.slice(i, i + size));
142
+ }
143
+ return result;
144
+ }
145
+ /**
146
+ * Remove duplicate values from an array
147
+ * @param arr - Array to deduplicate
148
+ * @returns Array without duplicates
149
+ */
150
+ function unique(arr) {
151
+ return [...new Set(arr)];
152
+ }
153
+ /**
154
+ * Get the intersection of two arrays
155
+ * @param arr1 - First array
156
+ * @param arr2 - Second array
157
+ * @returns Intersection array
158
+ */
159
+ function intersection(arr1, arr2) {
160
+ const set2 = new Set(arr2);
161
+ return arr1.filter((item) => set2.has(item));
162
+ }
163
+ /**
164
+ * Get the difference of two arrays (elements in arr1 but not in arr2)
165
+ * @param arr1 - First array
166
+ * @param arr2 - Second array
167
+ * @returns Difference array
168
+ */
169
+ function difference(arr1, arr2) {
170
+ const set2 = new Set(arr2);
171
+ return arr1.filter((item) => !set2.has(item));
172
+ }
173
+ /**
174
+ * Sort array in ascending or descending order
175
+ * @param array - Array to sort
176
+ * @param order - Sort order ('ASC' or 'DESC')
177
+ * @returns Sorted array
178
+ */
179
+ function sort(array, order = 'ASC') {
180
+ return [...array].sort((a, b) => {
181
+ if (order === 'ASC') {
182
+ return a < b ? -1 : a > b ? 1 : 0;
183
+ }
184
+ else {
185
+ return a < b ? 1 : a > b ? -1 : 0;
186
+ }
187
+ });
188
+ }
189
+ /**
190
+ * Sort array of objects by multiple properties
191
+ * @param array - Array to sort
192
+ * @param params - Sort parameters
193
+ * @returns Sorted array
194
+ */
195
+ function sortBy(array, params) {
196
+ return [...array].sort((a, b) => {
197
+ for (const param of params) {
198
+ const aVal = a[param.key];
199
+ const bVal = b[param.key];
200
+ let result = 0;
201
+ if (param.order === 'ASC') {
202
+ result = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
203
+ }
204
+ else {
205
+ result = aVal < bVal ? 1 : aVal > bVal ? -1 : 0;
206
+ }
207
+ if (result !== 0)
208
+ return result;
209
+ }
210
+ return 0;
211
+ });
212
+ }
213
+ /**
214
+ * Sort array items by key order priority
215
+ * @param items - Array to sort
216
+ * @param keyOrder - Priority order of keys
217
+ * @returns Sorted array
218
+ */
219
+ function sortByKeys(items, keyOrder) {
220
+ const keyOrderMap = new Map(keyOrder.map((key, index) => [key, index]));
221
+ return [...items].sort((a, b) => {
222
+ let orderA = keyOrder.length;
223
+ let orderB = keyOrder.length;
224
+ for (const [key, index] of keyOrderMap) {
225
+ if (a.startsWith(key)) {
226
+ orderA = index;
227
+ break;
228
+ }
229
+ }
230
+ for (const [key, index] of keyOrderMap) {
231
+ if (b.startsWith(key)) {
232
+ orderB = index;
233
+ break;
234
+ }
235
+ }
236
+ return orderA - orderB;
237
+ });
238
+ }
239
+ /**
240
+ * Group array elements by a key function
241
+ * @param arr - Array to group
242
+ * @param keyFn - Function that returns the group key
243
+ * @returns Object with grouped elements
244
+ */
245
+ function groupBy(arr, keyFn) {
246
+ return arr.reduce((result, item) => {
247
+ const key = keyFn(item);
248
+ if (!result[key]) {
249
+ result[key] = [];
250
+ }
251
+ result[key].push(item);
252
+ return result;
253
+ }, {});
254
+ }
255
+ /**
256
+ * Flatten nested arrays to specified depth
257
+ * @param arr - Array to flatten
258
+ * @param depth - Depth to flatten (default: 1)
259
+ * @returns Flattened array
260
+ */
261
+ function flatten$1(arr, depth = 1) {
262
+ if (depth === 0)
263
+ return arr;
264
+ return arr.reduce((acc, val) => {
265
+ return acc.concat(Array.isArray(val) ? flatten$1(val, depth - 1) : val);
266
+ }, []);
267
+ }
268
+
269
+ var array = /*#__PURE__*/Object.freeze({
270
+ __proto__: null,
271
+ chunk: chunk,
272
+ difference: difference,
273
+ flatten: flatten$1,
274
+ groupBy: groupBy,
275
+ intersection: intersection,
276
+ randomElement: randomElement,
277
+ randomElementIndex: randomElementIndex,
278
+ shuffle: shuffle,
279
+ sort: sort,
280
+ sortBy: sortBy,
281
+ sortByKeys: sortByKeys,
282
+ unique: unique
283
+ });
284
+
285
+ /**
286
+ * Generate a random integer between min and max (inclusive)
287
+ * @param min - Minimum value
288
+ * @param max - Maximum value
289
+ * @returns Random integer
290
+ */
291
+ function randomInt$1(min, max) {
292
+ if (max < min) {
293
+ [max, min] = [min, max];
294
+ }
295
+ return Math.floor(Math.random() * (max - min + 1)) + min;
296
+ }
297
+ /**
298
+ * Generate a random float between min and max
299
+ * @param min - Minimum value
300
+ * @param max - Maximum value
301
+ * @returns Random float
302
+ */
303
+ function randomFloat$1(min, max) {
304
+ if (max < min) {
305
+ [max, min] = [min, max];
306
+ }
307
+ return Math.random() * (max - min) + min;
308
+ }
309
+ /**
310
+ * Round a number to specified decimal places
311
+ * @param value - Value to round
312
+ * @param decimals - Number of decimal places
313
+ * @param mode - Rounding mode ('round' or 'truncate')
314
+ * @returns Rounded number
315
+ */
316
+ function round(value, decimals, mode = 'round') {
317
+ const factor = Math.pow(10, decimals);
318
+ if (mode === 'truncate') {
319
+ return Math.trunc(value * factor) / factor;
320
+ }
321
+ return Math.round(value * factor) / factor;
322
+ }
323
+ /**
324
+ * Linear interpolation between two values
325
+ * @param start - Start value
326
+ * @param end - End value
327
+ * @param amount - Interpolation amount (0-1)
328
+ * @returns Interpolated value
329
+ */
330
+ function lerp(start, end, amount) {
331
+ amount = Math.max(0, Math.min(1, amount));
332
+ return start + (end - start) * amount;
333
+ }
334
+ /**
335
+ * Linear interpolation between two 2D points
336
+ * @param point1 - First point
337
+ * @param point2 - Second point
338
+ * @param amount - Interpolation amount (0-1)
339
+ * @returns Interpolated point
340
+ */
341
+ function lerp2D(point1, point2, amount) {
342
+ return {
343
+ x: lerp(point1.x, point2.x, amount),
344
+ y: lerp(point1.y, point2.y, amount),
345
+ };
346
+ }
347
+ /**
348
+ * Linear interpolation between two 3D vectors
349
+ * @param vec1 - First vector
350
+ * @param vec2 - Second vector
351
+ * @param amount - Interpolation amount (0-1)
352
+ * @returns Interpolated vector
353
+ */
354
+ function lerp3D(vec1, vec2, amount) {
355
+ return {
356
+ x: lerp(vec1.x, vec2.x, amount),
357
+ y: lerp(vec1.y, vec2.y, amount),
358
+ z: lerp(vec1.z, vec2.z, amount),
359
+ };
360
+ }
361
+ /**
362
+ * Calculate time-based interpolation value
363
+ * @param start - Start timestamp
364
+ * @param end - End timestamp
365
+ * @param current - Current timestamp (default: Date.now())
366
+ * @returns Interpolation value (0-1)
367
+ */
368
+ function lerpTime(start, end, current = Date.now()) {
369
+ const duration = current - start;
370
+ return Math.max(0, Math.min(1, duration / (end - start)));
371
+ }
372
+ /**
373
+ * Clamp a value between min and max
374
+ * @param value - Value to clamp
375
+ * @param min - Minimum value
376
+ * @param max - Maximum value
377
+ * @returns Clamped value
378
+ */
379
+ function clamp(value, min, max) {
380
+ return Math.max(min, Math.min(max, value));
381
+ }
382
+ /**
383
+ * Map a value from one range to another
384
+ * @param value - Value to map
385
+ * @param inMin - Input range minimum
386
+ * @param inMax - Input range maximum
387
+ * @param outMin - Output range minimum
388
+ * @param outMax - Output range maximum
389
+ * @returns Mapped value
390
+ */
391
+ function mapRange(value, inMin, inMax, outMin, outMax) {
392
+ return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
393
+ }
394
+ /**
395
+ * Calculate percentage
396
+ * @param value - Value
397
+ * @param total - Total value
398
+ * @returns Percentage (0-100)
399
+ */
400
+ function percentage(value, total) {
401
+ if (total === 0)
402
+ return 0;
403
+ return (value / total) * 100;
404
+ }
405
+ /**
406
+ * Calculate average of numbers
407
+ * @param numbers - Array of numbers
408
+ * @returns Average value
409
+ */
410
+ function average(numbers) {
411
+ if (numbers.length === 0)
412
+ return 0;
413
+ return numbers.reduce((sum, num) => sum + num, 0) / numbers.length;
414
+ }
415
+ /**
416
+ * Calculate sum of numbers
417
+ * @param numbers - Array of numbers
418
+ * @returns Sum
419
+ */
420
+ function sum(numbers) {
421
+ return numbers.reduce((total, num) => total + num, 0);
422
+ }
423
+ /**
424
+ * Find minimum value in array
425
+ * @param numbers - Array of numbers
426
+ * @returns Minimum value
427
+ */
428
+ function min(numbers) {
429
+ return Math.min(...numbers);
430
+ }
431
+ /**
432
+ * Find maximum value in array
433
+ * @param numbers - Array of numbers
434
+ * @returns Maximum value
435
+ */
436
+ function max(numbers) {
437
+ return Math.max(...numbers);
438
+ }
439
+
440
+ var math = /*#__PURE__*/Object.freeze({
441
+ __proto__: null,
442
+ average: average,
443
+ clamp: clamp,
444
+ lerp: lerp,
445
+ lerp2D: lerp2D,
446
+ lerp3D: lerp3D,
447
+ lerpTime: lerpTime,
448
+ mapRange: mapRange,
449
+ max: max,
450
+ min: min,
451
+ percentage: percentage,
452
+ randomFloat: randomFloat$1,
453
+ randomInt: randomInt$1,
454
+ round: round,
455
+ sum: sum
456
+ });
457
+
458
+ /**
459
+ * Get current Unix timestamp in seconds
460
+ * @returns Current timestamp
461
+ */
462
+ function timestamp() {
463
+ return Math.floor(Date.now() / 1000);
464
+ }
465
+ /**
466
+ * Get current Unix timestamp in milliseconds
467
+ * @returns Current timestamp in ms
468
+ */
469
+ function timestampMs() {
470
+ return Date.now();
471
+ }
472
+ /**
473
+ * Format a number with leading zero
474
+ * @param num - Number to format
475
+ * @returns Formatted string
476
+ */
477
+ function padZero(num) {
478
+ return `0${num}`.slice(-2);
479
+ }
480
+ /**
481
+ * Get full date and time string
482
+ * @param date - Date object (default: current date)
483
+ * @returns Formatted date string (DD.MM.YYYY HH:MM)
484
+ */
485
+ function formatDateTime(date = new Date()) {
486
+ return `${padZero(date.getDate())}.${padZero(date.getMonth() + 1)}.${date.getFullYear()} ${padZero(date.getHours())}:${padZero(date.getMinutes())}`;
487
+ }
488
+ /**
489
+ * Get full date and time string with seconds
490
+ * @param date - Date object (default: current date)
491
+ * @returns Formatted date string (DD.MM.YYYY HH:MM:SS)
492
+ */
493
+ function formatDateTimeSeconds(date = new Date()) {
494
+ return `${padZero(date.getDate())}.${padZero(date.getMonth() + 1)}.${date.getFullYear()} ${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`;
495
+ }
496
+ /**
497
+ * Get date without time
498
+ * @param date - Date object (default: current date)
499
+ * @returns Formatted date string (DD.MM.YYYY)
500
+ */
501
+ function formatDate(date = new Date()) {
502
+ return `${padZero(date.getDate())}.${padZero(date.getMonth() + 1)}.${date.getFullYear()}`;
503
+ }
504
+ /**
505
+ * Format timestamp to readable date/time string
506
+ * Shows only time if date is today, otherwise shows date and time
507
+ * @param time - Unix timestamp in seconds (default: current time)
508
+ * @param alwaysShowDate - Always show date even if today
509
+ * @returns Formatted string
510
+ */
511
+ function formatTimestamp(time = timestamp(), alwaysShowDate = false) {
512
+ const now = new Date();
513
+ const date = new Date(time * 1000);
514
+ let result = `${padZero(date.getHours())}:${padZero(date.getMinutes())}`;
515
+ const isToday = now.getDate() === date.getDate() &&
516
+ now.getMonth() === date.getMonth() &&
517
+ now.getFullYear() === date.getFullYear();
518
+ if (alwaysShowDate || !isToday) {
519
+ const showYear = now.getFullYear() !== date.getFullYear() || alwaysShowDate;
520
+ result = `${padZero(date.getDate())}.${padZero(date.getMonth() + 1)}${showYear ? `.${date.getFullYear()}` : ''} ${result}`;
521
+ }
522
+ return result;
523
+ }
524
+ /**
525
+ * Convert seconds to formatted duration string (HH:MM:SS or D:HH:MM:SS)
526
+ * @param seconds - Duration in seconds
527
+ * @returns Formatted duration string
528
+ */
529
+ function formatDuration(seconds) {
530
+ const days = Math.floor(seconds / 86400);
531
+ const hours = Math.floor((seconds % 86400) / 3600);
532
+ const minutes = Math.floor((seconds % 3600) / 60);
533
+ const secs = seconds % 60;
534
+ let result = '';
535
+ if (days > 0) {
536
+ result += `${days}:`;
537
+ }
538
+ result += `${hours.toString().padStart(2, '0')}:`;
539
+ result += `${minutes.toString().padStart(2, '0')}:`;
540
+ result += `${secs.toString().padStart(2, '0')}`;
541
+ return result;
542
+ }
543
+ /**
544
+ * Convert milliseconds to formatted duration string with ms
545
+ * @param ms - Duration in milliseconds
546
+ * @returns Formatted duration string (HH:MM:SS.mmm)
547
+ */
548
+ function formatDurationMs(ms) {
549
+ const seconds = Math.floor(ms / 1000);
550
+ let result = formatDuration(seconds);
551
+ const milliseconds = (ms % 1000).toString().padStart(3, '0');
552
+ result += `.${milliseconds}`;
553
+ return result;
554
+ }
555
+ /**
556
+ * Sleep/delay for specified milliseconds
557
+ * @param ms - Milliseconds to sleep
558
+ * @returns Promise that resolves after delay
559
+ */
560
+ function sleep(ms) {
561
+ return new Promise((resolve) => setTimeout(resolve, ms));
562
+ }
563
+ /**
564
+ * Check if a date is today
565
+ * @param date - Date to check
566
+ * @returns True if date is today
567
+ */
568
+ function isToday(date) {
569
+ const today = new Date();
570
+ return (date.getDate() === today.getDate() &&
571
+ date.getMonth() === today.getMonth() &&
572
+ date.getFullYear() === today.getFullYear());
573
+ }
574
+ /**
575
+ * Check if a date is yesterday
576
+ * @param date - Date to check
577
+ * @returns True if date is yesterday
578
+ */
579
+ function isYesterday(date) {
580
+ const yesterday = new Date();
581
+ yesterday.setDate(yesterday.getDate() - 1);
582
+ return (date.getDate() === yesterday.getDate() &&
583
+ date.getMonth() === yesterday.getMonth() &&
584
+ date.getFullYear() === yesterday.getFullYear());
585
+ }
586
+ /**
587
+ * Get relative time string (e.g., "2 hours ago", "in 3 days")
588
+ * @param date - Date to compare
589
+ * @param now - Reference date (default: current date)
590
+ * @returns Relative time string
591
+ */
592
+ function timeAgo(date, now = new Date()) {
593
+ const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
594
+ const isFuture = seconds < 0;
595
+ const absSeconds = Math.abs(seconds);
596
+ const intervals = [
597
+ { label: 'year', seconds: 31536000 },
598
+ { label: 'month', seconds: 2592000 },
599
+ { label: 'week', seconds: 604800 },
600
+ { label: 'day', seconds: 86400 },
601
+ { label: 'hour', seconds: 3600 },
602
+ { label: 'minute', seconds: 60 },
603
+ ];
604
+ for (const interval of intervals) {
605
+ const count = Math.floor(absSeconds / interval.seconds);
606
+ if (count >= 1) {
607
+ const plural = count > 1 ? 's' : '';
608
+ return isFuture
609
+ ? `in ${count} ${interval.label}${plural}`
610
+ : `${count} ${interval.label}${plural} ago`;
611
+ }
612
+ }
613
+ return 'just now';
614
+ }
615
+
616
+ var time = /*#__PURE__*/Object.freeze({
617
+ __proto__: null,
618
+ formatDate: formatDate,
619
+ formatDateTime: formatDateTime,
620
+ formatDateTimeSeconds: formatDateTimeSeconds,
621
+ formatDuration: formatDuration,
622
+ formatDurationMs: formatDurationMs,
623
+ formatTimestamp: formatTimestamp,
624
+ isToday: isToday,
625
+ isYesterday: isYesterday,
626
+ padZero: padZero,
627
+ sleep: sleep,
628
+ timeAgo: timeAgo,
629
+ timestamp: timestamp,
630
+ timestampMs: timestampMs
631
+ });
632
+
633
+ /**
634
+ * Encode string to Base64
635
+ * @param str - String to encode
636
+ * @returns Base64 encoded string
637
+ */
638
+ function toBase64(str) {
639
+ if (typeof Buffer !== 'undefined') {
640
+ return Buffer.from(str, 'utf-8').toString('base64');
641
+ }
642
+ // Browser environment
643
+ const codeUnits = new Uint16Array(str.length);
644
+ for (let i = 0; i < codeUnits.length; i++) {
645
+ codeUnits[i] = str.charCodeAt(i);
646
+ }
647
+ const uint8Array = new Uint8Array(codeUnits.buffer);
648
+ const chunkSize = 0x8000;
649
+ let result = '';
650
+ for (let i = 0; i < uint8Array.length; i += chunkSize) {
651
+ const chunk = uint8Array.subarray(i, i + chunkSize);
652
+ result += String.fromCharCode(...chunk);
653
+ }
654
+ return btoa(result);
655
+ }
656
+ /**
657
+ * Decode Base64 string
658
+ * @param encoded - Base64 encoded string
659
+ * @returns Decoded string
660
+ */
661
+ function fromBase64(encoded) {
662
+ if (typeof Buffer !== 'undefined') {
663
+ return Buffer.from(encoded, 'base64').toString('utf-8');
664
+ }
665
+ // Browser environment
666
+ const binary = atob(encoded);
667
+ const bytes = new Uint8Array(binary.length);
668
+ for (let i = 0; i < bytes.length; i++) {
669
+ bytes[i] = binary.charCodeAt(i);
670
+ }
671
+ return String.fromCharCode(...new Uint16Array(bytes.buffer));
672
+ }
673
+ /**
674
+ * Generate MD5 hash of a string
675
+ * @param text - Text to hash
676
+ * @returns MD5 hash
677
+ */
678
+ function hash(text) {
679
+ return md5(text);
680
+ }
681
+ /**
682
+ * Encode URI component safely
683
+ * @param str - String to encode
684
+ * @returns Encoded URI component
685
+ */
686
+ function encodeUri(str) {
687
+ return encodeURIComponent(str);
688
+ }
689
+ /**
690
+ * Decode URI component safely
691
+ * @param str - String to decode
692
+ * @returns Decoded URI component
693
+ */
694
+ function decodeUri(str) {
695
+ try {
696
+ return decodeURIComponent(str);
697
+ }
698
+ catch {
699
+ return str;
700
+ }
701
+ }
702
+ /**
703
+ * Convert ArrayBuffer to Buffer (Node.js)
704
+ * @param ab - ArrayBuffer to convert
705
+ * @returns Buffer
706
+ */
707
+ function arrayBufferToBuffer(ab) {
708
+ const buf = Buffer.alloc(ab.byteLength);
709
+ const view = new Uint8Array(ab);
710
+ for (let i = 0; i < buf.length; i++) {
711
+ buf[i] = view[i];
712
+ }
713
+ return buf;
714
+ }
715
+ /**
716
+ * Convert hex string to bytes
717
+ * @param hex - Hex string
718
+ * @returns Uint8Array
719
+ */
720
+ function hexToBytes(hex) {
721
+ const bytes = new Uint8Array(hex.length / 2);
722
+ for (let i = 0; i < bytes.length; i++) {
723
+ bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
724
+ }
725
+ return bytes;
726
+ }
727
+ /**
728
+ * Convert bytes to hex string
729
+ * @param bytes - Byte array
730
+ * @returns Hex string
731
+ */
732
+ function bytesToHex(bytes) {
733
+ return Array.from(bytes)
734
+ .map((b) => b.toString(16).padStart(2, '0'))
735
+ .join('');
736
+ }
737
+
738
+ var encoding = /*#__PURE__*/Object.freeze({
739
+ __proto__: null,
740
+ arrayBufferToBuffer: arrayBufferToBuffer,
741
+ bytesToHex: bytesToHex,
742
+ decodeUri: decodeUri,
743
+ encodeUri: encodeUri,
744
+ fromBase64: fromBase64,
745
+ hash: hash,
746
+ hexToBytes: hexToBytes,
747
+ toBase64: toBase64
748
+ });
749
+
750
+ /**
751
+ * Check if a string is a valid emoji
752
+ * @param str - String to check
753
+ * @returns True if string is an emoji
754
+ */
755
+ function isEmoji(str) {
756
+ const emojiRegex = new RegExp('^(' +
757
+ // Regular and compound emojis
758
+ '(?:\\p{Extended_Pictographic}(?:\\p{Emoji_Modifier}|\\uFE0F)?' +
759
+ '(?:\\u200D\\p{Extended_Pictographic}(?:\\p{Emoji_Modifier}|\\uFE0F)?)*)' +
760
+ '|' +
761
+ // Flags (regional indicator pairs)
762
+ '(?:\\p{Regional_Indicator}{2})' +
763
+ ')$', 'u');
764
+ return emojiRegex.test(str);
765
+ }
766
+ /**
767
+ * Check if a URL is an image link
768
+ * @param url - URL to check
769
+ * @returns True if URL points to an image
770
+ */
771
+ function isImageUrl(url) {
772
+ const imageExtensions = /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico)(\?.*)?$/i;
773
+ return imageExtensions.test(url);
774
+ }
775
+ /**
776
+ * Check if a string is a valid email
777
+ * @param email - Email string to validate
778
+ * @returns True if email is valid
779
+ */
780
+ function isEmail(email) {
781
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
782
+ return emailRegex.test(email);
783
+ }
784
+ /**
785
+ * Check if a string is a valid URL
786
+ * @param url - URL string to validate
787
+ * @returns True if URL is valid
788
+ */
789
+ function isUrl(url) {
790
+ try {
791
+ new URL(url);
792
+ return true;
793
+ }
794
+ catch {
795
+ return false;
796
+ }
797
+ }
798
+ /**
799
+ * Check if a string contains only numbers
800
+ * @param str - String to check
801
+ * @returns True if string contains only numbers
802
+ */
803
+ function isNumeric(str) {
804
+ return /^\d+$/.test(str);
805
+ }
806
+ /**
807
+ * Check if a string contains only letters
808
+ * @param str - String to check
809
+ * @returns True if string contains only letters
810
+ */
811
+ function isAlpha(str) {
812
+ return /^[a-zA-Z]+$/.test(str);
813
+ }
814
+ /**
815
+ * Check if a string contains only letters and numbers
816
+ * @param str - String to check
817
+ * @returns True if string is alphanumeric
818
+ */
819
+ function isAlphanumeric(str) {
820
+ return /^[a-zA-Z0-9]+$/.test(str);
821
+ }
822
+ /**
823
+ * Check if a value is empty (null, undefined, empty string, empty array, empty object)
824
+ * @param value - Value to check
825
+ * @returns True if value is empty
826
+ */
827
+ function isEmpty(value) {
828
+ if (value === null || value === undefined)
829
+ return true;
830
+ if (typeof value === 'string')
831
+ return value.trim().length === 0;
832
+ if (Array.isArray(value))
833
+ return value.length === 0;
834
+ if (typeof value === 'object')
835
+ return Object.keys(value).length === 0;
836
+ return false;
837
+ }
838
+ /**
839
+ * Check if a value is a plain object
840
+ * @param value - Value to check
841
+ * @returns True if value is a plain object
842
+ */
843
+ function isPlainObject(value) {
844
+ return (typeof value === 'object' &&
845
+ value !== null &&
846
+ value.constructor === Object &&
847
+ Object.prototype.toString.call(value) === '[object Object]');
848
+ }
849
+ /**
850
+ * Extract URL information from a string
851
+ * @param str - String containing URL
852
+ * @returns URL info or null if no URL found
853
+ */
854
+ function extractUrl(str) {
855
+ const urlRegex = /(https?:\/\/[^\s]+|www\.[^\s]+|[\w-]+\.[\w-]+[^\s]*)/i;
856
+ const match = str.match(urlRegex);
857
+ if (!match)
858
+ return null;
859
+ const rawUrl = match[0].startsWith('http') ? match[0] : `https://${match[0]}`;
860
+ try {
861
+ const parsed = new URL(rawUrl);
862
+ return {
863
+ url: parsed.href,
864
+ domain: parsed.hostname.replace(/^www\./, ''),
865
+ };
866
+ }
867
+ catch {
868
+ return null;
869
+ }
870
+ }
871
+ /**
872
+ * Check if a string is a valid IPv4 address
873
+ * @param ip - IP address string
874
+ * @returns True if valid IPv4
875
+ */
876
+ function isIPv4(ip) {
877
+ const ipv4Regex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
878
+ return ipv4Regex.test(ip);
879
+ }
880
+ /**
881
+ * Check if a string is a valid hex color
882
+ * @param color - Color string
883
+ * @returns True if valid hex color
884
+ */
885
+ function isHexColor(color) {
886
+ return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color);
887
+ }
888
+
889
+ var validation = /*#__PURE__*/Object.freeze({
890
+ __proto__: null,
891
+ extractUrl: extractUrl,
892
+ isAlpha: isAlpha,
893
+ isAlphanumeric: isAlphanumeric,
894
+ isEmail: isEmail,
895
+ isEmoji: isEmoji,
896
+ isEmpty: isEmpty,
897
+ isHexColor: isHexColor,
898
+ isIPv4: isIPv4,
899
+ isImageUrl: isImageUrl,
900
+ isNumeric: isNumeric,
901
+ isPlainObject: isPlainObject,
902
+ isUrl: isUrl
903
+ });
904
+
905
+ /**
906
+ * Format a number with thousands separators
907
+ * @param num - Number to format
908
+ * @param removeTrailingZeros - Remove .00 from integers (default: true)
909
+ * @returns Formatted number string
910
+ */
911
+ function formatNumber(num, removeTrailingZeros = true) {
912
+ if (typeof num !== 'number') {
913
+ num = Number(num);
914
+ }
915
+ let n = num.toFixed(2);
916
+ if (removeTrailingZeros) {
917
+ n = n.replace('.00', '');
918
+ }
919
+ return n.replace(/.+?(?=\D|$)/, (match) => {
920
+ return match.replace(/(\d)(?=(?:\d{3})+$)/g, '$1 ');
921
+ });
922
+ }
923
+ /**
924
+ * Format bytes to human-readable size
925
+ * @param bytes - Number of bytes
926
+ * @param decimals - Decimal places (default: 2)
927
+ * @returns Formatted size string
928
+ */
929
+ function formatBytes(bytes, decimals = 2) {
930
+ if (bytes === 0)
931
+ return '0 Bytes';
932
+ const k = 1024;
933
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
934
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
935
+ return (parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i]);
936
+ }
937
+ /**
938
+ * Format a number as currency
939
+ * @param amount - Amount to format
940
+ * @param currency - Currency code (default: 'USD')
941
+ * @param locale - Locale for formatting (default: 'en-US')
942
+ * @returns Formatted currency string
943
+ */
944
+ function formatCurrency(amount, currency = 'USD', locale = 'en-US') {
945
+ return new Intl.NumberFormat(locale, {
946
+ style: 'currency',
947
+ currency: currency,
948
+ }).format(amount);
949
+ }
950
+ /**
951
+ * Format a phone number (basic US format)
952
+ * @param phone - Phone number string
953
+ * @returns Formatted phone number
954
+ */
955
+ function formatPhone(phone) {
956
+ const cleaned = phone.replace(/\D/g, '');
957
+ if (cleaned.length === 10) {
958
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`;
959
+ }
960
+ if (cleaned.length === 11 && cleaned[0] === '1') {
961
+ return `+1 (${cleaned.slice(1, 4)}) ${cleaned.slice(4, 7)}-${cleaned.slice(7)}`;
962
+ }
963
+ return phone;
964
+ }
965
+ /**
966
+ * Format a credit card number with spaces
967
+ * @param cardNumber - Card number string
968
+ * @returns Formatted card number
969
+ */
970
+ function formatCardNumber(cardNumber) {
971
+ const cleaned = cardNumber.replace(/\s/g, '');
972
+ const groups = cleaned.match(/.{1,4}/g);
973
+ return groups ? groups.join(' ') : cardNumber;
974
+ }
975
+ /**
976
+ * Mask sensitive data (show only last N characters)
977
+ * @param str - String to mask
978
+ * @param visibleChars - Number of visible characters at the end (default: 4)
979
+ * @param maskChar - Character to use for masking (default: '*')
980
+ * @returns Masked string
981
+ */
982
+ function maskString(str, visibleChars = 4, maskChar = '*') {
983
+ if (str.length <= visibleChars)
984
+ return str;
985
+ const masked = maskChar.repeat(str.length - visibleChars);
986
+ return masked + str.slice(-visibleChars);
987
+ }
988
+ /**
989
+ * Format percentage
990
+ * @param value - Value to format
991
+ * @param decimals - Decimal places (default: 2)
992
+ * @returns Formatted percentage string
993
+ */
994
+ function formatPercentage(value, decimals = 2) {
995
+ return `${value.toFixed(decimals)}%`;
996
+ }
997
+ /**
998
+ * Pluralize a word based on count
999
+ * @param count - Count number
1000
+ * @param singular - Singular form
1001
+ * @param plural - Plural form (optional, will add 's' if not provided)
1002
+ * @returns Pluralized string with count
1003
+ */
1004
+ function pluralize(count, singular, plural) {
1005
+ const word = count === 1 ? singular : plural || `${singular}s`;
1006
+ return `${count} ${word}`;
1007
+ }
1008
+ /**
1009
+ * Abbreviate large numbers (1000 -> 1K, 1000000 -> 1M)
1010
+ * @param num - Number to abbreviate
1011
+ * @param decimals - Decimal places (default: 1)
1012
+ * @returns Abbreviated number string
1013
+ */
1014
+ function abbreviateNumber(num, decimals = 1) {
1015
+ if (num < 1000)
1016
+ return num.toString();
1017
+ const units = ['K', 'M', 'B', 'T'];
1018
+ const order = Math.floor(Math.log10(num) / 3);
1019
+ const unitIndex = order - 1;
1020
+ if (unitIndex >= units.length) {
1021
+ return num.toExponential(decimals);
1022
+ }
1023
+ const value = num / Math.pow(1000, order);
1024
+ return `${value.toFixed(decimals)}${units[unitIndex]}`;
1025
+ }
1026
+ /**
1027
+ * Format file name with extension
1028
+ * @param name - File name without extension
1029
+ * @param extension - File extension
1030
+ * @returns Formatted file name
1031
+ */
1032
+ function formatFileName(name, extension) {
1033
+ const cleanName = name.replace(/[^a-z0-9_-]/gi, '_');
1034
+ const cleanExt = extension.replace(/^\./, '');
1035
+ return `${cleanName}.${cleanExt}`;
1036
+ }
1037
+ /**
1038
+ * Title case a string (capitalize first letter of each word)
1039
+ * @param str - String to title case
1040
+ * @returns Title cased string
1041
+ */
1042
+ function titleCase(str) {
1043
+ return str
1044
+ .toLowerCase()
1045
+ .split(' ')
1046
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
1047
+ .join(' ');
1048
+ }
1049
+
1050
+ var format = /*#__PURE__*/Object.freeze({
1051
+ __proto__: null,
1052
+ abbreviateNumber: abbreviateNumber,
1053
+ formatBytes: formatBytes,
1054
+ formatCardNumber: formatCardNumber,
1055
+ formatCurrency: formatCurrency,
1056
+ formatFileName: formatFileName,
1057
+ formatNumber: formatNumber,
1058
+ formatPercentage: formatPercentage,
1059
+ formatPhone: formatPhone,
1060
+ maskString: maskString,
1061
+ pluralize: pluralize,
1062
+ titleCase: titleCase
1063
+ });
1064
+
1065
+ /**
1066
+ * Generate a random integer between min and max (inclusive)
1067
+ * @param min - Minimum value
1068
+ * @param max - Maximum value
1069
+ * @returns Random integer
1070
+ */
1071
+ function randomInt(min, max) {
1072
+ if (max < min) {
1073
+ [max, min] = [min, max];
1074
+ }
1075
+ return Math.floor(Math.random() * (max - min + 1)) + min;
1076
+ }
1077
+ /**
1078
+ * Generate a random float between min and max
1079
+ * @param min - Minimum value
1080
+ * @param max - Maximum value
1081
+ * @returns Random float
1082
+ */
1083
+ function randomFloat(min, max) {
1084
+ if (max < min) {
1085
+ [max, min] = [min, max];
1086
+ }
1087
+ return Math.random() * (max - min) + min;
1088
+ }
1089
+ /**
1090
+ * Generate a random boolean
1091
+ * @param probability - Probability of true (0-1, default: 0.5)
1092
+ * @returns Random boolean
1093
+ */
1094
+ function randomBoolean(probability = 0.5) {
1095
+ return Math.random() < probability;
1096
+ }
1097
+ /**
1098
+ * Pick N random unique elements from an array
1099
+ * @param arr - Array to pick from
1100
+ * @param count - Number of elements to pick
1101
+ * @returns Array of random elements
1102
+ */
1103
+ function randomSample(arr, count) {
1104
+ const shuffled = [...arr].sort(() => Math.random() - 0.5);
1105
+ return shuffled.slice(0, Math.min(count, arr.length));
1106
+ }
1107
+ /**
1108
+ * Generate a random hex color
1109
+ * @returns Random hex color string
1110
+ */
1111
+ function randomColor() {
1112
+ return `#${Math.floor(Math.random() * 16777215)
1113
+ .toString(16)
1114
+ .padStart(6, '0')}`;
1115
+ }
1116
+ /**
1117
+ * Generate a random UUID v4
1118
+ * @returns UUID string
1119
+ */
1120
+ function randomUUID() {
1121
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
1122
+ const r = (Math.random() * 16) | 0;
1123
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
1124
+ return v.toString(16);
1125
+ });
1126
+ }
1127
+ /**
1128
+ * Generate random weighted choice based on weights
1129
+ * @param items - Array of items
1130
+ * @param weights - Array of weights (must match items length)
1131
+ * @returns Random item based on weights
1132
+ */
1133
+ function randomWeighted(items, weights) {
1134
+ if (items.length !== weights.length) {
1135
+ throw new Error('Items and weights arrays must have the same length');
1136
+ }
1137
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
1138
+ let random = Math.random() * totalWeight;
1139
+ for (let i = 0; i < items.length; i++) {
1140
+ random -= weights[i];
1141
+ if (random <= 0) {
1142
+ return items[i];
1143
+ }
1144
+ }
1145
+ return items[items.length - 1];
1146
+ }
1147
+ /**
1148
+ * Generate a random date between two dates
1149
+ * @param start - Start date
1150
+ * @param end - End date
1151
+ * @returns Random date
1152
+ */
1153
+ function randomDate(start, end) {
1154
+ return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));
1155
+ }
1156
+
1157
+ var random = /*#__PURE__*/Object.freeze({
1158
+ __proto__: null,
1159
+ randomBoolean: randomBoolean,
1160
+ randomColor: randomColor,
1161
+ randomDate: randomDate,
1162
+ randomFloat: randomFloat,
1163
+ randomInt: randomInt,
1164
+ randomSample: randomSample,
1165
+ randomUUID: randomUUID,
1166
+ randomWeighted: randomWeighted
1167
+ });
1168
+
1169
+ /**
1170
+ * Deep clone an object
1171
+ * @param obj - Object to clone
1172
+ * @returns Cloned object
1173
+ */
1174
+ function deepClone(obj) {
1175
+ if (obj === null || typeof obj !== 'object')
1176
+ return obj;
1177
+ if (obj instanceof Date)
1178
+ return new Date(obj.getTime());
1179
+ if (obj instanceof Array)
1180
+ return obj.map((item) => deepClone(item));
1181
+ if (obj instanceof Object) {
1182
+ const cloned = {};
1183
+ for (const key in obj) {
1184
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1185
+ cloned[key] = deepClone(obj[key]);
1186
+ }
1187
+ }
1188
+ return cloned;
1189
+ }
1190
+ return obj;
1191
+ }
1192
+ /**
1193
+ * Deep merge two or more objects (mutates target)
1194
+ */
1195
+ function deepMerge(target, ...sources) {
1196
+ for (const source of sources) {
1197
+ if (!isObject(source))
1198
+ continue;
1199
+ for (const key in source) {
1200
+ if (!Object.prototype.hasOwnProperty.call(source, key))
1201
+ continue;
1202
+ const sourceValue = source[key];
1203
+ const targetValue = target[key];
1204
+ if (isObject(sourceValue)) {
1205
+ if (!isObject(targetValue)) {
1206
+ target[key] = {};
1207
+ }
1208
+ deepMerge(target[key], sourceValue);
1209
+ }
1210
+ else {
1211
+ target[key] = sourceValue;
1212
+ }
1213
+ }
1214
+ }
1215
+ return target;
1216
+ }
1217
+ /**
1218
+ * Get nested property value from object using path
1219
+ * @param obj - Object to get value from
1220
+ * @param path - Property path (e.g., 'user.address.city')
1221
+ * @param defaultValue - Default value if path not found
1222
+ * @returns Property value or default
1223
+ */
1224
+ function get(obj, path, defaultValue) {
1225
+ const keys = path.split('.');
1226
+ let result = obj;
1227
+ for (const key of keys) {
1228
+ if (result && typeof result === 'object' && key in result) {
1229
+ result = result[key];
1230
+ }
1231
+ else {
1232
+ return defaultValue;
1233
+ }
1234
+ }
1235
+ return result;
1236
+ }
1237
+ /**
1238
+ * Set nested property value in object using path
1239
+ * @param obj - Object to set value in
1240
+ * @param path - Property path (e.g., 'user.address.city')
1241
+ * @param value - Value to set
1242
+ * @returns Modified object
1243
+ */
1244
+ function set(obj, path, value) {
1245
+ const keys = path.split('.');
1246
+ const lastKey = keys.pop();
1247
+ let current = obj;
1248
+ for (const key of keys) {
1249
+ if (!(key in current) || typeof current[key] !== 'object') {
1250
+ current[key] = {};
1251
+ }
1252
+ current = current[key];
1253
+ }
1254
+ current[lastKey] = value;
1255
+ return obj;
1256
+ }
1257
+ /**
1258
+ * Pick specific properties from object
1259
+ * @param obj - Source object
1260
+ * @param keys - Keys to pick
1261
+ * @returns New object with picked properties
1262
+ */
1263
+ function pick(obj, keys) {
1264
+ const result = {};
1265
+ for (const key of keys) {
1266
+ if (key in obj) {
1267
+ result[key] = obj[key];
1268
+ }
1269
+ }
1270
+ return result;
1271
+ }
1272
+ /**
1273
+ * Omit specific properties from object
1274
+ * @param obj - Source object
1275
+ * @param keys - Keys to omit
1276
+ * @returns New object without omitted properties
1277
+ */
1278
+ function omit(obj, keys) {
1279
+ const result = { ...obj };
1280
+ for (const key of keys) {
1281
+ delete result[key];
1282
+ }
1283
+ return result;
1284
+ }
1285
+ /**
1286
+ * Check if value is a plain object
1287
+ * @param value - Value to check
1288
+ * @returns True if plain object
1289
+ */
1290
+ function isObject(value) {
1291
+ return value && typeof value === 'object' && !Array.isArray(value);
1292
+ }
1293
+ /**
1294
+ * Flatten nested object to single level with dot notation keys
1295
+ * @param obj - Object to flatten
1296
+ * @param prefix - Key prefix (used internally)
1297
+ * @returns Flattened object
1298
+ */
1299
+ function flatten(obj, prefix = '') {
1300
+ const result = {};
1301
+ for (const key in obj) {
1302
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1303
+ const newKey = prefix ? `${prefix}.${key}` : key;
1304
+ if (isObject(obj[key]) && !Array.isArray(obj[key])) {
1305
+ Object.assign(result, flatten(obj[key], newKey));
1306
+ }
1307
+ else {
1308
+ result[newKey] = obj[key];
1309
+ }
1310
+ }
1311
+ }
1312
+ return result;
1313
+ }
1314
+ /**
1315
+ * Unflatten object with dot notation keys to nested object
1316
+ * @param obj - Flattened object
1317
+ * @returns Nested object
1318
+ */
1319
+ function unflatten(obj) {
1320
+ const result = {};
1321
+ for (const key in obj) {
1322
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1323
+ set(result, key, obj[key]);
1324
+ }
1325
+ }
1326
+ return result;
1327
+ }
1328
+ /**
1329
+ * Get all keys from object including nested keys (dot notation)
1330
+ * @param obj - Object to get keys from
1331
+ * @param prefix - Key prefix (used internally)
1332
+ * @returns Array of all keys
1333
+ */
1334
+ function keys(obj, prefix = '') {
1335
+ const result = [];
1336
+ for (const key in obj) {
1337
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1338
+ const newKey = prefix ? `${prefix}.${key}` : key;
1339
+ result.push(newKey);
1340
+ if (isObject(obj[key]) && !Array.isArray(obj[key])) {
1341
+ result.push(...keys(obj[key], newKey));
1342
+ }
1343
+ }
1344
+ }
1345
+ return result;
1346
+ }
1347
+ /**
1348
+ * Get all values from object including nested values
1349
+ * @param obj - Object to get values from
1350
+ * @returns Array of all values
1351
+ */
1352
+ function values(obj) {
1353
+ const result = [];
1354
+ for (const key in obj) {
1355
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1356
+ if (isObject(obj[key]) && !Array.isArray(obj[key])) {
1357
+ result.push(...values(obj[key]));
1358
+ }
1359
+ else {
1360
+ result.push(obj[key]);
1361
+ }
1362
+ }
1363
+ }
1364
+ return result;
1365
+ }
1366
+ /**
1367
+ * Check if two objects are deeply equal
1368
+ * @param obj1 - First object
1369
+ * @param obj2 - Second object
1370
+ * @returns True if objects are equal
1371
+ */
1372
+ function isEqual(obj1, obj2) {
1373
+ if (obj1 === obj2)
1374
+ return true;
1375
+ if (typeof obj1 !== 'object' ||
1376
+ typeof obj2 !== 'object' ||
1377
+ obj1 === null ||
1378
+ obj2 === null) {
1379
+ return false;
1380
+ }
1381
+ const keys1 = Object.keys(obj1);
1382
+ const keys2 = Object.keys(obj2);
1383
+ if (keys1.length !== keys2.length)
1384
+ return false;
1385
+ for (const key of keys1) {
1386
+ if (!keys2.includes(key) || !isEqual(obj1[key], obj2[key])) {
1387
+ return false;
1388
+ }
1389
+ }
1390
+ return true;
1391
+ }
1392
+
1393
+ var object = /*#__PURE__*/Object.freeze({
1394
+ __proto__: null,
1395
+ deepClone: deepClone,
1396
+ deepMerge: deepMerge,
1397
+ flatten: flatten,
1398
+ get: get,
1399
+ isEqual: isEqual,
1400
+ keys: keys,
1401
+ omit: omit,
1402
+ pick: pick,
1403
+ set: set,
1404
+ unflatten: unflatten,
1405
+ values: values
1406
+ });
1407
+
1408
+ // Import all modules
1409
+ // Default export - all utilities in one object (like original System class)
1410
+ const toolkit = {
1411
+ string,
1412
+ array,
1413
+ math,
1414
+ time,
1415
+ encoding,
1416
+ validation,
1417
+ format,
1418
+ random,
1419
+ object,
1420
+ // Convenience shortcuts at top level
1421
+ randomString: randomString,
1422
+ capitalize: capitalize,
1423
+ truncate: truncate,
1424
+ randomElement: randomElement,
1425
+ chunk: chunk,
1426
+ unique: unique,
1427
+ shuffle: shuffle,
1428
+ randomInt: randomInt$1,
1429
+ randomFloat: randomFloat$1,
1430
+ lerp: lerp,
1431
+ clamp: clamp,
1432
+ round: round,
1433
+ timestamp: timestamp,
1434
+ timestampMs: timestampMs,
1435
+ formatDateTime: formatDateTime,
1436
+ formatDuration: formatDuration,
1437
+ sleep: sleep,
1438
+ toBase64: toBase64,
1439
+ fromBase64: fromBase64,
1440
+ hash: hash,
1441
+ isEmail: isEmail,
1442
+ isUrl: isUrl,
1443
+ isEmpty: isEmpty,
1444
+ isEmoji: isEmoji,
1445
+ formatNumber: formatNumber,
1446
+ formatBytes: formatBytes,
1447
+ pluralize: pluralize,
1448
+ randomBoolean: randomBoolean,
1449
+ randomColor: randomColor,
1450
+ randomUUID: randomUUID,
1451
+ deepClone: deepClone,
1452
+ pick: pick,
1453
+ omit: omit,
1454
+ get: get,
1455
+ set: set,
1456
+ };
1457
+
1458
+ export { array, capitalize, chunk, clamp, deepClone, toolkit as default, encoding, format, formatBytes, formatDateTime, formatDuration, formatNumber, fromBase64, get, hash, isEmail, isEmoji, isEmpty, isUrl, lerp, math, object, omit, pick, pluralize, random, randomBoolean, randomColor, randomElement, randomFloat$1 as randomFloat, randomInt$1 as randomInt, randomString, randomUUID, round, set, shuffle, sleep, string, time, timestamp, timestampMs, toBase64, unique, validation };
1459
+ //# sourceMappingURL=index.esm.js.map