@simplysm/core-common 13.0.76 → 13.0.78

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 (126) hide show
  1. package/README.md +64 -21
  2. package/dist/extensions/arr-ext.d.ts +1 -1
  3. package/dist/extensions/arr-ext.d.ts.map +1 -1
  4. package/dist/extensions/arr-ext.helpers.d.ts +8 -0
  5. package/dist/extensions/arr-ext.helpers.d.ts.map +1 -1
  6. package/dist/extensions/arr-ext.helpers.js +65 -0
  7. package/dist/extensions/arr-ext.helpers.js.map +2 -2
  8. package/dist/extensions/arr-ext.js +16 -124
  9. package/dist/extensions/arr-ext.js.map +2 -2
  10. package/dist/extensions/arr-ext.types.d.ts +40 -32
  11. package/dist/extensions/arr-ext.types.d.ts.map +1 -1
  12. package/dist/extensions/map-ext.js.map +1 -1
  13. package/dist/extensions/set-ext.js.map +1 -1
  14. package/dist/features/event-emitter.d.ts +4 -4
  15. package/dist/features/event-emitter.d.ts.map +1 -1
  16. package/dist/features/event-emitter.js.map +1 -1
  17. package/dist/features/serial-queue.js +2 -2
  18. package/dist/features/serial-queue.js.map +1 -1
  19. package/dist/index.d.ts +13 -13
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +27 -13
  22. package/dist/index.js.map +1 -1
  23. package/dist/types/date-only.js +2 -2
  24. package/dist/types/date-only.js.map +1 -1
  25. package/dist/types/date-time.js +2 -2
  26. package/dist/types/date-time.js.map +1 -1
  27. package/dist/types/time.js +2 -2
  28. package/dist/types/time.js.map +1 -1
  29. package/dist/types/uuid.d.ts +2 -2
  30. package/dist/types/uuid.d.ts.map +1 -1
  31. package/dist/types/uuid.js +1 -1
  32. package/dist/types/uuid.js.map +1 -1
  33. package/dist/utils/bytes.d.ts +10 -10
  34. package/dist/utils/bytes.d.ts.map +1 -1
  35. package/dist/utils/bytes.js +10 -10
  36. package/dist/utils/bytes.js.map +1 -1
  37. package/dist/utils/date-format.d.ts +1 -1
  38. package/dist/utils/date-format.d.ts.map +1 -1
  39. package/dist/utils/date-format.js +2 -2
  40. package/dist/utils/date-format.js.map +1 -1
  41. package/dist/utils/error.d.ts +1 -1
  42. package/dist/utils/error.d.ts.map +1 -1
  43. package/dist/utils/error.js +2 -2
  44. package/dist/utils/error.js.map +1 -1
  45. package/dist/utils/json.d.ts +4 -2
  46. package/dist/utils/json.d.ts.map +1 -1
  47. package/dist/utils/json.js +9 -9
  48. package/dist/utils/json.js.map +1 -1
  49. package/dist/utils/num.d.ts +10 -10
  50. package/dist/utils/num.d.ts.map +1 -1
  51. package/dist/utils/num.js +11 -11
  52. package/dist/utils/num.js.map +1 -1
  53. package/dist/utils/obj.d.ts +40 -40
  54. package/dist/utils/obj.d.ts.map +1 -1
  55. package/dist/utils/obj.js +102 -99
  56. package/dist/utils/obj.js.map +1 -1
  57. package/dist/utils/path.d.ts +3 -3
  58. package/dist/utils/path.d.ts.map +1 -1
  59. package/dist/utils/path.js +6 -6
  60. package/dist/utils/path.js.map +1 -1
  61. package/dist/utils/primitive.d.ts +1 -1
  62. package/dist/utils/primitive.d.ts.map +1 -1
  63. package/dist/utils/primitive.js +2 -2
  64. package/dist/utils/primitive.js.map +1 -1
  65. package/dist/utils/str.d.ts +16 -16
  66. package/dist/utils/str.d.ts.map +1 -1
  67. package/dist/utils/str.js +16 -16
  68. package/dist/utils/str.js.map +1 -1
  69. package/dist/utils/transferable.d.ts +3 -3
  70. package/dist/utils/transferable.d.ts.map +1 -1
  71. package/dist/utils/transferable.js +10 -10
  72. package/dist/utils/transferable.js.map +1 -1
  73. package/dist/utils/wait.d.ts +2 -2
  74. package/dist/utils/wait.d.ts.map +1 -1
  75. package/dist/utils/wait.js +5 -5
  76. package/dist/utils/wait.js.map +1 -1
  77. package/dist/utils/xml.d.ts +2 -2
  78. package/dist/utils/xml.d.ts.map +1 -1
  79. package/dist/utils/xml.js +4 -4
  80. package/dist/utils/xml.js.map +1 -1
  81. package/dist/{zip/sd-zip.d.ts → utils/zip.d.ts} +1 -1
  82. package/dist/utils/zip.d.ts.map +1 -0
  83. package/dist/{zip/sd-zip.js → utils/zip.js} +1 -1
  84. package/dist/{zip/sd-zip.js.map → utils/zip.js.map} +1 -1
  85. package/package.json +1 -1
  86. package/src/extensions/arr-ext.helpers.ts +86 -0
  87. package/src/extensions/arr-ext.ts +22 -170
  88. package/src/extensions/arr-ext.types.ts +76 -48
  89. package/src/extensions/map-ext.ts +3 -3
  90. package/src/extensions/set-ext.ts +2 -2
  91. package/src/features/event-emitter.ts +6 -6
  92. package/src/features/serial-queue.ts +2 -2
  93. package/src/index.ts +16 -16
  94. package/src/types/date-only.ts +2 -2
  95. package/src/types/date-time.ts +2 -2
  96. package/src/types/time.ts +2 -2
  97. package/src/types/uuid.ts +2 -2
  98. package/src/utils/bytes.ts +15 -15
  99. package/src/utils/date-format.ts +1 -1
  100. package/src/utils/error.ts +1 -1
  101. package/src/utils/json.ts +9 -7
  102. package/src/utils/num.ts +15 -15
  103. package/src/utils/obj.ts +119 -116
  104. package/src/utils/path.ts +3 -3
  105. package/src/utils/primitive.ts +1 -1
  106. package/src/utils/str.ts +16 -16
  107. package/src/utils/transferable.ts +9 -9
  108. package/src/utils/wait.ts +3 -3
  109. package/src/utils/xml.ts +2 -2
  110. package/tests/extensions/array-extension.spec.ts +7 -5
  111. package/tests/types/uuid.spec.ts +4 -4
  112. package/tests/utils/bytes-utils.spec.ts +42 -49
  113. package/tests/utils/date-format.spec.ts +89 -88
  114. package/tests/utils/debounce-queue.spec.ts +3 -1
  115. package/tests/utils/json.spec.ts +61 -68
  116. package/tests/utils/number.spec.ts +41 -46
  117. package/tests/utils/object.spec.ts +120 -139
  118. package/tests/utils/path.spec.ts +19 -19
  119. package/tests/utils/primitive.spec.ts +12 -12
  120. package/tests/utils/string.spec.ts +66 -74
  121. package/tests/utils/transferable.spec.ts +55 -62
  122. package/tests/utils/wait.spec.ts +10 -10
  123. package/tests/utils/xml.spec.ts +25 -25
  124. package/dist/zip/sd-zip.d.ts.map +0 -1
  125. /package/src/{zip/sd-zip.ts → utils/zip.ts} +0 -0
  126. /package/tests/{zip/sd-zip.spec.ts → utils/zip.spec.ts} +0 -0
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import "./map-ext";
8
- import { objClone, objEqual, objMerge } from "../utils/obj";
8
+ import { clone, equal, merge } from "../utils/obj";
9
9
  import type { PrimitiveTypeStr, Type } from "../common.types";
10
10
  import { DateTime } from "../types/date-time";
11
11
  import { DateOnly } from "../types/date-only";
@@ -13,12 +13,12 @@ import { Time } from "../types/time";
13
13
  import { Uuid } from "../types/uuid";
14
14
  import { ArgumentError } from "../errors/argument-error";
15
15
  import { SdError } from "../errors/sd-error";
16
- import { compareForOrder } from "./arr-ext.helpers";
16
+ import { compareForOrder, getDistinctIndices } from "./arr-ext.helpers";
17
17
  import type {
18
18
  ReadonlyArrayExt,
19
19
  MutableArrayExt,
20
20
  ArrayDiffsResult,
21
- ArrayDiffs2Result,
21
+ ArrayOneWayDiffResult,
22
22
  TreeArray,
23
23
  } from "./arr-ext.types";
24
24
 
@@ -155,7 +155,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
155
155
  }
156
156
 
157
157
  // Object keys use the existing approach O(n²)
158
- const existsRecord = result.find((item) => objEqual(item.key, keyObj));
158
+ const existsRecord = result.find((item) => equal(item.key, keyObj));
159
159
  if (existsRecord !== undefined) {
160
160
  existsRecord.values.push(valueObj);
161
161
  } else {
@@ -298,7 +298,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
298
298
 
299
299
  const fn = (items: T[]): TreeArray<T>[] => {
300
300
  return items.map((item) => ({
301
- ...objClone(item),
301
+ ...clone(item),
302
302
  children: fn(childrenMap.get(item[key]) ?? []),
303
303
  }));
304
304
  };
@@ -310,66 +310,8 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
310
310
  distinct<T>(
311
311
  options?: boolean | { matchAddress?: boolean; keyFn?: (item: T) => string | number },
312
312
  ): T[] {
313
- // Normalize options
314
- const opts = typeof options === "boolean" ? { matchAddress: options } : (options ?? {});
315
-
316
- // matchAddress: Set-based O(n)
317
- if (opts.matchAddress === true) return [...new Set(this)];
318
-
319
- // keyFn provided: custom key-based O(n)
320
- if (opts.keyFn) {
321
- const seen = new Set<string | number>();
322
- const result: T[] = [];
323
- for (const item of this) {
324
- const key = opts.keyFn(item);
325
- if (!seen.has(key)) {
326
- seen.add(key);
327
- result.push(item);
328
- }
329
- }
330
- return result;
331
- }
332
-
333
- // Default: type-based processing
334
- const seen = new Map<string, T>();
335
- const seenRefs = new Set<symbol | ((...args: unknown[]) => unknown)>(); // O(n) processing for symbol/function
336
- const result: T[] = [];
337
-
338
- for (const item of this) {
339
- // primitive types take the fast path
340
- if (item === null || typeof item !== "object") {
341
- const type = typeof item;
342
-
343
- // symbol, function use Set for identity comparison (O(n))
344
- if (type === "symbol" || type === "function") {
345
- if (!seenRefs.has(item)) {
346
- seenRefs.add(item);
347
- result.push(item);
348
- }
349
- continue;
350
- }
351
-
352
- // Other primitives: type prefix + special case handling
353
- let key = type + ":";
354
- if (Object.is(item, -0)) {
355
- key += "-0";
356
- } else {
357
- key += String(item);
358
- }
359
-
360
- if (!seen.has(key)) {
361
- seen.set(key, item);
362
- result.push(item);
363
- }
364
- continue;
365
- }
366
-
367
- if (!result.some((item1) => objEqual(item1, item))) {
368
- result.push(item);
369
- }
370
- }
371
-
372
- return result;
313
+ const keptIndices = getDistinctIndices(this, options);
314
+ return Array.from(keptIndices).map((i) => this[i]);
373
315
  },
374
316
 
375
317
  orderBy<T>(
@@ -432,7 +374,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
432
374
  // Skip already matched items using Set-based skipping (avoid O(n) splice removal)
433
375
  for (const targetItem of uncheckedTarget) {
434
376
  if (!uncheckedTargetSet.has(targetItem)) continue;
435
- if (objEqual(targetItem, sourceItem, excludeOpts)) {
377
+ if (equal(targetItem, sourceItem, excludeOpts)) {
436
378
  sameTarget = targetItem;
437
379
  break;
438
380
  }
@@ -475,7 +417,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
475
417
  excludes?: string[];
476
418
  includes?: string[];
477
419
  },
478
- ): ArrayDiffs2Result<T>[] {
420
+ ): ArrayOneWayDiffResult<T>[] {
479
421
  const orgItemMap =
480
422
  orgItems instanceof Map
481
423
  ? orgItems
@@ -486,7 +428,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
486
428
  );
487
429
  const includeSame = options?.includeSame ?? false;
488
430
 
489
- const diffs: ArrayDiffs2Result<T>[] = [];
431
+ const diffs: ArrayOneWayDiffResult<T>[] = [];
490
432
  for (const item of this) {
491
433
  const keyValue =
492
434
  typeof keyPropNameOrGetValFn === "function"
@@ -504,7 +446,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
504
446
  }
505
447
 
506
448
  if (
507
- objEqual(orgItem, item, {
449
+ equal(orgItem, item, {
508
450
  topLevelExcludes: options?.excludes,
509
451
  topLevelIncludes: options?.includes,
510
452
  })
@@ -527,9 +469,9 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
527
469
  excludes?: string[];
528
470
  },
529
471
  ): (T | P | (T & P))[] {
530
- const diffs = this.diffs(target, options);
472
+ const diffs = this.diffs(target as any[], options as any);
531
473
 
532
- const result: (T | P | (T & P))[] = objClone(this);
474
+ const result: (T | P | (T & P))[] = clone(this);
533
475
 
534
476
  // Pre-calculate original index of source items to improve O(n) lookup to O(1)
535
477
  const sourceIndexMap = new Map<T, number>();
@@ -544,7 +486,7 @@ const arrayReadonlyExtensions: ReadonlyArrayExt<any> & ThisType<any[]> = {
544
486
  if (sourceIndex === undefined) {
545
487
  throw new SdError("Unexpected error: source item not found in merge.");
546
488
  }
547
- result[sourceIndex] = objMerge(diff.source, diff.target);
489
+ result[sourceIndex] = merge(diff.source, diff.target);
548
490
  }
549
491
  // When adding
550
492
  else if (diff.target !== undefined) {
@@ -622,107 +564,17 @@ const arrayMutableExtensions: MutableArrayExt<any> & ThisType<any[]> = {
622
564
  distinctThis<T>(
623
565
  options?: boolean | { matchAddress?: boolean; keyFn?: (item: T) => string | number },
624
566
  ): T[] {
625
- // Normalize options
626
- const opts = typeof options === "boolean" ? { matchAddress: options } : (options ?? {});
627
-
628
- // matchAddress: Set-based O(n)
629
- // To preserve first occurrence, collect indices to remove after forward traversal
630
- if (opts.matchAddress === true) {
631
- const seen = new Set<T>();
632
- const toRemove: number[] = [];
633
- for (let i = 0; i < this.length; i++) {
634
- if (seen.has(this[i])) {
635
- toRemove.push(i);
636
- } else {
637
- seen.add(this[i]);
638
- }
639
- }
640
- // Remove in reverse order (prevent index changes)
641
- for (let i = toRemove.length - 1; i >= 0; i--) {
642
- this.splice(toRemove[i], 1);
643
- }
644
- return this;
645
- }
646
-
647
- // keyFn provided: custom key-based O(n)
648
- // To preserve first occurrence, collect indices to remove after forward traversal
649
- if (opts.keyFn) {
650
- const seen = new Set<string | number>();
651
- const toRemove: number[] = [];
652
- for (let i = 0; i < this.length; i++) {
653
- const key = opts.keyFn(this[i]);
654
- if (seen.has(key)) {
655
- toRemove.push(i);
656
- } else {
657
- seen.add(key);
658
- }
659
- }
660
- // Remove in reverse order (prevent index changes)
661
- for (let i = toRemove.length - 1; i >= 0; i--) {
662
- this.splice(toRemove[i], 1);
663
- }
664
- return this;
665
- }
666
-
667
- // Default: type-based processing (primitive optimization)
668
- const seen = new Map<string, T>();
669
- const seenRefs = new Set<symbol | ((...args: unknown[]) => unknown)>();
670
- const toRemoveSet = new Set<number>();
671
-
567
+ const keptIndices = getDistinctIndices(this, options);
568
+ const indicesToRemove = [];
672
569
  for (let i = 0; i < this.length; i++) {
673
- const item = this[i];
674
-
675
- // primitive types take the fast path O(n)
676
- if (item === null || typeof item !== "object") {
677
- const type = typeof item;
678
-
679
- // symbol, function use Set for identity comparison
680
- if (type === "symbol" || type === "function") {
681
- if (seenRefs.has(item)) {
682
- toRemoveSet.add(i);
683
- } else {
684
- seenRefs.add(item);
685
- }
686
- continue;
687
- }
688
-
689
- // Other primitives: type prefix + special case handling
690
- let key = type + ":";
691
- if (Object.is(item, -0)) {
692
- key += "-0";
693
- } else {
694
- key += String(item);
695
- }
696
-
697
- if (seen.has(key)) {
698
- toRemoveSet.add(i);
699
- } else {
700
- seen.set(key, item);
701
- }
702
- continue;
703
- }
704
-
705
- // Objects: deep comparison O(n²) - compare with previous non-removed items
706
- let hasDuplicateBefore = false;
707
- for (let j = 0; j < i; j++) {
708
- // Skip indices in toRemoveSet (O(1) lookup)
709
- if (toRemoveSet.has(j)) continue;
710
- if (objEqual(this[j], item)) {
711
- hasDuplicateBefore = true;
712
- break;
713
- }
714
- }
715
- if (hasDuplicateBefore) {
716
- toRemoveSet.add(i);
570
+ if (!keptIndices.has(i)) {
571
+ indicesToRemove.push(i);
717
572
  }
718
573
  }
719
-
720
- // Remove in reverse order (prevent index changes)
721
- const toRemoveArr = Array.from(toRemoveSet).sort((a, b) => b - a);
722
- for (const idx of toRemoveArr) {
723
- this.splice(idx, 1);
574
+ // Remove in reverse order to prevent index shifts
575
+ for (let i = indicesToRemove.length - 1; i >= 0; i--) {
576
+ this.splice(indicesToRemove[i], 1);
724
577
  }
725
-
726
578
  return this;
727
579
  },
728
580
 
@@ -806,7 +658,7 @@ declare global {
806
658
 
807
659
  export type {
808
660
  ArrayDiffsResult,
809
- ArrayDiffs2Result,
661
+ ArrayOneWayDiffResult,
810
662
  TreeArray,
811
663
  ComparableType,
812
664
  } from "./arr-ext.types";
@@ -39,33 +39,33 @@ export interface ReadonlyArrayExt<TItem> {
39
39
  filterExists(): NonNullable<TItem>[];
40
40
 
41
41
  /** Filter only elements of specific type (PrimitiveTypeStr or constructor type) */
42
- ofType<K extends PrimitiveTypeStr>(type: K): Extract<TItem, PrimitiveTypeMap[K]>[];
43
- ofType<N extends TItem>(type: Type<N>): N[];
42
+ ofType<TKey extends PrimitiveTypeStr>(type: TKey): Extract<TItem, PrimitiveTypeMap[TKey]>[];
43
+ ofType<TNarrow extends TItem>(type: Type<TNarrow>): TNarrow[];
44
44
 
45
45
  /** Async mapping (sequential execution) */
46
- mapAsync<R>(selector: (item: TItem, index: number) => Promise<R>): Promise<R[]>;
46
+ mapAsync<TResult>(selector: (item: TItem, index: number) => Promise<TResult>): Promise<TResult[]>;
47
47
 
48
48
  /** Flatten nested array */
49
49
  mapMany(): TItem extends readonly (infer U)[] ? U[] : TItem;
50
50
 
51
51
  /** Map and then flatten */
52
- mapMany<R>(selector: (item: TItem, index: number) => R[]): R[];
52
+ mapMany<TResult>(selector: (item: TItem, index: number) => TResult[]): TResult[];
53
53
 
54
54
  /** Async mapping and then flatten (sequential execution) */
55
- mapManyAsync<R>(selector: (item: TItem, index: number) => Promise<R[]>): Promise<R[]>;
55
+ mapManyAsync<TResult>(selector: (item: TItem, index: number) => Promise<TResult[]>): Promise<TResult[]>;
56
56
 
57
57
  /**
58
58
  * Async parallel processing (using Promise.all)
59
59
  * @note If any rejects, entire operation fail-fast rejects (Promise.all behavior)
60
60
  */
61
- parallelAsync<R>(fn: (item: TItem, index: number) => Promise<R>): Promise<R[]>;
61
+ parallelAsync<TResult>(fn: (item: TItem, index: number) => Promise<TResult>): Promise<TResult[]>;
62
62
 
63
63
  /**
64
64
  * Group by key
65
65
  * @param keySelector Key selection function for group
66
66
  * @note O(n²) complexity (deep comparison for object key support). If only primitive keys are needed, toArrayMap() is more efficient at O(n)
67
67
  */
68
- groupBy<K>(keySelector: (item: TItem, index: number) => K): { key: K; values: TItem[] }[];
68
+ groupBy<TKey>(keySelector: (item: TItem, index: number) => TKey): { key: TKey; values: TItem[] }[];
69
69
 
70
70
  /**
71
71
  * Group by key (with value transformation)
@@ -73,52 +73,52 @@ export interface ReadonlyArrayExt<TItem> {
73
73
  * @param valueSelector Value transformation function
74
74
  * @note O(n²) complexity (deep comparison for object key support). If only primitive keys are needed, toArrayMap() is more efficient at O(n)
75
75
  */
76
- groupBy<K, V>(
77
- keySelector: (item: TItem, index: number) => K,
78
- valueSelector: (item: TItem, index: number) => V,
76
+ groupBy<TKey, TValue>(
77
+ keySelector: (item: TItem, index: number) => TKey,
78
+ valueSelector: (item: TItem, index: number) => TValue,
79
79
  ): {
80
- key: K;
81
- values: V[];
80
+ key: TKey;
81
+ values: TValue[];
82
82
  }[];
83
83
 
84
- toMap<K>(keySelector: (item: TItem, index: number) => K): Map<K, TItem>;
84
+ toMap<TKey>(keySelector: (item: TItem, index: number) => TKey): Map<TKey, TItem>;
85
85
 
86
- toMap<K, V>(
87
- keySelector: (item: TItem, index: number) => K,
88
- valueSelector: (item: TItem, index: number) => V,
89
- ): Map<K, V>;
86
+ toMap<TKey, TValue>(
87
+ keySelector: (item: TItem, index: number) => TKey,
88
+ valueSelector: (item: TItem, index: number) => TValue,
89
+ ): Map<TKey, TValue>;
90
90
 
91
- toMapAsync<K>(keySelector: (item: TItem, index: number) => Promise<K>): Promise<Map<K, TItem>>;
91
+ toMapAsync<TKey>(keySelector: (item: TItem, index: number) => Promise<TKey>): Promise<Map<TKey, TItem>>;
92
92
 
93
- toMapAsync<K, V>(
94
- keySelector: (item: TItem, index: number) => Promise<K> | K,
95
- valueSelector: (item: TItem, index: number) => Promise<V> | V,
96
- ): Promise<Map<K, V>>;
93
+ toMapAsync<TKey, TValue>(
94
+ keySelector: (item: TItem, index: number) => Promise<TKey> | TKey,
95
+ valueSelector: (item: TItem, index: number) => Promise<TValue> | TValue,
96
+ ): Promise<Map<TKey, TValue>>;
97
97
 
98
- toArrayMap<K>(keySelector: (item: TItem, index: number) => K): Map<K, TItem[]>;
98
+ toArrayMap<TKey>(keySelector: (item: TItem, index: number) => TKey): Map<TKey, TItem[]>;
99
99
 
100
- toArrayMap<K, V>(
101
- keySelector: (item: TItem, index: number) => K,
102
- valueSelector: (item: TItem, index: number) => V,
103
- ): Map<K, V[]>;
100
+ toArrayMap<TKey, TValue>(
101
+ keySelector: (item: TItem, index: number) => TKey,
102
+ valueSelector: (item: TItem, index: number) => TValue,
103
+ ): Map<TKey, TValue[]>;
104
104
 
105
- toSetMap<K>(keySelector: (item: TItem, index: number) => K): Map<K, Set<TItem>>;
106
- toSetMap<K, V>(
107
- keySelector: (item: TItem, index: number) => K,
108
- valueSelector: (item: TItem, index: number) => V,
109
- ): Map<K, Set<V>>;
105
+ toSetMap<TKey>(keySelector: (item: TItem, index: number) => TKey): Map<TKey, Set<TItem>>;
106
+ toSetMap<TKey, TValue>(
107
+ keySelector: (item: TItem, index: number) => TKey,
108
+ valueSelector: (item: TItem, index: number) => TValue,
109
+ ): Map<TKey, Set<TValue>>;
110
110
 
111
- toMapValues<K, V>(
112
- keySelector: (item: TItem, index: number) => K,
113
- valueSelector: (items: TItem[]) => V,
114
- ): Map<K, V>;
111
+ toMapValues<TKey, TValue>(
112
+ keySelector: (item: TItem, index: number) => TKey,
113
+ valueSelector: (items: TItem[]) => TValue,
114
+ ): Map<TKey, TValue>;
115
115
 
116
116
  toObject(keySelector: (item: TItem, index: number) => string): Record<string, TItem>;
117
117
 
118
- toObject<V>(
118
+ toObject<TValue>(
119
119
  keySelector: (item: TItem, index: number) => string,
120
- valueSelector: (item: TItem, index: number) => V,
121
- ): Record<string, V>;
120
+ valueSelector: (item: TItem, index: number) => TValue,
121
+ ): Record<string, TValue>;
122
122
 
123
123
  /**
124
124
  * Convert flat array to tree structure
@@ -186,22 +186,50 @@ export interface ReadonlyArrayExt<TItem> {
186
186
  */
187
187
  diffs<TOtherItem>(
188
188
  target: TOtherItem[],
189
- options?: { keys?: string[]; excludes?: string[] },
190
189
  ): ArrayDiffsResult<TItem, TOtherItem>[];
191
190
 
192
- oneWayDiffs<K extends keyof TItem>(
193
- orgItems: TItem[] | Map<TItem[K], TItem>,
194
- keyPropNameOrGetValFn: K | ((item: TItem) => string | number | undefined),
191
+ diffs<TOtherItem extends Record<string, unknown> = Record<string, unknown>>(
192
+ target: TOtherItem[],
193
+ options: {
194
+ keys: ((keyof TItem | keyof TOtherItem) & string)[];
195
+ excludes?: ((keyof TItem | keyof TOtherItem) & string)[];
196
+ },
197
+ ): ArrayDiffsResult<TItem, TOtherItem>[];
198
+
199
+ diffs<TOtherItem extends Record<string, unknown> = Record<string, unknown>>(
200
+ target: TOtherItem[],
201
+ options: {
202
+ excludes: ((keyof TItem | keyof TOtherItem) & string)[];
203
+ },
204
+ ): ArrayDiffsResult<TItem, TOtherItem>[];
205
+
206
+ oneWayDiffs<TKey extends keyof TItem>(
207
+ orgItems: TItem[] | Map<TItem[TKey], TItem>,
208
+ keyPropNameOrGetValFn: TKey | ((item: TItem) => string | number | undefined),
195
209
  options?: {
196
210
  includeSame?: boolean;
197
211
  excludes?: string[];
198
212
  includes?: string[];
199
213
  },
200
- ): ArrayDiffs2Result<TItem>[];
214
+ ): ArrayOneWayDiffResult<TItem>[];
201
215
 
202
216
  merge<TOtherItem>(
203
217
  target: TOtherItem[],
204
- options?: { keys?: string[]; excludes?: string[] },
218
+ ): (TItem | TOtherItem | (TItem & TOtherItem))[];
219
+
220
+ merge<TOtherItem extends Record<string, unknown> = Record<string, unknown>>(
221
+ target: TOtherItem[],
222
+ options: {
223
+ keys: ((keyof TItem | keyof TOtherItem) & string)[];
224
+ excludes?: ((keyof TItem | keyof TOtherItem) & string)[];
225
+ },
226
+ ): (TItem | TOtherItem | (TItem & TOtherItem))[];
227
+
228
+ merge<TOtherItem extends Record<string, unknown> = Record<string, unknown>>(
229
+ target: TOtherItem[],
230
+ options: {
231
+ excludes: ((keyof TItem | keyof TOtherItem) & string)[];
232
+ },
205
233
  ): (TItem | TOtherItem | (TItem & TOtherItem))[];
206
234
 
207
235
  /**
@@ -213,11 +241,11 @@ export interface ReadonlyArrayExt<TItem> {
213
241
 
214
242
  min(): TItem extends number | string ? TItem | undefined : never;
215
243
 
216
- min<P extends number | string>(selector?: (item: TItem, index: number) => P): P | undefined;
244
+ min<TProp extends number | string>(selector?: (item: TItem, index: number) => TProp): TProp | undefined;
217
245
 
218
246
  max(): TItem extends number | string ? TItem | undefined : never;
219
247
 
220
- max<P extends number | string>(selector?: (item: TItem, index: number) => P): P | undefined;
248
+ max<TProp extends number | string>(selector?: (item: TItem, index: number) => TProp): TProp | undefined;
221
249
 
222
250
  shuffle(): TItem[];
223
251
  }
@@ -272,7 +300,7 @@ export type ArrayDiffsResult<TOriginal, TOther> =
272
300
  | { source: TOriginal; target: undefined } // DELETE
273
301
  | { source: TOriginal; target: TOther }; // UPDATE
274
302
 
275
- export type ArrayDiffs2Result<TItem> =
303
+ export type ArrayOneWayDiffResult<TItem> =
276
304
  | { type: "create"; item: TItem; orgItem: undefined }
277
305
  | { type: "update"; item: TItem; orgItem: TItem }
278
306
  | { type: "same"; item: TItem; orgItem: TItem };
@@ -57,10 +57,10 @@ declare global {
57
57
  }
58
58
 
59
59
  Object.defineProperty(Map.prototype, "getOrCreate", {
60
- value: function <K, V>(this: Map<K, V>, key: K, newValue: V | (() => V)): V {
60
+ value: function <TKey, TValue>(this: Map<TKey, TValue>, key: TKey, newValue: TValue | (() => TValue)): TValue {
61
61
  if (!this.has(key)) {
62
62
  if (typeof newValue === "function") {
63
- this.set(key, (newValue as () => V)());
63
+ this.set(key, (newValue as () => TValue)());
64
64
  } else {
65
65
  this.set(key, newValue);
66
66
  }
@@ -73,7 +73,7 @@ Object.defineProperty(Map.prototype, "getOrCreate", {
73
73
  });
74
74
 
75
75
  Object.defineProperty(Map.prototype, "update", {
76
- value: function <K, V>(this: Map<K, V>, key: K, updateFn: (v: V | undefined) => V): void {
76
+ value: function <TKey, TValue>(this: Map<TKey, TValue>, key: TKey, updateFn: (v: TValue | undefined) => TValue): void {
77
77
  const val = this.get(key);
78
78
  const res = updateFn(val);
79
79
  this.set(key, res);
@@ -36,7 +36,7 @@ declare global {
36
36
  }
37
37
 
38
38
  Object.defineProperty(Set.prototype, "adds", {
39
- value: function <T>(this: Set<T>, ...values: T[]): Set<T> {
39
+ value: function <TItem>(this: Set<TItem>, ...values: TItem[]): Set<TItem> {
40
40
  for (const val of values) {
41
41
  this.add(val);
42
42
  }
@@ -48,7 +48,7 @@ Object.defineProperty(Set.prototype, "adds", {
48
48
  });
49
49
 
50
50
  Object.defineProperty(Set.prototype, "toggle", {
51
- value: function <T>(this: Set<T>, value: T, addOrDel?: "add" | "del"): Set<T> {
51
+ value: function <TItem>(this: Set<TItem>, value: TItem, addOrDel?: "add" | "del"): Set<TItem> {
52
52
  if (addOrDel === "add") {
53
53
  this.add(value);
54
54
  } else if (addOrDel === "del") {
@@ -35,7 +35,7 @@ export class EventEmitter<
35
35
  * @param listener Event handler
36
36
  * @note Duplicate registration of the same listener to the same event is ignored
37
37
  */
38
- on<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void {
38
+ on<TEventName extends keyof TEvents & string>(type: TEventName, listener: (data: TEvents[TEventName]) => void): void {
39
39
  // Get or create map for event type
40
40
  let typeMap = this._listenerMap.get(type);
41
41
  if (!typeMap) {
@@ -57,7 +57,7 @@ export class EventEmitter<
57
57
  * @param type Event type
58
58
  * @param listener Event handler to remove
59
59
  */
60
- off<K extends keyof TEvents & string>(type: K, listener: (data: TEvents[K]) => void): void {
60
+ off<TEventName extends keyof TEvents & string>(type: TEventName, listener: (data: TEvents[TEventName]) => void): void {
61
61
  const typeMap = this._listenerMap.get(type);
62
62
  if (!typeMap) return;
63
63
 
@@ -79,9 +79,9 @@ export class EventEmitter<
79
79
  * @param type Event type
80
80
  * @param args Event data (omitted if void type)
81
81
  */
82
- emit<K extends keyof TEvents & string>(
83
- type: K,
84
- ...args: TEvents[K] extends void ? [] : [data: TEvents[K]]
82
+ emit<TEventName extends keyof TEvents & string>(
83
+ type: TEventName,
84
+ ...args: TEvents[TEventName] extends void ? [] : [data: TEvents[TEventName]]
85
85
  ): void {
86
86
  this._target.dispatchEvent(new CustomEvent(type, { detail: args[0] }));
87
87
  }
@@ -92,7 +92,7 @@ export class EventEmitter<
92
92
  * @param type Event type
93
93
  * @returns Number of registered listeners
94
94
  */
95
- listenerCount<K extends keyof TEvents & string>(type: K): number {
95
+ listenerCount<TEventName extends keyof TEvents & string>(type: TEventName): number {
96
96
  return this._listenerMap.get(type)?.size ?? 0;
97
97
  }
98
98
 
@@ -18,7 +18,7 @@
18
18
  import { SdError } from "../errors/sd-error";
19
19
  import { EventEmitter } from "./event-emitter";
20
20
  import consola from "consola";
21
- import { waitTime } from "../utils/wait";
21
+ import { time } from "../utils/wait";
22
22
 
23
23
  interface SerialQueueEvents {
24
24
  error: SdError;
@@ -84,7 +84,7 @@ export class SerialQueue extends EventEmitter<SerialQueueEvents> {
84
84
  }
85
85
 
86
86
  if (this._gap > 0 && this._queue.length > 0) {
87
- await waitTime(this._gap);
87
+ await time(this._gap);
88
88
  }
89
89
  }
90
90
  } finally {
package/src/index.ts CHANGED
@@ -31,24 +31,24 @@ export * from "./features/serial-queue";
31
31
  export * from "./features/event-emitter";
32
32
  //#endregion
33
33
 
34
- //#region utils
35
- export * from "./utils/date-format";
36
- export * from "./utils/bytes";
37
- export * from "./utils/json";
38
- export * from "./utils/num";
39
- export * from "./utils/obj";
40
- export * from "./utils/primitive";
41
- export * from "./utils/str";
42
- export * from "./utils/template-strings";
43
- export * from "./utils/transferable";
44
- export * from "./utils/wait";
45
- export * from "./utils/xml";
46
- export * from "./utils/path";
47
- export * from "./utils/error";
34
+ //#region utils (namespace exports)
35
+ export * as obj from "./utils/obj";
36
+ export * as str from "./utils/str";
37
+ export * as num from "./utils/num";
38
+ export * as bytes from "./utils/bytes";
39
+ export * as path from "./utils/path";
40
+ export * as json from "./utils/json";
41
+ export * as xml from "./utils/xml";
42
+ export * as wait from "./utils/wait";
43
+ export * as transfer from "./utils/transferable";
44
+ export * as err from "./utils/error";
45
+ export * as dt from "./utils/date-format";
46
+ export * as primitive from "./utils/primitive";
48
47
  //#endregion
49
48
 
50
- //#region zip
51
- export * from "./zip/sd-zip";
49
+ //#region utils (direct exports)
50
+ export * from "./utils/template-strings";
51
+ export * from "./utils/zip";
52
52
  //#endregion
53
53
 
54
54
  //#region type utilities
@@ -1,5 +1,5 @@
1
1
  import { ArgumentError } from "../errors/argument-error";
2
- import { formatDate, normalizeMonth } from "../utils/date-format";
2
+ import { format, normalizeMonth } from "../utils/date-format";
3
3
 
4
4
  /**
5
5
  * Date class (without time: yyyy-MM-dd, immutable)
@@ -327,7 +327,7 @@ export class DateOnly {
327
327
  * @see dtFormat for supported format strings
328
328
  */
329
329
  toFormatString(formatStr: string): string {
330
- return formatDate(formatStr, {
330
+ return format(formatStr, {
331
331
  year: this.year,
332
332
  month: this.month,
333
333
  day: this.day,
@@ -1,5 +1,5 @@
1
1
  import { ArgumentError } from "../errors/argument-error";
2
- import { convert12To24, formatDate, normalizeMonth } from "../utils/date-format";
2
+ import { convert12To24, format, normalizeMonth } from "../utils/date-format";
3
3
 
4
4
  /**
5
5
  * DateTime class (immutable)
@@ -355,7 +355,7 @@ export class DateTime {
355
355
  * @see dtFormat for supported format strings
356
356
  */
357
357
  toFormatString(formatStr: string): string {
358
- return formatDate(formatStr, {
358
+ return format(formatStr, {
359
359
  year: this.year,
360
360
  month: this.month,
361
361
  day: this.day,