ts-data-forge 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.
Files changed (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +534 -0
  3. package/package.json +101 -0
  4. package/src/array/array-utils-creation.test.mts +443 -0
  5. package/src/array/array-utils-modification.test.mts +197 -0
  6. package/src/array/array-utils-overload-type-error.test.mts +149 -0
  7. package/src/array/array-utils-reducing-value.test.mts +425 -0
  8. package/src/array/array-utils-search.test.mts +169 -0
  9. package/src/array/array-utils-set-op.test.mts +335 -0
  10. package/src/array/array-utils-slice-clamped.test.mts +113 -0
  11. package/src/array/array-utils-slicing.test.mts +316 -0
  12. package/src/array/array-utils-transformation.test.mts +790 -0
  13. package/src/array/array-utils-validation.test.mts +492 -0
  14. package/src/array/array-utils.mts +4000 -0
  15. package/src/array/array.test.mts +146 -0
  16. package/src/array/index.mts +2 -0
  17. package/src/array/tuple-utils.mts +519 -0
  18. package/src/array/tuple-utils.test.mts +518 -0
  19. package/src/collections/imap-mapped.mts +801 -0
  20. package/src/collections/imap-mapped.test.mts +860 -0
  21. package/src/collections/imap.mts +651 -0
  22. package/src/collections/imap.test.mts +932 -0
  23. package/src/collections/index.mts +6 -0
  24. package/src/collections/iset-mapped.mts +889 -0
  25. package/src/collections/iset-mapped.test.mts +1187 -0
  26. package/src/collections/iset.mts +682 -0
  27. package/src/collections/iset.test.mts +1084 -0
  28. package/src/collections/queue.mts +390 -0
  29. package/src/collections/queue.test.mts +282 -0
  30. package/src/collections/stack.mts +423 -0
  31. package/src/collections/stack.test.mts +225 -0
  32. package/src/expect-type.mts +206 -0
  33. package/src/functional/index.mts +4 -0
  34. package/src/functional/match.mts +300 -0
  35. package/src/functional/match.test.mts +177 -0
  36. package/src/functional/optional.mts +733 -0
  37. package/src/functional/optional.test.mts +619 -0
  38. package/src/functional/pipe.mts +212 -0
  39. package/src/functional/pipe.test.mts +85 -0
  40. package/src/functional/result.mts +1134 -0
  41. package/src/functional/result.test.mts +777 -0
  42. package/src/globals.d.mts +38 -0
  43. package/src/guard/has-key.mts +119 -0
  44. package/src/guard/has-key.test.mts +219 -0
  45. package/src/guard/index.mts +7 -0
  46. package/src/guard/is-non-empty-string.mts +108 -0
  47. package/src/guard/is-non-empty-string.test.mts +91 -0
  48. package/src/guard/is-non-null-object.mts +106 -0
  49. package/src/guard/is-non-null-object.test.mts +90 -0
  50. package/src/guard/is-primitive.mts +165 -0
  51. package/src/guard/is-primitive.test.mts +102 -0
  52. package/src/guard/is-record.mts +153 -0
  53. package/src/guard/is-record.test.mts +112 -0
  54. package/src/guard/is-type.mts +450 -0
  55. package/src/guard/is-type.test.mts +496 -0
  56. package/src/guard/key-is-in.mts +163 -0
  57. package/src/guard/key-is-in.test.mts +19 -0
  58. package/src/index.mts +10 -0
  59. package/src/iterator/index.mts +1 -0
  60. package/src/iterator/range.mts +120 -0
  61. package/src/iterator/range.test.mts +33 -0
  62. package/src/json/index.mts +1 -0
  63. package/src/json/json.mts +711 -0
  64. package/src/json/json.test.mts +628 -0
  65. package/src/number/branded-types/finite-number.mts +354 -0
  66. package/src/number/branded-types/finite-number.test.mts +135 -0
  67. package/src/number/branded-types/index.mts +26 -0
  68. package/src/number/branded-types/int.mts +278 -0
  69. package/src/number/branded-types/int.test.mts +140 -0
  70. package/src/number/branded-types/int16.mts +192 -0
  71. package/src/number/branded-types/int16.test.mts +170 -0
  72. package/src/number/branded-types/int32.mts +193 -0
  73. package/src/number/branded-types/int32.test.mts +170 -0
  74. package/src/number/branded-types/non-negative-finite-number.mts +223 -0
  75. package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
  76. package/src/number/branded-types/non-negative-int16.mts +187 -0
  77. package/src/number/branded-types/non-negative-int16.test.mts +201 -0
  78. package/src/number/branded-types/non-negative-int32.mts +187 -0
  79. package/src/number/branded-types/non-negative-int32.test.mts +204 -0
  80. package/src/number/branded-types/non-zero-finite-number.mts +229 -0
  81. package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
  82. package/src/number/branded-types/non-zero-int.mts +167 -0
  83. package/src/number/branded-types/non-zero-int.test.mts +177 -0
  84. package/src/number/branded-types/non-zero-int16.mts +196 -0
  85. package/src/number/branded-types/non-zero-int16.test.mts +195 -0
  86. package/src/number/branded-types/non-zero-int32.mts +196 -0
  87. package/src/number/branded-types/non-zero-int32.test.mts +197 -0
  88. package/src/number/branded-types/non-zero-safe-int.mts +196 -0
  89. package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
  90. package/src/number/branded-types/non-zero-uint16.mts +189 -0
  91. package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
  92. package/src/number/branded-types/non-zero-uint32.mts +189 -0
  93. package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
  94. package/src/number/branded-types/positive-finite-number.mts +241 -0
  95. package/src/number/branded-types/positive-finite-number.test.mts +204 -0
  96. package/src/number/branded-types/positive-int.mts +304 -0
  97. package/src/number/branded-types/positive-int.test.mts +176 -0
  98. package/src/number/branded-types/positive-int16.mts +188 -0
  99. package/src/number/branded-types/positive-int16.test.mts +197 -0
  100. package/src/number/branded-types/positive-int32.mts +188 -0
  101. package/src/number/branded-types/positive-int32.test.mts +197 -0
  102. package/src/number/branded-types/positive-safe-int.mts +187 -0
  103. package/src/number/branded-types/positive-safe-int.test.mts +210 -0
  104. package/src/number/branded-types/positive-uint16.mts +188 -0
  105. package/src/number/branded-types/positive-uint16.test.mts +203 -0
  106. package/src/number/branded-types/positive-uint32.mts +188 -0
  107. package/src/number/branded-types/positive-uint32.test.mts +203 -0
  108. package/src/number/branded-types/safe-int.mts +291 -0
  109. package/src/number/branded-types/safe-int.test.mts +170 -0
  110. package/src/number/branded-types/safe-uint.mts +187 -0
  111. package/src/number/branded-types/safe-uint.test.mts +176 -0
  112. package/src/number/branded-types/uint.mts +179 -0
  113. package/src/number/branded-types/uint.test.mts +158 -0
  114. package/src/number/branded-types/uint16.mts +186 -0
  115. package/src/number/branded-types/uint16.test.mts +170 -0
  116. package/src/number/branded-types/uint32.mts +218 -0
  117. package/src/number/branded-types/uint32.test.mts +170 -0
  118. package/src/number/enum/index.mts +2 -0
  119. package/src/number/enum/int8.mts +344 -0
  120. package/src/number/enum/int8.test.mts +180 -0
  121. package/src/number/enum/uint8.mts +293 -0
  122. package/src/number/enum/uint8.test.mts +164 -0
  123. package/src/number/index.mts +4 -0
  124. package/src/number/num.mts +604 -0
  125. package/src/number/num.test.mts +242 -0
  126. package/src/number/refined-number-utils.mts +566 -0
  127. package/src/object/index.mts +1 -0
  128. package/src/object/object.mts +447 -0
  129. package/src/object/object.test.mts +124 -0
  130. package/src/others/cast-mutable.mts +113 -0
  131. package/src/others/cast-readonly.mts +192 -0
  132. package/src/others/cast-readonly.test.mts +89 -0
  133. package/src/others/if-then.mts +98 -0
  134. package/src/others/if-then.test.mts +75 -0
  135. package/src/others/index.mts +7 -0
  136. package/src/others/map-nullable.mts +172 -0
  137. package/src/others/map-nullable.test.mts +297 -0
  138. package/src/others/memoize-function.mts +196 -0
  139. package/src/others/memoize-function.test.mts +168 -0
  140. package/src/others/tuple.mts +160 -0
  141. package/src/others/tuple.test.mts +11 -0
  142. package/src/others/unknown-to-string.mts +215 -0
  143. package/src/others/unknown-to-string.test.mts +114 -0
@@ -0,0 +1,682 @@
1
+ import { Result } from '../functional/index.mjs';
2
+ import { asUint32 } from '../number/index.mjs';
3
+ import { unknownToString } from '../others/unknown-to-string.mjs';
4
+
5
+ /**
6
+ * Interface for an immutable set with O(1) lookup performance and set operation support.
7
+ *
8
+ * This interface defines all methods and properties available on ISet instances. All operations
9
+ * that modify the set return new ISet instances, preserving immutability. The underlying implementation
10
+ * uses JavaScript's native Set for O(1) average-case performance on add, has, and delete operations.
11
+ *
12
+ * **Immutability Guarantees:**
13
+ * - All mutation operations (add, delete) return new ISet instances
14
+ * - Original ISet instances are never modified
15
+ * - Safe for concurrent access and functional programming patterns
16
+ *
17
+ * **Performance Characteristics:**
18
+ * - has/add/delete: O(1) average case
19
+ * - Set operations (union, intersection, difference): O(n)
20
+ * - map/filter operations: O(n)
21
+ * - Iteration: O(n)
22
+ *
23
+ * @template K The type of the elements in the set. Must extend MapSetKeyType (string, number, boolean, etc.)
24
+ */
25
+ type ISetInterface<K extends MapSetKeyType> = Readonly<{
26
+ // Getting information
27
+
28
+ /** The number of elements in the set. */
29
+ size: SizeType.Arr;
30
+
31
+ /** Checks if the set is empty. */
32
+ isEmpty: boolean;
33
+ /**
34
+ * Checks if an element exists in the set.
35
+ * Allows for wider literal types for keys during checking.
36
+ * @param key The element to check.
37
+ * @returns `true` if the element exists, `false` otherwise.
38
+ */
39
+ has: (key: K | (WidenLiteral<K> & {})) => boolean;
40
+
41
+ // Reducing a value
42
+
43
+ /**
44
+ * Checks if all elements in the set satisfy a predicate.
45
+ * @param predicate A function to test each element.
46
+ * @returns `true` if all elements satisfy the predicate, `false` otherwise.
47
+ */
48
+ every: ((predicate: (key: K) => boolean) => boolean) &
49
+ /**
50
+ * Checks if all elements in the set satisfy a type predicate.
51
+ * Narrows the type of elements in the set if the predicate returns true for all elements.
52
+ * @template L The narrowed type of the elements.
53
+ * @param predicate A type predicate function.
54
+ * @returns `true` if all elements satisfy the predicate, `false` otherwise.
55
+ */
56
+ (<L extends K>(predicate: (key: K) => key is L) => this is ISet<L>);
57
+
58
+ /**
59
+ * Checks if at least one element in the set satisfies a predicate.
60
+ * @param predicate A function to test each element.
61
+ * @returns `true` if at least one element satisfies the predicate, `false` otherwise.
62
+ */
63
+ some: (predicate: (key: K) => boolean) => boolean;
64
+
65
+ // Mutation
66
+
67
+ /**
68
+ * Adds an element to the set.
69
+ * @param key The element to add.
70
+ * @returns A new ISet instance with the element added.
71
+ */
72
+ add: (key: K) => ISet<K>;
73
+
74
+ /**
75
+ * Deletes an element from the set.
76
+ * @param key The element to delete.
77
+ * @returns A new ISet instance without the specified element.
78
+ */
79
+ delete: (key: K) => ISet<K>;
80
+
81
+ /**
82
+ * Applies a series of mutations to the set.
83
+ * @param actions An array of mutation actions (add or delete).
84
+ * @returns A new ISet instance with all mutations applied.
85
+ */
86
+ withMutations: (
87
+ actions: readonly Readonly<
88
+ { type: 'add'; key: K } | { type: 'delete'; key: K }
89
+ >[],
90
+ ) => ISet<K>;
91
+
92
+ // Sequence algorithms
93
+
94
+ /**
95
+ * Maps the elements of the set to new elements.
96
+ * @template K2 The type of the new elements.
97
+ * @param mapFn A function that maps an element to a new element.
98
+ * @returns A new ISet instance with mapped elements.
99
+ */
100
+ map: <K2 extends MapSetKeyType>(mapFn: (key: K) => K2) => ISet<K2>;
101
+
102
+ /**
103
+ * Filters the elements of the set based on a type predicate.
104
+ * Narrows the type of elements in the resulting set.
105
+ * @template K2 The narrowed type of the elements.
106
+ * @param predicate A type predicate function.
107
+ * @returns A new ISet instance with elements that satisfy the type predicate.
108
+ */
109
+ filter: (<K2 extends K>(predicate: (key: K) => key is K2) => ISet<K2>) &
110
+ /**
111
+ * Filters the elements of the set based on a predicate.
112
+ * @param predicate A function to test each element.
113
+ * @returns A new ISet instance with elements that satisfy the predicate.
114
+ */
115
+ ((predicate: (key: K) => boolean) => ISet<K>);
116
+
117
+ /**
118
+ * Filters the elements of the set by excluding elements for which the predicate returns true.
119
+ * @param predicate A function to test each element.
120
+ * @returns A new ISet instance with elements for which the predicate returned `false`.
121
+ */
122
+ filterNot: (predicate: (key: K) => boolean) => ISet<K>;
123
+
124
+ // Set operations
125
+ /**
126
+ * Checks if this set is a subset of another set.
127
+ * @param set The other set.
128
+ * @returns `true` if this set is a subset of the other set, `false` otherwise.
129
+ */
130
+ isSubsetOf: (set: ISet<WidenLiteral<K>>) => boolean;
131
+ /**
132
+ * Checks if this set is a superset of another set.
133
+ * @param set The other set.
134
+ * @returns `true` if this set is a superset of the other set, `false` otherwise.
135
+ */
136
+ isSupersetOf: (set: ISet<WidenLiteral<K>>) => boolean;
137
+ /**
138
+ * Returns a new set with elements that are in this set but not in another set.
139
+ * @param set The other set.
140
+ * @returns A new ISet instance representing the set difference.
141
+ */
142
+ subtract: (set: ISet<K>) => ISet<K>;
143
+ /**
144
+ * Returns a new set with elements that are common to both this set and another set.
145
+ * @param set The other set.
146
+ * @returns A new ISet instance representing the set intersection.
147
+ */
148
+ intersect: (set: ISet<K>) => ISet<K>;
149
+ /**
150
+ * Returns a new set with all elements from both this set and another set.
151
+ * @template K2 The type of elements in the other set.
152
+ * @param set The other set.
153
+ * @returns A new ISet instance representing the set union.
154
+ */
155
+ union: <K2 extends MapSetKeyType>(set: ISet<K2>) => ISet<K | K2>;
156
+
157
+ // Side effects
158
+ /**
159
+ * Executes a callback function for each element in the set.
160
+ * @param callbackfn A function to execute for each element.
161
+ */
162
+ forEach: (callbackfn: (key: K) => void) => void;
163
+
164
+ // Iterators
165
+ /**
166
+ * Returns an iterator for the elements in the set (alias for values).
167
+ * @returns An iterable iterator of elements.
168
+ */
169
+ keys: () => IterableIterator<K>;
170
+ /**
171
+ * Returns an iterator for the elements in the set.
172
+ * @returns An iterable iterator of elements.
173
+ */
174
+ values: () => IterableIterator<K>;
175
+ /**
176
+ * Returns an iterator for the entries (element-element pairs) in the set.
177
+ * @returns An iterable iterator of entries.
178
+ */
179
+ entries: () => IterableIterator<readonly [K, K]>;
180
+
181
+ // Conversion
182
+ /**
183
+ * Converts the elements of the set to an array.
184
+ * @returns A readonly array of elements.
185
+ */
186
+ toArray: () => readonly K[];
187
+ /**
188
+ * Returns the underlying readonly JavaScript Set.
189
+ * @returns The raw ReadonlySet instance.
190
+ */
191
+ toRawSet: () => ReadonlySet<K>;
192
+ }>;
193
+
194
+ /**
195
+ * Represents an immutable set with high-performance operations and comprehensive set algebra support.
196
+ *
197
+ * ISet is a persistent data structure that provides all the functionality of JavaScript's Set
198
+ * while maintaining immutability. All operations that would normally mutate the set instead
199
+ * return new ISet instances, making it safe for functional programming and concurrent access.
200
+ *
201
+ * **Key Features:**
202
+ * - **Immutable**: All mutation operations return new instances
203
+ * - **High Performance**: O(1) average-case for has/add/delete operations
204
+ * - **Set Operations**: Full support for union, intersection, difference, subset/superset checks
205
+ * - **Type Safe**: Full TypeScript support with generic element types
206
+ * - **Iterable**: Implements standard JavaScript iteration protocols
207
+ * - **Functional**: Rich API for map, filter, and functional composition
208
+ *
209
+ * **When to Use:**
210
+ * - Managing collections of unique values with immutability guarantees
211
+ * - Set algebra operations (unions, intersections, differences)
212
+ * - Membership testing with O(1) performance
213
+ * - Functional programming patterns requiring immutable collections
214
+ *
215
+ * @template K The type of the elements in the set. Must extend MapSetKeyType.
216
+ */
217
+ export type ISet<K extends MapSetKeyType> = Iterable<K> & ISetInterface<K>;
218
+
219
+ /**
220
+ * Provides utility functions for ISet.
221
+ */
222
+ export namespace ISet {
223
+ /**
224
+ * Creates a new ISet instance from an iterable of elements.
225
+ *
226
+ * This factory function accepts any iterable of elements, including arrays,
227
+ * JavaScript Sets, other ISets, or custom iterables. Duplicate elements in the
228
+ * input iterable will be automatically deduplicated in the resulting set.
229
+ *
230
+ * **Performance:** O(n) where n is the number of elements in the iterable.
231
+ *
232
+ * @template K The type of the elements. Must extend MapSetKeyType.
233
+ * @param iterable An iterable of elements (e.g., Array, Set, ISet, etc.)
234
+ * @returns A new ISet instance containing all unique elements from the iterable.
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // From array (duplicates automatically removed)
239
+ * const uniqueIds = ISet.create([1, 2, 3, 2, 1]); // Contains: 1, 2, 3
240
+ * console.log(uniqueIds.size); // Output: 3
241
+ *
242
+ * // From JavaScript Set
243
+ * const jsSet = new Set(["red", "green", "blue"]);
244
+ * const colors = ISet.create(jsSet);
245
+ * console.log(colors.has("red")); // Output: true
246
+ *
247
+ * // From another ISet (creates a copy)
248
+ * const originalTags = ISet.create(["typescript", "immutable"]);
249
+ * const copiedTags = ISet.create(originalTags);
250
+ * console.log(copiedTags.size); // Output: 2
251
+ *
252
+ * // Empty set
253
+ * const emptyPermissions = ISet.create<string>([]);
254
+ * console.log(emptyPermissions.isEmpty); // Output: true
255
+ *
256
+ * // Fluent operations
257
+ * const processedNumbers = ISet.create([1, 2, 3, 4, 5])
258
+ * .filter(x => x % 2 === 0) // Keep even numbers: 2, 4
259
+ * .add(6) // Add 6: 2, 4, 6
260
+ * .delete(2); // Remove 2: 4, 6
261
+ * console.log(processedNumbers.toArray().sort()); // Output: [4, 6]
262
+ *
263
+ * // From generator function
264
+ * function* generatePrimes(): Generator<number> {
265
+ * yield 2; yield 3; yield 5; yield 7;
266
+ * }
267
+ * const primes = ISet.create(generatePrimes());
268
+ * console.log(primes.size); // Output: 4
269
+ * ```
270
+ */
271
+ export const create = <K extends MapSetKeyType>(
272
+ iterable: Iterable<K>,
273
+ ): ISet<K> => new ISetClass<K>(iterable);
274
+
275
+ /**
276
+ * Checks if two ISet instances are structurally equal.
277
+ *
278
+ * Two ISets are considered equal if they have the same size and contain exactly the same
279
+ * elements. The order of elements does not matter for equality comparison since sets are
280
+ * unordered collections. Elements are compared using JavaScript's `===` operator.
281
+ *
282
+ * **Performance:** O(n) where n is the size of the smaller set.
283
+ *
284
+ * @template K The type of the elements.
285
+ * @param a The first ISet instance to compare.
286
+ * @param b The second ISet instance to compare.
287
+ * @returns `true` if the sets contain exactly the same elements, `false` otherwise.
288
+ *
289
+ * @example
290
+ * ```typescript
291
+ * // Basic equality comparison
292
+ * const permissions1 = ISet.create(["read", "write", "execute"]);
293
+ * const permissions2 = ISet.create(["execute", "read", "write"]); // Order doesn't matter
294
+ * const permissions3 = ISet.create(["read", "write"]);
295
+ *
296
+ * console.log(ISet.equal(permissions1, permissions2)); // true
297
+ * console.log(ISet.equal(permissions1, permissions3)); // false (different sizes)
298
+ *
299
+ * // With different element types
300
+ * const numbers1 = ISet.create([1, 2, 3]);
301
+ * const numbers2 = ISet.create([3, 1, 2]);
302
+ * const numbers3 = ISet.create([1, 2, 4]); // Different element
303
+ *
304
+ * console.log(ISet.equal(numbers1, numbers2)); // true
305
+ * console.log(ISet.equal(numbers1, numbers3)); // false
306
+ *
307
+ * // Empty sets
308
+ * const empty1 = ISet.create<string>([]);
309
+ * const empty2 = ISet.create<string>([]);
310
+ * console.log(ISet.equal(empty1, empty2)); // true
311
+ *
312
+ * // Single element sets
313
+ * const single1 = ISet.create(["unique"]);
314
+ * const single2 = ISet.create(["unique"]);
315
+ * const single3 = ISet.create(["different"]);
316
+ *
317
+ * console.log(ISet.equal(single1, single2)); // true
318
+ * console.log(ISet.equal(single1, single3)); // false
319
+ * ```
320
+ */
321
+ export const equal = <K extends MapSetKeyType>(
322
+ a: ISet<K>,
323
+ b: ISet<K>,
324
+ ): boolean => a.size === b.size && a.every((e) => b.has(e));
325
+
326
+ /**
327
+ * Computes the difference between two ISet instances, identifying added and deleted elements.
328
+ *
329
+ * This function performs a set difference operation to determine what elements were added
330
+ * and what elements were deleted when transitioning from the old set to the new set.
331
+ * This is useful for change detection, state management, and synchronization scenarios.
332
+ *
333
+ * **Performance:** O(n + m) where n and m are the sizes of the old and new sets respectively.
334
+ *
335
+ * @template K The type of the elements.
336
+ * @param oldSet The original set representing the previous state.
337
+ * @param newSet The new set representing the current state.
338
+ * @returns An object with `added` and `deleted` properties, each containing an ISet
339
+ * of elements that were added or removed respectively.
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * // User permission changes
344
+ * const oldPermissions = ISet.create(["read", "write", "delete"]);
345
+ * const newPermissions = ISet.create(["read", "write", "execute", "admin"]);
346
+ *
347
+ * const permissionDiff = ISet.diff(oldPermissions, newPermissions);
348
+ *
349
+ * console.log("Permissions removed:", permissionDiff.deleted.toArray());
350
+ * // Output: ["delete"]
351
+ *
352
+ * console.log("Permissions added:", permissionDiff.added.toArray());
353
+ * // Output: ["execute", "admin"]
354
+ *
355
+ * // No changes
356
+ * const unchanged1 = ISet.create(["a", "b", "c"]);
357
+ * const unchanged2 = ISet.create(["a", "b", "c"]);
358
+ * const noDiff = ISet.diff(unchanged1, unchanged2);
359
+ *
360
+ * console.log(noDiff.added.isEmpty); // true
361
+ * console.log(noDiff.deleted.isEmpty); // true
362
+ *
363
+ * // Complete replacement
364
+ * const oldTags = ISet.create(["javascript", "react"]);
365
+ * const newTags = ISet.create(["typescript", "vue"]);
366
+ * const tagDiff = ISet.diff(oldTags, newTags);
367
+ *
368
+ * console.log(tagDiff.deleted.toArray()); // ["javascript", "react"]
369
+ * console.log(tagDiff.added.toArray()); // ["typescript", "vue"]
370
+ * ```
371
+ */
372
+ export const diff = <K extends MapSetKeyType>(
373
+ oldSet: ISet<K>,
374
+ newSet: ISet<K>,
375
+ ): ReadonlyRecord<'added' | 'deleted', ISet<K>> => ({
376
+ deleted: oldSet.subtract(newSet),
377
+ added: newSet.subtract(oldSet),
378
+ });
379
+
380
+ /**
381
+ * Computes the intersection of two ISet instances.
382
+ *
383
+ * Returns a new set containing only the elements that are present in both input sets.
384
+ * This operation is commutative: `intersection(a, b) === intersection(b, a)`.
385
+ *
386
+ * **Performance:** O(min(n, m)) where n and m are the sizes of the input sets.
387
+ *
388
+ * @template K The type of the elements.
389
+ * @param a The first set.
390
+ * @param b The second set.
391
+ * @returns A new ISet instance containing elements common to both sets.
392
+ *
393
+ * @example
394
+ * ```typescript
395
+ * // Finding common permissions between user and role
396
+ * const userPermissions = ISet.create(["read", "write", "delete", "admin"]);
397
+ * const rolePermissions = ISet.create(["read", "write", "execute"]);
398
+ *
399
+ * const commonPermissions = ISet.intersection(userPermissions, rolePermissions);
400
+ * console.log(commonPermissions.toArray()); // ["read", "write"]
401
+ *
402
+ * // No common elements
403
+ * const setA = ISet.create([1, 2, 3]);
404
+ * const setB = ISet.create([4, 5, 6]);
405
+ * const noCommon = ISet.intersection(setA, setB);
406
+ * console.log(noCommon.isEmpty); // true
407
+ *
408
+ * // Complete overlap
409
+ * const identical1 = ISet.create(["a", "b", "c"]);
410
+ * const identical2 = ISet.create(["a", "b", "c"]);
411
+ * const completeOverlap = ISet.intersection(identical1, identical2);
412
+ * console.log(ISet.equal(completeOverlap, identical1)); // true
413
+ *
414
+ * // Intersection with empty set
415
+ * const nonEmpty = ISet.create([1, 2, 3]);
416
+ * const empty = ISet.create<number>([]);
417
+ * const withEmpty = ISet.intersection(nonEmpty, empty);
418
+ * console.log(withEmpty.isEmpty); // true
419
+ * ```
420
+ */
421
+ export const intersection = <K extends MapSetKeyType>(
422
+ a: ISet<K>,
423
+ b: ISet<K>,
424
+ ): ISet<K> => a.intersect(b);
425
+
426
+ /**
427
+ * Computes the union of two ISet instances.
428
+ *
429
+ * Returns a new set containing all elements that are present in either input set.
430
+ * Duplicate elements are automatically handled since sets only contain unique values.
431
+ * This operation is commutative: `union(a, b) === union(b, a)`.
432
+ *
433
+ * **Performance:** O(n + m) where n and m are the sizes of the input sets.
434
+ *
435
+ * @template K1 The type of elements in the first set.
436
+ * @template K2 The type of elements in the second set.
437
+ * @param a The first set.
438
+ * @param b The second set.
439
+ * @returns A new ISet instance containing all elements from both sets.
440
+ *
441
+ * @example
442
+ * ```typescript
443
+ * // Combining permissions from multiple sources
444
+ * const userPermissions = ISet.create(["read", "write"]);
445
+ * const rolePermissions = ISet.create(["write", "execute", "admin"]);
446
+ *
447
+ * const allPermissions = ISet.union(userPermissions, rolePermissions);
448
+ * console.log(allPermissions.toArray().sort());
449
+ * // Output: ["admin", "execute", "read", "write"]
450
+ *
451
+ * // Union with different types (type widening)
452
+ * const numbers = ISet.create([1, 2, 3]);
453
+ * const strings = ISet.create(["a", "b"]);
454
+ * const mixed = ISet.union(numbers, strings); // ISet<number | string>
455
+ * console.log(mixed.size); // 5
456
+ *
457
+ * // Union with empty set
458
+ * const nonEmpty = ISet.create(["item1", "item2"]);
459
+ * const empty = ISet.create<string>([]);
460
+ * const withEmpty = ISet.union(nonEmpty, empty);
461
+ * console.log(ISet.equal(withEmpty, nonEmpty)); // true
462
+ *
463
+ * // Overlapping sets
464
+ * const featuresA = ISet.create(["feature1", "feature2", "feature3"]);
465
+ * const featuresB = ISet.create(["feature2", "feature3", "feature4"]);
466
+ * const allFeatures = ISet.union(featuresA, featuresB);
467
+ * console.log(allFeatures.size); // 4 (duplicates removed)
468
+ * ```
469
+ */
470
+ export const union = <K1 extends MapSetKeyType, K2 extends MapSetKeyType>(
471
+ a: ISet<K1>,
472
+ b: ISet<K2>,
473
+ ): ISet<K1 | K2> => a.union(b);
474
+ }
475
+
476
+ /**
477
+ * Internal class implementation for ISet providing immutable set operations.
478
+ *
479
+ * This class implements the ISet interface using JavaScript's native Set as the underlying
480
+ * storage mechanism for optimal performance. All mutation operations create new instances
481
+ * rather than modifying the existing set, ensuring immutability.
482
+ *
483
+ * **Implementation Details:**
484
+ * - Uses ReadonlySet<K> internally for type safety and performance
485
+ * - Implements copy-on-write semantics for efficiency
486
+ * - Provides optional debug messaging for development
487
+ *
488
+ * @template K The type of the elements. Must extend MapSetKeyType.
489
+ * @implements ISet
490
+ * @implements Iterable
491
+ * @internal This class should not be used directly. Use ISet.create() instead.
492
+ */
493
+ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
494
+ readonly #set: ReadonlySet<K>;
495
+ readonly #showNotFoundMessage: boolean;
496
+
497
+ /**
498
+ * Constructs an ISetClass instance with the given elements.
499
+ *
500
+ * @param iterable An iterable of elements to populate the set.
501
+ * @param showNotFoundMessage Whether to log warning messages when operations
502
+ * are performed on non-existent elements. Useful for debugging.
503
+ * Defaults to false for production use.
504
+ * @internal Use ISet.create() instead of calling this constructor directly.
505
+ */
506
+ constructor(iterable: Iterable<K>, showNotFoundMessage: boolean = false) {
507
+ this.#set = new Set(iterable);
508
+ this.#showNotFoundMessage = showNotFoundMessage;
509
+ }
510
+
511
+ /** @inheritdoc */
512
+ get size(): SizeType.Arr {
513
+ return asUint32(this.#set.size);
514
+ }
515
+
516
+ /** @inheritdoc */
517
+ get isEmpty(): boolean {
518
+ return this.size === 0;
519
+ }
520
+
521
+ /** @inheritdoc */
522
+ has(key: K | (WidenLiteral<K> & {})): boolean {
523
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
524
+ return this.#set.has(key as K);
525
+ }
526
+
527
+ /** @inheritdoc */
528
+ every<L extends K>(predicate: (key: K) => key is L): this is ISet<L>;
529
+
530
+ /** @inheritdoc */
531
+ every(predicate: (key: K) => boolean): boolean;
532
+
533
+ /** @inheritdoc */
534
+ every(predicate: (key: K) => boolean): boolean {
535
+ for (const key of this.values()) {
536
+ if (!predicate(key)) return false;
537
+ }
538
+
539
+ return true;
540
+ }
541
+
542
+ /** @inheritdoc */
543
+ some(predicate: (key: K) => boolean): boolean {
544
+ for (const key of this.values()) {
545
+ if (predicate(key)) return true;
546
+ }
547
+
548
+ return false;
549
+ }
550
+
551
+ /** @inheritdoc */
552
+ add(key: K): ISet<K> {
553
+ if (this.has(key)) return this;
554
+
555
+ return ISet.create([...this.#set, key]);
556
+ }
557
+
558
+ /** @inheritdoc */
559
+ delete(key: K): ISet<K> {
560
+ if (!this.has(key)) {
561
+ if (this.#showNotFoundMessage) {
562
+ const keyStr = unknownToString(key);
563
+ console.warn(
564
+ `ISet.delete: key not found: ${Result.isOk(keyStr) ? keyStr.value : '<error converting key to string>'}`,
565
+ );
566
+ }
567
+ return this;
568
+ }
569
+
570
+ return ISet.create(Array.from(this.#set).filter((k) => !Object.is(k, key)));
571
+ }
572
+
573
+ /** @inheritdoc */
574
+ withMutations(
575
+ actions: readonly Readonly<
576
+ { type: 'add'; key: K } | { type: 'delete'; key: K }
577
+ >[],
578
+ ): ISet<K> {
579
+ const mut_result = new Set<K>(this.#set);
580
+
581
+ for (const action of actions) {
582
+ switch (action.type) {
583
+ case 'delete':
584
+ mut_result.delete(action.key);
585
+ break;
586
+
587
+ case 'add':
588
+ mut_result.add(action.key);
589
+ break;
590
+ }
591
+ }
592
+
593
+ return ISet.create(mut_result);
594
+ }
595
+
596
+ /** @inheritdoc */
597
+ map<K2 extends MapSetKeyType>(mapFn: (key: K) => K2): ISet<K2> {
598
+ return ISet.create(this.toArray().map(mapFn));
599
+ }
600
+
601
+ /** @inheritdoc */
602
+ filter<K2 extends K>(predicate: (key: K) => key is K2): ISet<K2>;
603
+
604
+ /** @inheritdoc */
605
+ filter(predicate: (key: K) => boolean): ISet<K>;
606
+
607
+ /** @inheritdoc */
608
+ filter(predicate: (key: K) => boolean): ISet<K> {
609
+ return ISet.create(this.toArray().filter(predicate));
610
+ }
611
+
612
+ /** @inheritdoc */
613
+ filterNot(predicate: (key: K) => boolean): ISet<K> {
614
+ return ISet.create(this.toArray().filter((e) => !predicate(e)));
615
+ }
616
+
617
+ /** @inheritdoc */
618
+ forEach(callbackfn: (key: K) => void): void {
619
+ for (const v of this.#set.values()) {
620
+ callbackfn(v);
621
+ }
622
+ }
623
+
624
+ /** @inheritdoc */
625
+ isSubsetOf(set: ISet<WidenLiteral<K>>): boolean {
626
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
627
+ return this.every((k) => set.has(k as WidenLiteral<K>));
628
+ }
629
+
630
+ /** @inheritdoc */
631
+ isSupersetOf(set: ISet<WidenLiteral<K>>): boolean {
632
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
633
+ return set.every((k) => this.has(k as K));
634
+ }
635
+
636
+ /** @inheritdoc */
637
+ subtract(set: ISet<K>): ISet<K> {
638
+ return ISet.create(this.toArray().filter((k) => !set.has(k)));
639
+ }
640
+
641
+ /** @inheritdoc */
642
+ intersect(set: ISet<K>): ISet<K> {
643
+ return ISet.create(this.toArray().filter((k) => set.has(k)));
644
+ }
645
+
646
+ /** @inheritdoc */
647
+ union<K2 extends MapSetKeyType>(set: ISet<K2>): ISet<K | K2> {
648
+ return ISet.create([...this, ...set]);
649
+ }
650
+
651
+ /**
652
+ * @inheritdoc
653
+ */
654
+ [Symbol.iterator](): Iterator<K> {
655
+ return this.#set[Symbol.iterator]();
656
+ }
657
+
658
+ /** @inheritdoc */
659
+ keys(): IterableIterator<K> {
660
+ return this.#set.keys();
661
+ }
662
+
663
+ /** @inheritdoc */
664
+ values(): IterableIterator<K> {
665
+ return this.#set.values();
666
+ }
667
+
668
+ /** @inheritdoc */
669
+ entries(): IterableIterator<readonly [K, K]> {
670
+ return this.#set.entries();
671
+ }
672
+
673
+ /** @inheritdoc */
674
+ toArray(): readonly K[] {
675
+ return Array.from(this.values());
676
+ }
677
+
678
+ /** @inheritdoc */
679
+ toRawSet(): ReadonlySet<K> {
680
+ return this.#set;
681
+ }
682
+ }