@germondai/ts-utils 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './runtime';
2
+ export * from './types';
package/dist/module.js CHANGED
@@ -1,11 +1,99 @@
1
- // src/runtime/collection.ts
2
- var hasDuplicates = (array, keyExtractor) => new Set(array.map((value, index) => keyExtractor?.(value, index, array) || value)).size < array.length;
1
+ // src/runtime/array.ts
2
+ var hasDuplicates = (array, keyExtractor) => new Set(array.map((value, index) => keyExtractor?.(value, index, array) ?? value)).size < array.length;
3
3
  var uniqueArray = (array, keyExtractor) => [
4
4
  ...new Map(array.map((value, index, array2) => [
5
- keyExtractor?.(value, index, array2) || value,
5
+ keyExtractor?.(value, index, array2) ?? value,
6
6
  value
7
7
  ])).values()
8
8
  ];
9
+ var chunk = (array, size) => {
10
+ if (size <= 0)
11
+ return [];
12
+ const result = [];
13
+ for (let i = 0;i < array.length; i += size) {
14
+ result.push(array.slice(i, i + size));
15
+ }
16
+ return result;
17
+ };
18
+ var shuffle = (array) => {
19
+ const result = [...array];
20
+ for (let i = result.length - 1;i > 0; i--) {
21
+ const j = Math.floor(Math.random() * (i + 1));
22
+ [result[i], result[j]] = [result[j], result[i]];
23
+ }
24
+ return result;
25
+ };
26
+ var groupBy = (array, key) => {
27
+ const getKey = typeof key === "function" ? key : (item) => String(item[key]);
28
+ const result = {};
29
+ for (const item of array) {
30
+ const k = getKey(item);
31
+ if (!result[k])
32
+ result[k] = [];
33
+ result[k].push(item);
34
+ }
35
+ return result;
36
+ };
37
+ var intersection = (...arrays) => {
38
+ if (arrays.length === 0)
39
+ return [];
40
+ return arrays.reduce((acc, arr) => acc.filter((item) => arr.includes(item)));
41
+ };
42
+ var difference = (a, b) => a.filter((item) => !b.includes(item));
43
+ var range = (start, end, step = 1) => {
44
+ if (step <= 0)
45
+ return [];
46
+ const result = [];
47
+ for (let i = start;i < end; i += step) {
48
+ result.push(i);
49
+ }
50
+ return result;
51
+ };
52
+ var sortBy = (array, key, order = "asc") => {
53
+ const getVal = typeof key === "function" ? key : (item) => item[key];
54
+ return [...array].sort((a, b) => {
55
+ const va = getVal(a);
56
+ const vb = getVal(b);
57
+ const cmp = va < vb ? -1 : va > vb ? 1 : 0;
58
+ return order === "desc" ? -cmp : cmp;
59
+ });
60
+ };
61
+ var compact = (array) => array.filter(Boolean);
62
+ var last = (array) => array[array.length - 1];
63
+ var sample = (array) => array.length === 0 ? undefined : array[Math.floor(Math.random() * array.length)];
64
+ // src/runtime/color.ts
65
+ var hexToRgb = (hex) => {
66
+ const cleaned = hex.replace(/^#/, "");
67
+ let fullHex;
68
+ if (cleaned.length === 3) {
69
+ fullHex = cleaned.split("").map((c) => c + c).join("");
70
+ } else if (cleaned.length === 6) {
71
+ fullHex = cleaned;
72
+ } else {
73
+ return null;
74
+ }
75
+ const num = parseInt(fullHex, 16);
76
+ if (isNaN(num))
77
+ return null;
78
+ return {
79
+ r: num >> 16 & 255,
80
+ g: num >> 8 & 255,
81
+ b: num & 255
82
+ };
83
+ };
84
+ var rgbToHex = (r, g, b) => "#" + [r, g, b].map((v) => Math.max(0, Math.min(255, Math.round(v)))).map((v) => v.toString(16).padStart(2, "0")).join("");
85
+ var lighten = (hex, amount) => {
86
+ const rgb = hexToRgb(hex);
87
+ if (!rgb)
88
+ return hex;
89
+ return rgbToHex(rgb.r + (255 - rgb.r) * amount, rgb.g + (255 - rgb.g) * amount, rgb.b + (255 - rgb.b) * amount);
90
+ };
91
+ var darken = (hex, amount) => {
92
+ const rgb = hexToRgb(hex);
93
+ if (!rgb)
94
+ return hex;
95
+ return rgbToHex(rgb.r * (1 - amount), rgb.g * (1 - amount), rgb.b * (1 - amount));
96
+ };
9
97
  // src/runtime/constants.ts
10
98
  var SUPPORTED_DISPLAYABLE_MEDIA_TYPES = {
11
99
  images: [
@@ -25,13 +113,14 @@ var SUPPORTED_DISPLAYABLE_MEDIA_TYPES = {
25
113
  ]
26
114
  };
27
115
  // src/runtime/convertor.ts
116
+ var splitWords = (input) => input.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[\s_\-]+/).filter(Boolean);
28
117
  var toCamelCase = (input) => {
29
- const words = input.toLowerCase().split(/[\s_-]+/).filter(Boolean);
118
+ const words = splitWords(input);
30
119
  return words.map((word, index) => index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)).join("");
31
120
  };
32
- var toPascalCase = (input) => input.toLowerCase().split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
33
- var toSnakeCase = (input) => input.toLowerCase().split(/[\s-]+/).filter(Boolean).join("_");
34
- var toKebabCase = (input) => input.toLowerCase().split(/[\s_]+/).filter(Boolean).join("-");
121
+ var toPascalCase = (input) => splitWords(input).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
122
+ var toSnakeCase = (input) => splitWords(input).join("_");
123
+ var toKebabCase = (input) => splitWords(input).join("-");
35
124
  var toTitleCase = (input) => input.toLowerCase().split(/\s+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
36
125
  var toSentenceCase = (input) => {
37
126
  const lower = input.toLowerCase();
@@ -67,8 +156,64 @@ var unescapeHTML = (str) => {
67
156
  };
68
157
  return str.replace(/&[a-zA-Z0-9#]+;/g, (entity) => unescapeMap[entity] || entity);
69
158
  };
159
+ // src/runtime/crypto.ts
160
+ var generateId = (length = 16) => {
161
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
162
+ let result = "";
163
+ for (let i = 0;i < length; i++) {
164
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
165
+ }
166
+ return result;
167
+ };
168
+ var hash = (str) => {
169
+ let h = 5381;
170
+ for (let i = 0;i < str.length; i++) {
171
+ h = (h << 5) + h + str.charCodeAt(i) >>> 0;
172
+ }
173
+ return h;
174
+ };
70
175
  // src/runtime/data.ts
71
- var clone = (data) => JSON.parse(JSON.stringify(data));
176
+ var clone = (data) => {
177
+ if (data === null || typeof data !== "object")
178
+ return data;
179
+ try {
180
+ return structuredClone(data);
181
+ } catch {
182
+ return JSON.parse(JSON.stringify(data));
183
+ }
184
+ };
185
+ var isEqual = (a, b, seen = new WeakSet) => {
186
+ if (a === b)
187
+ return true;
188
+ if (a == null || b == null)
189
+ return false;
190
+ if (typeof a !== typeof b)
191
+ return false;
192
+ if (typeof a === "object") {
193
+ if (seen.has(a) || seen.has(b))
194
+ return a === b;
195
+ seen.add(a);
196
+ seen.add(b);
197
+ if (Array.isArray(a) && Array.isArray(b)) {
198
+ if (a.length !== b.length)
199
+ return false;
200
+ for (let i = 0;i < a.length; i++)
201
+ if (!isEqual(a[i], b[i], seen))
202
+ return false;
203
+ return true;
204
+ } else {
205
+ const keysA = Object.keys(a);
206
+ const keysB = Object.keys(b);
207
+ if (keysA.length !== keysB.length)
208
+ return false;
209
+ for (const key of keysA)
210
+ if (!keysB.includes(key) || !isEqual(a[key], b[key], seen))
211
+ return false;
212
+ return true;
213
+ }
214
+ }
215
+ return false;
216
+ };
72
217
  // src/runtime/errors.ts
73
218
  var catchError = async (promise, errorsToCatch) => {
74
219
  try {
@@ -80,6 +225,61 @@ var catchError = async (promise, errorsToCatch) => {
80
225
  throw error;
81
226
  }
82
227
  };
228
+ // src/runtime/function.ts
229
+ var debounce = (fn, ms) => {
230
+ let timer;
231
+ const debounced = (...args) => {
232
+ if (timer !== undefined)
233
+ clearTimeout(timer);
234
+ timer = setTimeout(() => fn(...args), ms);
235
+ };
236
+ debounced.cancel = () => {
237
+ if (timer !== undefined)
238
+ clearTimeout(timer);
239
+ timer = undefined;
240
+ };
241
+ return debounced;
242
+ };
243
+ var throttle = (fn, ms) => {
244
+ let timer;
245
+ let lastRun = 0;
246
+ const throttled = (...args) => {
247
+ const now = Date.now();
248
+ const remaining = ms - (now - lastRun);
249
+ if (remaining <= 0) {
250
+ if (timer !== undefined) {
251
+ clearTimeout(timer);
252
+ timer = undefined;
253
+ }
254
+ lastRun = now;
255
+ fn(...args);
256
+ } else if (timer === undefined) {
257
+ timer = setTimeout(() => {
258
+ lastRun = Date.now();
259
+ timer = undefined;
260
+ fn(...args);
261
+ }, remaining);
262
+ }
263
+ };
264
+ throttled.cancel = () => {
265
+ if (timer !== undefined)
266
+ clearTimeout(timer);
267
+ timer = undefined;
268
+ };
269
+ return throttled;
270
+ };
271
+ var once = (fn) => {
272
+ let called = false;
273
+ let result;
274
+ return (...args) => {
275
+ if (!called) {
276
+ called = true;
277
+ result = fn(...args);
278
+ }
279
+ return result;
280
+ };
281
+ };
282
+ var noop = () => {};
83
283
  // src/runtime/math.ts
84
284
  var rand = (n, m = 0) => Math.floor(Math.random() * (m - n + 1)) + n;
85
285
  var percentage = (value, maxValue, decimalPlaces = 2) => {
@@ -90,10 +290,175 @@ var percentage = (value, maxValue, decimalPlaces = 2) => {
90
290
  };
91
291
  var clamp = (num, min, max) => Math.min(Math.max(num, min), max);
92
292
  var formatNumber = (num) => num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
293
+ var sum = (numbers) => numbers.reduce((acc, n) => acc + n, 0);
294
+ var average = (numbers) => numbers.length === 0 ? 0 : sum(numbers) / numbers.length;
295
+ var median = (numbers) => {
296
+ if (numbers.length === 0)
297
+ return 0;
298
+ const sorted = [...numbers].sort((a, b) => a - b);
299
+ const mid = Math.floor(sorted.length / 2);
300
+ return sorted.length % 2 !== 0 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
301
+ };
302
+ var min = (numbers) => numbers.length === 0 ? Infinity : Math.min(...numbers);
303
+ var max = (numbers) => numbers.length === 0 ? -Infinity : Math.max(...numbers);
304
+ var round = (value, decimals = 0) => {
305
+ const factor = Math.pow(10, decimals);
306
+ return Math.round(value * factor) / factor;
307
+ };
93
308
  // src/runtime/media.ts
94
309
  var isSupportedDisplayableMedia = (mime, type = { image: true, video: true }) => type.image && SUPPORTED_DISPLAYABLE_MEDIA_TYPES.images.includes(mime) || type.video && SUPPORTED_DISPLAYABLE_MEDIA_TYPES.videos.includes(mime);
310
+ // src/runtime/normalize.ts
311
+ var normalize = (value) => {
312
+ let normalized;
313
+ switch (typeof value) {
314
+ case "string":
315
+ normalized = value === "" ? null : value;
316
+ break;
317
+ case "number":
318
+ normalized = isNaN(value) ? null : value;
319
+ break;
320
+ case "boolean":
321
+ normalized = value;
322
+ break;
323
+ case "object":
324
+ if (value === null || value === undefined)
325
+ normalized = value ?? null;
326
+ else if (value instanceof Date)
327
+ normalized = isNaN(value.getTime()) ? null : value;
328
+ else if (Array.isArray(value))
329
+ normalized = value.length === 0 ? null : value;
330
+ else if (value instanceof Set)
331
+ normalized = value.size === 0 ? null : value;
332
+ else if (value instanceof Map)
333
+ normalized = value.size === 0 ? null : value;
334
+ else
335
+ normalized = Object.keys(value).length === 0 ? null : value;
336
+ break;
337
+ default:
338
+ normalized = value ?? null;
339
+ }
340
+ return normalized;
341
+ };
342
+ // src/runtime/object.ts
343
+ var pick = (obj, keys) => {
344
+ const result = {};
345
+ for (const key of keys) {
346
+ if (key in obj)
347
+ result[key] = obj[key];
348
+ }
349
+ return result;
350
+ };
351
+ var omit = (obj, keys) => {
352
+ const result = { ...obj };
353
+ for (const key of keys)
354
+ delete result[key];
355
+ return result;
356
+ };
357
+ var merge = (...objects) => {
358
+ const isPlainObject = (val) => val !== null && typeof val === "object" && val.constructor === Object;
359
+ const result = {};
360
+ for (const obj of objects) {
361
+ for (const key of Object.keys(obj)) {
362
+ const val = obj[key];
363
+ if (isPlainObject(val) && isPlainObject(result[key])) {
364
+ result[key] = merge(result[key], val);
365
+ } else {
366
+ result[key] = val;
367
+ }
368
+ }
369
+ }
370
+ return result;
371
+ };
372
+ var flattenObject = (obj, prefix = "") => {
373
+ const result = {};
374
+ for (const key of Object.keys(obj)) {
375
+ const fullKey = prefix ? `${prefix}.${key}` : key;
376
+ const val = obj[key];
377
+ if (val !== null && typeof val === "object" && val.constructor === Object) {
378
+ Object.assign(result, flattenObject(val, fullKey));
379
+ } else {
380
+ result[fullKey] = val;
381
+ }
382
+ }
383
+ return result;
384
+ };
385
+ var unflattenObject = (obj) => {
386
+ const result = {};
387
+ for (const key of Object.keys(obj)) {
388
+ const parts = key.split(".");
389
+ let current = result;
390
+ for (let i = 0;i < parts.length - 1; i++) {
391
+ if (!(parts[i] in current))
392
+ current[parts[i]] = {};
393
+ current = current[parts[i]];
394
+ }
395
+ current[parts[parts.length - 1]] = obj[key];
396
+ }
397
+ return result;
398
+ };
399
+ var isPlainObj = (val) => val !== null && typeof val === "object" && val.constructor === Object;
400
+ var deepEqual = (a, b) => {
401
+ if (a === b)
402
+ return true;
403
+ if (a == null || b == null)
404
+ return false;
405
+ if (typeof a !== typeof b)
406
+ return false;
407
+ if (Array.isArray(a) && Array.isArray(b)) {
408
+ if (a.length !== b.length)
409
+ return false;
410
+ return a.every((v, i) => deepEqual(v, b[i]));
411
+ }
412
+ if (isPlainObj(a) && isPlainObj(b)) {
413
+ const keysA = Object.keys(a);
414
+ const keysB = Object.keys(b);
415
+ if (keysA.length !== keysB.length)
416
+ return false;
417
+ return keysA.every((k) => keysB.includes(k) && deepEqual(a[k], b[k]));
418
+ }
419
+ return false;
420
+ };
421
+ var diff = (a, b) => {
422
+ const result = {};
423
+ const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
424
+ for (const key of allKeys) {
425
+ const valA = a[key];
426
+ const valB = b[key];
427
+ if (deepEqual(valA, valB))
428
+ continue;
429
+ if (isPlainObj(valA) && isPlainObj(valB)) {
430
+ const nested = diff(valA, valB);
431
+ if (Object.keys(nested).length > 0) {
432
+ result[key] = nested;
433
+ }
434
+ } else {
435
+ result[key] = valB;
436
+ }
437
+ }
438
+ return result;
439
+ };
95
440
  // src/runtime/promise.ts
96
441
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
442
+ var retry = async (fn, options) => {
443
+ const { retries = 3, delay = 1000, backoff = false } = options ?? {};
444
+ let lastError;
445
+ for (let attempt = 0;attempt <= retries; attempt++) {
446
+ try {
447
+ return await fn();
448
+ } catch (error) {
449
+ lastError = error;
450
+ if (attempt < retries) {
451
+ const waitTime = backoff ? delay * Math.pow(2, attempt) : delay;
452
+ await sleep(waitTime);
453
+ }
454
+ }
455
+ }
456
+ throw lastError;
457
+ };
458
+ var timeout = (promise, ms, message) => Promise.race([
459
+ promise,
460
+ new Promise((_, reject) => setTimeout(() => reject(new Error(message ?? `Timed out after ${ms}ms`)), ms))
461
+ ]);
97
462
  // src/runtime/regex.ts
98
463
  var isEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
99
464
  var isUrl = (value) => /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/.test(value);
@@ -144,6 +509,48 @@ var toBytes = (size) => {
144
509
  return;
145
510
  }
146
511
  };
512
+ // src/runtime/string.ts
513
+ var reverse = (str) => [...str].reverse().join("");
514
+ var countOccurrences = (str, search) => {
515
+ if (search === "")
516
+ return 0;
517
+ let count = 0;
518
+ let pos = 0;
519
+ while ((pos = str.indexOf(search, pos)) !== -1) {
520
+ count++;
521
+ pos += search.length;
522
+ }
523
+ return count;
524
+ };
525
+ var pad = (str, length, char = " ", direction = "left") => {
526
+ if (str.length >= length)
527
+ return str;
528
+ const padChar = char[0] || " ";
529
+ switch (direction) {
530
+ case "right":
531
+ return str.padEnd(length, padChar);
532
+ case "both": {
533
+ const total = length - str.length;
534
+ const left = Math.floor(total / 2);
535
+ const right = total - left;
536
+ return padChar.repeat(left) + str + padChar.repeat(right);
537
+ }
538
+ default:
539
+ return str.padStart(length, padChar);
540
+ }
541
+ };
542
+ var mask = (str, visibleCount = 4, maskChar = "*") => {
543
+ if (str.length <= visibleCount)
544
+ return str;
545
+ const masked = maskChar[0].repeat(str.length - visibleCount);
546
+ return masked + str.slice(-visibleCount);
547
+ };
548
+ var initials = (name) => name.split(/\s+/).filter(Boolean).map((word) => word[0]).join("").toUpperCase();
549
+ var wordCount = (str) => {
550
+ const words = str.trim().split(/\s+/).filter(Boolean);
551
+ return str.trim() === "" ? 0 : words.length;
552
+ };
553
+ var isBlank = (str) => str.trim() === "";
147
554
  // src/runtime/time.ts
148
555
  var formatTime = (seconds) => {
149
556
  const hours = Math.floor(seconds / 3600);
@@ -159,15 +566,15 @@ var formatTime = (seconds) => {
159
566
  };
160
567
  var formatDuration = (ms) => {
161
568
  const sec = Math.floor(ms / 1000);
162
- const min = Math.floor(sec / 60);
163
- const hrs = Math.floor(min / 60);
569
+ const min2 = Math.floor(sec / 60);
570
+ const hrs = Math.floor(min2 / 60);
164
571
  const days = Math.floor(hrs / 24);
165
572
  if (days > 0)
166
573
  return `${days}d ${hrs % 24}h`;
167
574
  if (hrs > 0)
168
- return `${hrs}h ${min % 60}m`;
169
- if (min > 0)
170
- return `${min}m ${sec % 60}s`;
575
+ return `${hrs}h ${min2 % 60}m`;
576
+ if (min2 > 0)
577
+ return `${min2}m ${sec % 60}s`;
171
578
  return `${sec}s`;
172
579
  };
173
580
  var toSeconds = (time) => {
@@ -223,6 +630,13 @@ var isJSON = (value) => {
223
630
  return false;
224
631
  }
225
632
  };
633
+ var isFunction = (value) => typeof value === "function";
634
+ var isDate = (value) => value instanceof Date && !isNaN(value.getTime());
635
+ var isNumber = (value) => typeof value === "number" && !isNaN(value);
636
+ var isString = (value) => typeof value === "string";
637
+ var isBoolean = (value) => typeof value === "boolean";
638
+ var isNil = (value) => value === null || value === undefined;
639
+ var isRegExp = (value) => value instanceof RegExp;
226
640
  // src/runtime/url.ts
227
641
  var getQueryParams = (urlString) => {
228
642
  const url = new URL(urlString);
@@ -257,8 +671,10 @@ var buildUrl = (baseUrl, path, queryParams) => {
257
671
  return url.toString();
258
672
  };
259
673
  export {
674
+ wordCount,
260
675
  updateQueryParam,
261
676
  uniqueArray,
677
+ unflattenObject,
262
678
  unescapeHTML,
263
679
  truncate,
264
680
  toggleCase,
@@ -271,20 +687,49 @@ export {
271
687
  toConstantCase,
272
688
  toCamelCase,
273
689
  toBytes,
690
+ timeout,
691
+ throttle,
692
+ sum,
274
693
  stripTags,
694
+ splitWords,
695
+ sortBy,
275
696
  slugify,
276
697
  sleep,
698
+ shuffle,
699
+ sample,
700
+ round,
701
+ rgbToHex,
702
+ reverse,
703
+ retry,
277
704
  removeQueryParam,
705
+ range,
278
706
  rand,
707
+ pick,
279
708
  percentage,
709
+ pad,
710
+ once,
711
+ omit,
712
+ normalize,
713
+ noop,
714
+ min,
715
+ merge,
716
+ median,
717
+ max,
718
+ mask,
719
+ lighten,
720
+ last,
280
721
  isUrl,
281
722
  isUUID,
282
723
  isSupportedDisplayableMedia,
724
+ isString,
283
725
  isSlug,
726
+ isRegExp,
284
727
  isPrimitive,
285
728
  isPostalCode,
286
729
  isPhoneNumber,
287
730
  isObject,
731
+ isNumber,
732
+ isNil,
288
733
  isMacAddress,
289
734
  isJSON,
290
735
  isISODate,
@@ -292,23 +737,43 @@ export {
292
737
  isIPv4,
293
738
  isHexColor,
294
739
  isHex,
740
+ isFunction,
741
+ isEqual,
295
742
  isEmpty,
296
743
  isEmail,
297
744
  isDomain,
745
+ isDate,
298
746
  isCreditCard,
747
+ isBoolean,
748
+ isBlank,
299
749
  isBase64,
300
750
  isArray,
751
+ intersection,
752
+ initials,
753
+ hexToRgb,
754
+ hash,
301
755
  hasDuplicates,
756
+ groupBy,
302
757
  getQueryParams,
758
+ generateId,
303
759
  formatTime,
304
760
  formatNumber,
305
761
  formatDuration,
306
762
  formatBytes,
763
+ flattenObject,
307
764
  escapeHTML,
765
+ difference,
766
+ diff,
767
+ debounce,
768
+ darken,
769
+ countOccurrences,
770
+ compact,
308
771
  clone,
309
772
  clamp,
773
+ chunk,
310
774
  catchError,
311
775
  capitalize,
312
776
  buildUrl,
777
+ average,
313
778
  SUPPORTED_DISPLAYABLE_MEDIA_TYPES
314
779
  };